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