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
53const char BOOT_PATH[] = "/boot";
54
55const uint32 MSG_START_INSTALLING = 'eSRT';
56
57
58class SourceVisitor : public BDiskDeviceVisitor {
59public:
60	SourceVisitor(BMenu* menu);
61	virtual bool Visit(BDiskDevice* device);
62	virtual bool Visit(BPartition* partition, int32 level);
63
64private:
65	BMenu* fMenu;
66};
67
68
69class TargetVisitor : public BDiskDeviceVisitor {
70public:
71	TargetVisitor(BMenu* menu);
72	virtual bool Visit(BDiskDevice* device);
73	virtual bool Visit(BPartition* partition, int32 level);
74
75private:
76	BMenu* fMenu;
77};
78
79
80// #pragma mark - WorkerThread
81
82
83WorkerThread::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
95void
96WorkerThread::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
160void
161WorkerThread::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
177void
178WorkerThread::SetPackagesList(BList *list)
179{
180	// Executed in window thread.
181	BAutolock _(this);
182
183	delete fPackages;
184	fPackages = list;
185}
186
187
188void
189WorkerThread::StartInstall()
190{
191	// Executed in window thread.
192	PostMessage(MSG_START_INSTALLING, this);
193}
194
195
196void
197WorkerThread::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
217void
218WorkerThread::_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
233void
234WorkerThread::_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
249void
250WorkerThread::_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;
482error:
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
493status_t
494WorkerThread::_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
542status_t
543WorkerThread::_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
582status_t
583WorkerThread::_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
624void
625WorkerThread::_SetStatusMessage(const char *status)
626{
627	BMessage msg(MSG_STATUS_MESSAGE);
628	msg.AddString("status", status);
629	BMessenger(fWindow).SendMessage(&msg);
630}
631
632
633static void
634make_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
662SourceVisitor::SourceVisitor(BMenu *menu)
663	: fMenu(menu)
664{
665}
666
667bool
668SourceVisitor::Visit(BDiskDevice *device)
669{
670	return Visit(device, 0);
671}
672
673
674bool
675SourceVisitor::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
719TargetVisitor::TargetVisitor(BMenu *menu)
720	: fMenu(menu)
721{
722}
723
724
725bool
726TargetVisitor::Visit(BDiskDevice *device)
727{
728	if (device->IsReadOnlyMedia())
729		return false;
730	return Visit(device, 0);
731}
732
733
734bool
735TargetVisitor::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