github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/pkg/sentry/vfs/pathname.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  	"github.com/SagerNet/gvisor/pkg/context"
    19  	"github.com/SagerNet/gvisor/pkg/fspath"
    20  	"github.com/SagerNet/gvisor/pkg/sync"
    21  	"github.com/SagerNet/gvisor/pkg/syserror"
    22  )
    23  
    24  var fspathBuilderPool = sync.Pool{
    25  	New: func() interface{} {
    26  		return &fspath.Builder{}
    27  	},
    28  }
    29  
    30  func getFSPathBuilder() *fspath.Builder {
    31  	return fspathBuilderPool.Get().(*fspath.Builder)
    32  }
    33  
    34  func putFSPathBuilder(b *fspath.Builder) {
    35  	// No methods can be called on b after b.String(), so reset it to its zero
    36  	// value (as returned by fspathBuilderPool.New) instead.
    37  	*b = fspath.Builder{}
    38  	fspathBuilderPool.Put(b)
    39  }
    40  
    41  // PathnameWithDeleted returns an absolute pathname to vd, consistent with
    42  // Linux's d_path(). In particular, if vd.Dentry() has been disowned,
    43  // PathnameWithDeleted appends " (deleted)" to the returned pathname.
    44  func (vfs *VirtualFilesystem) PathnameWithDeleted(ctx context.Context, vfsroot, vd VirtualDentry) (string, error) {
    45  	b := getFSPathBuilder()
    46  	defer putFSPathBuilder(b)
    47  	haveRef := false
    48  	defer func() {
    49  		if haveRef {
    50  			vd.DecRef(ctx)
    51  		}
    52  	}()
    53  
    54  	origD := vd.dentry
    55  loop:
    56  	for {
    57  		err := vd.mount.fs.impl.PrependPath(ctx, vfsroot, vd, b)
    58  		switch err.(type) {
    59  		case nil:
    60  			if vd.mount == vfsroot.mount && vd.mount.root == vfsroot.dentry {
    61  				// genericfstree.PrependPath() will have returned
    62  				// PrependPathAtVFSRootError in this case since it checks
    63  				// against vfsroot before mnt.root, but other implementations
    64  				// of FilesystemImpl.PrependPath() may return nil instead.
    65  				break loop
    66  			}
    67  			nextVD := vfs.getMountpointAt(ctx, vd.mount, vfsroot)
    68  			if !nextVD.Ok() {
    69  				break loop
    70  			}
    71  			if haveRef {
    72  				vd.DecRef(ctx)
    73  			}
    74  			vd = nextVD
    75  			haveRef = true
    76  			// continue loop
    77  		case PrependPathSyntheticError:
    78  			// Skip prepending "/" and appending " (deleted)".
    79  			return b.String(), nil
    80  		case PrependPathAtVFSRootError, PrependPathAtNonMountRootError:
    81  			break loop
    82  		default:
    83  			return "", err
    84  		}
    85  	}
    86  	b.PrependByte('/')
    87  	if origD.IsDead() {
    88  		b.AppendString(" (deleted)")
    89  	}
    90  	return b.String(), nil
    91  }
    92  
    93  // PathnameReachable returns an absolute pathname to vd, consistent with
    94  // Linux's __d_path() (as used by seq_path_root()). If vfsroot.Ok() and vd is
    95  // not reachable from vfsroot, such that seq_path_root() would return SEQ_SKIP
    96  // (causing the entire containing entry to be skipped), PathnameReachable
    97  // returns ("", nil).
    98  func (vfs *VirtualFilesystem) PathnameReachable(ctx context.Context, vfsroot, vd VirtualDentry) (string, error) {
    99  	b := getFSPathBuilder()
   100  	defer putFSPathBuilder(b)
   101  	haveRef := false
   102  	defer func() {
   103  		if haveRef {
   104  			vd.DecRef(ctx)
   105  		}
   106  	}()
   107  loop:
   108  	for {
   109  		err := vd.mount.fs.impl.PrependPath(ctx, vfsroot, vd, b)
   110  		switch err.(type) {
   111  		case nil:
   112  			if vd.mount == vfsroot.mount && vd.mount.root == vfsroot.dentry {
   113  				break loop
   114  			}
   115  			nextVD := vfs.getMountpointAt(ctx, vd.mount, vfsroot)
   116  			if !nextVD.Ok() {
   117  				return "", nil
   118  			}
   119  			if haveRef {
   120  				vd.DecRef(ctx)
   121  			}
   122  			vd = nextVD
   123  			haveRef = true
   124  		case PrependPathAtVFSRootError:
   125  			break loop
   126  		case PrependPathAtNonMountRootError, PrependPathSyntheticError:
   127  			return "", nil
   128  		default:
   129  			return "", err
   130  		}
   131  	}
   132  	b.PrependByte('/')
   133  	return b.String(), nil
   134  }
   135  
   136  // PathnameForGetcwd returns an absolute pathname to vd, consistent with
   137  // Linux's sys_getcwd().
   138  func (vfs *VirtualFilesystem) PathnameForGetcwd(ctx context.Context, vfsroot, vd VirtualDentry) (string, error) {
   139  	if vd.dentry.IsDead() {
   140  		return "", syserror.ENOENT
   141  	}
   142  
   143  	b := getFSPathBuilder()
   144  	defer putFSPathBuilder(b)
   145  	haveRef := false
   146  	defer func() {
   147  		if haveRef {
   148  			vd.DecRef(ctx)
   149  		}
   150  	}()
   151  	unreachable := false
   152  loop:
   153  	for {
   154  		err := vd.mount.fs.impl.PrependPath(ctx, vfsroot, vd, b)
   155  		switch err.(type) {
   156  		case nil:
   157  			if vd.mount == vfsroot.mount && vd.mount.root == vfsroot.dentry {
   158  				break loop
   159  			}
   160  			nextVD := vfs.getMountpointAt(ctx, vd.mount, vfsroot)
   161  			if !nextVD.Ok() {
   162  				unreachable = true
   163  				break loop
   164  			}
   165  			if haveRef {
   166  				vd.DecRef(ctx)
   167  			}
   168  			vd = nextVD
   169  			haveRef = true
   170  		case PrependPathAtVFSRootError:
   171  			break loop
   172  		case PrependPathAtNonMountRootError, PrependPathSyntheticError:
   173  			unreachable = true
   174  			break loop
   175  		default:
   176  			return "", err
   177  		}
   178  	}
   179  	b.PrependByte('/')
   180  	if unreachable {
   181  		b.PrependString("(unreachable)")
   182  	}
   183  	return b.String(), nil
   184  }
   185  
   186  // As of this writing, we do not have equivalents to:
   187  //
   188  // - d_absolute_path(), which returns EINVAL if (effectively) any call to
   189  // FilesystemImpl.PrependPath() would return PrependPathAtNonMountRootError.
   190  //
   191  // - dentry_path(), which does not walk up mounts (and only returns the path
   192  // relative to Filesystem root), but also appends "//deleted" for disowned
   193  // Dentries.
   194  //
   195  // These should be added as necessary.