github.com/metacubex/gvisor@v0.0.0-20240320004321-933faba989ec/pkg/sentry/fsimpl/gofer/dentry_impl.go (about)

     1  // Copyright 2022 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 gofer
    16  
    17  import (
    18  	"golang.org/x/sys/unix"
    19  	"github.com/metacubex/gvisor/pkg/abi/linux"
    20  	"github.com/metacubex/gvisor/pkg/atomicbitops"
    21  	"github.com/metacubex/gvisor/pkg/context"
    22  	"github.com/metacubex/gvisor/pkg/errors/linuxerr"
    23  	"github.com/metacubex/gvisor/pkg/fsutil"
    24  	"github.com/metacubex/gvisor/pkg/log"
    25  	"github.com/metacubex/gvisor/pkg/sentry/kernel/auth"
    26  	"github.com/metacubex/gvisor/pkg/sentry/vfs"
    27  )
    28  
    29  // We do *not* define an interface for dentry.impl because making interface
    30  // method calls is almost 2.5x slower than calling the same method on a
    31  // concrete type. Instead, we use type assertions in switch statements. The
    32  // asserted type is a concrete dentry implementation and methods are called
    33  // directly on the concrete type. This helps in the following ways:
    34  //
    35  // 1. This is faster because concrete type assertion just needs to compare the
    36  //    itab pointer in the interface value to a constant which is relatively
    37  //    cheap. Benchmarking showed that such type switches don't add almost any
    38  //    overhead.
    39  // 2. Passing any pointer to an interface method immediately causes the pointed
    40  //    object to escape to heap. Making concrete method calls allows escape
    41  //    analysis to proceed as usual and avoids heap allocations.
    42  //
    43  // Also note that the default case in these type switch statements panics. We
    44  // do not do panic(fmt.Sprintf("... %T", d.impl)) because somehow it adds a lot
    45  // of overhead to the type switch. So instead we panic with a constant string.
    46  
    47  // Precondition: d.handleMu must be locked.
    48  func (d *dentry) isReadHandleOk() bool {
    49  	switch dt := d.impl.(type) {
    50  	case *lisafsDentry:
    51  		return dt.readFDLisa.Ok()
    52  	case *directfsDentry:
    53  		return d.readFD.RacyLoad() >= 0
    54  	case nil: // synthetic dentry
    55  		return false
    56  	default:
    57  		panic("unknown dentry implementation")
    58  	}
    59  }
    60  
    61  // Precondition: d.handleMu must be locked.
    62  func (d *dentry) isWriteHandleOk() bool {
    63  	switch dt := d.impl.(type) {
    64  	case *lisafsDentry:
    65  		return dt.writeFDLisa.Ok()
    66  	case *directfsDentry:
    67  		return d.writeFD.RacyLoad() >= 0
    68  	case nil: // synthetic dentry
    69  		return false
    70  	default:
    71  		panic("unknown dentry implementation")
    72  	}
    73  }
    74  
    75  // Precondition: d.handleMu must be locked.
    76  func (d *dentry) readHandle() handle {
    77  	switch dt := d.impl.(type) {
    78  	case *lisafsDentry:
    79  		return handle{
    80  			fdLisa: dt.readFDLisa,
    81  			fd:     d.readFD.RacyLoad(),
    82  		}
    83  	case *directfsDentry:
    84  		return handle{fd: d.readFD.RacyLoad()}
    85  	case nil: // synthetic dentry
    86  		return noHandle
    87  	default:
    88  		panic("unknown dentry implementation")
    89  	}
    90  }
    91  
    92  // Precondition: d.handleMu must be locked.
    93  func (d *dentry) writeHandle() handle {
    94  	switch dt := d.impl.(type) {
    95  	case *lisafsDentry:
    96  		return handle{
    97  			fdLisa: dt.writeFDLisa,
    98  			fd:     d.writeFD.RacyLoad(),
    99  		}
   100  	case *directfsDentry:
   101  		return handle{fd: d.writeFD.RacyLoad()}
   102  	case nil: // synthetic dentry
   103  		return noHandle
   104  	default:
   105  		panic("unknown dentry implementation")
   106  	}
   107  }
   108  
   109  // Preconditions:
   110  //   - !d.isSynthetic().
   111  //   - fs.renameMu is locked.
   112  func (d *dentry) openHandle(ctx context.Context, read, write, trunc bool) (handle, error) {
   113  	flags := uint32(unix.O_RDONLY)
   114  	switch {
   115  	case read && write:
   116  		flags = unix.O_RDWR
   117  	case read:
   118  		flags = unix.O_RDONLY
   119  	case write:
   120  		flags = unix.O_WRONLY
   121  	default:
   122  		log.Debugf("openHandle called with read = write = false. Falling back to read only FD.")
   123  	}
   124  	if trunc {
   125  		flags |= unix.O_TRUNC
   126  	}
   127  	switch dt := d.impl.(type) {
   128  	case *lisafsDentry:
   129  		return dt.openHandle(ctx, flags)
   130  	case *directfsDentry:
   131  		return dt.openHandle(ctx, flags)
   132  	default:
   133  		panic("unknown dentry implementation")
   134  	}
   135  }
   136  
   137  // Preconditions:
   138  //   - d.handleMu must be locked.
   139  //   - !d.isSynthetic().
   140  func (d *dentry) updateHandles(ctx context.Context, h handle, readable, writable bool) {
   141  	switch dt := d.impl.(type) {
   142  	case *lisafsDentry:
   143  		dt.updateHandles(ctx, h, readable, writable)
   144  	case *directfsDentry:
   145  		// No update needed.
   146  	default:
   147  		panic("unknown dentry implementation")
   148  	}
   149  }
   150  
   151  // Preconditions:
   152  //   - d.handleMu must be locked.
   153  //   - !d.isSynthetic().
   154  func (d *dentry) closeHostFDs() {
   155  	// We can use RacyLoad() because d.handleMu is locked.
   156  	if d.readFD.RacyLoad() >= 0 {
   157  		_ = unix.Close(int(d.readFD.RacyLoad()))
   158  	}
   159  	if d.writeFD.RacyLoad() >= 0 && d.readFD.RacyLoad() != d.writeFD.RacyLoad() {
   160  		_ = unix.Close(int(d.writeFD.RacyLoad()))
   161  	}
   162  	d.readFD = atomicbitops.FromInt32(-1)
   163  	d.writeFD = atomicbitops.FromInt32(-1)
   164  	d.mmapFD = atomicbitops.FromInt32(-1)
   165  
   166  	switch dt := d.impl.(type) {
   167  	case *directfsDentry:
   168  		if dt.controlFD >= 0 {
   169  			_ = unix.Close(dt.controlFD)
   170  			dt.controlFD = -1
   171  		}
   172  	}
   173  }
   174  
   175  // updateMetadataLocked updates the dentry's metadata fields. The h parameter
   176  // is optional. If it is not provided, an appropriate FD should be chosen to
   177  // stat the remote file.
   178  //
   179  // Preconditions:
   180  //   - !d.isSynthetic().
   181  //   - d.metadataMu is locked.
   182  //
   183  // +checklocks:d.metadataMu
   184  func (d *dentry) updateMetadataLocked(ctx context.Context, h handle) error {
   185  	// Need checklocksforce below because checklocks has no way of knowing that
   186  	// d.impl.(*dentryImpl).dentry == d. It can't know that the right metadataMu
   187  	// is already locked.
   188  	switch dt := d.impl.(type) {
   189  	case *lisafsDentry:
   190  		return dt.updateMetadataLocked(ctx, h) // +checklocksforce: acquired by precondition.
   191  	case *directfsDentry:
   192  		return dt.updateMetadataLocked(h) // +checklocksforce: acquired by precondition.
   193  	default:
   194  		panic("unknown dentry implementation")
   195  	}
   196  }
   197  
   198  // Preconditions:
   199  //   - !d.isSynthetic().
   200  //   - fs.renameMu is locked.
   201  func (d *dentry) prepareSetStat(ctx context.Context, stat *linux.Statx) error {
   202  	switch dt := d.impl.(type) {
   203  	case *lisafsDentry:
   204  		// Nothing to be done.
   205  		return nil
   206  	case *directfsDentry:
   207  		return dt.prepareSetStat(ctx, stat)
   208  	default:
   209  		panic("unknown dentry implementation")
   210  	}
   211  }
   212  
   213  // Precondition: fs.renameMu is locked if d is a socket.
   214  func (d *dentry) chmod(ctx context.Context, mode uint16) error {
   215  	switch dt := d.impl.(type) {
   216  	case *lisafsDentry:
   217  		return chmod(ctx, dt.controlFD, mode)
   218  	case *directfsDentry:
   219  		return dt.chmod(ctx, mode)
   220  	default:
   221  		panic("unknown dentry implementation")
   222  	}
   223  }
   224  
   225  // Preconditions:
   226  //   - !d.isSynthetic().
   227  //   - d.handleMu is locked.
   228  //   - fs.renameMu is locked.
   229  func (d *dentry) setStatLocked(ctx context.Context, stat *linux.Statx) (uint32, error, error) {
   230  	switch dt := d.impl.(type) {
   231  	case *lisafsDentry:
   232  		return dt.controlFD.SetStat(ctx, stat)
   233  	case *directfsDentry:
   234  		failureMask, failureErr := dt.setStatLocked(ctx, stat)
   235  		return failureMask, failureErr, nil
   236  	default:
   237  		panic("unknown dentry implementation")
   238  	}
   239  }
   240  
   241  // Precondition: d.handleMu must be locked.
   242  func (d *dentry) destroyImpl(ctx context.Context) {
   243  	switch dt := d.impl.(type) {
   244  	case *lisafsDentry:
   245  		dt.destroy(ctx)
   246  	case *directfsDentry:
   247  		dt.destroy(ctx)
   248  	case nil: // synthetic dentry
   249  	default:
   250  		panic("unknown dentry implementation")
   251  	}
   252  }
   253  
   254  // Postcondition: Caller must do dentry caching appropriately.
   255  //
   256  // +checklocksread:d.opMu
   257  func (d *dentry) getRemoteChild(ctx context.Context, name string) (*dentry, error) {
   258  	switch dt := d.impl.(type) {
   259  	case *lisafsDentry:
   260  		return dt.getRemoteChild(ctx, name)
   261  	case *directfsDentry:
   262  		return dt.getHostChild(name)
   263  	default:
   264  		panic("unknown dentry implementation")
   265  	}
   266  }
   267  
   268  // Preconditions:
   269  //   - fs.renameMu must be locked.
   270  //   - parent.opMu must be locked for reading.
   271  //   - parent.isDir().
   272  //   - !rp.Done() && rp.Component() is not "." or "..".
   273  //
   274  // Postcondition: The returned dentry is already cached appropriately.
   275  //
   276  // +checklocksread:d.opMu
   277  func (d *dentry) getRemoteChildAndWalkPathLocked(ctx context.Context, rp resolvingPath, ds **[]*dentry) (*dentry, error) {
   278  	switch dt := d.impl.(type) {
   279  	case *lisafsDentry:
   280  		return dt.getRemoteChildAndWalkPathLocked(ctx, rp, ds)
   281  	case *directfsDentry:
   282  		// We need to check for races because opMu is read locked which allows
   283  		// concurrent walks to occur.
   284  		return d.fs.getRemoteChildLocked(ctx, d, rp.Component(), true /* checkForRace */, ds)
   285  	default:
   286  		panic("unknown dentry implementation")
   287  	}
   288  }
   289  
   290  // Precondition: !d.isSynthetic().
   291  func (d *dentry) listXattrImpl(ctx context.Context, size uint64) ([]string, error) {
   292  	switch dt := d.impl.(type) {
   293  	case *lisafsDentry:
   294  		return dt.controlFD.ListXattr(ctx, size)
   295  	case *directfsDentry:
   296  		// Consistent with runsc/fsgofer.
   297  		return nil, linuxerr.EOPNOTSUPP
   298  	default:
   299  		panic("unknown dentry implementation")
   300  	}
   301  }
   302  
   303  // Precondition: !d.isSynthetic().
   304  func (d *dentry) getXattrImpl(ctx context.Context, opts *vfs.GetXattrOptions) (string, error) {
   305  	switch dt := d.impl.(type) {
   306  	case *lisafsDentry:
   307  		return dt.controlFD.GetXattr(ctx, opts.Name, opts.Size)
   308  	case *directfsDentry:
   309  		return dt.getXattr(opts.Name, opts.Size)
   310  	default:
   311  		panic("unknown dentry implementation")
   312  	}
   313  }
   314  
   315  // Precondition: !d.isSynthetic().
   316  func (d *dentry) setXattrImpl(ctx context.Context, opts *vfs.SetXattrOptions) error {
   317  	switch dt := d.impl.(type) {
   318  	case *lisafsDentry:
   319  		return dt.controlFD.SetXattr(ctx, opts.Name, opts.Value, opts.Flags)
   320  	case *directfsDentry:
   321  		// Consistent with runsc/fsgofer.
   322  		return linuxerr.EOPNOTSUPP
   323  	default:
   324  		panic("unknown dentry implementation")
   325  	}
   326  }
   327  
   328  // Precondition: !d.isSynthetic().
   329  func (d *dentry) removeXattrImpl(ctx context.Context, name string) error {
   330  	switch dt := d.impl.(type) {
   331  	case *lisafsDentry:
   332  		return dt.controlFD.RemoveXattr(ctx, name)
   333  	case *directfsDentry:
   334  		// Consistent with runsc/fsgofer.
   335  		return linuxerr.EOPNOTSUPP
   336  	default:
   337  		panic("unknown dentry implementation")
   338  	}
   339  }
   340  
   341  // Precondition: !d.isSynthetic().
   342  func (d *dentry) mknod(ctx context.Context, name string, creds *auth.Credentials, opts *vfs.MknodOptions) (*dentry, error) {
   343  	switch dt := d.impl.(type) {
   344  	case *lisafsDentry:
   345  		return dt.mknod(ctx, name, creds, opts)
   346  	case *directfsDentry:
   347  		return dt.mknod(ctx, name, creds, opts)
   348  	default:
   349  		panic("unknown dentry implementation")
   350  	}
   351  }
   352  
   353  // Preconditions:
   354  //   - !d.isSynthetic().
   355  //   - !target.isSynthetic().
   356  //   - d.fs.renameMu must be locked.
   357  func (d *dentry) link(ctx context.Context, target *dentry, name string) (*dentry, error) {
   358  	switch dt := d.impl.(type) {
   359  	case *lisafsDentry:
   360  		return dt.link(ctx, target.impl.(*lisafsDentry), name)
   361  	case *directfsDentry:
   362  		return dt.link(target.impl.(*directfsDentry), name)
   363  	default:
   364  		panic("unknown dentry implementation")
   365  	}
   366  }
   367  
   368  // Precondition: !d.isSynthetic().
   369  func (d *dentry) mkdir(ctx context.Context, name string, mode linux.FileMode, uid auth.KUID, gid auth.KGID) (*dentry, error) {
   370  	switch dt := d.impl.(type) {
   371  	case *lisafsDentry:
   372  		return dt.mkdir(ctx, name, mode, uid, gid)
   373  	case *directfsDentry:
   374  		return dt.mkdir(name, mode, uid, gid)
   375  	default:
   376  		panic("unknown dentry implementation")
   377  	}
   378  }
   379  
   380  // Precondition: !d.isSynthetic().
   381  func (d *dentry) symlink(ctx context.Context, name, target string, creds *auth.Credentials) (*dentry, error) {
   382  	switch dt := d.impl.(type) {
   383  	case *lisafsDentry:
   384  		return dt.symlink(ctx, name, target, creds)
   385  	case *directfsDentry:
   386  		return dt.symlink(name, target, creds)
   387  	default:
   388  		panic("unknown dentry implementation")
   389  	}
   390  }
   391  
   392  // Precondition: !d.isSynthetic().
   393  func (d *dentry) openCreate(ctx context.Context, name string, accessFlags uint32, mode linux.FileMode, uid auth.KUID, gid auth.KGID) (*dentry, handle, error) {
   394  	switch dt := d.impl.(type) {
   395  	case *lisafsDentry:
   396  		return dt.openCreate(ctx, name, accessFlags, mode, uid, gid)
   397  	case *directfsDentry:
   398  		return dt.openCreate(name, accessFlags, mode, uid, gid)
   399  	default:
   400  		panic("unknown dentry implementation")
   401  	}
   402  }
   403  
   404  // Preconditions:
   405  //   - d.isDir().
   406  //   - d.handleMu must be locked.
   407  //   - !d.isSynthetic().
   408  func (d *dentry) getDirentsLocked(ctx context.Context, recordDirent func(name string, key inoKey, dType uint8)) error {
   409  	switch dt := d.impl.(type) {
   410  	case *lisafsDentry:
   411  		return dt.getDirentsLocked(ctx, recordDirent)
   412  	case *directfsDentry:
   413  		return dt.getDirentsLocked(recordDirent)
   414  	default:
   415  		panic("unknown dentry implementation")
   416  	}
   417  }
   418  
   419  // Precondition: !d.isSynthetic().
   420  func (d *dentry) flush(ctx context.Context) error {
   421  	d.handleMu.RLock()
   422  	defer d.handleMu.RUnlock()
   423  	switch dt := d.impl.(type) {
   424  	case *lisafsDentry:
   425  		return flush(ctx, dt.writeFDLisa)
   426  	case *directfsDentry:
   427  		// Nothing to do here.
   428  		return nil
   429  	default:
   430  		panic("unknown dentry implementation")
   431  	}
   432  }
   433  
   434  // Precondition: !d.isSynthetic().
   435  func (d *dentry) allocate(ctx context.Context, mode, offset, length uint64) error {
   436  	d.handleMu.RLock()
   437  	defer d.handleMu.RUnlock()
   438  	switch dt := d.impl.(type) {
   439  	case *lisafsDentry:
   440  		return dt.writeFDLisa.Allocate(ctx, mode, offset, length)
   441  	case *directfsDentry:
   442  		return unix.Fallocate(int(d.writeFD.RacyLoad()), uint32(mode), int64(offset), int64(length))
   443  	default:
   444  		panic("unknown dentry implementation")
   445  	}
   446  }
   447  
   448  // Preconditions:
   449  //   - !d.isSynthetic().
   450  //   - fs.renameMu is locked.
   451  func (d *dentry) connect(ctx context.Context, sockType linux.SockType) (int, error) {
   452  	switch dt := d.impl.(type) {
   453  	case *lisafsDentry:
   454  		return dt.controlFD.Connect(ctx, sockType)
   455  	case *directfsDentry:
   456  		return dt.connect(ctx, sockType)
   457  	default:
   458  		panic("unknown dentry implementation")
   459  	}
   460  }
   461  
   462  // Precondition: !d.isSynthetic().
   463  func (d *dentry) readlinkImpl(ctx context.Context) (string, error) {
   464  	switch dt := d.impl.(type) {
   465  	case *lisafsDentry:
   466  		return dt.controlFD.ReadLinkAt(ctx)
   467  	case *directfsDentry:
   468  		return dt.readlink()
   469  	default:
   470  		panic("unknown dentry implementation")
   471  	}
   472  }
   473  
   474  // Precondition: !d.isSynthetic().
   475  func (d *dentry) unlink(ctx context.Context, name string, flags uint32) error {
   476  	switch dt := d.impl.(type) {
   477  	case *lisafsDentry:
   478  		return dt.controlFD.UnlinkAt(ctx, name, flags)
   479  	case *directfsDentry:
   480  		return unix.Unlinkat(dt.controlFD, name, int(flags))
   481  	default:
   482  		panic("unknown dentry implementation")
   483  	}
   484  }
   485  
   486  // Precondition: !d.isSynthetic().
   487  func (d *dentry) rename(ctx context.Context, oldName string, newParent *dentry, newName string) error {
   488  	switch dt := d.impl.(type) {
   489  	case *lisafsDentry:
   490  		return dt.controlFD.RenameAt(ctx, oldName, newParent.impl.(*lisafsDentry).controlFD.ID(), newName)
   491  	case *directfsDentry:
   492  		return fsutil.RenameAt(dt.controlFD, oldName, newParent.impl.(*directfsDentry).controlFD, newName)
   493  	default:
   494  		panic("unknown dentry implementation")
   495  	}
   496  }
   497  
   498  // Precondition: !d.isSynthetic().
   499  func (d *dentry) statfs(ctx context.Context) (linux.Statfs, error) {
   500  	switch dt := d.impl.(type) {
   501  	case *lisafsDentry:
   502  		return dt.statfs(ctx)
   503  	case *directfsDentry:
   504  		return dt.statfs()
   505  	default:
   506  		panic("unknown dentry implementation")
   507  	}
   508  }
   509  
   510  func (fs *filesystem) restoreRoot(ctx context.Context, opts *vfs.CompleteRestoreOptions) error {
   511  	rootInode, rootHostFD, err := fs.initClientAndGetRoot(ctx)
   512  	if err != nil {
   513  		return err
   514  	}
   515  
   516  	// The root is always non-synthetic.
   517  	switch dt := fs.root.impl.(type) {
   518  	case *lisafsDentry:
   519  		return dt.restoreFile(ctx, &rootInode, opts)
   520  	case *directfsDentry:
   521  		dt.controlFDLisa = fs.client.NewFD(rootInode.ControlFD)
   522  		return dt.restoreFile(ctx, rootHostFD, opts)
   523  	default:
   524  		panic("unknown dentry implementation")
   525  	}
   526  }
   527  
   528  // Preconditions:
   529  //   - !d.isSynthetic().
   530  //   - d.parent != nil and has been restored.
   531  func (d *dentry) restoreFile(ctx context.Context, opts *vfs.CompleteRestoreOptions) error {
   532  	switch dt := d.impl.(type) {
   533  	case *lisafsDentry:
   534  		inode, err := d.parent.Load().impl.(*lisafsDentry).controlFD.Walk(ctx, d.name)
   535  		if err != nil {
   536  			return err
   537  		}
   538  		return dt.restoreFile(ctx, &inode, opts)
   539  	case *directfsDentry:
   540  		childFD, err := tryOpen(func(flags int) (int, error) {
   541  			return unix.Openat(d.parent.Load().impl.(*directfsDentry).controlFD, d.name, flags, 0)
   542  		})
   543  		if err != nil {
   544  			return err
   545  		}
   546  		return dt.restoreFile(ctx, childFD, opts)
   547  	default:
   548  		panic("unknown dentry implementation")
   549  	}
   550  }
   551  
   552  // doRevalidation calls into r.start's dentry implementation to perform
   553  // revalidation on all the dentries contained in r.
   554  //
   555  // Preconditions:
   556  //   - fs.renameMu must be locked.
   557  //   - InteropModeShared is in effect.
   558  func (r *revalidateState) doRevalidation(ctx context.Context, vfsObj *vfs.VirtualFilesystem, ds **[]*dentry) error {
   559  	// Skip synthetic dentries because there is no actual implementation that can
   560  	// be used to walk the remote filesystem. A start dentry cannot be replaced.
   561  	if r.start.isSynthetic() {
   562  		return nil
   563  	}
   564  	switch r.start.impl.(type) {
   565  	case *lisafsDentry:
   566  		return doRevalidationLisafs(ctx, vfsObj, r, ds)
   567  	case *directfsDentry:
   568  		return doRevalidationDirectfs(ctx, vfsObj, r, ds)
   569  	default:
   570  		panic("unknown dentry implementation")
   571  	}
   572  }