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