|
9 | 9 | "os" |
10 | 10 | "path/filepath" |
11 | 11 | "reflect" |
| 12 | + "regexp" |
12 | 13 | "runtime" |
13 | 14 | "strings" |
14 | 15 | "unicode" |
@@ -401,15 +402,40 @@ func (v *Validator) CheckMounts() (msgs []string) { |
401 | 402 | return |
402 | 403 | } |
403 | 404 |
|
404 | | - for _, mount := range v.spec.Mounts { |
405 | | - if supportedTypes != nil { |
406 | | - if !supportedTypes[mount.Type] { |
407 | | - msgs = append(msgs, fmt.Sprintf("Unsupported mount type %q", mount.Type)) |
| 405 | + for i, mountA := range v.spec.Mounts { |
| 406 | + if supportedTypes != nil && !supportedTypes[mountA.Type] { |
| 407 | + msgs = append(msgs, fmt.Sprintf("Unsupported mount type %q", mountA.Type)) |
| 408 | + } |
| 409 | + if v.platform == "windows" { |
| 410 | + if err := pathValid(v.platform, mountA.Destination); err != nil { |
| 411 | + msgs = append(msgs, err.Error()) |
| 412 | + } |
| 413 | + if err := pathValid(v.platform, mountA.Source); err != nil { |
| 414 | + msgs = append(msgs, err.Error()) |
| 415 | + } |
| 416 | + } else { |
| 417 | + if err := pathValid(v.platform, mountA.Destination); err != nil { |
| 418 | + msgs = append(msgs, err.Error()) |
408 | 419 | } |
409 | 420 | } |
410 | | - |
411 | | - if !filepath.IsAbs(mount.Destination) { |
412 | | - msgs = append(msgs, fmt.Sprintf("destination %v is not an absolute path", mount.Destination)) |
| 421 | + for j, mountB := range v.spec.Mounts { |
| 422 | + if i == j { |
| 423 | + continue |
| 424 | + } |
| 425 | + // whether B.Desination is nested within A.Destination |
| 426 | + nested, err := nestedValid(v.platform, mountA.Destination, mountB.Destination) |
| 427 | + if err != nil { |
| 428 | + msgs = append(msgs, err.Error()) |
| 429 | + continue |
| 430 | + } |
| 431 | + if nested { |
| 432 | + if v.platform == "windows" && i < j { |
| 433 | + msgs = append(msgs, fmt.Sprintf("On Windows, %v nested within %v is forbidden", mountB.Destination, mountA.Destination)) |
| 434 | + } |
| 435 | + if i > j { |
| 436 | + logrus.Warnf("%v will be covered by %v", mountB.Destination, mountA.Destination) |
| 437 | + } |
| 438 | + } |
413 | 439 | } |
414 | 440 | } |
415 | 441 |
|
@@ -708,6 +734,65 @@ func namespaceValid(ns rspec.LinuxNamespace) bool { |
708 | 734 | return true |
709 | 735 | } |
710 | 736 |
|
| 737 | +func pathValid(os, path string) error { |
| 738 | + if os == "windows" { |
| 739 | + matched, err := regexp.MatchString("^[a-zA-Z]:(\\\\[^\\\\/<>|:*?\"]+)+$", path) |
| 740 | + if err != nil { |
| 741 | + return err |
| 742 | + } |
| 743 | + if !matched { |
| 744 | + return fmt.Errorf("invalid windows path %v", path) |
| 745 | + } |
| 746 | + return nil |
| 747 | + } |
| 748 | + if !filepath.IsAbs(path) { |
| 749 | + return fmt.Errorf("%v is not an absolute path", path) |
| 750 | + } |
| 751 | + return nil |
| 752 | +} |
| 753 | + |
| 754 | +// Check whether pathB is nested whithin pathA |
| 755 | +func nestedValid(os, pathA, pathB string) (bool, error) { |
| 756 | + if pathA == pathB { |
| 757 | + return false, nil |
| 758 | + } |
| 759 | + if pathA == "/" && pathB != "" { |
| 760 | + return true, nil |
| 761 | + } |
| 762 | + |
| 763 | + var sep string |
| 764 | + if os == "windows" { |
| 765 | + sep = "\\" |
| 766 | + } else { |
| 767 | + sep = "/" |
| 768 | + } |
| 769 | + |
| 770 | + splitedPathA := strings.Split(filepath.Clean(pathA), sep) |
| 771 | + splitedPathB := strings.Split(filepath.Clean(pathB), sep) |
| 772 | + lenA := len(splitedPathA) |
| 773 | + lenB := len(splitedPathB) |
| 774 | + |
| 775 | + if lenA > lenB { |
| 776 | + if (lenA - lenB) == 1 { |
| 777 | + // if pathA is longer but not end with separator |
| 778 | + if splitedPathA[lenA-1] != "" { |
| 779 | + return false, nil |
| 780 | + } |
| 781 | + splitedPathA = splitedPathA[:lenA-1] |
| 782 | + } else { |
| 783 | + return false, nil |
| 784 | + } |
| 785 | + } |
| 786 | + |
| 787 | + for i, partA := range splitedPathA { |
| 788 | + if partA != splitedPathB[i] { |
| 789 | + return false, nil |
| 790 | + } |
| 791 | + } |
| 792 | + |
| 793 | + return true, nil |
| 794 | +} |
| 795 | + |
711 | 796 | func deviceValid(d rspec.LinuxDevice) bool { |
712 | 797 | switch d.Type { |
713 | 798 | case "b", "c", "u": |
|
0 commit comments