github.com/metacubex/gvisor@v0.0.0-20240320004321-933faba989ec/pkg/sentry/fsimpl/fuse/file.go (about) 1 // Copyright 2020 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 "github.com/metacubex/gvisor/pkg/abi/linux" 19 "github.com/metacubex/gvisor/pkg/atomicbitops" 20 "github.com/metacubex/gvisor/pkg/context" 21 "github.com/metacubex/gvisor/pkg/errors/linuxerr" 22 "github.com/metacubex/gvisor/pkg/sentry/fsimpl/kernfs" 23 "github.com/metacubex/gvisor/pkg/sentry/kernel/auth" 24 "github.com/metacubex/gvisor/pkg/sentry/vfs" 25 "github.com/metacubex/gvisor/pkg/usermem" 26 ) 27 28 // fileDescription implements vfs.FileDescriptionImpl for fuse. 29 // 30 // +stateify savable 31 type fileDescription struct { 32 vfsfd vfs.FileDescription 33 vfs.FileDescriptionDefaultImpl 34 vfs.DentryMetadataFileDescriptionImpl 35 vfs.LockFD 36 37 // the file handle used in userspace. 38 Fh uint64 39 40 // Nonseekable indicates we cannot perform seek on a file. 41 Nonseekable bool 42 43 // DirectIO suggests that fuse use direct IO operations. 44 DirectIO bool 45 46 // OpenFlag is the flag returned by open. 47 OpenFlag uint32 48 49 // off is the file offset. 50 off atomicbitops.Int64 51 } 52 53 func (fd *fileDescription) dentry() *kernfs.Dentry { 54 return fd.vfsfd.Dentry().Impl().(*kernfs.Dentry) 55 } 56 57 func (fd *fileDescription) inode() *inode { 58 return fd.dentry().Inode().(*inode) 59 } 60 61 func (fd *fileDescription) filesystem() *vfs.Filesystem { 62 return fd.vfsfd.VirtualDentry().Mount().Filesystem() 63 } 64 65 func (fd *fileDescription) statusFlags() uint32 { 66 return fd.vfsfd.StatusFlags() 67 } 68 69 // Release implements vfs.FileDescriptionImpl.Release. 70 func (fd *fileDescription) Release(ctx context.Context) { 71 // no need to release if FUSE server doesn't implement Open. 72 conn := fd.inode().fs.conn 73 if conn.noOpen { 74 return 75 } 76 77 in := linux.FUSEReleaseIn{ 78 Fh: fd.Fh, 79 Flags: fd.statusFlags(), 80 } 81 // TODO(gvisor.dev/issue/3245): add logic when we support file lock owners. 82 inode := fd.inode() 83 inode.attrMu.Lock() 84 defer inode.attrMu.Unlock() 85 var opcode linux.FUSEOpcode 86 if inode.filemode().IsDir() { 87 opcode = linux.FUSE_RELEASEDIR 88 } else { 89 opcode = linux.FUSE_RELEASE 90 } 91 // Ignoring errors and FUSE server replies is analogous to Linux's behavior. 92 req := conn.NewRequest(auth.CredentialsFromContext(ctx), pidFromContext(ctx), inode.nodeID, opcode, &in) 93 // The reply will be ignored since no callback is defined in asyncCallBack(). 94 conn.CallAsync(ctx, req) 95 } 96 97 // OnClose implements vfs.FileDescriptionImpl.OnClose. 98 func (fd *fileDescription) OnClose(ctx context.Context) error { 99 inode := fd.inode() 100 conn := inode.fs.conn 101 inode.attrMu.Lock() 102 defer inode.attrMu.Unlock() 103 104 in := linux.FUSEFlushIn{ 105 Fh: fd.Fh, 106 LockOwner: 0, // TODO(gvisor.dev/issue/3245): file lock 107 } 108 req := conn.NewRequest(auth.CredentialsFromContext(ctx), pidFromContext(ctx), inode.nodeID, linux.FUSE_FLUSH, &in) 109 return conn.CallAsync(ctx, req) 110 } 111 112 // PRead implements vfs.FileDescriptionImpl.PRead. 113 func (fd *fileDescription) PRead(ctx context.Context, dst usermem.IOSequence, offset int64, opts vfs.ReadOptions) (int64, error) { 114 return 0, nil 115 } 116 117 // Read implements vfs.FileDescriptionImpl.Read. 118 func (fd *fileDescription) Read(ctx context.Context, dst usermem.IOSequence, opts vfs.ReadOptions) (int64, error) { 119 return 0, nil 120 } 121 122 // PWrite implements vfs.FileDescriptionImpl.PWrite. 123 func (fd *fileDescription) PWrite(ctx context.Context, src usermem.IOSequence, offset int64, opts vfs.WriteOptions) (int64, error) { 124 return 0, nil 125 } 126 127 // Write implements vfs.FileDescriptionImpl.Write. 128 func (fd *fileDescription) Write(ctx context.Context, src usermem.IOSequence, opts vfs.WriteOptions) (int64, error) { 129 return 0, nil 130 } 131 132 // Seek implements vfs.FileDescriptionImpl.Seek. 133 func (fd *fileDescription) Seek(ctx context.Context, offset int64, whence int32) (int64, error) { 134 return 0, nil 135 } 136 137 // Stat implements vfs.FileDescriptionImpl.Stat. 138 func (fd *fileDescription) Stat(ctx context.Context, opts vfs.StatOptions) (linux.Statx, error) { 139 fs := fd.filesystem() 140 inode := fd.inode() 141 return inode.Stat(ctx, fs, opts) 142 } 143 144 // SetStat implements vfs.FileDescriptionImpl.SetStat. 145 func (fd *fileDescription) SetStat(ctx context.Context, opts vfs.SetStatOptions) error { 146 fs := fd.filesystem() 147 creds := auth.CredentialsFromContext(ctx) 148 inode := fd.inode() 149 inode.attrMu.Lock() 150 defer inode.attrMu.Unlock() 151 if err := vfs.CheckSetStat(ctx, creds, &opts, inode.filemode(), auth.KUID(inode.uid.Load()), auth.KGID(inode.gid.Load())); err != nil { 152 return err 153 } 154 return inode.setAttr(ctx, fs, creds, opts, fhOptions{useFh: true, fh: fd.Fh}) 155 } 156 157 // Sync implements vfs.FileDescriptionImpl.Sync. 158 func (fd *fileDescription) Sync(ctx context.Context) error { 159 inode := fd.inode() 160 inode.attrMu.Lock() 161 defer inode.attrMu.Unlock() 162 conn := inode.fs.conn 163 // no need to proceed if FUSE server doesn't implement Open. 164 if conn.noOpen { 165 return linuxerr.EINVAL 166 } 167 168 in := linux.FUSEFsyncIn{ 169 Fh: fd.Fh, 170 FsyncFlags: fd.statusFlags(), 171 } 172 // Ignoring errors and FUSE server replies is analogous to Linux's behavior. 173 req := conn.NewRequest(auth.CredentialsFromContext(ctx), pidFromContext(ctx), inode.nodeID, linux.FUSE_FSYNC, &in) 174 // The reply will be ignored since no callback is defined in asyncCallBack(). 175 conn.CallAsync(ctx, req) 176 return nil 177 }