xref: /haiku/src/apps/installer/WorkerThread.cpp (revision e688bf23d48bfd1216a0cacbdbda5e35a1bcd779)
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