github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/pkg/sentry/vfs/resolving_path.go (about)

     1  // Copyright 2019 The gVisor Authors.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package vfs
    16  
    17  import (
    18  	"fmt"
    19  
    20  	"github.com/SagerNet/gvisor/pkg/abi/linux"
    21  	"github.com/SagerNet/gvisor/pkg/context"
    22  	"github.com/SagerNet/gvisor/pkg/errors/linuxerr"
    23  	"github.com/SagerNet/gvisor/pkg/fspath"
    24  	"github.com/SagerNet/gvisor/pkg/sentry/kernel/auth"
    25  	"github.com/SagerNet/gvisor/pkg/sync"
    26  	"github.com/SagerNet/gvisor/pkg/syserror"
    27  )
    28  
    29  // ResolvingPath represents the state of an in-progress path resolution, shared
    30  // between VFS and FilesystemImpl methods that take a path.
    31  //
    32  // From the perspective of FilesystemImpl methods, a ResolvingPath represents a
    33  // starting Dentry on the associated Filesystem (on which a reference is
    34  // already held), a stream of path components relative to that Dentry, and
    35  // elements of the invoking Context that are commonly required by
    36  // FilesystemImpl methods.
    37  //
    38  // ResolvingPath is loosely analogous to Linux's struct nameidata.
    39  //
    40  // +stateify savable
    41  type ResolvingPath struct {
    42  	vfs   *VirtualFilesystem
    43  	root  VirtualDentry // refs borrowed from PathOperation
    44  	mount *Mount
    45  	start *Dentry
    46  	pit   fspath.Iterator
    47  
    48  	flags     uint16
    49  	mustBeDir bool  // final file must be a directory?
    50  	symlinks  uint8 // number of symlinks traversed
    51  	curPart   uint8 // index into parts
    52  
    53  	creds *auth.Credentials
    54  
    55  	// Data associated with resolve*Errors, stored in ResolvingPath so that
    56  	// those errors don't need to allocate.
    57  	nextMount        *Mount  // ref held if not nil
    58  	nextStart        *Dentry // ref held if not nil
    59  	absSymlinkTarget fspath.Path
    60  
    61  	// ResolvingPath tracks relative paths, which is updated whenever a relative
    62  	// symlink is encountered.
    63  	parts [1 + linux.MaxSymlinkTraversals]fspath.Iterator
    64  }
    65  
    66  const (
    67  	rpflagsHaveMountRef       = 1 << iota // do we hold a reference on mount?
    68  	rpflagsHaveStartRef                   // do we hold a reference on start?
    69  	rpflagsFollowFinalSymlink             // same as PathOperation.FollowFinalSymlink
    70  )
    71  
    72  func init() {
    73  	if maxParts := len(ResolvingPath{}.parts); maxParts > 255 {
    74  		panic(fmt.Sprintf("uint8 is insufficient to accommodate len(ResolvingPath.parts) (%d)", maxParts))
    75  	}
    76  }
    77  
    78  // Error types that communicate state from the FilesystemImpl-caller,
    79  // VFS-callee side of path resolution (i.e. errors returned by
    80  // ResolvingPath.Resolve*()) to the VFS-caller, FilesystemImpl-callee side
    81  // (i.e. VFS methods => ResolvingPath.handleError()). These are empty structs
    82  // rather than error values because Go doesn't support non-primitive constants,
    83  // so error "constants" are really mutable vars, necessitating somewhat
    84  // expensive interface object comparisons.
    85  
    86  // +stateify savable
    87  type resolveMountRootOrJumpError struct{}
    88  
    89  // Error implements error.Error.
    90  func (resolveMountRootOrJumpError) Error() string {
    91  	return "resolving mount root or jump"
    92  }
    93  
    94  // +stateify savable
    95  type resolveMountPointError struct{}
    96  
    97  // Error implements error.Error.
    98  func (resolveMountPointError) Error() string {
    99  	return "resolving mount point"
   100  }
   101  
   102  // +stateify savable
   103  type resolveAbsSymlinkError struct{}
   104  
   105  // Error implements error.Error.
   106  func (resolveAbsSymlinkError) Error() string {
   107  	return "resolving absolute symlink"
   108  }
   109  
   110  var resolvingPathPool = sync.Pool{
   111  	New: func() interface{} {
   112  		return &ResolvingPath{}
   113  	},
   114  }
   115  
   116  // getResolvingPath gets a new ResolvingPath from the pool. Caller must call
   117  // ResolvingPath.Release() when done.
   118  func (vfs *VirtualFilesystem) getResolvingPath(creds *auth.Credentials, pop *PathOperation) *ResolvingPath {
   119  	rp := resolvingPathPool.Get().(*ResolvingPath)
   120  	rp.vfs = vfs
   121  	rp.root = pop.Root
   122  	rp.mount = pop.Start.mount
   123  	rp.start = pop.Start.dentry
   124  	rp.pit = pop.Path.Begin
   125  	rp.flags = 0
   126  	if pop.FollowFinalSymlink {
   127  		rp.flags |= rpflagsFollowFinalSymlink
   128  	}
   129  	rp.mustBeDir = pop.Path.Dir
   130  	rp.symlinks = 0
   131  	rp.curPart = 0
   132  	rp.creds = creds
   133  	rp.parts[0] = pop.Path.Begin
   134  	return rp
   135  }
   136  
   137  // Copy creates another ResolvingPath with the same state as the original.
   138  // Copies are independent, using the copy does not change the original and
   139  // vice-versa.
   140  //
   141  // Caller must call Resease() when done.
   142  func (rp *ResolvingPath) Copy() *ResolvingPath {
   143  	copy := resolvingPathPool.Get().(*ResolvingPath)
   144  	*copy = *rp // All fields all shallow copiable.
   145  
   146  	// Take extra reference for the copy if the original had them.
   147  	if copy.flags&rpflagsHaveStartRef != 0 {
   148  		copy.start.IncRef()
   149  	}
   150  	if copy.flags&rpflagsHaveMountRef != 0 {
   151  		copy.mount.IncRef()
   152  	}
   153  	// Reset error state.
   154  	copy.nextStart = nil
   155  	copy.nextMount = nil
   156  	return copy
   157  }
   158  
   159  // Release decrements references if needed and returns the object to the pool.
   160  func (rp *ResolvingPath) Release(ctx context.Context) {
   161  	rp.root = VirtualDentry{}
   162  	rp.decRefStartAndMount(ctx)
   163  	rp.mount = nil
   164  	rp.start = nil
   165  	rp.releaseErrorState(ctx)
   166  	resolvingPathPool.Put(rp)
   167  }
   168  
   169  func (rp *ResolvingPath) decRefStartAndMount(ctx context.Context) {
   170  	if rp.flags&rpflagsHaveStartRef != 0 {
   171  		rp.start.DecRef(ctx)
   172  	}
   173  	if rp.flags&rpflagsHaveMountRef != 0 {
   174  		rp.mount.DecRef(ctx)
   175  	}
   176  }
   177  
   178  func (rp *ResolvingPath) releaseErrorState(ctx context.Context) {
   179  	if rp.nextStart != nil {
   180  		rp.nextStart.DecRef(ctx)
   181  		rp.nextStart = nil
   182  	}
   183  	if rp.nextMount != nil {
   184  		rp.nextMount.DecRef(ctx)
   185  		rp.nextMount = nil
   186  	}
   187  }
   188  
   189  // VirtualFilesystem returns the containing VirtualFilesystem.
   190  func (rp *ResolvingPath) VirtualFilesystem() *VirtualFilesystem {
   191  	return rp.vfs
   192  }
   193  
   194  // Credentials returns the credentials of rp's provider.
   195  func (rp *ResolvingPath) Credentials() *auth.Credentials {
   196  	return rp.creds
   197  }
   198  
   199  // Mount returns the Mount on which path resolution is currently occurring. It
   200  // does not take a reference on the returned Mount.
   201  func (rp *ResolvingPath) Mount() *Mount {
   202  	return rp.mount
   203  }
   204  
   205  // Start returns the starting Dentry represented by rp. It does not take a
   206  // reference on the returned Dentry.
   207  func (rp *ResolvingPath) Start() *Dentry {
   208  	return rp.start
   209  }
   210  
   211  // Done returns true if there are no remaining path components in the stream
   212  // represented by rp.
   213  func (rp *ResolvingPath) Done() bool {
   214  	// We don't need to check for rp.curPart == 0 because rp.Advance() won't
   215  	// set rp.pit to a terminal iterator otherwise.
   216  	return !rp.pit.Ok()
   217  }
   218  
   219  // Final returns true if there is exactly one remaining path component in the
   220  // stream represented by rp.
   221  //
   222  // Preconditions: !rp.Done().
   223  func (rp *ResolvingPath) Final() bool {
   224  	return rp.curPart == 0 && !rp.pit.NextOk()
   225  }
   226  
   227  // Component returns the current path component in the stream represented by
   228  // rp.
   229  //
   230  // Preconditions: !rp.Done().
   231  func (rp *ResolvingPath) Component() string {
   232  	if checkInvariants {
   233  		if !rp.pit.Ok() {
   234  			panic("ResolvingPath.Component() called at end of relative path")
   235  		}
   236  	}
   237  	return rp.pit.String()
   238  }
   239  
   240  // Advance advances the stream of path components represented by rp.
   241  //
   242  // Preconditions: !rp.Done().
   243  func (rp *ResolvingPath) Advance() {
   244  	if checkInvariants {
   245  		if !rp.pit.Ok() {
   246  			panic("ResolvingPath.Advance() called at end of relative path")
   247  		}
   248  	}
   249  	next := rp.pit.Next()
   250  	if next.Ok() || rp.curPart == 0 { // have next component, or at end of path
   251  		rp.pit = next
   252  	} else { // at end of path segment, continue with next one
   253  		rp.curPart--
   254  		rp.pit = rp.parts[rp.curPart]
   255  	}
   256  }
   257  
   258  // CheckRoot is called before resolving the parent of the Dentry d. If the
   259  // Dentry is contextually a VFS root, such that path resolution should treat
   260  // d's parent as itself, CheckRoot returns (true, nil). If the Dentry is the
   261  // root of a non-root mount, such that path resolution should switch to another
   262  // Mount, CheckRoot returns (unspecified, non-nil error). Otherwise, path
   263  // resolution should resolve d's parent normally, and CheckRoot returns (false,
   264  // nil).
   265  func (rp *ResolvingPath) CheckRoot(ctx context.Context, d *Dentry) (bool, error) {
   266  	if d == rp.root.dentry && rp.mount == rp.root.mount {
   267  		// At contextual VFS root (due to e.g. chroot(2)).
   268  		return true, nil
   269  	} else if d == rp.mount.root {
   270  		// At mount root ...
   271  		vd := rp.vfs.getMountpointAt(ctx, rp.mount, rp.root)
   272  		if vd.Ok() {
   273  			// ... of non-root mount.
   274  			rp.nextMount = vd.mount
   275  			rp.nextStart = vd.dentry
   276  			return false, resolveMountRootOrJumpError{}
   277  		}
   278  		// ... of root mount.
   279  		return true, nil
   280  	}
   281  	return false, nil
   282  }
   283  
   284  // CheckMount is called after resolving the parent or child of another Dentry
   285  // to d. If d is a mount point, such that path resolution should switch to
   286  // another Mount, CheckMount returns a non-nil error. Otherwise, CheckMount
   287  // returns nil.
   288  func (rp *ResolvingPath) CheckMount(ctx context.Context, d *Dentry) error {
   289  	if !d.isMounted() {
   290  		return nil
   291  	}
   292  	if mnt := rp.vfs.getMountAt(ctx, rp.mount, d); mnt != nil {
   293  		rp.nextMount = mnt
   294  		return resolveMountPointError{}
   295  	}
   296  	return nil
   297  }
   298  
   299  // ShouldFollowSymlink returns true if, supposing that the current path
   300  // component in pcs represents a symbolic link, the symbolic link should be
   301  // followed.
   302  //
   303  // If path is terminated with '/', the '/' is considered the last element and
   304  // any symlink before that is followed:
   305  //   - For most non-creating walks, the last path component is handled by
   306  //     fs/namei.c:lookup_last(), which sets LOOKUP_FOLLOW if the first byte
   307  //     after the path component is non-NULL (which is only possible if it's '/')
   308  //     and the path component is of type LAST_NORM.
   309  //
   310  //   - For open/openat/openat2 without O_CREAT, the last path component is
   311  //     handled by fs/namei.c:do_last(), which does the same, though without the
   312  //     LAST_NORM check.
   313  //
   314  // Preconditions: !rp.Done().
   315  func (rp *ResolvingPath) ShouldFollowSymlink() bool {
   316  	// Non-final symlinks are always followed. Paths terminated with '/' are also
   317  	// always followed.
   318  	return rp.flags&rpflagsFollowFinalSymlink != 0 || !rp.Final() || rp.MustBeDir()
   319  }
   320  
   321  // HandleSymlink is called when the current path component is a symbolic link
   322  // to the given target. If the calling Filesystem method should continue path
   323  // traversal, HandleSymlink updates the path component stream to reflect the
   324  // symlink target and returns nil. Otherwise it returns a non-nil error.
   325  //
   326  // Preconditions: !rp.Done().
   327  //
   328  // Postconditions: If HandleSymlink returns a nil error, then !rp.Done().
   329  func (rp *ResolvingPath) HandleSymlink(target string) error {
   330  	if rp.symlinks >= linux.MaxSymlinkTraversals {
   331  		return linuxerr.ELOOP
   332  	}
   333  	if len(target) == 0 {
   334  		return syserror.ENOENT
   335  	}
   336  	rp.symlinks++
   337  	targetPath := fspath.Parse(target)
   338  	if targetPath.Absolute {
   339  		rp.absSymlinkTarget = targetPath
   340  		return resolveAbsSymlinkError{}
   341  	}
   342  	// Consume the path component that represented the symlink.
   343  	rp.Advance()
   344  	// Prepend the symlink target to the relative path.
   345  	if checkInvariants {
   346  		if !targetPath.HasComponents() {
   347  			panic(fmt.Sprintf("non-empty pathname %q parsed to relative path with no components", target))
   348  		}
   349  	}
   350  	rp.relpathPrepend(targetPath)
   351  	return nil
   352  }
   353  
   354  // Preconditions: path.HasComponents().
   355  func (rp *ResolvingPath) relpathPrepend(path fspath.Path) {
   356  	if rp.pit.Ok() {
   357  		rp.parts[rp.curPart] = rp.pit
   358  		rp.pit = path.Begin
   359  		rp.curPart++
   360  	} else {
   361  		// The symlink was the final path component, so now the symlink target
   362  		// is the whole path.
   363  		rp.pit = path.Begin
   364  		// Symlink targets can set rp.mustBeDir (if they end in a trailing /),
   365  		// but can't unset it.
   366  		if path.Dir {
   367  			rp.mustBeDir = true
   368  		}
   369  	}
   370  }
   371  
   372  // HandleJump is called when the current path component is a "magic" link to
   373  // the given VirtualDentry, like /proc/[pid]/fd/[fd]. If the calling Filesystem
   374  // method should continue path traversal, HandleMagicSymlink updates the path
   375  // component stream to reflect the magic link target and returns nil. Otherwise
   376  // it returns a non-nil error.
   377  //
   378  // Preconditions: !rp.Done().
   379  func (rp *ResolvingPath) HandleJump(target VirtualDentry) error {
   380  	if rp.symlinks >= linux.MaxSymlinkTraversals {
   381  		return linuxerr.ELOOP
   382  	}
   383  	rp.symlinks++
   384  	// Consume the path component that represented the magic link.
   385  	rp.Advance()
   386  	// Unconditionally return a resolveMountRootOrJumpError, even if the Mount
   387  	// isn't changing, to force restarting at the new Dentry.
   388  	target.IncRef()
   389  	rp.nextMount = target.mount
   390  	rp.nextStart = target.dentry
   391  	return resolveMountRootOrJumpError{}
   392  }
   393  
   394  func (rp *ResolvingPath) handleError(ctx context.Context, err error) bool {
   395  	switch err.(type) {
   396  	case resolveMountRootOrJumpError:
   397  		// Switch to the new Mount. We hold references on the Mount and Dentry.
   398  		rp.decRefStartAndMount(ctx)
   399  		rp.mount = rp.nextMount
   400  		rp.start = rp.nextStart
   401  		rp.flags |= rpflagsHaveMountRef | rpflagsHaveStartRef
   402  		rp.nextMount = nil
   403  		rp.nextStart = nil
   404  		// Don't consume the path component that caused us to traverse
   405  		// through the mount root - i.e. the ".." - because we still need to
   406  		// resolve the mount point's parent in the new FilesystemImpl.
   407  		//
   408  		// Restart path resolution on the new Mount. Don't bother calling
   409  		// rp.releaseErrorState() since we already set nextMount and nextStart
   410  		// to nil above.
   411  		return true
   412  
   413  	case resolveMountPointError:
   414  		// Switch to the new Mount. We hold a reference on the Mount, but
   415  		// borrow the reference on the mount root from the Mount.
   416  		rp.decRefStartAndMount(ctx)
   417  		rp.mount = rp.nextMount
   418  		rp.start = rp.nextMount.root
   419  		rp.flags = rp.flags&^rpflagsHaveStartRef | rpflagsHaveMountRef
   420  		rp.nextMount = nil
   421  		// Consume the path component that represented the mount point.
   422  		rp.Advance()
   423  		// Restart path resolution on the new Mount.
   424  		rp.releaseErrorState(ctx)
   425  		return true
   426  
   427  	case resolveAbsSymlinkError:
   428  		// Switch to the new Mount. References are borrowed from rp.root.
   429  		rp.decRefStartAndMount(ctx)
   430  		rp.mount = rp.root.mount
   431  		rp.start = rp.root.dentry
   432  		rp.flags &^= rpflagsHaveMountRef | rpflagsHaveStartRef
   433  		// Consume the path component that represented the symlink.
   434  		rp.Advance()
   435  		// Prepend the symlink target to the relative path.
   436  		rp.relpathPrepend(rp.absSymlinkTarget)
   437  		// Restart path resolution on the new Mount.
   438  		rp.releaseErrorState(ctx)
   439  		return true
   440  
   441  	default:
   442  		// Not an error we can handle.
   443  		return false
   444  	}
   445  }
   446  
   447  // canHandleError returns true if err is an error returned by rp.Resolve*()
   448  // that rp.handleError() may attempt to handle.
   449  func (rp *ResolvingPath) canHandleError(err error) bool {
   450  	switch err.(type) {
   451  	case resolveMountRootOrJumpError, resolveMountPointError, resolveAbsSymlinkError:
   452  		return true
   453  	default:
   454  		return false
   455  	}
   456  }
   457  
   458  // MustBeDir returns true if the file traversed by rp must be a directory.
   459  func (rp *ResolvingPath) MustBeDir() bool {
   460  	return rp.mustBeDir
   461  }