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