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