1 /* 2 * Copyright 2011-2014, Ingo Weinhold, ingo_weinhold@gmx.de. 3 * Distributed under the terms of the MIT License. 4 */ 5 6 7 #include "packagefs.h" 8 9 #include <errno.h> 10 #include <unistd.h> 11 12 #include <package/hpkg/DataReader.h> 13 #include <package/hpkg/ErrorOutput.h> 14 #include <package/hpkg/PackageDataReader.h> 15 #include <package/hpkg/PackageEntry.h> 16 #include <package/hpkg/PackageEntryAttribute.h> 17 #include <package/hpkg/PackageFileHeapReader.h> 18 #include <package/hpkg/PackageReaderImpl.h> 19 20 #include <AutoDeleter.h> 21 #include <FdIO.h> 22 23 #include <util/DoublyLinkedList.h> 24 25 #include <Referenceable.h> 26 27 #include <boot/PathBlocklist.h> 28 #include <boot/platform.h> 29 30 #include "PackageSettingsItem.h" 31 32 33 #if 0 34 # define RETURN_ERROR(error) return (error); 35 #else 36 # define RETURN_ERROR(err) \ 37 { \ 38 status_t _status = err; \ 39 if (_status < B_OK) \ 40 dprintf("%s:%d: %s\n", __FILE__, __LINE__, strerror(_status)); \ 41 return _status; \ 42 } 43 #endif 44 45 46 using namespace BPackageKit; 47 using namespace BPackageKit::BHPKG; 48 using BPackageKit::BHPKG::BPrivate::PackageFileHeapReader; 49 using BPackageKit::BHPKG::BPrivate::PackageReaderImpl; 50 51 using namespace PackageFS; 52 53 54 namespace PackageFS { 55 56 57 struct PackageDirectory; 58 struct PackageNode; 59 struct PackageVolume; 60 61 62 static status_t create_node(PackageNode* packageNode, ::Node*& _node); 63 64 65 // #pragma mark - PackageNode 66 67 68 struct PackageNode : DoublyLinkedListLinkImpl<PackageNode> { 69 PackageNode(PackageVolume* volume, mode_t mode) 70 : 71 fVolume(volume), 72 fParentDirectory(NULL), 73 fName(NULL), 74 fNodeID(0), 75 fMode(mode) 76 { 77 fModifiedTime.tv_sec = 0; 78 fModifiedTime.tv_nsec = 0; 79 } 80 81 virtual ~PackageNode() 82 { 83 free(fName); 84 } 85 86 status_t Init(PackageDirectory* parentDir, const char* name, ino_t nodeID) 87 { 88 fParentDirectory = parentDir; 89 fName = strdup(name); 90 fNodeID = nodeID; 91 92 return fName != NULL ? B_OK : B_NO_MEMORY; 93 } 94 95 PackageVolume* Volume() const 96 { 97 return fVolume; 98 } 99 100 const char* Name() const 101 { 102 return fName; 103 } 104 105 ino_t NodeID() const 106 { 107 return fNodeID; 108 } 109 110 mode_t Mode() const 111 { 112 return fMode; 113 } 114 115 void SetModifiedTime(const timespec& time) 116 { 117 fModifiedTime = time; 118 } 119 120 const timespec& ModifiedTime() const 121 { 122 return fModifiedTime; 123 } 124 125 virtual void RemoveEntry(const char* path) 126 { 127 } 128 129 protected: 130 PackageVolume* fVolume; 131 PackageDirectory* fParentDirectory; 132 char* fName; 133 ino_t fNodeID; 134 mode_t fMode; 135 timespec fModifiedTime; 136 }; 137 138 139 // #pragma mark - PackageFile 140 141 142 struct PackageFile : PackageNode { 143 PackageFile(PackageVolume* volume, mode_t mode, const BPackageData& data) 144 : 145 PackageNode(volume, mode), 146 fData(data) 147 { 148 } 149 150 const BPackageData& Data() const 151 { 152 return fData; 153 } 154 155 off_t Size() const 156 { 157 return fData.Size(); 158 } 159 160 private: 161 BPackageData fData; 162 }; 163 164 165 // #pragma mark - PackageSymlink 166 167 168 struct PackageSymlink : PackageNode { 169 PackageSymlink(PackageVolume* volume, mode_t mode) 170 : 171 PackageNode(volume, mode), 172 fPath(NULL) 173 { 174 } 175 176 ~PackageSymlink() 177 { 178 free(fPath); 179 } 180 181 status_t SetSymlinkPath(const char* path) 182 { 183 fPath = strdup(path); 184 return fPath != NULL ? B_OK : B_NO_MEMORY; 185 } 186 187 const char* SymlinkPath() const 188 { 189 return fPath; 190 } 191 192 private: 193 char* fPath; 194 }; 195 196 197 // #pragma mark - PackageDirectory 198 199 200 struct PackageDirectory : PackageNode { 201 PackageDirectory(PackageVolume* volume, mode_t mode) 202 : 203 PackageNode(volume, mode) 204 { 205 } 206 207 ~PackageDirectory() 208 { 209 while (PackageNode* node = fEntries.RemoveHead()) 210 delete node; 211 } 212 213 void AddChild(PackageNode* node) 214 { 215 fEntries.Add(node); 216 } 217 218 PackageNode* FirstChild() const 219 { 220 return fEntries.Head(); 221 } 222 223 PackageNode* NextChild(PackageNode* child) const 224 { 225 return fEntries.GetNext(child); 226 } 227 228 PackageNode* Lookup(const char* name) 229 { 230 if (strcmp(name, ".") == 0) 231 return this; 232 if (strcmp(name, "..") == 0) 233 return fParentDirectory; 234 235 return _LookupChild(name, strlen(name)); 236 } 237 238 virtual void RemoveEntry(const char* path) 239 { 240 const char* componentEnd = strchr(path, '/'); 241 if (componentEnd == NULL) 242 componentEnd = path + strlen(path); 243 244 PackageNode* child = _LookupChild(path, componentEnd - path); 245 if (child == NULL) 246 return; 247 248 if (*componentEnd == '\0') { 249 // last path component -- delete the child 250 fEntries.Remove(child); 251 delete child; 252 } else { 253 // must be a directory component -- continue resolving the path 254 child->RemoveEntry(componentEnd + 1); 255 } 256 } 257 258 private: 259 typedef DoublyLinkedList<PackageNode> NodeList; 260 261 private: 262 PackageNode* _LookupChild(const char* name, size_t nameLength) 263 { 264 for (NodeList::Iterator it = fEntries.GetIterator(); 265 PackageNode* child = it.Next();) { 266 if (strncmp(child->Name(), name, nameLength) == 0 267 && child->Name()[nameLength] == '\0') { 268 return child; 269 } 270 } 271 272 return NULL; 273 } 274 275 private: 276 NodeList fEntries; 277 }; 278 279 280 // #pragma mark - PackageLoaderErrorOutput 281 282 283 struct PackageLoaderErrorOutput : BErrorOutput { 284 PackageLoaderErrorOutput() 285 { 286 } 287 288 virtual void PrintErrorVarArgs(const char* format, va_list args) 289 { 290 char buffer[256]; 291 vsnprintf(buffer, sizeof(buffer), format, args); 292 dprintf("%s", buffer); 293 } 294 }; 295 296 297 // #pragma mark - PackageVolume 298 299 300 struct PackageVolume : BReferenceable, private PackageLoaderErrorOutput { 301 PackageVolume() 302 : 303 fNextNodeID(1), 304 fRootDirectory(this, S_IFDIR), 305 fHeapReader(NULL), 306 fFile(NULL) 307 { 308 } 309 310 ~PackageVolume() 311 { 312 delete fHeapReader; 313 delete fFile; 314 } 315 316 status_t Init(int fd, const PackageFileHeapReader* heapReader) 317 { 318 status_t error = fRootDirectory.Init(&fRootDirectory, ".", 319 NextNodeID()); 320 if (error != B_OK) 321 return error; 322 323 fd = dup(fd); 324 if (fd < 0) 325 return errno; 326 327 fFile = new(std::nothrow) BFdIO(fd, true); 328 if (fFile == NULL) { 329 close(fd); 330 return B_NO_MEMORY; 331 } 332 333 // clone a heap reader and adjust it for our use 334 fHeapReader = heapReader->Clone(); 335 if (fHeapReader == NULL) 336 return B_NO_MEMORY; 337 338 fHeapReader->SetErrorOutput(this); 339 fHeapReader->SetFile(fFile); 340 341 return B_OK; 342 } 343 344 PackageDirectory* RootDirectory() 345 { 346 return &fRootDirectory; 347 } 348 349 ino_t NextNodeID() 350 { 351 return fNextNodeID++; 352 } 353 354 status_t CreateFileDataReader(const BPackageData& data, 355 BAbstractBufferedDataReader*& _reader) 356 { 357 return BPackageDataReaderFactory().CreatePackageDataReader(fHeapReader, 358 data, _reader); 359 } 360 361 private: 362 ino_t fNextNodeID; 363 PackageDirectory fRootDirectory; 364 PackageFileHeapReader* fHeapReader; 365 BPositionIO* fFile; 366 }; 367 368 369 // #pragma mark - PackageLoaderContentHandler 370 371 372 struct PackageLoaderContentHandler : BPackageContentHandler { 373 PackageLoaderContentHandler(PackageVolume* volume, 374 PackageSettingsItem* settingsItem) 375 : 376 fVolume(volume), 377 fSettingsItem(settingsItem), 378 fLastSettingsEntry(NULL), 379 fLastSettingsEntryEntry(NULL), 380 fErrorOccurred(false) 381 { 382 } 383 384 status_t Init() 385 { 386 return B_OK; 387 } 388 389 virtual status_t HandleEntry(BPackageEntry* entry) 390 { 391 if (fErrorOccurred 392 || (fLastSettingsEntry != NULL 393 && fLastSettingsEntry->IsBlocked())) { 394 return B_OK; 395 } 396 397 PackageDirectory* parentDir = NULL; 398 if (const BPackageEntry* parentEntry = entry->Parent()) { 399 if (!S_ISDIR(parentEntry->Mode())) 400 RETURN_ERROR(B_BAD_DATA); 401 402 parentDir = static_cast<PackageDirectory*>( 403 (PackageNode*)parentEntry->UserToken()); 404 } 405 406 if (fSettingsItem != NULL 407 && (parentDir == NULL 408 || entry->Parent() == fLastSettingsEntryEntry)) { 409 PackageSettingsItem::Entry* settingsEntry 410 = fSettingsItem->FindEntry(fLastSettingsEntry, entry->Name()); 411 if (settingsEntry != NULL) { 412 fLastSettingsEntry = settingsEntry; 413 fLastSettingsEntryEntry = entry; 414 if (fLastSettingsEntry->IsBlocked()) 415 return B_OK; 416 } 417 } 418 419 if (parentDir == NULL) 420 parentDir = fVolume->RootDirectory(); 421 422 status_t error; 423 424 // get the file mode -- filter out write permissions 425 mode_t mode = entry->Mode() & ~(mode_t)(S_IWUSR | S_IWGRP | S_IWOTH); 426 427 // create the package node 428 PackageNode* node; 429 if (S_ISREG(mode)) { 430 // file 431 node = new(std::nothrow) PackageFile(fVolume, mode, entry->Data()); 432 } else if (S_ISLNK(mode)) { 433 // symlink 434 PackageSymlink* symlink = new(std::nothrow) PackageSymlink( 435 fVolume, mode); 436 if (symlink == NULL) 437 RETURN_ERROR(B_NO_MEMORY); 438 439 error = symlink->SetSymlinkPath(entry->SymlinkPath()); 440 if (error != B_OK) { 441 delete symlink; 442 return error; 443 } 444 445 node = symlink; 446 } else if (S_ISDIR(mode)) { 447 // directory 448 node = new(std::nothrow) PackageDirectory(fVolume, mode); 449 } else 450 RETURN_ERROR(B_BAD_DATA); 451 452 if (node == NULL) 453 RETURN_ERROR(B_NO_MEMORY); 454 455 error = node->Init(parentDir, entry->Name(), fVolume->NextNodeID()); 456 if (error != B_OK) { 457 delete node; 458 RETURN_ERROR(error); 459 } 460 461 node->SetModifiedTime(entry->ModifiedTime()); 462 463 // add it to the parent directory 464 parentDir->AddChild(node); 465 466 entry->SetUserToken(node); 467 468 return B_OK; 469 } 470 471 virtual status_t HandleEntryAttribute(BPackageEntry* entry, 472 BPackageEntryAttribute* attribute) 473 { 474 // attributes aren't needed in the boot loader 475 return B_OK; 476 } 477 478 virtual status_t HandleEntryDone(BPackageEntry* entry) 479 { 480 if (entry == fLastSettingsEntryEntry) { 481 fLastSettingsEntryEntry = entry->Parent(); 482 fLastSettingsEntry = fLastSettingsEntry->Parent(); 483 } 484 485 return B_OK; 486 } 487 488 virtual status_t HandlePackageAttribute( 489 const BPackageInfoAttributeValue& value) 490 { 491 // TODO? 492 return B_OK; 493 } 494 495 virtual void HandleErrorOccurred() 496 { 497 fErrorOccurred = true; 498 } 499 500 private: 501 PackageVolume* fVolume; 502 const PackageSettingsItem* fSettingsItem; 503 PackageSettingsItem::Entry* fLastSettingsEntry; 504 const BPackageEntry* fLastSettingsEntryEntry; 505 bool fErrorOccurred; 506 }; 507 508 509 // #pragma mark - File 510 511 512 struct File : ::Node { 513 File(PackageFile* file) 514 : 515 fFile(file) 516 { 517 fFile->Volume()->AcquireReference(); 518 } 519 520 ~File() 521 { 522 fFile->Volume()->ReleaseReference(); 523 } 524 525 virtual ssize_t ReadAt(void* cookie, off_t pos, void* buffer, 526 size_t bufferSize) 527 { 528 off_t size = fFile->Size(); 529 if (pos < 0 || pos > size) 530 return B_BAD_VALUE; 531 if (pos + (off_t)bufferSize > size) 532 bufferSize = size - pos; 533 534 if (bufferSize > 0) { 535 BAbstractBufferedDataReader* dataReader 536 = (BAbstractBufferedDataReader*)cookie; 537 status_t error = dataReader->ReadData(pos, buffer, bufferSize); 538 if (error != B_OK) 539 return error; 540 } 541 542 return bufferSize; 543 } 544 545 virtual ssize_t WriteAt(void* cookie, off_t pos, const void *buffer, 546 size_t bufferSize) 547 { 548 return B_READ_ONLY_DEVICE; 549 } 550 551 virtual status_t GetName(char* nameBuffer, size_t bufferSize) const 552 { 553 strlcpy(nameBuffer, fFile->Name(), bufferSize); 554 return B_OK; 555 } 556 557 virtual status_t Open(void** _cookie, int mode) 558 { 559 if ((mode & O_ACCMODE) != O_RDONLY && (mode & O_ACCMODE) != O_RDWR) 560 return B_NOT_ALLOWED; 561 562 BAbstractBufferedDataReader* dataReader; 563 status_t error = fFile->Volume()->CreateFileDataReader(fFile->Data(), 564 dataReader); 565 if (error != B_OK) 566 return error; 567 568 *_cookie = dataReader; 569 Acquire(); 570 return B_OK; 571 } 572 573 virtual status_t Close(void* cookie) 574 { 575 BAbstractBufferedDataReader* dataReader 576 = (BAbstractBufferedDataReader*)cookie; 577 delete dataReader; 578 Release(); 579 return B_OK; 580 } 581 582 583 virtual int32 Type() const 584 { 585 return fFile->Mode() & S_IFMT; 586 } 587 588 virtual off_t Size() const 589 { 590 return fFile->Size(); 591 } 592 593 virtual ino_t Inode() const 594 { 595 return fFile->NodeID(); 596 } 597 598 private: 599 PackageFile* fFile; 600 }; 601 602 603 // #pragma mark - Symlink 604 605 606 struct Symlink : ::Node { 607 Symlink(PackageSymlink* symlink) 608 : 609 fSymlink(symlink) 610 { 611 fSymlink->Volume()->AcquireReference(); 612 } 613 614 ~Symlink() 615 { 616 fSymlink->Volume()->ReleaseReference(); 617 } 618 619 virtual ssize_t ReadAt(void* cookie, off_t pos, void* buffer, 620 size_t bufferSize) 621 { 622 return B_BAD_VALUE; 623 } 624 625 virtual ssize_t WriteAt(void* cookie, off_t pos, const void *buffer, 626 size_t bufferSize) 627 { 628 return B_READ_ONLY_DEVICE; 629 } 630 631 virtual status_t ReadLink(char* buffer, size_t bufferSize) 632 { 633 const char* path = fSymlink->SymlinkPath(); 634 size_t size = strlen(path) + 1; 635 636 if (size > bufferSize) 637 return B_BUFFER_OVERFLOW; 638 639 memcpy(buffer, path, size); 640 return B_OK; 641 } 642 643 virtual status_t GetName(char* nameBuffer, size_t bufferSize) const 644 { 645 strlcpy(nameBuffer, fSymlink->Name(), bufferSize); 646 return B_OK; 647 } 648 649 virtual int32 Type() const 650 { 651 return fSymlink->Mode() & S_IFMT; 652 } 653 654 virtual off_t Size() const 655 { 656 return strlen(fSymlink->SymlinkPath()) + 1; 657 } 658 659 virtual ino_t Inode() const 660 { 661 return fSymlink->NodeID(); 662 } 663 664 private: 665 PackageSymlink* fSymlink; 666 }; 667 668 669 // #pragma mark - Directory 670 671 672 struct Directory : ::Directory { 673 Directory(PackageDirectory* symlink) 674 : 675 fDirectory(symlink) 676 { 677 fDirectory->Volume()->AcquireReference(); 678 } 679 680 ~Directory() 681 { 682 fDirectory->Volume()->ReleaseReference(); 683 } 684 685 void RemoveEntry(const char* path) 686 { 687 fDirectory->RemoveEntry(path); 688 } 689 690 virtual ssize_t ReadAt(void* cookie, off_t pos, void* buffer, 691 size_t bufferSize) 692 { 693 return B_IS_A_DIRECTORY; 694 } 695 696 virtual ssize_t WriteAt(void* cookie, off_t pos, const void *buffer, 697 size_t bufferSize) 698 { 699 return B_IS_A_DIRECTORY; 700 } 701 702 virtual status_t GetName(char* nameBuffer, size_t bufferSize) const 703 { 704 strlcpy(nameBuffer, fDirectory->Name(), bufferSize); 705 return B_OK; 706 } 707 708 virtual int32 Type() const 709 { 710 return fDirectory->Mode() & S_IFMT; 711 } 712 713 virtual ino_t Inode() const 714 { 715 return fDirectory->NodeID(); 716 } 717 718 virtual status_t Open(void** _cookie, int mode) 719 { 720 if ((mode & O_ACCMODE) != O_RDONLY && (mode & O_ACCMODE) != O_RDWR) 721 return B_NOT_ALLOWED; 722 723 Cookie* cookie = new(std::nothrow) Cookie; 724 if (cookie == NULL) 725 return B_NO_MEMORY; 726 727 cookie->nextChild = fDirectory->FirstChild(); 728 729 Acquire(); 730 *_cookie = cookie; 731 return B_OK; 732 } 733 734 virtual status_t Close(void* _cookie) 735 { 736 Cookie* cookie = (Cookie*)_cookie; 737 delete cookie; 738 Release(); 739 return B_OK; 740 } 741 742 virtual Node* LookupDontTraverse(const char* name) 743 { 744 // look up the child 745 PackageNode* child = fDirectory->Lookup(name); 746 if (child == NULL) 747 return NULL; 748 749 // create the node 750 ::Node* node; 751 return create_node(child, node) == B_OK ? node : NULL; 752 } 753 754 virtual status_t GetNextEntry(void* _cookie, char* nameBuffer, 755 size_t bufferSize) 756 { 757 Cookie* cookie = (Cookie*)_cookie; 758 PackageNode* child = cookie->nextChild; 759 if (child == NULL) 760 return B_ENTRY_NOT_FOUND; 761 762 cookie->nextChild = fDirectory->NextChild(child); 763 764 strlcpy(nameBuffer, child->Name(), bufferSize); 765 return B_OK; 766 } 767 768 virtual status_t GetNextNode(void* _cookie, Node** _node) 769 { 770 Cookie* cookie = (Cookie*)_cookie; 771 PackageNode* child = cookie->nextChild; 772 if (child == NULL) 773 return B_ENTRY_NOT_FOUND; 774 775 cookie->nextChild = fDirectory->NextChild(child); 776 777 return create_node(child, *_node); 778 } 779 780 virtual status_t Rewind(void* _cookie) 781 { 782 Cookie* cookie = (Cookie*)_cookie; 783 cookie->nextChild = NULL; 784 return B_OK; 785 } 786 787 virtual bool IsEmpty() 788 { 789 return fDirectory->FirstChild() == NULL; 790 } 791 792 private: 793 struct Cookie { 794 PackageNode* nextChild; 795 }; 796 797 798 private: 799 PackageDirectory* fDirectory; 800 }; 801 802 803 // #pragma mark - 804 805 806 static status_t 807 create_node(PackageNode* packageNode, ::Node*& _node) 808 { 809 if (packageNode == NULL) 810 return B_BAD_VALUE; 811 812 ::Node* node; 813 switch (packageNode->Mode() & S_IFMT) { 814 case S_IFREG: 815 node = new(std::nothrow) File( 816 static_cast<PackageFile*>(packageNode)); 817 break; 818 case S_IFLNK: 819 node = new(std::nothrow) Symlink( 820 static_cast<PackageSymlink*>(packageNode)); 821 break; 822 case S_IFDIR: 823 node = new(std::nothrow) Directory( 824 static_cast<PackageDirectory*>(packageNode)); 825 break; 826 default: 827 return B_BAD_VALUE; 828 } 829 830 if (node == NULL) 831 return B_NO_MEMORY; 832 833 _node = node; 834 return B_OK; 835 } 836 837 838 } // namespace PackageFS 839 840 841 status_t 842 packagefs_mount_file(int fd, ::Directory* systemDirectory, 843 ::Directory*& _mountedDirectory) 844 { 845 PackageLoaderErrorOutput errorOutput; 846 PackageReaderImpl packageReader(&errorOutput); 847 status_t error = packageReader.Init(fd, false, 0); 848 if (error != B_OK) 849 RETURN_ERROR(error); 850 851 // create the volume 852 PackageVolume* volume = new(std::nothrow) PackageVolume; 853 if (volume == NULL) 854 return B_NO_MEMORY; 855 BReference<PackageVolume> volumeReference(volume, true); 856 857 error = volume->Init(fd, packageReader.RawHeapReader()); 858 if (error != B_OK) 859 RETURN_ERROR(error); 860 861 // load settings for the package 862 PackageSettingsItem* settings = PackageSettingsItem::Load(systemDirectory, 863 "haiku"); 864 ObjectDeleter<PackageSettingsItem> settingsDeleter(settings); 865 866 // parse content -- this constructs the entry/node tree 867 PackageLoaderContentHandler handler(volume, settings); 868 error = handler.Init(); 869 if (error != B_OK) 870 RETURN_ERROR(error); 871 872 error = packageReader.ParseContent(&handler); 873 if (error != B_OK) 874 RETURN_ERROR(error); 875 876 // create a VFS node for the root node 877 ::Node* rootNode; 878 error = create_node(volume->RootDirectory(), rootNode); 879 if (error != B_OK) 880 RETURN_ERROR(error); 881 882 _mountedDirectory = static_cast< ::Directory*>(rootNode); 883 return B_OK; 884 } 885 886 887 void 888 packagefs_apply_path_blocklist(::Directory* systemDirectory, 889 const PathBlocklist& pathBlocklist) 890 { 891 PackageFS::Directory* directory 892 = static_cast<PackageFS::Directory*>(systemDirectory); 893 894 for (PathBlocklist::Iterator it = pathBlocklist.GetIterator(); 895 BlockedPath* path = it.Next();) { 896 directory->RemoveEntry(path->Path()); 897 } 898 } 899 900