diff --git a/internal/fsutil/path.go b/internal/fsutil/path.go new file mode 100644 index 00000000..15646e5e --- /dev/null +++ b/internal/fsutil/path.go @@ -0,0 +1,47 @@ +package fsutil + +import ( + "path/filepath" + "strings" +) + +// These functions exists because we work with slash terminated directory paths +// that come from deb package tarballs but standard library path functions +// treat slash terminated paths as unclean. + +// Dir is like filepath.Dir() but trailing slash on input is ignored and the +// return value always includes trailing slash. +// +// Comparison of filepath.Dir() with fsutil.Dir(): +// +// filepath.Dir("/foo/bar/") == "/foo/bar" +// filepath.Dir("/foo/bar") == "/foo" +// +// fsutil.Dir("/foo/bar") == "/foo/" +// fsutil.Dir("/foo/bar/") == "/foo/" +func Dir(path string) string { + parent := filepath.Dir(filepath.Clean(path)) + if parent != "/" { + parent += "/" + } + return parent +} + +// Clean is like filepath.Clean() but trailing slash is kept. +// +// Comparison of filepath.Clean() with fsutil.Clean(): +// +// filepath.Clean("/foo/bar") == "/foo/bar" +// filepath.Clean("/foo/bar/") == "/foo/bar" +// filepath.Clean("/foo/bar/.//) == "/foo/bar" +// +// fsutil.Clean("/foo/bar") == "/foo/bar" +// fsutil.Clean("/foo/bar/") == "/foo/bar/" +// fsutil.Clean("/foo/bar/.//") == "/foo/bar/" +func Clean(path string) string { + clean := filepath.Clean(path) + if strings.HasSuffix(path, "/") { + clean += "/" + } + return clean +} diff --git a/internal/fsutil/path_test.go b/internal/fsutil/path_test.go new file mode 100644 index 00000000..9d39230c --- /dev/null +++ b/internal/fsutil/path_test.go @@ -0,0 +1,52 @@ +package fsutil_test + +import ( + . "gopkg.in/check.v1" + + "github.com/canonical/chisel/internal/fsutil" +) + +var dirTests = []struct { + input string + output string +}{ + {"/a/b/c", "/a/b/"}, + {"/a/b/c/", "/a/b/"}, + {"/a/b/c//", "/a/b/"}, + {"/a/b/c/././", "/a/b/"}, + {"/a/b/c/.././", "/a/"}, + {"/a/b//c", "/a/b/"}, + {"/a/b/./c", "/a/b/"}, + {"a/b/./c", "a/b/"}, + {"./a/b/./c", "a/b/"}, + {"/", "/"}, + {"///.///", "/"}, + {".", "./"}, + {"", "./"}, +} + +var cleanTests = []struct { + input string + output string +}{ + {"/a/b/c", "/a/b/c"}, + {"/a/b/c/", "/a/b/c/"}, + {"/a/b/c/.//", "/a/b/c/"}, + {"/a/b//./c", "/a/b/c"}, + {"/a/b//./c/", "/a/b/c/"}, + {"/a/b/.././c/", "/a/c/"}, +} + +func (s *S) TestDir(c *C) { + for _, t := range dirTests { + c.Logf("%s => %s", t.input, t.output) + c.Assert(fsutil.Dir(t.input), Equals, t.output) + } +} + +func (s *S) TestClean(c *C) { + for _, t := range cleanTests { + c.Logf("%s => %s", t.input, t.output) + c.Assert(fsutil.Clean(t.input), Equals, t.output) + } +} diff --git a/internal/slicer/slicer.go b/internal/slicer/slicer.go index 2b684b5d..ea6b0b86 100644 --- a/internal/slicer/slicer.go +++ b/internal/slicer/slicer.go @@ -32,25 +32,21 @@ func Run(options *RunOptions) error { knownPaths["/"] = true + // addKnownPath path adds path and all its directory parent paths into + // knownPaths set. addKnownPath := func(path string) { if path[0] != '/' { panic("bug: tried to add relative path to known paths") } - cleanPath := filepath.Clean(path) - slashPath := cleanPath - if path[len(path)-1] == '/' && cleanPath != "/" { - slashPath += "/" - } + path = fsutil.Clean(path) for { - if _, ok := knownPaths[slashPath]; ok { + if _, ok := knownPaths[path]; ok { break } - knownPaths[slashPath] = true - cleanPath = filepath.Dir(cleanPath) - if cleanPath == "/" { + knownPaths[path] = true + if path = fsutil.Dir(path); path == "/" { break } - slashPath = cleanPath + "/" } } @@ -113,7 +109,7 @@ func Run(options *RunOptions) error { hasCopyright = true } } else { - targetDir := filepath.Dir(strings.TrimRight(targetPath, "/")) + "/" + targetDir := fsutil.Dir(targetPath) if targetDir == "" || targetDir == "/" { continue }