diff options
Diffstat (limited to 'pkg/sentry/fs/path.go')
-rw-r--r-- | pkg/sentry/fs/path.go | 119 |
1 files changed, 119 insertions, 0 deletions
diff --git a/pkg/sentry/fs/path.go b/pkg/sentry/fs/path.go new file mode 100644 index 000000000..e4dc02dbb --- /dev/null +++ b/pkg/sentry/fs/path.go @@ -0,0 +1,119 @@ +// Copyright 2018 The gVisor Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package fs + +import ( + "path/filepath" + "strings" +) + +// TrimTrailingSlashes trims any trailing slashes. +// +// The returned boolean indicates whether any changes were made. +// +//go:nosplit +func TrimTrailingSlashes(dir string) (trimmed string, changed bool) { + // Trim the trailing slash, except for root. + for len(dir) > 1 && dir[len(dir)-1] == '/' { + dir = dir[:len(dir)-1] + changed = true + } + return dir, changed +} + +// SplitLast splits the given path into a directory and a file. +// +// The "absoluteness" of the path is preserved, but dir is always stripped of +// trailing slashes. +// +//go:nosplit +func SplitLast(path string) (dir, file string) { + path, _ = TrimTrailingSlashes(path) + if path == "" { + return ".", "." + } else if path == "/" { + return "/", "." + } + + var slash int // Last location of slash in path. + for slash = len(path) - 1; slash >= 0 && path[slash] != '/'; slash-- { + } + switch { + case slash < 0: + return ".", path + case slash == 0: + // Directory of the form "/foo", or just "/". We need to + // preserve the first slash here, since it indicates an + // absolute path. + return "/", path[1:] + default: + // Drop the trailing slash. + dir, _ = TrimTrailingSlashes(path[:slash]) + return dir, path[slash+1:] + } +} + +// SplitFirst splits the given path into a first directory and the remainder. +// +// If remainder is empty, then the path is a single element. +// +//go:nosplit +func SplitFirst(path string) (current, remainder string) { + path, _ = TrimTrailingSlashes(path) + if path == "" { + return ".", "" + } + + var slash int // First location of slash in path. + for slash = 0; slash < len(path) && path[slash] != '/'; slash++ { + } + switch { + case slash >= len(path): + return path, "" + case slash == 0: + // See above. + return "/", path[1:] + default: + current = path[:slash] + remainder = path[slash+1:] + // Strip redundant slashes. + for len(remainder) > 0 && remainder[0] == '/' { + remainder = remainder[1:] + } + 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 +} |