github.com/metacubex/gvisor@v0.0.0-20240320004321-933faba989ec/pkg/sentry/fsimpl/gofer/dentry_impl.go (about) 1 // Copyright 2022 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 "golang.org/x/sys/unix" 19 "github.com/metacubex/gvisor/pkg/abi/linux" 20 "github.com/metacubex/gvisor/pkg/atomicbitops" 21 "github.com/metacubex/gvisor/pkg/context" 22 "github.com/metacubex/gvisor/pkg/errors/linuxerr" 23 "github.com/metacubex/gvisor/pkg/fsutil" 24 "github.com/metacubex/gvisor/pkg/log" 25 "github.com/metacubex/gvisor/pkg/sentry/kernel/auth" 26 "github.com/metacubex/gvisor/pkg/sentry/vfs" 27 ) 28 29 // We do *not* define an interface for dentry.impl because making interface 30 // method calls is almost 2.5x slower than calling the same method on a 31 // concrete type. Instead, we use type assertions in switch statements. The 32 // asserted type is a concrete dentry implementation and methods are called 33 // directly on the concrete type. This helps in the following ways: 34 // 35 // 1. This is faster because concrete type assertion just needs to compare the 36 // itab pointer in the interface value to a constant which is relatively 37 // cheap. Benchmarking showed that such type switches don't add almost any 38 // overhead. 39 // 2. Passing any pointer to an interface method immediately causes the pointed 40 // object to escape to heap. Making concrete method calls allows escape 41 // analysis to proceed as usual and avoids heap allocations. 42 // 43 // Also note that the default case in these type switch statements panics. We 44 // do not do panic(fmt.Sprintf("... %T", d.impl)) because somehow it adds a lot 45 // of overhead to the type switch. So instead we panic with a constant string. 46 47 // Precondition: d.handleMu must be locked. 48 func (d *dentry) isReadHandleOk() bool { 49 switch dt := d.impl.(type) { 50 case *lisafsDentry: 51 return dt.readFDLisa.Ok() 52 case *directfsDentry: 53 return d.readFD.RacyLoad() >= 0 54 case nil: // synthetic dentry 55 return false 56 default: 57 panic("unknown dentry implementation") 58 } 59 } 60 61 // Precondition: d.handleMu must be locked. 62 func (d *dentry) isWriteHandleOk() bool { 63 switch dt := d.impl.(type) { 64 case *lisafsDentry: 65 return dt.writeFDLisa.Ok() 66 case *directfsDentry: 67 return d.writeFD.RacyLoad() >= 0 68 case nil: // synthetic dentry 69 return false 70 default: 71 panic("unknown dentry implementation") 72 } 73 } 74 75 // Precondition: d.handleMu must be locked. 76 func (d *dentry) readHandle() handle { 77 switch dt := d.impl.(type) { 78 case *lisafsDentry: 79 return handle{ 80 fdLisa: dt.readFDLisa, 81 fd: d.readFD.RacyLoad(), 82 } 83 case *directfsDentry: 84 return handle{fd: d.readFD.RacyLoad()} 85 case nil: // synthetic dentry 86 return noHandle 87 default: 88 panic("unknown dentry implementation") 89 } 90 } 91 92 // Precondition: d.handleMu must be locked. 93 func (d *dentry) writeHandle() handle { 94 switch dt := d.impl.(type) { 95 case *lisafsDentry: 96 return handle{ 97 fdLisa: dt.writeFDLisa, 98 fd: d.writeFD.RacyLoad(), 99 } 100 case *directfsDentry: 101 return handle{fd: d.writeFD.RacyLoad()} 102 case nil: // synthetic dentry 103 return noHandle 104 default: 105 panic("unknown dentry implementation") 106 } 107 } 108 109 // Preconditions: 110 // - !d.isSynthetic(). 111 // - fs.renameMu is locked. 112 func (d *dentry) openHandle(ctx context.Context, read, write, trunc bool) (handle, error) { 113 flags := uint32(unix.O_RDONLY) 114 switch { 115 case read && write: 116 flags = unix.O_RDWR 117 case read: 118 flags = unix.O_RDONLY 119 case write: 120 flags = unix.O_WRONLY 121 default: 122 log.Debugf("openHandle called with read = write = false. Falling back to read only FD.") 123 } 124 if trunc { 125 flags |= unix.O_TRUNC 126 } 127 switch dt := d.impl.(type) { 128 case *lisafsDentry: 129 return dt.openHandle(ctx, flags) 130 case *directfsDentry: 131 return dt.openHandle(ctx, flags) 132 default: 133 panic("unknown dentry implementation") 134 } 135 } 136 137 // Preconditions: 138 // - d.handleMu must be locked. 139 // - !d.isSynthetic(). 140 func (d *dentry) updateHandles(ctx context.Context, h handle, readable, writable bool) { 141 switch dt := d.impl.(type) { 142 case *lisafsDentry: 143 dt.updateHandles(ctx, h, readable, writable) 144 case *directfsDentry: 145 // No update needed. 146 default: 147 panic("unknown dentry implementation") 148 } 149 } 150 151 // Preconditions: 152 // - d.handleMu must be locked. 153 // - !d.isSynthetic(). 154 func (d *dentry) closeHostFDs() { 155 // We can use RacyLoad() because d.handleMu is locked. 156 if d.readFD.RacyLoad() >= 0 { 157 _ = unix.Close(int(d.readFD.RacyLoad())) 158 } 159 if d.writeFD.RacyLoad() >= 0 && d.readFD.RacyLoad() != d.writeFD.RacyLoad() { 160 _ = unix.Close(int(d.writeFD.RacyLoad())) 161 } 162 d.readFD = atomicbitops.FromInt32(-1) 163 d.writeFD = atomicbitops.FromInt32(-1) 164 d.mmapFD = atomicbitops.FromInt32(-1) 165 166 switch dt := d.impl.(type) { 167 case *directfsDentry: 168 if dt.controlFD >= 0 { 169 _ = unix.Close(dt.controlFD) 170 dt.controlFD = -1 171 } 172 } 173 } 174 175 // updateMetadataLocked updates the dentry's metadata fields. The h parameter 176 // is optional. If it is not provided, an appropriate FD should be chosen to 177 // stat the remote file. 178 // 179 // Preconditions: 180 // - !d.isSynthetic(). 181 // - d.metadataMu is locked. 182 // 183 // +checklocks:d.metadataMu 184 func (d *dentry) updateMetadataLocked(ctx context.Context, h handle) error { 185 // Need checklocksforce below because checklocks has no way of knowing that 186 // d.impl.(*dentryImpl).dentry == d. It can't know that the right metadataMu 187 // is already locked. 188 switch dt := d.impl.(type) { 189 case *lisafsDentry: 190 return dt.updateMetadataLocked(ctx, h) // +checklocksforce: acquired by precondition. 191 case *directfsDentry: 192 return dt.updateMetadataLocked(h) // +checklocksforce: acquired by precondition. 193 default: 194 panic("unknown dentry implementation") 195 } 196 } 197 198 // Preconditions: 199 // - !d.isSynthetic(). 200 // - fs.renameMu is locked. 201 func (d *dentry) prepareSetStat(ctx context.Context, stat *linux.Statx) error { 202 switch dt := d.impl.(type) { 203 case *lisafsDentry: 204 // Nothing to be done. 205 return nil 206 case *directfsDentry: 207 return dt.prepareSetStat(ctx, stat) 208 default: 209 panic("unknown dentry implementation") 210 } 211 } 212 213 // Precondition: fs.renameMu is locked if d is a socket. 214 func (d *dentry) chmod(ctx context.Context, mode uint16) error { 215 switch dt := d.impl.(type) { 216 case *lisafsDentry: 217 return chmod(ctx, dt.controlFD, mode) 218 case *directfsDentry: 219 return dt.chmod(ctx, mode) 220 default: 221 panic("unknown dentry implementation") 222 } 223 } 224 225 // Preconditions: 226 // - !d.isSynthetic(). 227 // - d.handleMu is locked. 228 // - fs.renameMu is locked. 229 func (d *dentry) setStatLocked(ctx context.Context, stat *linux.Statx) (uint32, error, error) { 230 switch dt := d.impl.(type) { 231 case *lisafsDentry: 232 return dt.controlFD.SetStat(ctx, stat) 233 case *directfsDentry: 234 failureMask, failureErr := dt.setStatLocked(ctx, stat) 235 return failureMask, failureErr, nil 236 default: 237 panic("unknown dentry implementation") 238 } 239 } 240 241 // Precondition: d.handleMu must be locked. 242 func (d *dentry) destroyImpl(ctx context.Context) { 243 switch dt := d.impl.(type) { 244 case *lisafsDentry: 245 dt.destroy(ctx) 246 case *directfsDentry: 247 dt.destroy(ctx) 248 case nil: // synthetic dentry 249 default: 250 panic("unknown dentry implementation") 251 } 252 } 253 254 // Postcondition: Caller must do dentry caching appropriately. 255 // 256 // +checklocksread:d.opMu 257 func (d *dentry) getRemoteChild(ctx context.Context, name string) (*dentry, error) { 258 switch dt := d.impl.(type) { 259 case *lisafsDentry: 260 return dt.getRemoteChild(ctx, name) 261 case *directfsDentry: 262 return dt.getHostChild(name) 263 default: 264 panic("unknown dentry implementation") 265 } 266 } 267 268 // Preconditions: 269 // - fs.renameMu must be locked. 270 // - parent.opMu must be locked for reading. 271 // - parent.isDir(). 272 // - !rp.Done() && rp.Component() is not "." or "..". 273 // 274 // Postcondition: The returned dentry is already cached appropriately. 275 // 276 // +checklocksread:d.opMu 277 func (d *dentry) getRemoteChildAndWalkPathLocked(ctx context.Context, rp resolvingPath, ds **[]*dentry) (*dentry, error) { 278 switch dt := d.impl.(type) { 279 case *lisafsDentry: 280 return dt.getRemoteChildAndWalkPathLocked(ctx, rp, ds) 281 case *directfsDentry: 282 // We need to check for races because opMu is read locked which allows 283 // concurrent walks to occur. 284 return d.fs.getRemoteChildLocked(ctx, d, rp.Component(), true /* checkForRace */, ds) 285 default: 286 panic("unknown dentry implementation") 287 } 288 } 289 290 // Precondition: !d.isSynthetic(). 291 func (d *dentry) listXattrImpl(ctx context.Context, size uint64) ([]string, error) { 292 switch dt := d.impl.(type) { 293 case *lisafsDentry: 294 return dt.controlFD.ListXattr(ctx, size) 295 case *directfsDentry: 296 // Consistent with runsc/fsgofer. 297 return nil, linuxerr.EOPNOTSUPP 298 default: 299 panic("unknown dentry implementation") 300 } 301 } 302 303 // Precondition: !d.isSynthetic(). 304 func (d *dentry) getXattrImpl(ctx context.Context, opts *vfs.GetXattrOptions) (string, error) { 305 switch dt := d.impl.(type) { 306 case *lisafsDentry: 307 return dt.controlFD.GetXattr(ctx, opts.Name, opts.Size) 308 case *directfsDentry: 309 return dt.getXattr(opts.Name, opts.Size) 310 default: 311 panic("unknown dentry implementation") 312 } 313 } 314 315 // Precondition: !d.isSynthetic(). 316 func (d *dentry) setXattrImpl(ctx context.Context, opts *vfs.SetXattrOptions) error { 317 switch dt := d.impl.(type) { 318 case *lisafsDentry: 319 return dt.controlFD.SetXattr(ctx, opts.Name, opts.Value, opts.Flags) 320 case *directfsDentry: 321 // Consistent with runsc/fsgofer. 322 return linuxerr.EOPNOTSUPP 323 default: 324 panic("unknown dentry implementation") 325 } 326 } 327 328 // Precondition: !d.isSynthetic(). 329 func (d *dentry) removeXattrImpl(ctx context.Context, name string) error { 330 switch dt := d.impl.(type) { 331 case *lisafsDentry: 332 return dt.controlFD.RemoveXattr(ctx, name) 333 case *directfsDentry: 334 // Consistent with runsc/fsgofer. 335 return linuxerr.EOPNOTSUPP 336 default: 337 panic("unknown dentry implementation") 338 } 339 } 340 341 // Precondition: !d.isSynthetic(). 342 func (d *dentry) mknod(ctx context.Context, name string, creds *auth.Credentials, opts *vfs.MknodOptions) (*dentry, error) { 343 switch dt := d.impl.(type) { 344 case *lisafsDentry: 345 return dt.mknod(ctx, name, creds, opts) 346 case *directfsDentry: 347 return dt.mknod(ctx, name, creds, opts) 348 default: 349 panic("unknown dentry implementation") 350 } 351 } 352 353 // Preconditions: 354 // - !d.isSynthetic(). 355 // - !target.isSynthetic(). 356 // - d.fs.renameMu must be locked. 357 func (d *dentry) link(ctx context.Context, target *dentry, name string) (*dentry, error) { 358 switch dt := d.impl.(type) { 359 case *lisafsDentry: 360 return dt.link(ctx, target.impl.(*lisafsDentry), name) 361 case *directfsDentry: 362 return dt.link(target.impl.(*directfsDentry), name) 363 default: 364 panic("unknown dentry implementation") 365 } 366 } 367 368 // Precondition: !d.isSynthetic(). 369 func (d *dentry) mkdir(ctx context.Context, name string, mode linux.FileMode, uid auth.KUID, gid auth.KGID) (*dentry, error) { 370 switch dt := d.impl.(type) { 371 case *lisafsDentry: 372 return dt.mkdir(ctx, name, mode, uid, gid) 373 case *directfsDentry: 374 return dt.mkdir(name, mode, uid, gid) 375 default: 376 panic("unknown dentry implementation") 377 } 378 } 379 380 // Precondition: !d.isSynthetic(). 381 func (d *dentry) symlink(ctx context.Context, name, target string, creds *auth.Credentials) (*dentry, error) { 382 switch dt := d.impl.(type) { 383 case *lisafsDentry: 384 return dt.symlink(ctx, name, target, creds) 385 case *directfsDentry: 386 return dt.symlink(name, target, creds) 387 default: 388 panic("unknown dentry implementation") 389 } 390 } 391 392 // Precondition: !d.isSynthetic(). 393 func (d *dentry) openCreate(ctx context.Context, name string, accessFlags uint32, mode linux.FileMode, uid auth.KUID, gid auth.KGID) (*dentry, handle, error) { 394 switch dt := d.impl.(type) { 395 case *lisafsDentry: 396 return dt.openCreate(ctx, name, accessFlags, mode, uid, gid) 397 case *directfsDentry: 398 return dt.openCreate(name, accessFlags, mode, uid, gid) 399 default: 400 panic("unknown dentry implementation") 401 } 402 } 403 404 // Preconditions: 405 // - d.isDir(). 406 // - d.handleMu must be locked. 407 // - !d.isSynthetic(). 408 func (d *dentry) getDirentsLocked(ctx context.Context, recordDirent func(name string, key inoKey, dType uint8)) error { 409 switch dt := d.impl.(type) { 410 case *lisafsDentry: 411 return dt.getDirentsLocked(ctx, recordDirent) 412 case *directfsDentry: 413 return dt.getDirentsLocked(recordDirent) 414 default: 415 panic("unknown dentry implementation") 416 } 417 } 418 419 // Precondition: !d.isSynthetic(). 420 func (d *dentry) flush(ctx context.Context) error { 421 d.handleMu.RLock() 422 defer d.handleMu.RUnlock() 423 switch dt := d.impl.(type) { 424 case *lisafsDentry: 425 return flush(ctx, dt.writeFDLisa) 426 case *directfsDentry: 427 // Nothing to do here. 428 return nil 429 default: 430 panic("unknown dentry implementation") 431 } 432 } 433 434 // Precondition: !d.isSynthetic(). 435 func (d *dentry) allocate(ctx context.Context, mode, offset, length uint64) error { 436 d.handleMu.RLock() 437 defer d.handleMu.RUnlock() 438 switch dt := d.impl.(type) { 439 case *lisafsDentry: 440 return dt.writeFDLisa.Allocate(ctx, mode, offset, length) 441 case *directfsDentry: 442 return unix.Fallocate(int(d.writeFD.RacyLoad()), uint32(mode), int64(offset), int64(length)) 443 default: 444 panic("unknown dentry implementation") 445 } 446 } 447 448 // Preconditions: 449 // - !d.isSynthetic(). 450 // - fs.renameMu is locked. 451 func (d *dentry) connect(ctx context.Context, sockType linux.SockType) (int, error) { 452 switch dt := d.impl.(type) { 453 case *lisafsDentry: 454 return dt.controlFD.Connect(ctx, sockType) 455 case *directfsDentry: 456 return dt.connect(ctx, sockType) 457 default: 458 panic("unknown dentry implementation") 459 } 460 } 461 462 // Precondition: !d.isSynthetic(). 463 func (d *dentry) readlinkImpl(ctx context.Context) (string, error) { 464 switch dt := d.impl.(type) { 465 case *lisafsDentry: 466 return dt.controlFD.ReadLinkAt(ctx) 467 case *directfsDentry: 468 return dt.readlink() 469 default: 470 panic("unknown dentry implementation") 471 } 472 } 473 474 // Precondition: !d.isSynthetic(). 475 func (d *dentry) unlink(ctx context.Context, name string, flags uint32) error { 476 switch dt := d.impl.(type) { 477 case *lisafsDentry: 478 return dt.controlFD.UnlinkAt(ctx, name, flags) 479 case *directfsDentry: 480 return unix.Unlinkat(dt.controlFD, name, int(flags)) 481 default: 482 panic("unknown dentry implementation") 483 } 484 } 485 486 // Precondition: !d.isSynthetic(). 487 func (d *dentry) rename(ctx context.Context, oldName string, newParent *dentry, newName string) error { 488 switch dt := d.impl.(type) { 489 case *lisafsDentry: 490 return dt.controlFD.RenameAt(ctx, oldName, newParent.impl.(*lisafsDentry).controlFD.ID(), newName) 491 case *directfsDentry: 492 return fsutil.RenameAt(dt.controlFD, oldName, newParent.impl.(*directfsDentry).controlFD, newName) 493 default: 494 panic("unknown dentry implementation") 495 } 496 } 497 498 // Precondition: !d.isSynthetic(). 499 func (d *dentry) statfs(ctx context.Context) (linux.Statfs, error) { 500 switch dt := d.impl.(type) { 501 case *lisafsDentry: 502 return dt.statfs(ctx) 503 case *directfsDentry: 504 return dt.statfs() 505 default: 506 panic("unknown dentry implementation") 507 } 508 } 509 510 func (fs *filesystem) restoreRoot(ctx context.Context, opts *vfs.CompleteRestoreOptions) error { 511 rootInode, rootHostFD, err := fs.initClientAndGetRoot(ctx) 512 if err != nil { 513 return err 514 } 515 516 // The root is always non-synthetic. 517 switch dt := fs.root.impl.(type) { 518 case *lisafsDentry: 519 return dt.restoreFile(ctx, &rootInode, opts) 520 case *directfsDentry: 521 dt.controlFDLisa = fs.client.NewFD(rootInode.ControlFD) 522 return dt.restoreFile(ctx, rootHostFD, opts) 523 default: 524 panic("unknown dentry implementation") 525 } 526 } 527 528 // Preconditions: 529 // - !d.isSynthetic(). 530 // - d.parent != nil and has been restored. 531 func (d *dentry) restoreFile(ctx context.Context, opts *vfs.CompleteRestoreOptions) error { 532 switch dt := d.impl.(type) { 533 case *lisafsDentry: 534 inode, err := d.parent.Load().impl.(*lisafsDentry).controlFD.Walk(ctx, d.name) 535 if err != nil { 536 return err 537 } 538 return dt.restoreFile(ctx, &inode, opts) 539 case *directfsDentry: 540 childFD, err := tryOpen(func(flags int) (int, error) { 541 return unix.Openat(d.parent.Load().impl.(*directfsDentry).controlFD, d.name, flags, 0) 542 }) 543 if err != nil { 544 return err 545 } 546 return dt.restoreFile(ctx, childFD, opts) 547 default: 548 panic("unknown dentry implementation") 549 } 550 } 551 552 // doRevalidation calls into r.start's dentry implementation to perform 553 // revalidation on all the dentries contained in r. 554 // 555 // Preconditions: 556 // - fs.renameMu must be locked. 557 // - InteropModeShared is in effect. 558 func (r *revalidateState) doRevalidation(ctx context.Context, vfsObj *vfs.VirtualFilesystem, ds **[]*dentry) error { 559 // Skip synthetic dentries because there is no actual implementation that can 560 // be used to walk the remote filesystem. A start dentry cannot be replaced. 561 if r.start.isSynthetic() { 562 return nil 563 } 564 switch r.start.impl.(type) { 565 case *lisafsDentry: 566 return doRevalidationLisafs(ctx, vfsObj, r, ds) 567 case *directfsDentry: 568 return doRevalidationDirectfs(ctx, vfsObj, r, ds) 569 default: 570 panic("unknown dentry implementation") 571 } 572 }