github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/pkg/sentry/fs/gofer/inode.go (about)

     1  // Copyright 2018 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  	"errors"
    19  
    20  	"golang.org/x/sys/unix"
    21  	"github.com/SagerNet/gvisor/pkg/abi/linux"
    22  	"github.com/SagerNet/gvisor/pkg/context"
    23  	"github.com/SagerNet/gvisor/pkg/errors/linuxerr"
    24  	"github.com/SagerNet/gvisor/pkg/fd"
    25  	"github.com/SagerNet/gvisor/pkg/log"
    26  	"github.com/SagerNet/gvisor/pkg/p9"
    27  	"github.com/SagerNet/gvisor/pkg/safemem"
    28  	"github.com/SagerNet/gvisor/pkg/sentry/device"
    29  	"github.com/SagerNet/gvisor/pkg/sentry/fs"
    30  	"github.com/SagerNet/gvisor/pkg/sentry/fs/fdpipe"
    31  	"github.com/SagerNet/gvisor/pkg/sentry/fs/fsutil"
    32  	"github.com/SagerNet/gvisor/pkg/sentry/fs/host"
    33  	"github.com/SagerNet/gvisor/pkg/sentry/memmap"
    34  	"github.com/SagerNet/gvisor/pkg/sync"
    35  	"github.com/SagerNet/gvisor/pkg/syserror"
    36  )
    37  
    38  // inodeOperations implements fs.InodeOperations.
    39  //
    40  // +stateify savable
    41  type inodeOperations struct {
    42  	fsutil.InodeNotVirtual `state:"nosave"`
    43  
    44  	// fileState implements fs.CachedFileObject. It exists
    45  	// to break a circular load dependency between inodeOperations
    46  	// and cachingInodeOps (below).
    47  	fileState *inodeFileState `state:"wait"`
    48  
    49  	// cachingInodeOps implement memmap.Mappable for inodeOperations.
    50  	cachingInodeOps *fsutil.CachingInodeOperations
    51  
    52  	// readdirMu protects readdirCache and concurrent Readdirs.
    53  	readdirMu sync.Mutex `state:"nosave"`
    54  
    55  	// readdirCache is a cache of readdir results in the form of
    56  	// a fs.SortedDentryMap.
    57  	//
    58  	// Starts out as nil, and is initialized under readdirMu lazily;
    59  	// invalidating the cache means setting it to nil.
    60  	readdirCache *fs.SortedDentryMap `state:"nosave"`
    61  }
    62  
    63  // inodeFileState implements fs.CachedFileObject and otherwise fully
    64  // encapsulates state that needs to be manually loaded on restore for
    65  // this file object.
    66  //
    67  // This unfortunate structure exists because fs.CachingInodeOperations
    68  // defines afterLoad and therefore cannot be lazily loaded (to break a
    69  // circular load dependency between it and inodeOperations). Even with
    70  // lazy loading, this approach defines the dependencies between objects
    71  // and the expected load behavior more concretely.
    72  //
    73  // +stateify savable
    74  type inodeFileState struct {
    75  	// s is common file system state for Gofers.
    76  	s *session `state:"wait"`
    77  
    78  	// MultiDeviceKey consists of:
    79  	//
    80  	// * Device:          file system device from a specific gofer.
    81  	// * SecondaryDevice: unique identifier of the attach point.
    82  	// * Inode:           the inode of this resource, unique per Device.=
    83  	//
    84  	// These fields combined enable consistent hashing of virtual inodes
    85  	// on goferDevice.
    86  	key device.MultiDeviceKey `state:"nosave"`
    87  
    88  	// file is the p9 file that contains a single unopened fid.
    89  	file contextFile `state:"nosave"`
    90  
    91  	// sattr caches the stable attributes.
    92  	sattr fs.StableAttr `state:"wait"`
    93  
    94  	// handlesMu protects the below fields.
    95  	handlesMu sync.RWMutex `state:"nosave"`
    96  
    97  	// If readHandles is non-nil, it holds handles that are either read-only or
    98  	// read/write. If writeHandles is non-nil, it holds write-only handles if
    99  	// writeHandlesRW is false, and read/write handles if writeHandlesRW is
   100  	// true.
   101  	//
   102  	// Once readHandles becomes non-nil, it can't be changed until
   103  	// inodeFileState.Release()*, because of a defect in the
   104  	// fsutil.CachedFileObject interface: there's no way for the caller of
   105  	// fsutil.CachedFileObject.FD() to keep the returned FD open, so if we
   106  	// racily replace readHandles after inodeFileState.FD() has returned
   107  	// readHandles.Host.FD(), fsutil.CachingInodeOperations may use a closed
   108  	// FD. writeHandles can be changed if writeHandlesRW is false, since
   109  	// inodeFileState.FD() can't return a write-only FD, but can't be changed
   110  	// if writeHandlesRW is true for the same reason.
   111  	//
   112  	// * There is one notable exception in recreateReadHandles(), where it dup's
   113  	// the FD and invalidates the page cache.
   114  	readHandles    *handles `state:"nosave"`
   115  	writeHandles   *handles `state:"nosave"`
   116  	writeHandlesRW bool     `state:"nosave"`
   117  
   118  	// loading is acquired when the inodeFileState begins an asynchronous
   119  	// load. It releases when the load is complete. Callers that require all
   120  	// state to be available should call waitForLoad() to ensure that.
   121  	loading sync.CrossGoroutineMutex `state:".(struct{})"`
   122  
   123  	// savedUAttr is only allocated during S/R. It points to the save-time
   124  	// unstable attributes and is used to validate restore-time ones.
   125  	//
   126  	// Note that these unstable attributes are only used to detect cross-S/R
   127  	// external file system metadata changes. They may differ from the
   128  	// cached unstable attributes in cachingInodeOps, as that might differ
   129  	// from the external file system attributes if there had been WriteOut
   130  	// failures. S/R is transparent to Sentry and the latter will continue
   131  	// using its cached values after restore.
   132  	savedUAttr *fs.UnstableAttr
   133  
   134  	// hostMappable is created when using 'cacheRemoteRevalidating' to map pages
   135  	// directly from host.
   136  	hostMappable *fsutil.HostMappable
   137  }
   138  
   139  // Release releases file handles.
   140  func (i *inodeFileState) Release(ctx context.Context) {
   141  	i.file.close(ctx)
   142  	if i.readHandles != nil {
   143  		i.readHandles.DecRef()
   144  	}
   145  	if i.writeHandles != nil {
   146  		i.writeHandles.DecRef()
   147  	}
   148  }
   149  
   150  func (i *inodeFileState) canShareHandles() bool {
   151  	// Only share handles for regular files, since for other file types,
   152  	// distinct handles may have special semantics even if they represent the
   153  	// same file. Disable handle sharing for cache policy cacheNone, since this
   154  	// is legacy behavior.
   155  	return fs.IsFile(i.sattr) && i.s.cachePolicy != cacheNone
   156  }
   157  
   158  // Preconditions: i.handlesMu must be locked for writing.
   159  func (i *inodeFileState) setSharedHandlesLocked(flags fs.FileFlags, h *handles) {
   160  	if flags.Read && i.readHandles == nil {
   161  		h.IncRef()
   162  		i.readHandles = h
   163  	}
   164  	if flags.Write {
   165  		if i.writeHandles == nil {
   166  			h.IncRef()
   167  			i.writeHandles = h
   168  			i.writeHandlesRW = flags.Read
   169  		} else if !i.writeHandlesRW && flags.Read {
   170  			// Upgrade i.writeHandles.
   171  			i.writeHandles.DecRef()
   172  			h.IncRef()
   173  			i.writeHandles = h
   174  			i.writeHandlesRW = flags.Read
   175  		}
   176  	}
   177  }
   178  
   179  // getHandles returns a set of handles for a new file using i opened with the
   180  // given flags.
   181  func (i *inodeFileState) getHandles(ctx context.Context, flags fs.FileFlags, cache *fsutil.CachingInodeOperations) (*handles, error) {
   182  	if !i.canShareHandles() {
   183  		return newHandles(ctx, i.s.client, i.file, flags)
   184  	}
   185  
   186  	i.handlesMu.Lock()
   187  	h, invalidate, err := i.getHandlesLocked(ctx, flags)
   188  	i.handlesMu.Unlock()
   189  
   190  	if invalidate {
   191  		cache.NotifyChangeFD()
   192  		if i.hostMappable != nil {
   193  			i.hostMappable.NotifyChangeFD()
   194  		}
   195  	}
   196  
   197  	return h, err
   198  }
   199  
   200  // getHandlesLocked returns a pointer to cached handles and a boolean indicating
   201  // whether previously open read handle was recreated. Host mappings must be
   202  // invalidated if so.
   203  func (i *inodeFileState) getHandlesLocked(ctx context.Context, flags fs.FileFlags) (*handles, bool, error) {
   204  	// Check if we are able to use cached handles.
   205  	if flags.Truncate && p9.VersionSupportsOpenTruncateFlag(i.s.client.Version()) {
   206  		// If we are truncating (and the gofer supports it), then we
   207  		// always need a new handle. Don't return one from the cache.
   208  	} else if flags.Write {
   209  		if i.writeHandles != nil && (i.writeHandlesRW || !flags.Read) {
   210  			// File is opened for writing, and we have cached write
   211  			// handles that we can use.
   212  			i.writeHandles.IncRef()
   213  			return i.writeHandles, false, nil
   214  		}
   215  	} else if i.readHandles != nil {
   216  		// File is opened for reading and we have cached handles.
   217  		i.readHandles.IncRef()
   218  		return i.readHandles, false, nil
   219  	}
   220  
   221  	// Get new handles and cache them for future sharing.
   222  	h, err := newHandles(ctx, i.s.client, i.file, flags)
   223  	if err != nil {
   224  		return nil, false, err
   225  	}
   226  
   227  	// Read handles invalidation is needed if:
   228  	//   - Mount option 'overlayfs_stale_read' is set
   229  	//   - Read handle is open: nothing to invalidate otherwise
   230  	//   - Write handle is not open: file was not open for write and is being open
   231  	//     for write now (will trigger copy up in overlayfs).
   232  	invalidate := false
   233  	if i.s.overlayfsStaleRead && i.readHandles != nil && i.writeHandles == nil && flags.Write {
   234  		if err := i.recreateReadHandles(ctx, h, flags); err != nil {
   235  			return nil, false, err
   236  		}
   237  		invalidate = true
   238  	}
   239  	i.setSharedHandlesLocked(flags, h)
   240  	return h, invalidate, nil
   241  }
   242  
   243  func (i *inodeFileState) recreateReadHandles(ctx context.Context, writer *handles, flags fs.FileFlags) error {
   244  	h := writer
   245  	if !flags.Read {
   246  		// Writer can't be used for read, must create a new handle.
   247  		var err error
   248  		h, err = newHandles(ctx, i.s.client, i.file, fs.FileFlags{Read: true})
   249  		if err != nil {
   250  			return err
   251  		}
   252  		defer h.DecRef()
   253  	}
   254  
   255  	if i.readHandles.Host == nil {
   256  		// If current readHandles doesn't have a host FD, it can simply be replaced.
   257  		i.readHandles.DecRef()
   258  
   259  		h.IncRef()
   260  		i.readHandles = h
   261  		return nil
   262  	}
   263  
   264  	if h.Host == nil {
   265  		// Current read handle has a host FD and can't be replaced with one that
   266  		// doesn't, because it breaks fsutil.CachedFileObject.FD() contract.
   267  		log.Warningf("Read handle can't be invalidated, reads may return stale data")
   268  		return nil
   269  	}
   270  
   271  	// Due to a defect in the fsutil.CachedFileObject interface,
   272  	// readHandles.Host.FD() may be used outside locks, making it impossible to
   273  	// reliably close it. To workaround it, we dup the new FD into the old one, so
   274  	// operations on the old will see the new data. Then, make the new handle take
   275  	// ownereship of the old FD and mark the old readHandle to not close the FD
   276  	// when done.
   277  	if err := unix.Dup3(h.Host.FD(), i.readHandles.Host.FD(), unix.O_CLOEXEC); err != nil {
   278  		return err
   279  	}
   280  
   281  	h.Host.Close()
   282  	h.Host = fd.New(i.readHandles.Host.FD())
   283  	i.readHandles.isHostBorrowed = true
   284  	i.readHandles.DecRef()
   285  
   286  	h.IncRef()
   287  	i.readHandles = h
   288  	return nil
   289  }
   290  
   291  // ReadToBlocksAt implements fsutil.CachedFileObject.ReadToBlocksAt.
   292  func (i *inodeFileState) ReadToBlocksAt(ctx context.Context, dsts safemem.BlockSeq, offset uint64) (uint64, error) {
   293  	i.handlesMu.RLock()
   294  	n, err := i.readHandles.readWriterAt(ctx, int64(offset)).ReadToBlocks(dsts)
   295  	i.handlesMu.RUnlock()
   296  	return n, err
   297  }
   298  
   299  // WriteFromBlocksAt implements fsutil.CachedFileObject.WriteFromBlocksAt.
   300  func (i *inodeFileState) WriteFromBlocksAt(ctx context.Context, srcs safemem.BlockSeq, offset uint64) (uint64, error) {
   301  	i.handlesMu.RLock()
   302  	n, err := i.writeHandles.readWriterAt(ctx, int64(offset)).WriteFromBlocks(srcs)
   303  	i.handlesMu.RUnlock()
   304  	return n, err
   305  }
   306  
   307  // SetMaskedAttributes implements fsutil.CachedFileObject.SetMaskedAttributes.
   308  func (i *inodeFileState) SetMaskedAttributes(ctx context.Context, mask fs.AttrMask, attr fs.UnstableAttr, forceSetTimestamps bool) error {
   309  	if i.skipSetAttr(mask, forceSetTimestamps) {
   310  		return nil
   311  	}
   312  	as, ans := attr.AccessTime.Unix()
   313  	ms, mns := attr.ModificationTime.Unix()
   314  	// An update of status change time is implied by mask.AccessTime
   315  	// or mask.ModificationTime. Updating status change time to a
   316  	// time earlier than the system time is not possible.
   317  	return i.file.setAttr(
   318  		ctx,
   319  		p9.SetAttrMask{
   320  			Permissions:        mask.Perms,
   321  			Size:               mask.Size,
   322  			UID:                mask.UID,
   323  			GID:                mask.GID,
   324  			ATime:              mask.AccessTime,
   325  			ATimeNotSystemTime: true,
   326  			MTime:              mask.ModificationTime,
   327  			MTimeNotSystemTime: true,
   328  		}, p9.SetAttr{
   329  			Permissions:      p9.FileMode(attr.Perms.LinuxMode()),
   330  			UID:              p9.UID(attr.Owner.UID),
   331  			GID:              p9.GID(attr.Owner.GID),
   332  			Size:             uint64(attr.Size),
   333  			ATimeSeconds:     uint64(as),
   334  			ATimeNanoSeconds: uint64(ans),
   335  			MTimeSeconds:     uint64(ms),
   336  			MTimeNanoSeconds: uint64(mns),
   337  		})
   338  }
   339  
   340  // skipSetAttr checks if attribute change can be skipped. It can be skipped
   341  // when:
   342  //   - Mask is empty
   343  //   - Mask contains only attributes that cannot be set in the gofer
   344  //   - forceSetTimestamps is false and mask contains only atime and/or mtime
   345  //     and host FD exists
   346  //
   347  // Updates to atime and mtime can be skipped because cached value will be
   348  // "close enough" to host value, given that operation went directly to host FD.
   349  // Skipping atime updates is particularly important to reduce the number of
   350  // operations sent to the Gofer for readonly files.
   351  func (i *inodeFileState) skipSetAttr(mask fs.AttrMask, forceSetTimestamps bool) bool {
   352  	// First remove attributes that cannot be updated.
   353  	cpy := mask
   354  	cpy.Type = false
   355  	cpy.DeviceID = false
   356  	cpy.InodeID = false
   357  	cpy.BlockSize = false
   358  	cpy.Usage = false
   359  	cpy.Links = false
   360  	if cpy.Empty() {
   361  		return true
   362  	}
   363  
   364  	// Then check if more than just atime and mtime is being set.
   365  	cpy.AccessTime = false
   366  	cpy.ModificationTime = false
   367  	if !cpy.Empty() {
   368  		return false
   369  	}
   370  
   371  	// If forceSetTimestamps was passed, then we cannot skip.
   372  	if forceSetTimestamps {
   373  		return false
   374  	}
   375  
   376  	// Skip if we have a host FD.
   377  	i.handlesMu.RLock()
   378  	defer i.handlesMu.RUnlock()
   379  	return (i.readHandles != nil && i.readHandles.Host != nil) ||
   380  		(i.writeHandles != nil && i.writeHandles.Host != nil)
   381  }
   382  
   383  // Sync implements fsutil.CachedFileObject.Sync.
   384  func (i *inodeFileState) Sync(ctx context.Context) error {
   385  	i.handlesMu.RLock()
   386  	defer i.handlesMu.RUnlock()
   387  	if i.writeHandles == nil {
   388  		return nil
   389  	}
   390  	return i.writeHandles.File.fsync(ctx)
   391  }
   392  
   393  // FD implements fsutil.CachedFileObject.FD.
   394  func (i *inodeFileState) FD() int {
   395  	i.handlesMu.RLock()
   396  	defer i.handlesMu.RUnlock()
   397  	if i.writeHandlesRW && i.writeHandles != nil && i.writeHandles.Host != nil {
   398  		return int(i.writeHandles.Host.FD())
   399  	}
   400  	if i.readHandles != nil && i.readHandles.Host != nil {
   401  		return int(i.readHandles.Host.FD())
   402  	}
   403  	return -1
   404  }
   405  
   406  // waitForLoad makes sure any restore-issued loading is done.
   407  func (i *inodeFileState) waitForLoad() {
   408  	// This is not a no-op. The loading mutex is hold upon restore until
   409  	// all loading actions are done.
   410  	i.loading.Lock()
   411  	i.loading.Unlock()
   412  }
   413  
   414  func (i *inodeFileState) unstableAttr(ctx context.Context) (fs.UnstableAttr, error) {
   415  	_, valid, pattr, err := getattr(ctx, i.file)
   416  	if err != nil {
   417  		return fs.UnstableAttr{}, err
   418  	}
   419  	return unstable(ctx, valid, pattr, i.s.mounter, i.s.client), nil
   420  }
   421  
   422  func (i *inodeFileState) Allocate(ctx context.Context, offset, length int64) error {
   423  	i.handlesMu.RLock()
   424  	defer i.handlesMu.RUnlock()
   425  
   426  	// No options are supported for now.
   427  	mode := p9.AllocateMode{}
   428  	return i.writeHandles.File.allocate(ctx, mode, uint64(offset), uint64(length))
   429  }
   430  
   431  // session extracts the gofer's session from the MountSource.
   432  func (i *inodeOperations) session() *session {
   433  	return i.fileState.s
   434  }
   435  
   436  // Release implements fs.InodeOperations.Release.
   437  func (i *inodeOperations) Release(ctx context.Context) {
   438  	i.cachingInodeOps.Release()
   439  
   440  	// Releasing the fileState may make RPCs to the gofer. There is
   441  	// no need to wait for those to return, so we can do this
   442  	// asynchronously.
   443  	//
   444  	// We use AsyncWithContext to avoid needing to allocate an extra
   445  	// anonymous function on the heap. We must use background context
   446  	// because the async work cannot happen on the task context.
   447  	fs.AsyncWithContext(context.Background(), i.fileState.Release)
   448  }
   449  
   450  // Mappable implements fs.InodeOperations.Mappable.
   451  func (i *inodeOperations) Mappable(inode *fs.Inode) memmap.Mappable {
   452  	if i.session().cachePolicy.useCachingInodeOps(inode) {
   453  		return i.cachingInodeOps
   454  	}
   455  	// This check is necessary because it's returning an interface type.
   456  	if i.fileState.hostMappable != nil {
   457  		return i.fileState.hostMappable
   458  	}
   459  	return nil
   460  }
   461  
   462  // UnstableAttr implements fs.InodeOperations.UnstableAttr.
   463  func (i *inodeOperations) UnstableAttr(ctx context.Context, inode *fs.Inode) (fs.UnstableAttr, error) {
   464  	if i.session().cachePolicy.cacheUAttrs(inode) {
   465  		return i.cachingInodeOps.UnstableAttr(ctx, inode)
   466  	}
   467  	return i.fileState.unstableAttr(ctx)
   468  }
   469  
   470  // Check implements fs.InodeOperations.Check.
   471  func (i *inodeOperations) Check(ctx context.Context, inode *fs.Inode, p fs.PermMask) bool {
   472  	return fs.ContextCanAccessFile(ctx, inode, p)
   473  }
   474  
   475  // GetFile implements fs.InodeOperations.GetFile.
   476  func (i *inodeOperations) GetFile(ctx context.Context, d *fs.Dirent, flags fs.FileFlags) (*fs.File, error) {
   477  	switch d.Inode.StableAttr.Type {
   478  	case fs.Socket:
   479  		if i.session().overrides != nil {
   480  			return nil, linuxerr.ENXIO
   481  		}
   482  		return i.getFileSocket(ctx, d, flags)
   483  	case fs.Pipe:
   484  		return i.getFilePipe(ctx, d, flags)
   485  	default:
   486  		return i.getFileDefault(ctx, d, flags)
   487  	}
   488  }
   489  
   490  func (i *inodeOperations) getFileSocket(ctx context.Context, d *fs.Dirent, flags fs.FileFlags) (*fs.File, error) {
   491  	f, err := i.fileState.file.connect(ctx, p9.AnonymousSocket)
   492  	if err != nil {
   493  		return nil, unix.EIO
   494  	}
   495  	fsf, err := host.NewSocketWithDirent(ctx, d, f, flags)
   496  	if err != nil {
   497  		f.Close()
   498  		return nil, err
   499  	}
   500  	return fsf, nil
   501  }
   502  
   503  func (i *inodeOperations) getFilePipe(ctx context.Context, d *fs.Dirent, flags fs.FileFlags) (*fs.File, error) {
   504  	// Try to open as a host pipe; if that doesn't work, handle it normally.
   505  	pipeOps, err := fdpipe.Open(ctx, i, flags)
   506  	if err == errNotHostFile {
   507  		return i.getFileDefault(ctx, d, flags)
   508  	}
   509  	if err != nil {
   510  		return nil, err
   511  	}
   512  	return fs.NewFile(ctx, d, flags, pipeOps), nil
   513  }
   514  
   515  // errNotHostFile indicates that the file is not a host file.
   516  var errNotHostFile = errors.New("not a host file")
   517  
   518  // NonBlockingOpen implements fdpipe.NonBlockingOpener for opening host named pipes.
   519  func (i *inodeOperations) NonBlockingOpen(ctx context.Context, p fs.PermMask) (*fd.FD, error) {
   520  	i.fileState.waitForLoad()
   521  
   522  	// Get a cloned fid which we will open.
   523  	_, newFile, err := i.fileState.file.walk(ctx, nil)
   524  	if err != nil {
   525  		log.Warningf("Open Walk failed: %v", err)
   526  		return nil, err
   527  	}
   528  	defer newFile.close(ctx)
   529  
   530  	flags, err := openFlagsFromPerms(p)
   531  	if err != nil {
   532  		log.Warningf("Open flags %s parsing failed: %v", p, err)
   533  		return nil, err
   534  	}
   535  	hostFile, _, _, err := newFile.open(ctx, flags)
   536  	// If the host file returned is nil and the error is nil,
   537  	// then this was never a host file to begin with, and should
   538  	// be treated like a remote file.
   539  	if hostFile == nil && err == nil {
   540  		return nil, errNotHostFile
   541  	}
   542  	return hostFile, err
   543  }
   544  
   545  func (i *inodeOperations) getFileDefault(ctx context.Context, d *fs.Dirent, flags fs.FileFlags) (*fs.File, error) {
   546  	h, err := i.fileState.getHandles(ctx, flags, i.cachingInodeOps)
   547  	if err != nil {
   548  		return nil, err
   549  	}
   550  	return NewFile(ctx, d, d.BaseName(), flags, i, h), nil
   551  }
   552  
   553  // SetPermissions implements fs.InodeOperations.SetPermissions.
   554  func (i *inodeOperations) SetPermissions(ctx context.Context, inode *fs.Inode, p fs.FilePermissions) bool {
   555  	if i.session().cachePolicy.cacheUAttrs(inode) {
   556  		return i.cachingInodeOps.SetPermissions(ctx, inode, p)
   557  	}
   558  
   559  	mask := p9.SetAttrMask{Permissions: true}
   560  	pattr := p9.SetAttr{Permissions: p9.FileMode(p.LinuxMode())}
   561  	// Execute the chmod.
   562  	return i.fileState.file.setAttr(ctx, mask, pattr) == nil
   563  }
   564  
   565  // SetOwner implements fs.InodeOperations.SetOwner.
   566  func (i *inodeOperations) SetOwner(ctx context.Context, inode *fs.Inode, owner fs.FileOwner) error {
   567  	// Save the roundtrip.
   568  	if !owner.UID.Ok() && !owner.GID.Ok() {
   569  		return nil
   570  	}
   571  
   572  	if i.session().cachePolicy.cacheUAttrs(inode) {
   573  		return i.cachingInodeOps.SetOwner(ctx, inode, owner)
   574  	}
   575  
   576  	var mask p9.SetAttrMask
   577  	var attr p9.SetAttr
   578  	if owner.UID.Ok() {
   579  		mask.UID = true
   580  		attr.UID = p9.UID(owner.UID)
   581  	}
   582  	if owner.GID.Ok() {
   583  		mask.GID = true
   584  		attr.GID = p9.GID(owner.GID)
   585  	}
   586  	return i.fileState.file.setAttr(ctx, mask, attr)
   587  }
   588  
   589  // SetTimestamps implements fs.InodeOperations.SetTimestamps.
   590  func (i *inodeOperations) SetTimestamps(ctx context.Context, inode *fs.Inode, ts fs.TimeSpec) error {
   591  	if i.session().cachePolicy.cacheUAttrs(inode) {
   592  		return i.cachingInodeOps.SetTimestamps(ctx, inode, ts)
   593  	}
   594  
   595  	return utimes(ctx, i.fileState.file, ts)
   596  }
   597  
   598  // Truncate implements fs.InodeOperations.Truncate.
   599  func (i *inodeOperations) Truncate(ctx context.Context, inode *fs.Inode, length int64) error {
   600  	// This can only be called for files anyway.
   601  	if i.session().cachePolicy.useCachingInodeOps(inode) {
   602  		return i.cachingInodeOps.Truncate(ctx, inode, length)
   603  	}
   604  
   605  	uattr, err := i.fileState.unstableAttr(ctx)
   606  	if err != nil {
   607  		return err
   608  	}
   609  
   610  	if i.session().cachePolicy == cacheRemoteRevalidating {
   611  		return i.fileState.hostMappable.Truncate(ctx, length, uattr)
   612  	}
   613  
   614  	mask := p9.SetAttrMask{Size: true}
   615  	attr := p9.SetAttr{Size: uint64(length)}
   616  	if uattr.Perms.HasSetUIDOrGID() {
   617  		mask.Permissions = true
   618  		uattr.Perms.DropSetUIDAndMaybeGID()
   619  		attr.Permissions = p9.FileMode(uattr.Perms.LinuxMode())
   620  	}
   621  
   622  	return i.fileState.file.setAttr(ctx, mask, attr)
   623  }
   624  
   625  // GetXattr implements fs.InodeOperations.GetXattr.
   626  func (i *inodeOperations) GetXattr(ctx context.Context, _ *fs.Inode, name string, size uint64) (string, error) {
   627  	return i.fileState.file.getXattr(ctx, name, size)
   628  }
   629  
   630  // SetXattr implements fs.InodeOperations.SetXattr.
   631  func (i *inodeOperations) SetXattr(ctx context.Context, _ *fs.Inode, name string, value string, flags uint32) error {
   632  	return i.fileState.file.setXattr(ctx, name, value, flags)
   633  }
   634  
   635  // ListXattr implements fs.InodeOperations.ListXattr.
   636  func (i *inodeOperations) ListXattr(ctx context.Context, _ *fs.Inode, size uint64) (map[string]struct{}, error) {
   637  	return i.fileState.file.listXattr(ctx, size)
   638  }
   639  
   640  // RemoveXattr implements fs.InodeOperations.RemoveXattr.
   641  func (i *inodeOperations) RemoveXattr(ctx context.Context, _ *fs.Inode, name string) error {
   642  	return i.fileState.file.removeXattr(ctx, name)
   643  }
   644  
   645  // Allocate implements fs.InodeOperations.Allocate.
   646  func (i *inodeOperations) Allocate(ctx context.Context, inode *fs.Inode, offset, length int64) error {
   647  	// This can only be called for files anyway.
   648  	if i.session().cachePolicy.useCachingInodeOps(inode) {
   649  		return i.cachingInodeOps.Allocate(ctx, offset, length)
   650  	}
   651  	if i.session().cachePolicy == cacheRemoteRevalidating {
   652  		return i.fileState.hostMappable.Allocate(ctx, offset, length)
   653  	}
   654  
   655  	// No options are supported for now.
   656  	mode := p9.AllocateMode{}
   657  	return i.fileState.file.allocate(ctx, mode, uint64(offset), uint64(length))
   658  }
   659  
   660  // WriteOut implements fs.InodeOperations.WriteOut.
   661  func (i *inodeOperations) WriteOut(ctx context.Context, inode *fs.Inode) error {
   662  	if inode.MountSource.Flags.ReadOnly || !i.session().cachePolicy.cacheUAttrs(inode) {
   663  		return nil
   664  	}
   665  
   666  	return i.cachingInodeOps.WriteOut(ctx, inode)
   667  }
   668  
   669  // Readlink implements fs.InodeOperations.Readlink.
   670  func (i *inodeOperations) Readlink(ctx context.Context, inode *fs.Inode) (string, error) {
   671  	if !fs.IsSymlink(inode.StableAttr) {
   672  		return "", unix.ENOLINK
   673  	}
   674  	return i.fileState.file.readlink(ctx)
   675  }
   676  
   677  // Getlink implementfs fs.InodeOperations.Getlink.
   678  func (i *inodeOperations) Getlink(context.Context, *fs.Inode) (*fs.Dirent, error) {
   679  	if !fs.IsSymlink(i.fileState.sattr) {
   680  		return nil, linuxerr.ENOLINK
   681  	}
   682  	return nil, fs.ErrResolveViaReadlink
   683  }
   684  
   685  // StatFS makes a StatFS request.
   686  func (i *inodeOperations) StatFS(ctx context.Context) (fs.Info, error) {
   687  	fsstat, err := i.fileState.file.statFS(ctx)
   688  	if err != nil {
   689  		return fs.Info{}, err
   690  	}
   691  
   692  	info := fs.Info{
   693  		// This is primarily for distinguishing a gofer file system in
   694  		// tests. Testing is important, so instead of defining
   695  		// something completely random, use a standard value.
   696  		Type:        linux.V9FS_MAGIC,
   697  		TotalBlocks: fsstat.Blocks,
   698  		FreeBlocks:  fsstat.BlocksFree,
   699  		TotalFiles:  fsstat.Files,
   700  		FreeFiles:   fsstat.FilesFree,
   701  	}
   702  
   703  	// If blocks available is non-zero, prefer that.
   704  	if fsstat.BlocksAvailable != 0 {
   705  		info.FreeBlocks = fsstat.BlocksAvailable
   706  	}
   707  
   708  	return info, nil
   709  }
   710  
   711  func (i *inodeOperations) configureMMap(file *fs.File, opts *memmap.MMapOpts) error {
   712  	if i.session().cachePolicy.useCachingInodeOps(file.Dirent.Inode) {
   713  		return fsutil.GenericConfigureMMap(file, i.cachingInodeOps, opts)
   714  	}
   715  	if i.fileState.hostMappable != nil {
   716  		return fsutil.GenericConfigureMMap(file, i.fileState.hostMappable, opts)
   717  	}
   718  	return linuxerr.ENODEV
   719  }
   720  
   721  func init() {
   722  	syserror.AddErrorUnwrapper(func(err error) (unix.Errno, bool) {
   723  		if _, ok := err.(p9.ErrSocket); ok {
   724  			// Treat as an I/O error.
   725  			return unix.EIO, true
   726  		}
   727  		return 0, false
   728  	})
   729  }
   730  
   731  // AddLink implements InodeOperations.AddLink, but is currently a noop.
   732  func (*inodeOperations) AddLink() {}
   733  
   734  // DropLink implements InodeOperations.DropLink, but is currently a noop.
   735  func (*inodeOperations) DropLink() {}
   736  
   737  // NotifyStatusChange implements fs.InodeOperations.NotifyStatusChange.
   738  func (i *inodeOperations) NotifyStatusChange(ctx context.Context) {}