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