1 /* 2 * Copyright 2003-2013, Axel Dörfler, axeld@pinc-software.de. 3 * Distributed under the terms of the MIT License. 4 */ 5 6 7 #include <boot/vfs.h> 8 9 #include <errno.h> 10 #include <fcntl.h> 11 #include <string.h> 12 #include <sys/uio.h> 13 #include <unistd.h> 14 15 #include <StorageDefs.h> 16 17 #include <boot/platform.h> 18 #include <boot/partitions.h> 19 #include <boot/stdio.h> 20 #include <boot/stage2.h> 21 #include <syscall_utils.h> 22 23 #include "RootFileSystem.h" 24 #include "file_systems/packagefs/packagefs.h" 25 26 27 using namespace boot; 28 29 //#define TRACE_VFS 30 #ifdef TRACE_VFS 31 # define TRACE(x) dprintf x 32 #else 33 # define TRACE(x) ; 34 #endif 35 36 37 class Descriptor { 38 public: 39 Descriptor(Node *node, void *cookie); 40 ~Descriptor(); 41 42 ssize_t ReadAt(off_t pos, void *buffer, size_t bufferSize); 43 ssize_t Read(void *buffer, size_t bufferSize); 44 ssize_t WriteAt(off_t pos, const void *buffer, size_t bufferSize); 45 ssize_t Write(const void *buffer, size_t bufferSize); 46 47 status_t Stat(struct stat &stat); 48 49 off_t Offset() const { return fOffset; } 50 int32 RefCount() const { return fRefCount; } 51 52 status_t Acquire(); 53 status_t Release(); 54 55 Node *GetNode() const { return fNode; } 56 57 private: 58 Node *fNode; 59 void *fCookie; 60 off_t fOffset; 61 int32 fRefCount; 62 }; 63 64 #define MAX_VFS_DESCRIPTORS 64 65 66 NodeList gBootDevices; 67 NodeList gPartitions; 68 RootFileSystem *gRoot; 69 static Descriptor *sDescriptors[MAX_VFS_DESCRIPTORS]; 70 static Node *sBootDevice; 71 72 73 Node::Node() 74 : 75 fRefCount(1) 76 { 77 } 78 79 80 Node::~Node() 81 { 82 } 83 84 85 status_t 86 Node::Open(void **_cookie, int mode) 87 { 88 TRACE(("%p::Open()\n", this)); 89 return Acquire(); 90 } 91 92 93 status_t 94 Node::Close(void *cookie) 95 { 96 TRACE(("%p::Close()\n", this)); 97 return Release(); 98 } 99 100 101 status_t 102 Node::ReadLink(char* buffer, size_t bufferSize) 103 { 104 return B_BAD_VALUE; 105 } 106 107 108 status_t 109 Node::GetName(char *nameBuffer, size_t bufferSize) const 110 { 111 return B_ERROR; 112 } 113 114 115 status_t 116 Node::GetFileMap(struct file_map_run *runs, int32 *count) 117 { 118 return B_ERROR; 119 } 120 121 122 int32 123 Node::Type() const 124 { 125 return 0; 126 } 127 128 129 off_t 130 Node::Size() const 131 { 132 return 0LL; 133 } 134 135 136 ino_t 137 Node::Inode() const 138 { 139 return 0; 140 } 141 142 143 status_t 144 Node::Acquire() 145 { 146 fRefCount++; 147 TRACE(("%p::Acquire(), fRefCount = %ld\n", this, fRefCount)); 148 return B_OK; 149 } 150 151 152 status_t 153 Node::Release() 154 { 155 TRACE(("%p::Release(), fRefCount = %ld\n", this, fRefCount)); 156 if (--fRefCount == 0) { 157 TRACE(("delete node: %p\n", this)); 158 delete this; 159 return 1; 160 } 161 162 return B_OK; 163 } 164 165 166 // #pragma mark - 167 168 169 ConsoleNode::ConsoleNode() 170 : Node() 171 { 172 } 173 174 175 ssize_t 176 ConsoleNode::Read(void *buffer, size_t bufferSize) 177 { 178 return ReadAt(NULL, -1, buffer, bufferSize); 179 } 180 181 182 ssize_t 183 ConsoleNode::Write(const void *buffer, size_t bufferSize) 184 { 185 return WriteAt(NULL, -1, buffer, bufferSize); 186 } 187 188 189 // #pragma mark - 190 191 192 Directory::Directory() 193 : Node() 194 { 195 } 196 197 198 ssize_t 199 Directory::ReadAt(void *cookie, off_t pos, void *buffer, size_t bufferSize) 200 { 201 return B_ERROR; 202 } 203 204 205 ssize_t 206 Directory::WriteAt(void *cookie, off_t pos, const void *buffer, size_t bufferSize) 207 { 208 return B_ERROR; 209 } 210 211 212 int32 213 Directory::Type() const 214 { 215 return S_IFDIR; 216 } 217 218 219 Node* 220 Directory::Lookup(const char* name, bool traverseLinks) 221 { 222 Node* node = LookupDontTraverse(name); 223 if (node == NULL) 224 return NULL; 225 226 if (!traverseLinks || !S_ISLNK(node->Type())) 227 return node; 228 229 // the node is a symbolic link, so we have to resolve the path 230 char linkPath[B_PATH_NAME_LENGTH]; 231 status_t error = node->ReadLink(linkPath, sizeof(linkPath)); 232 233 node->Release(); 234 // we don't need this one anymore 235 236 if (error != B_OK) 237 return NULL; 238 239 // let open_from() do the real work 240 int fd = open_from(this, linkPath, O_RDONLY); 241 if (fd < 0) 242 return NULL; 243 244 node = get_node_from(fd); 245 if (node != NULL) 246 node->Acquire(); 247 248 close(fd); 249 return node; 250 } 251 252 253 status_t 254 Directory::CreateFile(const char *name, mode_t permissions, Node **_node) 255 { 256 return EROFS; 257 } 258 259 260 // #pragma mark - 261 262 263 MemoryDisk::MemoryDisk(const uint8* data, size_t size, const char* name) 264 : Node(), 265 fData(data), 266 fSize(size) 267 { 268 strlcpy(fName, name, sizeof(fName)); 269 } 270 271 272 ssize_t 273 MemoryDisk::ReadAt(void* cookie, off_t pos, void* buffer, size_t bufferSize) 274 { 275 if (pos >= fSize) 276 return 0; 277 278 if (pos + bufferSize > fSize) 279 bufferSize = fSize - pos; 280 281 memcpy(buffer, fData + pos, bufferSize); 282 return bufferSize; 283 } 284 285 286 ssize_t 287 MemoryDisk::WriteAt(void* cookie, off_t pos, const void* buffer, 288 size_t bufferSize) 289 { 290 return B_NOT_ALLOWED; 291 } 292 293 294 off_t 295 MemoryDisk::Size() const 296 { 297 return fSize; 298 } 299 300 301 status_t 302 MemoryDisk::GetName(char *nameBuffer, size_t bufferSize) const 303 { 304 if (!nameBuffer) 305 return B_BAD_VALUE; 306 307 strlcpy(nameBuffer, fName, bufferSize); 308 return B_OK; 309 } 310 311 312 // #pragma mark - 313 314 315 Descriptor::Descriptor(Node *node, void *cookie) 316 : 317 fNode(node), 318 fCookie(cookie), 319 fOffset(0), 320 fRefCount(1) 321 { 322 } 323 324 325 Descriptor::~Descriptor() 326 { 327 } 328 329 330 ssize_t 331 Descriptor::Read(void *buffer, size_t bufferSize) 332 { 333 ssize_t bytesRead = fNode->ReadAt(fCookie, fOffset, buffer, bufferSize); 334 if (bytesRead > B_OK) 335 fOffset += bytesRead; 336 337 return bytesRead; 338 } 339 340 341 ssize_t 342 Descriptor::ReadAt(off_t pos, void *buffer, size_t bufferSize) 343 { 344 return fNode->ReadAt(fCookie, pos, buffer, bufferSize); 345 } 346 347 348 ssize_t 349 Descriptor::Write(const void *buffer, size_t bufferSize) 350 { 351 ssize_t bytesWritten = fNode->WriteAt(fCookie, fOffset, buffer, bufferSize); 352 if (bytesWritten > B_OK) 353 fOffset += bytesWritten; 354 355 return bytesWritten; 356 } 357 358 359 ssize_t 360 Descriptor::WriteAt(off_t pos, const void *buffer, size_t bufferSize) 361 { 362 return fNode->WriteAt(fCookie, pos, buffer, bufferSize); 363 } 364 365 366 status_t 367 Descriptor::Stat(struct stat &stat) 368 { 369 stat.st_mode = fNode->Type(); 370 stat.st_size = fNode->Size(); 371 stat.st_ino = fNode->Inode(); 372 373 return B_OK; 374 } 375 376 377 status_t 378 Descriptor::Acquire() 379 { 380 fRefCount++; 381 return B_OK; 382 } 383 384 385 status_t 386 Descriptor::Release() 387 { 388 if (--fRefCount == 0) { 389 status_t status = fNode->Close(fCookie); 390 if (status != B_OK) 391 return status; 392 } 393 394 return B_OK; 395 } 396 397 398 // #pragma mark - 399 400 401 BootVolume::BootVolume() 402 : 403 fRootDirectory(NULL), 404 fSystemDirectory(NULL), 405 fPackaged(false) 406 { 407 } 408 409 410 BootVolume::~BootVolume() 411 { 412 Unset(); 413 } 414 415 416 status_t 417 BootVolume::SetTo(Directory* rootDirectory) 418 { 419 Unset(); 420 421 if (rootDirectory == NULL) 422 return B_BAD_VALUE; 423 424 fRootDirectory = rootDirectory; 425 426 // find the system directory 427 Node* systemNode = fRootDirectory->Lookup("system", true); 428 if (systemNode == NULL || !S_ISDIR(systemNode->Type())) { 429 if (systemNode != NULL) 430 systemNode->Release(); 431 Unset(); 432 return B_ENTRY_NOT_FOUND; 433 } 434 435 fSystemDirectory = static_cast<Directory*>(systemNode); 436 437 // try opening the system package 438 int packageFD = open_from(fSystemDirectory, "packages/haiku.hpkg", 439 O_RDONLY); 440 fPackaged = packageFD >= 0; 441 if (!fPackaged) 442 return B_OK; 443 444 // the system is packaged -- mount the packagefs 445 Directory* packageRootDirectory; 446 status_t error = packagefs_mount_file(packageFD, packageRootDirectory); 447 close(packageFD); 448 if (error != B_OK) { 449 Unset(); 450 return error; 451 } 452 453 fSystemDirectory->Release(); 454 fSystemDirectory = packageRootDirectory; 455 456 return B_OK; 457 } 458 459 460 void 461 BootVolume::Unset() 462 { 463 if (fRootDirectory != NULL) { 464 fRootDirectory->Release(); 465 fRootDirectory = NULL; 466 } 467 468 if (fSystemDirectory != NULL) { 469 fSystemDirectory->Release(); 470 fSystemDirectory = NULL; 471 } 472 473 fPackaged = false; 474 } 475 476 477 // #pragma mark - 478 479 480 status_t 481 vfs_init(stage2_args *args) 482 { 483 gRoot = new(nothrow) RootFileSystem(); 484 if (gRoot == NULL) 485 return B_NO_MEMORY; 486 487 return B_OK; 488 } 489 490 491 status_t 492 register_boot_file_system(BootVolume& bootVolume) 493 { 494 Directory* rootDirectory = bootVolume.RootDirectory(); 495 gRoot->AddLink("boot", rootDirectory); 496 497 Partition *partition; 498 status_t status = gRoot->GetPartitionFor(rootDirectory, &partition); 499 if (status != B_OK) { 500 dprintf("register_boot_file_system(): could not locate boot volume in " 501 "root!\n"); 502 return status; 503 } 504 505 gBootVolume.SetInt64(BOOT_VOLUME_PARTITION_OFFSET, 506 partition->offset); 507 if (bootVolume.IsPackaged()) 508 gBootVolume.SetBool(BOOT_VOLUME_PACKAGED, true); 509 510 Node *device = get_node_from(partition->FD()); 511 if (device == NULL) { 512 dprintf("register_boot_file_system(): could not get boot device!\n"); 513 return B_ERROR; 514 } 515 516 return platform_register_boot_device(device); 517 } 518 519 520 /*! Gets the boot device, scans all of its partitions, gets the 521 boot partition, and mounts its file system. 522 523 \param args The stage 2 arguments. 524 \param _bootVolume On success set to the boot volume. 525 \return \c B_OK on success, another error code otherwise. 526 */ 527 status_t 528 get_boot_file_system(stage2_args* args, BootVolume& _bootVolume) 529 { 530 Node *device; 531 status_t error = platform_add_boot_device(args, &gBootDevices); 532 if (error != B_OK) 533 return error; 534 535 // the boot device must be the first device in the list 536 device = gBootDevices.First(); 537 538 error = add_partitions_for(device, false, true); 539 if (error != B_OK) 540 return error; 541 542 Partition *partition; 543 error = platform_get_boot_partition(args, device, &gPartitions, &partition); 544 if (error != B_OK) 545 return error; 546 547 Directory *fileSystem; 548 error = partition->Mount(&fileSystem, true); 549 if (error != B_OK) { 550 // this partition doesn't contain any known file system; we 551 // don't need it anymore 552 gPartitions.Remove(partition); 553 delete partition; 554 return error; 555 } 556 557 // init the BootVolume 558 error = _bootVolume.SetTo(fileSystem); 559 if (error != B_OK) 560 return error; 561 562 sBootDevice = device; 563 return B_OK; 564 } 565 566 567 /** Mounts all file systems recognized on the given device by 568 * calling the add_partitions_for() function on them. 569 */ 570 571 status_t 572 mount_file_systems(stage2_args *args) 573 { 574 // mount other partitions on boot device (if any) 575 NodeIterator iterator = gPartitions.GetIterator(); 576 577 Partition *partition = NULL; 578 while ((partition = (Partition *)iterator.Next()) != NULL) { 579 // don't scan known partitions again 580 if (partition->IsFileSystem()) 581 continue; 582 583 // remove the partition if it doesn't contain a (known) file system 584 if (partition->Scan(true) != B_OK && !partition->IsFileSystem()) { 585 gPartitions.Remove(partition); 586 delete partition; 587 } 588 } 589 590 // add all block devices the platform has for us 591 592 status_t status = platform_add_block_devices(args, &gBootDevices); 593 if (status < B_OK) 594 return status; 595 596 iterator = gBootDevices.GetIterator(); 597 Node *device = NULL, *last = NULL; 598 while ((device = iterator.Next()) != NULL) { 599 // don't scan former boot device again 600 if (device == sBootDevice) 601 continue; 602 603 if (add_partitions_for(device, true) == B_OK) { 604 // ToDo: we can't delete the object here, because it must 605 // be removed from the list before we know that it was 606 // deleted. 607 608 /* // if the Release() deletes the object, we need to skip it 609 if (device->Release() > 0) { 610 list_remove_item(&gBootDevices, device); 611 device = last; 612 } 613 */ 614 } 615 last = device; 616 } 617 618 if (gPartitions.IsEmpty()) 619 return B_ENTRY_NOT_FOUND; 620 621 #if 0 622 void *cookie; 623 if (gRoot->Open(&cookie, O_RDONLY) == B_OK) { 624 Directory *directory; 625 while (gRoot->GetNextNode(cookie, (Node **)&directory) == B_OK) { 626 char name[256]; 627 if (directory->GetName(name, sizeof(name)) == B_OK) 628 printf(":: %s (%p)\n", name, directory); 629 630 void *subCookie; 631 if (directory->Open(&subCookie, O_RDONLY) == B_OK) { 632 while (directory->GetNextEntry(subCookie, name, sizeof(name)) == B_OK) { 633 printf("\t%s\n", name); 634 } 635 directory->Close(subCookie); 636 } 637 } 638 gRoot->Close(cookie); 639 } 640 #endif 641 642 return B_OK; 643 } 644 645 646 /*! Resolves \a directory + \a path to a node. 647 Note that \a path will be modified by the function. 648 */ 649 static status_t 650 get_node_for_path(Directory *directory, char *path, Node **_node) 651 { 652 directory->Acquire(); 653 // balance Acquire()/Release() calls 654 655 while (true) { 656 Node *nextNode; 657 char *nextPath; 658 659 // walk to find the next path component ("path" will point to a single 660 // path component), and filter out multiple slashes 661 for (nextPath = path + 1; nextPath[0] != '\0' && nextPath[0] != '/'; nextPath++); 662 663 if (*nextPath == '/') { 664 *nextPath = '\0'; 665 do 666 nextPath++; 667 while (*nextPath == '/'); 668 } 669 670 nextNode = directory->Lookup(path, true); 671 directory->Release(); 672 673 if (nextNode == NULL) 674 return B_ENTRY_NOT_FOUND; 675 676 path = nextPath; 677 if (S_ISDIR(nextNode->Type())) 678 directory = (Directory *)nextNode; 679 else if (path[0]) 680 return B_NOT_ALLOWED; 681 682 // are we done? 683 if (path[0] == '\0') { 684 *_node = nextNode; 685 return B_OK; 686 } 687 } 688 689 return B_ENTRY_NOT_FOUND; 690 } 691 692 693 // #pragma mark - 694 695 696 static Descriptor * 697 get_descriptor(int fd) 698 { 699 if (fd < 0 || fd >= MAX_VFS_DESCRIPTORS) 700 return NULL; 701 702 return sDescriptors[fd]; 703 } 704 705 706 static void 707 free_descriptor(int fd) 708 { 709 if (fd >= MAX_VFS_DESCRIPTORS) 710 return; 711 712 delete sDescriptors[fd]; 713 sDescriptors[fd] = NULL; 714 } 715 716 717 /** Reserves an entry of the descriptor table and 718 * assigns the given node to it. 719 */ 720 721 int 722 open_node(Node *node, int mode) 723 { 724 if (node == NULL) 725 return B_ERROR; 726 727 // get free descriptor 728 729 int fd = 0; 730 for (; fd < MAX_VFS_DESCRIPTORS; fd++) { 731 if (sDescriptors[fd] == NULL) 732 break; 733 } 734 if (fd == MAX_VFS_DESCRIPTORS) 735 return B_ERROR; 736 737 TRACE(("got descriptor %d for node %p\n", fd, node)); 738 739 // we got a free descriptor entry, now try to open the node 740 741 void *cookie; 742 status_t status = node->Open(&cookie, mode); 743 if (status < B_OK) 744 return status; 745 746 TRACE(("could open node at %p\n", node)); 747 748 Descriptor *descriptor = new(nothrow) Descriptor(node, cookie); 749 if (descriptor == NULL) 750 return B_NO_MEMORY; 751 752 sDescriptors[fd] = descriptor; 753 754 return fd; 755 } 756 757 758 int 759 dup(int fd) 760 { 761 Descriptor *descriptor = get_descriptor(fd); 762 if (descriptor == NULL) 763 RETURN_AND_SET_ERRNO(B_FILE_ERROR); 764 765 descriptor->Acquire(); 766 RETURN_AND_SET_ERRNO(fd); 767 } 768 769 770 ssize_t 771 read_pos(int fd, off_t offset, void *buffer, size_t bufferSize) 772 { 773 Descriptor *descriptor = get_descriptor(fd); 774 if (descriptor == NULL) 775 RETURN_AND_SET_ERRNO(B_FILE_ERROR); 776 777 RETURN_AND_SET_ERRNO(descriptor->ReadAt(offset, buffer, bufferSize)); 778 } 779 780 781 ssize_t 782 pread(int fd, void* buffer, size_t bufferSize, off_t offset) 783 { 784 return read_pos(fd, offset, buffer, bufferSize); 785 } 786 787 788 ssize_t 789 read(int fd, void *buffer, size_t bufferSize) 790 { 791 Descriptor *descriptor = get_descriptor(fd); 792 if (descriptor == NULL) 793 RETURN_AND_SET_ERRNO(B_FILE_ERROR); 794 795 RETURN_AND_SET_ERRNO(descriptor->Read(buffer, bufferSize)); 796 } 797 798 799 ssize_t 800 write_pos(int fd, off_t offset, const void *buffer, size_t bufferSize) 801 { 802 Descriptor *descriptor = get_descriptor(fd); 803 if (descriptor == NULL) 804 RETURN_AND_SET_ERRNO(B_FILE_ERROR); 805 806 RETURN_AND_SET_ERRNO(descriptor->WriteAt(offset, buffer, bufferSize)); 807 } 808 809 810 ssize_t 811 write(int fd, const void *buffer, size_t bufferSize) 812 { 813 Descriptor *descriptor = get_descriptor(fd); 814 if (descriptor == NULL) 815 RETURN_AND_SET_ERRNO(B_FILE_ERROR); 816 817 RETURN_AND_SET_ERRNO(descriptor->Write(buffer, bufferSize)); 818 } 819 820 821 ssize_t 822 writev(int fd, const struct iovec* vecs, size_t count) 823 { 824 size_t totalWritten = 0; 825 826 for (size_t i = 0; i < count; i++) { 827 ssize_t written = write(fd, vecs[i].iov_base, vecs[i].iov_len); 828 if (written < 0) 829 return totalWritten == 0 ? written : totalWritten; 830 831 totalWritten += written; 832 833 if ((size_t)written != vecs[i].iov_len) 834 break; 835 } 836 837 return totalWritten; 838 } 839 840 841 int 842 open(const char *name, int mode, ...) 843 { 844 mode_t permissions = 0; 845 if ((mode & O_CREAT) != 0) { 846 va_list args; 847 va_start(args, mode); 848 permissions = va_arg(args, int) /*& ~__gUmask*/; 849 // adapt the permissions as required by POSIX 850 va_end(args); 851 } 852 853 // we always start at the top (there is no notion of a current directory (yet?)) 854 RETURN_AND_SET_ERRNO(open_from(gRoot, name, mode, permissions)); 855 } 856 857 858 int 859 open_from(Directory *directory, const char *name, int mode, mode_t permissions) 860 { 861 if (name[0] == '/') { 862 // ignore the directory and start from root if we are asked to do that 863 directory = gRoot; 864 name++; 865 } 866 867 char path[B_PATH_NAME_LENGTH]; 868 if (strlcpy(path, name, sizeof(path)) >= sizeof(path)) 869 return B_NAME_TOO_LONG; 870 871 Node *node; 872 status_t error = get_node_for_path(directory, path, &node); 873 if (error != B_OK) { 874 if (error != B_ENTRY_NOT_FOUND) 875 return error; 876 877 if ((mode & O_CREAT) == 0) 878 return B_ENTRY_NOT_FOUND; 879 880 // try to resolve the parent directory 881 strlcpy(path, name, sizeof(path)); 882 if (char* lastSlash = strrchr(path, '/')) { 883 if (lastSlash[1] == '\0') 884 return B_ENTRY_NOT_FOUND; 885 886 lastSlash = '\0'; 887 name = lastSlash + 1; 888 889 // resolve the directory 890 if (get_node_for_path(directory, path, &node) != B_OK) 891 return B_ENTRY_NOT_FOUND; 892 893 if (node->Type() != S_IFDIR) { 894 node->Release(); 895 return B_NOT_A_DIRECTORY; 896 } 897 898 directory = static_cast<Directory*>(node); 899 } else 900 directory->Acquire(); 901 902 // create the file 903 error = directory->CreateFile(name, permissions, &node); 904 directory->Release(); 905 906 if (error != B_OK) 907 return error; 908 } else if ((mode & O_EXCL) != 0) { 909 node->Release(); 910 return B_FILE_EXISTS; 911 } 912 913 int fd = open_node(node, mode); 914 915 node->Release(); 916 return fd; 917 } 918 919 920 /** Since we don't have directory functions yet, this 921 * function is needed to get the contents of a directory. 922 * It should be removed once readdir() & co. are in place. 923 */ 924 925 Node * 926 get_node_from(int fd) 927 { 928 Descriptor *descriptor = get_descriptor(fd); 929 if (descriptor == NULL) 930 return NULL; 931 932 return descriptor->GetNode(); 933 } 934 935 936 int 937 close(int fd) 938 { 939 Descriptor *descriptor = get_descriptor(fd); 940 if (descriptor == NULL) 941 RETURN_AND_SET_ERRNO(B_FILE_ERROR); 942 943 status_t status = descriptor->Release(); 944 if (!descriptor->RefCount()) 945 free_descriptor(fd); 946 947 RETURN_AND_SET_ERRNO(status); 948 } 949 950 951 // ToDo: remove this kludge when possible 952 int 953 #if defined(fstat) && !defined(main) 954 _fstat(int fd, struct stat *stat, size_t /*statSize*/) 955 #else 956 fstat(int fd, struct stat *stat) 957 #endif 958 { 959 if (stat == NULL) 960 RETURN_AND_SET_ERRNO(B_BAD_VALUE); 961 962 Descriptor *descriptor = get_descriptor(fd); 963 if (descriptor == NULL) 964 RETURN_AND_SET_ERRNO(B_FILE_ERROR); 965 966 RETURN_AND_SET_ERRNO(descriptor->Stat(*stat)); 967 } 968