github.com/MerlinKodo/gvisor@v0.0.0-20231110090155-957f62ecf90e/pkg/p9/handlers.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 p9 16 17 import ( 18 errors2 "errors" 19 "fmt" 20 "io" 21 "os" 22 "path" 23 "strings" 24 25 "golang.org/x/sys/unix" 26 "github.com/MerlinKodo/gvisor/pkg/atomicbitops" 27 "github.com/MerlinKodo/gvisor/pkg/errors" 28 "github.com/MerlinKodo/gvisor/pkg/errors/linuxerr" 29 "github.com/MerlinKodo/gvisor/pkg/fd" 30 "github.com/MerlinKodo/gvisor/pkg/log" 31 ) 32 33 // ExtractErrno extracts a unix.Errno from a error, best effort. 34 func ExtractErrno(err error) unix.Errno { 35 switch err { 36 case os.ErrNotExist: 37 return unix.ENOENT 38 case os.ErrExist: 39 return unix.EEXIST 40 case os.ErrPermission: 41 return unix.EACCES 42 case os.ErrInvalid: 43 return unix.EINVAL 44 } 45 46 // Attempt to unwrap. 47 switch e := err.(type) { 48 case *errors.Error: 49 return linuxerr.ToUnix(e) 50 case unix.Errno: 51 return e 52 case *os.PathError: 53 return ExtractErrno(e.Err) 54 case *os.SyscallError: 55 return ExtractErrno(e.Err) 56 case *os.LinkError: 57 return ExtractErrno(e.Err) 58 } 59 60 // Default case. 61 log.Warningf("unknown error: %v", err) 62 return unix.EIO 63 } 64 65 // newErr returns a new error message from an error. 66 func newErr(err error) *Rlerror { 67 return &Rlerror{Error: uint32(ExtractErrno(err))} 68 } 69 70 // ExtractLinuxerrErrno extracts a *errors.Error from a error, best effort. 71 // TODO(b/34162363): Merge this with ExtractErrno. 72 func ExtractLinuxerrErrno(err error) error { 73 switch err { 74 case os.ErrNotExist: 75 return linuxerr.ENOENT 76 case os.ErrExist: 77 return linuxerr.EEXIST 78 case os.ErrPermission: 79 return linuxerr.EACCES 80 case os.ErrInvalid: 81 return linuxerr.EINVAL 82 } 83 84 // Attempt to unwrap. 85 switch e := err.(type) { 86 case *errors.Error: 87 return linuxerr.ToError(e) 88 case unix.Errno: 89 return linuxerr.ErrorFromUnix(e) 90 case *os.PathError: 91 return ExtractLinuxerrErrno(e.Err) 92 case *os.SyscallError: 93 return ExtractLinuxerrErrno(e.Err) 94 case *os.LinkError: 95 return ExtractLinuxerrErrno(e.Err) 96 } 97 98 // Default case. 99 log.Warningf("unknown error: %v", err) 100 return linuxerr.EIO 101 } 102 103 // newErrFromLinuxerr returns an Rlerror from the linuxerr list. 104 // TODO(b/34162363): Merge this with newErr. 105 func newErrFromLinuxerr(err error) *Rlerror { 106 return &Rlerror{Error: uint32(ExtractErrno(err))} 107 } 108 109 // handler is implemented for server-handled messages. 110 // 111 // See server.go for call information. 112 type handler interface { 113 // Handle handles the given message. 114 // 115 // This may modify the server state. The handle function must return a 116 // message which will be sent back to the client. It may be useful to 117 // use newErr to automatically extract an error message. 118 handle(cs *connState) message 119 } 120 121 // handle implements handler.handle. 122 func (t *Tversion) handle(cs *connState) message { 123 if t.MSize == 0 { 124 return newErr(unix.EINVAL) 125 } 126 if t.MSize > maximumLength { 127 return newErr(unix.EINVAL) 128 } 129 cs.messageSize.Store(t.MSize) 130 requested, ok := parseVersion(t.Version) 131 if !ok { 132 return newErr(unix.EINVAL) 133 } 134 // The server cannot support newer versions that it doesn't know about. In this 135 // case we return EAGAIN to tell the client to try again with a lower version. 136 if requested > highestSupportedVersion { 137 return newErr(unix.EAGAIN) 138 } 139 // From Tversion(9P): "The server may respond with the client’s version 140 // string, or a version string identifying an earlier defined protocol version". 141 cs.version.Store(requested) 142 return &Rversion{ 143 MSize: t.MSize, 144 Version: t.Version, 145 } 146 } 147 148 // handle implements handler.handle. 149 func (t *Tflush) handle(cs *connState) message { 150 cs.WaitTag(t.OldTag) 151 return &Rflush{} 152 } 153 154 // checkSafeName validates the name and returns nil or returns an error. 155 func checkSafeName(name string) error { 156 if name != "" && !strings.Contains(name, "/") && name != "." && name != ".." { 157 return nil 158 } 159 return unix.EINVAL 160 } 161 162 // handle implements handler.handle. 163 func (t *Tclunk) handle(cs *connState) message { 164 if !cs.DeleteFID(t.FID) { 165 return newErr(unix.EBADF) 166 } 167 return &Rclunk{} 168 } 169 170 func (t *Tsetattrclunk) handle(cs *connState) message { 171 ref, ok := cs.LookupFID(t.FID) 172 if !ok { 173 return newErr(unix.EBADF) 174 } 175 defer ref.DecRef() 176 177 setAttrErr := ref.safelyWrite(func() error { 178 // We don't allow setattr on files that have been deleted. 179 // This might be technically incorrect, as it's possible that 180 // there were multiple links and you can still change the 181 // corresponding inode information. 182 if !cs.server.options.SetAttrOnDeleted && ref.isDeleted() { 183 return unix.EINVAL 184 } 185 186 // Set the attributes. 187 return ref.file.SetAttr(t.Valid, t.SetAttr) 188 }) 189 190 // Try to delete FID even in case of failure above. Since the state of the 191 // file is unknown to the caller, it will not attempt to close the file again. 192 if !cs.DeleteFID(t.FID) { 193 return newErr(unix.EBADF) 194 } 195 if setAttrErr != nil { 196 return newErr(setAttrErr) 197 } 198 return &Rsetattrclunk{} 199 } 200 201 // handle implements handler.handle. 202 func (t *Tremove) handle(cs *connState) message { 203 ref, ok := cs.LookupFID(t.FID) 204 if !ok { 205 return newErr(unix.EBADF) 206 } 207 defer ref.DecRef() 208 209 // Frustratingly, because we can't be guaranteed that a rename is not 210 // occurring simultaneously with this removal, we need to acquire the 211 // global rename lock for this kind of remove operation to ensure that 212 // ref.parent does not change out from underneath us. 213 // 214 // This is why Tremove is a bad idea, and clients should generally use 215 // Tunlinkat. All p9 clients will use Tunlinkat. 216 err := ref.safelyGlobal(func() error { 217 // Is this a root? Can't remove that. 218 if ref.isRoot() { 219 return unix.EINVAL 220 } 221 222 // N.B. this remove operation is permitted, even if the file is open. 223 // See also rename below for reasoning. 224 225 // Is this file already deleted? 226 if ref.isDeleted() { 227 return unix.EINVAL 228 } 229 230 // Retrieve the file's proper name. 231 name := ref.parent.pathNode.nameFor(ref) 232 233 // Attempt the removal. 234 if err := ref.parent.file.UnlinkAt(name, 0); err != nil { 235 return err 236 } 237 238 // Mark all relevant fids as deleted. We don't need to lock any 239 // individual nodes because we already hold the global lock. 240 ref.parent.markChildDeleted(name) 241 return nil 242 }) 243 244 // "The remove request asks the file server both to remove the file 245 // represented by fid and to clunk the fid, even if the remove fails." 246 // 247 // "It is correct to consider remove to be a clunk with the side effect 248 // of removing the file if permissions allow." 249 // https://swtch.com/plan9port/man/man9/remove.html 250 if !cs.DeleteFID(t.FID) { 251 return newErr(unix.EBADF) 252 } 253 if err != nil { 254 return newErr(err) 255 } 256 257 return &Rremove{} 258 } 259 260 // handle implements handler.handle. 261 // 262 // We don't support authentication, so this just returns ENOSYS. 263 func (t *Tauth) handle(cs *connState) message { 264 return newErr(unix.ENOSYS) 265 } 266 267 // handle implements handler.handle. 268 func (t *Tattach) handle(cs *connState) message { 269 // Ensure no authentication FID is provided. 270 if t.Auth.AuthenticationFID != NoFID { 271 return newErr(unix.EINVAL) 272 } 273 274 // Must provide an absolute path. 275 if path.IsAbs(t.Auth.AttachName) { 276 // Trim off the leading / if the path is absolute. We always 277 // treat attach paths as absolute and call attach with the root 278 // argument on the server file for clarity. 279 t.Auth.AttachName = t.Auth.AttachName[1:] 280 } 281 282 // Do the attach on the root. 283 sf, err := cs.server.attacher.Attach() 284 if err != nil { 285 return newErr(err) 286 } 287 qid, valid, attr, err := sf.GetAttr(AttrMaskAll()) 288 if err != nil { 289 sf.Close() // Drop file. 290 return newErr(err) 291 } 292 if !valid.Mode { 293 sf.Close() // Drop file. 294 return newErr(unix.EINVAL) 295 } 296 297 // Build a transient reference. 298 root := &fidRef{ 299 server: cs.server, 300 parent: nil, 301 file: sf, 302 refs: atomicbitops.FromInt64(1), 303 mode: attr.Mode.FileType(), 304 pathNode: cs.server.pathTree, 305 } 306 defer root.DecRef() 307 308 // Attach the root? 309 if len(t.Auth.AttachName) == 0 { 310 cs.InsertFID(t.FID, root) 311 return &Rattach{QID: qid} 312 } 313 314 // We want the same traversal checks to apply on attach, so always 315 // attach at the root and use the regular walk paths. 316 names := strings.Split(t.Auth.AttachName, "/") 317 _, newRef, _, _, err := doWalk(cs, root, names, false) 318 if err != nil { 319 return newErr(err) 320 } 321 defer newRef.DecRef() 322 323 // Insert the FID. 324 cs.InsertFID(t.FID, newRef) 325 return &Rattach{QID: qid} 326 } 327 328 // CanOpen returns whether this file open can be opened, read and written to. 329 // 330 // This includes everything except symlinks and sockets. 331 func CanOpen(mode FileMode) bool { 332 return mode.IsRegular() || mode.IsDir() || mode.IsNamedPipe() || mode.IsBlockDevice() || mode.IsCharacterDevice() 333 } 334 335 // handle implements handler.handle. 336 func (t *Tlopen) handle(cs *connState) message { 337 ref, ok := cs.LookupFID(t.FID) 338 if !ok { 339 return newErr(unix.EBADF) 340 } 341 defer ref.DecRef() 342 343 var ( 344 qid QID 345 ioUnit uint32 346 osFile *fd.FD 347 ) 348 if err := ref.safelyRead(func() (err error) { 349 // Has it been deleted already? 350 if ref.isDeleted() { 351 return unix.EINVAL 352 } 353 354 // Has it been opened already? 355 if ref.opened || !CanOpen(ref.mode) { 356 return unix.EINVAL 357 } 358 359 if ref.mode.IsDir() { 360 // Directory must be opened ReadOnly. 361 if t.Flags&OpenFlagsModeMask != ReadOnly { 362 return unix.EISDIR 363 } 364 // Directory not truncatable. 365 if t.Flags&OpenTruncate != 0 { 366 return unix.EISDIR 367 } 368 } 369 370 osFile, qid, ioUnit, err = ref.file.Open(t.Flags) 371 return err 372 }); err != nil { 373 return newErr(err) 374 } 375 376 // Mark file as opened and set open mode. 377 ref.opened = true 378 ref.openFlags = t.Flags 379 380 rlopen := &Rlopen{QID: qid, IoUnit: ioUnit} 381 rlopen.SetFilePayload(osFile) 382 return rlopen 383 } 384 385 func (t *Tlcreate) do(cs *connState, uid UID) (*Rlcreate, error) { 386 if err := checkSafeName(t.Name); err != nil { 387 return nil, err 388 } 389 390 ref, ok := cs.LookupFID(t.FID) 391 if !ok { 392 return nil, unix.EBADF 393 } 394 defer ref.DecRef() 395 396 var ( 397 osFile *fd.FD 398 nsf File 399 qid QID 400 ioUnit uint32 401 newRef *fidRef 402 ) 403 if err := ref.safelyWrite(func() (err error) { 404 // Don't allow creation from non-directories or deleted directories. 405 if ref.isDeleted() || !ref.mode.IsDir() { 406 return unix.EINVAL 407 } 408 409 // Not allowed on open directories. 410 if ref.opened { 411 return unix.EINVAL 412 } 413 414 // Do the create. 415 osFile, nsf, qid, ioUnit, err = ref.file.Create(t.Name, t.OpenFlags, t.Permissions, uid, t.GID) 416 if err != nil { 417 return err 418 } 419 420 newRef = &fidRef{ 421 server: cs.server, 422 parent: ref, 423 file: nsf, 424 opened: true, 425 openFlags: t.OpenFlags, 426 mode: ModeRegular, 427 pathNode: ref.pathNode.pathNodeFor(t.Name), 428 } 429 ref.pathNode.addChild(newRef, t.Name) 430 ref.IncRef() // Acquire parent reference. 431 return nil 432 }); err != nil { 433 return nil, err 434 } 435 436 // Replace the FID reference. 437 cs.InsertFID(t.FID, newRef) 438 439 rlcreate := &Rlcreate{Rlopen: Rlopen{QID: qid, IoUnit: ioUnit}} 440 rlcreate.SetFilePayload(osFile) 441 return rlcreate, nil 442 } 443 444 // handle implements handler.handle. 445 func (t *Tlcreate) handle(cs *connState) message { 446 rlcreate, err := t.do(cs, NoUID) 447 if err != nil { 448 return newErr(err) 449 } 450 return rlcreate 451 } 452 453 // handle implements handler.handle. 454 func (t *Tsymlink) handle(cs *connState) message { 455 rsymlink, err := t.do(cs, NoUID) 456 if err != nil { 457 return newErr(err) 458 } 459 return rsymlink 460 } 461 462 func (t *Tsymlink) do(cs *connState, uid UID) (*Rsymlink, error) { 463 if err := checkSafeName(t.Name); err != nil { 464 return nil, err 465 } 466 467 ref, ok := cs.LookupFID(t.Directory) 468 if !ok { 469 return nil, unix.EBADF 470 } 471 defer ref.DecRef() 472 473 var qid QID 474 if err := ref.safelyWrite(func() (err error) { 475 // Don't allow symlinks from non-directories or deleted directories. 476 if ref.isDeleted() || !ref.mode.IsDir() { 477 return unix.EINVAL 478 } 479 480 // Not allowed on open directories. 481 if ref.opened { 482 return unix.EINVAL 483 } 484 485 // Do the symlink. 486 qid, err = ref.file.Symlink(t.Target, t.Name, uid, t.GID) 487 return err 488 }); err != nil { 489 return nil, err 490 } 491 492 return &Rsymlink{QID: qid}, nil 493 } 494 495 // handle implements handler.handle. 496 func (t *Tlink) handle(cs *connState) message { 497 if err := checkSafeName(t.Name); err != nil { 498 return newErr(err) 499 } 500 501 ref, ok := cs.LookupFID(t.Directory) 502 if !ok { 503 return newErr(unix.EBADF) 504 } 505 defer ref.DecRef() 506 507 refTarget, ok := cs.LookupFID(t.Target) 508 if !ok { 509 return newErr(unix.EBADF) 510 } 511 defer refTarget.DecRef() 512 513 if err := ref.safelyWrite(func() (err error) { 514 // Don't allow create links from non-directories or deleted directories. 515 if ref.isDeleted() || !ref.mode.IsDir() { 516 return unix.EINVAL 517 } 518 519 // Not allowed on open directories. 520 if ref.opened { 521 return unix.EINVAL 522 } 523 524 // Do the link. 525 return ref.file.Link(refTarget.file, t.Name) 526 }); err != nil { 527 return newErr(err) 528 } 529 530 return &Rlink{} 531 } 532 533 // handle implements handler.handle. 534 func (t *Trenameat) handle(cs *connState) message { 535 if err := checkSafeName(t.OldName); err != nil { 536 return newErr(err) 537 } 538 if err := checkSafeName(t.NewName); err != nil { 539 return newErr(err) 540 } 541 542 ref, ok := cs.LookupFID(t.OldDirectory) 543 if !ok { 544 return newErr(unix.EBADF) 545 } 546 defer ref.DecRef() 547 548 refTarget, ok := cs.LookupFID(t.NewDirectory) 549 if !ok { 550 return newErr(unix.EBADF) 551 } 552 defer refTarget.DecRef() 553 554 // Perform the rename holding the global lock. 555 if err := ref.safelyGlobal(func() (err error) { 556 // Don't allow renaming across deleted directories. 557 if ref.isDeleted() || !ref.mode.IsDir() || refTarget.isDeleted() || !refTarget.mode.IsDir() { 558 return unix.EINVAL 559 } 560 561 // Not allowed on open directories. 562 if ref.opened { 563 return unix.EINVAL 564 } 565 566 // Is this the same file? If yes, short-circuit and return success. 567 if ref.pathNode == refTarget.pathNode && t.OldName == t.NewName { 568 return nil 569 } 570 571 // Attempt the actual rename. 572 if err := ref.file.RenameAt(t.OldName, refTarget.file, t.NewName); err != nil { 573 return err 574 } 575 576 // Update the path tree. 577 ref.renameChildTo(t.OldName, refTarget, t.NewName) 578 return nil 579 }); err != nil { 580 return newErr(err) 581 } 582 583 return &Rrenameat{} 584 } 585 586 // handle implements handler.handle. 587 func (t *Tunlinkat) handle(cs *connState) message { 588 if err := checkSafeName(t.Name); err != nil { 589 return newErr(err) 590 } 591 592 ref, ok := cs.LookupFID(t.Directory) 593 if !ok { 594 return newErr(unix.EBADF) 595 } 596 defer ref.DecRef() 597 598 if err := ref.safelyWrite(func() (err error) { 599 // Don't allow deletion from non-directories or deleted directories. 600 if ref.isDeleted() || !ref.mode.IsDir() { 601 return unix.EINVAL 602 } 603 604 // Not allowed on open directories. 605 if ref.opened { 606 return unix.EINVAL 607 } 608 609 // Before we do the unlink itself, we need to ensure that there 610 // are no operations in flight on associated path node. The 611 // child's path node lock must be held to ensure that the 612 // unlinkat marking the child deleted below is atomic with 613 // respect to any other read or write operations. 614 // 615 // This is one case where we have a lock ordering issue, but 616 // since we always acquire deeper in the hierarchy, we know 617 // that we are free of lock cycles. 618 childPathNode := ref.pathNode.pathNodeFor(t.Name) 619 childPathNode.opMu.Lock() 620 defer childPathNode.opMu.Unlock() 621 622 // Do the unlink. 623 err = ref.file.UnlinkAt(t.Name, t.Flags) 624 if err != nil { 625 return err 626 } 627 628 // Mark the path as deleted. 629 ref.markChildDeleted(t.Name) 630 return nil 631 }); err != nil { 632 return newErr(err) 633 } 634 635 return &Runlinkat{} 636 } 637 638 // handle implements handler.handle. 639 func (t *Trename) handle(cs *connState) message { 640 if err := checkSafeName(t.Name); err != nil { 641 return newErr(err) 642 } 643 644 ref, ok := cs.LookupFID(t.FID) 645 if !ok { 646 return newErr(unix.EBADF) 647 } 648 defer ref.DecRef() 649 650 refTarget, ok := cs.LookupFID(t.Directory) 651 if !ok { 652 return newErr(unix.EBADF) 653 } 654 defer refTarget.DecRef() 655 656 if err := ref.safelyGlobal(func() (err error) { 657 // Don't allow a root rename. 658 if ref.isRoot() { 659 return unix.EINVAL 660 } 661 662 // Don't allow renaming deleting entries, or target non-directories. 663 if ref.isDeleted() || refTarget.isDeleted() || !refTarget.mode.IsDir() { 664 return unix.EINVAL 665 } 666 667 // If the parent is deleted, but we not, something is seriously wrong. 668 // It's fail to die at this point with an assertion failure. 669 if ref.parent.isDeleted() { 670 panic(fmt.Sprintf("parent %+v deleted, child %+v is not", ref.parent, ref)) 671 } 672 673 // N.B. The rename operation is allowed to proceed on open files. It 674 // does impact the state of its parent, but this is merely a sanity 675 // check in any case, and the operation is safe. There may be other 676 // files corresponding to the same path that are renamed anyways. 677 678 // Check for the exact same file and short-circuit. 679 oldName := ref.parent.pathNode.nameFor(ref) 680 if ref.parent.pathNode == refTarget.pathNode && oldName == t.Name { 681 return nil 682 } 683 684 // Call the rename method on the parent. 685 if err := ref.parent.file.RenameAt(oldName, refTarget.file, t.Name); err != nil { 686 return err 687 } 688 689 // Update the path tree. 690 ref.parent.renameChildTo(oldName, refTarget, t.Name) 691 return nil 692 }); err != nil { 693 return newErr(err) 694 } 695 696 return &Rrename{} 697 } 698 699 // handle implements handler.handle. 700 func (t *Treadlink) handle(cs *connState) message { 701 ref, ok := cs.LookupFID(t.FID) 702 if !ok { 703 return newErr(unix.EBADF) 704 } 705 defer ref.DecRef() 706 707 var target string 708 if err := ref.safelyRead(func() (err error) { 709 // Don't allow readlink on deleted files. There is no need to 710 // check if this file is opened because symlinks cannot be 711 // opened. 712 if ref.isDeleted() || !ref.mode.IsSymlink() { 713 return unix.EINVAL 714 } 715 716 // Do the read. 717 target, err = ref.file.Readlink() 718 return err 719 }); err != nil { 720 return newErr(err) 721 } 722 723 return &Rreadlink{target} 724 } 725 726 // handle implements handler.handle. 727 func (t *Tread) handle(cs *connState) message { 728 ref, ok := cs.LookupFID(t.FID) 729 if !ok { 730 return newErr(unix.EBADF) 731 } 732 defer ref.DecRef() 733 734 // Constrain the size of the read buffer. 735 if int(t.Count) > int(maximumLength) { 736 return newErr(unix.ENOBUFS) 737 } 738 739 var ( 740 data = make([]byte, t.Count) 741 n int 742 ) 743 if err := ref.safelyRead(func() (err error) { 744 // Has it been opened already? 745 if !ref.opened { 746 return unix.EINVAL 747 } 748 749 // Can it be read? Check permissions. 750 if ref.openFlags&OpenFlagsModeMask == WriteOnly { 751 return unix.EPERM 752 } 753 754 n, err = ref.file.ReadAt(data, t.Offset) 755 return err 756 }); err != nil && err != io.EOF { 757 return newErr(err) 758 } 759 760 return &Rread{Data: data[:n]} 761 } 762 763 // handle implements handler.handle. 764 func (t *Twrite) handle(cs *connState) message { 765 ref, ok := cs.LookupFID(t.FID) 766 if !ok { 767 return newErr(unix.EBADF) 768 } 769 defer ref.DecRef() 770 771 var n int 772 if err := ref.safelyRead(func() (err error) { 773 // Has it been opened already? 774 if !ref.opened { 775 return unix.EINVAL 776 } 777 778 // Can it be written? Check permissions. 779 if ref.openFlags&OpenFlagsModeMask == ReadOnly { 780 return unix.EPERM 781 } 782 783 n, err = ref.file.WriteAt(t.Data, t.Offset) 784 return err 785 }); err != nil { 786 return newErr(err) 787 } 788 789 return &Rwrite{Count: uint32(n)} 790 } 791 792 // handle implements handler.handle. 793 func (t *Tmknod) handle(cs *connState) message { 794 rmknod, err := t.do(cs, NoUID) 795 if err != nil { 796 return newErr(err) 797 } 798 return rmknod 799 } 800 801 func (t *Tmknod) do(cs *connState, uid UID) (*Rmknod, error) { 802 if err := checkSafeName(t.Name); err != nil { 803 return nil, err 804 } 805 806 ref, ok := cs.LookupFID(t.Directory) 807 if !ok { 808 return nil, unix.EBADF 809 } 810 defer ref.DecRef() 811 812 var qid QID 813 if err := ref.safelyWrite(func() (err error) { 814 // Don't allow mknod on deleted files. 815 if ref.isDeleted() || !ref.mode.IsDir() { 816 return unix.EINVAL 817 } 818 819 // Not allowed on open directories. 820 if ref.opened { 821 return unix.EINVAL 822 } 823 824 // Do the mknod. 825 qid, err = ref.file.Mknod(t.Name, t.Mode, t.Major, t.Minor, uid, t.GID) 826 return err 827 }); err != nil { 828 return nil, err 829 } 830 831 return &Rmknod{QID: qid}, nil 832 } 833 834 // handle implements handler.handle. 835 func (t *Tmkdir) handle(cs *connState) message { 836 rmkdir, err := t.do(cs, NoUID) 837 if err != nil { 838 return newErr(err) 839 } 840 return rmkdir 841 } 842 843 func (t *Tmkdir) do(cs *connState, uid UID) (*Rmkdir, error) { 844 if err := checkSafeName(t.Name); err != nil { 845 return nil, err 846 } 847 848 ref, ok := cs.LookupFID(t.Directory) 849 if !ok { 850 return nil, unix.EBADF 851 } 852 defer ref.DecRef() 853 854 var qid QID 855 if err := ref.safelyWrite(func() (err error) { 856 // Don't allow mkdir on deleted files. 857 if ref.isDeleted() || !ref.mode.IsDir() { 858 return unix.EINVAL 859 } 860 861 // Not allowed on open directories. 862 if ref.opened { 863 return unix.EINVAL 864 } 865 866 // Do the mkdir. 867 qid, err = ref.file.Mkdir(t.Name, t.Permissions, uid, t.GID) 868 return err 869 }); err != nil { 870 return nil, err 871 } 872 873 return &Rmkdir{QID: qid}, nil 874 } 875 876 // handle implements handler.handle. 877 func (t *Tgetattr) handle(cs *connState) message { 878 ref, ok := cs.LookupFID(t.FID) 879 if !ok { 880 return newErr(unix.EBADF) 881 } 882 defer ref.DecRef() 883 884 // We allow getattr on deleted files. Depending on the backing 885 // implementation, it's possible that races exist that might allow 886 // fetching attributes of other files. But we need to generally allow 887 // refreshing attributes and this is a minor leak, if at all. 888 889 var ( 890 qid QID 891 valid AttrMask 892 attr Attr 893 ) 894 if err := ref.safelyRead(func() (err error) { 895 qid, valid, attr, err = ref.file.GetAttr(t.AttrMask) 896 return err 897 }); err != nil { 898 return newErr(err) 899 } 900 901 return &Rgetattr{QID: qid, Valid: valid, Attr: attr} 902 } 903 904 // handle implements handler.handle. 905 func (t *Tsetattr) handle(cs *connState) message { 906 ref, ok := cs.LookupFID(t.FID) 907 if !ok { 908 return newErr(unix.EBADF) 909 } 910 defer ref.DecRef() 911 912 if err := ref.safelyWrite(func() error { 913 // We don't allow setattr on files that have been deleted. 914 // This might be technically incorrect, as it's possible that 915 // there were multiple links and you can still change the 916 // corresponding inode information. 917 if !cs.server.options.SetAttrOnDeleted && ref.isDeleted() { 918 return unix.EINVAL 919 } 920 921 // Set the attributes. 922 return ref.file.SetAttr(t.Valid, t.SetAttr) 923 }); err != nil { 924 return newErr(err) 925 } 926 927 return &Rsetattr{} 928 } 929 930 // handle implements handler.handle. 931 func (t *Tallocate) handle(cs *connState) message { 932 ref, ok := cs.LookupFID(t.FID) 933 if !ok { 934 return newErr(unix.EBADF) 935 } 936 defer ref.DecRef() 937 938 if err := ref.safelyWrite(func() error { 939 // Has it been opened already? 940 if !ref.opened { 941 return unix.EINVAL 942 } 943 944 // Can it be written? Check permissions. 945 if ref.openFlags&OpenFlagsModeMask == ReadOnly { 946 return unix.EBADF 947 } 948 949 // We don't allow allocate on files that have been deleted. 950 if !cs.server.options.AllocateOnDeleted && ref.isDeleted() { 951 return unix.EINVAL 952 } 953 954 return ref.file.Allocate(t.Mode, t.Offset, t.Length) 955 }); err != nil { 956 return newErr(err) 957 } 958 959 return &Rallocate{} 960 } 961 962 // handle implements handler.handle. 963 func (t *Txattrwalk) handle(cs *connState) message { 964 ref, ok := cs.LookupFID(t.FID) 965 if !ok { 966 return newErr(unix.EBADF) 967 } 968 defer ref.DecRef() 969 970 // We don't support extended attributes. 971 return newErr(unix.ENODATA) 972 } 973 974 // handle implements handler.handle. 975 func (t *Txattrcreate) handle(cs *connState) message { 976 ref, ok := cs.LookupFID(t.FID) 977 if !ok { 978 return newErr(unix.EBADF) 979 } 980 defer ref.DecRef() 981 982 // We don't support extended attributes. 983 return newErr(unix.ENOSYS) 984 } 985 986 // handle implements handler.handle. 987 func (t *Tgetxattr) handle(cs *connState) message { 988 ref, ok := cs.LookupFID(t.FID) 989 if !ok { 990 return newErr(unix.EBADF) 991 } 992 defer ref.DecRef() 993 994 var val string 995 if err := ref.safelyRead(func() (err error) { 996 // Don't allow getxattr on files that have been deleted. 997 if ref.isDeleted() { 998 return unix.EINVAL 999 } 1000 val, err = ref.file.GetXattr(t.Name, t.Size) 1001 return err 1002 }); err != nil { 1003 return newErr(err) 1004 } 1005 return &Rgetxattr{Value: val} 1006 } 1007 1008 // handle implements handler.handle. 1009 func (t *Tsetxattr) handle(cs *connState) message { 1010 ref, ok := cs.LookupFID(t.FID) 1011 if !ok { 1012 return newErr(unix.EBADF) 1013 } 1014 defer ref.DecRef() 1015 1016 if err := ref.safelyWrite(func() error { 1017 // Don't allow setxattr on files that have been deleted. 1018 if ref.isDeleted() { 1019 return unix.EINVAL 1020 } 1021 return ref.file.SetXattr(t.Name, t.Value, t.Flags) 1022 }); err != nil { 1023 return newErr(err) 1024 } 1025 return &Rsetxattr{} 1026 } 1027 1028 // handle implements handler.handle. 1029 func (t *Tlistxattr) handle(cs *connState) message { 1030 ref, ok := cs.LookupFID(t.FID) 1031 if !ok { 1032 return newErr(unix.EBADF) 1033 } 1034 defer ref.DecRef() 1035 1036 var xattrs map[string]struct{} 1037 if err := ref.safelyRead(func() (err error) { 1038 // Don't allow listxattr on files that have been deleted. 1039 if ref.isDeleted() { 1040 return unix.EINVAL 1041 } 1042 xattrs, err = ref.file.ListXattr(t.Size) 1043 return err 1044 }); err != nil { 1045 return newErr(err) 1046 } 1047 1048 xattrList := make([]string, 0, len(xattrs)) 1049 for x := range xattrs { 1050 xattrList = append(xattrList, x) 1051 } 1052 return &Rlistxattr{Xattrs: xattrList} 1053 } 1054 1055 // handle implements handler.handle. 1056 func (t *Tremovexattr) handle(cs *connState) message { 1057 ref, ok := cs.LookupFID(t.FID) 1058 if !ok { 1059 return newErr(unix.EBADF) 1060 } 1061 defer ref.DecRef() 1062 1063 if err := ref.safelyWrite(func() error { 1064 // Don't allow removexattr on files that have been deleted. 1065 if ref.isDeleted() { 1066 return unix.EINVAL 1067 } 1068 return ref.file.RemoveXattr(t.Name) 1069 }); err != nil { 1070 return newErr(err) 1071 } 1072 return &Rremovexattr{} 1073 } 1074 1075 // handle implements handler.handle. 1076 func (t *Treaddir) handle(cs *connState) message { 1077 ref, ok := cs.LookupFID(t.Directory) 1078 if !ok { 1079 return newErr(unix.EBADF) 1080 } 1081 defer ref.DecRef() 1082 1083 var entries []Dirent 1084 if err := ref.safelyRead(func() (err error) { 1085 // Don't allow reading deleted directories. 1086 if ref.isDeleted() || !ref.mode.IsDir() { 1087 return unix.EINVAL 1088 } 1089 1090 // Has it been opened yet? 1091 if !ref.opened { 1092 return unix.EINVAL 1093 } 1094 1095 // Read the entries. 1096 entries, err = ref.file.Readdir(t.DirentOffset, t.Count) 1097 if err != nil && err != io.EOF { 1098 return err 1099 } 1100 return nil 1101 }); err != nil { 1102 return newErr(err) 1103 } 1104 1105 return &Rreaddir{Count: t.Count, Entries: entries} 1106 } 1107 1108 // handle implements handler.handle. 1109 func (t *Tfsync) handle(cs *connState) message { 1110 ref, ok := cs.LookupFID(t.FID) 1111 if !ok { 1112 return newErr(unix.EBADF) 1113 } 1114 defer ref.DecRef() 1115 1116 if err := ref.safelyRead(func() (err error) { 1117 // Has it been opened yet? 1118 if !ref.opened { 1119 return unix.EINVAL 1120 } 1121 1122 // Perform the sync. 1123 return ref.file.FSync() 1124 }); err != nil { 1125 return newErr(err) 1126 } 1127 1128 return &Rfsync{} 1129 } 1130 1131 // handle implements handler.handle. 1132 func (t *Tstatfs) handle(cs *connState) message { 1133 ref, ok := cs.LookupFID(t.FID) 1134 if !ok { 1135 return newErr(unix.EBADF) 1136 } 1137 defer ref.DecRef() 1138 1139 st, err := ref.file.StatFS() 1140 if err != nil { 1141 return newErr(err) 1142 } 1143 1144 return &Rstatfs{st} 1145 } 1146 1147 // handle implements handler.handle. 1148 func (t *Tflushf) handle(cs *connState) message { 1149 ref, ok := cs.LookupFID(t.FID) 1150 if !ok { 1151 return newErr(unix.EBADF) 1152 } 1153 defer ref.DecRef() 1154 1155 if err := ref.safelyRead(ref.file.Flush); err != nil { 1156 return newErr(err) 1157 } 1158 1159 return &Rflushf{} 1160 } 1161 1162 // walkOne walks zero or one path elements. 1163 // 1164 // The slice passed as qids is append and returned. 1165 func walkOne(qids []QID, from File, names []string, getattr bool) ([]QID, File, AttrMask, Attr, error) { 1166 if len(names) > 1 { 1167 // We require exactly zero or one elements. 1168 return nil, nil, AttrMask{}, Attr{}, unix.EINVAL 1169 } 1170 var ( 1171 localQIDs []QID 1172 sf File 1173 valid AttrMask 1174 attr Attr 1175 err error 1176 ) 1177 switch { 1178 case getattr: 1179 localQIDs, sf, valid, attr, err = from.WalkGetAttr(names) 1180 // Can't put fallthrough in the if because Go. 1181 if err != unix.ENOSYS { 1182 break 1183 } 1184 fallthrough 1185 default: 1186 localQIDs, sf, err = from.Walk(names) 1187 if err != nil { 1188 // No way to walk this element. 1189 break 1190 } 1191 if getattr { 1192 _, valid, attr, err = sf.GetAttr(AttrMaskAll()) 1193 if err != nil { 1194 // Don't leak the file. 1195 sf.Close() 1196 } 1197 } 1198 } 1199 if err != nil { 1200 // Error walking, don't return anything. 1201 return nil, nil, AttrMask{}, Attr{}, err 1202 } 1203 if len(localQIDs) != 1 { 1204 // Expected a single QID. 1205 sf.Close() 1206 return nil, nil, AttrMask{}, Attr{}, unix.EINVAL 1207 } 1208 return append(qids, localQIDs...), sf, valid, attr, nil 1209 } 1210 1211 // doWalk walks from a given fidRef. 1212 // 1213 // This enforces that all intermediate nodes are walkable (directories). The 1214 // fidRef returned (newRef) has a reference associated with it that is now 1215 // owned by the caller and must be handled appropriately. 1216 func doWalk(cs *connState, ref *fidRef, names []string, getattr bool) (qids []QID, newRef *fidRef, valid AttrMask, attr Attr, err error) { 1217 // Check the names. 1218 for _, name := range names { 1219 err = checkSafeName(name) 1220 if err != nil { 1221 return 1222 } 1223 } 1224 1225 // Has it been opened already? 1226 err = ref.safelyRead(func() (err error) { 1227 if ref.opened { 1228 return unix.EBUSY 1229 } 1230 return nil 1231 }) 1232 if err != nil { 1233 return 1234 } 1235 1236 // Is this an empty list? Handle specially. We don't actually need to 1237 // validate anything since this is always permitted. 1238 if len(names) == 0 { 1239 var sf File // Temporary. 1240 if err := ref.maybeParent().safelyRead(func() (err error) { 1241 // Clone the single element. 1242 qids, sf, valid, attr, err = walkOne(nil, ref.file, nil, getattr) 1243 if err != nil { 1244 return err 1245 } 1246 1247 newRef = &fidRef{ 1248 server: cs.server, 1249 parent: ref.parent, 1250 file: sf, 1251 mode: ref.mode, 1252 pathNode: ref.pathNode, 1253 } 1254 if !ref.isRoot() { 1255 if !newRef.isDeleted() { 1256 // Add only if a non-root node; the same node. 1257 ref.parent.pathNode.addChild(newRef, ref.parent.pathNode.nameFor(ref)) 1258 } 1259 ref.parent.IncRef() // Acquire parent reference. 1260 } 1261 // doWalk returns a reference. 1262 newRef.IncRef() 1263 return nil 1264 }); err != nil { 1265 return nil, nil, AttrMask{}, Attr{}, err 1266 } 1267 // Do not return the new QID. 1268 return nil, newRef, valid, attr, nil 1269 } 1270 1271 // Do the walk, one element at a time. 1272 walkRef := ref 1273 walkRef.IncRef() 1274 for i := 0; i < len(names); i++ { 1275 // We won't allow beyond past symlinks; stop here if this isn't 1276 // a proper directory and we have additional paths to walk. 1277 if !walkRef.mode.IsDir() { 1278 walkRef.DecRef() // Drop walk reference; no lock required. 1279 return nil, nil, AttrMask{}, Attr{}, unix.EINVAL 1280 } 1281 1282 var sf File // Temporary. 1283 if err := walkRef.safelyRead(func() (err error) { 1284 // It is not safe to walk on a deleted directory. It could have been 1285 // replaced with a malicious symlink. 1286 if walkRef.isDeleted() { 1287 // Fail this operation as the result will not be meaningful if walkRef 1288 // is deleted. 1289 return unix.ENOENT 1290 } 1291 // Pass getattr = true to walkOne since we need the file type for 1292 // newRef. 1293 qids, sf, valid, attr, err = walkOne(qids, walkRef.file, names[i:i+1], true) 1294 if err != nil { 1295 return err 1296 } 1297 1298 // Note that we don't need to acquire a lock on any of 1299 // these individual instances. That's because they are 1300 // not actually addressable via a FID. They are 1301 // anonymous. They exist in the tree for tracking 1302 // purposes. 1303 newRef := &fidRef{ 1304 server: cs.server, 1305 parent: walkRef, 1306 file: sf, 1307 mode: attr.Mode.FileType(), 1308 pathNode: walkRef.pathNode.pathNodeFor(names[i]), 1309 } 1310 walkRef.pathNode.addChild(newRef, names[i]) 1311 // We allow our walk reference to become the new parent 1312 // reference here and so we don't IncRef. Instead, just 1313 // set walkRef to the newRef above and acquire a new 1314 // walk reference. 1315 walkRef = newRef 1316 walkRef.IncRef() 1317 return nil 1318 }); err != nil { 1319 walkRef.DecRef() // Drop the old walkRef. 1320 return nil, nil, AttrMask{}, Attr{}, err 1321 } 1322 } 1323 1324 // Success. 1325 return qids, walkRef, valid, attr, nil 1326 } 1327 1328 // handle implements handler.handle. 1329 func (t *Twalk) handle(cs *connState) message { 1330 ref, ok := cs.LookupFID(t.FID) 1331 if !ok { 1332 return newErr(unix.EBADF) 1333 } 1334 defer ref.DecRef() 1335 1336 // Do the walk. 1337 qids, newRef, _, _, err := doWalk(cs, ref, t.Names, false) 1338 if err != nil { 1339 return newErr(err) 1340 } 1341 defer newRef.DecRef() 1342 1343 // Install the new FID. 1344 cs.InsertFID(t.NewFID, newRef) 1345 return &Rwalk{QIDs: qids} 1346 } 1347 1348 // handle implements handler.handle. 1349 func (t *Twalkgetattr) handle(cs *connState) message { 1350 ref, ok := cs.LookupFID(t.FID) 1351 if !ok { 1352 return newErr(unix.EBADF) 1353 } 1354 defer ref.DecRef() 1355 1356 // Do the walk. 1357 qids, newRef, valid, attr, err := doWalk(cs, ref, t.Names, true) 1358 if err != nil { 1359 return newErr(err) 1360 } 1361 defer newRef.DecRef() 1362 1363 // Install the new FID. 1364 cs.InsertFID(t.NewFID, newRef) 1365 return &Rwalkgetattr{QIDs: qids, Valid: valid, Attr: attr} 1366 } 1367 1368 // handle implements handler.handle. 1369 func (t *Tucreate) handle(cs *connState) message { 1370 rlcreate, err := t.Tlcreate.do(cs, t.UID) 1371 if err != nil { 1372 return newErr(err) 1373 } 1374 return &Rucreate{*rlcreate} 1375 } 1376 1377 // handle implements handler.handle. 1378 func (t *Tumkdir) handle(cs *connState) message { 1379 rmkdir, err := t.Tmkdir.do(cs, t.UID) 1380 if err != nil { 1381 return newErr(err) 1382 } 1383 return &Rumkdir{*rmkdir} 1384 } 1385 1386 // handle implements handler.handle. 1387 func (t *Tusymlink) handle(cs *connState) message { 1388 rsymlink, err := t.Tsymlink.do(cs, t.UID) 1389 if err != nil { 1390 return newErr(err) 1391 } 1392 return &Rusymlink{*rsymlink} 1393 } 1394 1395 // handle implements handler.handle. 1396 func (t *Tumknod) handle(cs *connState) message { 1397 rmknod, err := t.Tmknod.do(cs, t.UID) 1398 if err != nil { 1399 return newErr(err) 1400 } 1401 return &Rumknod{*rmknod} 1402 } 1403 1404 // handle implements handler.handle. 1405 func (t *Tbind) handle(cs *connState) message { 1406 if err := checkSafeName(t.SockName); err != nil { 1407 return newErr(err) 1408 } 1409 1410 ref, ok := cs.LookupFID(t.Directory) 1411 if !ok { 1412 return newErr(unix.EBADF) 1413 } 1414 defer ref.DecRef() 1415 1416 var ( 1417 sockRef *fidRef 1418 qid QID 1419 valid AttrMask 1420 attr Attr 1421 ) 1422 if err := ref.safelyWrite(func() (err error) { 1423 // Don't allow creation from non-directories or deleted directories. 1424 if ref.isDeleted() || !ref.mode.IsDir() { 1425 return unix.EINVAL 1426 } 1427 1428 // Not allowed on open directories. 1429 if ref.opened { 1430 return unix.EINVAL 1431 } 1432 1433 var sockF File 1434 sockF, qid, valid, attr, err = ref.file.Bind(t.SockType, t.SockName, t.UID, t.GID) 1435 if err != nil { 1436 return err 1437 } 1438 1439 sockRef = &fidRef{ 1440 server: cs.server, 1441 parent: ref, 1442 file: sockF, 1443 mode: ModeSocket, 1444 pathNode: ref.pathNode.pathNodeFor(t.SockName), 1445 } 1446 ref.pathNode.addChild(sockRef, t.SockName) 1447 ref.IncRef() // Acquire parent reference. 1448 return nil 1449 }); err != nil { 1450 return newErr(err) 1451 } 1452 cs.InsertFID(t.NewFID, sockRef) 1453 return &Rbind{QID: qid, Valid: valid, Attr: attr} 1454 } 1455 1456 // handle implements handler.handle. 1457 func (t *Tlconnect) handle(cs *connState) message { 1458 ref, ok := cs.LookupFID(t.FID) 1459 if !ok { 1460 return newErr(unix.EBADF) 1461 } 1462 defer ref.DecRef() 1463 1464 var osFile *fd.FD 1465 if err := ref.safelyRead(func() (err error) { 1466 // Don't allow connecting to deleted files. 1467 if ref.isDeleted() || !ref.mode.IsSocket() { 1468 return unix.EINVAL 1469 } 1470 1471 // Do the connect. 1472 osFile, err = ref.file.Connect(t.SocketType) 1473 return err 1474 }); err != nil { 1475 return newErr(err) 1476 } 1477 1478 rlconnect := &Rlconnect{} 1479 rlconnect.SetFilePayload(osFile) 1480 return rlconnect 1481 } 1482 1483 // handle implements handler.handle. 1484 func (t *Tchannel) handle(cs *connState) message { 1485 // Ensure that channels are enabled. 1486 if err := cs.initializeChannels(); err != nil { 1487 return newErr(err) 1488 } 1489 1490 ch := cs.lookupChannel(t.ID) 1491 if ch == nil { 1492 return newErr(unix.ENOSYS) 1493 } 1494 1495 // Return the payload. Note that we need to duplicate the file 1496 // descriptor for the channel allocator, because sending is a 1497 // destructive operation between sendRecvLegacy (and now the newer 1498 // channel send operations). Same goes for the client FD. 1499 rchannel := &Rchannel{ 1500 Offset: uint64(ch.desc.Offset), 1501 Length: uint64(ch.desc.Length), 1502 } 1503 switch t.Control { 1504 case 0: 1505 // Open the main data channel. 1506 mfd, err := unix.Dup(int(cs.channelAlloc.FD())) 1507 if err != nil { 1508 return newErr(err) 1509 } 1510 rchannel.SetFilePayload(fd.New(mfd)) 1511 case 1: 1512 cfd, err := unix.Dup(ch.client.FD()) 1513 if err != nil { 1514 return newErr(err) 1515 } 1516 rchannel.SetFilePayload(fd.New(cfd)) 1517 default: 1518 return newErr(unix.EINVAL) 1519 } 1520 return rchannel 1521 } 1522 1523 // handle implements handler.handle. 1524 func (t *Tmultigetattr) handle(cs *connState) message { 1525 for i, name := range t.Names { 1526 if len(name) == 0 && i == 0 { 1527 // Empty name is allowed on the first entry to indicate that the current 1528 // FID needs to be included in the result. 1529 continue 1530 } 1531 if err := checkSafeName(name); err != nil { 1532 return newErr(err) 1533 } 1534 } 1535 ref, ok := cs.LookupFID(t.FID) 1536 if !ok { 1537 return newErr(unix.EBADF) 1538 } 1539 defer ref.DecRef() 1540 1541 if cs.server.options.MultiGetAttrSupported { 1542 var stats []FullStat 1543 if err := ref.safelyRead(func() (err error) { 1544 stats, err = ref.file.MultiGetAttr(t.Names) 1545 return err 1546 }); err != nil { 1547 return newErr(err) 1548 } 1549 return &Rmultigetattr{Stats: stats} 1550 } 1551 1552 stats := make([]FullStat, 0, len(t.Names)) 1553 mask := AttrMaskAll() 1554 start := ref.file 1555 startNode := ref.pathNode 1556 parent := start 1557 parentNode := startNode 1558 closeParent := func() { 1559 if parent != start { 1560 _ = parent.Close() 1561 } 1562 } 1563 defer closeParent() 1564 1565 cs.server.renameMu.RLock() 1566 defer cs.server.renameMu.RUnlock() 1567 1568 for i, name := range t.Names { 1569 if len(name) == 0 && i == 0 { 1570 startNode.opMu.RLock() 1571 qid, valid, attr, err := start.GetAttr(mask) 1572 startNode.opMu.RUnlock() 1573 if err != nil { 1574 return newErr(err) 1575 } 1576 stats = append(stats, FullStat{ 1577 QID: qid, 1578 Valid: valid, 1579 Attr: attr, 1580 }) 1581 continue 1582 } 1583 1584 parentNode.opMu.RLock() 1585 if parentNode.deleted.Load() != 0 { 1586 parentNode.opMu.RUnlock() 1587 break 1588 } 1589 qids, child, valid, attr, err := parent.WalkGetAttr([]string{name}) 1590 if err != nil { 1591 parentNode.opMu.RUnlock() 1592 if errors2.Is(err, unix.ENOENT) { 1593 break 1594 } 1595 return newErr(err) 1596 } 1597 stats = append(stats, FullStat{ 1598 QID: qids[0], 1599 Valid: valid, 1600 Attr: attr, 1601 }) 1602 // Update with next generation. 1603 closeParent() 1604 parent = child 1605 childNode := parentNode.pathNodeFor(name) 1606 parentNode.opMu.RUnlock() 1607 parentNode = childNode 1608 if attr.Mode.FileType() != ModeDirectory { 1609 // Doesn't need to continue if entry is not a dir. Including symlinks 1610 // that cannot be followed. 1611 break 1612 } 1613 } 1614 1615 return &Rmultigetattr{Stats: stats} 1616 }