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_TRANSLATE_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 && ((new BAlert("", B_TRANSLATE("The destination disk may not have " 321 "enough space. Try choosing a different disk or choose to not " 322 "install optional items."), B_TRANSLATE("Try installing anyway"), 323 B_TRANSLATE("Cancel"), 0, 324 B_WIDTH_AS_USUAL, B_STOP_ALERT))->Go() != 0)) { 325 goto error; 326 } 327 328 if (fDDRoster.GetPartitionWithID(srcItem->ID(), &device, &partition) == B_OK) { 329 if ((err = partition->GetMountPoint(&srcDirectory)) != B_OK) { 330 ERR("BPartition::GetMountPoint"); 331 goto error; 332 } 333 } else if (fDDRoster.GetDeviceWithID(srcItem->ID(), &device) == B_OK) { 334 if ((err = device.GetMountPoint(&srcDirectory)) != B_OK) { 335 ERR("BDiskDevice::GetMountPoint"); 336 goto error; 337 } 338 } else 339 goto error; // shouldn't happen 340 341 // check not installing on itself 342 if (strcmp(srcDirectory.Path(), targetDirectory.Path()) == 0) { 343 _SetStatusMessage(B_TRANSLATE("You can't install the contents of a " 344 "disk onto itself. Please choose a different disk.")); 345 goto error; 346 } 347 348 // check not installing on boot volume 349 if ((strncmp(BOOT_PATH, targetDirectory.Path(), strlen(BOOT_PATH)) == 0) 350 && ((new BAlert("", B_TRANSLATE("Are you sure you want to install " 351 "onto the current boot disk? The Installer will have to reboot " 352 "your machine if you proceed."), B_TRANSLATE("OK"), 353 B_TRANSLATE("Cancel"), 0, 354 B_WIDTH_AS_USUAL, B_STOP_ALERT))->Go() != 0)) { 355 _SetStatusMessage("Installation stopped."); 356 goto error; 357 } 358 359 // check if target volume's trash dir has anything in it 360 // (target volume w/ only an empty trash dir is considered 361 // an empty volume) 362 if (find_directory(B_TRASH_DIRECTORY, &trashPath, false, 363 &targetVolume) == B_OK && targetDir.SetTo(trashPath.Path()) == B_OK) { 364 while (targetDir.GetNextRef(&testRef) == B_OK) { 365 // Something in the Trash 366 entries++; 367 break; 368 } 369 } 370 371 targetDir.SetTo(targetDirectory.Path()); 372 373 // check if target volume otherwise has any entries 374 while (entries == 0 && targetDir.GetNextRef(&testRef) == B_OK) { 375 if (testPath.SetTo(&testRef) == B_OK && testPath != trashPath) 376 entries++; 377 } 378 379 if (entries != 0 380 && ((new BAlert("", B_TRANSLATE("The target volume is not empty. Are " 381 "you sure you want to install anyway?\n\nNote: The 'system' folder " 382 "will be a clean copy from the source volume, all other folders " 383 "will be merged, whereas files and links that exist on both the " 384 "source and target volume will be overwritten with the source " 385 "volume version."), 386 B_TRANSLATE("Install anyway"), B_TRANSLATE("Cancel"), 0, 387 B_WIDTH_AS_USUAL, B_STOP_ALERT))->Go() != 0)) { 388 // TODO: Would be cool to offer the option here to clean additional 389 // folders at the user's choice (like /boot/common and /boot/develop). 390 err = B_CANCELED; 391 goto error; 392 } 393 394 // Begin actual installation 395 396 _LaunchInitScript(targetDirectory); 397 398 // Create the default indices which should always be present on a proper 399 // boot volume. We don't care if the source volume does not have them. 400 // After all, the user might be re-installing to another drive and may 401 // want problems fixed along the way... 402 err = _CreateDefaultIndices(targetDirectory); 403 if (err != B_OK) 404 goto error; 405 // Mirror all the indices which are present on the source volume onto 406 // the target volume. 407 err = _MirrorIndices(srcDirectory, targetDirectory); 408 if (err != B_OK) 409 goto error; 410 411 // Let the engine collect information for the progress bar later on 412 engine.ResetTargets(srcDirectory.Path()); 413 err = engine.CollectTargets(srcDirectory.Path(), fCancelSemaphore); 414 if (err != B_OK) 415 goto error; 416 417 // Collect selected packages also 418 if (fPackages) { 419 BPath pkgRootDir(srcDirectory.Path(), PACKAGES_DIRECTORY); 420 int32 count = fPackages->CountItems(); 421 for (int32 i = 0; i < count; i++) { 422 Package *p = static_cast<Package*>(fPackages->ItemAt(i)); 423 BPath packageDir(pkgRootDir.Path(), p->Folder()); 424 err = engine.CollectTargets(packageDir.Path(), fCancelSemaphore); 425 if (err != B_OK) 426 goto error; 427 } 428 } 429 430 // collect information about all zip packages 431 err = _ProcessZipPackages(srcDirectory.Path(), targetDirectory.Path(), 432 &reporter, unzipEngines); 433 if (err != B_OK) 434 goto error; 435 436 reporter.StartTimer(); 437 438 // copy source volume 439 err = engine.CopyFolder(srcDirectory.Path(), targetDirectory.Path(), 440 fCancelSemaphore); 441 if (err != B_OK) 442 goto error; 443 444 // copy selected packages 445 if (fPackages) { 446 BPath pkgRootDir(srcDirectory.Path(), PACKAGES_DIRECTORY); 447 int32 count = fPackages->CountItems(); 448 for (int32 i = 0; i < count; i++) { 449 Package *p = static_cast<Package*>(fPackages->ItemAt(i)); 450 BPath packageDir(pkgRootDir.Path(), p->Folder()); 451 err = engine.CopyFolder(packageDir.Path(), targetDirectory.Path(), 452 fCancelSemaphore); 453 if (err != B_OK) 454 goto error; 455 } 456 } 457 458 // Extract all zip packages. If an error occured, delete the rest of 459 // the engines, but stop extracting. 460 for (int32 i = 0; i < unzipEngines.CountItems(); i++) { 461 UnzipEngine* engine = reinterpret_cast<UnzipEngine*>( 462 unzipEngines.ItemAtFast(i)); 463 if (err == B_OK) 464 err = engine->UnzipPackage(); 465 delete engine; 466 } 467 if (err != B_OK) 468 goto error; 469 470 _LaunchFinishScript(targetDirectory); 471 472 BMessenger(fWindow).SendMessage(MSG_INSTALL_FINISHED); 473 474 return; 475 error: 476 BMessage statusMessage(MSG_RESET); 477 if (err == B_CANCELED) 478 _SetStatusMessage(B_TRANSLATE("Installation canceled.")); 479 else 480 statusMessage.AddInt32("error", err); 481 ERR("_PerformInstall failed"); 482 BMessenger(fWindow).SendMessage(&statusMessage); 483 } 484 485 486 status_t 487 WorkerThread::_MirrorIndices(const BPath& sourceDirectory, 488 const BPath& targetDirectory) const 489 { 490 dev_t sourceDevice = dev_for_path(sourceDirectory.Path()); 491 if (sourceDevice < 0) 492 return (status_t)sourceDevice; 493 dev_t targetDevice = dev_for_path(targetDirectory.Path()); 494 if (targetDevice < 0) 495 return (status_t)targetDevice; 496 DIR* indices = fs_open_index_dir(sourceDevice); 497 if (indices == NULL) { 498 printf("%s: fs_open_index_dir(): (%d) %s\n", sourceDirectory.Path(), 499 errno, strerror(errno)); 500 // Opening the index directory will fail for example on ISO-Live 501 // CDs. The default indices have already been created earlier, so 502 // we simply bail. 503 return B_OK; 504 } 505 while (dirent* index = fs_read_index_dir(indices)) { 506 if (strcmp(index->d_name, "name") == 0 507 || strcmp(index->d_name, "size") == 0 508 || strcmp(index->d_name, "last_modified") == 0) { 509 continue; 510 } 511 512 index_info info; 513 if (fs_stat_index(sourceDevice, index->d_name, &info) != B_OK) { 514 printf("Failed to mirror index %s: fs_stat_index(): (%d) %s\n", 515 index->d_name, errno, strerror(errno)); 516 continue; 517 } 518 519 uint32 flags = 0; 520 // Flags are always 0 for the moment. 521 if (fs_create_index(targetDevice, index->d_name, info.type, flags) 522 != B_OK) { 523 if (errno == B_FILE_EXISTS) 524 continue; 525 printf("Failed to mirror index %s: fs_create_index(): (%d) %s\n", 526 index->d_name, errno, strerror(errno)); 527 continue; 528 } 529 } 530 fs_close_index_dir(indices); 531 return B_OK; 532 } 533 534 535 status_t 536 WorkerThread::_CreateDefaultIndices(const BPath& targetDirectory) const 537 { 538 dev_t targetDevice = dev_for_path(targetDirectory.Path()); 539 if (targetDevice < 0) 540 return (status_t)targetDevice; 541 542 struct IndexInfo { 543 const char* name; 544 uint32_t type; 545 }; 546 547 const IndexInfo defaultIndices[] = { 548 { "BEOS:APP_SIG", B_STRING_TYPE }, 549 { "BEOS:LOCALE_LANGUAGE", B_STRING_TYPE }, 550 { "BEOS:LOCALE_SIGNATURE", B_STRING_TYPE }, 551 { "_trk/qrylastchange", B_INT32_TYPE }, 552 { "_trk/recentQuery", B_INT32_TYPE }, 553 { "be:deskbar_item_status", B_STRING_TYPE } 554 }; 555 556 uint32 flags = 0; 557 // Flags are always 0 for the moment. 558 559 for (uint32 i = 0; i < sizeof(defaultIndices) / sizeof(IndexInfo); i++) { 560 const IndexInfo& info = defaultIndices[i]; 561 if (fs_create_index(targetDevice, info.name, info.type, flags) 562 != B_OK) { 563 if (errno == B_FILE_EXISTS) 564 continue; 565 printf("Failed to create index %s: fs_create_index(): (%d) %s\n", 566 info.name, errno, strerror(errno)); 567 return errno; 568 } 569 } 570 571 return B_OK; 572 } 573 574 575 status_t 576 WorkerThread::_ProcessZipPackages(const char* sourcePath, 577 const char* targetPath, ProgressReporter* reporter, BList& unzipEngines) 578 { 579 // TODO: Put those in the optional packages list view 580 // TODO: Implement mechanism to handle dependencies between these 581 // packages. (Selecting one will auto-select others.) 582 BPath pkgRootDir(sourcePath, PACKAGES_DIRECTORY); 583 BDirectory directory(pkgRootDir.Path()); 584 BEntry entry; 585 while (directory.GetNextEntry(&entry) == B_OK) { 586 char name[B_FILE_NAME_LENGTH]; 587 if (entry.GetName(name) != B_OK) 588 continue; 589 int nameLength = strlen(name); 590 if (nameLength <= 0) 591 continue; 592 char* nameExtension = name + nameLength - 4; 593 if (strcasecmp(nameExtension, ".zip") != 0) 594 continue; 595 printf("found .zip package: %s\n", name); 596 597 UnzipEngine* unzipEngine = new(std::nothrow) UnzipEngine(reporter, 598 fCancelSemaphore); 599 if (unzipEngine == NULL || !unzipEngines.AddItem(unzipEngine)) { 600 delete unzipEngine; 601 return B_NO_MEMORY; 602 } 603 BPath path; 604 entry.GetPath(&path); 605 status_t ret = unzipEngine->SetTo(path.Path(), targetPath); 606 if (ret != B_OK) 607 return ret; 608 609 reporter->AddItems(unzipEngine->ItemsToUncompress(), 610 unzipEngine->BytesToUncompress()); 611 } 612 613 return B_OK; 614 } 615 616 617 void 618 WorkerThread::_SetStatusMessage(const char *status) 619 { 620 BMessage msg(MSG_STATUS_MESSAGE); 621 msg.AddString("status", status); 622 BMessenger(fWindow).SendMessage(&msg); 623 } 624 625 626 static void 627 make_partition_label(BPartition* partition, char* label, char* menuLabel, 628 bool showContentType) 629 { 630 char size[20]; 631 string_for_size(partition->Size(), size, sizeof(size)); 632 633 BPath path; 634 partition->GetPath(&path); 635 636 if (showContentType) { 637 const char* type = partition->ContentType(); 638 if (type == NULL) 639 type = B_TRANSLATE_COMMENT("Unknown Type", "Partition content type"); 640 641 sprintf(label, "%s - %s [%s] (%s)", partition->ContentName(), size, 642 path.Path(), type); 643 } else { 644 sprintf(label, "%s - %s [%s]", partition->ContentName(), size, 645 path.Path()); 646 } 647 648 sprintf(menuLabel, "%s - %s", partition->ContentName(), size); 649 } 650 651 652 // #pragma mark - SourceVisitor 653 654 655 SourceVisitor::SourceVisitor(BMenu *menu) 656 : fMenu(menu) 657 { 658 } 659 660 bool 661 SourceVisitor::Visit(BDiskDevice *device) 662 { 663 return Visit(device, 0); 664 } 665 666 667 bool 668 SourceVisitor::Visit(BPartition *partition, int32 level) 669 { 670 BPath path; 671 if (partition->GetPath(&path) == B_OK) 672 printf("SourceVisitor::Visit(BPartition *) : %s\n", path.Path()); 673 printf("SourceVisitor::Visit(BPartition *) : %s\n", 674 partition->ContentName()); 675 676 if (partition->ContentType() == NULL) 677 return false; 678 679 bool isBootPartition = false; 680 if (partition->IsMounted()) { 681 BPath mountPoint; 682 partition->GetMountPoint(&mountPoint); 683 isBootPartition = strcmp(BOOT_PATH, mountPoint.Path()) == 0; 684 } 685 686 if (!isBootPartition 687 && strcmp(partition->ContentType(), kPartitionTypeBFS) != 0) { 688 // Except only BFS partitions, except this is the boot partition 689 // (ISO9660 with write overlay for example). 690 return false; 691 } 692 693 // TODO: We could probably check if this volume contains 694 // the Haiku kernel or something. Does it make sense to "install" 695 // from your BFS volume containing the music collection? 696 // TODO: Then the check for BFS could also be removed above. 697 698 char label[255]; 699 char menuLabel[255]; 700 make_partition_label(partition, label, menuLabel, false); 701 PartitionMenuItem* item = new PartitionMenuItem(partition->ContentName(), 702 label, menuLabel, new BMessage(SOURCE_PARTITION), partition->ID()); 703 item->SetMarked(isBootPartition); 704 fMenu->AddItem(item); 705 return false; 706 } 707 708 709 // #pragma mark - TargetVisitor 710 711 712 TargetVisitor::TargetVisitor(BMenu *menu) 713 : fMenu(menu) 714 { 715 } 716 717 718 bool 719 TargetVisitor::Visit(BDiskDevice *device) 720 { 721 if (device->IsReadOnlyMedia()) 722 return false; 723 return Visit(device, 0); 724 } 725 726 727 bool 728 TargetVisitor::Visit(BPartition *partition, int32 level) 729 { 730 BPath path; 731 if (partition->GetPath(&path) == B_OK) 732 printf("TargetVisitor::Visit(BPartition *) : %s\n", path.Path()); 733 printf("TargetVisitor::Visit(BPartition *) : %s\n", 734 partition->ContentName()); 735 736 if (partition->ContentSize() < 20 * 1024 * 1024) { 737 // reject partitions which are too small anyway 738 // TODO: Could depend on the source size 739 printf(" too small\n"); 740 return false; 741 } 742 743 if (partition->CountChildren() > 0) { 744 // Looks like an extended partition, or the device itself. 745 // Do not accept this as target... 746 printf(" no leaf partition\n"); 747 return false; 748 } 749 750 // TODO: After running DriveSetup and doing another scan, it would 751 // be great to pick the partition which just appeared! 752 753 bool isBootPartition = false; 754 if (partition->IsMounted()) { 755 BPath mountPoint; 756 partition->GetMountPoint(&mountPoint); 757 isBootPartition = strcmp(BOOT_PATH, mountPoint.Path()) == 0; 758 } 759 760 // Only non-boot BFS partitions are valid targets, but we want to display the 761 // other partitions as well, in order not to irritate the user. 762 bool isValidTarget = isBootPartition == false 763 && partition->ContentType() != NULL 764 && strcmp(partition->ContentType(), kPartitionTypeBFS) == 0; 765 766 char label[255]; 767 char menuLabel[255]; 768 make_partition_label(partition, label, menuLabel, !isValidTarget); 769 PartitionMenuItem* item = new PartitionMenuItem(partition->ContentName(), 770 label, menuLabel, new BMessage(TARGET_PARTITION), partition->ID()); 771 772 item->SetIsValidTarget(isValidTarget); 773 774 775 fMenu->AddItem(item); 776 return false; 777 } 778 779