summaryrefslogtreecommitdiffhomepage
path: root/pkg/sentry/vfs/resolving_path.go
diff options
context:
space:
mode:
Diffstat (limited to 'pkg/sentry/vfs/resolving_path.go')
-rw-r--r--pkg/sentry/vfs/resolving_path.go453
1 files changed, 0 insertions, 453 deletions
diff --git a/pkg/sentry/vfs/resolving_path.go b/pkg/sentry/vfs/resolving_path.go
deleted file mode 100644
index 8d05c8583..000000000
--- a/pkg/sentry/vfs/resolving_path.go
+++ /dev/null
@@ -1,453 +0,0 @@
-// Copyright 2019 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 vfs
-
-import (
- "fmt"
- "sync"
-
- "gvisor.dev/gvisor/pkg/abi/linux"
- "gvisor.dev/gvisor/pkg/fspath"
- "gvisor.dev/gvisor/pkg/sentry/kernel/auth"
- "gvisor.dev/gvisor/pkg/syserror"
-)
-
-// ResolvingPath represents the state of an in-progress path resolution, shared
-// between VFS and FilesystemImpl methods that take a path.
-//
-// From the perspective of FilesystemImpl methods, a ResolvingPath represents a
-// starting Dentry on the associated Filesystem (on which a reference is
-// already held) and a stream of path components relative to that Dentry.
-//
-// ResolvingPath is loosely analogous to Linux's struct nameidata.
-type ResolvingPath struct {
- vfs *VirtualFilesystem
- root VirtualDentry // refs borrowed from PathOperation
- mount *Mount
- start *Dentry
- pit fspath.Iterator
-
- flags uint16
- mustBeDir bool // final file must be a directory?
- mustBeDirOrig bool
- symlinks uint8 // number of symlinks traversed
- symlinksOrig uint8
- curPart uint8 // index into parts
- numOrigParts uint8
-
- creds *auth.Credentials
-
- // Data associated with resolve*Errors, stored in ResolvingPath so that
- // those errors don't need to allocate.
- nextMount *Mount // ref held if not nil
- nextStart *Dentry // ref held if not nil
- absSymlinkTarget fspath.Path
-
- // ResolvingPath must track up to two relative paths: the "current"
- // relative path, which is updated whenever a relative symlink is
- // encountered, and the "original" relative path, which is updated from the
- // current relative path by handleError() when resolution must change
- // filesystems (due to reaching a mount boundary or absolute symlink) and
- // overwrites the current relative path when Restart() is called.
- parts [1 + linux.MaxSymlinkTraversals]fspath.Iterator
- origParts [1 + linux.MaxSymlinkTraversals]fspath.Iterator
-}
-
-const (
- rpflagsHaveMountRef = 1 << iota // do we hold a reference on mount?
- rpflagsHaveStartRef // do we hold a reference on start?
- rpflagsFollowFinalSymlink // same as PathOperation.FollowFinalSymlink
-)
-
-func init() {
- if maxParts := len(ResolvingPath{}.parts); maxParts > 255 {
- panic(fmt.Sprintf("uint8 is insufficient to accommodate len(ResolvingPath.parts) (%d)", maxParts))
- }
-}
-
-// Error types that communicate state from the FilesystemImpl-caller,
-// VFS-callee side of path resolution (i.e. errors returned by
-// ResolvingPath.Resolve*()) to the VFS-caller, FilesystemImpl-callee side
-// (i.e. VFS methods => ResolvingPath.handleError()). These are empty structs
-// rather than error values because Go doesn't support non-primitive constants,
-// so error "constants" are really mutable vars, necessitating somewhat
-// expensive interface object comparisons.
-
-type resolveMountRootError struct{}
-
-// Error implements error.Error.
-func (resolveMountRootError) Error() string {
- return "resolving mount root"
-}
-
-type resolveMountPointError struct{}
-
-// Error implements error.Error.
-func (resolveMountPointError) Error() string {
- return "resolving mount point"
-}
-
-type resolveAbsSymlinkError struct{}
-
-// Error implements error.Error.
-func (resolveAbsSymlinkError) Error() string {
- return "resolving absolute symlink"
-}
-
-var resolvingPathPool = sync.Pool{
- New: func() interface{} {
- return &ResolvingPath{}
- },
-}
-
-func (vfs *VirtualFilesystem) getResolvingPath(creds *auth.Credentials, pop *PathOperation) (*ResolvingPath, error) {
- path, err := fspath.Parse(pop.Pathname)
- if err != nil {
- return nil, err
- }
- rp := resolvingPathPool.Get().(*ResolvingPath)
- rp.vfs = vfs
- rp.root = pop.Root
- rp.mount = pop.Start.mount
- rp.start = pop.Start.dentry
- rp.pit = path.Begin
- rp.flags = 0
- if pop.FollowFinalSymlink {
- rp.flags |= rpflagsFollowFinalSymlink
- }
- rp.mustBeDir = path.Dir
- rp.mustBeDirOrig = path.Dir
- rp.symlinks = 0
- rp.curPart = 0
- rp.numOrigParts = 1
- rp.creds = creds
- rp.parts[0] = path.Begin
- rp.origParts[0] = path.Begin
- return rp, nil
-}
-
-func (vfs *VirtualFilesystem) putResolvingPath(rp *ResolvingPath) {
- rp.root = VirtualDentry{}
- rp.decRefStartAndMount()
- rp.mount = nil
- rp.start = nil
- rp.releaseErrorState()
- resolvingPathPool.Put(rp)
-}
-
-func (rp *ResolvingPath) decRefStartAndMount() {
- if rp.flags&rpflagsHaveStartRef != 0 {
- rp.start.decRef(rp.mount.fs)
- }
- if rp.flags&rpflagsHaveMountRef != 0 {
- rp.mount.decRef()
- }
-}
-
-func (rp *ResolvingPath) releaseErrorState() {
- if rp.nextStart != nil {
- rp.nextStart.decRef(rp.nextMount.fs)
- rp.nextStart = nil
- }
- if rp.nextMount != nil {
- rp.nextMount.decRef()
- rp.nextMount = nil
- }
-}
-
-// VirtualFilesystem returns the containing VirtualFilesystem.
-func (rp *ResolvingPath) VirtualFilesystem() *VirtualFilesystem {
- return rp.vfs
-}
-
-// Credentials returns the credentials of rp's provider.
-func (rp *ResolvingPath) Credentials() *auth.Credentials {
- return rp.creds
-}
-
-// Mount returns the Mount on which path resolution is currently occurring. It
-// does not take a reference on the returned Mount.
-func (rp *ResolvingPath) Mount() *Mount {
- return rp.mount
-}
-
-// Start returns the starting Dentry represented by rp. It does not take a
-// reference on the returned Dentry.
-func (rp *ResolvingPath) Start() *Dentry {
- return rp.start
-}
-
-// Done returns true if there are no remaining path components in the stream
-// represented by rp.
-func (rp *ResolvingPath) Done() bool {
- // We don't need to check for rp.curPart == 0 because rp.Advance() won't
- // set rp.pit to a terminal iterator otherwise.
- return !rp.pit.Ok()
-}
-
-// Final returns true if there is exactly one remaining path component in the
-// stream represented by rp.
-//
-// Preconditions: !rp.Done().
-func (rp *ResolvingPath) Final() bool {
- return rp.curPart == 0 && !rp.pit.NextOk()
-}
-
-// Component returns the current path component in the stream represented by
-// rp.
-//
-// Preconditions: !rp.Done().
-func (rp *ResolvingPath) Component() string {
- if checkInvariants {
- if !rp.pit.Ok() {
- panic("ResolvingPath.Component() called at end of relative path")
- }
- }
- return rp.pit.String()
-}
-
-// Advance advances the stream of path components represented by rp.
-//
-// Preconditions: !rp.Done().
-func (rp *ResolvingPath) Advance() {
- if checkInvariants {
- if !rp.pit.Ok() {
- panic("ResolvingPath.Advance() called at end of relative path")
- }
- }
- next := rp.pit.Next()
- if next.Ok() || rp.curPart == 0 { // have next component, or at end of path
- rp.pit = next
- } else { // at end of path segment, continue with next one
- rp.curPart--
- rp.pit = rp.parts[rp.curPart-1]
- }
-}
-
-// Restart resets the stream of path components represented by rp to its state
-// on entry to the current FilesystemImpl method.
-func (rp *ResolvingPath) Restart() {
- rp.pit = rp.origParts[rp.numOrigParts-1]
- rp.mustBeDir = rp.mustBeDirOrig
- rp.symlinks = rp.symlinksOrig
- rp.curPart = rp.numOrigParts - 1
- copy(rp.parts[:], rp.origParts[:rp.numOrigParts])
- rp.releaseErrorState()
-}
-
-func (rp *ResolvingPath) relpathCommit() {
- rp.mustBeDirOrig = rp.mustBeDir
- rp.symlinksOrig = rp.symlinks
- rp.numOrigParts = rp.curPart + 1
- copy(rp.origParts[:rp.curPart], rp.parts[:])
- rp.origParts[rp.curPart] = rp.pit
-}
-
-// ResolveParent returns the VFS parent of d. It does not take a reference on
-// the returned Dentry.
-//
-// Preconditions: There are no concurrent mutators of d.
-//
-// Postconditions: If the returned error is nil, then the returned Dentry is
-// not nil.
-func (rp *ResolvingPath) ResolveParent(d *Dentry) (*Dentry, error) {
- var parent *Dentry
- if d == rp.root.dentry && rp.mount == rp.root.mount {
- // At contextual VFS root.
- parent = d
- } else if d == rp.mount.root {
- // At mount root ...
- mnt, mntpt := rp.vfs.getMountpointAt(rp.mount, rp.root)
- if mnt != nil {
- // ... of non-root mount.
- rp.nextMount = mnt
- rp.nextStart = mntpt
- return nil, resolveMountRootError{}
- }
- // ... of root mount.
- parent = d
- } else if d.parent == nil {
- // At filesystem root.
- parent = d
- } else {
- parent = d.parent
- }
- if parent.isMounted() {
- if mnt := rp.vfs.getMountAt(rp.mount, parent); mnt != nil {
- rp.nextMount = mnt
- return nil, resolveMountPointError{}
- }
- }
- return parent, nil
-}
-
-// ResolveChild returns the VFS child of d with the given name. It does not
-// take a reference on the returned Dentry. If no such child exists,
-// ResolveChild returns (nil, nil).
-//
-// Preconditions: There are no concurrent mutators of d.
-func (rp *ResolvingPath) ResolveChild(d *Dentry, name string) (*Dentry, error) {
- child := d.children[name]
- if child == nil {
- return nil, nil
- }
- if child.isMounted() {
- if mnt := rp.vfs.getMountAt(rp.mount, child); mnt != nil {
- rp.nextMount = mnt
- return nil, resolveMountPointError{}
- }
- }
- return child, nil
-}
-
-// ResolveComponent returns the Dentry reached by starting at d and resolving
-// the current path component in the stream represented by rp. It does not
-// advance the stream. It does not take a reference on the returned Dentry. If
-// no such Dentry exists, ResolveComponent returns (nil, nil).
-//
-// Preconditions: !rp.Done(). There are no concurrent mutators of d.
-func (rp *ResolvingPath) ResolveComponent(d *Dentry) (*Dentry, error) {
- switch pc := rp.Component(); pc {
- case ".":
- return d, nil
- case "..":
- return rp.ResolveParent(d)
- default:
- return rp.ResolveChild(d, pc)
- }
-}
-
-// ShouldFollowSymlink returns true if, supposing that the current path
-// component in pcs represents a symbolic link, the symbolic link should be
-// followed.
-//
-// Preconditions: !rp.Done().
-func (rp *ResolvingPath) ShouldFollowSymlink() bool {
- // Non-final symlinks are always followed.
- return rp.flags&rpflagsFollowFinalSymlink != 0 || !rp.Final()
-}
-
-// HandleSymlink is called when the current path component is a symbolic link
-// to the given target. If the calling Filesystem method should continue path
-// traversal, HandleSymlink updates the path component stream to reflect the
-// symlink target and returns nil. Otherwise it returns a non-nil error.
-//
-// Preconditions: !rp.Done().
-func (rp *ResolvingPath) HandleSymlink(target string) error {
- if rp.symlinks >= linux.MaxSymlinkTraversals {
- return syserror.ELOOP
- }
- targetPath, err := fspath.Parse(target)
- if err != nil {
- return err
- }
- rp.symlinks++
- if targetPath.Absolute {
- rp.absSymlinkTarget = targetPath
- return resolveAbsSymlinkError{}
- }
- if !targetPath.Begin.Ok() {
- panic(fmt.Sprintf("symbolic link has non-empty target %q that is both relative and has no path components?", target))
- }
- // Consume the path component that represented the symlink.
- rp.Advance()
- // Prepend the symlink target to the relative path.
- rp.relpathPrepend(targetPath)
- return nil
-}
-
-func (rp *ResolvingPath) relpathPrepend(path fspath.Path) {
- if rp.pit.Ok() {
- rp.parts[rp.curPart] = rp.pit
- rp.pit = path.Begin
- rp.curPart++
- } else {
- // The symlink was the final path component, so now the symlink target
- // is the whole path.
- rp.pit = path.Begin
- // Symlink targets can set rp.mustBeDir (if they end in a trailing /),
- // but can't unset it.
- if path.Dir {
- rp.mustBeDir = true
- }
- }
-}
-
-func (rp *ResolvingPath) handleError(err error) bool {
- switch err.(type) {
- case resolveMountRootError:
- // Switch to the new Mount. We hold references on the Mount and Dentry
- // (from VFS.getMountpointAt()).
- rp.decRefStartAndMount()
- rp.mount = rp.nextMount
- rp.start = rp.nextStart
- rp.flags |= rpflagsHaveMountRef | rpflagsHaveStartRef
- rp.nextMount = nil
- rp.nextStart = nil
- // Commit the previous FileystemImpl's progress through the relative
- // path. (Don't consume the path component that caused us to traverse
- // through the mount root - i.e. the ".." - because we still need to
- // resolve the mount point's parent in the new FilesystemImpl.)
- rp.relpathCommit()
- // Restart path resolution on the new Mount. Don't bother calling
- // rp.releaseErrorState() since we already set nextMount and nextStart
- // to nil above.
- return true
-
- case resolveMountPointError:
- // Switch to the new Mount. We hold a reference on the Mount (from
- // VFS.getMountAt()), but borrow the reference on the mount root from
- // the Mount.
- rp.decRefStartAndMount()
- rp.mount = rp.nextMount
- rp.start = rp.nextMount.root
- rp.flags = rp.flags&^rpflagsHaveStartRef | rpflagsHaveMountRef
- rp.nextMount = nil
- // Consume the path component that represented the mount point.
- rp.Advance()
- // Commit the previous FilesystemImpl's progress through the relative
- // path.
- rp.relpathCommit()
- // Restart path resolution on the new Mount.
- rp.releaseErrorState()
- return true
-
- case resolveAbsSymlinkError:
- // Switch to the new Mount. References are borrowed from rp.root.
- rp.decRefStartAndMount()
- rp.mount = rp.root.mount
- rp.start = rp.root.dentry
- rp.flags &^= rpflagsHaveMountRef | rpflagsHaveStartRef
- // Consume the path component that represented the symlink.
- rp.Advance()
- // Prepend the symlink target to the relative path.
- rp.relpathPrepend(rp.absSymlinkTarget)
- // Commit the previous FilesystemImpl's progress through the relative
- // path, including the symlink target we just prepended.
- rp.relpathCommit()
- // Restart path resolution on the new Mount.
- rp.releaseErrorState()
- return true
-
- default:
- // Not an error we can handle.
- return false
- }
-}
-
-// MustBeDir returns true if the file traversed by rp must be a directory.
-func (rp *ResolvingPath) MustBeDir() bool {
- return rp.mustBeDir
-}