xref: /haiku/src/apps/installer/WorkerThread.cpp (revision c80809a3ab0b0a2ce53ea861a2b00ace24ff452d)
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 	return B_OK;
531 }
532 
533 
534 status_t
535 WorkerThread::_CreateDefaultIndices(const BPath& targetDirectory) const
536 {
537 	dev_t targetDevice = dev_for_path(targetDirectory.Path());
538 	if (targetDevice < 0)
539 		return (status_t)targetDevice;
540 
541 	struct IndexInfo {
542 		const char* name;
543 		uint32_t	type;
544 	};
545 
546 	const IndexInfo defaultIndices[] = {
547 		{ "BEOS:APP_SIG", B_STRING_TYPE },
548 		{ "BEOS:LOCALE_LANGUAGE", B_STRING_TYPE },
549 		{ "BEOS:LOCALE_SIGNATURE", B_STRING_TYPE },
550 		{ "_trk/qrylastchange", B_INT32_TYPE },
551 		{ "_trk/recentQuery", B_INT32_TYPE },
552 		{ "be:deskbar_item_status", B_STRING_TYPE }
553 	};
554 
555 	uint32 flags = 0;
556 		// Flags are always 0 for the moment.
557 
558 	for (uint32 i = 0; i < sizeof(defaultIndices) / sizeof(IndexInfo); i++) {
559 		const IndexInfo& info = defaultIndices[i];
560 		if (fs_create_index(targetDevice, info.name, info.type, flags)
561 			!= B_OK) {
562 			if (errno == B_FILE_EXISTS)
563 				continue;
564 			printf("Failed to create index %s: fs_create_index(): (%d) %s\n",
565 				info.name, errno, strerror(errno));
566 			return errno;
567 		}
568 	}
569 
570 	return B_OK;
571 }
572 
573 
574 status_t
575 WorkerThread::_ProcessZipPackages(const char* sourcePath,
576 	const char* targetPath, ProgressReporter* reporter, BList& unzipEngines)
577 {
578 	// TODO: Put those in the optional packages list view
579 	// TODO: Implement mechanism to handle dependencies between these
580 	// packages. (Selecting one will auto-select others.)
581 	BPath pkgRootDir(sourcePath, PACKAGES_DIRECTORY);
582 	BDirectory directory(pkgRootDir.Path());
583 	BEntry entry;
584 	while (directory.GetNextEntry(&entry) == B_OK) {
585 		char name[B_FILE_NAME_LENGTH];
586 		if (entry.GetName(name) != B_OK)
587 			continue;
588 		int nameLength = strlen(name);
589 		if (nameLength <= 0)
590 			continue;
591 		char* nameExtension = name + nameLength - 4;
592 		if (strcasecmp(nameExtension, ".zip") != 0)
593 			continue;
594 		printf("found .zip package: %s\n", name);
595 
596 		UnzipEngine* unzipEngine = new(std::nothrow) UnzipEngine(reporter,
597 			fCancelSemaphore);
598 		if (unzipEngine == NULL || !unzipEngines.AddItem(unzipEngine)) {
599 			delete unzipEngine;
600 			return B_NO_MEMORY;
601 		}
602 		BPath path;
603 		entry.GetPath(&path);
604 		status_t ret = unzipEngine->SetTo(path.Path(), targetPath);
605 		if (ret != B_OK)
606 			return ret;
607 
608 		reporter->AddItems(unzipEngine->ItemsToUncompress(),
609 			unzipEngine->BytesToUncompress());
610 	}
611 
612 	return B_OK;
613 }
614 
615 
616 void
617 WorkerThread::_SetStatusMessage(const char *status)
618 {
619 	BMessage msg(MSG_STATUS_MESSAGE);
620 	msg.AddString("status", status);
621 	BMessenger(fWindow).SendMessage(&msg);
622 }
623 
624 
625 static void
626 make_partition_label(BPartition* partition, char* label, char* menuLabel,
627 	bool showContentType)
628 {
629 	char size[20];
630 	string_for_size(partition->Size(), size, sizeof(size));
631 
632 	BPath path;
633 	partition->GetPath(&path);
634 
635 	if (showContentType) {
636 		const char* type = partition->ContentType();
637 		if (type == NULL)
638 			type = B_TRANSLATE_COMMENT("Unknown Type", "Partition content type");
639 
640 		sprintf(label, "%s - %s [%s] (%s)", partition->ContentName(), size,
641 			path.Path(), type);
642 	} else {
643 		sprintf(label, "%s - %s [%s]", partition->ContentName(), size,
644 			path.Path());
645 	}
646 
647 	sprintf(menuLabel, "%s - %s", partition->ContentName(), size);
648 }
649 
650 
651 // #pragma mark - SourceVisitor
652 
653 
654 SourceVisitor::SourceVisitor(BMenu *menu)
655 	: fMenu(menu)
656 {
657 }
658 
659 bool
660 SourceVisitor::Visit(BDiskDevice *device)
661 {
662 	return Visit(device, 0);
663 }
664 
665 
666 bool
667 SourceVisitor::Visit(BPartition *partition, int32 level)
668 {
669 	BPath path;
670 	if (partition->GetPath(&path) == B_OK)
671 		printf("SourceVisitor::Visit(BPartition *) : %s\n", path.Path());
672 	printf("SourceVisitor::Visit(BPartition *) : %s\n",
673 		partition->ContentName());
674 
675 	if (partition->ContentType() == NULL)
676 		return false;
677 
678 	bool isBootPartition = false;
679 	if (partition->IsMounted()) {
680 		BPath mountPoint;
681 		partition->GetMountPoint(&mountPoint);
682 		isBootPartition = strcmp(BOOT_PATH, mountPoint.Path()) == 0;
683 	}
684 
685 	if (!isBootPartition
686 		&& strcmp(partition->ContentType(), kPartitionTypeBFS) != 0) {
687 		// Except only BFS partitions, except this is the boot partition
688 		// (ISO9660 with write overlay for example).
689 		return false;
690 	}
691 
692 	// TODO: We could probably check if this volume contains
693 	// the Haiku kernel or something. Does it make sense to "install"
694 	// from your BFS volume containing the music collection?
695 	// TODO: Then the check for BFS could also be removed above.
696 
697 	char label[255];
698 	char menuLabel[255];
699 	make_partition_label(partition, label, menuLabel, false);
700 	PartitionMenuItem* item = new PartitionMenuItem(partition->ContentName(),
701 		label, menuLabel, new BMessage(SOURCE_PARTITION), partition->ID());
702 	item->SetMarked(isBootPartition);
703 	fMenu->AddItem(item);
704 	return false;
705 }
706 
707 
708 // #pragma mark - TargetVisitor
709 
710 
711 TargetVisitor::TargetVisitor(BMenu *menu)
712 	: fMenu(menu)
713 {
714 }
715 
716 
717 bool
718 TargetVisitor::Visit(BDiskDevice *device)
719 {
720 	if (device->IsReadOnlyMedia())
721 		return false;
722 	return Visit(device, 0);
723 }
724 
725 
726 bool
727 TargetVisitor::Visit(BPartition *partition, int32 level)
728 {
729 	BPath path;
730 	if (partition->GetPath(&path) == B_OK)
731 		printf("TargetVisitor::Visit(BPartition *) : %s\n", path.Path());
732 	printf("TargetVisitor::Visit(BPartition *) : %s\n",
733 		partition->ContentName());
734 
735 	if (partition->ContentSize() < 20 * 1024 * 1024) {
736 		// reject partitions which are too small anyway
737 		// TODO: Could depend on the source size
738 		printf("  too small\n");
739 		return false;
740 	}
741 
742 	if (partition->CountChildren() > 0) {
743 		// Looks like an extended partition, or the device itself.
744 		// Do not accept this as target...
745 		printf("  no leaf partition\n");
746 		return false;
747 	}
748 
749 	// TODO: After running DriveSetup and doing another scan, it would
750 	// be great to pick the partition which just appeared!
751 
752 	bool isBootPartition = false;
753 	if (partition->IsMounted()) {
754 		BPath mountPoint;
755 		partition->GetMountPoint(&mountPoint);
756 		isBootPartition = strcmp(BOOT_PATH, mountPoint.Path()) == 0;
757 	}
758 
759 	// Only non-boot BFS partitions are valid targets, but we want to display the
760 	// other partitions as well, in order not to irritate the user.
761 	bool isValidTarget = isBootPartition == false
762 		&& partition->ContentType() != NULL
763 		&& strcmp(partition->ContentType(), kPartitionTypeBFS) == 0;
764 
765 	char label[255];
766 	char menuLabel[255];
767 	make_partition_label(partition, label, menuLabel, !isValidTarget);
768 	PartitionMenuItem* item = new PartitionMenuItem(partition->ContentName(),
769 		label, menuLabel, new BMessage(TARGET_PARTITION), partition->ID());
770 
771 	item->SetIsValidTarget(isValidTarget);
772 
773 
774 	fMenu->AddItem(item);
775 	return false;
776 }
777 
778