github.com/nicocha30/gvisor-ligolo@v0.0.0-20230726075806-989fa2c0a413/pkg/sentry/fsimpl/gofer/regular_file.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 gofer 16 17 import ( 18 "fmt" 19 "io" 20 "math" 21 22 "github.com/nicocha30/gvisor-ligolo/pkg/abi/linux" 23 "github.com/nicocha30/gvisor-ligolo/pkg/context" 24 "github.com/nicocha30/gvisor-ligolo/pkg/errors/linuxerr" 25 "github.com/nicocha30/gvisor-ligolo/pkg/hostarch" 26 "github.com/nicocha30/gvisor-ligolo/pkg/log" 27 "github.com/nicocha30/gvisor-ligolo/pkg/metric" 28 "github.com/nicocha30/gvisor-ligolo/pkg/safemem" 29 "github.com/nicocha30/gvisor-ligolo/pkg/sentry/fsmetric" 30 "github.com/nicocha30/gvisor-ligolo/pkg/sentry/fsutil" 31 "github.com/nicocha30/gvisor-ligolo/pkg/sentry/memmap" 32 "github.com/nicocha30/gvisor-ligolo/pkg/sentry/pgalloc" 33 "github.com/nicocha30/gvisor-ligolo/pkg/sentry/usage" 34 "github.com/nicocha30/gvisor-ligolo/pkg/sentry/vfs" 35 "github.com/nicocha30/gvisor-ligolo/pkg/sync" 36 "github.com/nicocha30/gvisor-ligolo/pkg/usermem" 37 ) 38 39 func (d *dentry) isRegularFile() bool { 40 return d.fileType() == linux.S_IFREG 41 } 42 43 // +stateify savable 44 type regularFileFD struct { 45 fileDescription 46 47 // off is the file offset. off is protected by mu. 48 mu sync.Mutex `state:"nosave"` 49 off int64 50 } 51 52 func newRegularFileFD(mnt *vfs.Mount, d *dentry, flags uint32) (*regularFileFD, error) { 53 fd := ®ularFileFD{} 54 fd.LockFD.Init(&d.locks) 55 if err := fd.vfsfd.Init(fd, flags, mnt, &d.vfsd, &vfs.FileDescriptionOptions{ 56 AllowDirectIO: true, 57 }); err != nil { 58 return nil, err 59 } 60 if fd.vfsfd.IsWritable() && (d.mode.Load()&0111 != 0) { 61 metric.SuspiciousOperationsMetric.Increment(&metric.SuspiciousOperationsTypeOpenedWriteExecuteFile) 62 } 63 if d.mmapFD.Load() >= 0 { 64 fsmetric.GoferOpensHost.Increment() 65 } else { 66 fsmetric.GoferOpens9P.Increment() 67 } 68 return fd, nil 69 } 70 71 // Release implements vfs.FileDescriptionImpl.Release. 72 func (fd *regularFileFD) Release(context.Context) { 73 } 74 75 // OnClose implements vfs.FileDescriptionImpl.OnClose. 76 func (fd *regularFileFD) OnClose(ctx context.Context) error { 77 if !fd.vfsfd.IsWritable() { 78 return nil 79 } 80 d := fd.dentry() 81 if d.fs.opts.interop == InteropModeExclusive { 82 // d may have dirty pages that we won't write back now (and wouldn't 83 // have in VFS1), making a flushf RPC ineffective. If this is the case, 84 // skip the flushf. 85 // 86 // Note that it's also possible to have dirty pages under other interop 87 // modes if forcePageCache is in effect; we conservatively assume that 88 // applications have some way of tolerating this and still want the 89 // flushf. 90 d.dataMu.RLock() 91 haveDirtyPages := !d.dirty.IsEmpty() 92 d.dataMu.RUnlock() 93 if haveDirtyPages { 94 return nil 95 } 96 } 97 return d.flush(ctx) 98 } 99 100 // Allocate implements vfs.FileDescriptionImpl.Allocate. 101 func (fd *regularFileFD) Allocate(ctx context.Context, mode, offset, length uint64) error { 102 d := fd.dentry() 103 return d.doAllocate(ctx, offset, length, func() error { 104 return d.allocate(ctx, mode, offset, length) 105 }) 106 } 107 108 // PRead implements vfs.FileDescriptionImpl.PRead. 109 func (fd *regularFileFD) PRead(ctx context.Context, dst usermem.IOSequence, offset int64, opts vfs.ReadOptions) (int64, error) { 110 start := fsmetric.StartReadWait() 111 d := fd.dentry() 112 defer func() { 113 if d.readFD.Load() >= 0 { 114 fsmetric.GoferReadsHost.Increment() 115 fsmetric.FinishReadWait(fsmetric.GoferReadWaitHost, start) 116 } else { 117 fsmetric.GoferReads9P.Increment() 118 fsmetric.FinishReadWait(fsmetric.GoferReadWait9P, start) 119 } 120 }() 121 122 if offset < 0 { 123 return 0, linuxerr.EINVAL 124 } 125 126 // Check that flags are supported. 127 // 128 // TODO(gvisor.dev/issue/2601): Support select preadv2 flags. 129 if opts.Flags&^linux.RWF_HIPRI != 0 { 130 return 0, linuxerr.EOPNOTSUPP 131 } 132 133 // Check for reading at EOF before calling into MM (but not under 134 // InteropModeShared, which makes d.size unreliable). 135 if d.cachedMetadataAuthoritative() && uint64(offset) >= d.size.Load() { 136 return 0, io.EOF 137 } 138 139 var ( 140 n int64 141 readErr error 142 ) 143 if fd.vfsfd.StatusFlags()&linux.O_DIRECT != 0 { 144 // Write dirty cached pages that will be touched by the read back to 145 // the remote file. 146 if err := d.writeback(ctx, offset, dst.NumBytes()); err != nil { 147 return 0, err 148 } 149 rw := getDentryReadWriter(ctx, d, offset) 150 // Require the read to go to the remote file. 151 rw.direct = true 152 n, readErr = dst.CopyOutFrom(ctx, rw) 153 putDentryReadWriter(rw) 154 if d.fs.opts.interop != InteropModeShared { 155 // Compare Linux's mm/filemap.c:do_generic_file_read() => file_accessed(). 156 d.touchAtimeLocked(fd.vfsfd.Mount()) 157 } 158 } else { 159 rw := getDentryReadWriter(ctx, d, offset) 160 n, readErr = dst.CopyOutFrom(ctx, rw) 161 putDentryReadWriter(rw) 162 if d.fs.opts.interop != InteropModeShared { 163 // Compare Linux's mm/filemap.c:do_generic_file_read() => file_accessed(). 164 d.touchAtime(fd.vfsfd.Mount()) 165 } 166 } 167 return n, readErr 168 } 169 170 // Read implements vfs.FileDescriptionImpl.Read. 171 func (fd *regularFileFD) Read(ctx context.Context, dst usermem.IOSequence, opts vfs.ReadOptions) (int64, error) { 172 fd.mu.Lock() 173 n, err := fd.PRead(ctx, dst, fd.off, opts) 174 fd.off += n 175 fd.mu.Unlock() 176 return n, err 177 } 178 179 // PWrite implements vfs.FileDescriptionImpl.PWrite. 180 func (fd *regularFileFD) PWrite(ctx context.Context, src usermem.IOSequence, offset int64, opts vfs.WriteOptions) (int64, error) { 181 n, _, err := fd.pwrite(ctx, src, offset, opts) 182 return n, err 183 } 184 185 // pwrite returns the number of bytes written, final offset, error. The final 186 // offset should be ignored by PWrite. 187 func (fd *regularFileFD) pwrite(ctx context.Context, src usermem.IOSequence, offset int64, opts vfs.WriteOptions) (written, finalOff int64, err error) { 188 if offset < 0 { 189 return 0, offset, linuxerr.EINVAL 190 } 191 192 // Check that flags are supported. 193 // 194 // TODO(gvisor.dev/issue/2601): Support select pwritev2 flags. 195 if opts.Flags&^linux.RWF_HIPRI != 0 { 196 return 0, offset, linuxerr.EOPNOTSUPP 197 } 198 199 d := fd.dentry() 200 201 d.metadataMu.Lock() 202 defer d.metadataMu.Unlock() 203 204 // If the fd was opened with O_APPEND, make sure the file size is updated. 205 // There is a possible race here if size is modified externally after 206 // metadata cache is updated. 207 if fd.vfsfd.StatusFlags()&linux.O_APPEND != 0 && !d.cachedMetadataAuthoritative() { 208 if err := d.refreshSizeLocked(ctx); err != nil { 209 return 0, offset, err 210 } 211 } 212 213 // Set offset to file size if the fd was opened with O_APPEND. 214 if fd.vfsfd.StatusFlags()&linux.O_APPEND != 0 { 215 // Holding d.metadataMu is sufficient for reading d.size. 216 offset = int64(d.size.RacyLoad()) 217 } 218 limit, err := vfs.CheckLimit(ctx, offset, src.NumBytes()) 219 if err != nil { 220 return 0, offset, err 221 } 222 src = src.TakeFirst64(limit) 223 224 if d.fs.opts.interop != InteropModeShared { 225 // Compare Linux's mm/filemap.c:__generic_file_write_iter() => 226 // file_update_time(). This is d.touchCMtime(), but without locking 227 // d.metadataMu (recursively). 228 d.touchCMtimeLocked() 229 } 230 231 rw := getDentryReadWriter(ctx, d, offset) 232 defer putDentryReadWriter(rw) 233 234 if fd.vfsfd.StatusFlags()&linux.O_DIRECT != 0 { 235 if err := fd.writeCache(ctx, d, offset, src); err != nil { 236 return 0, offset, err 237 } 238 239 // Require the write to go to the remote file. 240 rw.direct = true 241 } 242 243 n, err := src.CopyInTo(ctx, rw) 244 if err != nil { 245 return n, offset + n, err 246 } 247 if n > 0 && fd.vfsfd.StatusFlags()&(linux.O_DSYNC|linux.O_SYNC) != 0 { 248 // Note that if any of the following fail, then we can't guarantee that 249 // any data was actually written with the semantics of O_DSYNC or 250 // O_SYNC, so we return zero bytes written. Compare Linux's 251 // mm/filemap.c:generic_file_write_iter() => 252 // include/linux/fs.h:generic_write_sync(). 253 // 254 // Write dirty cached pages touched by the write back to the remote 255 // file. 256 if err := d.writeback(ctx, offset, src.NumBytes()); err != nil { 257 return 0, offset, err 258 } 259 // Request the remote filesystem to sync the remote file. 260 if err := d.syncRemoteFile(ctx); err != nil { 261 return 0, offset, err 262 } 263 } 264 265 // As with Linux, writing clears the setuid and setgid bits. 266 if n > 0 { 267 oldMode := d.mode.Load() 268 // If setuid or setgid were set, update d.mode and propagate 269 // changes to the host. 270 if newMode := vfs.ClearSUIDAndSGID(oldMode); newMode != oldMode { 271 if err := d.chmod(ctx, uint16(newMode)); err != nil { 272 return 0, offset, err 273 } 274 d.mode.Store(newMode) 275 } 276 } 277 278 return n, offset + n, nil 279 } 280 281 func (fd *regularFileFD) writeCache(ctx context.Context, d *dentry, offset int64, src usermem.IOSequence) error { 282 // Write dirty cached pages that will be touched by the write back to 283 // the remote file. 284 if err := d.writeback(ctx, offset, src.NumBytes()); err != nil { 285 return err 286 } 287 288 // Remove touched pages from the cache. 289 pgstart := hostarch.PageRoundDown(uint64(offset)) 290 pgend, ok := hostarch.PageRoundUp(uint64(offset + src.NumBytes())) 291 if !ok { 292 return linuxerr.EINVAL 293 } 294 mr := memmap.MappableRange{pgstart, pgend} 295 var freed []memmap.FileRange 296 297 d.dataMu.Lock() 298 cseg := d.cache.LowerBoundSegment(mr.Start) 299 for cseg.Ok() && cseg.Start() < mr.End { 300 cseg = d.cache.Isolate(cseg, mr) 301 freed = append(freed, memmap.FileRange{cseg.Value(), cseg.Value() + cseg.Range().Length()}) 302 cseg = d.cache.Remove(cseg).NextSegment() 303 } 304 d.dataMu.Unlock() 305 306 // Invalidate mappings of removed pages. 307 d.mapsMu.Lock() 308 d.mappings.Invalidate(mr, memmap.InvalidateOpts{}) 309 d.mapsMu.Unlock() 310 311 // Finally free pages removed from the cache. 312 mf := d.fs.mfp.MemoryFile() 313 for _, freedFR := range freed { 314 mf.DecRef(freedFR) 315 } 316 return nil 317 } 318 319 // Write implements vfs.FileDescriptionImpl.Write. 320 func (fd *regularFileFD) Write(ctx context.Context, src usermem.IOSequence, opts vfs.WriteOptions) (int64, error) { 321 fd.mu.Lock() 322 n, off, err := fd.pwrite(ctx, src, fd.off, opts) 323 fd.off = off 324 fd.mu.Unlock() 325 return n, err 326 } 327 328 type dentryReadWriter struct { 329 ctx context.Context 330 d *dentry 331 off uint64 332 direct bool 333 } 334 335 var dentryReadWriterPool = sync.Pool{ 336 New: func() any { 337 return &dentryReadWriter{} 338 }, 339 } 340 341 func getDentryReadWriter(ctx context.Context, d *dentry, offset int64) *dentryReadWriter { 342 rw := dentryReadWriterPool.Get().(*dentryReadWriter) 343 rw.ctx = ctx 344 rw.d = d 345 rw.off = uint64(offset) 346 rw.direct = false 347 return rw 348 } 349 350 func putDentryReadWriter(rw *dentryReadWriter) { 351 rw.ctx = nil 352 rw.d = nil 353 dentryReadWriterPool.Put(rw) 354 } 355 356 // ReadToBlocks implements safemem.Reader.ReadToBlocks. 357 func (rw *dentryReadWriter) ReadToBlocks(dsts safemem.BlockSeq) (uint64, error) { 358 if dsts.IsEmpty() { 359 return 0, nil 360 } 361 362 // If we have a mmappable host FD (which must be used here to ensure 363 // coherence with memory-mapped I/O), or if InteropModeShared is in effect 364 // (which prevents us from caching file contents and makes dentry.size 365 // unreliable), or if the file was opened O_DIRECT, read directly from 366 // readHandle() without locking dentry.dataMu. 367 rw.d.handleMu.RLock() 368 h := rw.d.readHandle() 369 if (rw.d.mmapFD.RacyLoad() >= 0 && !rw.d.fs.opts.forcePageCache) || rw.d.fs.opts.interop == InteropModeShared || rw.direct { 370 n, err := h.readToBlocksAt(rw.ctx, dsts, rw.off) 371 rw.d.handleMu.RUnlock() 372 rw.off += n 373 return n, err 374 } 375 376 // Otherwise read from/through the cache. 377 mf := rw.d.fs.mfp.MemoryFile() 378 fillCache := mf.ShouldCacheEvictable() 379 var dataMuUnlock func() 380 if fillCache { 381 rw.d.dataMu.Lock() 382 dataMuUnlock = rw.d.dataMu.Unlock 383 } else { 384 rw.d.dataMu.RLock() 385 dataMuUnlock = rw.d.dataMu.RUnlock 386 } 387 388 // Compute the range to read (limited by file size and overflow-checked). 389 end := rw.d.size.Load() 390 if rw.off >= end { 391 dataMuUnlock() 392 rw.d.handleMu.RUnlock() 393 return 0, io.EOF 394 } 395 if rend := rw.off + dsts.NumBytes(); rend > rw.off && rend < end { 396 end = rend 397 } 398 399 var done uint64 400 seg, gap := rw.d.cache.Find(rw.off) 401 for rw.off < end { 402 mr := memmap.MappableRange{rw.off, end} 403 switch { 404 case seg.Ok(): 405 // Get internal mappings from the cache. 406 ims, err := mf.MapInternal(seg.FileRangeOf(seg.Range().Intersect(mr)), hostarch.Read) 407 if err != nil { 408 dataMuUnlock() 409 rw.d.handleMu.RUnlock() 410 return done, err 411 } 412 413 // Copy from internal mappings. 414 n, err := safemem.CopySeq(dsts, ims) 415 done += n 416 rw.off += n 417 dsts = dsts.DropFirst64(n) 418 if err != nil { 419 dataMuUnlock() 420 rw.d.handleMu.RUnlock() 421 return done, err 422 } 423 424 // Continue. 425 seg, gap = seg.NextNonEmpty() 426 427 case gap.Ok(): 428 gapMR := gap.Range().Intersect(mr) 429 if fillCache { 430 // Read into the cache, then re-enter the loop to read from the 431 // cache. 432 gapEnd, _ := hostarch.PageRoundUp(gapMR.End) 433 reqMR := memmap.MappableRange{ 434 Start: hostarch.PageRoundDown(gapMR.Start), 435 End: gapEnd, 436 } 437 optMR := gap.Range() 438 _, err := rw.d.cache.Fill(rw.ctx, reqMR, maxFillRange(reqMR, optMR), rw.d.size.Load(), mf, usage.PageCache, pgalloc.AllocateAndWritePopulate, h.readToBlocksAt) 439 mf.MarkEvictable(rw.d, pgalloc.EvictableRange{optMR.Start, optMR.End}) 440 seg, gap = rw.d.cache.Find(rw.off) 441 if !seg.Ok() { 442 dataMuUnlock() 443 rw.d.handleMu.RUnlock() 444 return done, err 445 } 446 // err might have occurred in part of gap.Range() outside gapMR 447 // (in particular, gap.End() might be beyond EOF). Forget about 448 // it for now; if the error matters and persists, we'll run 449 // into it again in a later iteration of this loop. 450 } else { 451 // Read directly from the file. 452 gapDsts := dsts.TakeFirst64(gapMR.Length()) 453 n, err := h.readToBlocksAt(rw.ctx, gapDsts, gapMR.Start) 454 done += n 455 rw.off += n 456 dsts = dsts.DropFirst64(n) 457 // Partial reads are fine. But we must stop reading. 458 if n != gapDsts.NumBytes() || err != nil { 459 dataMuUnlock() 460 rw.d.handleMu.RUnlock() 461 return done, err 462 } 463 464 // Continue. 465 seg, gap = gap.NextSegment(), fsutil.FileRangeGapIterator{} 466 } 467 } 468 } 469 dataMuUnlock() 470 rw.d.handleMu.RUnlock() 471 return done, nil 472 } 473 474 // WriteFromBlocks implements safemem.Writer.WriteFromBlocks. 475 // 476 // Preconditions: rw.d.metadataMu must be locked. 477 func (rw *dentryReadWriter) WriteFromBlocks(srcs safemem.BlockSeq) (uint64, error) { 478 if srcs.IsEmpty() { 479 return 0, nil 480 } 481 482 // If we have a mmappable host FD (which must be used here to ensure 483 // coherence with memory-mapped I/O), or if InteropModeShared is in effect 484 // (which prevents us from caching file contents), or if the file was 485 // opened with O_DIRECT, write directly to dentry.writeHandle() 486 // without locking dentry.dataMu. 487 rw.d.handleMu.RLock() 488 h := rw.d.writeHandle() 489 if (rw.d.mmapFD.RacyLoad() >= 0 && !rw.d.fs.opts.forcePageCache) || rw.d.fs.opts.interop == InteropModeShared || rw.direct { 490 n, err := h.writeFromBlocksAt(rw.ctx, srcs, rw.off) 491 rw.off += n 492 rw.d.dataMu.Lock() 493 if rw.off > rw.d.size.Load() { 494 rw.d.size.Store(rw.off) 495 // The remote file's size will implicitly be extended to the correct 496 // value when we write back to it. 497 } 498 rw.d.dataMu.Unlock() 499 rw.d.handleMu.RUnlock() 500 return n, err 501 } 502 503 // Otherwise write to/through the cache. 504 mf := rw.d.fs.mfp.MemoryFile() 505 rw.d.dataMu.Lock() 506 507 // Compute the range to write (overflow-checked). 508 start := rw.off 509 end := rw.off + srcs.NumBytes() 510 if end <= rw.off { 511 end = math.MaxInt64 512 } 513 514 var ( 515 done uint64 516 retErr error 517 ) 518 seg, gap := rw.d.cache.Find(rw.off) 519 for rw.off < end { 520 mr := memmap.MappableRange{rw.off, end} 521 switch { 522 case seg.Ok(): 523 // Get internal mappings from the cache. 524 segMR := seg.Range().Intersect(mr) 525 ims, err := mf.MapInternal(seg.FileRangeOf(segMR), hostarch.Write) 526 if err != nil { 527 retErr = err 528 goto exitLoop 529 } 530 531 // Copy to internal mappings. 532 n, err := safemem.CopySeq(ims, srcs) 533 done += n 534 rw.off += n 535 srcs = srcs.DropFirst64(n) 536 rw.d.dirty.MarkDirty(segMR) 537 if err != nil { 538 retErr = err 539 goto exitLoop 540 } 541 542 // Continue. 543 seg, gap = seg.NextNonEmpty() 544 545 case gap.Ok(): 546 // Write directly to the file. At present, we never fill the cache 547 // when writing, since doing so can convert small writes into 548 // inefficient read-modify-write cycles, and we have no mechanism 549 // for detecting or avoiding this. 550 gapMR := gap.Range().Intersect(mr) 551 gapSrcs := srcs.TakeFirst64(gapMR.Length()) 552 n, err := h.writeFromBlocksAt(rw.ctx, gapSrcs, gapMR.Start) 553 done += n 554 rw.off += n 555 srcs = srcs.DropFirst64(n) 556 // Partial writes are fine. But we must stop writing. 557 if n != gapSrcs.NumBytes() || err != nil { 558 retErr = err 559 goto exitLoop 560 } 561 562 // Continue. 563 seg, gap = gap.NextSegment(), fsutil.FileRangeGapIterator{} 564 } 565 } 566 exitLoop: 567 if rw.off > rw.d.size.Load() { 568 rw.d.size.Store(rw.off) 569 // The remote file's size will implicitly be extended to the correct 570 // value when we write back to it. 571 } 572 // If InteropModeWritethrough is in effect, flush written data back to the 573 // remote filesystem. 574 if rw.d.fs.opts.interop == InteropModeWritethrough && done != 0 { 575 if err := fsutil.SyncDirty(rw.ctx, memmap.MappableRange{ 576 Start: start, 577 End: rw.off, 578 }, &rw.d.cache, &rw.d.dirty, rw.d.size.Load(), mf, h.writeFromBlocksAt); err != nil { 579 // We have no idea how many bytes were actually flushed. 580 rw.off = start 581 done = 0 582 retErr = err 583 } 584 } 585 rw.d.dataMu.Unlock() 586 rw.d.handleMu.RUnlock() 587 return done, retErr 588 } 589 590 func (d *dentry) writeback(ctx context.Context, offset, size int64) error { 591 if size == 0 { 592 return nil 593 } 594 d.handleMu.RLock() 595 defer d.handleMu.RUnlock() 596 h := d.writeHandle() 597 d.dataMu.Lock() 598 defer d.dataMu.Unlock() 599 // Compute the range of valid bytes (overflow-checked). 600 dentrySize := d.size.Load() 601 if uint64(offset) >= dentrySize { 602 return nil 603 } 604 end := int64(dentrySize) 605 if rend := offset + size; rend > offset && rend < end { 606 end = rend 607 } 608 return fsutil.SyncDirty(ctx, memmap.MappableRange{ 609 Start: uint64(offset), 610 End: uint64(end), 611 }, &d.cache, &d.dirty, dentrySize, d.fs.mfp.MemoryFile(), h.writeFromBlocksAt) 612 } 613 614 // Seek implements vfs.FileDescriptionImpl.Seek. 615 func (fd *regularFileFD) Seek(ctx context.Context, offset int64, whence int32) (int64, error) { 616 fd.mu.Lock() 617 defer fd.mu.Unlock() 618 newOffset, err := regularFileSeekLocked(ctx, fd.dentry(), fd.off, offset, whence) 619 if err != nil { 620 return 0, err 621 } 622 fd.off = newOffset 623 return newOffset, nil 624 } 625 626 // Calculate the new offset for a seek operation on a regular file. 627 func regularFileSeekLocked(ctx context.Context, d *dentry, fdOffset, offset int64, whence int32) (int64, error) { 628 switch whence { 629 case linux.SEEK_SET: 630 // Use offset as specified. 631 case linux.SEEK_CUR: 632 offset += fdOffset 633 case linux.SEEK_END, linux.SEEK_DATA, linux.SEEK_HOLE: 634 // Ensure file size is up to date. 635 if !d.cachedMetadataAuthoritative() { 636 if err := d.updateMetadata(ctx); err != nil { 637 return 0, err 638 } 639 } 640 size := int64(d.size.Load()) 641 // For SEEK_DATA and SEEK_HOLE, treat the file as a single contiguous 642 // block of data. 643 switch whence { 644 case linux.SEEK_END: 645 offset += size 646 case linux.SEEK_DATA: 647 if offset >= size { 648 return 0, linuxerr.ENXIO 649 } 650 // Use offset as specified. 651 case linux.SEEK_HOLE: 652 if offset >= size { 653 return 0, linuxerr.ENXIO 654 } 655 offset = size 656 } 657 default: 658 return 0, linuxerr.EINVAL 659 } 660 if offset < 0 { 661 return 0, linuxerr.EINVAL 662 } 663 return offset, nil 664 } 665 666 // Sync implements vfs.FileDescriptionImpl.Sync. 667 func (fd *regularFileFD) Sync(ctx context.Context) error { 668 return fd.dentry().syncCachedFile(ctx, false /* forFilesystemSync */) 669 } 670 671 // ConfigureMMap implements vfs.FileDescriptionImpl.ConfigureMMap. 672 func (fd *regularFileFD) ConfigureMMap(ctx context.Context, opts *memmap.MMapOpts) error { 673 d := fd.dentry() 674 // Force sentry page caching at your own risk. 675 if !d.fs.opts.forcePageCache { 676 switch d.fs.opts.interop { 677 case InteropModeExclusive: 678 // Any mapping is fine. 679 case InteropModeWritethrough: 680 // Shared writable mappings require a host FD, since otherwise we 681 // can't synchronously flush memory-mapped writes to the remote 682 // file. 683 if opts.Private || !opts.MaxPerms.Write { 684 break 685 } 686 fallthrough 687 case InteropModeShared: 688 // All mappings require a host FD to be coherent with other 689 // filesystem users. 690 if d.mmapFD.Load() < 0 { 691 return linuxerr.ENODEV 692 } 693 default: 694 panic(fmt.Sprintf("unknown InteropMode %v", d.fs.opts.interop)) 695 } 696 } 697 // After this point, d may be used as a memmap.Mappable. 698 d.pf.hostFileMapperInitOnce.Do(d.pf.hostFileMapper.Init) 699 opts.SentryOwnedContent = d.fs.opts.forcePageCache 700 return vfs.GenericConfigureMMap(&fd.vfsfd, d, opts) 701 } 702 703 func (fs *filesystem) mayCachePagesInMemoryFile() bool { 704 return fs.opts.forcePageCache || fs.opts.interop != InteropModeShared 705 } 706 707 // AddMapping implements memmap.Mappable.AddMapping. 708 func (d *dentry) AddMapping(ctx context.Context, ms memmap.MappingSpace, ar hostarch.AddrRange, offset uint64, writable bool) error { 709 d.mapsMu.Lock() 710 mapped := d.mappings.AddMapping(ms, ar, offset, writable) 711 // Do this unconditionally since whether we have a host FD can change 712 // across save/restore. 713 for _, r := range mapped { 714 d.pf.hostFileMapper.IncRefOn(r) 715 } 716 if d.fs.mayCachePagesInMemoryFile() { 717 // d.Evict() will refuse to evict memory-mapped pages, so tell the 718 // MemoryFile to not bother trying. 719 mf := d.fs.mfp.MemoryFile() 720 for _, r := range mapped { 721 mf.MarkUnevictable(d, pgalloc.EvictableRange{r.Start, r.End}) 722 } 723 } 724 d.mapsMu.Unlock() 725 return nil 726 } 727 728 // RemoveMapping implements memmap.Mappable.RemoveMapping. 729 func (d *dentry) RemoveMapping(ctx context.Context, ms memmap.MappingSpace, ar hostarch.AddrRange, offset uint64, writable bool) { 730 d.mapsMu.Lock() 731 unmapped := d.mappings.RemoveMapping(ms, ar, offset, writable) 732 for _, r := range unmapped { 733 d.pf.hostFileMapper.DecRefOn(r) 734 } 735 if d.fs.mayCachePagesInMemoryFile() { 736 // Pages that are no longer referenced by any application memory 737 // mappings are now considered unused; allow MemoryFile to evict them 738 // when necessary. 739 mf := d.fs.mfp.MemoryFile() 740 d.dataMu.Lock() 741 for _, r := range unmapped { 742 // Since these pages are no longer mapped, they are no longer 743 // concurrently dirtyable by a writable memory mapping. 744 d.dirty.AllowClean(r) 745 mf.MarkEvictable(d, pgalloc.EvictableRange{r.Start, r.End}) 746 } 747 d.dataMu.Unlock() 748 } 749 d.mapsMu.Unlock() 750 } 751 752 // CopyMapping implements memmap.Mappable.CopyMapping. 753 func (d *dentry) CopyMapping(ctx context.Context, ms memmap.MappingSpace, srcAR, dstAR hostarch.AddrRange, offset uint64, writable bool) error { 754 return d.AddMapping(ctx, ms, dstAR, offset, writable) 755 } 756 757 // Translate implements memmap.Mappable.Translate. 758 func (d *dentry) Translate(ctx context.Context, required, optional memmap.MappableRange, at hostarch.AccessType) ([]memmap.Translation, error) { 759 d.handleMu.RLock() 760 if d.mmapFD.RacyLoad() >= 0 && !d.fs.opts.forcePageCache { 761 d.handleMu.RUnlock() 762 mr := optional 763 if d.fs.opts.limitHostFDTranslation { 764 mr = maxFillRange(required, optional) 765 } 766 return []memmap.Translation{ 767 { 768 Source: mr, 769 File: &d.pf, 770 Offset: mr.Start, 771 Perms: hostarch.AnyAccess, 772 }, 773 }, nil 774 } 775 776 d.dataMu.Lock() 777 778 // Constrain translations to d.size (rounded up) to prevent translation to 779 // pages that may be concurrently truncated. 780 pgend, _ := hostarch.PageRoundUp(d.size.Load()) 781 var beyondEOF bool 782 if required.End > pgend { 783 if required.Start >= pgend { 784 d.dataMu.Unlock() 785 d.handleMu.RUnlock() 786 return nil, &memmap.BusError{io.EOF} 787 } 788 beyondEOF = true 789 required.End = pgend 790 } 791 if optional.End > pgend { 792 optional.End = pgend 793 } 794 795 mf := d.fs.mfp.MemoryFile() 796 h := d.readHandle() 797 _, cerr := d.cache.Fill(ctx, required, maxFillRange(required, optional), d.size.Load(), mf, usage.PageCache, pgalloc.AllocateAndWritePopulate, h.readToBlocksAt) 798 799 var ts []memmap.Translation 800 var translatedEnd uint64 801 for seg := d.cache.FindSegment(required.Start); seg.Ok() && seg.Start() < required.End; seg, _ = seg.NextNonEmpty() { 802 segMR := seg.Range().Intersect(optional) 803 // TODO(jamieliu): Make Translations writable even if writability is 804 // not required if already kept-dirty by another writable translation. 805 perms := hostarch.AccessType{ 806 Read: true, 807 Execute: true, 808 } 809 if at.Write { 810 // From this point forward, this memory can be dirtied through the 811 // mapping at any time. 812 d.dirty.KeepDirty(segMR) 813 perms.Write = true 814 } 815 ts = append(ts, memmap.Translation{ 816 Source: segMR, 817 File: mf, 818 Offset: seg.FileRangeOf(segMR).Start, 819 Perms: perms, 820 }) 821 translatedEnd = segMR.End 822 } 823 824 d.dataMu.Unlock() 825 d.handleMu.RUnlock() 826 827 // Don't return the error returned by c.cache.Fill if it occurred outside 828 // of required. 829 if translatedEnd < required.End && cerr != nil { 830 return ts, &memmap.BusError{cerr} 831 } 832 if beyondEOF { 833 return ts, &memmap.BusError{io.EOF} 834 } 835 return ts, nil 836 } 837 838 func maxFillRange(required, optional memmap.MappableRange) memmap.MappableRange { 839 const maxReadahead = 64 << 10 // 64 KB, chosen arbitrarily 840 if required.Length() >= maxReadahead { 841 return required 842 } 843 if optional.Length() <= maxReadahead { 844 return optional 845 } 846 optional.Start = required.Start 847 if optional.Length() <= maxReadahead { 848 return optional 849 } 850 optional.End = optional.Start + maxReadahead 851 return optional 852 } 853 854 // InvalidateUnsavable implements memmap.Mappable.InvalidateUnsavable. 855 func (d *dentry) InvalidateUnsavable(ctx context.Context) error { 856 // Whether we have a host fd (and consequently what memmap.File is 857 // mapped) can change across save/restore, so invalidate all translations 858 // unconditionally. 859 d.mapsMu.Lock() 860 defer d.mapsMu.Unlock() 861 d.mappings.InvalidateAll(memmap.InvalidateOpts{}) 862 863 // Write the cache's contents back to the remote file so that if we have a 864 // host fd after restore, the remote file's contents are coherent. 865 mf := d.fs.mfp.MemoryFile() 866 d.handleMu.RLock() 867 defer d.handleMu.RUnlock() 868 h := d.writeHandle() 869 d.dataMu.Lock() 870 defer d.dataMu.Unlock() 871 if err := fsutil.SyncDirtyAll(ctx, &d.cache, &d.dirty, d.size.Load(), mf, h.writeFromBlocksAt); err != nil { 872 return err 873 } 874 875 // Discard the cache so that it's not stored in saved state. This is safe 876 // because per InvalidateUnsavable invariants, no new translations can have 877 // been returned after we invalidated all existing translations above. 878 d.cache.DropAll(mf) 879 d.dirty.RemoveAll() 880 881 return nil 882 } 883 884 // Evict implements pgalloc.EvictableMemoryUser.Evict. 885 func (d *dentry) Evict(ctx context.Context, er pgalloc.EvictableRange) { 886 mr := memmap.MappableRange{er.Start, er.End} 887 mf := d.fs.mfp.MemoryFile() 888 d.mapsMu.Lock() 889 defer d.mapsMu.Unlock() 890 d.handleMu.RLock() 891 defer d.handleMu.RUnlock() 892 h := d.writeHandle() 893 d.dataMu.Lock() 894 defer d.dataMu.Unlock() 895 896 // Only allow pages that are no longer memory-mapped to be evicted. 897 for mgap := d.mappings.LowerBoundGap(mr.Start); mgap.Ok() && mgap.Start() < mr.End; mgap = mgap.NextGap() { 898 mgapMR := mgap.Range().Intersect(mr) 899 if mgapMR.Length() == 0 { 900 continue 901 } 902 if err := fsutil.SyncDirty(ctx, mgapMR, &d.cache, &d.dirty, d.size.Load(), mf, h.writeFromBlocksAt); err != nil { 903 log.Warningf("Failed to writeback cached data %v: %v", mgapMR, err) 904 } 905 d.cache.Drop(mgapMR, mf) 906 d.dirty.KeepClean(mgapMR) 907 } 908 } 909 910 // dentryPlatformFile implements memmap.File. It exists solely because dentry 911 // cannot implement both vfs.DentryImpl.IncRef and memmap.File.IncRef. 912 // 913 // dentryPlatformFile is only used when a host FD representing the remote file 914 // is available (i.e. dentry.mmapFD >= 0), and that FD is used for application 915 // memory mappings (i.e. !filesystem.opts.forcePageCache). 916 // 917 // +stateify savable 918 type dentryPlatformFile struct { 919 *dentry 920 921 // fdRefs counts references on memmap.File offsets. fdRefs is protected 922 // by dentry.dataMu. 923 fdRefs fsutil.FrameRefSet 924 925 // If this dentry represents a regular file, and dentry.mmapFD >= 0, 926 // hostFileMapper caches mappings of dentry.mmapFD. 927 hostFileMapper fsutil.HostFileMapper 928 929 // hostFileMapperInitOnce is used to lazily initialize hostFileMapper. 930 hostFileMapperInitOnce sync.Once `state:"nosave"` 931 } 932 933 // IncRef implements memmap.File.IncRef. 934 func (d *dentryPlatformFile) IncRef(fr memmap.FileRange, memCgID uint32) { 935 d.dataMu.Lock() 936 d.fdRefs.IncRefAndAccount(fr, memCgID) 937 d.dataMu.Unlock() 938 } 939 940 // DecRef implements memmap.File.DecRef. 941 func (d *dentryPlatformFile) DecRef(fr memmap.FileRange) { 942 d.dataMu.Lock() 943 d.fdRefs.DecRefAndAccount(fr) 944 d.dataMu.Unlock() 945 } 946 947 // MapInternal implements memmap.File.MapInternal. 948 func (d *dentryPlatformFile) MapInternal(fr memmap.FileRange, at hostarch.AccessType) (safemem.BlockSeq, error) { 949 d.handleMu.RLock() 950 defer d.handleMu.RUnlock() 951 return d.hostFileMapper.MapInternal(fr, int(d.mmapFD.RacyLoad()), at.Write) 952 } 953 954 // FD implements memmap.File.FD. 955 func (d *dentryPlatformFile) FD() int { 956 d.handleMu.RLock() 957 defer d.handleMu.RUnlock() 958 return int(d.mmapFD.RacyLoad()) 959 }