1 //---------------------------------------------------------------------- 2 // This software is part of the OpenBeOS distribution and is covered 3 // by the OpenBeOS license. 4 //--------------------------------------------------------------------- 5 /*! 6 \file ResourceFile.cpp 7 ResourceFile implementation. 8 */ 9 10 #include <ResourceFile.h> 11 12 #include <algorithm> 13 #include <new> 14 #include <stdio.h> 15 16 #include "Elf.h" 17 #include "Exception.h" 18 #include "Pef.h" 19 #include "ResourceItem.h" 20 #include "ResourcesContainer.h" 21 #include "ResourcesDefs.h" 22 //#include "Warnings.h" 23 24 using namespace std; 25 26 namespace BPrivate { 27 namespace Storage { 28 29 // ELF defs 30 static const uint32 kMaxELFHeaderSize = sizeof(Elf32_Ehdr) + 32; 31 static const char kELFFileMagic[4] = { 0x7f, 'E', 'L', 'F' }; 32 33 // sanity bounds 34 static const uint32 kMaxResourceCount = 10000; 35 static const uint32 kELFMaxResourceAlignment = 1024 * 1024 * 10; // 10 MB 36 37 // recognized file types (indices into kFileTypeNames) 38 enum { 39 FILE_TYPE_UNKNOWN = 0, 40 FILE_TYPE_X86_RESOURCE = 1, 41 FILE_TYPE_PPC_RESOURCE = 2, 42 FILE_TYPE_ELF = 3, 43 FILE_TYPE_PEF = 4, 44 FILE_TYPE_EMPTY = 5, 45 }; 46 47 const char *kFileTypeNames[] = { 48 "unknown", 49 "x86 resource file", 50 "PPC resource file", 51 "ELF object file", 52 "PEF object file", 53 "empty file", 54 }; 55 56 57 // helper functions/classes 58 59 // read_exactly 60 static 61 void 62 read_exactly(BPositionIO &file, off_t position, void *buffer, size_t size, 63 const char *errorMessage = NULL) 64 { 65 ssize_t read = file.ReadAt(position, buffer, size); 66 if (read < 0) 67 throw Exception(read, errorMessage); 68 else if ((size_t)read != size) { 69 if (errorMessage) { 70 throw Exception("%s Read too few bytes (%ld/%lu).", errorMessage, 71 read, size); 72 } else 73 throw Exception("Read too few bytes (%ld/%lu).", read, size); 74 } 75 } 76 77 // write_exactly 78 static 79 void 80 write_exactly(BPositionIO &file, off_t position, const void *buffer, 81 size_t size, const char *errorMessage = NULL) 82 { 83 ssize_t written = file.WriteAt(position, buffer, size); 84 if (written < 0) 85 throw Exception(written, errorMessage); 86 else if ((size_t)written != size) { 87 if (errorMessage) { 88 throw Exception("%s Wrote too few bytes (%ld/%lu).", errorMessage, 89 written, size); 90 } else 91 throw Exception("Wrote too few bytes (%ld/%lu).", written, size); 92 } 93 } 94 95 // align_value 96 template<typename TV, typename TA> 97 static inline 98 TV 99 align_value(const TV &value, const TA &alignment) 100 { 101 return ((value + alignment - 1) / alignment) * alignment; 102 } 103 104 // calculate_checksum 105 static 106 uint32 107 calculate_checksum(const void *data, uint32 size) 108 { 109 uint32 checkSum = 0; 110 const uint8 *csData = (const uint8*)data; 111 const uint8 *dataEnd = csData + size; 112 const uint8 *current = csData; 113 for (; current < dataEnd; current += 4) { 114 uint32 word = 0; 115 int32 bytes = min(4L, (int32)(dataEnd - current)); 116 for (int32 i = 0; i < bytes; i++) 117 word = (word << 8) + current[i]; 118 checkSum += word; 119 } 120 return checkSum; 121 } 122 123 // skip_bytes 124 static inline 125 const void* 126 skip_bytes(const void *buffer, int32 offset) 127 { 128 return (const char*)buffer + offset; 129 } 130 131 // skip_bytes 132 static inline 133 void* 134 skip_bytes(void *buffer, int32 offset) 135 { 136 return (char*)buffer + offset; 137 } 138 139 // fill_pattern 140 static 141 void 142 fill_pattern(uint32 byteOffset, void *_buffer, uint32 count) 143 { 144 uint32 *buffer = (uint32*)_buffer; 145 for (uint32 i = 0; i < count; i++) 146 buffer[i] = kUnusedResourceDataPattern[(byteOffset / 4 + i) % 3]; 147 } 148 149 // fill_pattern 150 static 151 void 152 fill_pattern(const void *dataBegin, void *buffer, uint32 count) 153 { 154 fill_pattern((char*)buffer - (const char*)dataBegin, buffer, count); 155 } 156 157 // fill_pattern 158 static 159 void 160 fill_pattern(const void *dataBegin, void *buffer, const void *bufferEnd) 161 { 162 fill_pattern(dataBegin, buffer, 163 ((const char*)bufferEnd - (char*)buffer) / 4); 164 } 165 166 // check_pattern 167 static 168 bool 169 check_pattern(uint32 byteOffset, void *_buffer, uint32 count, 170 bool hostEndianess) 171 { 172 bool result = true; 173 uint32 *buffer = (uint32*)_buffer; 174 for (uint32 i = 0; result && i < count; i++) { 175 uint32 value = buffer[i]; 176 if (!hostEndianess) 177 value = B_SWAP_INT32(value); 178 result 179 = (value == kUnusedResourceDataPattern[(byteOffset / 4 + i) % 3]); 180 } 181 return result; 182 } 183 184 // MemArea 185 struct MemArea { 186 MemArea(const void *data, uint32 size) : data(data), size(size) {} 187 188 inline bool check(const void *_current, uint32 skip = 0) const 189 { 190 const char *start = (const char*)data; 191 const char *current = (const char*)_current; 192 return (start <= current && start + size >= current + skip); 193 } 194 195 const void* data; 196 uint32 size; 197 }; 198 199 // AutoDeleter 200 template<typename C> 201 struct AutoDeleter { 202 AutoDeleter(C *object, bool array = false) : object(object), array(array) 203 { 204 } 205 206 ~AutoDeleter() 207 { 208 if (array) 209 delete[] object; 210 else 211 delete object; 212 } 213 214 C* object; 215 bool array; 216 }; 217 218 // resource_parse_info 219 struct resource_parse_info { 220 off_t file_size; 221 int32 resource_count; 222 ResourcesContainer *container; 223 char *info_table; 224 uint32 info_table_offset; 225 uint32 info_table_size; 226 }; 227 228 229 // constructor 230 ResourceFile::ResourceFile() 231 : fFile(), 232 fFileType(FILE_TYPE_UNKNOWN), 233 fHostEndianess(true), 234 fEmptyResources(true) 235 { 236 } 237 238 // destructor 239 ResourceFile::~ResourceFile() 240 { 241 Unset(); 242 } 243 244 // SetTo 245 status_t 246 ResourceFile::SetTo(BFile *file, bool clobber) 247 { 248 status_t error = (file ? B_OK : B_BAD_VALUE); 249 Unset(); 250 if (error == B_OK) { 251 try { 252 _InitFile(*file, clobber); 253 } catch (Exception exception) { 254 Unset(); 255 if (exception.Error() != B_OK) 256 error = exception.Error(); 257 else 258 error = B_ERROR; 259 } 260 } 261 return error; 262 } 263 264 // Unset 265 void 266 ResourceFile::Unset() 267 { 268 // file 269 fFile.Unset(); 270 fFileType = FILE_TYPE_UNKNOWN; 271 fHostEndianess = true; 272 fEmptyResources = true; 273 } 274 275 // InitCheck 276 status_t 277 ResourceFile::InitCheck() const 278 { 279 return fFile.InitCheck(); 280 } 281 282 // InitContainer 283 status_t 284 ResourceFile::InitContainer(ResourcesContainer &container) 285 { 286 container.MakeEmpty(); 287 status_t error = InitCheck(); 288 if (error == B_OK && !fEmptyResources) { 289 resource_parse_info parseInfo; 290 parseInfo.file_size = 0; 291 parseInfo.resource_count = 0; 292 parseInfo.container = &container; 293 parseInfo.info_table = NULL; 294 parseInfo.info_table_offset = 0; 295 parseInfo.info_table_size = 0; 296 try { 297 // get the file size 298 error = fFile.GetSize(&parseInfo.file_size); 299 if (error != B_OK) 300 throw Exception(error, "Failed to get the file size."); 301 _ReadHeader(parseInfo); 302 _ReadIndex(parseInfo); 303 _ReadInfoTable(parseInfo); 304 container.SetModified(false); 305 } catch (Exception exception) { 306 if (exception.Error() != B_OK) 307 error = exception.Error(); 308 else 309 error = B_ERROR; 310 } 311 delete[] parseInfo.info_table; 312 } 313 return error; 314 } 315 316 // ReadResource 317 status_t 318 ResourceFile::ReadResource(ResourceItem &resource, bool force) 319 { 320 status_t error = InitCheck(); 321 size_t size = resource.DataSize(); 322 if (error == B_OK && (force || !resource.IsLoaded())) { 323 if (error == B_OK) 324 error = resource.SetSize(size); 325 void *data = NULL; 326 if (error == B_OK) { 327 data = resource.Data(); 328 ssize_t bytesRead = fFile.ReadAt(resource.Offset(), data, size); 329 if (bytesRead < 0) 330 error = bytesRead; 331 else if ((size_t)bytesRead != size) 332 error = B_IO_ERROR; 333 } 334 if (error == B_OK) { 335 // convert the data, if necessary 336 if (!fHostEndianess) 337 swap_data(resource.Type(), data, size, B_SWAP_ALWAYS); 338 resource.SetLoaded(true); 339 resource.SetModified(false); 340 } 341 } 342 return error; 343 } 344 345 // ReadResources 346 status_t 347 ResourceFile::ReadResources(ResourcesContainer &container, bool force) 348 { 349 status_t error = InitCheck(); 350 int32 count = container.CountResources(); 351 for (int32 i = 0; error == B_OK && i < count; i++) { 352 if (ResourceItem *resource = container.ResourceAt(i)) 353 error = ReadResource(*resource, force); 354 else 355 error = B_ERROR; 356 } 357 return error; 358 } 359 360 // WriteResources 361 status_t 362 ResourceFile::WriteResources(ResourcesContainer &container) 363 { 364 status_t error = InitCheck(); 365 if (error == B_OK && !fFile.File()->IsWritable()) 366 error = B_NOT_ALLOWED; 367 if (error == B_OK && fFileType == FILE_TYPE_EMPTY) 368 error = _MakeEmptyResourceFile(); 369 if (error == B_OK) 370 error = _WriteResources(container); 371 if (error == B_OK) 372 fEmptyResources = false; 373 return error; 374 } 375 376 377 // _InitFile 378 void 379 ResourceFile::_InitFile(BFile &file, bool clobber) 380 { 381 status_t error = B_OK; 382 fFile.Unset(); 383 // get the file size first 384 off_t fileSize = 0; 385 error = file.GetSize(&fileSize); 386 if (error != B_OK) 387 throw Exception(error, "Failed to get the file size."); 388 // read the first four bytes, and check, if they identify a resource file 389 char magic[4]; 390 if (fileSize >= 4) 391 read_exactly(file, 0, magic, 4, "Failed to read magic number."); 392 else if (fileSize > 0 && !clobber) 393 throw Exception(B_IO_ERROR, "File is not a resource file."); 394 if (fileSize == 0) { 395 // empty file 396 fHostEndianess = true; 397 fFileType = FILE_TYPE_EMPTY; 398 fFile.SetTo(&file, 0); 399 fEmptyResources = true; 400 } else if (!memcmp(magic, kX86ResourceFileMagic, 4)) { 401 // x86 resource file 402 fHostEndianess = B_HOST_IS_LENDIAN; 403 fFileType = FILE_TYPE_X86_RESOURCE; 404 fFile.SetTo(&file, kX86ResourcesOffset); 405 fEmptyResources = false; 406 } else if (!memcmp(magic, kPEFFileMagic1, 4)) { 407 PEFContainerHeader pefHeader; 408 read_exactly(file, 0, &pefHeader, kPEFContainerHeaderSize, 409 "Failed to read PEF container header."); 410 if (!memcmp(pefHeader.tag2, kPPCResourceFileMagic, 4)) { 411 // PPC resource file 412 fHostEndianess = B_HOST_IS_BENDIAN; 413 fFileType = FILE_TYPE_PPC_RESOURCE; 414 fFile.SetTo(&file, kPPCResourcesOffset); 415 fEmptyResources = false; 416 } else if (!memcmp(pefHeader.tag2, kPEFFileMagic2, 4)) { 417 // PEF file 418 fFileType = FILE_TYPE_PEF; 419 _InitPEFFile(file, pefHeader); 420 } else 421 throw Exception(B_IO_ERROR, "File is not a resource file."); 422 } else if (!memcmp(magic, kELFFileMagic, 4)) { 423 // ELF file 424 fFileType = FILE_TYPE_ELF; 425 _InitELFFile(file); 426 } else if (!memcmp(magic, kX86ResourceFileMagic, 2)) { 427 // x86 resource file with screwed magic? 428 // Warnings::AddCurrentWarning("File magic is 0x%08lx. Should be 0x%08lx " 429 // "for x86 resource file. Try anyway.", 430 // ntohl(*(uint32*)magic), 431 // ntohl(*(uint32*)kX86ResourceFileMagic)); 432 fHostEndianess = B_HOST_IS_LENDIAN; 433 fFileType = FILE_TYPE_X86_RESOURCE; 434 fFile.SetTo(&file, kX86ResourcesOffset); 435 fEmptyResources = true; 436 } else { 437 if (clobber) { 438 // make it an x86 resource file 439 fHostEndianess = true; 440 fFileType = FILE_TYPE_EMPTY; 441 fFile.SetTo(&file, 0); 442 } else 443 throw Exception(B_IO_ERROR, "File is not a resource file."); 444 } 445 error = fFile.InitCheck(); 446 if (error != B_OK) 447 throw Exception(error, "Failed to initialize resource file."); 448 // clobber, if desired 449 if (clobber) { 450 // just write an empty resources container 451 ResourcesContainer container; 452 WriteResources(container); 453 } 454 } 455 456 // _InitELFFile 457 void 458 ResourceFile::_InitELFFile(BFile &file) 459 { 460 status_t error = B_OK; 461 // get the file size 462 off_t fileSize = 0; 463 error = file.GetSize(&fileSize); 464 if (error != B_OK) 465 throw Exception(error, "Failed to get the file size."); 466 // read ELF header 467 Elf32_Ehdr fileHeader; 468 read_exactly(file, 0, &fileHeader, sizeof(Elf32_Ehdr), 469 "Failed to read ELF header."); 470 // check data encoding (endianess) 471 switch (fileHeader.e_ident[EI_DATA]) { 472 case ELFDATA2LSB: 473 fHostEndianess = B_HOST_IS_LENDIAN; 474 break; 475 case ELFDATA2MSB: 476 fHostEndianess = B_HOST_IS_BENDIAN; 477 break; 478 default: 479 case ELFDATANONE: 480 throw Exception(B_IO_ERROR, "Unsupported ELF data encoding."); 481 break; 482 } 483 // get the header values 484 uint32 headerSize = _GetUInt16(fileHeader.e_ehsize); 485 uint32 programHeaderTableOffset = _GetUInt32(fileHeader.e_phoff); 486 uint32 programHeaderSize = _GetUInt16(fileHeader.e_phentsize); 487 uint32 programHeaderCount = _GetUInt16(fileHeader.e_phnum); 488 uint32 sectionHeaderTableOffset = _GetUInt32(fileHeader.e_shoff); 489 uint32 sectionHeaderSize = _GetUInt16(fileHeader.e_shentsize); 490 uint32 sectionHeaderCount = _GetUInt16(fileHeader.e_shnum); 491 bool hasProgramHeaderTable = (programHeaderTableOffset != 0); 492 bool hasSectionHeaderTable = (sectionHeaderTableOffset != 0); 493 // check the sanity of the header values 494 // ELF header size 495 if (headerSize < sizeof(Elf32_Ehdr) || headerSize > kMaxELFHeaderSize) { 496 throw Exception(B_IO_ERROR, 497 "Invalid ELF header: invalid ELF header size: %lu.", 498 headerSize); 499 } 500 uint32 resourceOffset = headerSize; 501 uint32 resourceAlignment = 0; 502 // program header table offset and entry count/size 503 uint32 programHeaderTableSize = 0; 504 if (hasProgramHeaderTable) { 505 if (programHeaderTableOffset < headerSize 506 || programHeaderTableOffset > fileSize) { 507 throw Exception(B_IO_ERROR, "Invalid ELF header: invalid program " 508 "header table offset: %lu.", 509 programHeaderTableOffset); 510 } 511 programHeaderTableSize = programHeaderSize * programHeaderCount; 512 if (programHeaderSize < sizeof(Elf32_Phdr) 513 || programHeaderTableOffset + programHeaderTableSize > fileSize) { 514 throw Exception(B_IO_ERROR, "Invalid ELF header: program header " 515 "table exceeds file: %lu.", 516 programHeaderTableOffset + programHeaderTableSize); 517 } 518 resourceOffset = max(resourceOffset, programHeaderTableOffset 519 + programHeaderTableSize); 520 // iterate through the program headers 521 for (int32 i = 0; i < (int32)programHeaderCount; i++) { 522 uint32 shOffset = programHeaderTableOffset + i * programHeaderSize; 523 Elf32_Phdr programHeader; 524 read_exactly(file, shOffset, &programHeader, sizeof(Elf32_Shdr), 525 "Failed to read ELF program header."); 526 // get the header values 527 uint32 type = _GetUInt32(programHeader.p_type); 528 uint32 offset = _GetUInt32(programHeader.p_offset); 529 uint32 size = _GetUInt32(programHeader.p_filesz); 530 uint32 alignment = _GetUInt32(programHeader.p_align); 531 // check the values 532 // PT_NULL marks the header unused, 533 if (type != PT_NULL) { 534 if (/*offset < headerSize ||*/ offset > fileSize) { 535 throw Exception(B_IO_ERROR, "Invalid ELF program header: " 536 "invalid program offset: %lu.", offset); 537 } 538 uint32 segmentEnd = offset + size; 539 if (segmentEnd > fileSize) { 540 throw Exception(B_IO_ERROR, "Invalid ELF section header: " 541 "segment exceeds file: %lu.", segmentEnd); 542 } 543 resourceOffset = max(resourceOffset, segmentEnd); 544 resourceAlignment = max(resourceAlignment, alignment); 545 } 546 } 547 } 548 // section header table offset and entry count/size 549 uint32 sectionHeaderTableSize = 0; 550 if (hasSectionHeaderTable) { 551 if (sectionHeaderTableOffset < headerSize 552 || sectionHeaderTableOffset > fileSize) { 553 throw Exception(B_IO_ERROR, "Invalid ELF header: invalid section " 554 "header table offset: %lu.", 555 sectionHeaderTableOffset); 556 } 557 sectionHeaderTableSize = sectionHeaderSize * sectionHeaderCount; 558 if (sectionHeaderSize < sizeof(Elf32_Shdr) 559 || sectionHeaderTableOffset + sectionHeaderTableSize > fileSize) { 560 throw Exception(B_IO_ERROR, "Invalid ELF header: section header " 561 "table exceeds file: %lu.", 562 sectionHeaderTableOffset + sectionHeaderTableSize); 563 } 564 resourceOffset = max(resourceOffset, sectionHeaderTableOffset 565 + sectionHeaderTableSize); 566 // iterate through the section headers 567 for (int32 i = 0; i < (int32)sectionHeaderCount; i++) { 568 uint32 shOffset = sectionHeaderTableOffset + i * sectionHeaderSize; 569 Elf32_Shdr sectionHeader; 570 read_exactly(file, shOffset, §ionHeader, sizeof(Elf32_Shdr), 571 "Failed to read ELF section header."); 572 // get the header values 573 uint32 type = _GetUInt32(sectionHeader.sh_type); 574 uint32 offset = _GetUInt32(sectionHeader.sh_offset); 575 uint32 size = _GetUInt32(sectionHeader.sh_size); 576 // check the values 577 // SHT_NULL marks the header unused, 578 // SHT_NOBITS sections take no space in the file 579 if (type != SHT_NULL && type != SHT_NOBITS) { 580 if (offset < headerSize || offset > fileSize) { 581 throw Exception(B_IO_ERROR, "Invalid ELF section header: " 582 "invalid section offset: %lu.", offset); 583 } 584 uint32 sectionEnd = offset + size; 585 if (sectionEnd > fileSize) { 586 throw Exception(B_IO_ERROR, "Invalid ELF section header: " 587 "section exceeds file: %lu.", sectionEnd); 588 } 589 resourceOffset = max(resourceOffset, sectionEnd); 590 } 591 } 592 } 593 // align the offset 594 if (resourceAlignment < kELFMinResourceAlignment) 595 resourceAlignment = kELFMinResourceAlignment; 596 if (resourceAlignment > kELFMaxResourceAlignment) { 597 throw Exception(B_IO_ERROR, "The ELF object file requires an invalid " 598 "alignment: %lu.", resourceAlignment); 599 } 600 resourceOffset = align_value(resourceOffset, resourceAlignment); 601 if (resourceOffset >= fileSize) { 602 // throw Exception("The ELF object file does not contain resources."); 603 fEmptyResources = true; 604 } else 605 fEmptyResources = false; 606 // fine, init the offset file 607 fFile.SetTo(&file, resourceOffset); 608 } 609 610 // _InitPEFFile 611 void 612 ResourceFile::_InitPEFFile(BFile &file, const PEFContainerHeader &pefHeader) 613 { 614 status_t error = B_OK; 615 // get the file size 616 off_t fileSize = 0; 617 error = file.GetSize(&fileSize); 618 if (error != B_OK) 619 throw Exception(error, "Failed to get the file size."); 620 // check architecture -- we support PPC only 621 if (memcmp(pefHeader.architecture, kPEFArchitecturePPC, 4)) 622 throw Exception(B_IO_ERROR, "PEF file architecture is not PPC."); 623 fHostEndianess = B_HOST_IS_BENDIAN; 624 // get the section count 625 uint16 sectionCount = _GetUInt16(pefHeader.sectionCount); 626 // iterate through the PEF sections headers 627 uint32 sectionHeaderTableOffset = kPEFContainerHeaderSize; 628 uint32 sectionHeaderTableEnd 629 = sectionHeaderTableOffset + sectionCount * kPEFSectionHeaderSize; 630 uint32 resourceOffset = sectionHeaderTableEnd; 631 for (int32 i = 0; i < (int32)sectionCount; i++) { 632 uint32 shOffset = sectionHeaderTableOffset + i * kPEFSectionHeaderSize; 633 PEFSectionHeader sectionHeader; 634 read_exactly(file, shOffset, §ionHeader, kPEFSectionHeaderSize, 635 "Failed to read PEF section header."); 636 // get the header values 637 uint32 offset = _GetUInt32(sectionHeader.containerOffset); 638 uint32 size = _GetUInt32(sectionHeader.packedSize); 639 // check the values 640 if (offset < sectionHeaderTableEnd || offset > fileSize) { 641 throw Exception(B_IO_ERROR, "Invalid PEF section header: invalid " 642 "section offset: %lu.", offset); 643 } 644 uint32 sectionEnd = offset + size; 645 if (sectionEnd > fileSize) { 646 throw Exception(B_IO_ERROR, "Invalid PEF section header: section " 647 "exceeds file: %lu.", sectionEnd); 648 } 649 resourceOffset = max(resourceOffset, sectionEnd); 650 } 651 if (resourceOffset >= fileSize) { 652 // throw Exception("The PEF object file does not contain resources."); 653 fEmptyResources = true; 654 } else 655 fEmptyResources = false; 656 // init the offset file 657 fFile.SetTo(&file, resourceOffset); 658 } 659 660 // _ReadHeader 661 void 662 ResourceFile::_ReadHeader(resource_parse_info &parseInfo) 663 { 664 // read the header 665 resources_header header; 666 read_exactly(fFile, 0, &header, kResourcesHeaderSize, 667 "Failed to read the header."); 668 // check the header 669 // magic 670 uint32 magic = _GetUInt32(header.rh_resources_magic); 671 if (magic == kResourcesHeaderMagic) { 672 // everything is fine 673 } else if (B_SWAP_INT32(magic) == kResourcesHeaderMagic) { 674 // const char *endianessStr[2] = { "little", "big" }; 675 // int32 endianess 676 // = (fHostEndianess == ((bool)B_HOST_IS_LENDIAN ? 0 : 1)); 677 // Warnings::AddCurrentWarning("Endianess seems to be %s, although %s " 678 // "was expected.", 679 // endianessStr[1 - endianess], 680 // endianessStr[endianess]); 681 fHostEndianess = !fHostEndianess; 682 } else 683 throw Exception(B_IO_ERROR, "Invalid resources header magic."); 684 // resource count 685 uint32 resourceCount = _GetUInt32(header.rh_resource_count); 686 if (resourceCount > kMaxResourceCount) 687 throw Exception(B_IO_ERROR, "Bad number of resources."); 688 // index section offset 689 uint32 indexSectionOffset = _GetUInt32(header.rh_index_section_offset); 690 if (indexSectionOffset != kResourceIndexSectionOffset) { 691 throw Exception(B_IO_ERROR, "Unexpected resource index section " 692 "offset. Is: %lu, should be: %lu.", indexSectionOffset, 693 kResourceIndexSectionOffset); 694 } 695 // admin section size 696 uint32 indexSectionSize = kResourceIndexSectionHeaderSize 697 + kResourceIndexEntrySize * resourceCount; 698 indexSectionSize = align_value(indexSectionSize, 699 kResourceIndexSectionAlignment); 700 uint32 adminSectionSize = _GetUInt32(header.rh_admin_section_size); 701 if (adminSectionSize != indexSectionOffset + indexSectionSize) { 702 throw Exception(B_IO_ERROR, "Unexpected resource admin section size. " 703 "Is: %lu, should be: %lu.", adminSectionSize, 704 indexSectionOffset + indexSectionSize); 705 } 706 // set the resource count 707 parseInfo.resource_count = resourceCount; 708 } 709 710 // _ReadIndex 711 void 712 ResourceFile::_ReadIndex(resource_parse_info &parseInfo) 713 { 714 int32 &resourceCount = parseInfo.resource_count; 715 off_t &fileSize = parseInfo.file_size; 716 // read the header 717 resource_index_section_header header; 718 read_exactly(fFile, kResourceIndexSectionOffset, &header, 719 kResourceIndexSectionHeaderSize, 720 "Failed to read the resource index section header."); 721 // check the header 722 // index section offset 723 uint32 indexSectionOffset = _GetUInt32(header.rish_index_section_offset); 724 if (indexSectionOffset != kResourceIndexSectionOffset) { 725 throw Exception(B_IO_ERROR, "Unexpected resource index section " 726 "offset. Is: %lu, should be: %lu.", indexSectionOffset, 727 kResourceIndexSectionOffset); 728 } 729 // index section size 730 uint32 expectedIndexSectionSize = kResourceIndexSectionHeaderSize 731 + kResourceIndexEntrySize * resourceCount; 732 expectedIndexSectionSize = align_value(expectedIndexSectionSize, 733 kResourceIndexSectionAlignment); 734 uint32 indexSectionSize = _GetUInt32(header.rish_index_section_size); 735 if (indexSectionSize != expectedIndexSectionSize) { 736 throw Exception(B_IO_ERROR, "Unexpected resource index section size. " 737 "Is: %lu, should be: %lu.", indexSectionSize, 738 expectedIndexSectionSize); 739 } 740 // unknown section offset 741 uint32 unknownSectionOffset 742 = _GetUInt32(header.rish_unknown_section_offset); 743 if (unknownSectionOffset != indexSectionOffset + indexSectionSize) { 744 throw Exception(B_IO_ERROR, "Unexpected resource index section size. " 745 "Is: %lu, should be: %lu.", unknownSectionOffset, 746 indexSectionOffset + indexSectionSize); 747 } 748 // unknown section size 749 uint32 unknownSectionSize = _GetUInt32(header.rish_unknown_section_size); 750 if (unknownSectionSize != kUnknownResourceSectionSize) { 751 throw Exception(B_IO_ERROR, "Unexpected resource index section " 752 "offset. Is: %lu, should be: %lu.", 753 unknownSectionOffset, kUnknownResourceSectionSize); 754 } 755 // info table offset and size 756 uint32 infoTableOffset = _GetUInt32(header.rish_info_table_offset); 757 uint32 infoTableSize = _GetUInt32(header.rish_info_table_size); 758 if (infoTableOffset + infoTableSize > fileSize) 759 throw Exception(B_IO_ERROR, "Invalid info table location."); 760 parseInfo.info_table_offset = infoTableOffset; 761 parseInfo.info_table_size = infoTableSize; 762 // read the index entries 763 uint32 indexTableOffset = indexSectionOffset 764 + kResourceIndexSectionHeaderSize; 765 int32 maxResourceCount = (unknownSectionOffset - indexTableOffset) 766 / kResourceIndexEntrySize; 767 int32 actualResourceCount = 0; 768 bool tableEndReached = false; 769 for (int32 i = 0; !tableEndReached && i < maxResourceCount; i++) { 770 // read one entry 771 tableEndReached = !_ReadIndexEntry(parseInfo, i, indexTableOffset, 772 (i >= resourceCount)); 773 if (!tableEndReached) 774 actualResourceCount++; 775 } 776 // check resource count 777 if (actualResourceCount != resourceCount) { 778 if (actualResourceCount > resourceCount) { 779 // Warnings::AddCurrentWarning("Resource index table contains " 780 // "%ld entries, although it should be " 781 // "%ld only.", actualResourceCount, 782 // resourceCount); 783 } 784 resourceCount = actualResourceCount; 785 } 786 } 787 788 // _ReadIndexEntry 789 bool 790 ResourceFile::_ReadIndexEntry(resource_parse_info &parseInfo, int32 index, 791 uint32 tableOffset, bool peekAhead) 792 { 793 off_t &fileSize = parseInfo.file_size; 794 // 795 bool result = true; 796 resource_index_entry entry; 797 // read one entry 798 off_t entryOffset = tableOffset + index * kResourceIndexEntrySize; 799 read_exactly(fFile, entryOffset, &entry, kResourceIndexEntrySize, 800 "Failed to read a resource index entry."); 801 // check, if the end is reached early 802 if (result && check_pattern(entryOffset, &entry, 803 kResourceIndexEntrySize / 4, fHostEndianess)) { 804 if (!peekAhead) { 805 // Warnings::AddCurrentWarning("Unexpected end of resource index " 806 // "table at index: %ld (/%ld).", 807 // index + 1, resourceCount); 808 } 809 result = false; 810 } 811 uint32 offset = _GetUInt32(entry.rie_offset); 812 uint32 size = _GetUInt32(entry.rie_size); 813 // check the location 814 if (result && offset + size > fileSize) { 815 if (peekAhead) { 816 // Warnings::AddCurrentWarning("Invalid data after resource index " 817 // "table."); 818 } else { 819 throw Exception(B_IO_ERROR, "Invalid resource index entry: index: " 820 "%ld, offset: %lu (%lx), size: %lu (%lx).", 821 index + 1, offset, offset, size, size); 822 } 823 result = false; 824 } 825 // add the entry 826 if (result) { 827 ResourceItem *item = new(nothrow) ResourceItem; 828 if (!item) 829 throw Exception(B_NO_MEMORY); 830 item->SetLocation(offset, size); 831 if (!parseInfo.container->AddResource(item, index, false)) { 832 delete item; 833 throw Exception(B_NO_MEMORY); 834 } 835 } 836 return result; 837 } 838 839 // _ReadInfoTable 840 void 841 ResourceFile::_ReadInfoTable(resource_parse_info &parseInfo) 842 { 843 int32 &resourceCount = parseInfo.resource_count; 844 // read the info table 845 // alloc memory for the table 846 char *tableData = new(nothrow) char[parseInfo.info_table_size]; 847 if (!tableData) 848 throw Exception(B_NO_MEMORY); 849 int32 dataSize = parseInfo.info_table_size; 850 parseInfo.info_table = tableData; // freed by the info owner 851 read_exactly(fFile, parseInfo.info_table_offset, tableData, dataSize, 852 "Failed to read resource info table."); 853 // 854 bool *readIndices = new(nothrow) bool[resourceCount + 1]; 855 // + 1 => always > 0 856 if (!readIndices) 857 throw Exception(B_NO_MEMORY); 858 AutoDeleter<bool> readIndicesDeleter(readIndices, true); 859 for (int32 i = 0; i < resourceCount; i++) 860 readIndices[i] = false; 861 MemArea area(tableData, dataSize); 862 const void *data = tableData; 863 // check the table end/check sum 864 if (_ReadInfoTableEnd(data, dataSize)) 865 dataSize -= kResourceInfoTableEndSize; 866 // read the infos 867 int32 resourceIndex = 1; 868 uint32 minRemainderSize 869 = kMinResourceInfoBlockSize + kResourceInfoSeparatorSize; 870 while (area.check(data, minRemainderSize)) { 871 // read a resource block 872 if (!area.check(data, kMinResourceInfoBlockSize)) { 873 throw Exception(B_IO_ERROR, "Unexpected end of resource info " 874 "table at index %ld.", resourceIndex); 875 } 876 const resource_info_block *infoBlock 877 = (const resource_info_block*)data; 878 type_code type = _GetUInt32(infoBlock->rib_type); 879 // read the infos of this block 880 const resource_info *info = infoBlock->rib_info; 881 while (info) { 882 data = _ReadResourceInfo(parseInfo, area, info, type, readIndices); 883 // prepare for next iteration, if there is another info 884 if (!area.check(data, kResourceInfoSeparatorSize)) { 885 throw Exception(B_IO_ERROR, "Unexpected end of resource info " 886 "table after index %ld.", resourceIndex); 887 } 888 const resource_info_separator *separator 889 = (const resource_info_separator*)data; 890 if (_GetUInt32(separator->ris_value1) == 0xffffffff 891 && _GetUInt32(separator->ris_value2) == 0xffffffff) { 892 // info block ends 893 info = NULL; 894 data = skip_bytes(data, kResourceInfoSeparatorSize); 895 } else { 896 // another info follows 897 info = (const resource_info*)data; 898 } 899 resourceIndex++; 900 } 901 // end of the info block 902 } 903 // handle special case: empty resource info table 904 if (resourceIndex == 1) { 905 if (!area.check(data, kResourceInfoSeparatorSize)) { 906 throw Exception(B_IO_ERROR, "Unexpected end of resource info " 907 "table."); 908 } 909 const resource_info_separator *tableTerminator 910 = (const resource_info_separator*)data; 911 if (_GetUInt32(tableTerminator->ris_value1) != 0xffffffff 912 || _GetUInt32(tableTerminator->ris_value2) != 0xffffffff) { 913 throw Exception(B_IO_ERROR, "The resource info table ought to be " 914 "empty, but is not properly terminated."); 915 } 916 data = skip_bytes(data, kResourceInfoSeparatorSize); 917 } 918 // Check, if the correct number of bytes are remaining. 919 uint32 bytesLeft = (const char*)tableData + dataSize - (const char*)data; 920 if (bytesLeft != 0) { 921 throw Exception(B_IO_ERROR, "Error at the end of the resource info " 922 "table: %lu bytes are remaining.", bytesLeft); 923 } 924 // check, if all items have been initialized 925 for (int32 i = resourceCount - 1; i >= 0; i--) { 926 if (!readIndices[i]) { 927 // Warnings::AddCurrentWarning("Resource item at index %ld " 928 // "has no info. Item removed.", i + 1); 929 if (ResourceItem *item = parseInfo.container->RemoveResource(i)) 930 delete item; 931 resourceCount--; 932 } 933 } 934 } 935 936 // _ReadInfoTableEnd 937 bool 938 ResourceFile::_ReadInfoTableEnd(const void *data, int32 dataSize) 939 { 940 bool hasTableEnd = true; 941 if ((uint32)dataSize < kResourceInfoSeparatorSize) 942 throw Exception(B_IO_ERROR, "Info table is too short."); 943 if ((uint32)dataSize < kResourceInfoTableEndSize) 944 hasTableEnd = false; 945 if (hasTableEnd) { 946 const resource_info_table_end *tableEnd 947 = (const resource_info_table_end*) 948 skip_bytes(data, dataSize - kResourceInfoTableEndSize); 949 if (_GetInt32(tableEnd->rite_terminator) != 0) 950 hasTableEnd = false; 951 if (hasTableEnd) { 952 dataSize -= kResourceInfoTableEndSize; 953 // checksum 954 uint32 checkSum = calculate_checksum(data, dataSize); 955 uint32 fileCheckSum = _GetUInt32(tableEnd->rite_check_sum); 956 if (checkSum != fileCheckSum) { 957 throw Exception(B_IO_ERROR, "Invalid resource info table check" 958 " sum: In file: %lx, calculated: %lx.", 959 fileCheckSum, checkSum); 960 } 961 } 962 } 963 // if (!hasTableEnd) 964 // Warnings::AddCurrentWarning("resource info table has no check sum."); 965 return hasTableEnd; 966 } 967 968 // _ReadResourceInfo 969 const void* 970 ResourceFile::_ReadResourceInfo(resource_parse_info &parseInfo, 971 const MemArea &area, const resource_info *info, 972 type_code type, bool *readIndices) 973 { 974 int32 &resourceCount = parseInfo.resource_count; 975 // 976 int32 id = _GetInt32(info->ri_id); 977 int32 index = _GetInt32(info->ri_index); 978 uint16 nameSize = _GetUInt16(info->ri_name_size); 979 const char *name = info->ri_name; 980 // check the values 981 bool ignore = false; 982 // index 983 if (index < 1 || index > resourceCount) { 984 // Warnings::AddCurrentWarning("Invalid index field in resource " 985 // "info table: %lu.", index); 986 ignore = true; 987 } 988 if (!ignore) { 989 if (readIndices[index - 1]) { 990 throw Exception(B_IO_ERROR, "Multiple resource infos with the " 991 "same index field: %ld.", index); 992 } 993 readIndices[index - 1] = true; 994 } 995 // name size 996 if (!area.check(name, nameSize)) { 997 throw Exception(B_IO_ERROR, "Invalid name size (%d) for index %ld in " 998 "resource info table.", (int)nameSize, index); 999 } 1000 // check, if name is null terminated 1001 if (name[nameSize - 1] != 0) { 1002 // Warnings::AddCurrentWarning("Name for index %ld in " 1003 // "resource info table is not null " 1004 // "terminated.", index); 1005 } 1006 // set the values 1007 if (!ignore) { 1008 BString resourceName(name, nameSize); 1009 if (ResourceItem *item = parseInfo.container->ResourceAt(index - 1)) 1010 item->SetIdentity(type, id, resourceName.String()); 1011 else { 1012 throw Exception(B_IO_ERROR, "Unexpected error: No resource item " 1013 "at index %ld.", index); 1014 } 1015 } 1016 return skip_bytes(name, nameSize); 1017 } 1018 1019 // _WriteResources 1020 status_t 1021 ResourceFile::_WriteResources(ResourcesContainer &container) 1022 { 1023 status_t error = B_OK; 1024 int32 resourceCount = container.CountResources(); 1025 char *buffer = NULL; 1026 try { 1027 // calculate sizes and offsets 1028 // header 1029 uint32 size = kResourcesHeaderSize; 1030 size_t bufferSize = size; 1031 // index section 1032 uint32 indexSectionOffset = size; 1033 uint32 indexSectionSize = kResourceIndexSectionHeaderSize 1034 + resourceCount * kResourceIndexEntrySize; 1035 indexSectionSize = align_value(indexSectionSize, 1036 kResourceIndexSectionAlignment); 1037 size += indexSectionSize; 1038 bufferSize = max((uint32)bufferSize, indexSectionSize); 1039 // unknown section 1040 uint32 unknownSectionOffset = size; 1041 uint32 unknownSectionSize = kUnknownResourceSectionSize; 1042 size += unknownSectionSize; 1043 bufferSize = max((uint32)bufferSize, unknownSectionSize); 1044 // data 1045 uint32 dataOffset = size; 1046 uint32 dataSize = 0; 1047 for (int32 i = 0; i < resourceCount; i++) { 1048 ResourceItem *item = container.ResourceAt(i); 1049 if (!item->IsLoaded()) 1050 throw Exception(B_IO_ERROR, "Resource is not loaded."); 1051 dataSize += item->DataSize(); 1052 bufferSize = max(bufferSize, item->DataSize()); 1053 } 1054 size += dataSize; 1055 // info table 1056 uint32 infoTableOffset = size; 1057 uint32 infoTableSize = 0; 1058 type_code type = 0; 1059 for (int32 i = 0; i < resourceCount; i++) { 1060 ResourceItem *item = container.ResourceAt(i); 1061 if (i == 0 || type != item->Type()) { 1062 if (i != 0) 1063 infoTableSize += kResourceInfoSeparatorSize; 1064 type = item->Type(); 1065 infoTableSize += kMinResourceInfoBlockSize; 1066 } else 1067 infoTableSize += kMinResourceInfoSize; 1068 if (const char *name = item->Name()) 1069 infoTableSize += strlen(name) + 1; 1070 } 1071 infoTableSize += kResourceInfoSeparatorSize 1072 + kResourceInfoTableEndSize; 1073 size += infoTableSize; 1074 bufferSize = max((uint32)bufferSize, infoTableSize); 1075 1076 // write... 1077 // set the file size 1078 fFile.SetSize(size); 1079 buffer = new(nothrow) char[bufferSize]; 1080 if (!buffer) 1081 throw Exception(B_NO_MEMORY); 1082 void *data = buffer; 1083 // header 1084 resources_header *resourcesHeader = (resources_header*)data; 1085 resourcesHeader->rh_resources_magic = kResourcesHeaderMagic; 1086 resourcesHeader->rh_resource_count = resourceCount; 1087 resourcesHeader->rh_index_section_offset = indexSectionOffset; 1088 resourcesHeader->rh_admin_section_size = indexSectionOffset 1089 + indexSectionSize; 1090 for (int32 i = 0; i < 13; i++) 1091 resourcesHeader->rh_pad[i] = 0; 1092 write_exactly(fFile, 0, buffer, kResourcesHeaderSize, 1093 "Failed to write resources header."); 1094 // index section 1095 data = buffer; 1096 // header 1097 resource_index_section_header *indexHeader 1098 = (resource_index_section_header*)data; 1099 indexHeader->rish_index_section_offset = indexSectionOffset; 1100 indexHeader->rish_index_section_size = indexSectionSize; 1101 indexHeader->rish_unknown_section_offset = unknownSectionOffset; 1102 indexHeader->rish_unknown_section_size = unknownSectionSize; 1103 indexHeader->rish_info_table_offset = infoTableOffset; 1104 indexHeader->rish_info_table_size = infoTableSize; 1105 fill_pattern(buffer - indexSectionOffset, 1106 &indexHeader->rish_unused_data1, 1); 1107 fill_pattern(buffer - indexSectionOffset, 1108 indexHeader->rish_unused_data2, 25); 1109 fill_pattern(buffer - indexSectionOffset, 1110 &indexHeader->rish_unused_data3, 1); 1111 // index table 1112 data = skip_bytes(data, kResourceIndexSectionHeaderSize); 1113 resource_index_entry *entry = (resource_index_entry*)data; 1114 uint32 entryOffset = dataOffset; 1115 for (int32 i = 0; i < resourceCount; i++, entry++) { 1116 ResourceItem *item = container.ResourceAt(i); 1117 uint32 entrySize = item->DataSize(); 1118 entry->rie_offset = entryOffset; 1119 entry->rie_size = entrySize; 1120 entry->rie_pad = 0; 1121 entryOffset += entrySize; 1122 } 1123 fill_pattern(buffer - indexSectionOffset, entry, 1124 buffer + indexSectionSize); 1125 write_exactly(fFile, indexSectionOffset, buffer, indexSectionSize, 1126 "Failed to write index section."); 1127 // unknown section 1128 fill_pattern(unknownSectionOffset, buffer, unknownSectionSize / 4); 1129 write_exactly(fFile, unknownSectionOffset, buffer, unknownSectionSize, 1130 "Failed to write unknown section."); 1131 // data 1132 uint32 itemOffset = dataOffset; 1133 for (int32 i = 0; i < resourceCount; i++) { 1134 data = buffer; 1135 ResourceItem *item = container.ResourceAt(i); 1136 const void *itemData = item->Data(); 1137 uint32 itemSize = item->DataSize(); 1138 if (!itemData && itemSize > 0) 1139 throw Exception(error, "Invalid resource item data."); 1140 if (itemData) { 1141 // swap data, if necessary 1142 if (!fHostEndianess) { 1143 memcpy(data, itemData, itemSize); 1144 swap_data(item->Type(), data, itemSize, B_SWAP_ALWAYS); 1145 itemData = data; 1146 } 1147 write_exactly(fFile, itemOffset, itemData, itemSize, 1148 "Failed to write resource item data."); 1149 } 1150 item->SetOffset(itemOffset); 1151 itemOffset += itemSize; 1152 } 1153 // info table 1154 data = buffer; 1155 type = 0; 1156 for (int32 i = 0; i < resourceCount; i++) { 1157 ResourceItem *item = container.ResourceAt(i); 1158 resource_info *info = NULL; 1159 if (i == 0 || type != item->Type()) { 1160 if (i != 0) { 1161 resource_info_separator *separator 1162 = (resource_info_separator*)data; 1163 separator->ris_value1 = 0xffffffff; 1164 separator->ris_value2 = 0xffffffff; 1165 data = skip_bytes(data, kResourceInfoSeparatorSize); 1166 } 1167 type = item->Type(); 1168 resource_info_block *infoBlock = (resource_info_block*)data; 1169 infoBlock->rib_type = type; 1170 info = infoBlock->rib_info; 1171 } else 1172 info = (resource_info*)data; 1173 // info 1174 info->ri_id = item->ID(); 1175 info->ri_index = i + 1; 1176 info->ri_name_size = 0; 1177 data = info->ri_name; 1178 if (const char *name = item->Name()) { 1179 uint32 nameLen = strlen(name); 1180 memcpy(info->ri_name, name, nameLen + 1); 1181 data = skip_bytes(data, nameLen + 1); 1182 info->ri_name_size = nameLen + 1; 1183 } 1184 } 1185 // separator 1186 resource_info_separator *separator = (resource_info_separator*)data; 1187 separator->ris_value1 = 0xffffffff; 1188 separator->ris_value2 = 0xffffffff; 1189 // table end 1190 data = skip_bytes(data, kResourceInfoSeparatorSize); 1191 resource_info_table_end *tableEnd = (resource_info_table_end*)data; 1192 tableEnd->rite_check_sum = calculate_checksum(buffer, 1193 infoTableSize - kResourceInfoTableEndSize); 1194 tableEnd->rite_terminator = 0; 1195 write_exactly(fFile, infoTableOffset, buffer, infoTableSize, 1196 "Failed to write info table."); 1197 } catch (Exception exception) { 1198 if (exception.Error() != B_OK) 1199 error = exception.Error(); 1200 else 1201 error = B_ERROR; 1202 } 1203 delete[] buffer; 1204 return error; 1205 } 1206 1207 // _MakeEmptyResourceFile 1208 status_t 1209 ResourceFile::_MakeEmptyResourceFile() 1210 { 1211 status_t error = fFile.InitCheck(); 1212 if (error == B_OK && !fFile.File()->IsWritable()) 1213 error = B_NOT_ALLOWED; 1214 if (error == B_OK) { 1215 try { 1216 BFile *file = fFile.File(); 1217 // make it an x86 resource file 1218 error = file->SetSize(4); 1219 if (error != B_OK) 1220 throw Exception(error, "Failed to set file size."); 1221 write_exactly(*file, 0, kX86ResourceFileMagic, 4, 1222 "Failed to write magic number."); 1223 fHostEndianess = B_HOST_IS_LENDIAN; 1224 fFileType = FILE_TYPE_X86_RESOURCE; 1225 fFile.SetTo(file, kX86ResourcesOffset); 1226 fEmptyResources = true; 1227 } catch (Exception exception) { 1228 if (exception.Error() != B_OK) 1229 error = exception.Error(); 1230 else 1231 error = B_ERROR; 1232 } 1233 } 1234 return error; 1235 } 1236 1237 1238 }; // namespace Storage 1239 }; // namespace BPrivate 1240 1241 1242 1243 1244