github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/pkg/sentry/fs/fsutil/file.go (about) 1 // Copyright 2018 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 fsutil 16 17 import ( 18 "io" 19 20 "github.com/SagerNet/gvisor/pkg/context" 21 "github.com/SagerNet/gvisor/pkg/errors/linuxerr" 22 "github.com/SagerNet/gvisor/pkg/sentry/arch" 23 "github.com/SagerNet/gvisor/pkg/sentry/fs" 24 "github.com/SagerNet/gvisor/pkg/sentry/memmap" 25 "github.com/SagerNet/gvisor/pkg/syserror" 26 "github.com/SagerNet/gvisor/pkg/usermem" 27 "github.com/SagerNet/gvisor/pkg/waiter" 28 ) 29 30 // FileNoopRelease implements fs.FileOperations.Release for files that have no 31 // resources to release. 32 type FileNoopRelease struct{} 33 34 // Release is a no-op. 35 func (FileNoopRelease) Release(context.Context) {} 36 37 // SeekWithDirCursor is used to implement fs.FileOperations.Seek. If dirCursor 38 // is not nil and the seek was on a directory, the cursor will be updated. 39 // 40 // Currently only seeking to 0 on a directory is supported. 41 // 42 // FIXME(b/33075855): Lift directory seeking limitations. 43 func SeekWithDirCursor(ctx context.Context, file *fs.File, whence fs.SeekWhence, offset int64, dirCursor *string) (int64, error) { 44 inode := file.Dirent.Inode 45 current := file.Offset() 46 47 // Does the Inode represents a non-seekable type? 48 if fs.IsPipe(inode.StableAttr) || fs.IsSocket(inode.StableAttr) { 49 return current, linuxerr.ESPIPE 50 } 51 52 // Does the Inode represent a character device? 53 if fs.IsCharDevice(inode.StableAttr) { 54 // Ignore seek requests. 55 // 56 // FIXME(b/34716638): This preserves existing 57 // behavior but is not universally correct. 58 return 0, nil 59 } 60 61 // Otherwise compute the new offset. 62 switch whence { 63 case fs.SeekSet: 64 switch inode.StableAttr.Type { 65 case fs.RegularFile, fs.SpecialFile, fs.BlockDevice: 66 if offset < 0 { 67 return current, linuxerr.EINVAL 68 } 69 return offset, nil 70 case fs.Directory, fs.SpecialDirectory: 71 if offset != 0 { 72 return current, linuxerr.EINVAL 73 } 74 // SEEK_SET to 0 moves the directory "cursor" to the beginning. 75 if dirCursor != nil { 76 *dirCursor = "" 77 } 78 return 0, nil 79 default: 80 return current, linuxerr.EINVAL 81 } 82 case fs.SeekCurrent: 83 switch inode.StableAttr.Type { 84 case fs.RegularFile, fs.SpecialFile, fs.BlockDevice: 85 if current+offset < 0 { 86 return current, linuxerr.EINVAL 87 } 88 return current + offset, nil 89 case fs.Directory, fs.SpecialDirectory: 90 if offset != 0 { 91 return current, linuxerr.EINVAL 92 } 93 return current, nil 94 default: 95 return current, linuxerr.EINVAL 96 } 97 case fs.SeekEnd: 98 switch inode.StableAttr.Type { 99 case fs.RegularFile, fs.BlockDevice: 100 // Allow the file to determine the end. 101 uattr, err := inode.UnstableAttr(ctx) 102 if err != nil { 103 return current, err 104 } 105 sz := uattr.Size 106 if sz+offset < 0 { 107 return current, linuxerr.EINVAL 108 } 109 return sz + offset, nil 110 // FIXME(b/34778850): This is not universally correct. 111 // Remove SpecialDirectory. 112 case fs.SpecialDirectory: 113 if offset != 0 { 114 return current, linuxerr.EINVAL 115 } 116 // SEEK_END to 0 moves the directory "cursor" to the end. 117 // 118 // FIXME(b/35442290): The ensures that after the seek, 119 // reading on the directory will get EOF. But it is not 120 // correct in general because the directory can grow in 121 // size; attempting to read those new entries will be 122 // futile (EOF will always be the result). 123 return fs.FileMaxOffset, nil 124 default: 125 return current, linuxerr.EINVAL 126 } 127 } 128 129 // Not a valid seek request. 130 return current, linuxerr.EINVAL 131 } 132 133 // FileGenericSeek implements fs.FileOperations.Seek for files that use a 134 // generic seek implementation. 135 type FileGenericSeek struct{} 136 137 // Seek implements fs.FileOperations.Seek. 138 func (FileGenericSeek) Seek(ctx context.Context, file *fs.File, whence fs.SeekWhence, offset int64) (int64, error) { 139 return SeekWithDirCursor(ctx, file, whence, offset, nil) 140 } 141 142 // FileZeroSeek implements fs.FileOperations.Seek for files that maintain a 143 // constant zero-value offset and require a no-op Seek. 144 type FileZeroSeek struct{} 145 146 // Seek implements fs.FileOperations.Seek. 147 func (FileZeroSeek) Seek(context.Context, *fs.File, fs.SeekWhence, int64) (int64, error) { 148 return 0, nil 149 } 150 151 // FileNoSeek implements fs.FileOperations.Seek to return EINVAL. 152 type FileNoSeek struct{} 153 154 // Seek implements fs.FileOperations.Seek. 155 func (FileNoSeek) Seek(context.Context, *fs.File, fs.SeekWhence, int64) (int64, error) { 156 return 0, linuxerr.EINVAL 157 } 158 159 // FilePipeSeek implements fs.FileOperations.Seek and can be used for files 160 // that behave like pipes (seeking is not supported). 161 type FilePipeSeek struct{} 162 163 // Seek implements fs.FileOperations.Seek. 164 func (FilePipeSeek) Seek(context.Context, *fs.File, fs.SeekWhence, int64) (int64, error) { 165 return 0, linuxerr.ESPIPE 166 } 167 168 // FileNotDirReaddir implements fs.FileOperations.Readdir for non-directories. 169 type FileNotDirReaddir struct{} 170 171 // Readdir implements fs.FileOperations.FileNotDirReaddir. 172 func (FileNotDirReaddir) Readdir(context.Context, *fs.File, fs.DentrySerializer) (int64, error) { 173 return 0, syserror.ENOTDIR 174 } 175 176 // FileNoFsync implements fs.FileOperations.Fsync for files that don't support 177 // syncing. 178 type FileNoFsync struct{} 179 180 // Fsync implements fs.FileOperations.Fsync. 181 func (FileNoFsync) Fsync(context.Context, *fs.File, int64, int64, fs.SyncType) error { 182 return linuxerr.EINVAL 183 } 184 185 // FileNoopFsync implements fs.FileOperations.Fsync for files that don't need 186 // to synced. 187 type FileNoopFsync struct{} 188 189 // Fsync implements fs.FileOperations.Fsync. 190 func (FileNoopFsync) Fsync(context.Context, *fs.File, int64, int64, fs.SyncType) error { 191 return nil 192 } 193 194 // FileNoopFlush implements fs.FileOperations.Flush as a no-op. 195 type FileNoopFlush struct{} 196 197 // Flush implements fs.FileOperations.Flush. 198 func (FileNoopFlush) Flush(context.Context, *fs.File) error { 199 return nil 200 } 201 202 // FileNoMMap implements fs.FileOperations.Mappable for files that cannot 203 // be memory mapped. 204 type FileNoMMap struct{} 205 206 // ConfigureMMap implements fs.FileOperations.ConfigureMMap. 207 func (FileNoMMap) ConfigureMMap(context.Context, *fs.File, *memmap.MMapOpts) error { 208 return linuxerr.ENODEV 209 } 210 211 // GenericConfigureMMap implements fs.FileOperations.ConfigureMMap for most 212 // filesystems that support memory mapping. 213 func GenericConfigureMMap(file *fs.File, m memmap.Mappable, opts *memmap.MMapOpts) error { 214 opts.Mappable = m 215 opts.MappingIdentity = file 216 file.IncRef() 217 return nil 218 } 219 220 // FileNoIoctl implements fs.FileOperations.Ioctl for files that don't 221 // implement the ioctl syscall. 222 type FileNoIoctl struct{} 223 224 // Ioctl implements fs.FileOperations.Ioctl. 225 func (FileNoIoctl) Ioctl(context.Context, *fs.File, usermem.IO, arch.SyscallArguments) (uintptr, error) { 226 return 0, syserror.ENOTTY 227 } 228 229 // FileNoSplice implements fs.FileOperations.ReadFrom and 230 // fs.FileOperations.WriteTo for files that don't support splice. 231 type FileNoSplice struct{} 232 233 // WriteTo implements fs.FileOperations.WriteTo. 234 func (FileNoSplice) WriteTo(context.Context, *fs.File, io.Writer, int64, bool) (int64, error) { 235 return 0, syserror.ENOSYS 236 } 237 238 // ReadFrom implements fs.FileOperations.ReadFrom. 239 func (FileNoSplice) ReadFrom(context.Context, *fs.File, io.Reader, int64) (int64, error) { 240 return 0, syserror.ENOSYS 241 } 242 243 // DirFileOperations implements most of fs.FileOperations for directories, 244 // except for Readdir and UnstableAttr which the embedding type must implement. 245 type DirFileOperations struct { 246 waiter.AlwaysReady 247 FileGenericSeek 248 FileNoIoctl 249 FileNoMMap 250 FileNoopFlush 251 FileNoopFsync 252 FileNoopRelease 253 FileNoSplice 254 } 255 256 // Read implements fs.FileOperations.Read 257 func (*DirFileOperations) Read(context.Context, *fs.File, usermem.IOSequence, int64) (int64, error) { 258 return 0, syserror.EISDIR 259 } 260 261 // Write implements fs.FileOperations.Write. 262 func (*DirFileOperations) Write(context.Context, *fs.File, usermem.IOSequence, int64) (int64, error) { 263 return 0, syserror.EISDIR 264 } 265 266 // StaticDirFileOperations implements fs.FileOperations for directories with 267 // static children. 268 // 269 // +stateify savable 270 type StaticDirFileOperations struct { 271 DirFileOperations `state:"nosave"` 272 FileUseInodeUnstableAttr `state:"nosave"` 273 274 // dentryMap is a SortedDentryMap used to implement Readdir. 275 dentryMap *fs.SortedDentryMap 276 277 // dirCursor contains the name of the last directory entry that was 278 // serialized. 279 dirCursor string 280 } 281 282 // NewStaticDirFileOperations returns a new StaticDirFileOperations that will 283 // iterate the given denty map. 284 func NewStaticDirFileOperations(dentries *fs.SortedDentryMap) *StaticDirFileOperations { 285 return &StaticDirFileOperations{ 286 dentryMap: dentries, 287 } 288 } 289 290 // IterateDir implements DirIterator.IterateDir. 291 func (sdfo *StaticDirFileOperations) IterateDir(ctx context.Context, d *fs.Dirent, dirCtx *fs.DirCtx, offset int) (int, error) { 292 n, err := fs.GenericReaddir(dirCtx, sdfo.dentryMap) 293 return offset + n, err 294 } 295 296 // Readdir implements fs.FileOperations.Readdir. 297 func (sdfo *StaticDirFileOperations) Readdir(ctx context.Context, file *fs.File, serializer fs.DentrySerializer) (int64, error) { 298 root := fs.RootFromContext(ctx) 299 if root != nil { 300 defer root.DecRef(ctx) 301 } 302 dirCtx := &fs.DirCtx{ 303 Serializer: serializer, 304 DirCursor: &sdfo.dirCursor, 305 } 306 return fs.DirentReaddir(ctx, file.Dirent, sdfo, root, dirCtx, file.Offset()) 307 } 308 309 // NoReadWriteFile is a file that does not support reading or writing. 310 // 311 // +stateify savable 312 type NoReadWriteFile struct { 313 waiter.AlwaysReady `state:"nosave"` 314 FileGenericSeek `state:"nosave"` 315 FileNoIoctl `state:"nosave"` 316 FileNoMMap `state:"nosave"` 317 FileNoopFsync `state:"nosave"` 318 FileNoopFlush `state:"nosave"` 319 FileNoopRelease `state:"nosave"` 320 FileNoRead `state:"nosave"` 321 FileNoWrite `state:"nosave"` 322 FileNotDirReaddir `state:"nosave"` 323 FileUseInodeUnstableAttr `state:"nosave"` 324 FileNoSplice `state:"nosave"` 325 } 326 327 var _ fs.FileOperations = (*NoReadWriteFile)(nil) 328 329 // FileStaticContentReader is a helper to implement fs.FileOperations.Read with 330 // static content. 331 // 332 // +stateify savable 333 type FileStaticContentReader struct { 334 // content is immutable. 335 content []byte 336 } 337 338 // NewFileStaticContentReader initializes a FileStaticContentReader with the 339 // given content. 340 func NewFileStaticContentReader(b []byte) FileStaticContentReader { 341 return FileStaticContentReader{ 342 content: b, 343 } 344 } 345 346 // Read implements fs.FileOperations.Read. 347 func (scr *FileStaticContentReader) Read(ctx context.Context, _ *fs.File, dst usermem.IOSequence, offset int64) (int64, error) { 348 if offset < 0 { 349 return 0, linuxerr.EINVAL 350 } 351 if offset >= int64(len(scr.content)) { 352 return 0, nil 353 } 354 n, err := dst.CopyOut(ctx, scr.content[offset:]) 355 return int64(n), err 356 } 357 358 // FileNoopWrite implements fs.FileOperations.Write as a noop. 359 type FileNoopWrite struct{} 360 361 // Write implements fs.FileOperations.Write. 362 func (FileNoopWrite) Write(_ context.Context, _ *fs.File, src usermem.IOSequence, _ int64) (int64, error) { 363 return src.NumBytes(), nil 364 } 365 366 // FileNoRead implements fs.FileOperations.Read to return EINVAL. 367 type FileNoRead struct{} 368 369 // Read implements fs.FileOperations.Read. 370 func (FileNoRead) Read(context.Context, *fs.File, usermem.IOSequence, int64) (int64, error) { 371 return 0, linuxerr.EINVAL 372 } 373 374 // FileNoWrite implements fs.FileOperations.Write to return EINVAL. 375 type FileNoWrite struct{} 376 377 // Write implements fs.FileOperations.Write. 378 func (FileNoWrite) Write(context.Context, *fs.File, usermem.IOSequence, int64) (int64, error) { 379 return 0, linuxerr.EINVAL 380 } 381 382 // FileNoopRead implement fs.FileOperations.Read as a noop. 383 type FileNoopRead struct{} 384 385 // Read implements fs.FileOperations.Read. 386 func (FileNoopRead) Read(context.Context, *fs.File, usermem.IOSequence, int64) (int64, error) { 387 return 0, nil 388 } 389 390 // FileUseInodeUnstableAttr implements fs.FileOperations.UnstableAttr by calling 391 // InodeOperations.UnstableAttr. 392 type FileUseInodeUnstableAttr struct{} 393 394 // UnstableAttr implements fs.FileOperations.UnstableAttr. 395 func (FileUseInodeUnstableAttr) UnstableAttr(ctx context.Context, file *fs.File) (fs.UnstableAttr, error) { 396 return file.Dirent.Inode.UnstableAttr(ctx) 397 }