diff --git a/config/v3_0/types/config.go b/config/v3_0/types/config.go index 1ac295948c..b6be12aa70 100644 --- a/config/v3_0/types/config.go +++ b/config/v3_0/types/config.go @@ -15,10 +15,13 @@ package types import ( - "github.com/coreos/ignition/v2/config/shared/errors" - "github.com/coreos/ignition/v2/config/util" + "path/filepath" + "sort" + "strings" "github.com/coreos/go-semver/semver" + "github.com/coreos/ignition/v2/config/shared/errors" + "github.com/coreos/ignition/v2/config/util" "github.com/coreos/vcontext/path" "github.com/coreos/vcontext/report" ) @@ -30,6 +33,11 @@ var ( } ) +type pathEntry struct { + Path string + Type string +} + func (cfg Config) Validate(c path.ContextPath) (r report.Report) { systemdPath := "/etc/systemd/system/" unitPaths := map[string]struct{}{} @@ -60,5 +68,69 @@ func (cfg Config) Validate(c path.ContextPath) (r report.Report) { r.AddOnError(c.Append("storage", "links", i, "path"), errors.ErrPathConflictsSystemd) } } - return + + entries, r := cfg.validateParents(c) + for i, entry := range entries { + if i > 0 && isWithin(entry.Path, entries[i-1].Path) { + if entries[i-1].Type == "directory" { + r.AddOnError(c.Append("storage", "directories", i, "path"), errors.ErrPathConflictsParentDir) + return r + } + } + } + + return r +} + +func (cfg Config) validateParents(c path.ContextPath) ([]pathEntry, report.Report) { + paths := map[string]struct{}{} + entries := []pathEntry{} + r := report.Report{} + + for i, f := range cfg.Storage.Files { + if _, exists := paths[f.Path]; exists { + r.AddOnError(c.Append("storage", "files", i, "path"), errors.ErrPathConflictsParentDir) //TODO: should add different error? + return nil, r + } + paths[f.Path] = struct{}{} + entries = append(entries, pathEntry{Path: f.Path, Type: "file"}) + } + + for i, d := range cfg.Storage.Directories { + if _, exists := paths[d.Path]; exists { + r.AddOnError(c.Append("storage", "directories", i, "path"), errors.ErrPathConflictsParentDir) //TODO: should add different error? + return nil, r + } + paths[d.Path] = struct{}{} + entries = append(entries, pathEntry{Path: d.Path, Type: "directory"}) + } + + for i, l := range cfg.Storage.Links { + if _, exists := paths[l.Path]; exists { + r.AddOnError(c.Append("storage", "links", i, "path"), errors.ErrPathConflictsParentDir) //TODO: should add different error? + return nil, r + } + paths[l.Path] = struct{}{} + entries = append(entries, pathEntry{Path: l.Path, Type: "link"}) + } + + sort.Slice(entries, func(i, j int) bool { + return depth(entries[i].Path) < depth(entries[j].Path) + }) + + return entries, r +} + +// check the depth +func depth(path string) uint { + var count uint + for p := filepath.Clean(path); p != "/" && p != "."; count++ { + p = filepath.Dir(p) + } + return count +} + +// isWithin checks if newPath is within prevPath. +func isWithin(newPath, prevPath string) bool { + return strings.HasPrefix(newPath, prevPath) && newPath != prevPath }