Skip to content

Commit

Permalink
fsutil: Add Dir() and Clean() functions
Browse files Browse the repository at this point in the history
From the code:

  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.

We use ad-hoc code to make filepath.Dir() slash terminated in two places
right now. For example this code

  targetDir := filepath.Dir(strings.TrimRight(targetPath, "/")) + "/"

is not strictly correct as "/a/b/.///" will be turned into "/a/b/./"
which is still "/a/b".

Since we deal with slash terminated directory paths everywhere, create
and use dedicated helper functions to process them.
  • Loading branch information
woky committed Sep 19, 2023
1 parent ddd61ef commit 7c281e9
Show file tree
Hide file tree
Showing 3 changed files with 106 additions and 11 deletions.
47 changes: 47 additions & 0 deletions internal/fsutil/path.go
Original file line number Diff line number Diff line change
@@ -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
}
52 changes: 52 additions & 0 deletions internal/fsutil/path_test.go
Original file line number Diff line number Diff line change
@@ -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)
}
}
18 changes: 7 additions & 11 deletions internal/slicer/slicer.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 + "/"
}
}

Expand Down Expand Up @@ -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
}
Expand Down

0 comments on commit 7c281e9

Please sign in to comment.