github.com/nicocha30/gvisor-ligolo@v0.0.0-20230726075806-989fa2c0a413/pkg/sentry/fsimpl/kernfs/fd_impl_util.go (about) 1 // Copyright 2019 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 kernfs 16 17 import ( 18 "fmt" 19 20 "github.com/nicocha30/gvisor-ligolo/pkg/abi/linux" 21 "github.com/nicocha30/gvisor-ligolo/pkg/context" 22 "github.com/nicocha30/gvisor-ligolo/pkg/errors/linuxerr" 23 "github.com/nicocha30/gvisor-ligolo/pkg/sentry/kernel/auth" 24 "github.com/nicocha30/gvisor-ligolo/pkg/sentry/memmap" 25 "github.com/nicocha30/gvisor-ligolo/pkg/sentry/vfs" 26 "github.com/nicocha30/gvisor-ligolo/pkg/sync" 27 "github.com/nicocha30/gvisor-ligolo/pkg/usermem" 28 ) 29 30 // SeekEndConfig describes the SEEK_END behaviour for FDs. 31 // 32 // +stateify savable 33 type SeekEndConfig int 34 35 // Constants related to SEEK_END behaviour for FDs. 36 const ( 37 // Consider the end of the file to be after the final static entry. This is 38 // the default option. 39 SeekEndStaticEntries = iota 40 // Consider the end of the file to be at offset 0. 41 SeekEndZero 42 ) 43 44 // GenericDirectoryFDOptions contains configuration for a GenericDirectoryFD. 45 // 46 // +stateify savable 47 type GenericDirectoryFDOptions struct { 48 SeekEnd SeekEndConfig 49 } 50 51 // GenericDirectoryFD implements vfs.FileDescriptionImpl for a generic directory 52 // inode that uses OrderChildren to track child nodes. 53 // 54 // Note that GenericDirectoryFD holds a lock over OrderedChildren while calling 55 // IterDirents callback. The IterDirents callback therefore cannot hash or 56 // unhash children, or recursively call IterDirents on the same underlying 57 // inode. 58 // 59 // Must be initialize with Init before first use. 60 // 61 // Lock ordering: mu => children.mu. 62 // 63 // +stateify savable 64 type GenericDirectoryFD struct { 65 vfs.FileDescriptionDefaultImpl 66 vfs.DirectoryFileDescriptionDefaultImpl 67 vfs.LockFD 68 69 // Immutable. 70 seekEnd SeekEndConfig 71 72 vfsfd vfs.FileDescription 73 children *OrderedChildren 74 75 // mu protects the fields below. 76 mu sync.Mutex `state:"nosave"` 77 78 // off is the current directory offset. Protected by "mu". 79 off int64 80 } 81 82 // NewGenericDirectoryFD creates a new GenericDirectoryFD and returns its 83 // dentry. 84 func NewGenericDirectoryFD(m *vfs.Mount, d *Dentry, children *OrderedChildren, locks *vfs.FileLocks, opts *vfs.OpenOptions, fdOpts GenericDirectoryFDOptions) (*GenericDirectoryFD, error) { 85 fd := &GenericDirectoryFD{} 86 if err := fd.Init(children, locks, opts, fdOpts); err != nil { 87 return nil, err 88 } 89 if err := fd.vfsfd.Init(fd, opts.Flags, m, d.VFSDentry(), &vfs.FileDescriptionOptions{}); err != nil { 90 return nil, err 91 } 92 return fd, nil 93 } 94 95 // Init initializes a GenericDirectoryFD. Use it when overriding 96 // GenericDirectoryFD. Caller must call fd.VFSFileDescription.Init() with the 97 // correct implementation. 98 func (fd *GenericDirectoryFD) Init(children *OrderedChildren, locks *vfs.FileLocks, opts *vfs.OpenOptions, fdOpts GenericDirectoryFDOptions) error { 99 if vfs.AccessTypesForOpenFlags(opts)&vfs.MayWrite != 0 { 100 // Can't open directories for writing. 101 return linuxerr.EISDIR 102 } 103 fd.LockFD.Init(locks) 104 fd.seekEnd = fdOpts.SeekEnd 105 fd.children = children 106 return nil 107 } 108 109 // VFSFileDescription returns a pointer to the vfs.FileDescription representing 110 // this object. 111 func (fd *GenericDirectoryFD) VFSFileDescription() *vfs.FileDescription { 112 return &fd.vfsfd 113 } 114 115 // ConfigureMMap implements vfs.FileDescriptionImpl.ConfigureMMap. 116 func (fd *GenericDirectoryFD) ConfigureMMap(ctx context.Context, opts *memmap.MMapOpts) error { 117 return fd.FileDescriptionDefaultImpl.ConfigureMMap(ctx, opts) 118 } 119 120 // Read implmenets vfs.FileDescriptionImpl.Read. 121 func (fd *GenericDirectoryFD) Read(ctx context.Context, dst usermem.IOSequence, opts vfs.ReadOptions) (int64, error) { 122 return fd.DirectoryFileDescriptionDefaultImpl.Read(ctx, dst, opts) 123 } 124 125 // PRead implmenets vfs.FileDescriptionImpl.PRead. 126 func (fd *GenericDirectoryFD) PRead(ctx context.Context, dst usermem.IOSequence, offset int64, opts vfs.ReadOptions) (int64, error) { 127 return fd.DirectoryFileDescriptionDefaultImpl.PRead(ctx, dst, offset, opts) 128 } 129 130 // Write implements vfs.FileDescriptionImpl.Write. 131 func (fd *GenericDirectoryFD) Write(ctx context.Context, src usermem.IOSequence, opts vfs.WriteOptions) (int64, error) { 132 return fd.DirectoryFileDescriptionDefaultImpl.Write(ctx, src, opts) 133 } 134 135 // PWrite implements vfs.FileDescriptionImpl.PWrite. 136 func (fd *GenericDirectoryFD) PWrite(ctx context.Context, src usermem.IOSequence, offset int64, opts vfs.WriteOptions) (int64, error) { 137 return fd.DirectoryFileDescriptionDefaultImpl.PWrite(ctx, src, offset, opts) 138 } 139 140 // Release implements vfs.FileDescriptionImpl.Release. 141 func (fd *GenericDirectoryFD) Release(context.Context) {} 142 143 func (fd *GenericDirectoryFD) filesystem() *vfs.Filesystem { 144 return fd.vfsfd.VirtualDentry().Mount().Filesystem() 145 } 146 147 func (fd *GenericDirectoryFD) dentry() *Dentry { 148 return fd.vfsfd.Dentry().Impl().(*Dentry) 149 } 150 151 func (fd *GenericDirectoryFD) inode() Inode { 152 return fd.dentry().inode 153 } 154 155 // IterDirents implements vfs.FileDescriptionImpl.IterDirents. IterDirents holds 156 // o.mu when calling cb. 157 func (fd *GenericDirectoryFD) IterDirents(ctx context.Context, cb vfs.IterDirentsCallback) error { 158 fd.mu.Lock() 159 defer fd.mu.Unlock() 160 161 opts := vfs.StatOptions{Mask: linux.STATX_INO} 162 // Handle ".". 163 if fd.off == 0 { 164 stat, err := fd.inode().Stat(ctx, fd.filesystem(), opts) 165 if err != nil { 166 return err 167 } 168 dirent := vfs.Dirent{ 169 Name: ".", 170 Type: linux.DT_DIR, 171 Ino: stat.Ino, 172 NextOff: 1, 173 } 174 if err := cb.Handle(dirent); err != nil { 175 return err 176 } 177 fd.off++ 178 } 179 180 // Handle "..". 181 if fd.off == 1 { 182 parentInode := genericParentOrSelf(fd.dentry()).inode 183 stat, err := parentInode.Stat(ctx, fd.filesystem(), opts) 184 if err != nil { 185 return err 186 } 187 dirent := vfs.Dirent{ 188 Name: "..", 189 Type: linux.FileMode(stat.Mode).DirentType(), 190 Ino: stat.Ino, 191 NextOff: 2, 192 } 193 if err := cb.Handle(dirent); err != nil { 194 return err 195 } 196 fd.off++ 197 } 198 199 // Handle static children. 200 fd.children.mu.RLock() 201 defer fd.children.mu.RUnlock() 202 // fd.off accounts for "." and "..", but fd.children do not track 203 // these. 204 childIdx := fd.off - 2 205 for it := fd.children.nthLocked(childIdx); it != nil; it = it.Next() { 206 stat, err := it.inode.Stat(ctx, fd.filesystem(), opts) 207 if err != nil { 208 return err 209 } 210 dirent := vfs.Dirent{ 211 Name: it.name, 212 Type: linux.FileMode(stat.Mode).DirentType(), 213 Ino: stat.Ino, 214 NextOff: fd.off + 1, 215 } 216 if err := cb.Handle(dirent); err != nil { 217 return err 218 } 219 fd.off++ 220 } 221 222 var err error 223 relOffset := fd.off - int64(len(fd.children.set)) - 2 224 fd.off, err = fd.inode().IterDirents(ctx, fd.vfsfd.Mount(), cb, fd.off, relOffset) 225 return err 226 } 227 228 // Seek implements vfs.FileDescriptionImpl.Seek. 229 func (fd *GenericDirectoryFD) Seek(ctx context.Context, offset int64, whence int32) (int64, error) { 230 fd.mu.Lock() 231 defer fd.mu.Unlock() 232 233 switch whence { 234 case linux.SEEK_SET: 235 // Use offset as given. 236 case linux.SEEK_CUR: 237 offset += fd.off 238 case linux.SEEK_END: 239 switch fd.seekEnd { 240 case SeekEndStaticEntries: 241 fd.children.mu.RLock() 242 offset += int64(len(fd.children.set)) 243 offset += 2 // '.' and '..' aren't tracked in children. 244 fd.children.mu.RUnlock() 245 case SeekEndZero: 246 // No-op: offset += 0. 247 default: 248 panic(fmt.Sprintf("Invalid GenericDirectoryFD.seekEnd = %v", fd.seekEnd)) 249 } 250 default: 251 return 0, linuxerr.EINVAL 252 } 253 if offset < 0 { 254 return 0, linuxerr.EINVAL 255 } 256 fd.off = offset 257 return offset, nil 258 } 259 260 // Stat implements vfs.FileDescriptionImpl.Stat. 261 func (fd *GenericDirectoryFD) Stat(ctx context.Context, opts vfs.StatOptions) (linux.Statx, error) { 262 fs := fd.filesystem() 263 inode := fd.inode() 264 return inode.Stat(ctx, fs, opts) 265 } 266 267 // SetStat implements vfs.FileDescriptionImpl.SetStat. 268 func (fd *GenericDirectoryFD) SetStat(ctx context.Context, opts vfs.SetStatOptions) error { 269 creds := auth.CredentialsFromContext(ctx) 270 return fd.inode().SetStat(ctx, fd.filesystem(), creds, opts) 271 } 272 273 // Allocate implements vfs.FileDescriptionImpl.Allocate. 274 func (fd *GenericDirectoryFD) Allocate(ctx context.Context, mode, offset, length uint64) error { 275 return fd.DirectoryFileDescriptionDefaultImpl.Allocate(ctx, mode, offset, length) 276 }