summaryrefslogtreecommitdiffhomepage
path: root/pkg/sentry/fsimpl/fuse/fusefs.go
diff options
context:
space:
mode:
authorgVisor bot <gvisor-bot@google.com>2020-09-11 23:13:20 +0000
committergVisor bot <gvisor-bot@google.com>2020-09-11 23:13:20 +0000
commit2c7d03d2378865ee92d7ae24e48748f17233e783 (patch)
treef84e553a41525dd9dc4dc64f6b6fb1bbdf1481a8 /pkg/sentry/fsimpl/fuse/fusefs.go
parent51194daa8da2bee362c9ecb6491016038e5f8436 (diff)
parent1f4fb817c8ff8be7239a99baff01d8f20b2e9abd (diff)
Merge release-20200907.0-36-g1f4fb817c (automated)
Diffstat (limited to 'pkg/sentry/fsimpl/fuse/fusefs.go')
-rw-r--r--pkg/sentry/fsimpl/fuse/fusefs.go569
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 := &regularFileFD{}
- 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
-}