1 /* 2 * Copyright 2009-2014, Ingo Weinhold, ingo_weinhold@gmx.de. 3 * Copyright 2011, Oliver Tappe <zooey@hirschkaefer.de> 4 * Distributed under the terms of the MIT License. 5 */ 6 7 8 #include <package/hpkg/v1/PackageReaderImpl.h> 9 10 #include <errno.h> 11 #include <fcntl.h> 12 #include <stdio.h> 13 #include <stdlib.h> 14 #include <string.h> 15 #include <sys/stat.h> 16 #include <unistd.h> 17 18 #include <algorithm> 19 #include <new> 20 21 #include <ByteOrder.h> 22 23 #include <package/hpkg/v1/HPKGDefsPrivate.h> 24 25 #include <package/hpkg/ErrorOutput.h> 26 #include <package/hpkg/v1/PackageData.h> 27 #include <package/hpkg/v1/PackageEntry.h> 28 #include <package/hpkg/v1/PackageEntryAttribute.h> 29 30 31 namespace BPackageKit { 32 33 namespace BHPKG { 34 35 namespace V1 { 36 37 namespace BPrivate { 38 39 40 //#define TRACE(format...) printf(format) 41 #define TRACE(format...) do {} while (false) 42 43 44 // maximum TOC size we support reading 45 static const size_t kMaxTOCSize = 64 * 1024 * 1024; 46 47 // maximum package attributes size we support reading 48 static const size_t kMaxPackageAttributesSize = 1 * 1024 * 1024; 49 50 51 // #pragma mark - DataAttributeHandler 52 53 54 struct PackageReaderImpl::DataAttributeHandler : AttributeHandler { 55 DataAttributeHandler(BPackageData* data) 56 : 57 fData(data) 58 { 59 } 60 61 static status_t InitData(AttributeHandlerContext* context, 62 BPackageData* data, const AttributeValue& value) 63 { 64 if (value.encoding == B_HPKG_ATTRIBUTE_ENCODING_RAW_INLINE) 65 data->SetData(value.data.size, value.data.raw); 66 else 67 data->SetData(value.data.size, value.data.offset); 68 69 data->SetUncompressedSize(value.data.size); 70 71 return B_OK; 72 } 73 74 static status_t Create(AttributeHandlerContext* context, 75 BPackageData* data, const AttributeValue& value, 76 AttributeHandler*& _handler) 77 { 78 DataAttributeHandler* handler = new(std::nothrow) DataAttributeHandler( 79 data); 80 if (handler == NULL) 81 return B_NO_MEMORY; 82 83 InitData(context, data, value); 84 85 _handler = handler; 86 return B_OK; 87 } 88 89 virtual status_t HandleAttribute(AttributeHandlerContext* context, 90 uint8 id, const AttributeValue& value, AttributeHandler** _handler) 91 { 92 switch (id) { 93 case B_HPKG_ATTRIBUTE_ID_DATA_SIZE: 94 fData->SetUncompressedSize(value.unsignedInt); 95 return B_OK; 96 97 case B_HPKG_ATTRIBUTE_ID_DATA_COMPRESSION: 98 { 99 switch (value.unsignedInt) { 100 case B_HPKG_COMPRESSION_NONE: 101 case B_HPKG_COMPRESSION_ZLIB: 102 break; 103 default: 104 context->errorOutput->PrintError("Error: Invalid " 105 "compression type for data (%llu)\n", 106 value.unsignedInt); 107 return B_BAD_DATA; 108 } 109 110 fData->SetCompression(value.unsignedInt); 111 return B_OK; 112 } 113 114 case B_HPKG_ATTRIBUTE_ID_DATA_CHUNK_SIZE: 115 fData->SetChunkSize(value.unsignedInt); 116 return B_OK; 117 } 118 119 return AttributeHandler::HandleAttribute(context, id, value, _handler); 120 } 121 122 private: 123 BPackageData* fData; 124 }; 125 126 127 // #pragma mark - AttributeAttributeHandler 128 129 130 struct PackageReaderImpl::AttributeAttributeHandler : AttributeHandler { 131 AttributeAttributeHandler(BPackageEntry* entry, const char* name) 132 : 133 fEntry(entry), 134 fAttribute(name) 135 { 136 } 137 138 virtual status_t HandleAttribute(AttributeHandlerContext* context, 139 uint8 id, const AttributeValue& value, AttributeHandler** _handler) 140 { 141 switch (id) { 142 case B_HPKG_ATTRIBUTE_ID_DATA: 143 if (_handler != NULL) { 144 return DataAttributeHandler::Create(context, 145 &fAttribute.Data(), value, *_handler); 146 } 147 return DataAttributeHandler::InitData(context, 148 &fAttribute.Data(), value); 149 150 case B_HPKG_ATTRIBUTE_ID_FILE_ATTRIBUTE_TYPE: 151 fAttribute.SetType(value.unsignedInt); 152 return B_OK; 153 } 154 155 return AttributeHandler::HandleAttribute(context, id, value, _handler); 156 } 157 158 virtual status_t Delete(AttributeHandlerContext* context) 159 { 160 status_t error = context->packageContentHandler->HandleEntryAttribute( 161 fEntry, &fAttribute); 162 163 delete this; 164 return error; 165 } 166 167 private: 168 BPackageEntry* fEntry; 169 BPackageEntryAttribute fAttribute; 170 }; 171 172 173 // #pragma mark - EntryAttributeHandler 174 175 176 struct PackageReaderImpl::EntryAttributeHandler : AttributeHandler { 177 EntryAttributeHandler(AttributeHandlerContext* context, 178 BPackageEntry* parentEntry, const char* name) 179 : 180 fEntry(parentEntry, name), 181 fNotified(false) 182 { 183 _SetFileType(context, B_HPKG_DEFAULT_FILE_TYPE); 184 } 185 186 static status_t Create(AttributeHandlerContext* context, 187 BPackageEntry* parentEntry, const char* name, 188 AttributeHandler*& _handler) 189 { 190 // check name 191 if (name[0] == '\0' || strcmp(name, ".") == 0 192 || strcmp(name, "..") == 0 || strchr(name, '/') != NULL) { 193 context->errorOutput->PrintError("Error: Invalid package: Invalid " 194 "entry name: \"%s\"\n", name); 195 return B_BAD_DATA; 196 } 197 198 // create handler 199 EntryAttributeHandler* handler = new(std::nothrow) 200 EntryAttributeHandler(context, parentEntry, name); 201 if (handler == NULL) 202 return B_NO_MEMORY; 203 204 _handler = handler; 205 return B_OK; 206 } 207 208 virtual status_t HandleAttribute(AttributeHandlerContext* context, 209 uint8 id, const AttributeValue& value, AttributeHandler** _handler) 210 { 211 switch (id) { 212 case B_HPKG_ATTRIBUTE_ID_DIRECTORY_ENTRY: 213 { 214 status_t error = _Notify(context); 215 if (error != B_OK) 216 return error; 217 218 //TRACE("%*sentry \"%s\"\n", fLevel * 2, "", value.string); 219 if (_handler != NULL) { 220 return EntryAttributeHandler::Create(context, &fEntry, 221 value.string, *_handler); 222 } 223 return B_OK; 224 } 225 226 case B_HPKG_ATTRIBUTE_ID_FILE_TYPE: 227 return _SetFileType(context, value.unsignedInt); 228 229 case B_HPKG_ATTRIBUTE_ID_FILE_PERMISSIONS: 230 fEntry.SetPermissions(value.unsignedInt); 231 return B_OK; 232 233 case B_HPKG_ATTRIBUTE_ID_FILE_USER: 234 case B_HPKG_ATTRIBUTE_ID_FILE_GROUP: 235 // TODO:... 236 break; 237 238 case B_HPKG_ATTRIBUTE_ID_FILE_ATIME: 239 fEntry.SetAccessTime(value.unsignedInt); 240 return B_OK; 241 242 case B_HPKG_ATTRIBUTE_ID_FILE_MTIME: 243 fEntry.SetModifiedTime(value.unsignedInt); 244 return B_OK; 245 246 case B_HPKG_ATTRIBUTE_ID_FILE_CRTIME: 247 fEntry.SetCreationTime(value.unsignedInt); 248 return B_OK; 249 250 case B_HPKG_ATTRIBUTE_ID_FILE_ATIME_NANOS: 251 fEntry.SetAccessTimeNanos(value.unsignedInt); 252 return B_OK; 253 254 case B_HPKG_ATTRIBUTE_ID_FILE_MTIME_NANOS: 255 fEntry.SetModifiedTimeNanos(value.unsignedInt); 256 return B_OK; 257 258 case B_HPKG_ATTRIBUTE_ID_FILE_CRTIM_NANOS: 259 fEntry.SetCreationTimeNanos(value.unsignedInt); 260 return B_OK; 261 262 case B_HPKG_ATTRIBUTE_ID_FILE_ATTRIBUTE: 263 { 264 status_t error = _Notify(context); 265 if (error != B_OK) 266 return error; 267 268 if (_handler != NULL) { 269 *_handler = new(std::nothrow) AttributeAttributeHandler( 270 &fEntry, value.string); 271 if (*_handler == NULL) 272 return B_NO_MEMORY; 273 return B_OK; 274 } else { 275 BPackageEntryAttribute attribute(value.string); 276 return context->packageContentHandler->HandleEntryAttribute( 277 &fEntry, &attribute); 278 } 279 } 280 281 case B_HPKG_ATTRIBUTE_ID_DATA: 282 if (_handler != NULL) { 283 return DataAttributeHandler::Create(context, &fEntry.Data(), 284 value, *_handler); 285 } 286 return DataAttributeHandler::InitData(context, &fEntry.Data(), 287 value); 288 289 case B_HPKG_ATTRIBUTE_ID_SYMLINK_PATH: 290 fEntry.SetSymlinkPath(value.string); 291 return B_OK; 292 } 293 294 return AttributeHandler::HandleAttribute(context, id, value, _handler); 295 } 296 297 virtual status_t Delete(AttributeHandlerContext* context) 298 { 299 // notify if not done yet 300 status_t error = _Notify(context); 301 302 // notify done 303 if (error == B_OK) 304 error = context->packageContentHandler->HandleEntryDone(&fEntry); 305 else 306 context->packageContentHandler->HandleEntryDone(&fEntry); 307 308 delete this; 309 return error; 310 } 311 312 private: 313 status_t _Notify(AttributeHandlerContext* context) 314 { 315 if (fNotified) 316 return B_OK; 317 318 fNotified = true; 319 return context->packageContentHandler->HandleEntry(&fEntry); 320 } 321 322 status_t _SetFileType(AttributeHandlerContext* context, uint64 fileType) 323 { 324 switch (fileType) { 325 case B_HPKG_FILE_TYPE_FILE: 326 fEntry.SetType(S_IFREG); 327 fEntry.SetPermissions(B_HPKG_DEFAULT_FILE_PERMISSIONS); 328 break; 329 330 case B_HPKG_FILE_TYPE_DIRECTORY: 331 fEntry.SetType(S_IFDIR); 332 fEntry.SetPermissions(B_HPKG_DEFAULT_DIRECTORY_PERMISSIONS); 333 break; 334 335 case B_HPKG_FILE_TYPE_SYMLINK: 336 fEntry.SetType(S_IFLNK); 337 fEntry.SetPermissions(B_HPKG_DEFAULT_SYMLINK_PERMISSIONS); 338 break; 339 340 default: 341 context->errorOutput->PrintError("Error: Invalid file type for " 342 "package entry (%llu)\n", fileType); 343 return B_BAD_DATA; 344 } 345 return B_OK; 346 } 347 348 private: 349 BPackageEntry fEntry; 350 bool fNotified; 351 }; 352 353 354 // #pragma mark - RootAttributeHandler 355 356 357 struct PackageReaderImpl::RootAttributeHandler : PackageAttributeHandler { 358 typedef PackageAttributeHandler inherited; 359 360 virtual status_t HandleAttribute(AttributeHandlerContext* context, 361 uint8 id, const AttributeValue& value, AttributeHandler** _handler) 362 { 363 if (id == B_HPKG_ATTRIBUTE_ID_DIRECTORY_ENTRY) { 364 if (_handler != NULL) { 365 return EntryAttributeHandler::Create(context, NULL, 366 value.string, *_handler); 367 } 368 return B_OK; 369 } 370 371 return inherited::HandleAttribute(context, id, value, _handler); 372 } 373 }; 374 375 376 // #pragma mark - PackageReaderImpl 377 378 379 PackageReaderImpl::PackageReaderImpl(BErrorOutput* errorOutput) 380 : 381 inherited(errorOutput), 382 fTOCSection("TOC") 383 { 384 } 385 386 387 PackageReaderImpl::~PackageReaderImpl() 388 { 389 } 390 391 392 status_t 393 PackageReaderImpl::Init(const char* fileName) 394 { 395 // open file 396 int fd = open(fileName, O_RDONLY); 397 if (fd < 0) { 398 ErrorOutput()->PrintError("Error: Failed to open package file \"%s\": " 399 "%s\n", fileName, strerror(errno)); 400 return errno; 401 } 402 403 return Init(fd, true); 404 } 405 406 407 status_t 408 PackageReaderImpl::Init(int fd, bool keepFD) 409 { 410 status_t error = inherited::Init(fd, keepFD); 411 if (error != B_OK) 412 return error; 413 414 // stat it 415 struct stat st; 416 if (fstat(FD(), &st) < 0) { 417 ErrorOutput()->PrintError("Error: Failed to access package file: %s\n", 418 strerror(errno)); 419 return errno; 420 } 421 422 // read the header 423 hpkg_header header; 424 if ((error = ReadBuffer(0, &header, sizeof(header))) != B_OK) 425 return error; 426 427 // check the header 428 429 // magic 430 if (B_BENDIAN_TO_HOST_INT32(header.magic) != B_HPKG_MAGIC) { 431 ErrorOutput()->PrintError("Error: Invalid package file: Invalid " 432 "magic\n"); 433 return B_BAD_DATA; 434 } 435 436 // version 437 if (B_BENDIAN_TO_HOST_INT16(header.version) != B_HPKG_VERSION) { 438 ErrorOutput()->PrintError("Error: Invalid/unsupported package file " 439 "version (%d)\n", B_BENDIAN_TO_HOST_INT16(header.version)); 440 return B_MISMATCHED_VALUES; 441 } 442 443 // header size 444 fHeapOffset = B_BENDIAN_TO_HOST_INT16(header.header_size); 445 if ((size_t)fHeapOffset < sizeof(hpkg_header)) { 446 ErrorOutput()->PrintError("Error: Invalid package file: Invalid header " 447 "size (%llu)\n", fHeapOffset); 448 return B_BAD_DATA; 449 } 450 451 // total size 452 fTotalSize = B_BENDIAN_TO_HOST_INT64(header.total_size); 453 if (fTotalSize != (uint64)st.st_size) { 454 ErrorOutput()->PrintError("Error: Invalid package file: Total size in " 455 "header (%llu) doesn't agree with total file size (%lld)\n", 456 fTotalSize, st.st_size); 457 return B_BAD_DATA; 458 } 459 460 // package attributes length and compression 461 fPackageAttributesSection.compression 462 = B_BENDIAN_TO_HOST_INT32(header.attributes_compression); 463 fPackageAttributesSection.compressedLength 464 = B_BENDIAN_TO_HOST_INT32(header.attributes_length_compressed); 465 fPackageAttributesSection.uncompressedLength 466 = B_BENDIAN_TO_HOST_INT32(header.attributes_length_uncompressed); 467 fPackageAttributesSection.stringsLength 468 = B_BENDIAN_TO_HOST_INT32(header.attributes_strings_length); 469 fPackageAttributesSection.stringsCount 470 = B_BENDIAN_TO_HOST_INT32(header.attributes_strings_count); 471 472 if (const char* errorString = CheckCompression( 473 fPackageAttributesSection)) { 474 ErrorOutput()->PrintError("Error: Invalid package file: package " 475 "attributes section: %s\n", errorString); 476 return B_BAD_DATA; 477 } 478 479 // TOC length and compression 480 fTOCSection.compression = B_BENDIAN_TO_HOST_INT32(header.toc_compression); 481 fTOCSection.compressedLength 482 = B_BENDIAN_TO_HOST_INT64(header.toc_length_compressed); 483 fTOCSection.uncompressedLength 484 = B_BENDIAN_TO_HOST_INT64(header.toc_length_uncompressed); 485 486 if (const char* errorString = CheckCompression(fTOCSection)) { 487 ErrorOutput()->PrintError("Error: Invalid package file: TOC section: " 488 "%s\n", errorString); 489 return B_BAD_DATA; 490 } 491 492 // TOC subsections 493 fTOCSection.stringsLength 494 = B_BENDIAN_TO_HOST_INT64(header.toc_strings_length); 495 fTOCSection.stringsCount 496 = B_BENDIAN_TO_HOST_INT64(header.toc_strings_count); 497 498 if (fTOCSection.stringsLength > fTOCSection.uncompressedLength 499 || fTOCSection.stringsCount > fTOCSection.stringsLength) { 500 ErrorOutput()->PrintError("Error: Invalid package file: Invalid TOC " 501 "subsections description\n"); 502 return B_BAD_DATA; 503 } 504 505 // check whether the sections fit together 506 if (fPackageAttributesSection.compressedLength > fTotalSize 507 || fTOCSection.compressedLength 508 > fTotalSize - fPackageAttributesSection.compressedLength 509 || fHeapOffset 510 > fTotalSize - fPackageAttributesSection.compressedLength 511 - fTOCSection.compressedLength) { 512 ErrorOutput()->PrintError("Error: Invalid package file: The sum of the " 513 "sections sizes is greater than the package size\n"); 514 return B_BAD_DATA; 515 } 516 517 fPackageAttributesSection.offset 518 = fTotalSize - fPackageAttributesSection.compressedLength; 519 fTOCSection.offset = fPackageAttributesSection.offset 520 - fTOCSection.compressedLength; 521 fHeapSize = fTOCSection.offset - fHeapOffset; 522 523 // TOC size sanity check 524 if (fTOCSection.uncompressedLength > kMaxTOCSize) { 525 ErrorOutput()->PrintError("Error: Package file TOC section size " 526 "is %llu bytes. This is beyond the reader's sanity limit\n", 527 fTOCSection.uncompressedLength); 528 return B_UNSUPPORTED; 529 } 530 531 // package attributes size sanity check 532 if (fPackageAttributesSection.uncompressedLength 533 > kMaxPackageAttributesSize) { 534 ErrorOutput()->PrintError( 535 "Error: Package file package attributes section size " 536 "is %llu bytes. This is beyond the reader's sanity limit\n", 537 fPackageAttributesSection.uncompressedLength); 538 return B_UNSUPPORTED; 539 } 540 541 // read in the complete TOC 542 fTOCSection.data 543 = new(std::nothrow) uint8[fTOCSection.uncompressedLength]; 544 if (fTOCSection.data == NULL) { 545 ErrorOutput()->PrintError("Error: Out of memory!\n"); 546 return B_NO_MEMORY; 547 } 548 error = ReadCompressedBuffer(fTOCSection); 549 if (error != B_OK) 550 return error; 551 552 // read in the complete package attributes section 553 fPackageAttributesSection.data 554 = new(std::nothrow) uint8[fPackageAttributesSection.uncompressedLength]; 555 if (fPackageAttributesSection.data == NULL) { 556 ErrorOutput()->PrintError("Error: Out of memory!\n"); 557 return B_NO_MEMORY; 558 } 559 error = ReadCompressedBuffer(fPackageAttributesSection); 560 if (error != B_OK) 561 return error; 562 563 // start parsing the TOC 564 fTOCSection.currentOffset = 0; 565 SetCurrentSection(&fTOCSection); 566 567 // strings 568 error = ParseStrings(); 569 if (error != B_OK) 570 return error; 571 572 // parse strings from package attributes section 573 fPackageAttributesSection.currentOffset = 0; 574 SetCurrentSection(&fPackageAttributesSection); 575 576 // strings 577 error = ParseStrings(); 578 if (error != B_OK) 579 return error; 580 581 SetCurrentSection(NULL); 582 583 return B_OK; 584 } 585 586 587 status_t 588 PackageReaderImpl::ParseContent(BPackageContentHandler* contentHandler) 589 { 590 AttributeHandlerContext context(ErrorOutput(), contentHandler, 591 B_HPKG_SECTION_PACKAGE_ATTRIBUTES); 592 RootAttributeHandler rootAttributeHandler; 593 594 status_t error 595 = ParsePackageAttributesSection(&context, &rootAttributeHandler); 596 597 if (error == B_OK) { 598 context.section = B_HPKG_SECTION_PACKAGE_TOC; 599 error = _ParseTOC(&context, &rootAttributeHandler); 600 } 601 602 return error; 603 } 604 605 606 status_t 607 PackageReaderImpl::ParseContent(BLowLevelPackageContentHandler* contentHandler) 608 { 609 AttributeHandlerContext context(ErrorOutput(), contentHandler, 610 B_HPKG_SECTION_PACKAGE_ATTRIBUTES); 611 LowLevelAttributeHandler rootAttributeHandler; 612 613 status_t error 614 = ParsePackageAttributesSection(&context, &rootAttributeHandler); 615 616 if (error == B_OK) { 617 context.section = B_HPKG_SECTION_PACKAGE_TOC; 618 error = _ParseTOC(&context, &rootAttributeHandler); 619 } 620 621 return error; 622 } 623 624 625 status_t 626 PackageReaderImpl::_ParseTOC(AttributeHandlerContext* context, 627 AttributeHandler* rootAttributeHandler) 628 { 629 // parse the TOC 630 fTOCSection.currentOffset = fTOCSection.stringsLength; 631 SetCurrentSection(&fTOCSection); 632 633 // prepare attribute handler context 634 context->heapOffset = fHeapOffset; 635 context->heapSize = fHeapSize; 636 637 // init the attribute handler stack 638 rootAttributeHandler->SetLevel(0); 639 ClearAttributeHandlerStack(); 640 PushAttributeHandler(rootAttributeHandler); 641 642 bool sectionHandled; 643 status_t error = ParseAttributeTree(context, sectionHandled); 644 if (error == B_OK && sectionHandled) { 645 if (fTOCSection.currentOffset < fTOCSection.uncompressedLength) { 646 ErrorOutput()->PrintError("Error: %llu excess byte(s) in TOC " 647 "section\n", 648 fTOCSection.uncompressedLength - fTOCSection.currentOffset); 649 error = B_BAD_DATA; 650 } 651 } 652 653 // clean up on error 654 if (error != B_OK) { 655 context->ErrorOccurred(); 656 while (AttributeHandler* handler = PopAttributeHandler()) { 657 if (handler != rootAttributeHandler) 658 handler->Delete(context); 659 } 660 return error; 661 } 662 663 return B_OK; 664 } 665 666 667 status_t 668 PackageReaderImpl::ReadAttributeValue(uint8 type, uint8 encoding, 669 AttributeValue& _value) 670 { 671 switch (type) { 672 case B_HPKG_ATTRIBUTE_TYPE_RAW: 673 { 674 uint64 size; 675 status_t error = ReadUnsignedLEB128(size); 676 if (error != B_OK) 677 return error; 678 679 if (encoding == B_HPKG_ATTRIBUTE_ENCODING_RAW_HEAP) { 680 uint64 offset; 681 error = ReadUnsignedLEB128(offset); 682 if (error != B_OK) 683 return error; 684 685 if (offset > fHeapSize || size > fHeapSize - offset) { 686 ErrorOutput()->PrintError("Error: Invalid %s section: " 687 "invalid data reference\n", CurrentSection()->name); 688 return B_BAD_DATA; 689 } 690 691 _value.SetToData(size, fHeapOffset + offset); 692 } else if (encoding == B_HPKG_ATTRIBUTE_ENCODING_RAW_INLINE) { 693 if (size > B_HPKG_MAX_INLINE_DATA_SIZE) { 694 ErrorOutput()->PrintError("Error: Invalid %s section: " 695 "inline data too long\n", CurrentSection()->name); 696 return B_BAD_DATA; 697 } 698 699 const void* buffer; 700 error = _GetTOCBuffer(size, buffer); 701 if (error != B_OK) 702 return error; 703 _value.SetToData(size, buffer); 704 } else { 705 ErrorOutput()->PrintError("Error: Invalid %s section: invalid " 706 "raw encoding (%u)\n", CurrentSection()->name, encoding); 707 return B_BAD_DATA; 708 } 709 710 return B_OK; 711 } 712 713 default: 714 return inherited::ReadAttributeValue(type, encoding, _value); 715 } 716 } 717 718 719 status_t 720 PackageReaderImpl::_GetTOCBuffer(size_t size, const void*& _buffer) 721 { 722 if (size > fTOCSection.uncompressedLength - fTOCSection.currentOffset) { 723 ErrorOutput()->PrintError("_GetTOCBuffer(%lu): read beyond TOC end\n", 724 size); 725 return B_BAD_DATA; 726 } 727 728 _buffer = fTOCSection.data + fTOCSection.currentOffset; 729 fTOCSection.currentOffset += size; 730 return B_OK; 731 } 732 733 734 } // namespace BPrivate 735 736 } // namespace V1 737 738 } // namespace BHPKG 739 740 } // namespace BPackageKit 741