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