github.com/MerlinKodo/gvisor@v0.0.0-20231110090155-957f62ecf90e/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/MerlinKodo/gvisor/pkg/context"
    19  	"github.com/MerlinKodo/gvisor/pkg/errors/linuxerr"
    20  	"github.com/MerlinKodo/gvisor/pkg/fspath"
    21  	"github.com/MerlinKodo/gvisor/pkg/sync"
    22  )
    23  
    24  var fspathBuilderPool = sync.Pool{
    25  	New: func() any {
    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  // PathnameInFilesystem returns an absolute path to vd relative to vd's
   137  // Filesystem root. It also appends //deleted to for disowned entries. It is
   138  // equivalent to Linux's dentry_path().
   139  func (vfs *VirtualFilesystem) PathnameInFilesystem(ctx context.Context, vd VirtualDentry) (string, error) {
   140  	b := getFSPathBuilder()
   141  	defer putFSPathBuilder(b)
   142  	if vd.dentry.IsDead() {
   143  		b.PrependString("//deleted")
   144  	}
   145  	if err := vd.mount.fs.impl.PrependPath(ctx, VirtualDentry{}, VirtualDentry{dentry: vd.dentry}, b); err != nil {
   146  		// PrependPath returns an error if it encounters a filesystem root before
   147  		// the provided vfsroot. We don't provide a vfsroot, so encountering this
   148  		// error is expected and can be ignored.
   149  		switch err.(type) {
   150  		case PrependPathAtNonMountRootError:
   151  		default:
   152  			return "", err
   153  		}
   154  	}
   155  	b.PrependByte('/')
   156  	return b.String(), nil
   157  }
   158  
   159  // PathnameForGetcwd returns an absolute pathname to vd, consistent with
   160  // Linux's sys_getcwd().
   161  func (vfs *VirtualFilesystem) PathnameForGetcwd(ctx context.Context, vfsroot, vd VirtualDentry) (string, error) {
   162  	if vd.dentry.IsDead() {
   163  		return "", linuxerr.ENOENT
   164  	}
   165  
   166  	b := getFSPathBuilder()
   167  	defer putFSPathBuilder(b)
   168  	haveRef := false
   169  	defer func() {
   170  		if haveRef {
   171  			vd.DecRef(ctx)
   172  		}
   173  	}()
   174  	unreachable := false
   175  loop:
   176  	for {
   177  		err := vd.mount.fs.impl.PrependPath(ctx, vfsroot, vd, b)
   178  		switch err.(type) {
   179  		case nil:
   180  			if vd.mount == vfsroot.mount && vd.mount.root == vfsroot.dentry {
   181  				break loop
   182  			}
   183  			nextVD := vfs.getMountpointAt(ctx, vd.mount, vfsroot)
   184  			if !nextVD.Ok() {
   185  				unreachable = true
   186  				break loop
   187  			}
   188  			if haveRef {
   189  				vd.DecRef(ctx)
   190  			}
   191  			vd = nextVD
   192  			haveRef = true
   193  		case PrependPathAtVFSRootError:
   194  			break loop
   195  		case PrependPathAtNonMountRootError, PrependPathSyntheticError:
   196  			unreachable = true
   197  			break loop
   198  		default:
   199  			return "", err
   200  		}
   201  	}
   202  	b.PrependByte('/')
   203  	if unreachable {
   204  		b.PrependString("(unreachable)")
   205  	}
   206  	return b.String(), nil
   207  }
   208  
   209  // As of this writing, we do not have equivalents to:
   210  //
   211  //	- d_absolute_path(), which returns EINVAL if (effectively) any call to
   212  //		FilesystemImpl.PrependPath() would return PrependPathAtNonMountRootError.
   213  //
   214  //	- dentry_path(), which does not walk up mounts (and only returns the path
   215  //		relative to Filesystem root), but also appends "//deleted" for disowned
   216  //		Dentries.
   217  //
   218  // These should be added as necessary.