github.com/ttpreport/gvisor-ligolo@v0.0.0-20240123134145-a858404967ba/pkg/sentry/fsimpl/fuse/inode.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 fuse
    16  
    17  import (
    18  	"fmt"
    19  	"sync"
    20  	"time"
    21  
    22  	"github.com/ttpreport/gvisor-ligolo/pkg/abi/linux"
    23  	"github.com/ttpreport/gvisor-ligolo/pkg/atomicbitops"
    24  	"github.com/ttpreport/gvisor-ligolo/pkg/context"
    25  	"github.com/ttpreport/gvisor-ligolo/pkg/errors/linuxerr"
    26  	"github.com/ttpreport/gvisor-ligolo/pkg/hostarch"
    27  	"github.com/ttpreport/gvisor-ligolo/pkg/marshal"
    28  	"github.com/ttpreport/gvisor-ligolo/pkg/marshal/primitive"
    29  	"github.com/ttpreport/gvisor-ligolo/pkg/sentry/fsimpl/kernfs"
    30  	"github.com/ttpreport/gvisor-ligolo/pkg/sentry/kernel"
    31  	"github.com/ttpreport/gvisor-ligolo/pkg/sentry/kernel/auth"
    32  	"github.com/ttpreport/gvisor-ligolo/pkg/sentry/vfs"
    33  )
    34  
    35  // +stateify savable
    36  type fileHandle struct {
    37  	new    bool
    38  	handle uint64
    39  	flags  uint32
    40  }
    41  
    42  // inode implements kernfs.Inode.
    43  //
    44  // +stateify savable
    45  type inode struct {
    46  	inodeRefs
    47  	kernfs.InodeAlwaysValid
    48  	kernfs.InodeNotAnonymous
    49  	kernfs.InodeNotSymlink
    50  	kernfs.InodeWatches
    51  	kernfs.OrderedChildren
    52  	kernfs.CachedMappable
    53  
    54  	// the owning filesystem. fs is immutable.
    55  	fs *filesystem
    56  
    57  	// nodeID is a unique id which identifies the inode between userspace
    58  	// and the sentry. Immutable.
    59  	nodeID uint64
    60  
    61  	// attrVersion is the version of the last attribute change.
    62  	attrVersion atomicbitops.Uint64
    63  
    64  	// attrTime is the time until the attributes are valid.
    65  	attrTime uint64
    66  
    67  	// link is result of following a symbolic link.
    68  	link string
    69  
    70  	// fh caches the file handle returned by the server from a FUSE_CREATE request
    71  	// so we don't have to send a separate FUSE_OPEN request.
    72  	fh fileHandle
    73  
    74  	locks   vfs.FileLocks
    75  	watches vfs.Watches
    76  
    77  	// attrMu protects the attributes of this inode.
    78  	attrMu sync.Mutex
    79  
    80  	// +checklocks:attrMu
    81  	ino atomicbitops.Uint64 // Stat data, not accessed for path walking.
    82  	// +checklocks:attrMu
    83  	uid atomicbitops.Uint32 // auth.KUID, but stored as raw uint32 for sync/atomic.
    84  	// +checklocks:attrMu
    85  	gid atomicbitops.Uint32 // auth.KGID, but...
    86  	// +checklocks:attrMu
    87  	mode atomicbitops.Uint32 // File type and mode.
    88  
    89  	// Timestamps in nanoseconds from the unix epoch.
    90  	// +checklocks:attrMu
    91  	atime atomicbitops.Int64
    92  	// +checklocks:attrMu
    93  	mtime atomicbitops.Int64
    94  	// +checklocks:attrMu
    95  	ctime atomicbitops.Int64
    96  
    97  	// +checklocks:attrMu
    98  	size atomicbitops.Uint64
    99  
   100  	// nlink counts the number of hard links to this inode. It's updated and
   101  	// accessed used atomic operations but not protected by attrMu.
   102  	nlink atomicbitops.Uint32
   103  
   104  	// +checklocks:attrMu
   105  	blockSize atomicbitops.Uint32 // 0 if unknown.
   106  }
   107  
   108  func blockerFromContext(ctx context.Context) context.Blocker {
   109  	kernelTask := kernel.TaskFromContext(ctx)
   110  	if kernelTask == nil {
   111  		return ctx
   112  	}
   113  	return kernelTask
   114  }
   115  
   116  func pidFromContext(ctx context.Context) uint32 {
   117  	kernelTask := kernel.TaskFromContext(ctx)
   118  	if kernelTask == nil {
   119  		return 0
   120  	}
   121  	return uint32(kernelTask.ThreadID())
   122  }
   123  
   124  func umaskFromContext(ctx context.Context) uint32 {
   125  	kernelTask := kernel.TaskFromContext(ctx)
   126  	umask := uint32(0)
   127  	if kernelTask != nil {
   128  		umask = uint32(kernelTask.FSContext().Umask())
   129  	}
   130  	return umask
   131  }
   132  
   133  func (i *inode) Mode() linux.FileMode {
   134  	i.attrMu.Lock()
   135  	defer i.attrMu.Unlock()
   136  	return i.filemode()
   137  }
   138  
   139  func (i *inode) UID() auth.KUID {
   140  	i.attrMu.Lock()
   141  	defer i.attrMu.Unlock()
   142  	return auth.KUID(i.uid.Load())
   143  }
   144  
   145  func (i *inode) GID() auth.KGID {
   146  	i.attrMu.Lock()
   147  	defer i.attrMu.Unlock()
   148  	return auth.KGID(i.gid.Load())
   149  }
   150  
   151  // +checklocks:i.attrMu
   152  func (i *inode) filemode() linux.FileMode {
   153  	return linux.FileMode(i.mode.Load())
   154  }
   155  
   156  // touchCMTime updates the ctime and mtime attributes to be the current time.
   157  //
   158  // +checklocks:i.attrMu
   159  func (i *inode) touchCMtime() {
   160  	now := i.fs.clock.Now().Nanoseconds()
   161  	i.mtime.Store(now)
   162  	i.ctime.Store(now)
   163  }
   164  
   165  // touchAtime updates the atime attribut to be the current time.
   166  //
   167  // +checklocks:i.attrMu
   168  func (i *inode) touchAtime() {
   169  	i.atime.Store(i.fs.clock.Now().Nanoseconds())
   170  }
   171  
   172  // +checklocks:i.attrMu
   173  func (i *inode) init(creds *auth.Credentials, devMajor, devMinor uint32, nodeid uint64, mode linux.FileMode, nlink uint32) {
   174  	if mode.FileType() == 0 {
   175  		panic(fmt.Sprintf("No file type specified in 'mode' for InodeAttrs.Init(): mode=0%o", mode))
   176  	}
   177  
   178  	i.nodeID = nodeid
   179  	i.ino.Store(nodeid)
   180  	i.mode.Store(uint32(mode))
   181  	i.uid.Store(uint32(creds.EffectiveKUID))
   182  	i.gid.Store(uint32(creds.EffectiveKGID))
   183  	i.nlink.Store(nlink)
   184  	i.blockSize.Store(hostarch.PageSize)
   185  
   186  	now := i.fs.clock.Now().Nanoseconds()
   187  	i.atime.Store(now)
   188  	i.mtime.Store(now)
   189  	i.ctime.Store(now)
   190  }
   191  
   192  // CheckPermissions implements kernfs.Inode.CheckPermissions.
   193  func (i *inode) CheckPermissions(ctx context.Context, creds *auth.Credentials, ats vfs.AccessTypes) error {
   194  	// Since FUSE operations are ultimately backed by a userspace process (the
   195  	// fuse daemon), allowing a process to call into fusefs grants the daemon
   196  	// ptrace-like capabilities over the calling process. Because of this, by
   197  	// default FUSE only allows the mount owner to interact with the
   198  	// filesystem. This explicitly excludes setuid/setgid processes.
   199  	//
   200  	// This behaviour can be overriden with the 'allow_other' mount option.
   201  	//
   202  	// See fs/fuse/dir.c:fuse_allow_current_process() in Linux.
   203  	if !i.fs.opts.allowOther {
   204  		if creds.RealKUID != i.fs.opts.uid ||
   205  			creds.EffectiveKUID != i.fs.opts.uid ||
   206  			creds.SavedKUID != i.fs.opts.uid ||
   207  			creds.RealKGID != i.fs.opts.gid ||
   208  			creds.EffectiveKGID != i.fs.opts.gid ||
   209  			creds.SavedKGID != i.fs.opts.gid {
   210  			return linuxerr.EACCES
   211  		}
   212  	}
   213  
   214  	// By default, fusefs delegates all permission checks to the server.
   215  	// However, standard unix permission checks can be enabled with the
   216  	// default_permissions mount option.
   217  	i.attrMu.Lock()
   218  	defer i.attrMu.Unlock()
   219  	refreshed := false
   220  	opts := vfs.StatOptions{Mask: linux.STATX_MODE | linux.STATX_UID | linux.STATX_GID}
   221  	if i.fs.opts.defaultPermissions || (ats.MayExec() && i.filemode().FileType() == linux.S_IFREG) {
   222  		if uint64(i.fs.clock.Now().Nanoseconds()) > i.attrTime {
   223  			refreshed = true
   224  			if _, err := i.getAttr(ctx, i.fs.VFSFilesystem(), opts, 0, 0); err != nil {
   225  				return err
   226  			}
   227  		}
   228  	}
   229  
   230  	if i.fs.opts.defaultPermissions || (ats.MayExec() && i.filemode().FileType() == linux.S_IFREG) {
   231  		err := vfs.GenericCheckPermissions(creds, ats, linux.FileMode(i.mode.Load()), auth.KUID(i.uid.Load()), auth.KGID(i.gid.Load()))
   232  		if linuxerr.Equals(linuxerr.EACCES, err) && !refreshed {
   233  			if _, err := i.getAttr(ctx, i.fs.VFSFilesystem(), opts, 0, 0); err != nil {
   234  				return err
   235  			}
   236  			return vfs.GenericCheckPermissions(creds, ats, linux.FileMode(i.mode.Load()), auth.KUID(i.uid.Load()), auth.KGID(i.gid.Load()))
   237  		}
   238  		return err
   239  	} else if ats.MayRead() || ats.MayWrite() || ats.MayExec() {
   240  		in := linux.FUSEAccessIn{Mask: uint32(ats)}
   241  		req := i.fs.conn.NewRequest(auth.CredentialsFromContext(ctx), pidFromContext(ctx), i.nodeID, linux.FUSE_ACCESS, &in)
   242  		res, err := i.fs.conn.Call(ctx, req)
   243  		if err != nil {
   244  			return err
   245  		}
   246  		return res.Error()
   247  	}
   248  	return nil
   249  }
   250  
   251  // Open implements kernfs.Inode.Open.
   252  func (i *inode) Open(ctx context.Context, rp *vfs.ResolvingPath, d *kernfs.Dentry, opts vfs.OpenOptions) (*vfs.FileDescription, error) {
   253  	opts.Flags &= linux.O_ACCMODE | linux.O_CREAT | linux.O_EXCL | linux.O_TRUNC |
   254  		linux.O_DIRECTORY | linux.O_NOFOLLOW | linux.O_NONBLOCK | linux.O_NOCTTY |
   255  		linux.O_APPEND | linux.O_DIRECT
   256  	i.attrMu.Lock()
   257  	defer i.attrMu.Unlock()
   258  	if opts.Flags&linux.O_LARGEFILE == 0 && i.size.Load() > linux.MAX_NON_LFS {
   259  		return nil, linuxerr.EOVERFLOW
   260  	}
   261  
   262  	var (
   263  		fd     *fileDescription
   264  		fdImpl vfs.FileDescriptionImpl
   265  		opcode linux.FUSEOpcode
   266  	)
   267  	switch i.filemode().FileType() {
   268  	case linux.S_IFREG:
   269  		regularFD := &regularFileFD{}
   270  		fd = &(regularFD.fileDescription)
   271  		fdImpl = regularFD
   272  		opcode = linux.FUSE_OPEN
   273  	case linux.S_IFDIR:
   274  		if opts.Flags&linux.O_CREAT != 0 {
   275  			return nil, linuxerr.EISDIR
   276  		}
   277  		if ats := vfs.AccessTypesForOpenFlags(&opts); ats.MayWrite() {
   278  			return nil, linuxerr.EISDIR
   279  		}
   280  		if opts.Flags&linux.O_DIRECT != 0 {
   281  			return nil, linuxerr.EINVAL
   282  		}
   283  		directoryFD := &directoryFD{}
   284  		fd = &(directoryFD.fileDescription)
   285  		fdImpl = directoryFD
   286  		opcode = linux.FUSE_OPENDIR
   287  	case linux.S_IFLNK:
   288  		return nil, linuxerr.ELOOP
   289  	}
   290  
   291  	fd.LockFD.Init(&i.locks)
   292  	// FOPEN_KEEP_CACHE is the default flag for noOpen.
   293  	fd.OpenFlag = linux.FOPEN_KEEP_CACHE
   294  
   295  	if i.fh.new {
   296  		fd.OpenFlag = i.fh.flags
   297  		fd.Fh = i.fh.handle
   298  		i.fh.new = false
   299  		// Only send an open request when the FUSE server supports open or is
   300  		// opening a directory.
   301  	} else if !i.fs.conn.noOpen || i.filemode().IsDir() {
   302  		in := linux.FUSEOpenIn{Flags: opts.Flags & ^uint32(linux.O_CREAT|linux.O_EXCL|linux.O_NOCTTY)}
   303  		// Truncating with SETATTR instead of O_TRUNC, so clear the flag.
   304  		if !i.fs.conn.atomicOTrunc {
   305  			in.Flags &= ^uint32(linux.O_TRUNC)
   306  		}
   307  
   308  		req := i.fs.conn.NewRequest(auth.CredentialsFromContext(ctx), pidFromContext(ctx), i.nodeID, opcode, &in)
   309  		res, err := i.fs.conn.Call(ctx, req)
   310  		if err != nil {
   311  			return nil, err
   312  		}
   313  		if err := res.Error(); err != nil {
   314  			if linuxerr.Equals(linuxerr.ENOSYS, err) && !i.filemode().IsDir() {
   315  				i.fs.conn.noOpen = true
   316  			} else {
   317  				return nil, err
   318  			}
   319  		} else {
   320  			out := linux.FUSEOpenOut{}
   321  			if err := res.UnmarshalPayload(&out); err != nil {
   322  				return nil, err
   323  			}
   324  			fd.OpenFlag = out.OpenFlag
   325  			fd.Fh = out.Fh
   326  		}
   327  	}
   328  	if i.filemode().IsDir() {
   329  		fd.OpenFlag &= ^uint32(linux.FOPEN_DIRECT_IO)
   330  	}
   331  
   332  	// TODO(gvisor.dev/issue/3234): invalidate mmap after implemented it for FUSE Inode
   333  	fd.DirectIO = fd.OpenFlag&linux.FOPEN_DIRECT_IO != 0
   334  	fdOptions := &vfs.FileDescriptionOptions{}
   335  	if fd.OpenFlag&linux.FOPEN_NONSEEKABLE != 0 {
   336  		fdOptions.DenyPRead = true
   337  		fdOptions.DenyPWrite = true
   338  		fd.Nonseekable = true
   339  	}
   340  
   341  	// If atomicOTrunc and O_TRUNC are set, just update the inode's version number
   342  	// and set its size to 0 since the truncation is handled by the FUSE daemon.
   343  	// Otherwise send a separate SETATTR to truncate the file size.
   344  	if opts.Flags&linux.O_TRUNC != 0 && i.filemode().FileType() == linux.S_IFREG {
   345  		if i.fs.conn.atomicOTrunc {
   346  			i.fs.conn.mu.Lock()
   347  			i.attrVersion.Store(i.fs.conn.attributeVersion.Add(1))
   348  			i.fs.conn.mu.Unlock()
   349  			i.size.Store(0)
   350  			i.touchCMtime()
   351  		} else {
   352  			opts := vfs.SetStatOptions{Stat: linux.Statx{Size: 0, Mask: linux.STATX_SIZE}}
   353  			i.setAttr(ctx, i.fs.VFSFilesystem(), auth.CredentialsFromContext(ctx), opts, fhOptions{useFh: true, fh: i.fh.handle})
   354  		}
   355  	}
   356  
   357  	if err := fd.vfsfd.Init(fdImpl, opts.Flags, rp.Mount(), d.VFSDentry(), fdOptions); err != nil {
   358  		return nil, err
   359  	}
   360  	return &fd.vfsfd, nil
   361  }
   362  
   363  // Lookup implements kernfs.Inode.Lookup.
   364  func (i *inode) Lookup(ctx context.Context, name string) (kernfs.Inode, error) {
   365  	in := linux.FUSELookupIn{Name: linux.CString(name)}
   366  	return i.newEntry(ctx, name, 0, linux.FUSE_LOOKUP, &in)
   367  }
   368  
   369  // Keep implements kernfs.Inode.Keep.
   370  func (i *inode) Keep() bool {
   371  	// Return true so that kernfs keeps the new dentry pointing to this
   372  	// inode in the dentry tree. This is needed because inodes created via
   373  	// Lookup are not temporary. They might refer to existing files on server
   374  	// that can be Unlink'd/Rmdir'd.
   375  	return true
   376  }
   377  
   378  // IterDirents implements kernfs.Inode.IterDirents.
   379  func (*inode) IterDirents(ctx context.Context, mnt *vfs.Mount, callback vfs.IterDirentsCallback, offset, relOffset int64) (int64, error) {
   380  	return offset, nil
   381  }
   382  
   383  // NewFile implements kernfs.Inode.NewFile.
   384  func (i *inode) NewFile(ctx context.Context, name string, opts vfs.OpenOptions) (kernfs.Inode, error) {
   385  	opts.Flags &= linux.O_ACCMODE | linux.O_CREAT | linux.O_EXCL | linux.O_TRUNC |
   386  		linux.O_DIRECTORY | linux.O_NOFOLLOW | linux.O_NONBLOCK | linux.O_NOCTTY
   387  	in := linux.FUSECreateIn{
   388  		CreateMeta: linux.FUSECreateMeta{
   389  			Flags: opts.Flags,
   390  			Mode:  uint32(opts.Mode) | linux.S_IFREG,
   391  			Umask: umaskFromContext(ctx),
   392  		},
   393  		Name: linux.CString(name),
   394  	}
   395  	return i.newEntry(ctx, name, linux.S_IFREG, linux.FUSE_CREATE, &in)
   396  }
   397  
   398  // NewNode implements kernfs.Inode.NewNode.
   399  func (i *inode) NewNode(ctx context.Context, name string, opts vfs.MknodOptions) (kernfs.Inode, error) {
   400  	in := linux.FUSEMknodIn{
   401  		MknodMeta: linux.FUSEMknodMeta{
   402  			Mode:  uint32(opts.Mode),
   403  			Rdev:  linux.MakeDeviceID(uint16(opts.DevMajor), opts.DevMinor),
   404  			Umask: umaskFromContext(ctx),
   405  		},
   406  		Name: linux.CString(name),
   407  	}
   408  	return i.newEntry(ctx, name, opts.Mode.FileType(), linux.FUSE_MKNOD, &in)
   409  }
   410  
   411  // NewSymlink implements kernfs.Inode.NewSymlink.
   412  func (i *inode) NewSymlink(ctx context.Context, name, target string) (kernfs.Inode, error) {
   413  	in := linux.FUSESymlinkIn{
   414  		Name:   linux.CString(name),
   415  		Target: linux.CString(target),
   416  	}
   417  	return i.newEntry(ctx, name, linux.S_IFLNK, linux.FUSE_SYMLINK, &in)
   418  }
   419  
   420  // NewLink implements kernfs.Inode.NewLink.
   421  func (i *inode) NewLink(ctx context.Context, name string, target kernfs.Inode) (kernfs.Inode, error) {
   422  	targetInode := target.(*inode)
   423  	in := linux.FUSELinkIn{
   424  		OldNodeID: primitive.Uint64(targetInode.nodeID),
   425  		Name:      linux.CString(name),
   426  	}
   427  	return i.newEntry(ctx, name, targetInode.Mode().FileType(), linux.FUSE_LINK, &in)
   428  }
   429  
   430  // Unlink implements kernfs.Inode.Unlink.
   431  func (i *inode) Unlink(ctx context.Context, name string, child kernfs.Inode) error {
   432  	in := linux.FUSEUnlinkIn{Name: linux.CString(name)}
   433  	req := i.fs.conn.NewRequest(auth.CredentialsFromContext(ctx), pidFromContext(ctx), i.nodeID, linux.FUSE_UNLINK, &in)
   434  	res, err := i.fs.conn.Call(ctx, req)
   435  	if err != nil {
   436  		return err
   437  	}
   438  	// only return error, discard res.
   439  	return res.Error()
   440  }
   441  
   442  // NewDir implements kernfs.Inode.NewDir.
   443  func (i *inode) NewDir(ctx context.Context, name string, opts vfs.MkdirOptions) (kernfs.Inode, error) {
   444  	in := linux.FUSEMkdirIn{
   445  		MkdirMeta: linux.FUSEMkdirMeta{
   446  			Mode:  uint32(opts.Mode),
   447  			Umask: umaskFromContext(ctx),
   448  		},
   449  		Name: linux.CString(name),
   450  	}
   451  	return i.newEntry(ctx, name, linux.S_IFDIR, linux.FUSE_MKDIR, &in)
   452  }
   453  
   454  // RmDir implements kernfs.Inode.RmDir.
   455  func (i *inode) RmDir(ctx context.Context, name string, child kernfs.Inode) error {
   456  	in := linux.FUSERmDirIn{Name: linux.CString(name)}
   457  	req := i.fs.conn.NewRequest(auth.CredentialsFromContext(ctx), pidFromContext(ctx), i.nodeID, linux.FUSE_RMDIR, &in)
   458  	res, err := i.fs.conn.Call(ctx, req)
   459  	if err != nil {
   460  		return err
   461  	}
   462  	return res.Error()
   463  }
   464  
   465  // Rename implements kernfs.Inode.Rename.
   466  func (i *inode) Rename(ctx context.Context, oldname, newname string, child, dstDir kernfs.Inode) error {
   467  	dstDirInode := dstDir.(*inode)
   468  	in := linux.FUSERenameIn{
   469  		Newdir:  primitive.Uint64(dstDirInode.nodeID),
   470  		Oldname: linux.CString(oldname),
   471  		Newname: linux.CString(newname),
   472  	}
   473  	req := i.fs.conn.NewRequest(auth.CredentialsFromContext(ctx), pidFromContext(ctx), i.nodeID, linux.FUSE_RENAME, &in)
   474  	res, err := i.fs.conn.Call(ctx, req)
   475  	if err != nil {
   476  		return err
   477  	}
   478  	return res.Error()
   479  }
   480  
   481  // newEntry calls FUSE server for entry creation and allocates corresponding
   482  // entry according to response. Shared by FUSE_MKNOD, FUSE_MKDIR, FUSE_SYMLINK,
   483  // FUSE_LINK and FUSE_LOOKUP.
   484  func (i *inode) newEntry(ctx context.Context, name string, fileType linux.FileMode, opcode linux.FUSEOpcode, payload marshal.Marshallable) (kernfs.Inode, error) {
   485  	req := i.fs.conn.NewRequest(auth.CredentialsFromContext(ctx), pidFromContext(ctx), i.nodeID, opcode, payload)
   486  	res, err := i.fs.conn.Call(ctx, req)
   487  	if err != nil {
   488  		return nil, err
   489  	}
   490  	if err := res.Error(); err != nil {
   491  		return nil, err
   492  	}
   493  	out := linux.FUSECreateOut{}
   494  	if opcode == linux.FUSE_CREATE {
   495  		if err := res.UnmarshalPayload(&out); err != nil {
   496  			return nil, err
   497  		}
   498  	} else {
   499  		if err := res.UnmarshalPayload(&out.FUSEEntryOut); err != nil {
   500  			return nil, err
   501  		}
   502  	}
   503  	if opcode != linux.FUSE_LOOKUP && ((out.Attr.Mode&linux.S_IFMT)^uint32(fileType) != 0 || out.NodeID == 0 || out.NodeID == linux.FUSE_ROOT_ID) {
   504  		return nil, linuxerr.EIO
   505  	}
   506  	child := i.fs.newInode(ctx, out.NodeID, out.Attr)
   507  	if opcode == linux.FUSE_CREATE {
   508  		// File handler is returned by fuse server at a time of file create.
   509  		// Save it temporary in a created child, so Open could return it when invoked
   510  		// to be sure after fh is consumed reset 'isNewFh' flag of inode
   511  		childI, ok := child.(*inode)
   512  		if ok {
   513  			childI.fh.new = true
   514  			childI.fh.handle = out.FUSEOpenOut.Fh
   515  			childI.fh.flags = out.FUSEOpenOut.OpenFlag
   516  		}
   517  	}
   518  	return child, nil
   519  }
   520  
   521  // Getlink implements kernfs.Inode.Getlink.
   522  func (i *inode) Getlink(ctx context.Context, mnt *vfs.Mount) (vfs.VirtualDentry, string, error) {
   523  	path, err := i.Readlink(ctx, mnt)
   524  	return vfs.VirtualDentry{}, path, err
   525  }
   526  
   527  // Readlink implements kernfs.Inode.Readlink.
   528  func (i *inode) Readlink(ctx context.Context, mnt *vfs.Mount) (string, error) {
   529  	i.attrMu.Lock()
   530  	defer i.attrMu.Unlock()
   531  	if i.filemode().FileType()&linux.S_IFLNK == 0 {
   532  		return "", linuxerr.EINVAL
   533  	}
   534  	if len(i.link) == 0 {
   535  		req := i.fs.conn.NewRequest(auth.CredentialsFromContext(ctx), pidFromContext(ctx), i.nodeID, linux.FUSE_READLINK, &linux.FUSEEmptyIn{})
   536  		res, err := i.fs.conn.Call(ctx, req)
   537  		if err != nil {
   538  			return "", err
   539  		}
   540  		i.link = string(res.data[res.hdr.SizeBytes():])
   541  		if !mnt.Options().ReadOnly {
   542  			i.attrTime = 0
   543  		}
   544  	}
   545  	return i.link, nil
   546  }
   547  
   548  // getFUSEAttr returns a linux.FUSEAttr of this inode stored in local cache.
   549  //
   550  // +checklocks:i.attrMu
   551  func (i *inode) getFUSEAttr() linux.FUSEAttr {
   552  	ns := time.Second.Nanoseconds()
   553  	return linux.FUSEAttr{
   554  		Ino:       i.nodeID,
   555  		UID:       i.uid.Load(),
   556  		GID:       i.gid.Load(),
   557  		Size:      i.size.Load(),
   558  		Mode:      uint32(i.filemode()),
   559  		BlkSize:   i.blockSize.Load(),
   560  		Atime:     uint64(i.atime.Load() / ns),
   561  		Mtime:     uint64(i.mtime.Load() / ns),
   562  		Ctime:     uint64(i.ctime.Load() / ns),
   563  		AtimeNsec: uint32(i.atime.Load() % ns),
   564  		MtimeNsec: uint32(i.mtime.Load() % ns),
   565  		CtimeNsec: uint32(i.ctime.Load() % ns),
   566  		Nlink:     i.nlink.Load(),
   567  	}
   568  }
   569  
   570  // statFromFUSEAttr makes attributes from linux.FUSEAttr to linux.Statx. The
   571  // opts.Sync attribute is ignored since the synchronization is handled by the
   572  // FUSE server.
   573  func statFromFUSEAttr(attr linux.FUSEAttr, mask, devMinor uint32) linux.Statx {
   574  	var stat linux.Statx
   575  	stat.Blksize = attr.BlkSize
   576  	stat.DevMajor, stat.DevMinor = linux.UNNAMED_MAJOR, devMinor
   577  
   578  	rdevMajor, rdevMinor := linux.DecodeDeviceID(attr.Rdev)
   579  	stat.RdevMajor, stat.RdevMinor = uint32(rdevMajor), rdevMinor
   580  
   581  	if mask&linux.STATX_MODE != 0 {
   582  		stat.Mode = uint16(attr.Mode)
   583  	}
   584  	if mask&linux.STATX_NLINK != 0 {
   585  		stat.Nlink = attr.Nlink
   586  	}
   587  	if mask&linux.STATX_UID != 0 {
   588  		stat.UID = attr.UID
   589  	}
   590  	if mask&linux.STATX_GID != 0 {
   591  		stat.GID = attr.GID
   592  	}
   593  	if mask&linux.STATX_ATIME != 0 {
   594  		stat.Atime = linux.StatxTimestamp{
   595  			Sec:  int64(attr.Atime),
   596  			Nsec: attr.AtimeNsec,
   597  		}
   598  	}
   599  	if mask&linux.STATX_MTIME != 0 {
   600  		stat.Mtime = linux.StatxTimestamp{
   601  			Sec:  int64(attr.Mtime),
   602  			Nsec: attr.MtimeNsec,
   603  		}
   604  	}
   605  	if mask&linux.STATX_CTIME != 0 {
   606  		stat.Ctime = linux.StatxTimestamp{
   607  			Sec:  int64(attr.Ctime),
   608  			Nsec: attr.CtimeNsec,
   609  		}
   610  	}
   611  	if mask&linux.STATX_INO != 0 {
   612  		stat.Ino = attr.Ino
   613  	}
   614  	if mask&linux.STATX_SIZE != 0 {
   615  		stat.Size = attr.Size
   616  	}
   617  	if mask&linux.STATX_BLOCKS != 0 {
   618  		stat.Blocks = attr.Blocks
   619  	}
   620  	return stat
   621  }
   622  
   623  // getAttr gets the attribute of this inode by issuing a FUSE_GETATTR request
   624  // or read from local cache. It updates the corresponding attributes if
   625  // necessary.
   626  //
   627  // +checklocks:i.attrMu
   628  func (i *inode) getAttr(ctx context.Context, fs *vfs.Filesystem, opts vfs.StatOptions, flags uint32, fh uint64) (linux.FUSEAttr, error) {
   629  	// TODO(gvisor.dev/issue/3679): send the request only if
   630  	//	- invalid local cache for fields specified in the opts.Mask
   631  	//	- forced update
   632  	//	- i.attributeTime expired
   633  	// If local cache is still valid, return local cache.
   634  	// Currently we always send a request,
   635  	// and we always set the metadata with the new result,
   636  	// unless attributeVersion has changed.
   637  	creds := auth.CredentialsFromContext(ctx)
   638  
   639  	in := linux.FUSEGetAttrIn{
   640  		GetAttrFlags: flags,
   641  		Fh:           fh,
   642  	}
   643  	req := i.fs.conn.NewRequest(creds, pidFromContext(ctx), i.nodeID, linux.FUSE_GETATTR, &in)
   644  	res, err := i.fs.conn.Call(ctx, req)
   645  	if err != nil {
   646  		return linux.FUSEAttr{}, err
   647  	}
   648  	if err := res.Error(); err != nil {
   649  		return linux.FUSEAttr{}, err
   650  	}
   651  	var out linux.FUSEAttrOut
   652  	if err := res.UnmarshalPayload(&out); err != nil {
   653  		return linux.FUSEAttr{}, err
   654  	}
   655  
   656  	// Local version is newer, return the local one.
   657  	i.fs.conn.mu.Lock()
   658  	attributeVersion := i.fs.conn.attributeVersion.Load()
   659  	if attributeVersion != 0 && i.attrVersion.Load() > attributeVersion {
   660  		i.fs.conn.mu.Unlock()
   661  		return i.getFUSEAttr(), nil
   662  	}
   663  	i.fs.conn.mu.Unlock()
   664  	i.updateAttrs(out.Attr, out.AttrValid)
   665  	return out.Attr, nil
   666  }
   667  
   668  // reviseAttr attempts to update the attributes for internal purposes
   669  // by calling getAttr with a pre-specified mask.
   670  // Used by read, write, lseek.
   671  //
   672  // +checklocks:i.attrMu
   673  func (i *inode) reviseAttr(ctx context.Context, flags uint32, fh uint64) error {
   674  	// Never need atime for internal purposes.
   675  	_, err := i.getAttr(ctx, i.fs.VFSFilesystem(), vfs.StatOptions{
   676  		Mask: linux.STATX_BASIC_STATS &^ linux.STATX_ATIME,
   677  	}, flags, fh)
   678  	return err
   679  }
   680  
   681  // Stat implements kernfs.Inode.Stat.
   682  func (i *inode) Stat(ctx context.Context, fs *vfs.Filesystem, opts vfs.StatOptions) (linux.Statx, error) {
   683  	i.attrMu.Lock()
   684  	defer i.attrMu.Unlock()
   685  	attr, err := i.getAttr(ctx, fs, opts, 0, 0)
   686  	if err != nil {
   687  		return linux.Statx{}, err
   688  	}
   689  
   690  	return statFromFUSEAttr(attr, opts.Mask, i.fs.devMinor), nil
   691  }
   692  
   693  // DecRef implements kernfs.Inode.DecRef.
   694  func (i *inode) DecRef(ctx context.Context) {
   695  	i.inodeRefs.DecRef(func() { i.Destroy(ctx) })
   696  }
   697  
   698  // StatFS implements kernfs.Inode.StatFS.
   699  func (i *inode) StatFS(ctx context.Context, fs *vfs.Filesystem) (linux.Statfs, error) {
   700  	req := i.fs.conn.NewRequest(auth.CredentialsFromContext(ctx), pidFromContext(ctx), i.nodeID,
   701  		linux.FUSE_STATFS, &linux.FUSEEmptyIn{},
   702  	)
   703  	res, err := i.fs.conn.Call(ctx, req)
   704  	if err != nil {
   705  		return linux.Statfs{}, err
   706  	}
   707  	if err := res.Error(); err != nil {
   708  		return linux.Statfs{}, err
   709  	}
   710  
   711  	var out linux.FUSEStatfsOut
   712  	if err := res.UnmarshalPayload(&out); err != nil {
   713  		return linux.Statfs{}, err
   714  	}
   715  
   716  	return linux.Statfs{
   717  		Type:            linux.FUSE_SUPER_MAGIC,
   718  		Blocks:          uint64(out.Blocks),
   719  		BlocksFree:      out.BlocksFree,
   720  		BlocksAvailable: out.BlocksAvailable,
   721  		Files:           out.Files,
   722  		FilesFree:       out.FilesFree,
   723  		BlockSize:       int64(out.BlockSize),
   724  		NameLength:      uint64(out.NameLength),
   725  		FragmentSize:    int64(out.FragmentSize),
   726  	}, nil
   727  }
   728  
   729  // fattrMaskFromStats converts vfs.SetStatOptions.Stat.Mask to linux stats mask
   730  // aligned with the attribute mask defined in include/linux/fs.h.
   731  func fattrMaskFromStats(mask uint32) uint32 {
   732  	var fuseAttrMask uint32
   733  	maskMap := map[uint32]uint32{
   734  		linux.STATX_MODE:  linux.FATTR_MODE,
   735  		linux.STATX_UID:   linux.FATTR_UID,
   736  		linux.STATX_GID:   linux.FATTR_GID,
   737  		linux.STATX_SIZE:  linux.FATTR_SIZE,
   738  		linux.STATX_ATIME: linux.FATTR_ATIME,
   739  		linux.STATX_MTIME: linux.FATTR_MTIME,
   740  		linux.STATX_CTIME: linux.FATTR_CTIME,
   741  	}
   742  	for statxMask, fattrMask := range maskMap {
   743  		if mask&statxMask != 0 {
   744  			fuseAttrMask |= fattrMask
   745  		}
   746  	}
   747  	return fuseAttrMask
   748  }
   749  
   750  // SetStat implements kernfs.Inode.SetStat.
   751  func (i *inode) SetStat(ctx context.Context, fs *vfs.Filesystem, creds *auth.Credentials, opts vfs.SetStatOptions) error {
   752  	i.attrMu.Lock()
   753  	defer i.attrMu.Unlock()
   754  	if err := vfs.CheckSetStat(ctx, creds, &opts, i.filemode(), auth.KUID(i.uid.Load()), auth.KGID(i.gid.Load())); err != nil {
   755  		return err
   756  	}
   757  	if opts.Stat.Mask == 0 {
   758  		return nil
   759  	}
   760  	return i.setAttr(ctx, fs, creds, opts, fhOptions{useFh: false})
   761  }
   762  
   763  type fhOptions struct {
   764  	useFh bool
   765  	fh    uint64
   766  }
   767  
   768  // +checklocks:i.attrMu
   769  func (i *inode) setAttr(ctx context.Context, fs *vfs.Filesystem, creds *auth.Credentials, opts vfs.SetStatOptions, fhOpts fhOptions) error {
   770  	// We should retain the original file type when assigning a new mode.
   771  	fattrMask := fattrMaskFromStats(opts.Stat.Mask)
   772  	if fhOpts.useFh {
   773  		fattrMask |= linux.FATTR_FH
   774  	}
   775  	if opts.Stat.Mask&linux.STATX_ATIME != 0 && opts.Stat.Atime.Nsec == linux.UTIME_NOW {
   776  		fattrMask |= linux.FATTR_ATIME_NOW
   777  	}
   778  	if opts.Stat.Mask&linux.STATX_MTIME != 0 && opts.Stat.Mtime.Nsec == linux.UTIME_NOW {
   779  		fattrMask |= linux.FATTR_ATIME_NOW
   780  	}
   781  	in := linux.FUSESetAttrIn{
   782  		Valid:     fattrMask,
   783  		Fh:        fhOpts.fh,
   784  		Size:      opts.Stat.Size,
   785  		Atime:     uint64(opts.Stat.Atime.Sec),
   786  		Mtime:     uint64(opts.Stat.Mtime.Sec),
   787  		Ctime:     uint64(opts.Stat.Ctime.Sec),
   788  		AtimeNsec: opts.Stat.Atime.Nsec,
   789  		MtimeNsec: opts.Stat.Mtime.Nsec,
   790  		CtimeNsec: opts.Stat.Ctime.Nsec,
   791  		Mode:      uint32(uint16(i.filemode().FileType()) | opts.Stat.Mode),
   792  		UID:       opts.Stat.UID,
   793  		GID:       opts.Stat.GID,
   794  	}
   795  	req := i.fs.conn.NewRequest(creds, pidFromContext(ctx), i.nodeID, linux.FUSE_SETATTR, &in)
   796  	res, err := i.fs.conn.Call(ctx, req)
   797  	if err != nil {
   798  		return err
   799  	}
   800  	if err := res.Error(); err != nil {
   801  		return err
   802  	}
   803  	out := linux.FUSEAttrOut{}
   804  	if err := res.UnmarshalPayload(&out); err != nil {
   805  		return err
   806  	}
   807  	i.updateAttrs(out.Attr, out.AttrValid)
   808  	return nil
   809  }
   810  
   811  // +checklocks:i.attrMu
   812  func (i *inode) updateAttrs(attr linux.FUSEAttr, attrTimeout uint64) {
   813  	i.fs.conn.mu.Lock()
   814  	i.attrVersion.Store(i.fs.conn.attributeVersion.Add(1))
   815  	i.fs.conn.mu.Unlock()
   816  	i.attrTime = attrTimeout
   817  
   818  	i.ino.Store(attr.Ino)
   819  
   820  	i.mode.Store((attr.Mode & 07777) | (i.mode.Load() & linux.S_IFMT))
   821  	i.uid.Store(attr.UID)
   822  	i.gid.Store(attr.GID)
   823  
   824  	i.atime.Store(attr.ATimeNsec())
   825  	i.mtime.Store(attr.MTimeNsec())
   826  	i.ctime.Store(attr.CTimeNsec())
   827  
   828  	i.size.Store(attr.Size)
   829  	i.nlink.Store(attr.Nlink)
   830  
   831  	if !i.fs.opts.defaultPermissions {
   832  		i.mode.Store(i.mode.Load() & ^uint32(linux.S_ISVTX))
   833  	}
   834  }