entry: rework e.Path() handling for casync-mtree
The core issue comes about when you consider a trivial example of a path like "./README". This path is lexically equivalent within mtree to "README", but a simple string comparison will yield the wrong result. Instead you need to lexically clean the path first (filepath.Clean isn't enough here -- you need to prepend a "/" and then do filepath.Clean). In gomtree we generate spec files in the same style of FreeBSD's mtree(8), so you would be very hard-pressed to find an example of such an inconsistency. However casync's mtree implementation does not generate leading "./" for root paths which results in "missing" entries. The implementation of CleanPath was written by me for umoci originally, then later I copied it to runc for other uses, and now I've copied it here. Since I'm the sole author I'm effectively dual-licensing it under this project's license to avoid having to relicense go-mtree for no good reason (or deal with the multiple-license hassle). Signed-off-by: Aleksa Sarai <asarai@suse.de>
This commit is contained in:
parent
37d776ac40
commit
4766cebac0
2 changed files with 37 additions and 4 deletions
|
@ -350,7 +350,6 @@ func Compare(oldDh, newDh *DirectoryHierarchy, keys []Keyword) ([]InodeDelta, er
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
//fmt.Printf("new: %q\n", path)
|
|
||||||
|
|
||||||
// Cannot take &kv because it's the iterator.
|
// Cannot take &kv because it's the iterator.
|
||||||
cEntry := new(Entry)
|
cEntry := new(Entry)
|
||||||
|
@ -373,7 +372,6 @@ func Compare(oldDh, newDh *DirectoryHierarchy, keys []Keyword) ([]InodeDelta, er
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
//fmt.Printf("old: %q\n", path)
|
|
||||||
|
|
||||||
// Cannot take &kv because it's the iterator.
|
// Cannot take &kv because it's the iterator.
|
||||||
cEntry := new(Entry)
|
cEntry := new(Entry)
|
||||||
|
|
39
entry.go
39
entry.go
|
@ -2,6 +2,7 @@ package mtree
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
@ -67,6 +68,39 @@ func (e Entry) Ascend() *Entry {
|
||||||
return e.Parent
|
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 "../../../../<etc>/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
|
// Path provides the full path of the file, despite RelativeType or FullType. It
|
||||||
// will be in Unvis'd form.
|
// will be in Unvis'd form.
|
||||||
func (e Entry) Path() (string, error) {
|
func (e Entry) Path() (string, error) {
|
||||||
|
@ -74,14 +108,15 @@ func (e Entry) Path() (string, error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
decodedName = CleanPath(decodedName)
|
||||||
if e.Parent == nil || e.Type == FullType {
|
if e.Parent == nil || e.Type == FullType {
|
||||||
return filepath.Clean(decodedName), nil
|
return decodedName, nil
|
||||||
}
|
}
|
||||||
parentName, err := e.Parent.Path()
|
parentName, err := e.Parent.Path()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
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
|
// String joins a file with its associated keywords. The file name will be the
|
||||||
|
|
Loading…
Reference in a new issue