diff --git a/compare.go b/compare.go index 458df85..625a792 100644 --- a/compare.go +++ b/compare.go @@ -350,7 +350,6 @@ func Compare(oldDh, newDh *DirectoryHierarchy, keys []Keyword) ([]InodeDelta, er if err != nil { return nil, err } - //fmt.Printf("new: %q\n", path) // Cannot take &kv because it's the iterator. cEntry := new(Entry) @@ -373,7 +372,6 @@ func Compare(oldDh, newDh *DirectoryHierarchy, keys []Keyword) ([]InodeDelta, er if err != nil { return nil, err } - //fmt.Printf("old: %q\n", path) // Cannot take &kv because it's the iterator. cEntry := new(Entry) diff --git a/entry.go b/entry.go index fc8c1c9..366a15b 100644 --- a/entry.go +++ b/entry.go @@ -2,6 +2,7 @@ package mtree import ( "fmt" + "os" "path/filepath" "strings" @@ -67,6 +68,39 @@ func (e Entry) Ascend() *Entry { return e.Parent } +// CleanPath makes a path safe for use with filepath.Join. This is done by not +// only cleaning the path, but also (if the path is relative) adding a leading +// '/' and cleaning it (then removing the leading '/'). This ensures that a +// path resulting from prepending another path will always resolve to lexically +// be a subdirectory of the prefixed path. This is all done lexically, so paths +// that include symlinks won't be safe as a result of using CleanPath. +// +// This code was copied from runc/libcontainer/utils/utils.go. It was +// originally written by myself, so I am dual-licensing it for the purpose of +// this project. +func CleanPath(path string) string { + // Deal with empty strings nicely. + if path == "" { + return "" + } + + // Ensure that all paths are cleaned (especially problematic ones like + // "/../../../../../" which can cause lots of issues). + path = filepath.Clean(path) + + // If the path isn't absolute, we need to do more processing to fix paths + // such as "../../../..//some/path". We also shouldn't convert absolute + // paths to relative ones. + if !filepath.IsAbs(path) { + path = filepath.Clean(string(os.PathSeparator) + path) + // This can't fail, as (by definition) all paths are relative to root. + path, _ = filepath.Rel(string(os.PathSeparator), path) + } + + // Clean the path again for good measure. + return filepath.Clean(path) +} + // Path provides the full path of the file, despite RelativeType or FullType. It // will be in Unvis'd form. func (e Entry) Path() (string, error) { @@ -74,14 +108,15 @@ func (e Entry) Path() (string, error) { if err != nil { return "", err } + decodedName = CleanPath(decodedName) if e.Parent == nil || e.Type == FullType { - return filepath.Clean(decodedName), nil + return decodedName, nil } parentName, err := e.Parent.Path() if err != nil { return "", err } - return filepath.Clean(filepath.Join(parentName, decodedName)), nil + return CleanPath(filepath.Join(parentName, decodedName)), nil } // String joins a file with its associated keywords. The file name will be the