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