github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/pkg/sentry/fs/gofer/inode.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 gofer 16 17 import ( 18 "errors" 19 20 "golang.org/x/sys/unix" 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/fd" 25 "github.com/SagerNet/gvisor/pkg/log" 26 "github.com/SagerNet/gvisor/pkg/p9" 27 "github.com/SagerNet/gvisor/pkg/safemem" 28 "github.com/SagerNet/gvisor/pkg/sentry/device" 29 "github.com/SagerNet/gvisor/pkg/sentry/fs" 30 "github.com/SagerNet/gvisor/pkg/sentry/fs/fdpipe" 31 "github.com/SagerNet/gvisor/pkg/sentry/fs/fsutil" 32 "github.com/SagerNet/gvisor/pkg/sentry/fs/host" 33 "github.com/SagerNet/gvisor/pkg/sentry/memmap" 34 "github.com/SagerNet/gvisor/pkg/sync" 35 "github.com/SagerNet/gvisor/pkg/syserror" 36 ) 37 38 // inodeOperations implements fs.InodeOperations. 39 // 40 // +stateify savable 41 type inodeOperations struct { 42 fsutil.InodeNotVirtual `state:"nosave"` 43 44 // fileState implements fs.CachedFileObject. It exists 45 // to break a circular load dependency between inodeOperations 46 // and cachingInodeOps (below). 47 fileState *inodeFileState `state:"wait"` 48 49 // cachingInodeOps implement memmap.Mappable for inodeOperations. 50 cachingInodeOps *fsutil.CachingInodeOperations 51 52 // readdirMu protects readdirCache and concurrent Readdirs. 53 readdirMu sync.Mutex `state:"nosave"` 54 55 // readdirCache is a cache of readdir results in the form of 56 // a fs.SortedDentryMap. 57 // 58 // Starts out as nil, and is initialized under readdirMu lazily; 59 // invalidating the cache means setting it to nil. 60 readdirCache *fs.SortedDentryMap `state:"nosave"` 61 } 62 63 // inodeFileState implements fs.CachedFileObject and otherwise fully 64 // encapsulates state that needs to be manually loaded on restore for 65 // this file object. 66 // 67 // This unfortunate structure exists because fs.CachingInodeOperations 68 // defines afterLoad and therefore cannot be lazily loaded (to break a 69 // circular load dependency between it and inodeOperations). Even with 70 // lazy loading, this approach defines the dependencies between objects 71 // and the expected load behavior more concretely. 72 // 73 // +stateify savable 74 type inodeFileState struct { 75 // s is common file system state for Gofers. 76 s *session `state:"wait"` 77 78 // MultiDeviceKey consists of: 79 // 80 // * Device: file system device from a specific gofer. 81 // * SecondaryDevice: unique identifier of the attach point. 82 // * Inode: the inode of this resource, unique per Device.= 83 // 84 // These fields combined enable consistent hashing of virtual inodes 85 // on goferDevice. 86 key device.MultiDeviceKey `state:"nosave"` 87 88 // file is the p9 file that contains a single unopened fid. 89 file contextFile `state:"nosave"` 90 91 // sattr caches the stable attributes. 92 sattr fs.StableAttr `state:"wait"` 93 94 // handlesMu protects the below fields. 95 handlesMu sync.RWMutex `state:"nosave"` 96 97 // If readHandles is non-nil, it holds handles that are either read-only or 98 // read/write. If writeHandles is non-nil, it holds write-only handles if 99 // writeHandlesRW is false, and read/write handles if writeHandlesRW is 100 // true. 101 // 102 // Once readHandles becomes non-nil, it can't be changed until 103 // inodeFileState.Release()*, because of a defect in the 104 // fsutil.CachedFileObject interface: there's no way for the caller of 105 // fsutil.CachedFileObject.FD() to keep the returned FD open, so if we 106 // racily replace readHandles after inodeFileState.FD() has returned 107 // readHandles.Host.FD(), fsutil.CachingInodeOperations may use a closed 108 // FD. writeHandles can be changed if writeHandlesRW is false, since 109 // inodeFileState.FD() can't return a write-only FD, but can't be changed 110 // if writeHandlesRW is true for the same reason. 111 // 112 // * There is one notable exception in recreateReadHandles(), where it dup's 113 // the FD and invalidates the page cache. 114 readHandles *handles `state:"nosave"` 115 writeHandles *handles `state:"nosave"` 116 writeHandlesRW bool `state:"nosave"` 117 118 // loading is acquired when the inodeFileState begins an asynchronous 119 // load. It releases when the load is complete. Callers that require all 120 // state to be available should call waitForLoad() to ensure that. 121 loading sync.CrossGoroutineMutex `state:".(struct{})"` 122 123 // savedUAttr is only allocated during S/R. It points to the save-time 124 // unstable attributes and is used to validate restore-time ones. 125 // 126 // Note that these unstable attributes are only used to detect cross-S/R 127 // external file system metadata changes. They may differ from the 128 // cached unstable attributes in cachingInodeOps, as that might differ 129 // from the external file system attributes if there had been WriteOut 130 // failures. S/R is transparent to Sentry and the latter will continue 131 // using its cached values after restore. 132 savedUAttr *fs.UnstableAttr 133 134 // hostMappable is created when using 'cacheRemoteRevalidating' to map pages 135 // directly from host. 136 hostMappable *fsutil.HostMappable 137 } 138 139 // Release releases file handles. 140 func (i *inodeFileState) Release(ctx context.Context) { 141 i.file.close(ctx) 142 if i.readHandles != nil { 143 i.readHandles.DecRef() 144 } 145 if i.writeHandles != nil { 146 i.writeHandles.DecRef() 147 } 148 } 149 150 func (i *inodeFileState) canShareHandles() bool { 151 // Only share handles for regular files, since for other file types, 152 // distinct handles may have special semantics even if they represent the 153 // same file. Disable handle sharing for cache policy cacheNone, since this 154 // is legacy behavior. 155 return fs.IsFile(i.sattr) && i.s.cachePolicy != cacheNone 156 } 157 158 // Preconditions: i.handlesMu must be locked for writing. 159 func (i *inodeFileState) setSharedHandlesLocked(flags fs.FileFlags, h *handles) { 160 if flags.Read && i.readHandles == nil { 161 h.IncRef() 162 i.readHandles = h 163 } 164 if flags.Write { 165 if i.writeHandles == nil { 166 h.IncRef() 167 i.writeHandles = h 168 i.writeHandlesRW = flags.Read 169 } else if !i.writeHandlesRW && flags.Read { 170 // Upgrade i.writeHandles. 171 i.writeHandles.DecRef() 172 h.IncRef() 173 i.writeHandles = h 174 i.writeHandlesRW = flags.Read 175 } 176 } 177 } 178 179 // getHandles returns a set of handles for a new file using i opened with the 180 // given flags. 181 func (i *inodeFileState) getHandles(ctx context.Context, flags fs.FileFlags, cache *fsutil.CachingInodeOperations) (*handles, error) { 182 if !i.canShareHandles() { 183 return newHandles(ctx, i.s.client, i.file, flags) 184 } 185 186 i.handlesMu.Lock() 187 h, invalidate, err := i.getHandlesLocked(ctx, flags) 188 i.handlesMu.Unlock() 189 190 if invalidate { 191 cache.NotifyChangeFD() 192 if i.hostMappable != nil { 193 i.hostMappable.NotifyChangeFD() 194 } 195 } 196 197 return h, err 198 } 199 200 // getHandlesLocked returns a pointer to cached handles and a boolean indicating 201 // whether previously open read handle was recreated. Host mappings must be 202 // invalidated if so. 203 func (i *inodeFileState) getHandlesLocked(ctx context.Context, flags fs.FileFlags) (*handles, bool, error) { 204 // Check if we are able to use cached handles. 205 if flags.Truncate && p9.VersionSupportsOpenTruncateFlag(i.s.client.Version()) { 206 // If we are truncating (and the gofer supports it), then we 207 // always need a new handle. Don't return one from the cache. 208 } else if flags.Write { 209 if i.writeHandles != nil && (i.writeHandlesRW || !flags.Read) { 210 // File is opened for writing, and we have cached write 211 // handles that we can use. 212 i.writeHandles.IncRef() 213 return i.writeHandles, false, nil 214 } 215 } else if i.readHandles != nil { 216 // File is opened for reading and we have cached handles. 217 i.readHandles.IncRef() 218 return i.readHandles, false, nil 219 } 220 221 // Get new handles and cache them for future sharing. 222 h, err := newHandles(ctx, i.s.client, i.file, flags) 223 if err != nil { 224 return nil, false, err 225 } 226 227 // Read handles invalidation is needed if: 228 // - Mount option 'overlayfs_stale_read' is set 229 // - Read handle is open: nothing to invalidate otherwise 230 // - Write handle is not open: file was not open for write and is being open 231 // for write now (will trigger copy up in overlayfs). 232 invalidate := false 233 if i.s.overlayfsStaleRead && i.readHandles != nil && i.writeHandles == nil && flags.Write { 234 if err := i.recreateReadHandles(ctx, h, flags); err != nil { 235 return nil, false, err 236 } 237 invalidate = true 238 } 239 i.setSharedHandlesLocked(flags, h) 240 return h, invalidate, nil 241 } 242 243 func (i *inodeFileState) recreateReadHandles(ctx context.Context, writer *handles, flags fs.FileFlags) error { 244 h := writer 245 if !flags.Read { 246 // Writer can't be used for read, must create a new handle. 247 var err error 248 h, err = newHandles(ctx, i.s.client, i.file, fs.FileFlags{Read: true}) 249 if err != nil { 250 return err 251 } 252 defer h.DecRef() 253 } 254 255 if i.readHandles.Host == nil { 256 // If current readHandles doesn't have a host FD, it can simply be replaced. 257 i.readHandles.DecRef() 258 259 h.IncRef() 260 i.readHandles = h 261 return nil 262 } 263 264 if h.Host == nil { 265 // Current read handle has a host FD and can't be replaced with one that 266 // doesn't, because it breaks fsutil.CachedFileObject.FD() contract. 267 log.Warningf("Read handle can't be invalidated, reads may return stale data") 268 return nil 269 } 270 271 // Due to a defect in the fsutil.CachedFileObject interface, 272 // readHandles.Host.FD() may be used outside locks, making it impossible to 273 // reliably close it. To workaround it, we dup the new FD into the old one, so 274 // operations on the old will see the new data. Then, make the new handle take 275 // ownereship of the old FD and mark the old readHandle to not close the FD 276 // when done. 277 if err := unix.Dup3(h.Host.FD(), i.readHandles.Host.FD(), unix.O_CLOEXEC); err != nil { 278 return err 279 } 280 281 h.Host.Close() 282 h.Host = fd.New(i.readHandles.Host.FD()) 283 i.readHandles.isHostBorrowed = true 284 i.readHandles.DecRef() 285 286 h.IncRef() 287 i.readHandles = h 288 return nil 289 } 290 291 // ReadToBlocksAt implements fsutil.CachedFileObject.ReadToBlocksAt. 292 func (i *inodeFileState) ReadToBlocksAt(ctx context.Context, dsts safemem.BlockSeq, offset uint64) (uint64, error) { 293 i.handlesMu.RLock() 294 n, err := i.readHandles.readWriterAt(ctx, int64(offset)).ReadToBlocks(dsts) 295 i.handlesMu.RUnlock() 296 return n, err 297 } 298 299 // WriteFromBlocksAt implements fsutil.CachedFileObject.WriteFromBlocksAt. 300 func (i *inodeFileState) WriteFromBlocksAt(ctx context.Context, srcs safemem.BlockSeq, offset uint64) (uint64, error) { 301 i.handlesMu.RLock() 302 n, err := i.writeHandles.readWriterAt(ctx, int64(offset)).WriteFromBlocks(srcs) 303 i.handlesMu.RUnlock() 304 return n, err 305 } 306 307 // SetMaskedAttributes implements fsutil.CachedFileObject.SetMaskedAttributes. 308 func (i *inodeFileState) SetMaskedAttributes(ctx context.Context, mask fs.AttrMask, attr fs.UnstableAttr, forceSetTimestamps bool) error { 309 if i.skipSetAttr(mask, forceSetTimestamps) { 310 return nil 311 } 312 as, ans := attr.AccessTime.Unix() 313 ms, mns := attr.ModificationTime.Unix() 314 // An update of status change time is implied by mask.AccessTime 315 // or mask.ModificationTime. Updating status change time to a 316 // time earlier than the system time is not possible. 317 return i.file.setAttr( 318 ctx, 319 p9.SetAttrMask{ 320 Permissions: mask.Perms, 321 Size: mask.Size, 322 UID: mask.UID, 323 GID: mask.GID, 324 ATime: mask.AccessTime, 325 ATimeNotSystemTime: true, 326 MTime: mask.ModificationTime, 327 MTimeNotSystemTime: true, 328 }, p9.SetAttr{ 329 Permissions: p9.FileMode(attr.Perms.LinuxMode()), 330 UID: p9.UID(attr.Owner.UID), 331 GID: p9.GID(attr.Owner.GID), 332 Size: uint64(attr.Size), 333 ATimeSeconds: uint64(as), 334 ATimeNanoSeconds: uint64(ans), 335 MTimeSeconds: uint64(ms), 336 MTimeNanoSeconds: uint64(mns), 337 }) 338 } 339 340 // skipSetAttr checks if attribute change can be skipped. It can be skipped 341 // when: 342 // - Mask is empty 343 // - Mask contains only attributes that cannot be set in the gofer 344 // - forceSetTimestamps is false and mask contains only atime and/or mtime 345 // and host FD exists 346 // 347 // Updates to atime and mtime can be skipped because cached value will be 348 // "close enough" to host value, given that operation went directly to host FD. 349 // Skipping atime updates is particularly important to reduce the number of 350 // operations sent to the Gofer for readonly files. 351 func (i *inodeFileState) skipSetAttr(mask fs.AttrMask, forceSetTimestamps bool) bool { 352 // First remove attributes that cannot be updated. 353 cpy := mask 354 cpy.Type = false 355 cpy.DeviceID = false 356 cpy.InodeID = false 357 cpy.BlockSize = false 358 cpy.Usage = false 359 cpy.Links = false 360 if cpy.Empty() { 361 return true 362 } 363 364 // Then check if more than just atime and mtime is being set. 365 cpy.AccessTime = false 366 cpy.ModificationTime = false 367 if !cpy.Empty() { 368 return false 369 } 370 371 // If forceSetTimestamps was passed, then we cannot skip. 372 if forceSetTimestamps { 373 return false 374 } 375 376 // Skip if we have a host FD. 377 i.handlesMu.RLock() 378 defer i.handlesMu.RUnlock() 379 return (i.readHandles != nil && i.readHandles.Host != nil) || 380 (i.writeHandles != nil && i.writeHandles.Host != nil) 381 } 382 383 // Sync implements fsutil.CachedFileObject.Sync. 384 func (i *inodeFileState) Sync(ctx context.Context) error { 385 i.handlesMu.RLock() 386 defer i.handlesMu.RUnlock() 387 if i.writeHandles == nil { 388 return nil 389 } 390 return i.writeHandles.File.fsync(ctx) 391 } 392 393 // FD implements fsutil.CachedFileObject.FD. 394 func (i *inodeFileState) FD() int { 395 i.handlesMu.RLock() 396 defer i.handlesMu.RUnlock() 397 if i.writeHandlesRW && i.writeHandles != nil && i.writeHandles.Host != nil { 398 return int(i.writeHandles.Host.FD()) 399 } 400 if i.readHandles != nil && i.readHandles.Host != nil { 401 return int(i.readHandles.Host.FD()) 402 } 403 return -1 404 } 405 406 // waitForLoad makes sure any restore-issued loading is done. 407 func (i *inodeFileState) waitForLoad() { 408 // This is not a no-op. The loading mutex is hold upon restore until 409 // all loading actions are done. 410 i.loading.Lock() 411 i.loading.Unlock() 412 } 413 414 func (i *inodeFileState) unstableAttr(ctx context.Context) (fs.UnstableAttr, error) { 415 _, valid, pattr, err := getattr(ctx, i.file) 416 if err != nil { 417 return fs.UnstableAttr{}, err 418 } 419 return unstable(ctx, valid, pattr, i.s.mounter, i.s.client), nil 420 } 421 422 func (i *inodeFileState) Allocate(ctx context.Context, offset, length int64) error { 423 i.handlesMu.RLock() 424 defer i.handlesMu.RUnlock() 425 426 // No options are supported for now. 427 mode := p9.AllocateMode{} 428 return i.writeHandles.File.allocate(ctx, mode, uint64(offset), uint64(length)) 429 } 430 431 // session extracts the gofer's session from the MountSource. 432 func (i *inodeOperations) session() *session { 433 return i.fileState.s 434 } 435 436 // Release implements fs.InodeOperations.Release. 437 func (i *inodeOperations) Release(ctx context.Context) { 438 i.cachingInodeOps.Release() 439 440 // Releasing the fileState may make RPCs to the gofer. There is 441 // no need to wait for those to return, so we can do this 442 // asynchronously. 443 // 444 // We use AsyncWithContext to avoid needing to allocate an extra 445 // anonymous function on the heap. We must use background context 446 // because the async work cannot happen on the task context. 447 fs.AsyncWithContext(context.Background(), i.fileState.Release) 448 } 449 450 // Mappable implements fs.InodeOperations.Mappable. 451 func (i *inodeOperations) Mappable(inode *fs.Inode) memmap.Mappable { 452 if i.session().cachePolicy.useCachingInodeOps(inode) { 453 return i.cachingInodeOps 454 } 455 // This check is necessary because it's returning an interface type. 456 if i.fileState.hostMappable != nil { 457 return i.fileState.hostMappable 458 } 459 return nil 460 } 461 462 // UnstableAttr implements fs.InodeOperations.UnstableAttr. 463 func (i *inodeOperations) UnstableAttr(ctx context.Context, inode *fs.Inode) (fs.UnstableAttr, error) { 464 if i.session().cachePolicy.cacheUAttrs(inode) { 465 return i.cachingInodeOps.UnstableAttr(ctx, inode) 466 } 467 return i.fileState.unstableAttr(ctx) 468 } 469 470 // Check implements fs.InodeOperations.Check. 471 func (i *inodeOperations) Check(ctx context.Context, inode *fs.Inode, p fs.PermMask) bool { 472 return fs.ContextCanAccessFile(ctx, inode, p) 473 } 474 475 // GetFile implements fs.InodeOperations.GetFile. 476 func (i *inodeOperations) GetFile(ctx context.Context, d *fs.Dirent, flags fs.FileFlags) (*fs.File, error) { 477 switch d.Inode.StableAttr.Type { 478 case fs.Socket: 479 if i.session().overrides != nil { 480 return nil, linuxerr.ENXIO 481 } 482 return i.getFileSocket(ctx, d, flags) 483 case fs.Pipe: 484 return i.getFilePipe(ctx, d, flags) 485 default: 486 return i.getFileDefault(ctx, d, flags) 487 } 488 } 489 490 func (i *inodeOperations) getFileSocket(ctx context.Context, d *fs.Dirent, flags fs.FileFlags) (*fs.File, error) { 491 f, err := i.fileState.file.connect(ctx, p9.AnonymousSocket) 492 if err != nil { 493 return nil, unix.EIO 494 } 495 fsf, err := host.NewSocketWithDirent(ctx, d, f, flags) 496 if err != nil { 497 f.Close() 498 return nil, err 499 } 500 return fsf, nil 501 } 502 503 func (i *inodeOperations) getFilePipe(ctx context.Context, d *fs.Dirent, flags fs.FileFlags) (*fs.File, error) { 504 // Try to open as a host pipe; if that doesn't work, handle it normally. 505 pipeOps, err := fdpipe.Open(ctx, i, flags) 506 if err == errNotHostFile { 507 return i.getFileDefault(ctx, d, flags) 508 } 509 if err != nil { 510 return nil, err 511 } 512 return fs.NewFile(ctx, d, flags, pipeOps), nil 513 } 514 515 // errNotHostFile indicates that the file is not a host file. 516 var errNotHostFile = errors.New("not a host file") 517 518 // NonBlockingOpen implements fdpipe.NonBlockingOpener for opening host named pipes. 519 func (i *inodeOperations) NonBlockingOpen(ctx context.Context, p fs.PermMask) (*fd.FD, error) { 520 i.fileState.waitForLoad() 521 522 // Get a cloned fid which we will open. 523 _, newFile, err := i.fileState.file.walk(ctx, nil) 524 if err != nil { 525 log.Warningf("Open Walk failed: %v", err) 526 return nil, err 527 } 528 defer newFile.close(ctx) 529 530 flags, err := openFlagsFromPerms(p) 531 if err != nil { 532 log.Warningf("Open flags %s parsing failed: %v", p, err) 533 return nil, err 534 } 535 hostFile, _, _, err := newFile.open(ctx, flags) 536 // If the host file returned is nil and the error is nil, 537 // then this was never a host file to begin with, and should 538 // be treated like a remote file. 539 if hostFile == nil && err == nil { 540 return nil, errNotHostFile 541 } 542 return hostFile, err 543 } 544 545 func (i *inodeOperations) getFileDefault(ctx context.Context, d *fs.Dirent, flags fs.FileFlags) (*fs.File, error) { 546 h, err := i.fileState.getHandles(ctx, flags, i.cachingInodeOps) 547 if err != nil { 548 return nil, err 549 } 550 return NewFile(ctx, d, d.BaseName(), flags, i, h), nil 551 } 552 553 // SetPermissions implements fs.InodeOperations.SetPermissions. 554 func (i *inodeOperations) SetPermissions(ctx context.Context, inode *fs.Inode, p fs.FilePermissions) bool { 555 if i.session().cachePolicy.cacheUAttrs(inode) { 556 return i.cachingInodeOps.SetPermissions(ctx, inode, p) 557 } 558 559 mask := p9.SetAttrMask{Permissions: true} 560 pattr := p9.SetAttr{Permissions: p9.FileMode(p.LinuxMode())} 561 // Execute the chmod. 562 return i.fileState.file.setAttr(ctx, mask, pattr) == nil 563 } 564 565 // SetOwner implements fs.InodeOperations.SetOwner. 566 func (i *inodeOperations) SetOwner(ctx context.Context, inode *fs.Inode, owner fs.FileOwner) error { 567 // Save the roundtrip. 568 if !owner.UID.Ok() && !owner.GID.Ok() { 569 return nil 570 } 571 572 if i.session().cachePolicy.cacheUAttrs(inode) { 573 return i.cachingInodeOps.SetOwner(ctx, inode, owner) 574 } 575 576 var mask p9.SetAttrMask 577 var attr p9.SetAttr 578 if owner.UID.Ok() { 579 mask.UID = true 580 attr.UID = p9.UID(owner.UID) 581 } 582 if owner.GID.Ok() { 583 mask.GID = true 584 attr.GID = p9.GID(owner.GID) 585 } 586 return i.fileState.file.setAttr(ctx, mask, attr) 587 } 588 589 // SetTimestamps implements fs.InodeOperations.SetTimestamps. 590 func (i *inodeOperations) SetTimestamps(ctx context.Context, inode *fs.Inode, ts fs.TimeSpec) error { 591 if i.session().cachePolicy.cacheUAttrs(inode) { 592 return i.cachingInodeOps.SetTimestamps(ctx, inode, ts) 593 } 594 595 return utimes(ctx, i.fileState.file, ts) 596 } 597 598 // Truncate implements fs.InodeOperations.Truncate. 599 func (i *inodeOperations) Truncate(ctx context.Context, inode *fs.Inode, length int64) error { 600 // This can only be called for files anyway. 601 if i.session().cachePolicy.useCachingInodeOps(inode) { 602 return i.cachingInodeOps.Truncate(ctx, inode, length) 603 } 604 605 uattr, err := i.fileState.unstableAttr(ctx) 606 if err != nil { 607 return err 608 } 609 610 if i.session().cachePolicy == cacheRemoteRevalidating { 611 return i.fileState.hostMappable.Truncate(ctx, length, uattr) 612 } 613 614 mask := p9.SetAttrMask{Size: true} 615 attr := p9.SetAttr{Size: uint64(length)} 616 if uattr.Perms.HasSetUIDOrGID() { 617 mask.Permissions = true 618 uattr.Perms.DropSetUIDAndMaybeGID() 619 attr.Permissions = p9.FileMode(uattr.Perms.LinuxMode()) 620 } 621 622 return i.fileState.file.setAttr(ctx, mask, attr) 623 } 624 625 // GetXattr implements fs.InodeOperations.GetXattr. 626 func (i *inodeOperations) GetXattr(ctx context.Context, _ *fs.Inode, name string, size uint64) (string, error) { 627 return i.fileState.file.getXattr(ctx, name, size) 628 } 629 630 // SetXattr implements fs.InodeOperations.SetXattr. 631 func (i *inodeOperations) SetXattr(ctx context.Context, _ *fs.Inode, name string, value string, flags uint32) error { 632 return i.fileState.file.setXattr(ctx, name, value, flags) 633 } 634 635 // ListXattr implements fs.InodeOperations.ListXattr. 636 func (i *inodeOperations) ListXattr(ctx context.Context, _ *fs.Inode, size uint64) (map[string]struct{}, error) { 637 return i.fileState.file.listXattr(ctx, size) 638 } 639 640 // RemoveXattr implements fs.InodeOperations.RemoveXattr. 641 func (i *inodeOperations) RemoveXattr(ctx context.Context, _ *fs.Inode, name string) error { 642 return i.fileState.file.removeXattr(ctx, name) 643 } 644 645 // Allocate implements fs.InodeOperations.Allocate. 646 func (i *inodeOperations) Allocate(ctx context.Context, inode *fs.Inode, offset, length int64) error { 647 // This can only be called for files anyway. 648 if i.session().cachePolicy.useCachingInodeOps(inode) { 649 return i.cachingInodeOps.Allocate(ctx, offset, length) 650 } 651 if i.session().cachePolicy == cacheRemoteRevalidating { 652 return i.fileState.hostMappable.Allocate(ctx, offset, length) 653 } 654 655 // No options are supported for now. 656 mode := p9.AllocateMode{} 657 return i.fileState.file.allocate(ctx, mode, uint64(offset), uint64(length)) 658 } 659 660 // WriteOut implements fs.InodeOperations.WriteOut. 661 func (i *inodeOperations) WriteOut(ctx context.Context, inode *fs.Inode) error { 662 if inode.MountSource.Flags.ReadOnly || !i.session().cachePolicy.cacheUAttrs(inode) { 663 return nil 664 } 665 666 return i.cachingInodeOps.WriteOut(ctx, inode) 667 } 668 669 // Readlink implements fs.InodeOperations.Readlink. 670 func (i *inodeOperations) Readlink(ctx context.Context, inode *fs.Inode) (string, error) { 671 if !fs.IsSymlink(inode.StableAttr) { 672 return "", unix.ENOLINK 673 } 674 return i.fileState.file.readlink(ctx) 675 } 676 677 // Getlink implementfs fs.InodeOperations.Getlink. 678 func (i *inodeOperations) Getlink(context.Context, *fs.Inode) (*fs.Dirent, error) { 679 if !fs.IsSymlink(i.fileState.sattr) { 680 return nil, linuxerr.ENOLINK 681 } 682 return nil, fs.ErrResolveViaReadlink 683 } 684 685 // StatFS makes a StatFS request. 686 func (i *inodeOperations) StatFS(ctx context.Context) (fs.Info, error) { 687 fsstat, err := i.fileState.file.statFS(ctx) 688 if err != nil { 689 return fs.Info{}, err 690 } 691 692 info := fs.Info{ 693 // This is primarily for distinguishing a gofer file system in 694 // tests. Testing is important, so instead of defining 695 // something completely random, use a standard value. 696 Type: linux.V9FS_MAGIC, 697 TotalBlocks: fsstat.Blocks, 698 FreeBlocks: fsstat.BlocksFree, 699 TotalFiles: fsstat.Files, 700 FreeFiles: fsstat.FilesFree, 701 } 702 703 // If blocks available is non-zero, prefer that. 704 if fsstat.BlocksAvailable != 0 { 705 info.FreeBlocks = fsstat.BlocksAvailable 706 } 707 708 return info, nil 709 } 710 711 func (i *inodeOperations) configureMMap(file *fs.File, opts *memmap.MMapOpts) error { 712 if i.session().cachePolicy.useCachingInodeOps(file.Dirent.Inode) { 713 return fsutil.GenericConfigureMMap(file, i.cachingInodeOps, opts) 714 } 715 if i.fileState.hostMappable != nil { 716 return fsutil.GenericConfigureMMap(file, i.fileState.hostMappable, opts) 717 } 718 return linuxerr.ENODEV 719 } 720 721 func init() { 722 syserror.AddErrorUnwrapper(func(err error) (unix.Errno, bool) { 723 if _, ok := err.(p9.ErrSocket); ok { 724 // Treat as an I/O error. 725 return unix.EIO, true 726 } 727 return 0, false 728 }) 729 } 730 731 // AddLink implements InodeOperations.AddLink, but is currently a noop. 732 func (*inodeOperations) AddLink() {} 733 734 // DropLink implements InodeOperations.DropLink, but is currently a noop. 735 func (*inodeOperations) DropLink() {} 736 737 // NotifyStatusChange implements fs.InodeOperations.NotifyStatusChange. 738 func (i *inodeOperations) NotifyStatusChange(ctx context.Context) {}