1 /* 2 * Copyright (c) 2007-2009, Haiku, Inc. 3 * Distributed under the terms of the MIT license. 4 * 5 * Author: 6 * Łukasz 'Sil2100' Zemczak <sil2100@vexillium.org> 7 */ 8 9 10 #include "PackageItem.h" 11 12 #include <Alert.h> 13 #include <ByteOrder.h> 14 #include <Directory.h> 15 #include <NodeInfo.h> 16 #include <SymLink.h> 17 #include <Volume.h> 18 19 #include <fs_info.h> 20 #include "zlib.h" 21 22 // Macro reserved for later localization 23 #define T(x) x 24 25 enum { 26 P_CHUNK_SIZE = 256 27 }; 28 29 static const uint32 kDefaultMode = 0777; 30 static const uint8 padding[7] = { 0, 0, 0, 0, 0, 0, 0 }; 31 32 enum { 33 P_DATA = 0, 34 P_ATTRIBUTE 35 }; 36 37 38 status_t 39 inflate_data(uint8 *in, uint32 inSize, uint8 *out, uint32 outSize) 40 { 41 z_stream stream; 42 stream.zalloc = Z_NULL; 43 stream.zfree = Z_NULL; 44 stream.opaque = Z_NULL; 45 stream.avail_in = inSize; 46 stream.next_in = in; 47 status_t ret; 48 49 ret = inflateInit(&stream); 50 if (ret != Z_OK) { 51 parser_debug("inflatInit failed\n"); 52 return B_ERROR; 53 } 54 55 stream.avail_out = outSize; 56 stream.next_out = out; 57 58 ret = inflate(&stream, Z_NO_FLUSH); 59 if (ret != Z_STREAM_END) { 60 // Uncompressed file size in package info corrupted 61 parser_debug("Left: %d\n", stream.avail_out); 62 return B_ERROR; 63 } 64 65 inflateEnd(&stream); 66 return B_OK; 67 } 68 69 70 static inline int 71 inflate_file_to_file(BFile *in, uint64 in_size, BFile *out, uint64 out_size) 72 { 73 z_stream stream; 74 stream.zalloc = Z_NULL; 75 stream.zfree = Z_NULL; 76 stream.opaque = Z_NULL; 77 stream.avail_in = 0; 78 stream.next_in = Z_NULL; 79 status_t ret; 80 81 uint8 buffer_out[P_CHUNK_SIZE], buffer_in[P_CHUNK_SIZE]; 82 uint64 bytes_read = 0, read = P_CHUNK_SIZE, write = 0; 83 84 ret = inflateInit(&stream); 85 if (ret != Z_OK) { 86 parser_debug("inflate_file_to_file: inflateInit failed\n"); 87 return B_ERROR; 88 } 89 90 do { 91 bytes_read += P_CHUNK_SIZE; 92 if (bytes_read > in_size) { 93 read = in_size - (bytes_read - P_CHUNK_SIZE); 94 bytes_read = in_size; 95 } 96 97 stream.avail_in = in->Read(buffer_in, read); 98 if (stream.avail_in != read) { 99 parser_debug("inflate_file_to_file: read failed\n"); 100 (void)inflateEnd(&stream); 101 return B_ERROR; 102 } 103 stream.next_in = buffer_in; 104 105 do { 106 stream.avail_out = P_CHUNK_SIZE; 107 stream.next_out = buffer_out; 108 109 ret = inflate(&stream, Z_NO_FLUSH); 110 if (ret != Z_OK && ret != Z_STREAM_END && ret != Z_BUF_ERROR) { 111 parser_debug("inflate_file_to_file: inflate failed\n"); 112 (void)inflateEnd(&stream); 113 return B_ERROR; 114 } 115 116 write = P_CHUNK_SIZE - stream.avail_out; 117 if (static_cast<uint64>(out->Write(buffer_out, write)) != write) { 118 parser_debug("inflate_file_to_file: write failed\n"); 119 (void)inflateEnd(&stream); 120 return B_ERROR; 121 } 122 } 123 while (stream.avail_out == 0); 124 } 125 while (bytes_read != in_size); 126 127 (void)inflateEnd(&stream); 128 129 return B_OK; 130 } 131 132 133 // #pragma mark - PackageItem 134 135 136 PackageItem::PackageItem(BFile *parent, const BString &path, uint8 type, 137 uint32 ctime, uint32 mtime, uint64 offset, uint64 size) 138 { 139 SetTo(parent, path, type, ctime, mtime, offset, size); 140 } 141 142 143 PackageItem::~PackageItem() 144 { 145 } 146 147 148 void 149 PackageItem::SetTo(BFile *parent, const BString &path, uint8 type, uint32 ctime, 150 uint32 mtime, uint64 offset, uint64 size) 151 { 152 fPackage = parent; 153 fPath = path; 154 155 fOffset = offset; 156 fSize = size; 157 fPathType = type; 158 fCreationTime = ctime; 159 fModificationTime = mtime; 160 } 161 162 163 int32 164 PackageItem::ItemExists(const char *name) 165 { 166 // TODO: this function doesn't really fit in, the GUI should be separated 167 // from the package engine completely 168 169 BString alertString = "The "; 170 171 alertString << ItemKind() << " named \'" << name << "\' "; 172 alertString << T("already exists in the given path. Should I replace " 173 "the existing file with the one from this package?"); 174 175 BAlert *alert = new BAlert(T("file_exists"), alertString.String(), 176 T("Yes"), T("No"), T("Abort")); 177 178 return alert->Go(); 179 } 180 181 182 status_t 183 PackageItem::InitPath(const char *path, BPath *destination) 184 { 185 status_t ret = B_OK; 186 187 if (fPathType == P_INSTALL_PATH) { 188 if (!path) { 189 parser_debug("InitPath path is NULL\n"); 190 return B_ERROR; 191 } 192 ret = destination->SetTo(path, fPath.String()); 193 } 194 else if (fPathType == P_SYSTEM_PATH) 195 ret = destination->SetTo(fPath.String()); 196 else { 197 if (!path) { 198 parser_debug("InitPath path is NULL\n"); 199 return B_ERROR; 200 } 201 202 BVolume volume(dev_for_path(path)); 203 ret = volume.InitCheck(); 204 if (ret != B_OK) 205 return ret; 206 207 BDirectory temp; 208 ret = volume.GetRootDirectory(&temp); 209 if (ret != B_OK) 210 return ret; 211 212 BPath mountPoint(&temp, NULL); 213 ret = destination->SetTo(mountPoint.Path(), fPath.String()); 214 } 215 216 return ret; 217 } 218 219 220 status_t 221 PackageItem::HandleAttributes(BPath *destination, BNode *node, 222 const char *header) 223 { 224 status_t ret = B_OK; 225 226 BVolume volume(dev_for_path(destination->Path())); 227 if (volume.KnowsAttr()) { 228 parser_debug("We have an offset\n"); 229 if (!fPackage) 230 return B_ERROR; 231 232 ret = fPackage->InitCheck(); 233 if (ret != B_OK) 234 return ret; 235 236 // We need to parse the data section now 237 fPackage->Seek(fOffset, SEEK_SET); 238 uint8 buffer[7]; 239 if (fPackage->Read(buffer, 7) != 7 || memcmp(buffer, header, 5)) 240 return B_ERROR; 241 parser_debug("Header validated!\n"); 242 243 char *attrName = 0; 244 uint32 nameSize = 0; 245 uint8 *attrData = new uint8[P_CHUNK_SIZE]; 246 uint64 dataSize = P_CHUNK_SIZE; 247 uint8 *temp = new uint8[P_CHUNK_SIZE]; 248 uint64 tempSize = P_CHUNK_SIZE; 249 250 uint64 attrCSize = 0, attrOSize = 0; 251 uint32 attrType = 0; // type_code type 252 bool attrStarted = false, done = false; 253 254 while (fPackage->Read(buffer, 7) == 7) { 255 if (!memcmp(buffer, "FBeA", 5)) 256 continue; 257 258 ret = ParseAttribute(buffer, node, &attrName, &nameSize, &attrType, 259 &attrData, &dataSize, &temp, &tempSize, &attrCSize, &attrOSize, 260 &attrStarted, &done); 261 if (ret != B_OK || done) { 262 if (ret != B_OK) { 263 parser_debug("_ParseAttribute failed for %s\n", 264 destination->Path()); 265 } 266 break; 267 } 268 } 269 270 delete[] attrData; 271 delete[] temp; 272 } 273 274 return ret; 275 } 276 277 278 status_t 279 PackageItem::ParseAttribute(uint8 *buffer, BNode *node, char **attrName, 280 uint32 *nameSize, uint32 *attrType, uint8 **attrData, uint64 *dataSize, 281 uint8 **temp, uint64 *tempSize, uint64 *attrCSize, uint64 *attrOSize, 282 bool *attrStarted, bool *done) 283 { 284 status_t ret = B_OK; 285 uint32 length; 286 287 if (!memcmp(buffer, "BeAI", 5)) { 288 parser_debug(" Attribute started.\n"); 289 if (*attrName) 290 *attrName[0] = 0; 291 *attrCSize = 0; 292 *attrOSize = 0; 293 294 *attrStarted = true; 295 } else if (!memcmp(buffer, "BeAN", 5)) { 296 if (!*attrStarted) { 297 ret = B_ERROR; 298 return ret; 299 } 300 301 parser_debug(" BeAN.\n"); 302 fPackage->Read(&length, 4); 303 swap_data(B_UINT32_TYPE, &length, sizeof(uint32), 304 B_SWAP_BENDIAN_TO_HOST); 305 306 if (*nameSize < (length + 1)) { 307 delete *attrName; 308 *nameSize = length + 1; 309 *attrName = new char[*nameSize]; 310 } 311 fPackage->Read(*attrName, length); 312 (*attrName)[length] = 0; 313 314 parser_debug(" (%ld) = %s\n", length, *attrName); 315 } else if (!memcmp(buffer, "BeAT", 5)) { 316 if (!*attrStarted) { 317 ret = B_ERROR; 318 return ret; 319 } 320 321 parser_debug(" BeAT.\n"); 322 fPackage->Read(attrType, 4); 323 swap_data(B_UINT32_TYPE, attrType, sizeof(*attrType), 324 B_SWAP_BENDIAN_TO_HOST); 325 } else if (!memcmp(buffer, "BeAD", 5)) { 326 if (!*attrStarted) { 327 ret = B_ERROR; 328 return ret; 329 } 330 331 parser_debug(" BeAD.\n"); 332 fPackage->Read(attrCSize, 8); 333 swap_data(B_UINT64_TYPE, attrCSize, sizeof(*attrCSize), 334 B_SWAP_BENDIAN_TO_HOST); 335 336 fPackage->Read(attrOSize, 8); 337 swap_data(B_UINT64_TYPE, attrOSize, sizeof(*attrOSize), 338 B_SWAP_BENDIAN_TO_HOST); 339 340 fPackage->Seek(4, SEEK_CUR); // TODO: Check what this means 341 342 if (*tempSize < *attrCSize) { 343 delete *temp; 344 *tempSize = *attrCSize; 345 *temp = new uint8[*tempSize]; 346 } 347 if (*dataSize < *attrOSize) { 348 delete *attrData; 349 *dataSize = *attrOSize; 350 *attrData = new uint8[*dataSize]; 351 } 352 353 if (fPackage->Read(*temp, *attrCSize) 354 != static_cast<ssize_t>(*attrCSize)) { 355 ret = B_ERROR; 356 return ret; 357 } 358 359 parser_debug(" Data read successfuly. Inflating!\n"); 360 ret = inflate_data(*temp, *tempSize, *attrData, *dataSize); 361 if (ret != B_OK) 362 return ret; 363 } else if (!memcmp(buffer, padding, 7)) { 364 if (!*attrStarted) { 365 *done = true; 366 return ret; 367 } 368 369 parser_debug(" Padding.\n"); 370 ssize_t wrote = node->WriteAttr(*attrName, *attrType, 0, *attrData, 371 *attrOSize); 372 if (wrote != static_cast<ssize_t>(*attrOSize)) { 373 parser_debug("Failed to write attribute %s %s\n", *attrName, strerror(wrote)); 374 return B_ERROR; 375 } 376 377 *attrStarted = false; 378 if (*attrName) 379 *attrName[0] = 0; 380 *attrCSize = 0; 381 *attrOSize = 0; 382 383 parser_debug(" > Attribute added.\n"); 384 } else { 385 parser_debug(" Unknown attribute\n"); 386 ret = B_ERROR; 387 } 388 389 return ret; 390 } 391 392 393 status_t 394 PackageItem::ParseData(uint8 *buffer, BFile *file, uint64 originalSize, 395 bool *done) 396 { 397 status_t ret = B_OK; 398 399 if (!memcmp(buffer, "FiMF", 5)) { 400 parser_debug(" Found file data.\n"); 401 uint64 compressed, original; 402 fPackage->Read(&compressed, 8); 403 swap_data(B_UINT64_TYPE, &compressed, sizeof(uint64), 404 B_SWAP_BENDIAN_TO_HOST); 405 406 fPackage->Read(&original, 8); 407 swap_data(B_UINT64_TYPE, &original, sizeof(uint64), 408 B_SWAP_BENDIAN_TO_HOST); 409 parser_debug(" Still good... (%llu : %llu)\n", original, 410 originalSize); 411 412 if (original != originalSize) { 413 parser_debug(" File size mismatch\n"); 414 return B_ERROR; // File size mismatch 415 } 416 parser_debug(" Still good...\n"); 417 418 if (fPackage->Read(buffer, 4) != 4) { 419 parser_debug(" Read(buffer, 4) failed\n"); 420 return B_ERROR; 421 } 422 parser_debug(" Still good...\n"); 423 424 ret = inflate_file_to_file(fPackage, compressed, file, original); 425 if (ret != B_OK) { 426 parser_debug(" inflate_file_to_file failed\n"); 427 return ret; 428 } 429 parser_debug(" File data inflation complete!\n"); 430 } 431 else if (!memcmp(buffer, padding, 7)) { 432 *done = true; 433 return ret; 434 } 435 else { 436 parser_debug("_ParseData unknown tag\n"); 437 ret = B_ERROR; 438 } 439 440 return ret; 441 } 442 443 444 // #pragma mark - PackageDirectory 445 446 447 PackageDirectory::PackageDirectory(BFile *parent, const BString &path, 448 uint8 type, uint32 ctime, uint32 mtime, uint64 offset, uint64 size) 449 : PackageItem(parent, path, type, ctime, mtime, offset, size) 450 { 451 } 452 453 454 status_t 455 PackageDirectory::WriteToPath(const char *path, BPath *final) 456 { 457 BPath destination; 458 status_t ret; 459 parser_debug("Directory: %s WriteToPath() called!\n", fPath.String()); 460 461 ret = InitPath(path, &destination); 462 if (ret != B_OK) 463 return ret; 464 465 // Since Haiku is single-user right now, we give the newly 466 // created directory default permissions 467 ret = create_directory(destination.Path(), kDefaultMode); 468 if (ret != B_OK) 469 return ret; 470 BDirectory dir(destination.Path()); 471 parser_debug("Directory created!\n"); 472 473 if (fCreationTime) 474 dir.SetCreationTime(static_cast<time_t>(fCreationTime)); 475 476 if (fModificationTime) 477 dir.SetModificationTime(static_cast<time_t>(fModificationTime)); 478 479 // Since directories can only have attributes in the offset section, 480 // we can check here whether it is necessary to continue 481 if (fOffset) 482 ret = HandleAttributes(&destination, &dir, "FoDa"); 483 484 if (final) 485 *final = destination; 486 487 return ret; 488 } 489 490 491 const char* 492 PackageDirectory::ItemKind() 493 { 494 return "directory"; 495 } 496 497 498 // #pragma mark - PackageFile 499 500 501 PackageFile::PackageFile(BFile *parent, const BString &path, uint8 type, 502 uint32 ctime, uint32 mtime, uint64 offset, uint64 size, 503 uint64 originalSize, uint32 platform, const BString &mime, 504 const BString &signature, uint32 mode) 505 : PackageItem(parent, path, type, ctime, mtime, offset, size), 506 fOriginalSize(originalSize), 507 fPlatform(platform), 508 fMode(mode), 509 fMimeType(mime), 510 fSignature(signature) 511 { 512 } 513 514 515 status_t 516 PackageFile::WriteToPath(const char *path, BPath *final) 517 { 518 BPath destination; 519 status_t ret; 520 parser_debug("File: %s WriteToPath() called!\n", fPath.String()); 521 522 ret = InitPath(path, &destination); 523 if (ret != B_OK) 524 return ret; 525 526 BFile file(destination.Path(), 527 B_WRITE_ONLY | B_CREATE_FILE | B_FAIL_IF_EXISTS); 528 ret = file.InitCheck(); 529 if (ret == B_FILE_EXISTS) { 530 int32 selection = ItemExists(destination.Leaf()); 531 switch (selection) { 532 case 0: 533 ret = file.SetTo(destination.Path(), 534 B_WRITE_ONLY | B_ERASE_FILE); 535 if (ret != B_OK) 536 return ret; 537 break; 538 case 1: 539 return B_OK; 540 default: 541 return B_FILE_EXISTS; 542 } 543 } else if (ret == B_ENTRY_NOT_FOUND) { 544 BPath directory; 545 destination.GetParent(&directory); 546 if (create_directory(directory.Path(), kDefaultMode) != B_OK) 547 return B_ERROR; 548 549 ret = file.SetTo(destination.Path(), B_WRITE_ONLY | B_CREATE_FILE); 550 if (ret != B_OK) 551 return ret; 552 } else if (ret != B_OK) 553 return ret; 554 555 parser_debug(" File created!\n"); 556 557 // Set the file permissions, creation and modification times 558 ret = file.SetPermissions(static_cast<mode_t>(fMode)); 559 if (fCreationTime && ret == B_OK) 560 ret = file.SetCreationTime(static_cast<time_t>(fCreationTime)); 561 if (fModificationTime && ret == B_OK) 562 ret = file.SetModificationTime(static_cast<time_t>(fModificationTime)); 563 564 if (ret != B_OK) 565 return ret; 566 567 // Set the mimetype and application signature if present 568 BNodeInfo info(&file); 569 if (fMimeType.Length() > 0) { 570 ret = info.SetType(fMimeType.String()); 571 if (ret != B_OK) 572 return ret; 573 } 574 if (fSignature.Length() > 0) { 575 ret = info.SetPreferredApp(fSignature.String()); 576 if (ret != B_OK) 577 return ret; 578 } 579 580 if (fOffset) { 581 parser_debug("We have an offset\n"); 582 if (!fPackage) 583 return B_ERROR; 584 585 ret = fPackage->InitCheck(); 586 if (ret != B_OK) 587 return ret; 588 589 // We need to parse the data section now 590 fPackage->Seek(fOffset, SEEK_SET); 591 uint8 buffer[7]; 592 593 char *attrName = 0; 594 uint32 nameSize = 0; 595 uint8 *attrData = new uint8[P_CHUNK_SIZE]; 596 uint64 dataSize = P_CHUNK_SIZE; 597 uint8 *temp = new uint8[P_CHUNK_SIZE]; 598 uint64 tempSize = P_CHUNK_SIZE; 599 600 uint64 attrCSize = 0, attrOSize = 0; 601 uint32 attrType = 0; // type_code type 602 bool attrStarted = false, done = false; 603 604 uint8 section = P_ATTRIBUTE; 605 606 while (fPackage->Read(buffer, 7) == 7) { 607 if (!memcmp(buffer, "FBeA", 5)) { 608 parser_debug("-> Attribute\n"); 609 section = P_ATTRIBUTE; 610 continue; 611 } else if (!memcmp(buffer, "FiDa", 5)) { 612 parser_debug("-> File data\n"); 613 section = P_DATA; 614 continue; 615 } 616 617 switch (section) { 618 case P_ATTRIBUTE: 619 ret = ParseAttribute(buffer, &file, &attrName, &nameSize, 620 &attrType, &attrData, &dataSize, &temp, &tempSize, 621 &attrCSize, &attrOSize, &attrStarted, &done); 622 break; 623 624 case P_DATA: 625 ret = ParseData(buffer, &file, fOriginalSize, &done); 626 break; 627 628 default: 629 return B_ERROR; 630 } 631 632 if (ret != B_OK || done) 633 break; 634 } 635 636 delete[] attrData; 637 delete[] temp; 638 } 639 640 if (final) 641 *final = destination; 642 643 return ret; 644 } 645 646 647 const char* 648 PackageFile::ItemKind() 649 { 650 return "file"; 651 } 652 653 654 // #pragma mark - 655 656 657 PackageLink::PackageLink(BFile *parent, const BString &path, 658 const BString &link, uint8 type, uint32 ctime, uint32 mtime, 659 uint32 mode, uint64 offset, uint64 size) 660 : PackageItem(parent, path, type, ctime, mtime, offset, size), 661 fMode(mode), 662 fLink(link) 663 { 664 } 665 666 667 status_t 668 PackageLink::WriteToPath(const char *path, BPath *final) 669 { 670 parser_debug("Symlink: %s WriteToPath() called!\n", fPath.String()); 671 672 BPath destination; 673 status_t ret = InitPath(path, &destination); 674 if (ret != B_OK) 675 return ret; 676 677 BString linkName(destination.Leaf()); 678 parser_debug("%s:%s:%s\n", fPath.String(), destination.Path(), 679 linkName.String()); 680 681 BPath dirPath; 682 ret = destination.GetParent(&dirPath); 683 BDirectory dir(dirPath.Path()); 684 685 ret = dir.InitCheck(); 686 if (ret == B_ENTRY_NOT_FOUND) { 687 if ((ret = create_directory(dirPath.Path(), kDefaultMode)) != B_OK) { 688 parser_debug("create_directory()) failed\n"); 689 return B_ERROR; 690 } 691 } 692 if (ret != B_OK) { 693 parser_debug("destination InitCheck failed %s for %s\n", strerror(ret), dirPath.Path()); 694 return ret; 695 } 696 697 BSymLink symlink; 698 ret = dir.CreateSymLink(destination.Path(), fLink.String(), &symlink); 699 if (ret == B_FILE_EXISTS) { 700 // We need to check if the existing symlink is pointing at the same path 701 // as our new one - if not, let's prompt the user 702 symlink.SetTo(destination.Path()); 703 BPath oldLink; 704 705 ret = symlink.MakeLinkedPath(&dir, &oldLink); 706 chdir(dirPath.Path()); 707 708 if (ret == B_BAD_VALUE || oldLink != fLink.String()) { 709 // The old symlink is different (or not a symlink) - ask the user 710 int32 selection = ItemExists(destination.Leaf()); 711 switch (selection) { 712 case 0: 713 { 714 symlink.Unset(); 715 BEntry entry; 716 ret = entry.SetTo(destination.Path()); 717 if (ret != B_OK) 718 return ret; 719 720 entry.Remove(); 721 ret = dir.CreateSymLink(destination.Path(), fLink.String(), 722 &symlink); 723 break; 724 } 725 case 1: 726 parser_debug("Skipping already existent SymLink\n"); 727 return B_OK; 728 default: 729 ret = B_FILE_EXISTS; 730 } 731 } else { 732 ret = B_OK; 733 } 734 } 735 if (ret != B_OK) { 736 parser_debug("CreateSymLink failed\n"); 737 return ret; 738 } 739 740 parser_debug(" Symlink created!\n"); 741 742 ret = symlink.SetPermissions(static_cast<mode_t>(fMode)); 743 744 if (fCreationTime && ret == B_OK) 745 ret = symlink.SetCreationTime(static_cast<time_t>(fCreationTime)); 746 747 if (fModificationTime && ret == B_OK) { 748 ret = symlink.SetModificationTime(static_cast<time_t>( 749 fModificationTime)); 750 } 751 752 if (ret != B_OK) { 753 parser_debug("Failed to set symlink attributes\n"); 754 return ret; 755 } 756 757 if (fOffset) { 758 // Symlinks also seem to have attributes - so parse them 759 ret = HandleAttributes(&destination, &symlink, "LnDa"); 760 } 761 762 if (final) { 763 *final = destination; 764 } 765 766 return ret; 767 } 768 769 770 const char* 771 PackageLink::ItemKind() 772 { 773 return "symbolic link"; 774 } 775