diff options
Diffstat (limited to 'pkg/sentry/fs')
-rw-r--r-- | pkg/sentry/fs/path.go | 27 | ||||
-rw-r--r-- | pkg/sentry/fs/path_test.go | 78 |
2 files changed, 105 insertions, 0 deletions
diff --git a/pkg/sentry/fs/path.go b/pkg/sentry/fs/path.go index 91a9a8ffd..52139b648 100644 --- a/pkg/sentry/fs/path.go +++ b/pkg/sentry/fs/path.go @@ -14,6 +14,11 @@ package fs +import ( + "path/filepath" + "strings" +) + // TrimTrailingSlashes trims any trailing slashes. // // The returned boolean indicates whether any changes were made. @@ -90,3 +95,25 @@ func SplitFirst(path string) (current, remainder string) { return current, remainder } } + +// IsSubpath checks whether the first path is a (strict) descendent of the +// second. If it is a subpath, then true is returned along with a clean +// relative path from the second path to the first. Otherwise false is +// returned. +func IsSubpath(subpath, path string) (string, bool) { + cleanPath := filepath.Clean(path) + cleanSubpath := filepath.Clean(subpath) + + // Add a trailing slash to the path if it does not already have one. + if len(cleanPath) == 0 || cleanPath[len(cleanPath)-1] != '/' { + cleanPath += "/" + } + if cleanPath == cleanSubpath { + // Paths are equal, thus not a strict subpath. + return "", false + } + if strings.HasPrefix(cleanSubpath, cleanPath) { + return strings.TrimPrefix(cleanSubpath, cleanPath), true + } + return "", false +} diff --git a/pkg/sentry/fs/path_test.go b/pkg/sentry/fs/path_test.go index 391b010a7..4ba1498f6 100644 --- a/pkg/sentry/fs/path_test.go +++ b/pkg/sentry/fs/path_test.go @@ -209,3 +209,81 @@ func TestSplitFirst(t *testing.T) { } } } + +// TestIsSubpath tests the IsSubpath method. +func TestIsSubpath(t *testing.T) { + tcs := []struct { + // Two absolute paths. + pathA string + pathB string + + // Whether pathA is a subpath of pathB. + wantIsSubpath bool + + // Relative path from pathA to pathB. Only checked if + // wantIsSubpath is true. + wantRelpath string + }{ + { + pathA: "/foo/bar/baz", + pathB: "/foo", + wantIsSubpath: true, + wantRelpath: "bar/baz", + }, + { + pathA: "/foo", + pathB: "/foo/bar/baz", + wantIsSubpath: false, + }, + { + pathA: "/foo", + pathB: "/foo", + wantIsSubpath: false, + }, + { + pathA: "/foobar", + pathB: "/foo", + wantIsSubpath: false, + }, + { + pathA: "/foo", + pathB: "/foobar", + wantIsSubpath: false, + }, + { + pathA: "/foo", + pathB: "/foobar", + wantIsSubpath: false, + }, + { + pathA: "/", + pathB: "/foo", + wantIsSubpath: false, + }, + { + pathA: "/foo", + pathB: "/", + wantIsSubpath: true, + wantRelpath: "foo", + }, + { + pathA: "/foo/bar/../bar", + pathB: "/foo", + wantIsSubpath: true, + wantRelpath: "bar", + }, + { + pathA: "/foo/bar", + pathB: "/foo/../foo", + wantIsSubpath: true, + wantRelpath: "bar", + }, + } + + for _, tc := range tcs { + gotRelpath, gotIsSubpath := IsSubpath(tc.pathA, tc.pathB) + if gotRelpath != tc.wantRelpath || gotIsSubpath != tc.wantIsSubpath { + t.Errorf("IsSubpath(%q, %q) got %q %t, want %q %t", tc.pathA, tc.pathB, gotRelpath, gotIsSubpath, tc.wantRelpath, tc.wantIsSubpath) + } + } +} |