1 /* 2 * Copyright 2009, Stephan Aßmus <superstippi@gmx.de>. 3 * Copyright 2005-2008, Jérôme DUVAL. 4 * All rights reserved. Distributed under the terms of the MIT License. 5 */ 6 7 #include "WorkerThread.h" 8 9 #include <errno.h> 10 #include <stdio.h> 11 12 #include <set> 13 #include <string> 14 #include <strings.h> 15 16 #include <Alert.h> 17 #include <Autolock.h> 18 #include <Catalog.h> 19 #include <Directory.h> 20 #include <DiskDeviceVisitor.h> 21 #include <DiskDeviceTypes.h> 22 #include <FindDirectory.h> 23 #include <fs_index.h> 24 #include <Locale.h> 25 #include <Menu.h> 26 #include <MenuItem.h> 27 #include <Message.h> 28 #include <Messenger.h> 29 #include <Path.h> 30 #include <String.h> 31 #include <VolumeRoster.h> 32 33 #include "AutoLocker.h" 34 #include "CopyEngine.h" 35 #include "InstallerDefs.h" 36 #include "PackageViews.h" 37 #include "PartitionMenuItem.h" 38 #include "ProgressReporter.h" 39 #include "StringForSize.h" 40 #include "UnzipEngine.h" 41 42 43 #define B_TRANSLATION_CONTEXT "InstallProgress" 44 45 46 //#define COPY_TRACE 47 #ifdef COPY_TRACE 48 #define CALLED() printf("CALLED %s\n",__PRETTY_FUNCTION__) 49 #define ERR2(x, y...) fprintf(stderr, "WorkerThread: "x" %s\n", y, strerror(err)) 50 #define ERR(x) fprintf(stderr, "WorkerThread: "x" %s\n", strerror(err)) 51 #else 52 #define CALLED() 53 #define ERR(x) 54 #define ERR2(x, y...) 55 #endif 56 57 const char BOOT_PATH[] = "/boot"; 58 59 const uint32 MSG_START_INSTALLING = 'eSRT'; 60 61 62 class SourceVisitor : public BDiskDeviceVisitor { 63 public: 64 SourceVisitor(BMenu* menu); 65 virtual bool Visit(BDiskDevice* device); 66 virtual bool Visit(BPartition* partition, int32 level); 67 68 private: 69 BMenu* fMenu; 70 }; 71 72 73 class TargetVisitor : public BDiskDeviceVisitor { 74 public: 75 TargetVisitor(BMenu* menu); 76 virtual bool Visit(BDiskDevice* device); 77 virtual bool Visit(BPartition* partition, int32 level); 78 79 private: 80 BMenu* fMenu; 81 }; 82 83 84 // #pragma mark - WorkerThread 85 86 87 class WorkerThread::EntryFilter : public CopyEngine::EntryFilter { 88 public: 89 EntryFilter(const char* sourceDirectory) 90 : 91 fIgnorePaths(), 92 fSourceDevice(-1) 93 { 94 try { 95 fIgnorePaths.insert(kPackagesDirectoryPath); 96 fIgnorePaths.insert(kSourcesDirectoryPath); 97 fIgnorePaths.insert("rr_moved"); 98 fIgnorePaths.insert("boot.catalog"); 99 fIgnorePaths.insert("haiku-boot-floppy.image"); 100 fIgnorePaths.insert("system/var/swap"); 101 fIgnorePaths.insert("system/var/shared_memory"); 102 fIgnorePaths.insert("system/var/log/syslog"); 103 fIgnorePaths.insert("system/var/log/syslog.old"); 104 105 fPackageFSRootPaths.insert("system"); 106 fPackageFSRootPaths.insert("home/config"); 107 } catch (std::bad_alloc&) { 108 } 109 110 struct stat st; 111 if (stat(sourceDirectory, &st) == 0) 112 fSourceDevice = st.st_dev; 113 } 114 115 virtual bool ShouldCopyEntry(const BEntry& entry, const char* path, 116 const struct stat& statInfo, int32 level) const 117 { 118 if (fIgnorePaths.find(path) != fIgnorePaths.end()) { 119 printf("ignoring '%s'.\n", path); 120 return false; 121 } 122 123 if (statInfo.st_dev != fSourceDevice) { 124 // Allow that only for the root of the packagefs mounts, since 125 // those contain directories that shine through from the 126 // underlying volume. 127 if (fPackageFSRootPaths.find(path) == fPackageFSRootPaths.end()) 128 return false; 129 } 130 131 return true; 132 } 133 134 virtual bool ShouldClobberFolder(const BEntry& entry, const char* path, 135 const struct stat& statInfo, int32 level) const 136 { 137 if (level == 2 && S_ISDIR(statInfo.st_mode) 138 && strncmp("system/", path, 7) == 0 139 && strcmp("system/settings", path) != 0) { 140 // Replace everything in "system" besides "settings" 141 printf("clobbering '%s'.\n", path); 142 return true; 143 } 144 return false; 145 } 146 147 private: 148 typedef std::set<std::string> StringSet; 149 150 StringSet fIgnorePaths; 151 StringSet fPackageFSRootPaths; 152 dev_t fSourceDevice; 153 }; 154 155 156 // #pragma mark - WorkerThread 157 158 159 WorkerThread::WorkerThread(const BMessenger& owner) 160 : 161 BLooper("copy_engine"), 162 fOwner(owner), 163 fPackages(NULL), 164 fSpaceRequired(0), 165 fCancelSemaphore(-1) 166 { 167 Run(); 168 } 169 170 171 void 172 WorkerThread::MessageReceived(BMessage* message) 173 { 174 CALLED(); 175 176 switch (message->what) { 177 case MSG_START_INSTALLING: 178 _PerformInstall(message->GetInt32("source", -1), 179 message->GetInt32("target", -1)); 180 break; 181 182 case MSG_WRITE_BOOT_SECTOR: 183 { 184 int32 id; 185 if (message->FindInt32("id", &id) != B_OK) { 186 _SetStatusMessage(B_TRANSLATE("Boot sector not written " 187 "because of an internal error.")); 188 break; 189 } 190 191 // TODO: Refactor with _PerformInstall() 192 BPath targetDirectory; 193 BDiskDevice device; 194 BPartition* partition; 195 196 if (fDDRoster.GetPartitionWithID(id, &device, &partition) == B_OK) { 197 if (!partition->IsMounted()) { 198 if (partition->Mount() < B_OK) { 199 _SetStatusMessage(B_TRANSLATE("The partition can't be " 200 "mounted. Please choose a different partition.")); 201 break; 202 } 203 } 204 if (partition->GetMountPoint(&targetDirectory) != B_OK) { 205 _SetStatusMessage(B_TRANSLATE("The mount point could not " 206 "be retrieved.")); 207 break; 208 } 209 } else if (fDDRoster.GetDeviceWithID(id, &device) == B_OK) { 210 if (!device.IsMounted()) { 211 if (device.Mount() < B_OK) { 212 _SetStatusMessage(B_TRANSLATE("The disk can't be " 213 "mounted. Please choose a different disk.")); 214 break; 215 } 216 } 217 if (device.GetMountPoint(&targetDirectory) != B_OK) { 218 _SetStatusMessage(B_TRANSLATE("The mount point could not " 219 "be retrieved.")); 220 break; 221 } 222 } 223 224 if (_LaunchFinishScript(targetDirectory) != B_OK) { 225 _SetStatusMessage( 226 B_TRANSLATE("Error writing boot sector.")); 227 break; 228 } 229 _SetStatusMessage( 230 B_TRANSLATE("Boot sector successfully written.")); 231 } 232 default: 233 BLooper::MessageReceived(message); 234 } 235 } 236 237 238 239 240 void 241 WorkerThread::ScanDisksPartitions(BMenu *srcMenu, BMenu *targetMenu) 242 { 243 // NOTE: This is actually executed in the window thread. 244 BDiskDevice device; 245 BPartition *partition = NULL; 246 247 printf("\nScanDisksPartitions source partitions begin\n"); 248 SourceVisitor srcVisitor(srcMenu); 249 fDDRoster.VisitEachMountedPartition(&srcVisitor, &device, &partition); 250 251 printf("\nScanDisksPartitions target partitions begin\n"); 252 TargetVisitor targetVisitor(targetMenu); 253 fDDRoster.VisitEachPartition(&targetVisitor, &device, &partition); 254 } 255 256 257 void 258 WorkerThread::SetPackagesList(BList *list) 259 { 260 // Executed in window thread. 261 BAutolock _(this); 262 263 delete fPackages; 264 fPackages = list; 265 } 266 267 268 void 269 WorkerThread::StartInstall(partition_id sourcePartitionID, 270 partition_id targetPartitionID) 271 { 272 // Executed in window thread. 273 BMessage message(MSG_START_INSTALLING); 274 message.AddInt32("source", sourcePartitionID); 275 message.AddInt32("target", targetPartitionID); 276 277 PostMessage(&message, this); 278 } 279 280 281 void 282 WorkerThread::WriteBootSector(BMenu* targetMenu) 283 { 284 // Executed in window thread. 285 CALLED(); 286 287 PartitionMenuItem* item = (PartitionMenuItem*)targetMenu->FindMarked(); 288 if (item == NULL) { 289 ERR("bad menu items\n"); 290 return; 291 } 292 293 BMessage message(MSG_WRITE_BOOT_SECTOR); 294 message.AddInt32("id", item->ID()); 295 PostMessage(&message, this); 296 } 297 298 299 // #pragma mark - 300 301 302 status_t 303 WorkerThread::_LaunchInitScript(BPath &path) 304 { 305 BPath bootPath; 306 find_directory(B_BEOS_BOOT_DIRECTORY, &bootPath); 307 BString command("/bin/sh "); 308 command += bootPath.Path(); 309 command += "/InstallerInitScript "; 310 command += "\""; 311 command += path.Path(); 312 command += "\""; 313 _SetStatusMessage(B_TRANSLATE("Starting installation.")); 314 return system(command.String()); 315 } 316 317 318 status_t 319 WorkerThread::_LaunchFinishScript(BPath &path) 320 { 321 BPath bootPath; 322 find_directory(B_BEOS_BOOT_DIRECTORY, &bootPath); 323 BString command("/bin/sh "); 324 command += bootPath.Path(); 325 command += "/InstallerFinishScript "; 326 command += "\""; 327 command += path.Path(); 328 command += "\""; 329 _SetStatusMessage(B_TRANSLATE("Finishing installation.")); 330 return system(command.String()); 331 } 332 333 334 status_t 335 WorkerThread::_PerformInstall(partition_id sourcePartitionID, 336 partition_id targetPartitionID) 337 { 338 CALLED(); 339 340 BPath targetDirectory; 341 BPath srcDirectory; 342 BPath trashPath; 343 BPath testPath; 344 BDirectory targetDir; 345 BDiskDevice device; 346 BPartition* partition; 347 BVolume targetVolume; 348 status_t err = B_OK; 349 int32 entries = 0; 350 entry_ref testRef; 351 const char* mountError = B_TRANSLATE("The disk can't be mounted. Please " 352 "choose a different disk."); 353 354 if (sourcePartitionID < 0 || targetPartitionID < 0) { 355 ERR("bad source or target partition ID\n"); 356 return _InstallationError(err); 357 } 358 359 // check if target is initialized 360 // ask if init or mount as is 361 if (fDDRoster.GetPartitionWithID(targetPartitionID, &device, 362 &partition) == B_OK) { 363 if (!partition->IsMounted()) { 364 if ((err = partition->Mount()) < B_OK) { 365 _SetStatusMessage(mountError); 366 ERR("BPartition::Mount"); 367 return _InstallationError(err); 368 } 369 } 370 if ((err = partition->GetVolume(&targetVolume)) != B_OK) { 371 ERR("BPartition::GetVolume"); 372 return _InstallationError(err); 373 } 374 if ((err = partition->GetMountPoint(&targetDirectory)) != B_OK) { 375 ERR("BPartition::GetMountPoint"); 376 return _InstallationError(err); 377 } 378 } else if (fDDRoster.GetDeviceWithID(targetPartitionID, &device) == B_OK) { 379 if (!device.IsMounted()) { 380 if ((err = device.Mount()) < B_OK) { 381 _SetStatusMessage(mountError); 382 ERR("BDiskDevice::Mount"); 383 return _InstallationError(err); 384 } 385 } 386 if ((err = device.GetVolume(&targetVolume)) != B_OK) { 387 ERR("BDiskDevice::GetVolume"); 388 return _InstallationError(err); 389 } 390 if ((err = device.GetMountPoint(&targetDirectory)) != B_OK) { 391 ERR("BDiskDevice::GetMountPoint"); 392 return _InstallationError(err); 393 } 394 } else 395 return _InstallationError(err); // shouldn't happen 396 397 // check if target has enough space 398 if (fSpaceRequired > 0 && targetVolume.FreeBytes() < fSpaceRequired) { 399 BAlert* alert = new BAlert("", B_TRANSLATE("The destination disk may " 400 "not have enough space. Try choosing a different disk or choose " 401 "to not install optional items."), 402 B_TRANSLATE("Try installing anyway"), B_TRANSLATE("Cancel"), 0, 403 B_WIDTH_AS_USUAL, B_STOP_ALERT); 404 alert->SetShortcut(1, B_ESCAPE); 405 if (alert->Go() != 0) 406 return _InstallationError(err); 407 } 408 409 if (fDDRoster.GetPartitionWithID(sourcePartitionID, &device, &partition) 410 == B_OK) { 411 if ((err = partition->GetMountPoint(&srcDirectory)) != B_OK) { 412 ERR("BPartition::GetMountPoint"); 413 return _InstallationError(err); 414 } 415 } else if (fDDRoster.GetDeviceWithID(sourcePartitionID, &device) == B_OK) { 416 if ((err = device.GetMountPoint(&srcDirectory)) != B_OK) { 417 ERR("BDiskDevice::GetMountPoint"); 418 return _InstallationError(err); 419 } 420 } else 421 return _InstallationError(err); // shouldn't happen 422 423 // check not installing on itself 424 if (strcmp(srcDirectory.Path(), targetDirectory.Path()) == 0) { 425 _SetStatusMessage(B_TRANSLATE("You can't install the contents of a " 426 "disk onto itself. Please choose a different disk.")); 427 return _InstallationError(err); 428 } 429 430 // check not installing on boot volume 431 if (strncmp(BOOT_PATH, targetDirectory.Path(), strlen(BOOT_PATH)) == 0) { 432 BAlert* alert = new BAlert("", B_TRANSLATE("Are you sure you want to " 433 "install onto the current boot disk? The Installer will have to " 434 "reboot your machine if you proceed."), B_TRANSLATE("OK"), 435 B_TRANSLATE("Cancel"), 0, B_WIDTH_AS_USUAL, B_STOP_ALERT); 436 alert->SetShortcut(1, B_ESCAPE); 437 if (alert->Go() != 0) { 438 _SetStatusMessage("Installation stopped."); 439 return _InstallationError(err); 440 } 441 } 442 443 // check if target volume's trash dir has anything in it 444 // (target volume w/ only an empty trash dir is considered 445 // an empty volume) 446 if (find_directory(B_TRASH_DIRECTORY, &trashPath, false, 447 &targetVolume) == B_OK && targetDir.SetTo(trashPath.Path()) == B_OK) { 448 while (targetDir.GetNextRef(&testRef) == B_OK) { 449 // Something in the Trash 450 entries++; 451 break; 452 } 453 } 454 455 targetDir.SetTo(targetDirectory.Path()); 456 457 // check if target volume otherwise has any entries 458 while (entries == 0 && targetDir.GetNextRef(&testRef) == B_OK) { 459 if (testPath.SetTo(&testRef) == B_OK && testPath != trashPath) 460 entries++; 461 } 462 463 if (entries != 0) { 464 BAlert* alert = new BAlert("", B_TRANSLATE("The target volume is not " 465 "empty. Are you sure you want to install anyway?\n\nNote: The " 466 "'system' folder will be a clean copy from the source volume but " 467 "will retain its settings folder, all other folders will be " 468 "merged, whereas files and links that exist on both the source " 469 "and target volume will be overwritten with the source volume " 470 "version."), 471 B_TRANSLATE("Install anyway"), B_TRANSLATE("Cancel"), 0, 472 B_WIDTH_AS_USUAL, B_STOP_ALERT); 473 alert->SetShortcut(1, B_ESCAPE); 474 if (alert->Go() != 0) { 475 // TODO: Would be cool to offer the option here to clean additional 476 // folders at the user's choice. 477 return _InstallationError(B_CANCELED); 478 } 479 } 480 481 // Begin actual installation 482 483 ProgressReporter reporter(fOwner, new BMessage(MSG_STATUS_MESSAGE)); 484 EntryFilter entryFilter(srcDirectory.Path()); 485 CopyEngine engine(&reporter, &entryFilter); 486 BList unzipEngines; 487 488 err = _LaunchInitScript(targetDirectory); 489 if (err != B_OK) 490 return _InstallationError(err); 491 492 // Create the default indices which should always be present on a proper 493 // boot volume. We don't care if the source volume does not have them. 494 // After all, the user might be re-installing to another drive and may 495 // want problems fixed along the way... 496 err = _CreateDefaultIndices(targetDirectory); 497 if (err != B_OK) 498 return _InstallationError(err); 499 // Mirror all the indices which are present on the source volume onto 500 // the target volume. 501 err = _MirrorIndices(srcDirectory, targetDirectory); 502 if (err != B_OK) 503 return _InstallationError(err); 504 505 // Let the engine collect information for the progress bar later on 506 engine.ResetTargets(srcDirectory.Path()); 507 err = engine.CollectTargets(srcDirectory.Path(), fCancelSemaphore); 508 if (err != B_OK) 509 return _InstallationError(err); 510 511 // Collect selected packages also 512 if (fPackages) { 513 BPath pkgRootDir(srcDirectory.Path(), kPackagesDirectoryPath); 514 int32 count = fPackages->CountItems(); 515 for (int32 i = 0; i < count; i++) { 516 Package *p = static_cast<Package*>(fPackages->ItemAt(i)); 517 BPath packageDir(pkgRootDir.Path(), p->Folder()); 518 err = engine.CollectTargets(packageDir.Path(), fCancelSemaphore); 519 if (err != B_OK) 520 return _InstallationError(err); 521 } 522 } 523 524 // collect information about all zip packages 525 err = _ProcessZipPackages(srcDirectory.Path(), targetDirectory.Path(), 526 &reporter, unzipEngines); 527 if (err != B_OK) 528 return _InstallationError(err); 529 530 reporter.StartTimer(); 531 532 // copy source volume 533 err = engine.CopyFolder(srcDirectory.Path(), targetDirectory.Path(), 534 fCancelSemaphore); 535 if (err != B_OK) 536 return _InstallationError(err); 537 538 // copy selected packages 539 if (fPackages) { 540 BPath pkgRootDir(srcDirectory.Path(), kPackagesDirectoryPath); 541 int32 count = fPackages->CountItems(); 542 for (int32 i = 0; i < count; i++) { 543 Package *p = static_cast<Package*>(fPackages->ItemAt(i)); 544 BPath packageDir(pkgRootDir.Path(), p->Folder()); 545 err = engine.CopyFolder(packageDir.Path(), targetDirectory.Path(), 546 fCancelSemaphore); 547 if (err != B_OK) 548 return _InstallationError(err); 549 } 550 } 551 552 // Extract all zip packages. If an error occured, delete the rest of 553 // the engines, but stop extracting. 554 for (int32 i = 0; i < unzipEngines.CountItems(); i++) { 555 UnzipEngine* engine = reinterpret_cast<UnzipEngine*>( 556 unzipEngines.ItemAtFast(i)); 557 if (err == B_OK) 558 err = engine->UnzipPackage(); 559 delete engine; 560 } 561 if (err != B_OK) 562 return _InstallationError(err); 563 564 err = _LaunchFinishScript(targetDirectory); 565 if (err != B_OK) 566 return _InstallationError(err); 567 568 fOwner.SendMessage(MSG_INSTALL_FINISHED); 569 return B_OK; 570 } 571 572 573 status_t 574 WorkerThread::_InstallationError(status_t error) 575 { 576 BMessage statusMessage(MSG_RESET); 577 if (error == B_CANCELED) 578 _SetStatusMessage(B_TRANSLATE("Installation canceled.")); 579 else 580 statusMessage.AddInt32("error", error); 581 ERR("_PerformInstall failed"); 582 fOwner.SendMessage(&statusMessage); 583 return error; 584 } 585 586 587 status_t 588 WorkerThread::_MirrorIndices(const BPath& sourceDirectory, 589 const BPath& targetDirectory) const 590 { 591 dev_t sourceDevice = dev_for_path(sourceDirectory.Path()); 592 if (sourceDevice < 0) 593 return (status_t)sourceDevice; 594 dev_t targetDevice = dev_for_path(targetDirectory.Path()); 595 if (targetDevice < 0) 596 return (status_t)targetDevice; 597 DIR* indices = fs_open_index_dir(sourceDevice); 598 if (indices == NULL) { 599 printf("%s: fs_open_index_dir(): (%d) %s\n", sourceDirectory.Path(), 600 errno, strerror(errno)); 601 // Opening the index directory will fail for example on ISO-Live 602 // CDs. The default indices have already been created earlier, so 603 // we simply bail. 604 return B_OK; 605 } 606 while (dirent* index = fs_read_index_dir(indices)) { 607 if (strcmp(index->d_name, "name") == 0 608 || strcmp(index->d_name, "size") == 0 609 || strcmp(index->d_name, "last_modified") == 0) { 610 continue; 611 } 612 613 index_info info; 614 if (fs_stat_index(sourceDevice, index->d_name, &info) != B_OK) { 615 printf("Failed to mirror index %s: fs_stat_index(): (%d) %s\n", 616 index->d_name, errno, strerror(errno)); 617 continue; 618 } 619 620 uint32 flags = 0; 621 // Flags are always 0 for the moment. 622 if (fs_create_index(targetDevice, index->d_name, info.type, flags) 623 != B_OK) { 624 if (errno == B_FILE_EXISTS) 625 continue; 626 printf("Failed to mirror index %s: fs_create_index(): (%d) %s\n", 627 index->d_name, errno, strerror(errno)); 628 continue; 629 } 630 } 631 fs_close_index_dir(indices); 632 return B_OK; 633 } 634 635 636 status_t 637 WorkerThread::_CreateDefaultIndices(const BPath& targetDirectory) const 638 { 639 dev_t targetDevice = dev_for_path(targetDirectory.Path()); 640 if (targetDevice < 0) 641 return (status_t)targetDevice; 642 643 struct IndexInfo { 644 const char* name; 645 uint32_t type; 646 }; 647 648 const IndexInfo defaultIndices[] = { 649 { "BEOS:APP_SIG", B_STRING_TYPE }, 650 { "BEOS:LOCALE_LANGUAGE", B_STRING_TYPE }, 651 { "BEOS:LOCALE_SIGNATURE", B_STRING_TYPE }, 652 { "_trk/qrylastchange", B_INT32_TYPE }, 653 { "_trk/recentQuery", B_INT32_TYPE }, 654 { "be:deskbar_item_status", B_STRING_TYPE } 655 }; 656 657 uint32 flags = 0; 658 // Flags are always 0 for the moment. 659 660 for (uint32 i = 0; i < sizeof(defaultIndices) / sizeof(IndexInfo); i++) { 661 const IndexInfo& info = defaultIndices[i]; 662 if (fs_create_index(targetDevice, info.name, info.type, flags) 663 != B_OK) { 664 if (errno == B_FILE_EXISTS) 665 continue; 666 printf("Failed to create index %s: fs_create_index(): (%d) %s\n", 667 info.name, errno, strerror(errno)); 668 return errno; 669 } 670 } 671 672 return B_OK; 673 } 674 675 676 status_t 677 WorkerThread::_ProcessZipPackages(const char* sourcePath, 678 const char* targetPath, ProgressReporter* reporter, BList& unzipEngines) 679 { 680 // TODO: Put those in the optional packages list view 681 // TODO: Implement mechanism to handle dependencies between these 682 // packages. (Selecting one will auto-select others.) 683 BPath pkgRootDir(sourcePath, kPackagesDirectoryPath); 684 BDirectory directory(pkgRootDir.Path()); 685 BEntry entry; 686 while (directory.GetNextEntry(&entry) == B_OK) { 687 char name[B_FILE_NAME_LENGTH]; 688 if (entry.GetName(name) != B_OK) 689 continue; 690 int nameLength = strlen(name); 691 if (nameLength <= 0) 692 continue; 693 char* nameExtension = name + nameLength - 4; 694 if (strcasecmp(nameExtension, ".zip") != 0) 695 continue; 696 printf("found .zip package: %s\n", name); 697 698 UnzipEngine* unzipEngine = new(std::nothrow) UnzipEngine(reporter, 699 fCancelSemaphore); 700 if (unzipEngine == NULL || !unzipEngines.AddItem(unzipEngine)) { 701 delete unzipEngine; 702 return B_NO_MEMORY; 703 } 704 BPath path; 705 entry.GetPath(&path); 706 status_t ret = unzipEngine->SetTo(path.Path(), targetPath); 707 if (ret != B_OK) 708 return ret; 709 710 reporter->AddItems(unzipEngine->ItemsToUncompress(), 711 unzipEngine->BytesToUncompress()); 712 } 713 714 return B_OK; 715 } 716 717 718 void 719 WorkerThread::_SetStatusMessage(const char *status) 720 { 721 BMessage msg(MSG_STATUS_MESSAGE); 722 msg.AddString("status", status); 723 fOwner.SendMessage(&msg); 724 } 725 726 727 static void 728 make_partition_label(BPartition* partition, char* label, char* menuLabel, 729 bool showContentType) 730 { 731 char size[20]; 732 string_for_size(partition->Size(), size, sizeof(size)); 733 734 BPath path; 735 partition->GetPath(&path); 736 737 if (showContentType) { 738 const char* type = partition->ContentType(); 739 if (type == NULL) 740 type = B_TRANSLATE_COMMENT("Unknown Type", "Partition content type"); 741 742 sprintf(label, "%s - %s [%s] (%s)", partition->ContentName(), size, 743 path.Path(), type); 744 } else { 745 sprintf(label, "%s - %s [%s]", partition->ContentName(), size, 746 path.Path()); 747 } 748 749 sprintf(menuLabel, "%s - %s", partition->ContentName(), size); 750 } 751 752 753 // #pragma mark - SourceVisitor 754 755 756 SourceVisitor::SourceVisitor(BMenu *menu) 757 : fMenu(menu) 758 { 759 } 760 761 bool 762 SourceVisitor::Visit(BDiskDevice *device) 763 { 764 return Visit(device, 0); 765 } 766 767 768 bool 769 SourceVisitor::Visit(BPartition *partition, int32 level) 770 { 771 BPath path; 772 printf("SourceVisitor::Visit(BPartition *) : %s '%s'\n", 773 (partition->GetPath(&path) == B_OK) ? path.Path() : "", 774 partition->ContentName()); 775 776 if (partition->ContentType() == NULL) 777 return false; 778 779 bool isBootPartition = false; 780 if (partition->IsMounted()) { 781 BPath mountPoint; 782 if (partition->GetMountPoint(&mountPoint) != B_OK) 783 return false; 784 isBootPartition = strcmp(BOOT_PATH, mountPoint.Path()) == 0; 785 } 786 787 if (!isBootPartition 788 && strcmp(partition->ContentType(), kPartitionTypeBFS) != 0) { 789 // Except only BFS partitions, except this is the boot partition 790 // (ISO9660 with write overlay for example). 791 return false; 792 } 793 794 // TODO: We could probably check if this volume contains 795 // the Haiku kernel or something. Does it make sense to "install" 796 // from your BFS volume containing the music collection? 797 // TODO: Then the check for BFS could also be removed above. 798 799 char label[255]; 800 char menuLabel[255]; 801 make_partition_label(partition, label, menuLabel, false); 802 PartitionMenuItem* item = new PartitionMenuItem(partition->ContentName(), 803 label, menuLabel, new BMessage(SOURCE_PARTITION), partition->ID()); 804 item->SetMarked(isBootPartition); 805 fMenu->AddItem(item); 806 return false; 807 } 808 809 810 // #pragma mark - TargetVisitor 811 812 813 TargetVisitor::TargetVisitor(BMenu *menu) 814 : fMenu(menu) 815 { 816 } 817 818 819 bool 820 TargetVisitor::Visit(BDiskDevice *device) 821 { 822 if (device->IsReadOnlyMedia()) 823 return false; 824 return Visit(device, 0); 825 } 826 827 828 bool 829 TargetVisitor::Visit(BPartition *partition, int32 level) 830 { 831 BPath path; 832 if (partition->GetPath(&path) == B_OK) 833 printf("TargetVisitor::Visit(BPartition *) : %s\n", path.Path()); 834 printf("TargetVisitor::Visit(BPartition *) : %s\n", 835 partition->ContentName()); 836 837 if (partition->ContentSize() < 20 * 1024 * 1024) { 838 // reject partitions which are too small anyway 839 // TODO: Could depend on the source size 840 printf(" too small\n"); 841 return false; 842 } 843 844 if (partition->CountChildren() > 0) { 845 // Looks like an extended partition, or the device itself. 846 // Do not accept this as target... 847 printf(" no leaf partition\n"); 848 return false; 849 } 850 851 // TODO: After running DriveSetup and doing another scan, it would 852 // be great to pick the partition which just appeared! 853 854 bool isBootPartition = false; 855 if (partition->IsMounted()) { 856 BPath mountPoint; 857 partition->GetMountPoint(&mountPoint); 858 isBootPartition = strcmp(BOOT_PATH, mountPoint.Path()) == 0; 859 } 860 861 // Only non-boot BFS partitions are valid targets, but we want to display the 862 // other partitions as well, in order not to irritate the user. 863 bool isValidTarget = isBootPartition == false 864 && partition->ContentType() != NULL 865 && strcmp(partition->ContentType(), kPartitionTypeBFS) == 0; 866 867 char label[255]; 868 char menuLabel[255]; 869 make_partition_label(partition, label, menuLabel, !isValidTarget); 870 PartitionMenuItem* item = new PartitionMenuItem(partition->ContentName(), 871 label, menuLabel, new BMessage(TARGET_PARTITION), partition->ID()); 872 873 item->SetIsValidTarget(isValidTarget); 874 875 876 fMenu->AddItem(item); 877 return false; 878 } 879 880