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 delete[] attrData; 297 delete[] temp; 298 } 299 300 return ret; 301 } 302 303 304 inline status_t 305 PkgDirectory::_ParseAttribute(uint8 *buffer, BNode *node, char **attrName, 306 uint32 *nameSize, uint32 *attrType, uint8 **attrData, uint64 *dataSize, 307 uint8 **temp, uint64 *tempSize, uint64 *attrCSize, uint64 *attrOSize, 308 bool *attrStarted, bool *done) 309 { 310 status_t ret = B_OK; 311 uint32 length; 312 313 if (!memcmp(buffer, "BeAI", 5)) { 314 parser_debug(" Attribute started.\n"); 315 if (*attrName) 316 *attrName[0] = 0; 317 *attrCSize = 0; 318 *attrOSize = 0; 319 320 *attrStarted = true; 321 } 322 else if (!memcmp(buffer, "BeAN", 5)) { 323 if (!*attrStarted) { 324 ret = B_ERROR; 325 return ret; 326 } 327 328 parser_debug(" BeAN.\n"); 329 fPackage->Read(&length, 4); 330 swap_data(B_UINT32_TYPE, &length, sizeof(uint32), B_SWAP_BENDIAN_TO_HOST); 331 332 if (*nameSize < (length + 1)) { 333 delete *attrName; 334 *nameSize = length + 1; 335 *attrName = new char[*nameSize]; 336 } 337 fPackage->Read(*attrName, length); 338 (*attrName)[length] = 0; 339 340 parser_debug(" (%d) = %s\n", length, *attrName); 341 } 342 else if (!memcmp(buffer, "BeAT", 5)) { 343 if (!*attrStarted) { 344 ret = B_ERROR; 345 return ret; 346 } 347 348 parser_debug(" BeAT.\n"); 349 fPackage->Read(attrType, 4); 350 swap_data(B_UINT32_TYPE, attrType, sizeof(*attrType), 351 B_SWAP_BENDIAN_TO_HOST); 352 } 353 else if (!memcmp(buffer, "BeAD", 5)) { 354 if (!*attrStarted) { 355 ret = B_ERROR; 356 return ret; 357 } 358 359 parser_debug(" BeAD.\n"); 360 fPackage->Read(attrCSize, 8); 361 swap_data(B_UINT64_TYPE, attrCSize, sizeof(*attrCSize), 362 B_SWAP_BENDIAN_TO_HOST); 363 364 fPackage->Read(attrOSize, 8); 365 swap_data(B_UINT64_TYPE, attrOSize, sizeof(*attrOSize), 366 B_SWAP_BENDIAN_TO_HOST); 367 368 fPackage->Seek(4, SEEK_CUR); // TODO: Check what this means 369 370 if (*tempSize < *attrCSize) { 371 delete *temp; 372 *tempSize = *attrCSize; 373 *temp = new uint8[*tempSize]; 374 } 375 if (*dataSize < *attrOSize) { 376 delete *attrData; 377 *dataSize = *attrOSize; 378 *attrData = new uint8[*dataSize]; 379 } 380 381 if (fPackage->Read(*temp, *attrCSize) 382 != static_cast<ssize_t>(*attrCSize)) { 383 ret = B_ERROR; 384 return ret; 385 } 386 387 parser_debug(" Data read successfuly. Inflating!\n"); 388 ret = inflate_data(*temp, *tempSize, *attrData, *dataSize); 389 if (ret != B_OK) 390 return ret; 391 } 392 else if (!memcmp(buffer, padding, 7)) { 393 if (!*attrStarted) { 394 *done = true; 395 return ret; 396 } 397 398 parser_debug(" Padding.\n"); 399 ssize_t wrote = node->WriteAttr(*attrName, *attrType, 0, *attrData, 400 *attrOSize); 401 if(wrote != static_cast<ssize_t>(*attrOSize)) { 402 ret = B_ERROR; 403 return ret; 404 } 405 406 *attrStarted = false; 407 if (*attrName) 408 *attrName[0] = 0; 409 *attrCSize = 0; 410 *attrOSize = 0; 411 412 parser_debug(" > Attribute added.\n"); 413 } 414 else { 415 ret = B_ERROR; 416 } 417 418 return ret; 419 } 420 421 422 inline status_t 423 PkgDirectory::_ParseData(uint8 *buffer, BFile *file, uint64 originalSize, 424 bool *done) 425 { 426 status_t ret = B_OK; 427 428 if (!memcmp(buffer, "FiMF", 5)) { 429 parser_debug(" Found file data.\n"); 430 uint64 compressed, original; 431 fPackage->Read(&compressed, 8); 432 swap_data(B_UINT64_TYPE, &compressed, sizeof(uint64), 433 B_SWAP_BENDIAN_TO_HOST); 434 435 fPackage->Read(&original, 8); 436 swap_data(B_UINT64_TYPE, &original, sizeof(uint64), 437 B_SWAP_BENDIAN_TO_HOST); 438 parser_debug(" Still good... (%llu : %llu)\n", original, 439 originalSize); 440 441 if (original != originalSize) { 442 ret = B_ERROR; // File size missmatch 443 return ret; 444 } 445 parser_debug(" Still good...\n"); 446 447 if (fPackage->Read(buffer, 4) != 4) { 448 ret = B_ERROR; 449 return ret; 450 } 451 parser_debug(" Still good...\n"); 452 453 ret = inflate_file_to_file(fPackage, compressed, file, original); 454 if (ret != B_OK) 455 return ret; 456 parser_debug(" File data inflation complete!\n"); 457 } 458 else if (!memcmp(buffer, padding, 7)) { 459 *done = true; 460 return ret; 461 } 462 else { 463 ret = B_ERROR; 464 } 465 466 return ret; 467 } 468 469 470 471 PkgFile::PkgFile(BFile *parent, BString path, uint8 type, uint32 ctime, 472 uint32 mtime, uint64 offset, uint64 size, uint64 originalSize, 473 uint32 platform, BString mime, BString signature, uint32 mode) 474 : PkgItem(parent, path, type, ctime, mtime, offset, size), 475 fOriginalSize(originalSize), 476 fPlatform(platform), 477 fMode(mode), 478 fMimeType(mime), 479 fSignature(signature) 480 { 481 } 482 483 484 PkgFile::~PkgFile() 485 { 486 } 487 488 489 status_t 490 PkgFile::WriteToPath(const char *path, BPath *final) 491 { 492 BPath destination; 493 status_t ret; 494 parser_debug("File: %s WriteToPath() called!\n", fPath.String()); 495 496 ret = _InitPath(path, &destination); 497 if (ret != B_OK) 498 return ret; 499 500 BFile file(destination.Path(), B_WRITE_ONLY | B_CREATE_FILE | B_FAIL_IF_EXISTS); 501 ret = file.InitCheck(); 502 if (ret == B_FILE_EXISTS) { 503 int32 selection = _ItemExists(destination.Leaf()); 504 switch (selection) { 505 case 0: 506 ret = file.SetTo(destination.Path(), B_WRITE_ONLY | B_ERASE_FILE); 507 if (ret != B_OK) 508 return ret; 509 break; 510 case 1: 511 return B_OK; 512 default: 513 return B_FILE_EXISTS; 514 } 515 } 516 else if (ret == B_ENTRY_NOT_FOUND) { 517 BPath directory; 518 destination.GetParent(&directory); 519 if (create_directory(directory.Path(), kDefaultMode) != B_OK) 520 return B_ERROR; 521 522 ret = file.SetTo(destination.Path(), B_WRITE_ONLY | B_CREATE_FILE); 523 if (ret != B_OK) 524 return ret; 525 } 526 else if (ret != B_OK) 527 return ret; 528 529 parser_debug(" File created!\n"); 530 531 // Set the file permissions, creation and modification times 532 ret = file.SetPermissions(static_cast<mode_t>(fMode)); 533 if (fCreationTime) 534 ret |= file.SetCreationTime(static_cast<time_t>(fCreationTime)); 535 if (fModificationTime) 536 ret |= file.SetModificationTime(static_cast<time_t>(fModificationTime)); 537 538 if (ret != B_OK) 539 return ret; 540 541 // Set the mimetype and application signature if present 542 BNodeInfo info(&file); 543 if (fMimeType.Length() > 0) { 544 ret = info.SetType(fMimeType.String()); 545 if (ret != B_OK) 546 return ret; 547 } 548 if (fSignature.Length() > 0) { 549 ret = info.SetPreferredApp(fSignature.String()); 550 if (ret != B_OK) 551 return ret; 552 } 553 554 if (fOffset) { 555 parser_debug("We have an offset\n"); 556 if (!fPackage) 557 return B_ERROR; 558 559 ret = fPackage->InitCheck(); 560 if (ret != B_OK) 561 return ret; 562 563 // We need to parse the data section now 564 fPackage->Seek(fOffset, SEEK_SET); 565 uint8 buffer[7]; 566 567 char *attrName = 0; 568 uint32 nameSize = 0; 569 uint8 *attrData = new uint8[P_CHUNK_SIZE]; 570 uint64 dataSize = P_CHUNK_SIZE; 571 uint8 *temp = new uint8[P_CHUNK_SIZE]; 572 uint64 tempSize = P_CHUNK_SIZE; 573 574 uint64 attrCSize = 0, attrOSize = 0; 575 uint32 attrType = 0; // type_code type 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 } 586 else if (!memcmp(buffer, "FiDa", 5)) { 587 parser_debug("-> File data\n"); 588 section = P_DATA; 589 continue; 590 } 591 592 switch (section) { 593 case P_ATTRIBUTE: 594 { 595 ret = _ParseAttribute(buffer, &file, &attrName, &nameSize, &attrType, 596 &attrData, &dataSize, &temp, &tempSize, &attrCSize, &attrOSize, 597 &attrStarted, &done); 598 break; 599 } 600 case P_DATA: 601 { 602 ret = _ParseData(buffer, &file, fOriginalSize, &done); 603 break; 604 } 605 default: 606 return B_ERROR; 607 } 608 609 if (ret != B_OK || done) 610 break; 611 } 612 613 delete[] attrData; 614 delete[] temp; 615 } 616 617 if (final) { 618 *final = destination; 619 } 620 621 return ret; 622 } 623 624 625 PkgLink::PkgLink(BFile *parent, BString path, BString link, uint8 type, 626 uint32 ctime, uint32 mtime, uint32 mode, uint64 offset, uint64 size) 627 : PkgItem(parent, path, type, ctime, mtime, offset, size), 628 fMode(mode), 629 fLink(link) 630 { 631 } 632 633 634 PkgLink::~PkgLink() 635 { 636 } 637 638 639 status_t 640 PkgLink::WriteToPath(const char *path, BPath *final) 641 { 642 BPath destination; 643 status_t ret; 644 parser_debug("Symlink: %s WriteToPath() called!\n", fPath.String()); 645 646 ret = _InitPath(path, &destination); 647 if (ret != B_OK) 648 return ret; 649 650 BString linkName(destination.Leaf()); 651 parser_debug("%s:%s:%s\n", fPath.String(), destination.Path(), linkName.String()); 652 BPath dirPath; 653 ret = destination.GetParent(&dirPath); 654 BDirectory dir(dirPath.Path()); 655 656 ret = dir.InitCheck(); 657 if (ret == B_ENTRY_NOT_FOUND) { 658 if (create_directory(destination.Path(), kDefaultMode) != B_OK) 659 return B_ERROR; 660 } 661 if (ret != B_OK) 662 return ret; 663 664 BSymLink symlink; 665 ret = dir.CreateSymLink(linkName.String(), fLink.String(), &symlink); 666 if (ret != B_OK) 667 return ret; 668 669 parser_debug(" Symlink created!\n"); 670 671 ret = symlink.SetPermissions(static_cast<mode_t>(fMode)); 672 673 if (fCreationTime) 674 ret |= symlink.SetCreationTime(static_cast<time_t>(fCreationTime)); 675 676 if (fModificationTime) 677 ret |= symlink.SetModificationTime(static_cast<time_t>(fModificationTime)); 678 679 if (ret != B_OK) 680 return ret; 681 682 if (fOffset) { 683 // Simlinks also seem to have attributes - so parse them 684 ret = _HandleAttributes(&destination, &dir, "LnDa"); 685 } 686 687 if (final) { 688 *final = destination; 689 } 690 691 return ret; 692 } 693 694