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