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