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