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/PathBlacklist.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 } 291 }; 292 293 294 // #pragma mark - PackageVolume 295 296 297 struct PackageVolume : BReferenceable, private PackageLoaderErrorOutput { 298 PackageVolume() 299 : 300 fNextNodeID(1), 301 fRootDirectory(this, S_IFDIR), 302 fHeapReader(NULL), 303 fFile(NULL) 304 { 305 } 306 307 ~PackageVolume() 308 { 309 delete fHeapReader; 310 delete fFile; 311 } 312 313 status_t Init(int fd, const PackageFileHeapReader* heapReader) 314 { 315 status_t error = fRootDirectory.Init(&fRootDirectory, ".", 316 NextNodeID()); 317 if (error != B_OK) 318 return error; 319 320 fd = dup(fd); 321 if (fd < 0) 322 return errno; 323 324 fFile = new(std::nothrow) BFdIO(fd, true); 325 if (fFile == NULL) { 326 close(fd); 327 return B_NO_MEMORY; 328 } 329 330 // clone a heap reader and adjust it for our use 331 fHeapReader = heapReader->Clone(); 332 if (fHeapReader == NULL) 333 return B_NO_MEMORY; 334 335 fHeapReader->SetErrorOutput(this); 336 fHeapReader->SetFile(fFile); 337 338 return B_OK; 339 } 340 341 PackageDirectory* RootDirectory() 342 { 343 return &fRootDirectory; 344 } 345 346 ino_t NextNodeID() 347 { 348 return fNextNodeID++; 349 } 350 351 status_t CreateFileDataReader(const BPackageData& data, 352 BAbstractBufferedDataReader*& _reader) 353 { 354 return BPackageDataReaderFactory().CreatePackageDataReader(fHeapReader, 355 data, _reader); 356 } 357 358 private: 359 ino_t fNextNodeID; 360 PackageDirectory fRootDirectory; 361 PackageFileHeapReader* fHeapReader; 362 BPositionIO* fFile; 363 }; 364 365 366 // #pragma mark - PackageLoaderContentHandler 367 368 369 struct PackageLoaderContentHandler : BPackageContentHandler { 370 PackageLoaderContentHandler(PackageVolume* volume, 371 PackageSettingsItem* settingsItem) 372 : 373 fVolume(volume), 374 fSettingsItem(settingsItem), 375 fLastSettingsEntry(NULL), 376 fLastSettingsEntryEntry(NULL), 377 fErrorOccurred(false) 378 { 379 } 380 381 status_t Init() 382 { 383 return B_OK; 384 } 385 386 virtual status_t HandleEntry(BPackageEntry* entry) 387 { 388 if (fErrorOccurred 389 || (fLastSettingsEntry != NULL 390 && fLastSettingsEntry->IsBlackListed())) { 391 return B_OK; 392 } 393 394 PackageDirectory* parentDir = NULL; 395 if (const BPackageEntry* parentEntry = entry->Parent()) { 396 if (!S_ISDIR(parentEntry->Mode())) 397 RETURN_ERROR(B_BAD_DATA); 398 399 parentDir = static_cast<PackageDirectory*>( 400 (PackageNode*)parentEntry->UserToken()); 401 } 402 403 if (fSettingsItem != NULL 404 && (parentDir == NULL 405 || entry->Parent() == fLastSettingsEntryEntry)) { 406 PackageSettingsItem::Entry* settingsEntry 407 = fSettingsItem->FindEntry(fLastSettingsEntry, entry->Name()); 408 if (settingsEntry != NULL) { 409 fLastSettingsEntry = settingsEntry; 410 fLastSettingsEntryEntry = entry; 411 if (fLastSettingsEntry->IsBlackListed()) 412 return B_OK; 413 } 414 } 415 416 if (parentDir == NULL) 417 parentDir = fVolume->RootDirectory(); 418 419 status_t error; 420 421 // get the file mode -- filter out write permissions 422 mode_t mode = entry->Mode() & ~(mode_t)(S_IWUSR | S_IWGRP | S_IWOTH); 423 424 // create the package node 425 PackageNode* node; 426 if (S_ISREG(mode)) { 427 // file 428 node = new(std::nothrow) PackageFile(fVolume, mode, entry->Data()); 429 } else if (S_ISLNK(mode)) { 430 // symlink 431 PackageSymlink* symlink = new(std::nothrow) PackageSymlink( 432 fVolume, mode); 433 if (symlink == NULL) 434 RETURN_ERROR(B_NO_MEMORY); 435 436 error = symlink->SetSymlinkPath(entry->SymlinkPath()); 437 if (error != B_OK) { 438 delete symlink; 439 return error; 440 } 441 442 node = symlink; 443 } else if (S_ISDIR(mode)) { 444 // directory 445 node = new(std::nothrow) PackageDirectory(fVolume, mode); 446 } else 447 RETURN_ERROR(B_BAD_DATA); 448 449 if (node == NULL) 450 RETURN_ERROR(B_NO_MEMORY); 451 452 error = node->Init(parentDir, entry->Name(), fVolume->NextNodeID()); 453 if (error != B_OK) { 454 delete node; 455 RETURN_ERROR(error); 456 } 457 458 node->SetModifiedTime(entry->ModifiedTime()); 459 460 // add it to the parent directory 461 parentDir->AddChild(node); 462 463 entry->SetUserToken(node); 464 465 return B_OK; 466 } 467 468 virtual status_t HandleEntryAttribute(BPackageEntry* entry, 469 BPackageEntryAttribute* attribute) 470 { 471 // attributes aren't needed in the boot loader 472 return B_OK; 473 } 474 475 virtual status_t HandleEntryDone(BPackageEntry* entry) 476 { 477 if (entry == fLastSettingsEntryEntry) { 478 fLastSettingsEntryEntry = entry->Parent(); 479 fLastSettingsEntry = fLastSettingsEntry->Parent(); 480 } 481 482 return B_OK; 483 } 484 485 virtual status_t HandlePackageAttribute( 486 const BPackageInfoAttributeValue& value) 487 { 488 // TODO? 489 return B_OK; 490 } 491 492 virtual void HandleErrorOccurred() 493 { 494 fErrorOccurred = true; 495 } 496 497 private: 498 PackageVolume* fVolume; 499 const PackageSettingsItem* fSettingsItem; 500 PackageSettingsItem::Entry* fLastSettingsEntry; 501 const BPackageEntry* fLastSettingsEntryEntry; 502 bool fErrorOccurred; 503 }; 504 505 506 // #pragma mark - File 507 508 509 struct File : ::Node { 510 File(PackageFile* file) 511 : 512 fFile(file) 513 { 514 fFile->Volume()->AcquireReference(); 515 } 516 517 ~File() 518 { 519 fFile->Volume()->ReleaseReference(); 520 } 521 522 virtual ssize_t ReadAt(void* cookie, off_t pos, void* buffer, 523 size_t bufferSize) 524 { 525 off_t size = fFile->Size(); 526 if (pos < 0 || pos > size) 527 return B_BAD_VALUE; 528 if (pos + (off_t)bufferSize > size) 529 bufferSize = size - pos; 530 531 if (bufferSize > 0) { 532 BAbstractBufferedDataReader* dataReader 533 = (BAbstractBufferedDataReader*)cookie; 534 status_t error = dataReader->ReadData(pos, buffer, bufferSize); 535 if (error != B_OK) 536 return error; 537 } 538 539 return bufferSize; 540 } 541 542 virtual ssize_t WriteAt(void* cookie, off_t pos, const void *buffer, 543 size_t bufferSize) 544 { 545 return B_READ_ONLY_DEVICE; 546 } 547 548 virtual status_t GetName(char* nameBuffer, size_t bufferSize) const 549 { 550 strlcpy(nameBuffer, fFile->Name(), bufferSize); 551 return B_OK; 552 } 553 554 virtual status_t Open(void** _cookie, int mode) 555 { 556 if ((mode & O_ACCMODE) != O_RDONLY && (mode & O_ACCMODE) != O_RDWR) 557 return B_NOT_ALLOWED; 558 559 BAbstractBufferedDataReader* dataReader; 560 status_t error = fFile->Volume()->CreateFileDataReader(fFile->Data(), 561 dataReader); 562 if (error != B_OK) 563 return error; 564 565 *_cookie = dataReader; 566 Acquire(); 567 return B_OK; 568 } 569 570 virtual status_t Close(void* cookie) 571 { 572 BAbstractBufferedDataReader* dataReader 573 = (BAbstractBufferedDataReader*)cookie; 574 delete dataReader; 575 Release(); 576 return B_OK; 577 } 578 579 580 virtual int32 Type() const 581 { 582 return fFile->Mode() & S_IFMT; 583 } 584 585 virtual off_t Size() const 586 { 587 return fFile->Size(); 588 } 589 590 virtual ino_t Inode() const 591 { 592 return fFile->NodeID(); 593 } 594 595 private: 596 PackageFile* fFile; 597 }; 598 599 600 // #pragma mark - Symlink 601 602 603 struct Symlink : ::Node { 604 Symlink(PackageSymlink* symlink) 605 : 606 fSymlink(symlink) 607 { 608 fSymlink->Volume()->AcquireReference(); 609 } 610 611 ~Symlink() 612 { 613 fSymlink->Volume()->ReleaseReference(); 614 } 615 616 virtual ssize_t ReadAt(void* cookie, off_t pos, void* buffer, 617 size_t bufferSize) 618 { 619 return B_BAD_VALUE; 620 } 621 622 virtual ssize_t WriteAt(void* cookie, off_t pos, const void *buffer, 623 size_t bufferSize) 624 { 625 return B_READ_ONLY_DEVICE; 626 } 627 628 virtual status_t ReadLink(char* buffer, size_t bufferSize) 629 { 630 const char* path = fSymlink->SymlinkPath(); 631 size_t size = strlen(path) + 1; 632 633 if (size > bufferSize) 634 return B_BUFFER_OVERFLOW; 635 636 memcpy(buffer, path, size); 637 return B_OK; 638 } 639 640 virtual status_t GetName(char* nameBuffer, size_t bufferSize) const 641 { 642 strlcpy(nameBuffer, fSymlink->Name(), bufferSize); 643 return B_OK; 644 } 645 646 virtual int32 Type() const 647 { 648 return fSymlink->Mode() & S_IFMT; 649 } 650 651 virtual off_t Size() const 652 { 653 return strlen(fSymlink->SymlinkPath()) + 1; 654 } 655 656 virtual ino_t Inode() const 657 { 658 return fSymlink->NodeID(); 659 } 660 661 private: 662 PackageSymlink* fSymlink; 663 }; 664 665 666 // #pragma mark - Directory 667 668 669 struct Directory : ::Directory { 670 Directory(PackageDirectory* symlink) 671 : 672 fDirectory(symlink) 673 { 674 fDirectory->Volume()->AcquireReference(); 675 } 676 677 ~Directory() 678 { 679 fDirectory->Volume()->ReleaseReference(); 680 } 681 682 void RemoveEntry(const char* path) 683 { 684 fDirectory->RemoveEntry(path); 685 } 686 687 virtual ssize_t ReadAt(void* cookie, off_t pos, void* buffer, 688 size_t bufferSize) 689 { 690 return B_IS_A_DIRECTORY; 691 } 692 693 virtual ssize_t WriteAt(void* cookie, off_t pos, const void *buffer, 694 size_t bufferSize) 695 { 696 return B_IS_A_DIRECTORY; 697 } 698 699 virtual status_t GetName(char* nameBuffer, size_t bufferSize) const 700 { 701 strlcpy(nameBuffer, fDirectory->Name(), bufferSize); 702 return B_OK; 703 } 704 705 virtual int32 Type() const 706 { 707 return fDirectory->Mode() & S_IFMT; 708 } 709 710 virtual ino_t Inode() const 711 { 712 return fDirectory->NodeID(); 713 } 714 715 virtual status_t Open(void** _cookie, int mode) 716 { 717 if ((mode & O_ACCMODE) != O_RDONLY && (mode & O_ACCMODE) != O_RDWR) 718 return B_NOT_ALLOWED; 719 720 Cookie* cookie = new(std::nothrow) Cookie; 721 if (cookie == NULL) 722 return B_NO_MEMORY; 723 724 cookie->nextChild = fDirectory->FirstChild(); 725 726 Acquire(); 727 *_cookie = cookie; 728 return B_OK; 729 } 730 731 virtual status_t Close(void* _cookie) 732 { 733 Cookie* cookie = (Cookie*)_cookie; 734 delete cookie; 735 Release(); 736 return B_OK; 737 } 738 739 virtual Node* LookupDontTraverse(const char* name) 740 { 741 // look up the child 742 PackageNode* child = fDirectory->Lookup(name); 743 if (child == NULL) 744 return NULL; 745 746 // create the node 747 ::Node* node; 748 return create_node(child, node) == B_OK ? node : NULL; 749 } 750 751 virtual status_t GetNextEntry(void* _cookie, char* nameBuffer, 752 size_t bufferSize) 753 { 754 Cookie* cookie = (Cookie*)_cookie; 755 PackageNode* child = cookie->nextChild; 756 if (child == NULL) 757 return B_ENTRY_NOT_FOUND; 758 759 cookie->nextChild = fDirectory->NextChild(child); 760 761 strlcpy(nameBuffer, child->Name(), bufferSize); 762 return B_OK; 763 } 764 765 virtual status_t GetNextNode(void* _cookie, Node** _node) 766 { 767 Cookie* cookie = (Cookie*)_cookie; 768 PackageNode* child = cookie->nextChild; 769 if (child == NULL) 770 return B_ENTRY_NOT_FOUND; 771 772 cookie->nextChild = fDirectory->NextChild(child); 773 774 return create_node(child, *_node); 775 } 776 777 virtual status_t Rewind(void* _cookie) 778 { 779 Cookie* cookie = (Cookie*)_cookie; 780 cookie->nextChild = NULL; 781 return B_OK; 782 } 783 784 virtual bool IsEmpty() 785 { 786 return fDirectory->FirstChild() == NULL; 787 } 788 789 private: 790 struct Cookie { 791 PackageNode* nextChild; 792 }; 793 794 795 private: 796 PackageDirectory* fDirectory; 797 }; 798 799 800 // #pragma mark - 801 802 803 static status_t 804 create_node(PackageNode* packageNode, ::Node*& _node) 805 { 806 if (packageNode == NULL) 807 return B_BAD_VALUE; 808 809 ::Node* node; 810 switch (packageNode->Mode() & S_IFMT) { 811 case S_IFREG: 812 node = new(std::nothrow) File( 813 static_cast<PackageFile*>(packageNode)); 814 break; 815 case S_IFLNK: 816 node = new(std::nothrow) Symlink( 817 static_cast<PackageSymlink*>(packageNode)); 818 break; 819 case S_IFDIR: 820 node = new(std::nothrow) Directory( 821 static_cast<PackageDirectory*>(packageNode)); 822 break; 823 default: 824 return B_BAD_VALUE; 825 } 826 827 if (node == NULL) 828 return B_NO_MEMORY; 829 830 _node = node; 831 return B_OK; 832 } 833 834 835 } // namespace PackageFS 836 837 838 status_t 839 packagefs_mount_file(int fd, ::Directory* systemDirectory, 840 ::Directory*& _mountedDirectory) 841 { 842 PackageLoaderErrorOutput errorOutput; 843 PackageReaderImpl packageReader(&errorOutput); 844 status_t error = packageReader.Init(fd, false, 0); 845 if (error != B_OK) 846 RETURN_ERROR(error); 847 848 // create the volume 849 PackageVolume* volume = new(std::nothrow) PackageVolume; 850 if (volume == NULL) 851 return B_NO_MEMORY; 852 BReference<PackageVolume> volumeReference(volume, true); 853 854 error = volume->Init(fd, packageReader.RawHeapReader()); 855 if (error != B_OK) 856 RETURN_ERROR(error); 857 858 // load settings for the package 859 PackageSettingsItem* settings = PackageSettingsItem::Load(systemDirectory, 860 "haiku"); 861 ObjectDeleter<PackageSettingsItem> settingsDeleter(settings); 862 863 // parse content -- this constructs the entry/node tree 864 PackageLoaderContentHandler handler(volume, settings); 865 error = handler.Init(); 866 if (error != B_OK) 867 RETURN_ERROR(error); 868 869 error = packageReader.ParseContent(&handler); 870 if (error != B_OK) 871 RETURN_ERROR(error); 872 873 // create a VFS node for the root node 874 ::Node* rootNode; 875 error = create_node(volume->RootDirectory(), rootNode); 876 if (error != B_OK) 877 RETURN_ERROR(error); 878 879 _mountedDirectory = static_cast< ::Directory*>(rootNode); 880 return B_OK; 881 } 882 883 884 void 885 packagefs_apply_path_blacklist(::Directory* systemDirectory, 886 const PathBlacklist& pathBlacklist) 887 { 888 PackageFS::Directory* directory 889 = static_cast<PackageFS::Directory*>(systemDirectory); 890 891 for (PathBlacklist::Iterator it = pathBlacklist.GetIterator(); 892 BlacklistedPath* path = it.Next();) { 893 directory->RemoveEntry(path->Path()); 894 } 895 } 896 897