1 /* 2 * Copyright 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 <stdlib.h> 13 #include <string.h> 14 15 #include <Alert.h> 16 #include <ByteOrder.h> 17 #include <Catalog.h> 18 #include <Directory.h> 19 #include <FindDirectory.h> 20 #include <fs_info.h> 21 #include <Locale.h> 22 #include <NodeInfo.h> 23 #include <OS.h> 24 #include <SymLink.h> 25 #include <Volume.h> 26 27 #include "zlib.h" 28 29 30 #undef B_TRANSLATION_CONTEXT 31 #define B_TRANSLATION_CONTEXT "PackageItem" 32 33 enum { 34 P_CHUNK_SIZE = 256 35 }; 36 37 static const uint32 kDefaultMode = 0777; 38 static const uint8 padding[7] = { 0, 0, 0, 0, 0, 0, 0 }; 39 40 extern bool gVerbose; 41 42 enum { 43 P_DATA = 0, 44 P_ATTRIBUTE 45 }; 46 47 48 status_t 49 inflate_data(uint8 *in, uint32 inSize, uint8 *out, uint32 outSize) 50 { 51 parser_debug("inflate_data() called - input_size: %ld, output_size: %ld\n", 52 inSize, outSize); 53 z_stream stream; 54 stream.zalloc = Z_NULL; 55 stream.zfree = Z_NULL; 56 stream.opaque = Z_NULL; 57 stream.avail_in = inSize; 58 stream.next_in = in; 59 status_t ret; 60 61 ret = inflateInit(&stream); 62 if (ret != Z_OK) { 63 parser_debug("inflatInit failed\n"); 64 return B_ERROR; 65 } 66 67 stream.avail_out = outSize; 68 stream.next_out = out; 69 70 ret = inflate(&stream, Z_NO_FLUSH); 71 if (ret != Z_STREAM_END) { 72 // Uncompressed file size in package info corrupted 73 parser_debug("Left: %d\n", stream.avail_out); 74 return B_ERROR; 75 } 76 77 inflateEnd(&stream); 78 return B_OK; 79 } 80 81 82 static inline int 83 inflate_file_to_file(BFile *in, uint64 in_size, BFile *out, uint64 out_size) 84 { 85 z_stream stream; 86 stream.zalloc = Z_NULL; 87 stream.zfree = Z_NULL; 88 stream.opaque = Z_NULL; 89 stream.avail_in = 0; 90 stream.next_in = Z_NULL; 91 status_t ret; 92 93 uint8 buffer_out[P_CHUNK_SIZE], buffer_in[P_CHUNK_SIZE]; 94 uint64 bytes_read = 0, read = P_CHUNK_SIZE, write = 0; 95 96 ret = inflateInit(&stream); 97 if (ret != Z_OK) { 98 parser_debug("inflate_file_to_file: inflateInit failed\n"); 99 return B_ERROR; 100 } 101 102 do { 103 bytes_read += P_CHUNK_SIZE; 104 if (bytes_read > in_size) { 105 read = in_size - (bytes_read - P_CHUNK_SIZE); 106 bytes_read = in_size; 107 } 108 109 stream.avail_in = in->Read(buffer_in, read); 110 if (stream.avail_in != read) { 111 parser_debug("inflate_file_to_file: read failed\n"); 112 (void)inflateEnd(&stream); 113 return B_ERROR; 114 } 115 stream.next_in = buffer_in; 116 117 do { 118 stream.avail_out = P_CHUNK_SIZE; 119 stream.next_out = buffer_out; 120 121 ret = inflate(&stream, Z_NO_FLUSH); 122 if (ret != Z_OK && ret != Z_STREAM_END && ret != Z_BUF_ERROR) { 123 parser_debug("inflate_file_to_file: inflate failed with '%s'\n", 124 stream.msg); 125 (void)inflateEnd(&stream); 126 return B_ERROR; 127 } 128 129 write = P_CHUNK_SIZE - stream.avail_out; 130 if (static_cast<uint64>(out->Write(buffer_out, write)) != write) { 131 parser_debug("inflate_file_to_file: write failed\n"); 132 (void)inflateEnd(&stream); 133 return B_ERROR; 134 } 135 } while (stream.avail_out == 0); 136 } while (bytes_read != in_size); 137 138 (void)inflateEnd(&stream); 139 140 return B_OK; 141 } 142 143 144 // #pragma mark - PackageItem 145 146 147 PackageItem::PackageItem(BFile* parent, const BString& path, uint8 type, 148 uint32 ctime, uint32 mtime, uint64 offset, uint64 size) 149 { 150 SetTo(parent, path, type, ctime, mtime, offset, size); 151 } 152 153 154 PackageItem::~PackageItem() 155 { 156 } 157 158 159 void 160 PackageItem::SetTo(BFile* parent, const BString& path, uint8 type, uint32 ctime, 161 uint32 mtime, uint64 offset, uint64 size) 162 { 163 fPackage = parent; 164 fPath = path; 165 166 fOffset = offset; 167 fSize = size; 168 fPathType = type; 169 fCreationTime = ctime; 170 fModificationTime = mtime; 171 } 172 173 174 status_t 175 PackageItem::InitPath(const char* path, BPath* destination) 176 { 177 status_t ret = B_OK; 178 179 // If the path starts with /boot/, treat it as a P_SYSTEM_PATH. 180 // This may not be the correct way to handle such a case, but it fixes 181 // installation of the RefLine pkg. 182 if (fPathType == P_SYSTEM_PATH || (fPathType == P_INSTALL_PATH 183 && strncmp("/boot/", path, 6) == 0)) { 184 if (gVerbose) 185 printf("InitPath - absolute: %s\n", fPath.String()); 186 if (fPath == "") 187 fPath = "/"; 188 ret = destination->SetTo(fPath.String()); 189 } else if (fPathType == P_INSTALL_PATH) { 190 if (gVerbose) 191 printf("InitPath - relative: %s + %s\n", path, fPath.String()); 192 if (path == NULL) { 193 parser_debug("InitPath path is NULL\n"); 194 return B_ERROR; 195 } 196 ret = destination->SetTo(path, fPath.String()); 197 } else { 198 if (gVerbose) 199 printf("InitPath - volume: %s + %s\n", path, fPath.String()); 200 if (path == NULL) { 201 parser_debug("InitPath path is NULL\n"); 202 return B_ERROR; 203 } 204 205 BVolume volume(dev_for_path(path)); 206 ret = volume.InitCheck(); 207 if (ret == B_OK) { 208 BDirectory temp; 209 ret = volume.GetRootDirectory(&temp); 210 if (ret == B_OK) { 211 BPath mountPoint(&temp, NULL); 212 ret = destination->SetTo(mountPoint.Path(), fPath.String()); 213 } 214 } 215 } 216 217 if (ret != B_OK) { 218 fprintf(stderr, "InitPath(%s): %s\n", path, strerror(ret)); 219 return ret; 220 } 221 222 BString pathString(destination->Path()); 223 224 // Hardcoded paths, the .pkg files hardcode this to the same 225 if (pathString.FindFirst("non-packaged") < 0) { 226 bool wasRewritten = false; 227 228 if (pathString.StartsWith("/boot/beos/system")) { 229 BPath systemNonPackagedDir; 230 find_directory(B_SYSTEM_NONPACKAGED_DIRECTORY, 231 &systemNonPackagedDir); 232 pathString.ReplaceFirst("/boot/beos/system", 233 systemNonPackagedDir.Path()); 234 wasRewritten = true; 235 } else if (pathString.StartsWith("/boot/system")) { 236 BPath systemNonPackagedDir; 237 find_directory(B_SYSTEM_NONPACKAGED_DIRECTORY, 238 &systemNonPackagedDir); 239 pathString.ReplaceFirst("/boot/system", 240 systemNonPackagedDir.Path()); 241 wasRewritten = true; 242 } else if (pathString.StartsWith("/boot/home/config")) { 243 BPath userNonPackagedDir; 244 find_directory(B_USER_NONPACKAGED_DIRECTORY, &userNonPackagedDir); 245 pathString.ReplaceFirst("/boot/home/config", 246 userNonPackagedDir.Path()); 247 wasRewritten = true; 248 } 249 250 if (wasRewritten) { 251 if (gVerbose) 252 printf("rewritten: %s\n", pathString.String()); 253 destination->SetTo(pathString.String()); 254 } 255 } 256 257 return ret; 258 } 259 260 261 status_t 262 PackageItem::HandleAttributes(BPath *destination, BNode *node, 263 const char *header) 264 { 265 status_t ret = B_OK; 266 267 BVolume volume(dev_for_path(destination->Path())); 268 if (volume.KnowsAttr()) { 269 parser_debug("We have an offset\n"); 270 if (!fPackage) 271 return B_ERROR; 272 273 ret = fPackage->InitCheck(); 274 if (ret != B_OK) 275 return ret; 276 277 // We need to parse the data section now 278 fPackage->Seek(fOffset, SEEK_SET); 279 uint8 buffer[7]; 280 if (fPackage->Read(buffer, 7) != 7 || memcmp(buffer, header, 5)) 281 return B_ERROR; 282 parser_debug("Header validated!\n"); 283 284 char *attrName = 0; 285 uint32 nameSize = 0; 286 uint8 *attrData = new uint8[P_CHUNK_SIZE]; 287 uint64 dataSize = P_CHUNK_SIZE; 288 uint8 *temp = new uint8[P_CHUNK_SIZE]; 289 uint64 tempSize = P_CHUNK_SIZE; 290 291 uint64 attrCSize = 0, attrOSize = 0; 292 uint32 attrType = 0; // type_code type 293 bool attrStarted = false, done = false; 294 295 while (fPackage->Read(buffer, 7) == 7) { 296 if (!memcmp(buffer, "FBeA", 5)) 297 continue; 298 299 ret = ParseAttribute(buffer, node, &attrName, &nameSize, &attrType, 300 &attrData, &dataSize, &temp, &tempSize, &attrCSize, &attrOSize, 301 &attrStarted, &done); 302 if (ret != B_OK || done) { 303 if (ret != B_OK) { 304 parser_debug("_ParseAttribute failed for %s\n", 305 destination->Path()); 306 } 307 break; 308 } 309 } 310 311 delete[] attrData; 312 delete[] temp; 313 } 314 315 return ret; 316 } 317 318 319 status_t 320 PackageItem::ParseAttribute(uint8* buffer, BNode* node, char** attrName, 321 uint32* nameSize, uint32* attrType, uint8** attrData, uint64* dataSize, 322 uint8** temp, uint64* tempSize, uint64* attrCSize, uint64* attrOSize, 323 bool* attrStarted, bool* done) 324 { 325 status_t ret = B_OK; 326 uint32 length; 327 328 if (!memcmp(buffer, "BeAI", 5)) { 329 parser_debug(" Attribute started.\n"); 330 if (*attrName) 331 *attrName[0] = 0; 332 *attrCSize = 0; 333 *attrOSize = 0; 334 335 *attrStarted = true; 336 } else if (!memcmp(buffer, "BeAN", 5)) { 337 if (!*attrStarted) { 338 ret = B_ERROR; 339 return ret; 340 } 341 342 parser_debug(" BeAN.\n"); 343 fPackage->Read(&length, 4); 344 swap_data(B_UINT32_TYPE, &length, sizeof(uint32), 345 B_SWAP_BENDIAN_TO_HOST); 346 347 if (*nameSize < (length + 1)) { 348 delete[] *attrName; 349 *nameSize = length + 1; 350 *attrName = new char[*nameSize]; 351 } 352 fPackage->Read(*attrName, length); 353 (*attrName)[length] = 0; 354 355 parser_debug(" (%ld) = %s\n", length, *attrName); 356 } else if (!memcmp(buffer, "BeAT", 5)) { 357 if (!*attrStarted) { 358 ret = B_ERROR; 359 return ret; 360 } 361 362 parser_debug(" BeAT.\n"); 363 fPackage->Read(attrType, 4); 364 swap_data(B_UINT32_TYPE, attrType, sizeof(*attrType), 365 B_SWAP_BENDIAN_TO_HOST); 366 } else if (!memcmp(buffer, "BeAD", 5)) { 367 if (!*attrStarted) { 368 ret = B_ERROR; 369 return ret; 370 } 371 372 parser_debug(" BeAD.\n"); 373 fPackage->Read(attrCSize, 8); 374 swap_data(B_UINT64_TYPE, attrCSize, sizeof(*attrCSize), 375 B_SWAP_BENDIAN_TO_HOST); 376 377 fPackage->Read(attrOSize, 8); 378 swap_data(B_UINT64_TYPE, attrOSize, sizeof(*attrOSize), 379 B_SWAP_BENDIAN_TO_HOST); 380 381 fPackage->Seek(4, SEEK_CUR); // TODO: Check what this means 382 383 if (*tempSize < *attrCSize) { 384 delete[] *temp; 385 *tempSize = *attrCSize; 386 *temp = new uint8[*tempSize]; 387 } 388 if (*dataSize < *attrOSize) { 389 delete[] *attrData; 390 *dataSize = *attrOSize; 391 *attrData = new uint8[*dataSize]; 392 } 393 394 if (fPackage->Read(*temp, *attrCSize) 395 != static_cast<ssize_t>(*attrCSize)) { 396 ret = B_ERROR; 397 return ret; 398 } 399 400 parser_debug(" Data read successfuly. Inflating!\n"); 401 ret = inflate_data(*temp, *tempSize, *attrData, *dataSize); 402 if (ret != B_OK) 403 return ret; 404 } else if (!memcmp(buffer, padding, 7)) { 405 if (!*attrStarted) { 406 *done = true; 407 return ret; 408 } 409 410 parser_debug(" Padding.\n"); 411 ssize_t wrote = node->WriteAttr(*attrName, *attrType, 0, *attrData, 412 *attrOSize); 413 if (wrote != static_cast<ssize_t>(*attrOSize)) { 414 parser_debug("Failed to write attribute %s %s\n", *attrName, strerror(wrote)); 415 return B_ERROR; 416 } 417 418 *attrStarted = false; 419 if (*attrName) 420 *attrName[0] = 0; 421 *attrCSize = 0; 422 *attrOSize = 0; 423 424 parser_debug(" > Attribute added.\n"); 425 } else { 426 parser_debug(" Unknown attribute\n"); 427 ret = B_ERROR; 428 } 429 430 return ret; 431 } 432 433 434 status_t 435 PackageItem::SkipAttribute(uint8* buffer, bool* attrStarted, bool* done) 436 { 437 status_t ret = B_OK; 438 uint32 length; 439 440 if (!memcmp(buffer, "BeAI", 5)) { 441 parser_debug(" Attribute started.\n"); 442 *attrStarted = true; 443 } else if (!memcmp(buffer, "BeAN", 5)) { 444 if (!*attrStarted) { 445 ret = B_ERROR; 446 return ret; 447 } 448 449 parser_debug(" BeAN.\n"); 450 fPackage->Read(&length, 4); 451 swap_data(B_UINT32_TYPE, &length, sizeof(uint32), 452 B_SWAP_BENDIAN_TO_HOST); 453 454 fPackage->Seek(length, SEEK_CUR); 455 } else if (!memcmp(buffer, "BeAT", 5)) { 456 if (!*attrStarted) { 457 ret = B_ERROR; 458 return ret; 459 } 460 461 parser_debug(" BeAT.\n"); 462 fPackage->Seek(4, SEEK_CUR); 463 } else if (!memcmp(buffer, "BeAD", 5)) { 464 if (!*attrStarted) { 465 ret = B_ERROR; 466 return ret; 467 } 468 469 parser_debug(" BeAD.\n"); 470 uint64 length64; 471 fPackage->Read(&length64, 8); 472 swap_data(B_UINT64_TYPE, &length64, sizeof(length64), 473 B_SWAP_BENDIAN_TO_HOST); 474 475 fPackage->Seek(12 + length64, SEEK_CUR); 476 477 parser_debug(" Data skipped successfuly.\n"); 478 } else if (!memcmp(buffer, padding, 7)) { 479 if (!*attrStarted) { 480 *done = true; 481 return ret; 482 } 483 484 parser_debug(" Padding.\n"); 485 *attrStarted = false; 486 parser_debug(" > Attribute skipped.\n"); 487 } else { 488 parser_debug(" Unknown attribute\n"); 489 ret = B_ERROR; 490 } 491 492 return ret; 493 } 494 495 496 status_t 497 PackageItem::ParseData(uint8* buffer, BFile* file, uint64 originalSize, 498 bool *done) 499 { 500 status_t ret = B_OK; 501 502 if (!memcmp(buffer, "FiMF", 5)) { 503 parser_debug(" Found file data.\n"); 504 uint64 compressed, original; 505 fPackage->Read(&compressed, 8); 506 swap_data(B_UINT64_TYPE, &compressed, sizeof(uint64), 507 B_SWAP_BENDIAN_TO_HOST); 508 509 fPackage->Read(&original, 8); 510 swap_data(B_UINT64_TYPE, &original, sizeof(uint64), 511 B_SWAP_BENDIAN_TO_HOST); 512 parser_debug(" Still good... (%llu : %llu)\n", original, 513 originalSize); 514 515 if (original != originalSize) { 516 parser_debug(" File size mismatch\n"); 517 return B_ERROR; // File size mismatch 518 } 519 parser_debug(" Still good...\n"); 520 521 if (fPackage->Read(buffer, 4) != 4) { 522 parser_debug(" Read(buffer, 4) failed\n"); 523 return B_ERROR; 524 } 525 parser_debug(" Still good...\n"); 526 527 ret = inflate_file_to_file(fPackage, compressed, file, original); 528 if (ret != B_OK) { 529 parser_debug(" inflate_file_to_file failed\n"); 530 return ret; 531 } 532 parser_debug(" File data inflation complete!\n"); 533 } else if (!memcmp(buffer, padding, 7)) { 534 *done = true; 535 return ret; 536 } else { 537 parser_debug("_ParseData unknown tag\n"); 538 ret = B_ERROR; 539 } 540 541 return ret; 542 } 543 544 545 // #pragma mark - PackageScript 546 547 548 PackageScript::PackageScript(BFile* parent, const BString& path, uint8 type, 549 uint64 offset, uint64 size, uint64 originalSize) 550 : 551 PackageItem(parent, path, type, 0, 0, offset, size), 552 fOriginalSize(originalSize), 553 fThreadId(-1) 554 { 555 } 556 557 558 status_t 559 PackageScript::DoInstall(const char* path, ItemState* state) 560 { 561 status_t ret = B_OK; 562 parser_debug("Script: DoInstall() called!\n"); 563 564 if (fOffset) { 565 parser_debug("We have an offset\n"); 566 if (!fPackage) 567 return B_ERROR; 568 569 ret = fPackage->InitCheck(); 570 if (ret != B_OK) 571 return ret; 572 573 // We need to parse the data section now 574 fPackage->Seek(fOffset, SEEK_SET); 575 uint8 buffer[7]; 576 bool attrStarted = false, done = false; 577 578 uint8 section = P_ATTRIBUTE; 579 580 while (fPackage->Read(buffer, 7) == 7) { 581 if (!memcmp(buffer, "FBeA", 5)) { 582 parser_debug("-> Attribute\n"); 583 section = P_ATTRIBUTE; 584 continue; 585 } else if (!memcmp(buffer, "FiDa", 5)) { 586 parser_debug("-> File data\n"); 587 section = P_DATA; 588 continue; 589 } 590 591 switch (section) { 592 case P_ATTRIBUTE: 593 ret = SkipAttribute(buffer, &attrStarted, &done); 594 break; 595 596 case P_DATA: 597 { 598 BString script; 599 ret = _ParseScript(buffer, fOriginalSize, script, &done); 600 if (ret == B_OK) { 601 // Rewrite Deskbar entry targets. NOTE: It would 602 // also work to Replace("/config/be", "/config...") 603 // but it would be less save. For example, an app 604 // could have a folder named "config/be..." inside 605 // its installation folder. 606 // TODO: Use find_paths() or we are no better than 607 // these scripts. 608 script.ReplaceAll( 609 "/boot/beos/system/", 610 "/boot/system/"); 611 script.ReplaceAll( 612 "~/config/be", 613 "~/config/settings/deskbar/menu"); 614 script.ReplaceAll( 615 "/boot/home/config/be", 616 "/boot/home/config/settings/deskbar/menu"); 617 // Rewrite all sorts of other old BeOS paths 618 script.ReplaceAll( 619 "/boot/preferences", 620 "/boot/system/preferences"); 621 script.ReplaceAll( 622 "/boot/apps", 623 "/boot/system/non-packaged/apps"); 624 script.ReplaceAll( 625 "~/config/add-ons/Screen\\ Savers", 626 "~/config/non-packaged/add-ons/Screen\\ Savers"); 627 // TODO: More. These should also be put into a 628 // common source location, since it can also be used 629 // for the retargetting of install file locations. 630 // Packages seem to declare which system paths they 631 // use, and then package items can reference one of 632 // those global paths by index. A more elegent solution 633 // compared to what happens now in InitPath() would be 634 // to replace those global package paths. Or maybe 635 // that's more fragile... but a common source for 636 // the rewriting of BeOS paths is needed. 637 638 if (gVerbose) 639 printf("%s\n", script.String()); 640 641 BPath workingDirectory; 642 if (path != NULL) 643 ret = InitPath(path, &workingDirectory); 644 else 645 ret = workingDirectory.SetTo("."); 646 if (ret == B_OK) 647 ret = _RunScript(workingDirectory.Path(), script); 648 } 649 break; 650 } 651 652 default: 653 return B_ERROR; 654 } 655 656 if (ret != B_OK || done) 657 break; 658 } 659 } 660 661 parser_debug("Ret: %ld %s\n", ret, strerror(ret)); 662 return ret; 663 } 664 665 666 const uint32 667 PackageScript::ItemKind() 668 { 669 return P_KIND_SCRIPT; 670 } 671 672 673 status_t 674 PackageScript::_ParseScript(uint8 *buffer, uint64 originalSize, 675 BString& _script, bool *done) 676 { 677 status_t ret = B_OK; 678 679 if (!memcmp(buffer, "FiMF", 5)) { 680 parser_debug(" Found file (script) data.\n"); 681 uint64 compressed, original; 682 fPackage->Read(&compressed, 8); 683 swap_data(B_UINT64_TYPE, &compressed, sizeof(uint64), 684 B_SWAP_BENDIAN_TO_HOST); 685 686 fPackage->Read(&original, 8); 687 swap_data(B_UINT64_TYPE, &original, sizeof(uint64), 688 B_SWAP_BENDIAN_TO_HOST); 689 parser_debug(" Still good... (%llu : %llu)\n", original, 690 originalSize); 691 692 if (original != originalSize) { 693 parser_debug(" File size mismatch\n"); 694 return B_ERROR; // File size mismatch 695 } 696 parser_debug(" Still good...\n"); 697 698 if (fPackage->Read(buffer, 4) != 4) { 699 parser_debug(" Read(buffer, 4) failed\n"); 700 return B_ERROR; 701 } 702 parser_debug(" Still good...\n"); 703 704 uint8 *temp = new uint8[compressed]; 705 if (fPackage->Read(temp, compressed) != (int64)compressed) { 706 parser_debug(" Read(temp, compressed) failed\n"); 707 delete[] temp; 708 return B_ERROR; 709 } 710 711 uint8* script = new uint8[original]; 712 ret = inflate_data(temp, compressed, script, original); 713 if (ret != B_OK) { 714 parser_debug(" inflate_data failed\n"); 715 delete[] temp; 716 delete[] script; 717 return ret; 718 } 719 720 _script.SetTo((char*)script, originalSize); 721 722 delete[] script; 723 delete[] temp; 724 parser_debug(" Script data inflation complete!\n"); 725 } else if (!memcmp(buffer, padding, 7)) { 726 *done = true; 727 return ret; 728 } else { 729 parser_debug("_ParseData unknown tag\n"); 730 ret = B_ERROR; 731 } 732 733 return ret; 734 } 735 736 737 status_t 738 PackageScript::_RunScript(const char* workingDirectory, const BString& script) 739 { 740 // This function written by Peter Folk <pfolk@uni.uiuc.edu> 741 // and published in the BeDevTalk FAQ, modified for use in the 742 // PackageInstaller 743 // http://www.abisoft.com/faq/BeDevTalk_FAQ.html#FAQ-209 744 745 // Change current working directory to install path 746 char oldWorkingDirectory[B_PATH_NAME_LENGTH]; 747 getcwd(oldWorkingDirectory, sizeof(oldWorkingDirectory)); 748 chdir(workingDirectory); 749 750 // Save current FDs 751 int old_in = dup(0); 752 int old_out = dup(1); 753 int old_err = dup(2); 754 755 int filedes[2]; 756 757 /* Create new pipe FDs as stdin, stdout, stderr */ 758 pipe(filedes); dup2(filedes[0], 0); close(filedes[0]); 759 int in = filedes[1]; // Write to in, appears on cmd's stdin 760 pipe(filedes); dup2(filedes[1], 1); close(filedes[1]); 761 pipe(filedes); dup2(filedes[1], 2); close(filedes[1]); 762 763 const char **argv = new const char * [3]; 764 argv[0] = strdup("/bin/sh"); 765 argv[1] = strdup("-s"); 766 argv[2] = NULL; 767 768 // "load" command. 769 fThreadId = load_image(2, argv, (const char**)environ); 770 771 int i; 772 for (i = 0; i < 2; i++) 773 delete argv[i]; 774 delete [] argv; 775 776 if (fThreadId < B_OK) 777 return fThreadId; 778 779 // thread id is now suspended. 780 setpgid(fThreadId, fThreadId); 781 782 // Restore old FDs 783 close(0); dup(old_in); close(old_in); 784 close(1); dup(old_out); close(old_out); 785 close(2); dup(old_err); close(old_err); 786 787 set_thread_priority(fThreadId, B_LOW_PRIORITY); 788 resume_thread(fThreadId); 789 790 // Write the script 791 if (write(in, script.String(), script.Length() - 1) != script.Length() - 1 792 || write(in, "\nexit\n", 6) != 6) { 793 parser_debug("Writing script failed\n"); 794 kill_thread(fThreadId); 795 return B_ERROR; 796 } 797 798 // Restore current working directory 799 chdir(oldWorkingDirectory); 800 801 return B_OK; 802 } 803 804 805 // #pragma mark - PackageDirectory 806 807 808 PackageDirectory::PackageDirectory(BFile* parent, const BString& path, 809 uint8 type, uint32 ctime, uint32 mtime, uint64 offset, uint64 size) 810 : 811 PackageItem(parent, path, type, ctime, mtime, offset, size) 812 { 813 } 814 815 816 status_t 817 PackageDirectory::DoInstall(const char* path, ItemState* state) 818 { 819 BPath &destination = state->destination; 820 status_t ret; 821 parser_debug("Directory: %s DoInstall() called!\n", fPath.String()); 822 823 ret = InitPath(path, &destination); 824 parser_debug("Ret: %ld %s\n", ret, strerror(ret)); 825 if (ret != B_OK) 826 return ret; 827 828 // Since Haiku is single-user right now, we give the newly 829 // created directory default permissions 830 ret = create_directory(destination.Path(), kDefaultMode); 831 parser_debug("Create dir ret: %ld %s\n", ret, strerror(ret)); 832 if (ret != B_OK) 833 return ret; 834 BDirectory dir(destination.Path()); 835 parser_debug("Directory created!\n"); 836 837 if (fCreationTime) 838 dir.SetCreationTime(static_cast<time_t>(fCreationTime)); 839 840 if (fModificationTime) 841 dir.SetModificationTime(static_cast<time_t>(fModificationTime)); 842 843 // Since directories can only have attributes in the offset section, 844 // we can check here whether it is necessary to continue 845 if (fOffset) 846 ret = HandleAttributes(&destination, &dir, "FoDa"); 847 848 parser_debug("Ret: %ld %s\n", ret, strerror(ret)); 849 return ret; 850 } 851 852 853 const uint32 854 PackageDirectory::ItemKind() 855 { 856 return P_KIND_DIRECTORY; 857 } 858 859 860 // #pragma mark - PackageFile 861 862 863 PackageFile::PackageFile(BFile *parent, const BString &path, uint8 type, 864 uint32 ctime, uint32 mtime, uint64 offset, uint64 size, 865 uint64 originalSize, uint32 platform, const BString &mime, 866 const BString &signature, uint32 mode) 867 : 868 PackageItem(parent, path, type, ctime, mtime, offset, size), 869 fOriginalSize(originalSize), 870 fPlatform(platform), 871 fMode(mode), 872 fMimeType(mime), 873 fSignature(signature) 874 { 875 } 876 877 878 status_t 879 PackageFile::DoInstall(const char* path, ItemState* state) 880 { 881 if (state == NULL) 882 return B_ERROR; 883 884 BPath& destination = state->destination; 885 status_t ret = B_OK; 886 parser_debug("File: %s DoInstall() called!\n", fPath.String()); 887 888 BFile file; 889 if (state->status == B_NO_INIT || destination.InitCheck() != B_OK) { 890 ret = InitPath(path, &destination); 891 if (ret != B_OK) 892 return ret; 893 894 ret = file.SetTo(destination.Path(), 895 B_WRITE_ONLY | B_CREATE_FILE | B_FAIL_IF_EXISTS); 896 if (ret == B_ENTRY_NOT_FOUND) { 897 BPath directory; 898 destination.GetParent(&directory); 899 if (create_directory(directory.Path(), kDefaultMode) != B_OK) 900 return B_ERROR; 901 902 ret = file.SetTo(destination.Path(), B_WRITE_ONLY | B_CREATE_FILE); 903 } else if (ret == B_FILE_EXISTS) 904 state->status = B_FILE_EXISTS; 905 906 if (ret != B_OK) 907 return ret; 908 } 909 910 if (state->status == B_FILE_EXISTS) { 911 switch (state->policy) { 912 case P_EXISTS_OVERWRITE: 913 ret = file.SetTo(destination.Path(), 914 B_WRITE_ONLY | B_ERASE_FILE); 915 break; 916 917 case P_EXISTS_NONE: 918 case P_EXISTS_ASK: 919 ret = B_FILE_EXISTS; 920 break; 921 922 case P_EXISTS_SKIP: 923 return B_OK; 924 } 925 } 926 927 if (ret != B_OK) 928 return ret; 929 930 parser_debug(" File created!\n"); 931 932 // Set the file permissions, creation and modification times 933 ret = file.SetPermissions(static_cast<mode_t>(fMode)); 934 if (fCreationTime && ret == B_OK) 935 ret = file.SetCreationTime(static_cast<time_t>(fCreationTime)); 936 if (fModificationTime && ret == B_OK) 937 ret = file.SetModificationTime(static_cast<time_t>(fModificationTime)); 938 939 if (ret != B_OK) 940 return ret; 941 942 // Set the mimetype and application signature if present 943 BNodeInfo info(&file); 944 if (fMimeType.Length() > 0) { 945 ret = info.SetType(fMimeType.String()); 946 if (ret != B_OK) 947 return ret; 948 } 949 if (fSignature.Length() > 0) { 950 ret = info.SetPreferredApp(fSignature.String()); 951 if (ret != B_OK) 952 return ret; 953 } 954 955 if (fOffset) { 956 parser_debug("We have an offset\n"); 957 if (!fPackage) 958 return B_ERROR; 959 960 ret = fPackage->InitCheck(); 961 if (ret != B_OK) 962 return ret; 963 964 // We need to parse the data section now 965 fPackage->Seek(fOffset, SEEK_SET); 966 uint8 buffer[7]; 967 968 char *attrName = 0; 969 uint32 nameSize = 0; 970 uint8 *attrData = new uint8[P_CHUNK_SIZE]; 971 uint64 dataSize = P_CHUNK_SIZE; 972 uint8 *temp = new uint8[P_CHUNK_SIZE]; 973 uint64 tempSize = P_CHUNK_SIZE; 974 975 uint64 attrCSize = 0, attrOSize = 0; 976 uint32 attrType = 0; // type_code type 977 bool attrStarted = false, done = false; 978 979 uint8 section = P_ATTRIBUTE; 980 981 while (fPackage->Read(buffer, 7) == 7) { 982 if (!memcmp(buffer, "FBeA", 5)) { 983 parser_debug("-> Attribute\n"); 984 section = P_ATTRIBUTE; 985 continue; 986 } else if (!memcmp(buffer, "FiDa", 5)) { 987 parser_debug("-> File data\n"); 988 section = P_DATA; 989 continue; 990 } 991 992 switch (section) { 993 case P_ATTRIBUTE: 994 ret = ParseAttribute(buffer, &file, &attrName, &nameSize, 995 &attrType, &attrData, &dataSize, &temp, &tempSize, 996 &attrCSize, &attrOSize, &attrStarted, &done); 997 break; 998 999 case P_DATA: 1000 ret = ParseData(buffer, &file, fOriginalSize, &done); 1001 break; 1002 1003 default: 1004 return B_ERROR; 1005 } 1006 1007 if (ret != B_OK || done) 1008 break; 1009 } 1010 1011 delete[] attrData; 1012 delete[] temp; 1013 } 1014 1015 return ret; 1016 } 1017 1018 1019 const uint32 1020 PackageFile::ItemKind() 1021 { 1022 return P_KIND_FILE; 1023 } 1024 1025 1026 // #pragma mark - 1027 1028 1029 PackageLink::PackageLink(BFile *parent, const BString &path, 1030 const BString &link, uint8 type, uint32 ctime, uint32 mtime, 1031 uint32 mode, uint64 offset, uint64 size) 1032 : 1033 PackageItem(parent, path, type, ctime, mtime, offset, size), 1034 fMode(mode), 1035 fLink(link) 1036 { 1037 } 1038 1039 1040 status_t 1041 PackageLink::DoInstall(const char *path, ItemState *state) 1042 { 1043 if (state == NULL) 1044 return B_ERROR; 1045 1046 status_t ret = B_OK; 1047 BSymLink symlink; 1048 parser_debug("Symlink: %s DoInstall() called!\n", fPath.String()); 1049 1050 BPath &destination = state->destination; 1051 BDirectory *dir = &state->parent; 1052 1053 if (state->status == B_NO_INIT || destination.InitCheck() != B_OK 1054 || dir->InitCheck() != B_OK) { 1055 // Not yet initialized 1056 ret = InitPath(path, &destination); 1057 if (ret != B_OK) 1058 return ret; 1059 1060 BString linkName(destination.Leaf()); 1061 parser_debug("%s:%s:%s\n", fPath.String(), destination.Path(), 1062 linkName.String()); 1063 1064 BPath dirPath; 1065 ret = destination.GetParent(&dirPath); 1066 ret = dir->SetTo(dirPath.Path()); 1067 1068 if (ret == B_ENTRY_NOT_FOUND) { 1069 ret = create_directory(dirPath.Path(), kDefaultMode); 1070 if (ret != B_OK) { 1071 parser_debug("create_directory()) failed\n"); 1072 return B_ERROR; 1073 } 1074 } 1075 if (ret != B_OK) { 1076 parser_debug("destination InitCheck failed %s for %s\n", 1077 strerror(ret), dirPath.Path()); 1078 return ret; 1079 } 1080 1081 ret = dir->CreateSymLink(destination.Path(), fLink.String(), &symlink); 1082 if (ret == B_FILE_EXISTS) { 1083 // We need to check if the existing symlink is pointing at the same path 1084 // as our new one - if not, let's prompt the user 1085 symlink.SetTo(destination.Path()); 1086 BPath oldLink; 1087 1088 ret = symlink.MakeLinkedPath(dir, &oldLink); 1089 chdir(dirPath.Path()); 1090 1091 if (ret == B_BAD_VALUE || oldLink != fLink.String()) 1092 state->status = ret = B_FILE_EXISTS; 1093 else 1094 ret = B_OK; 1095 } 1096 } 1097 1098 if (state->status == B_FILE_EXISTS) { 1099 switch (state->policy) { 1100 case P_EXISTS_OVERWRITE: 1101 { 1102 BEntry entry; 1103 ret = entry.SetTo(destination.Path()); 1104 if (ret != B_OK) 1105 return ret; 1106 1107 entry.Remove(); 1108 ret = dir->CreateSymLink(destination.Path(), fLink.String(), 1109 &symlink); 1110 break; 1111 } 1112 1113 case P_EXISTS_NONE: 1114 case P_EXISTS_ASK: 1115 ret = B_FILE_EXISTS; 1116 break; 1117 1118 case P_EXISTS_SKIP: 1119 return B_OK; 1120 } 1121 } 1122 1123 if (ret != B_OK) { 1124 parser_debug("CreateSymLink failed\n"); 1125 return ret; 1126 } 1127 1128 parser_debug(" Symlink created!\n"); 1129 1130 ret = symlink.SetPermissions(static_cast<mode_t>(fMode)); 1131 1132 if (fCreationTime && ret == B_OK) 1133 ret = symlink.SetCreationTime(static_cast<time_t>(fCreationTime)); 1134 1135 if (fModificationTime && ret == B_OK) { 1136 ret = symlink.SetModificationTime(static_cast<time_t>( 1137 fModificationTime)); 1138 } 1139 1140 if (ret != B_OK) { 1141 parser_debug("Failed to set symlink attributes\n"); 1142 return ret; 1143 } 1144 1145 if (fOffset) { 1146 // Symlinks also seem to have attributes - so parse them 1147 ret = HandleAttributes(&destination, &symlink, "LnDa"); 1148 } 1149 1150 return ret; 1151 } 1152 1153 1154 const uint32 1155 PackageLink::ItemKind() 1156 { 1157 return P_KIND_SYM_LINK; 1158 } 1159 1160