|
9 | 9 | "os" |
10 | 10 | "path/filepath" |
11 | 11 | "reflect" |
| 12 | + "regexp" |
12 | 13 | "runtime" |
13 | 14 | "strings" |
14 | 15 | "syscall" |
@@ -402,15 +403,40 @@ func (v *Validator) CheckMounts() (msgs []string) { |
402 | 403 | return |
403 | 404 | } |
404 | 405 |
|
405 | | - for _, mount := range v.spec.Mounts { |
406 | | - if supportedTypes != nil { |
407 | | - if !supportedTypes[mount.Type] { |
408 | | - msgs = append(msgs, fmt.Sprintf("Unsupported mount type %q", mount.Type)) |
| 406 | + for i, mountA := range v.spec.Mounts { |
| 407 | + if supportedTypes != nil && !supportedTypes[mountA.Type] { |
| 408 | + msgs = append(msgs, fmt.Sprintf("Unsupported mount type %q", mountA.Type)) |
| 409 | + } |
| 410 | + if v.platform == "windows" { |
| 411 | + if err := pathValid(v.platform, mountA.Destination); err != nil { |
| 412 | + msgs = append(msgs, err.Error()) |
| 413 | + } |
| 414 | + if err := pathValid(v.platform, mountA.Source); err != nil { |
| 415 | + msgs = append(msgs, err.Error()) |
| 416 | + } |
| 417 | + } else { |
| 418 | + if err := pathValid(v.platform, mountA.Destination); err != nil { |
| 419 | + msgs = append(msgs, err.Error()) |
409 | 420 | } |
410 | 421 | } |
411 | | - |
412 | | - if !filepath.IsAbs(mount.Destination) { |
413 | | - msgs = append(msgs, fmt.Sprintf("destination %v is not an absolute path", mount.Destination)) |
| 422 | + for j, mountB := range v.spec.Mounts { |
| 423 | + if i == j { |
| 424 | + continue |
| 425 | + } |
| 426 | + // whether B.Desination is nested within A.Destination |
| 427 | + nested, err := nestedValid(v.platform, mountA.Destination, mountB.Destination) |
| 428 | + if err != nil { |
| 429 | + msgs = append(msgs, err.Error()) |
| 430 | + continue |
| 431 | + } |
| 432 | + if nested { |
| 433 | + if v.platform == "windows" && i < j { |
| 434 | + msgs = append(msgs, fmt.Sprintf("On Windows, %v nested within %v is forbidden", mountB.Destination, mountA.Destination)) |
| 435 | + } |
| 436 | + if i > j { |
| 437 | + logrus.Warnf("%v will be covered by %v", mountB.Destination, mountA.Destination) |
| 438 | + } |
| 439 | + } |
414 | 440 | } |
415 | 441 | } |
416 | 442 |
|
@@ -795,6 +821,65 @@ func namespaceValid(ns rspec.LinuxNamespace) bool { |
795 | 821 | return true |
796 | 822 | } |
797 | 823 |
|
| 824 | +func pathValid(os, path string) error { |
| 825 | + if os == "windows" { |
| 826 | + matched, err := regexp.MatchString("^[a-zA-Z]:(\\\\[^\\\\/<>|:*?\"]+)+$", path) |
| 827 | + if err != nil { |
| 828 | + return err |
| 829 | + } |
| 830 | + if !matched { |
| 831 | + return fmt.Errorf("invalid windows path %v", path) |
| 832 | + } |
| 833 | + return nil |
| 834 | + } |
| 835 | + if !filepath.IsAbs(path) { |
| 836 | + return fmt.Errorf("%v is not an absolute path", path) |
| 837 | + } |
| 838 | + return nil |
| 839 | +} |
| 840 | + |
| 841 | +// Check whether pathB is nested whithin pathA |
| 842 | +func nestedValid(os, pathA, pathB string) (bool, error) { |
| 843 | + if pathA == pathB { |
| 844 | + return false, nil |
| 845 | + } |
| 846 | + if pathA == "/" && pathB != "" { |
| 847 | + return true, nil |
| 848 | + } |
| 849 | + |
| 850 | + var sep string |
| 851 | + if os == "windows" { |
| 852 | + sep = "\\" |
| 853 | + } else { |
| 854 | + sep = "/" |
| 855 | + } |
| 856 | + |
| 857 | + splitedPathA := strings.Split(filepath.Clean(pathA), sep) |
| 858 | + splitedPathB := strings.Split(filepath.Clean(pathB), sep) |
| 859 | + lenA := len(splitedPathA) |
| 860 | + lenB := len(splitedPathB) |
| 861 | + |
| 862 | + if lenA > lenB { |
| 863 | + if (lenA - lenB) == 1 { |
| 864 | + // if pathA is longer but not end with separator |
| 865 | + if splitedPathA[lenA-1] != "" { |
| 866 | + return false, nil |
| 867 | + } |
| 868 | + splitedPathA = splitedPathA[:lenA-1] |
| 869 | + } else { |
| 870 | + return false, nil |
| 871 | + } |
| 872 | + } |
| 873 | + |
| 874 | + for i, partA := range splitedPathA { |
| 875 | + if partA != splitedPathB[i] { |
| 876 | + return false, nil |
| 877 | + } |
| 878 | + } |
| 879 | + |
| 880 | + return true, nil |
| 881 | +} |
| 882 | + |
798 | 883 | func deviceValid(d rspec.LinuxDevice) bool { |
799 | 884 | switch d.Type { |
800 | 885 | case "b", "c", "u": |
|
0 commit comments