diff options
Diffstat (limited to 'pkg/sentry/fsimpl/fuse/fusefs.go')
-rw-r--r-- | pkg/sentry/fsimpl/fuse/fusefs.go | 569 |
1 files changed, 41 insertions, 528 deletions
diff --git a/pkg/sentry/fsimpl/fuse/fusefs.go b/pkg/sentry/fsimpl/fuse/fusefs.go index 402dabe5a..810819ae4 100644 --- a/pkg/sentry/fsimpl/fuse/fusefs.go +++ b/pkg/sentry/fsimpl/fuse/fusefs.go @@ -16,10 +16,7 @@ package fuse import ( - "math" "strconv" - "sync" - "sync/atomic" "gvisor.dev/gvisor/pkg/abi/linux" "gvisor.dev/gvisor/pkg/context" @@ -29,17 +26,11 @@ import ( "gvisor.dev/gvisor/pkg/sentry/kernel/auth" "gvisor.dev/gvisor/pkg/sentry/vfs" "gvisor.dev/gvisor/pkg/syserror" - "gvisor.dev/gvisor/pkg/waiter" - "gvisor.dev/gvisor/tools/go_marshal/marshal" ) // Name is the default filesystem name. const Name = "fuse" -// maxActiveRequestsDefault is the default setting controlling the upper bound -// on the number of active requests at any given time. -const maxActiveRequestsDefault = 10000 - // FilesystemType implements vfs.FilesystemType. type FilesystemType struct{} @@ -65,11 +56,6 @@ type filesystemOptions struct { // exist at any time. Any further requests will block when trying to // Call the server. maxActiveRequests uint64 - - // maxRead is the max number of bytes to read, - // specified as "max_read" in fs parameters. - // If not specified by user, use math.MaxUint32 as default value. - maxRead uint32 } // filesystem implements vfs.FilesystemImpl. @@ -83,9 +69,6 @@ type filesystem struct { // opts is the options the fusefs is initialized with. opts *filesystemOptions - - // umounted is true if filesystem.Release() has been called. - umounted bool } // Name implements vfs.FilesystemType.Name. @@ -159,29 +142,14 @@ func (fsType FilesystemType) GetFilesystem(ctx context.Context, vfsObj *vfs.Virt // Set the maxInFlightRequests option. fsopts.maxActiveRequests = maxActiveRequestsDefault - if maxReadStr, ok := mopts["max_read"]; ok { - delete(mopts, "max_read") - maxRead, err := strconv.ParseUint(maxReadStr, 10, 32) - if err != nil { - log.Warningf("%s.GetFilesystem: invalid max_read: max_read=%s", fsType.Name(), maxReadStr) - return nil, nil, syserror.EINVAL - } - if maxRead < fuseMinMaxRead { - maxRead = fuseMinMaxRead - } - fsopts.maxRead = uint32(maxRead) - } else { - fsopts.maxRead = math.MaxUint32 - } - // Check for unparsed options. if len(mopts) != 0 { - log.Warningf("%s.GetFilesystem: unsupported or unknown options: %v", fsType.Name(), mopts) + log.Warningf("%s.GetFilesystem: unknown options: %v", fsType.Name(), mopts) return nil, nil, syserror.EINVAL } // Create a new FUSE filesystem. - fs, err := newFUSEFilesystem(ctx, devMinor, &fsopts, fuseFd) + fs, err := NewFUSEFilesystem(ctx, devMinor, &fsopts, fuseFd) if err != nil { log.Warningf("%s.NewFUSEFilesystem: failed with error: %v", fsType.Name(), err) return nil, nil, err @@ -197,27 +165,26 @@ func (fsType FilesystemType) GetFilesystem(ctx context.Context, vfsObj *vfs.Virt } // root is the fusefs root directory. - root := fs.newRootInode(creds, fsopts.rootMode) + root := fs.newInode(creds, fsopts.rootMode) return fs.VFSFilesystem(), root.VFSDentry(), nil } -// newFUSEFilesystem creates a new FUSE filesystem. -func newFUSEFilesystem(ctx context.Context, devMinor uint32, opts *filesystemOptions, device *vfs.FileDescription) (*filesystem, error) { - conn, err := newFUSEConnection(ctx, device, opts) +// NewFUSEFilesystem creates a new FUSE filesystem. +func NewFUSEFilesystem(ctx context.Context, devMinor uint32, opts *filesystemOptions, device *vfs.FileDescription) (*filesystem, error) { + fs := &filesystem{ + devMinor: devMinor, + opts: opts, + } + + conn, err := newFUSEConnection(ctx, device, opts.maxActiveRequests) if err != nil { log.Warningf("fuse.NewFUSEFilesystem: NewFUSEConnection failed with error: %v", err) return nil, syserror.EINVAL } + fs.conn = conn fuseFD := device.Impl().(*DeviceFD) - - fs := &filesystem{ - devMinor: devMinor, - opts: opts, - conn: conn, - } - fuseFD.fs = fs return fs, nil @@ -225,14 +192,6 @@ func newFUSEFilesystem(ctx context.Context, devMinor uint32, opts *filesystemOpt // Release implements vfs.FilesystemImpl.Release. func (fs *filesystem) Release(ctx context.Context) { - fs.umounted = true - fs.conn.Abort(ctx) - - fs.conn.fd.mu.Lock() - // Notify all the waiters on this fd. - fs.conn.fd.waitQueue.Notify(waiter.EventIn) - fs.conn.fd.mu.Unlock() - fs.Filesystem.VFSFilesystem().VirtualFilesystem().PutAnonBlockDevMinor(fs.devMinor) fs.Filesystem.Release(ctx) } @@ -246,49 +205,14 @@ type inode struct { kernfs.InodeNotSymlink kernfs.OrderedChildren - dentry kernfs.Dentry - - // the owning filesystem. fs is immutable. - fs *filesystem - - // metaDataMu protects the metadata of this inode. - metadataMu sync.Mutex - - nodeID uint64 - locks vfs.FileLocks - // size of the file. - size uint64 - - // attributeVersion is the version of inode's attributes. - attributeVersion uint64 - - // attributeTime is the remaining vaild time of attributes. - attributeTime uint64 - - // version of the inode. - version uint64 - - // link is result of following a symbolic link. - link string -} - -func (fs *filesystem) newRootInode(creds *auth.Credentials, mode linux.FileMode) *kernfs.Dentry { - i := &inode{fs: fs} - i.InodeAttrs.Init(creds, linux.UNNAMED_MAJOR, fs.devMinor, 1, linux.ModeDirectory|0755) - i.OrderedChildren.Init(kernfs.OrderedChildrenOptions{}) - i.dentry.Init(i) - i.nodeID = 1 - - return &i.dentry + dentry kernfs.Dentry } -func (fs *filesystem) newInode(nodeID uint64, attr linux.FUSEAttr) *kernfs.Dentry { - i := &inode{fs: fs, nodeID: nodeID} - creds := auth.Credentials{EffectiveKGID: auth.KGID(attr.UID), EffectiveKUID: auth.KUID(attr.UID)} - i.InodeAttrs.Init(&creds, linux.UNNAMED_MAJOR, fs.devMinor, fs.NextIno(), linux.FileMode(attr.Mode)) - atomic.StoreUint64(&i.size, attr.Size) +func (fs *filesystem) newInode(creds *auth.Credentials, mode linux.FileMode) *kernfs.Dentry { + i := &inode{} + i.InodeAttrs.Init(creds, linux.UNNAMED_MAJOR, fs.devMinor, fs.NextIno(), linux.ModeDirectory|0755) i.OrderedChildren.Init(kernfs.OrderedChildrenOptions{}) i.EnableLeakCheck() i.dentry.Init(i) @@ -298,301 +222,13 @@ func (fs *filesystem) newInode(nodeID uint64, attr linux.FUSEAttr) *kernfs.Dentr // Open implements kernfs.Inode.Open. func (i *inode) Open(ctx context.Context, rp *vfs.ResolvingPath, vfsd *vfs.Dentry, opts vfs.OpenOptions) (*vfs.FileDescription, error) { - isDir := i.InodeAttrs.Mode().IsDir() - // return error if specified to open directory but inode is not a directory. - if !isDir && opts.Mode.IsDir() { - return nil, syserror.ENOTDIR - } - if opts.Flags&linux.O_LARGEFILE == 0 && atomic.LoadUint64(&i.size) > linux.MAX_NON_LFS { - return nil, syserror.EOVERFLOW - } - - var fd *fileDescription - var fdImpl vfs.FileDescriptionImpl - if isDir { - directoryFD := &directoryFD{} - fd = &(directoryFD.fileDescription) - fdImpl = directoryFD - } else { - regularFD := ®ularFileFD{} - fd = &(regularFD.fileDescription) - fdImpl = regularFD - } - // FOPEN_KEEP_CACHE is the defualt flag for noOpen. - fd.OpenFlag = linux.FOPEN_KEEP_CACHE - - // Only send open request when FUSE server support open or is opening a directory. - if !i.fs.conn.noOpen || isDir { - kernelTask := kernel.TaskFromContext(ctx) - if kernelTask == nil { - log.Warningf("fusefs.Inode.Open: couldn't get kernel task from context") - return nil, syserror.EINVAL - } - - // Build the request. - var opcode linux.FUSEOpcode - if isDir { - opcode = linux.FUSE_OPENDIR - } else { - opcode = linux.FUSE_OPEN - } - - in := linux.FUSEOpenIn{Flags: opts.Flags & ^uint32(linux.O_CREAT|linux.O_EXCL|linux.O_NOCTTY)} - if !i.fs.conn.atomicOTrunc { - in.Flags &= ^uint32(linux.O_TRUNC) - } - - req, err := i.fs.conn.NewRequest(auth.CredentialsFromContext(ctx), uint32(kernelTask.ThreadID()), i.nodeID, opcode, &in) - if err != nil { - return nil, err - } - - // Send the request and receive the reply. - res, err := i.fs.conn.Call(kernelTask, req) - if err != nil { - return nil, err - } - if err := res.Error(); err == syserror.ENOSYS && !isDir { - i.fs.conn.noOpen = true - } else if err != nil { - return nil, err - } else { - out := linux.FUSEOpenOut{} - if err := res.UnmarshalPayload(&out); err != nil { - return nil, err - } - - // Process the reply. - fd.OpenFlag = out.OpenFlag - if isDir { - fd.OpenFlag &= ^uint32(linux.FOPEN_DIRECT_IO) - } - - fd.Fh = out.Fh - } - } - - // TODO(gvisor.dev/issue/3234): invalidate mmap after implemented it for FUSE Inode - fd.DirectIO = fd.OpenFlag&linux.FOPEN_DIRECT_IO != 0 - fdOptions := &vfs.FileDescriptionOptions{} - if fd.OpenFlag&linux.FOPEN_NONSEEKABLE != 0 { - fdOptions.DenyPRead = true - fdOptions.DenyPWrite = true - fd.Nonseekable = true - } - - // If we don't send SETATTR before open (which is indicated by atomicOTrunc) - // and O_TRUNC is set, update the inode's version number and clean existing data - // by setting the file size to 0. - if i.fs.conn.atomicOTrunc && opts.Flags&linux.O_TRUNC != 0 { - i.fs.conn.mu.Lock() - i.fs.conn.attributeVersion++ - i.attributeVersion = i.fs.conn.attributeVersion - atomic.StoreUint64(&i.size, 0) - i.fs.conn.mu.Unlock() - i.attributeTime = 0 - } - - if err := fd.vfsfd.Init(fdImpl, opts.Flags, rp.Mount(), vfsd, fdOptions); err != nil { - return nil, err - } - return &fd.vfsfd, nil -} - -// Lookup implements kernfs.Inode.Lookup. -func (i *inode) Lookup(ctx context.Context, name string) (*vfs.Dentry, error) { - in := linux.FUSELookupIn{Name: name} - return i.newEntry(ctx, name, 0, linux.FUSE_LOOKUP, &in) -} - -// IterDirents implements kernfs.Inode.IterDirents. -func (*inode) IterDirents(ctx context.Context, callback vfs.IterDirentsCallback, offset, relOffset int64) (int64, error) { - return offset, nil -} - -// Valid implements kernfs.Inode.Valid. -func (*inode) Valid(ctx context.Context) bool { - return true -} - -// NewFile implements kernfs.Inode.NewFile. -func (i *inode) NewFile(ctx context.Context, name string, opts vfs.OpenOptions) (*vfs.Dentry, error) { - kernelTask := kernel.TaskFromContext(ctx) - if kernelTask == nil { - log.Warningf("fusefs.Inode.NewFile: couldn't get kernel task from context", i.nodeID) - return nil, syserror.EINVAL - } - in := linux.FUSECreateIn{ - CreateMeta: linux.FUSECreateMeta{ - Flags: opts.Flags, - Mode: uint32(opts.Mode) | linux.S_IFREG, - Umask: uint32(kernelTask.FSContext().Umask()), - }, - Name: name, - } - return i.newEntry(ctx, name, linux.S_IFREG, linux.FUSE_CREATE, &in) -} - -// NewNode implements kernfs.Inode.NewNode. -func (i *inode) NewNode(ctx context.Context, name string, opts vfs.MknodOptions) (*vfs.Dentry, error) { - in := linux.FUSEMknodIn{ - MknodMeta: linux.FUSEMknodMeta{ - Mode: uint32(opts.Mode), - Rdev: linux.MakeDeviceID(uint16(opts.DevMajor), opts.DevMinor), - Umask: uint32(kernel.TaskFromContext(ctx).FSContext().Umask()), - }, - Name: name, - } - return i.newEntry(ctx, name, opts.Mode.FileType(), linux.FUSE_MKNOD, &in) -} - -// NewSymlink implements kernfs.Inode.NewSymlink. -func (i *inode) NewSymlink(ctx context.Context, name, target string) (*vfs.Dentry, error) { - in := linux.FUSESymLinkIn{ - Name: name, - Target: target, - } - return i.newEntry(ctx, name, linux.S_IFLNK, linux.FUSE_SYMLINK, &in) -} - -// Unlink implements kernfs.Inode.Unlink. -func (i *inode) Unlink(ctx context.Context, name string, child *vfs.Dentry) error { - kernelTask := kernel.TaskFromContext(ctx) - if kernelTask == nil { - log.Warningf("fusefs.Inode.newEntry: couldn't get kernel task from context", i.nodeID) - return syserror.EINVAL - } - in := linux.FUSEUnlinkIn{Name: name} - req, err := i.fs.conn.NewRequest(auth.CredentialsFromContext(ctx), uint32(kernelTask.ThreadID()), i.nodeID, linux.FUSE_UNLINK, &in) - if err != nil { - return err - } - res, err := i.fs.conn.Call(kernelTask, req) + fd, err := kernfs.NewGenericDirectoryFD(rp.Mount(), vfsd, &i.OrderedChildren, &i.locks, &opts, kernfs.GenericDirectoryFDOptions{ + SeekEnd: kernfs.SeekEndStaticEntries, + }) if err != nil { - return err - } - // only return error, discard res. - if err := res.Error(); err != nil { - return err - } - return i.dentry.RemoveChildLocked(name, child) -} - -// NewDir implements kernfs.Inode.NewDir. -func (i *inode) NewDir(ctx context.Context, name string, opts vfs.MkdirOptions) (*vfs.Dentry, error) { - in := linux.FUSEMkdirIn{ - MkdirMeta: linux.FUSEMkdirMeta{ - Mode: uint32(opts.Mode), - Umask: uint32(kernel.TaskFromContext(ctx).FSContext().Umask()), - }, - Name: name, - } - return i.newEntry(ctx, name, linux.S_IFDIR, linux.FUSE_MKDIR, &in) -} - -// RmDir implements kernfs.Inode.RmDir. -func (i *inode) RmDir(ctx context.Context, name string, child *vfs.Dentry) error { - fusefs := i.fs - task, creds := kernel.TaskFromContext(ctx), auth.CredentialsFromContext(ctx) - - in := linux.FUSERmDirIn{Name: name} - req, err := fusefs.conn.NewRequest(creds, uint32(task.ThreadID()), i.nodeID, linux.FUSE_RMDIR, &in) - if err != nil { - return err - } - - res, err := i.fs.conn.Call(task, req) - if err != nil { - return err - } - if err := res.Error(); err != nil { - return err - } - - // TODO(Before merging): When creating new nodes, should we add nodes to the ordered children? - // If so we'll probably need to call this. We will also need to add them with the writable flag when - // appropriate. - // return i.OrderedChildren.RmDir(ctx, name, child) - - return nil -} - -// newEntry calls FUSE server for entry creation and allocates corresponding entry according to response. -// Shared by FUSE_MKNOD, FUSE_MKDIR, FUSE_SYMLINK, FUSE_LINK and FUSE_LOOKUP. -func (i *inode) newEntry(ctx context.Context, name string, fileType linux.FileMode, opcode linux.FUSEOpcode, payload marshal.Marshallable) (*vfs.Dentry, error) { - kernelTask := kernel.TaskFromContext(ctx) - if kernelTask == nil { - log.Warningf("fusefs.Inode.newEntry: couldn't get kernel task from context", i.nodeID) - return nil, syserror.EINVAL - } - req, err := i.fs.conn.NewRequest(auth.CredentialsFromContext(ctx), uint32(kernelTask.ThreadID()), i.nodeID, opcode, payload) - if err != nil { - return nil, err - } - res, err := i.fs.conn.Call(kernelTask, req) - if err != nil { - return nil, err - } - if err := res.Error(); err != nil { return nil, err } - out := linux.FUSEEntryOut{} - if err := res.UnmarshalPayload(&out); err != nil { - return nil, err - } - if opcode != linux.FUSE_LOOKUP && ((out.Attr.Mode&linux.S_IFMT)^uint32(fileType) != 0 || out.NodeID == 0 || out.NodeID == linux.FUSE_ROOT_ID) { - return nil, syserror.EIO - } - child := i.fs.newInode(out.NodeID, out.Attr) - if opcode == linux.FUSE_LOOKUP { - i.dentry.InsertChildLocked(name, child) - } else { - i.dentry.InsertChild(name, child) - } - return child.VFSDentry(), nil -} - -// Getlink implements kernfs.Inode.Getlink. -func (i *inode) Getlink(ctx context.Context, mnt *vfs.Mount) (vfs.VirtualDentry, string, error) { - path, err := i.Readlink(ctx, mnt) - return vfs.VirtualDentry{}, path, err -} - -// Readlink implements kernfs.Inode.Readlink. -func (i *inode) Readlink(ctx context.Context, mnt *vfs.Mount) (string, error) { - if i.Mode().FileType()&linux.S_IFLNK == 0 { - return "", syserror.EINVAL - } - if len(i.link) == 0 { - kernelTask := kernel.TaskFromContext(ctx) - if kernelTask == nil { - log.Warningf("fusefs.Inode.Readlink: couldn't get kernel task from context") - return "", syserror.EINVAL - } - req, err := i.fs.conn.NewRequest(auth.CredentialsFromContext(ctx), uint32(kernelTask.ThreadID()), i.nodeID, linux.FUSE_READLINK, &linux.FUSEEmptyIn{}) - if err != nil { - return "", err - } - res, err := i.fs.conn.Call(kernelTask, req) - if err != nil { - return "", err - } - i.link = string(res.data[res.hdr.SizeBytes():]) - if !mnt.Options().ReadOnly { - i.attributeTime = 0 - } - } - return i.link, nil -} - -// getFUSEAttr returns a linux.FUSEAttr of this inode stored in local cache. -// TODO(gvisor.dev/issue/3679): Add support for other fields. -func (i *inode) getFUSEAttr() linux.FUSEAttr { - return linux.FUSEAttr{ - Ino: i.Ino(), - Size: atomic.LoadUint64(&i.size), - Mode: uint32(i.Mode()), - } + return fd.VFSFileDescription(), nil } // statFromFUSEAttr makes attributes from linux.FUSEAttr to linux.Statx. The @@ -648,89 +284,47 @@ func statFromFUSEAttr(attr linux.FUSEAttr, mask, devMinor uint32) linux.Statx { return stat } -// getAttr gets the attribute of this inode by issuing a FUSE_GETATTR request -// or read from local cache. It updates the corresponding attributes if -// necessary. -func (i *inode) getAttr(ctx context.Context, fs *vfs.Filesystem, opts vfs.StatOptions, flags uint32, fh uint64) (linux.FUSEAttr, error) { - attributeVersion := atomic.LoadUint64(&i.fs.conn.attributeVersion) - - // TODO(gvisor.dev/issue/3679): send the request only if - // - invalid local cache for fields specified in the opts.Mask - // - forced update - // - i.attributeTime expired - // If local cache is still valid, return local cache. - // Currently we always send a request, - // and we always set the metadata with the new result, - // unless attributeVersion has changed. - - task := kernel.TaskFromContext(ctx) +// Stat implements kernfs.Inode.Stat. +func (i *inode) Stat(ctx context.Context, fs *vfs.Filesystem, opts vfs.StatOptions) (linux.Statx, error) { + fusefs := fs.Impl().(*filesystem) + conn := fusefs.conn + task, creds := kernel.TaskFromContext(ctx), auth.CredentialsFromContext(ctx) if task == nil { log.Warningf("couldn't get kernel task from context") - return linux.FUSEAttr{}, syserror.EINVAL + return linux.Statx{}, syserror.EINVAL } - creds := auth.CredentialsFromContext(ctx) - - in := linux.FUSEGetAttrIn{ - GetAttrFlags: flags, - Fh: fh, - } - req, err := i.fs.conn.NewRequest(creds, uint32(task.ThreadID()), i.nodeID, linux.FUSE_GETATTR, &in) + var in linux.FUSEGetAttrIn + // We don't set any attribute in the request, because in VFS2 fstat(2) will + // finally be translated into vfs.FilesystemImpl.StatAt() (see + // pkg/sentry/syscalls/linux/vfs2/stat.go), resulting in the same flow + // as stat(2). Thus GetAttrFlags and Fh variable will never be used in VFS2. + req, err := conn.NewRequest(creds, uint32(task.ThreadID()), i.Ino(), linux.FUSE_GETATTR, &in) if err != nil { - return linux.FUSEAttr{}, err + return linux.Statx{}, err } - res, err := i.fs.conn.Call(task, req) + res, err := conn.Call(task, req) if err != nil { - return linux.FUSEAttr{}, err + return linux.Statx{}, err } if err := res.Error(); err != nil { - return linux.FUSEAttr{}, err + return linux.Statx{}, err } var out linux.FUSEGetAttrOut if err := res.UnmarshalPayload(&out); err != nil { - return linux.FUSEAttr{}, err - } - - // Local version is newer, return the local one. - // Skip the update. - if attributeVersion != 0 && atomic.LoadUint64(&i.attributeVersion) > attributeVersion { - return i.getFUSEAttr(), nil + return linux.Statx{}, err } - // Set the metadata of kernfs.InodeAttrs. - if err := i.SetInodeStat(ctx, fs, creds, vfs.SetStatOptions{ - Stat: statFromFUSEAttr(out.Attr, linux.STATX_ALL, i.fs.devMinor), + // Set all metadata into kernfs.InodeAttrs. + if err := i.SetStat(ctx, fs, creds, vfs.SetStatOptions{ + Stat: statFromFUSEAttr(out.Attr, linux.STATX_ALL, fusefs.devMinor), }); err != nil { - return linux.FUSEAttr{}, err - } - - // Set the size if no error (after SetStat() check). - atomic.StoreUint64(&i.size, out.Attr.Size) - - return out.Attr, nil -} - -// reviseAttr attempts to update the attributes for internal purposes -// by calling getAttr with a pre-specified mask. -// Used by read, write, lseek. -func (i *inode) reviseAttr(ctx context.Context, flags uint32, fh uint64) error { - // Never need atime for internal purposes. - _, err := i.getAttr(ctx, i.fs.VFSFilesystem(), vfs.StatOptions{ - Mask: linux.STATX_BASIC_STATS &^ linux.STATX_ATIME, - }, flags, fh) - return err -} - -// Stat implements kernfs.Inode.Stat. -func (i *inode) Stat(ctx context.Context, fs *vfs.Filesystem, opts vfs.StatOptions) (linux.Statx, error) { - attr, err := i.getAttr(ctx, fs, opts, 0, 0) - if err != nil { return linux.Statx{}, err } - return statFromFUSEAttr(attr, opts.Mask, i.fs.devMinor), nil + return statFromFUSEAttr(out.Attr, opts.Mask, fusefs.devMinor), nil } // DecRef implements kernfs.Inode. @@ -743,84 +337,3 @@ func (i *inode) StatFS(ctx context.Context, fs *vfs.Filesystem) (linux.Statfs, e // TODO(gvisor.dev/issues/3413): Complete the implementation of statfs. return vfs.GenericStatFS(linux.FUSE_SUPER_MAGIC), nil } - -// fattrMaskFromStats converts vfs.SetStatOptions.Stat.Mask to linux stats mask -// aligned with the attribute mask defined in include/linux/fs.h. -func fattrMaskFromStats(mask uint32) uint32 { - var fuseAttrMask uint32 - maskMap := map[uint32]uint32{ - linux.STATX_MODE: linux.FATTR_MODE, - linux.STATX_UID: linux.FATTR_UID, - linux.STATX_GID: linux.FATTR_GID, - linux.STATX_SIZE: linux.FATTR_SIZE, - linux.STATX_ATIME: linux.FATTR_ATIME, - linux.STATX_MTIME: linux.FATTR_MTIME, - linux.STATX_CTIME: linux.FATTR_CTIME, - } - for statxMask, fattrMask := range maskMap { - if mask&statxMask != 0 { - fuseAttrMask |= fattrMask - } - } - return fuseAttrMask -} - -// SetStat implements kernfs.Inode.SetStat. -func (i *inode) SetStat(ctx context.Context, fs *vfs.Filesystem, creds *auth.Credentials, opts vfs.SetStatOptions) error { - return i.setAttr(ctx, fs, creds, opts, false, 0) -} - -func (i *inode) setAttr(ctx context.Context, fs *vfs.Filesystem, creds *auth.Credentials, opts vfs.SetStatOptions, useFh bool, fh uint64) error { - conn := i.fs.conn - task := kernel.TaskFromContext(ctx) - if task == nil { - log.Warningf("couldn't get kernel task from context") - return syserror.EINVAL - } - - // We should retain the original file type when assigning new mode. - fileType := uint16(i.Mode()) & linux.S_IFMT - fattrMask := fattrMaskFromStats(opts.Stat.Mask) - if useFh { - fattrMask |= linux.FATTR_FH - } - in := linux.FUSESetAttrIn{ - Valid: fattrMask, - Fh: fh, - Size: opts.Stat.Size, - Atime: uint64(opts.Stat.Atime.Sec), - Mtime: uint64(opts.Stat.Mtime.Sec), - Ctime: uint64(opts.Stat.Ctime.Sec), - AtimeNsec: opts.Stat.Atime.Nsec, - MtimeNsec: opts.Stat.Mtime.Nsec, - CtimeNsec: opts.Stat.Ctime.Nsec, - Mode: uint32(fileType | opts.Stat.Mode), - UID: opts.Stat.UID, - GID: opts.Stat.GID, - } - req, err := conn.NewRequest(creds, uint32(task.ThreadID()), i.nodeID, linux.FUSE_SETATTR, &in) - if err != nil { - return err - } - - res, err := conn.Call(task, req) - if err != nil { - return err - } - if err := res.Error(); err != nil { - return err - } - out := linux.FUSEGetAttrOut{} - if err := res.UnmarshalPayload(&out); err != nil { - return err - } - - // Set the metadata of kernfs.InodeAttrs. - if err := i.SetInodeStat(ctx, fs, creds, vfs.SetStatOptions{ - Stat: statFromFUSEAttr(out.Attr, linux.STATX_ALL, i.fs.devMinor), - }); err != nil { - return err - } - - return nil -} |