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