github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/pkg/sentry/vfs/file_description_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 vfs 16 17 import ( 18 "bytes" 19 "io" 20 21 "github.com/SagerNet/gvisor/pkg/abi/linux" 22 "github.com/SagerNet/gvisor/pkg/context" 23 "github.com/SagerNet/gvisor/pkg/errors/linuxerr" 24 "github.com/SagerNet/gvisor/pkg/sentry/arch" 25 fslock "github.com/SagerNet/gvisor/pkg/sentry/fs/lock" 26 "github.com/SagerNet/gvisor/pkg/sentry/memmap" 27 "github.com/SagerNet/gvisor/pkg/sync" 28 "github.com/SagerNet/gvisor/pkg/syserror" 29 "github.com/SagerNet/gvisor/pkg/usermem" 30 "github.com/SagerNet/gvisor/pkg/waiter" 31 ) 32 33 // The following design pattern is strongly recommended for filesystem 34 // implementations to adapt: 35 // - Have a local fileDescription struct (containing FileDescription) which 36 // embeds FileDescriptionDefaultImpl and overrides the default methods 37 // which are common to all fd implementations for that filesystem like 38 // StatusFlags, SetStatusFlags, Stat, SetStat, StatFS, etc. 39 // - This should be embedded in all file description implementations as the 40 // first field by value. 41 // - Directory FDs would also embed DirectoryFileDescriptionDefaultImpl. 42 43 // FileDescriptionDefaultImpl may be embedded by implementations of 44 // FileDescriptionImpl to obtain implementations of many FileDescriptionImpl 45 // methods with default behavior analogous to Linux's. 46 // 47 // +stateify savable 48 type FileDescriptionDefaultImpl struct{} 49 50 // OnClose implements FileDescriptionImpl.OnClose analogously to 51 // file_operations::flush == NULL in Linux. 52 func (FileDescriptionDefaultImpl) OnClose(ctx context.Context) error { 53 return nil 54 } 55 56 // StatFS implements FileDescriptionImpl.StatFS analogously to 57 // super_operations::statfs == NULL in Linux. 58 func (FileDescriptionDefaultImpl) StatFS(ctx context.Context) (linux.Statfs, error) { 59 return linux.Statfs{}, syserror.ENOSYS 60 } 61 62 // Allocate implements FileDescriptionImpl.Allocate analogously to 63 // fallocate called on an invalid type of file in Linux. 64 // 65 // Note that directories can rely on this implementation even though they 66 // should technically return EISDIR. Allocate should never be called for a 67 // directory, because it requires a writable fd. 68 func (FileDescriptionDefaultImpl) Allocate(ctx context.Context, mode, offset, length uint64) error { 69 return linuxerr.ENODEV 70 } 71 72 // Readiness implements waiter.Waitable.Readiness analogously to 73 // file_operations::poll == NULL in Linux. 74 func (FileDescriptionDefaultImpl) Readiness(mask waiter.EventMask) waiter.EventMask { 75 // include/linux/poll.h:vfs_poll() => DEFAULT_POLLMASK 76 return waiter.ReadableEvents | waiter.WritableEvents 77 } 78 79 // EventRegister implements waiter.Waitable.EventRegister analogously to 80 // file_operations::poll == NULL in Linux. 81 func (FileDescriptionDefaultImpl) EventRegister(e *waiter.Entry, mask waiter.EventMask) { 82 } 83 84 // EventUnregister implements waiter.Waitable.EventUnregister analogously to 85 // file_operations::poll == NULL in Linux. 86 func (FileDescriptionDefaultImpl) EventUnregister(e *waiter.Entry) { 87 } 88 89 // PRead implements FileDescriptionImpl.PRead analogously to 90 // file_operations::read == file_operations::read_iter == NULL in Linux. 91 func (FileDescriptionDefaultImpl) PRead(ctx context.Context, dst usermem.IOSequence, offset int64, opts ReadOptions) (int64, error) { 92 return 0, linuxerr.EINVAL 93 } 94 95 // Read implements FileDescriptionImpl.Read analogously to 96 // file_operations::read == file_operations::read_iter == NULL in Linux. 97 func (FileDescriptionDefaultImpl) Read(ctx context.Context, dst usermem.IOSequence, opts ReadOptions) (int64, error) { 98 return 0, linuxerr.EINVAL 99 } 100 101 // PWrite implements FileDescriptionImpl.PWrite analogously to 102 // file_operations::write == file_operations::write_iter == NULL in Linux. 103 func (FileDescriptionDefaultImpl) PWrite(ctx context.Context, src usermem.IOSequence, offset int64, opts WriteOptions) (int64, error) { 104 return 0, linuxerr.EINVAL 105 } 106 107 // Write implements FileDescriptionImpl.Write analogously to 108 // file_operations::write == file_operations::write_iter == NULL in Linux. 109 func (FileDescriptionDefaultImpl) Write(ctx context.Context, src usermem.IOSequence, opts WriteOptions) (int64, error) { 110 return 0, linuxerr.EINVAL 111 } 112 113 // IterDirents implements FileDescriptionImpl.IterDirents analogously to 114 // file_operations::iterate == file_operations::iterate_shared == NULL in 115 // Linux. 116 func (FileDescriptionDefaultImpl) IterDirents(ctx context.Context, cb IterDirentsCallback) error { 117 return syserror.ENOTDIR 118 } 119 120 // Seek implements FileDescriptionImpl.Seek analogously to 121 // file_operations::llseek == NULL in Linux. 122 func (FileDescriptionDefaultImpl) Seek(ctx context.Context, offset int64, whence int32) (int64, error) { 123 return 0, linuxerr.ESPIPE 124 } 125 126 // Sync implements FileDescriptionImpl.Sync analogously to 127 // file_operations::fsync == NULL in Linux. 128 func (FileDescriptionDefaultImpl) Sync(ctx context.Context) error { 129 return linuxerr.EINVAL 130 } 131 132 // ConfigureMMap implements FileDescriptionImpl.ConfigureMMap analogously to 133 // file_operations::mmap == NULL in Linux. 134 func (FileDescriptionDefaultImpl) ConfigureMMap(ctx context.Context, opts *memmap.MMapOpts) error { 135 return linuxerr.ENODEV 136 } 137 138 // Ioctl implements FileDescriptionImpl.Ioctl analogously to 139 // file_operations::unlocked_ioctl == NULL in Linux. 140 func (FileDescriptionDefaultImpl) Ioctl(ctx context.Context, uio usermem.IO, args arch.SyscallArguments) (uintptr, error) { 141 return 0, syserror.ENOTTY 142 } 143 144 // ListXattr implements FileDescriptionImpl.ListXattr analogously to 145 // inode_operations::listxattr == NULL in Linux. 146 func (FileDescriptionDefaultImpl) ListXattr(ctx context.Context, size uint64) ([]string, error) { 147 // This isn't exactly accurate; see FileDescription.ListXattr. 148 return nil, linuxerr.ENOTSUP 149 } 150 151 // GetXattr implements FileDescriptionImpl.GetXattr analogously to 152 // inode::i_opflags & IOP_XATTR == 0 in Linux. 153 func (FileDescriptionDefaultImpl) GetXattr(ctx context.Context, opts GetXattrOptions) (string, error) { 154 return "", linuxerr.ENOTSUP 155 } 156 157 // SetXattr implements FileDescriptionImpl.SetXattr analogously to 158 // inode::i_opflags & IOP_XATTR == 0 in Linux. 159 func (FileDescriptionDefaultImpl) SetXattr(ctx context.Context, opts SetXattrOptions) error { 160 return linuxerr.ENOTSUP 161 } 162 163 // RemoveXattr implements FileDescriptionImpl.RemoveXattr analogously to 164 // inode::i_opflags & IOP_XATTR == 0 in Linux. 165 func (FileDescriptionDefaultImpl) RemoveXattr(ctx context.Context, name string) error { 166 return linuxerr.ENOTSUP 167 } 168 169 // DirectoryFileDescriptionDefaultImpl may be embedded by implementations of 170 // FileDescriptionImpl that always represent directories to obtain 171 // implementations of non-directory I/O methods that return EISDIR. 172 // 173 // +stateify savable 174 type DirectoryFileDescriptionDefaultImpl struct{} 175 176 // Allocate implements DirectoryFileDescriptionDefaultImpl.Allocate. 177 func (DirectoryFileDescriptionDefaultImpl) Allocate(ctx context.Context, mode, offset, length uint64) error { 178 return syserror.EISDIR 179 } 180 181 // PRead implements FileDescriptionImpl.PRead. 182 func (DirectoryFileDescriptionDefaultImpl) PRead(ctx context.Context, dst usermem.IOSequence, offset int64, opts ReadOptions) (int64, error) { 183 return 0, syserror.EISDIR 184 } 185 186 // Read implements FileDescriptionImpl.Read. 187 func (DirectoryFileDescriptionDefaultImpl) Read(ctx context.Context, dst usermem.IOSequence, opts ReadOptions) (int64, error) { 188 return 0, syserror.EISDIR 189 } 190 191 // PWrite implements FileDescriptionImpl.PWrite. 192 func (DirectoryFileDescriptionDefaultImpl) PWrite(ctx context.Context, src usermem.IOSequence, offset int64, opts WriteOptions) (int64, error) { 193 return 0, syserror.EISDIR 194 } 195 196 // Write implements FileDescriptionImpl.Write. 197 func (DirectoryFileDescriptionDefaultImpl) Write(ctx context.Context, src usermem.IOSequence, opts WriteOptions) (int64, error) { 198 return 0, syserror.EISDIR 199 } 200 201 // DentryMetadataFileDescriptionImpl may be embedded by implementations of 202 // FileDescriptionImpl for which FileDescriptionOptions.UseDentryMetadata is 203 // true to obtain implementations of Stat and SetStat that panic. 204 // 205 // +stateify savable 206 type DentryMetadataFileDescriptionImpl struct{} 207 208 // Stat implements FileDescriptionImpl.Stat. 209 func (DentryMetadataFileDescriptionImpl) Stat(ctx context.Context, opts StatOptions) (linux.Statx, error) { 210 panic("illegal call to DentryMetadataFileDescriptionImpl.Stat") 211 } 212 213 // SetStat implements FileDescriptionImpl.SetStat. 214 func (DentryMetadataFileDescriptionImpl) SetStat(ctx context.Context, opts SetStatOptions) error { 215 panic("illegal call to DentryMetadataFileDescriptionImpl.SetStat") 216 } 217 218 // DynamicBytesSource represents a data source for a 219 // DynamicBytesFileDescriptionImpl. 220 // 221 // +stateify savable 222 type DynamicBytesSource interface { 223 // Generate writes the file's contents to buf. 224 Generate(ctx context.Context, buf *bytes.Buffer) error 225 } 226 227 // StaticData implements DynamicBytesSource over a static string. 228 // 229 // +stateify savable 230 type StaticData struct { 231 Data string 232 } 233 234 // Generate implements DynamicBytesSource. 235 func (s *StaticData) Generate(ctx context.Context, buf *bytes.Buffer) error { 236 buf.WriteString(s.Data) 237 return nil 238 } 239 240 // WritableDynamicBytesSource extends DynamicBytesSource to allow writes to the 241 // underlying source. 242 // 243 // TODO(b/179825241): Make utility for integer-based writable files. 244 type WritableDynamicBytesSource interface { 245 DynamicBytesSource 246 247 // Write sends writes to the source. 248 Write(ctx context.Context, src usermem.IOSequence, offset int64) (int64, error) 249 } 250 251 // DynamicBytesFileDescriptionImpl may be embedded by implementations of 252 // FileDescriptionImpl that represent read-only regular files whose contents 253 // are backed by a bytes.Buffer that is regenerated when necessary, consistent 254 // with Linux's fs/seq_file.c:single_open(). 255 // 256 // If data additionally implements WritableDynamicBytesSource, writes are 257 // dispatched to the implementer. The source data is not automatically modified. 258 // 259 // DynamicBytesFileDescriptionImpl.SetDataSource() must be called before first 260 // use. 261 // 262 // +stateify savable 263 type DynamicBytesFileDescriptionImpl struct { 264 data DynamicBytesSource // immutable 265 mu sync.Mutex `state:"nosave"` // protects the following fields 266 buf bytes.Buffer `state:".([]byte)"` 267 off int64 268 lastRead int64 // offset at which the last Read, PRead, or Seek ended 269 } 270 271 func (fd *DynamicBytesFileDescriptionImpl) saveBuf() []byte { 272 return fd.buf.Bytes() 273 } 274 275 func (fd *DynamicBytesFileDescriptionImpl) loadBuf(p []byte) { 276 fd.buf.Write(p) 277 } 278 279 // SetDataSource must be called exactly once on fd before first use. 280 func (fd *DynamicBytesFileDescriptionImpl) SetDataSource(data DynamicBytesSource) { 281 fd.data = data 282 } 283 284 // Preconditions: fd.mu must be locked. 285 func (fd *DynamicBytesFileDescriptionImpl) preadLocked(ctx context.Context, dst usermem.IOSequence, offset int64, opts *ReadOptions) (int64, error) { 286 // Regenerate the buffer if it's empty, or before pread() at a new offset. 287 // Compare fs/seq_file.c:seq_read() => traverse(). 288 switch { 289 case offset != fd.lastRead: 290 fd.buf.Reset() 291 fallthrough 292 case fd.buf.Len() == 0: 293 if err := fd.data.Generate(ctx, &fd.buf); err != nil { 294 fd.buf.Reset() 295 // fd.off is not updated in this case. 296 fd.lastRead = 0 297 return 0, err 298 } 299 } 300 bs := fd.buf.Bytes() 301 if offset >= int64(len(bs)) { 302 return 0, io.EOF 303 } 304 n, err := dst.CopyOut(ctx, bs[offset:]) 305 fd.lastRead = offset + int64(n) 306 return int64(n), err 307 } 308 309 // PRead implements FileDescriptionImpl.PRead. 310 func (fd *DynamicBytesFileDescriptionImpl) PRead(ctx context.Context, dst usermem.IOSequence, offset int64, opts ReadOptions) (int64, error) { 311 fd.mu.Lock() 312 n, err := fd.preadLocked(ctx, dst, offset, &opts) 313 fd.mu.Unlock() 314 return n, err 315 } 316 317 // Read implements FileDescriptionImpl.Read. 318 func (fd *DynamicBytesFileDescriptionImpl) Read(ctx context.Context, dst usermem.IOSequence, opts ReadOptions) (int64, error) { 319 fd.mu.Lock() 320 n, err := fd.preadLocked(ctx, dst, fd.off, &opts) 321 fd.off += n 322 fd.mu.Unlock() 323 return n, err 324 } 325 326 // Seek implements FileDescriptionImpl.Seek. 327 func (fd *DynamicBytesFileDescriptionImpl) Seek(ctx context.Context, offset int64, whence int32) (int64, error) { 328 fd.mu.Lock() 329 defer fd.mu.Unlock() 330 switch whence { 331 case linux.SEEK_SET: 332 // Use offset as given. 333 case linux.SEEK_CUR: 334 offset += fd.off 335 default: 336 // fs/seq_file:seq_lseek() rejects SEEK_END etc. 337 return 0, linuxerr.EINVAL 338 } 339 if offset < 0 { 340 return 0, linuxerr.EINVAL 341 } 342 if offset != fd.lastRead { 343 // Regenerate the file's contents immediately. Compare 344 // fs/seq_file.c:seq_lseek() => traverse(). 345 fd.buf.Reset() 346 if err := fd.data.Generate(ctx, &fd.buf); err != nil { 347 fd.buf.Reset() 348 fd.off = 0 349 fd.lastRead = 0 350 return 0, err 351 } 352 fd.lastRead = offset 353 } 354 fd.off = offset 355 return offset, nil 356 } 357 358 // Preconditions: fd.mu must be locked. 359 func (fd *DynamicBytesFileDescriptionImpl) pwriteLocked(ctx context.Context, src usermem.IOSequence, offset int64, opts WriteOptions) (int64, error) { 360 if opts.Flags&^(linux.RWF_HIPRI|linux.RWF_DSYNC|linux.RWF_SYNC) != 0 { 361 return 0, syserror.EOPNOTSUPP 362 } 363 limit, err := CheckLimit(ctx, offset, src.NumBytes()) 364 if err != nil { 365 return 0, err 366 } 367 src = src.TakeFirst64(limit) 368 369 writable, ok := fd.data.(WritableDynamicBytesSource) 370 if !ok { 371 return 0, syserror.EIO 372 } 373 n, err := writable.Write(ctx, src, offset) 374 if err != nil { 375 return 0, err 376 } 377 378 // Invalidate cached data that might exist prior to this call. 379 fd.buf.Reset() 380 return n, nil 381 } 382 383 // PWrite implements FileDescriptionImpl.PWrite. 384 func (fd *DynamicBytesFileDescriptionImpl) PWrite(ctx context.Context, src usermem.IOSequence, offset int64, opts WriteOptions) (int64, error) { 385 fd.mu.Lock() 386 n, err := fd.pwriteLocked(ctx, src, offset, opts) 387 fd.mu.Unlock() 388 return n, err 389 } 390 391 // Write implements FileDescriptionImpl.Write. 392 func (fd *DynamicBytesFileDescriptionImpl) Write(ctx context.Context, src usermem.IOSequence, opts WriteOptions) (int64, error) { 393 fd.mu.Lock() 394 n, err := fd.pwriteLocked(ctx, src, fd.off, opts) 395 fd.off += n 396 fd.mu.Unlock() 397 return n, err 398 } 399 400 // GenericConfigureMMap may be used by most implementations of 401 // FileDescriptionImpl.ConfigureMMap. 402 func GenericConfigureMMap(fd *FileDescription, m memmap.Mappable, opts *memmap.MMapOpts) error { 403 opts.Mappable = m 404 opts.MappingIdentity = fd 405 fd.IncRef() 406 return nil 407 } 408 409 // LockFD may be used by most implementations of FileDescriptionImpl.Lock* 410 // functions. Caller must call Init(). 411 // 412 // +stateify savable 413 type LockFD struct { 414 locks *FileLocks 415 } 416 417 // SupportsLocks implements FileDescriptionImpl.SupportsLocks. 418 func (LockFD) SupportsLocks() bool { 419 return true 420 } 421 422 // Init initializes fd with FileLocks to use. 423 func (fd *LockFD) Init(locks *FileLocks) { 424 fd.locks = locks 425 } 426 427 // Locks returns the locks associated with this file. 428 func (fd *LockFD) Locks() *FileLocks { 429 return fd.locks 430 } 431 432 // LockBSD implements FileDescriptionImpl.LockBSD. 433 func (fd *LockFD) LockBSD(ctx context.Context, uid fslock.UniqueID, ownerPID int32, t fslock.LockType, block fslock.Blocker) error { 434 return fd.locks.LockBSD(ctx, uid, ownerPID, t, block) 435 } 436 437 // UnlockBSD implements FileDescriptionImpl.UnlockBSD. 438 func (fd *LockFD) UnlockBSD(ctx context.Context, uid fslock.UniqueID) error { 439 fd.locks.UnlockBSD(uid) 440 return nil 441 } 442 443 // LockPOSIX implements FileDescriptionImpl.LockPOSIX. 444 func (fd *LockFD) LockPOSIX(ctx context.Context, uid fslock.UniqueID, ownerPID int32, t fslock.LockType, r fslock.LockRange, block fslock.Blocker) error { 445 return fd.locks.LockPOSIX(ctx, uid, ownerPID, t, r, block) 446 } 447 448 // UnlockPOSIX implements FileDescriptionImpl.UnlockPOSIX. 449 func (fd *LockFD) UnlockPOSIX(ctx context.Context, uid fslock.UniqueID, r fslock.LockRange) error { 450 return fd.locks.UnlockPOSIX(ctx, uid, r) 451 } 452 453 // TestPOSIX implements FileDescriptionImpl.TestPOSIX. 454 func (fd *LockFD) TestPOSIX(ctx context.Context, uid fslock.UniqueID, t fslock.LockType, r fslock.LockRange) (linux.Flock, error) { 455 return fd.locks.TestPOSIX(ctx, uid, t, r) 456 } 457 458 // NoLockFD implements Lock*/Unlock* portion of FileDescriptionImpl interface 459 // returning ENOLCK. 460 // 461 // +stateify savable 462 type NoLockFD struct{} 463 464 // SupportsLocks implements FileDescriptionImpl.SupportsLocks. 465 func (NoLockFD) SupportsLocks() bool { 466 return false 467 } 468 469 // LockBSD implements FileDescriptionImpl.LockBSD. 470 func (NoLockFD) LockBSD(ctx context.Context, uid fslock.UniqueID, ownerPID int32, t fslock.LockType, block fslock.Blocker) error { 471 return linuxerr.ENOLCK 472 } 473 474 // UnlockBSD implements FileDescriptionImpl.UnlockBSD. 475 func (NoLockFD) UnlockBSD(ctx context.Context, uid fslock.UniqueID) error { 476 return linuxerr.ENOLCK 477 } 478 479 // LockPOSIX implements FileDescriptionImpl.LockPOSIX. 480 func (NoLockFD) LockPOSIX(ctx context.Context, uid fslock.UniqueID, ownerPID int32, t fslock.LockType, r fslock.LockRange, block fslock.Blocker) error { 481 return linuxerr.ENOLCK 482 } 483 484 // UnlockPOSIX implements FileDescriptionImpl.UnlockPOSIX. 485 func (NoLockFD) UnlockPOSIX(ctx context.Context, uid fslock.UniqueID, r fslock.LockRange) error { 486 return linuxerr.ENOLCK 487 } 488 489 // TestPOSIX implements FileDescriptionImpl.TestPOSIX. 490 func (NoLockFD) TestPOSIX(ctx context.Context, uid fslock.UniqueID, t fslock.LockType, r fslock.LockRange) (linux.Flock, error) { 491 return linux.Flock{}, linuxerr.ENOLCK 492 } 493 494 // BadLockFD implements Lock*/Unlock* portion of FileDescriptionImpl interface 495 // returning EBADF. 496 // 497 // +stateify savable 498 type BadLockFD struct{} 499 500 // SupportsLocks implements FileDescriptionImpl.SupportsLocks. 501 func (BadLockFD) SupportsLocks() bool { 502 return false 503 } 504 505 // LockBSD implements FileDescriptionImpl.LockBSD. 506 func (BadLockFD) LockBSD(ctx context.Context, uid fslock.UniqueID, ownerPID int32, t fslock.LockType, block fslock.Blocker) error { 507 return linuxerr.EBADF 508 } 509 510 // UnlockBSD implements FileDescriptionImpl.UnlockBSD. 511 func (BadLockFD) UnlockBSD(ctx context.Context, uid fslock.UniqueID) error { 512 return linuxerr.EBADF 513 } 514 515 // LockPOSIX implements FileDescriptionImpl.LockPOSIX. 516 func (BadLockFD) LockPOSIX(ctx context.Context, uid fslock.UniqueID, ownerPID int32, t fslock.LockType, r fslock.LockRange, block fslock.Blocker) error { 517 return linuxerr.EBADF 518 } 519 520 // UnlockPOSIX implements FileDescriptionImpl.UnlockPOSIX. 521 func (BadLockFD) UnlockPOSIX(ctx context.Context, uid fslock.UniqueID, r fslock.LockRange) error { 522 return linuxerr.EBADF 523 } 524 525 // TestPOSIX implements FileDescriptionImpl.TestPOSIX. 526 func (BadLockFD) TestPOSIX(ctx context.Context, uid fslock.UniqueID, t fslock.LockType, r fslock.LockRange) (linux.Flock, error) { 527 return linux.Flock{}, linuxerr.EBADF 528 }