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