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