xref: /haiku/src/apps/installer/CopyEngine.cpp (revision b30304acc8c37e678a1bf66976d15bdab103f931)
1 /*
2  * Copyright 2005-2006, Jérôme DUVAL. All rights reserved.
3  * Distributed under the terms of the MIT License.
4  */
5 
6 #include "CopyEngine.h"
7 #include "InstallerWindow.h"
8 #include "PartitionMenuItem.h"
9 #include "FSUndoRedo.h"
10 #include "FSUtils.h"
11 #include <Alert.h>
12 #include <DiskDeviceVisitor.h>
13 #include <DiskDeviceTypes.h>
14 #include <FindDirectory.h>
15 #include <Path.h>
16 #include <String.h>
17 #include <VolumeRoster.h>
18 
19 //#define COPY_TRACE
20 #ifdef COPY_TRACE
21 #define CALLED() 		printf("CALLED %s\n",__PRETTY_FUNCTION__)
22 #define ERR2(x, y...)	fprintf(stderr, "CopyEngine: "x" %s\n", y, strerror(err))
23 #define ERR(x)			fprintf(stderr, "CopyEngine: "x" %s\n", strerror(err))
24 #else
25 #define CALLED()
26 #define ERR(x)
27 #define ERR2(x, y...)
28 #endif
29 
30 const char BOOT_PATH[] = "/boot";
31 
32 extern void SizeAsString(off_t size, char *string);
33 
34 class SourceVisitor : public BDiskDeviceVisitor
35 {
36 	public:
37 		SourceVisitor(BMenu *menu);
38 		virtual bool Visit(BDiskDevice *device);
39 		virtual bool Visit(BPartition *partition, int32 level);
40 	private:
41 		BMenu *fMenu;
42 };
43 
44 
45 class TargetVisitor : public BDiskDeviceVisitor
46 {
47 	public:
48 		TargetVisitor(BMenu *menu);
49 		virtual bool Visit(BDiskDevice *device);
50 		virtual bool Visit(BPartition *partition, int32 level);
51 	private:
52 		void _MakeLabel(BPartition *partition, char *label, char *menuLabel);
53 		BMenu *fMenu;
54 };
55 
56 
57 CopyEngine::CopyEngine(InstallerWindow *window)
58 	: BLooper("copy_engine"),
59 	fWindow(window),
60 	fPackages(NULL),
61 	fSpaceRequired(0)
62 {
63 	fControl = new InstallerCopyLoopControl(window);
64 	Run();
65 }
66 
67 
68 void
69 CopyEngine::MessageReceived(BMessage*msg)
70 {
71 	CALLED();
72 	switch (msg->what) {
73 		case ENGINE_START:
74 		{
75 			status_t err = Start(fWindow->GetSourceMenu(),
76 				fWindow->GetTargetMenu());
77 			if (err != B_OK) {
78 				ERR("Start failed");
79 				BMessenger(fWindow).SendMessage(RESET_INSTALL);
80 			}
81 			break;
82 		}
83 	}
84 }
85 
86 
87 void
88 CopyEngine::SetStatusMessage(char *status)
89 {
90 	BMessage msg(STATUS_MESSAGE);
91 	msg.AddString("status", status);
92 	BMessenger(fWindow).SendMessage(&msg);
93 }
94 
95 
96 void
97 CopyEngine::LaunchInitScript(BPath &path)
98 {
99 	BPath bootPath;
100 	find_directory(B_BEOS_BOOT_DIRECTORY, &bootPath);
101 	BString command("/bin/sh ");
102 	command += bootPath.Path();
103 	command += "/InstallerInitScript ";
104 	command += path.Path();
105 	SetStatusMessage("Starting Installation.");
106 	system(command.String());
107 }
108 
109 
110 void
111 CopyEngine::LaunchFinishScript(BPath &path)
112 {
113 	BPath bootPath;
114 	find_directory(B_BEOS_BOOT_DIRECTORY, &bootPath);
115 	BString command("/bin/sh ");
116 	command += bootPath.Path();
117 	command += "/InstallerFinishScript ";
118 	command += path.Path();
119 	SetStatusMessage("Finishing Installation.");
120 	system(command.String());
121 }
122 
123 
124 status_t
125 CopyEngine::Start(BMenu *srcMenu, BMenu *targetMenu)
126 {
127 	CALLED();
128 
129 	fControl->Reset();
130 
131 	status_t err = B_OK;
132 	PartitionMenuItem *targetItem = (PartitionMenuItem *)targetMenu->FindMarked();
133 	PartitionMenuItem *srcItem = (PartitionMenuItem *)srcMenu->FindMarked();
134 	if (!srcItem || !targetItem) {
135 		ERR("bad menu items\n");
136 		return B_BAD_VALUE;
137 	}
138 
139 	// check if target is initialized
140 	// ask if init or mount as is
141 
142 	BPath targetDirectory;
143 	BDiskDevice device;
144 	BPartition *partition;
145 	BVolume targetVolume;
146 
147 	if (fDDRoster.GetPartitionWithID(targetItem->ID(), &device, &partition) == B_OK) {
148 		if (!partition->IsMounted()) {
149 			if ((err = partition->Mount()) < B_OK) {
150 				SetStatusMessage("The disk can't be mounted. Please choose a "
151 					"different disk.");
152 				ERR("BPartition::Mount");
153 				return err;
154 			}
155 		}
156 		if ((err = partition->GetVolume(&targetVolume)) != B_OK) {
157 			ERR("BPartition::GetVolume");
158 			return err;
159 		}
160 		if ((err = partition->GetMountPoint(&targetDirectory)) != B_OK) {
161 			ERR("BPartition::GetMountPoint");
162 			return err;
163 		}
164 	} else if (fDDRoster.GetDeviceWithID(targetItem->ID(), &device) == B_OK) {
165 		if (!device.IsMounted()) {
166 			if ((err = device.Mount()) < B_OK) {
167 				SetStatusMessage("The disk can't be mounted. Please choose a "
168 					"different disk.");
169 				ERR("BDiskDevice::Mount");
170 				return err;
171 			}
172 		}
173 		if ((err = device.GetVolume(&targetVolume)) != B_OK) {
174 			ERR("BDiskDevice::GetVolume");
175 			return err;
176 		}
177 		if ((err = device.GetMountPoint(&targetDirectory)) != B_OK) {
178 			ERR("BDiskDevice::GetMountPoint");
179 			return err;
180 		}
181 	} else
182 		return B_ERROR; // shouldn't happen
183 
184 	// check if target has enough space
185 	if ((fSpaceRequired > 0 && targetVolume.FreeBytes() < fSpaceRequired)
186 		&& ((new BAlert("", "The destination disk may not have enough space. "
187 			"Try choosing a different disk or choose to not install optional "
188 			"items.", "Try installing anyway", "Cancel", 0,
189 			B_WIDTH_AS_USUAL, B_STOP_ALERT))->Go() != 0)) {
190 		BMessenger(fWindow).SendMessage(RESET_INSTALL);
191 		return B_OK;
192 	}
193 
194 	BPath srcDirectory;
195 	if (fDDRoster.GetPartitionWithID(srcItem->ID(), &device, &partition) == B_OK) {
196 		if ((err = partition->GetMountPoint(&srcDirectory)) != B_OK) {
197 			ERR("BPartition::GetMountPoint");
198 			return err;
199 		}
200 	} else if (fDDRoster.GetDeviceWithID(srcItem->ID(), &device) == B_OK) {
201 		if ((err = device.GetMountPoint(&srcDirectory)) != B_OK) {
202 			ERR("BDiskDevice::GetMountPoint");
203 			return err;
204 		}
205 	} else
206 		return B_ERROR; // shouldn't happen
207 
208 	// check not installing on itself
209 	if (strcmp(srcDirectory.Path(), targetDirectory.Path()) == 0) {
210 		SetStatusMessage("You can't install the contents of a disk onto "
211 			"itself. Please choose a different disk.");
212 		BMessenger(fWindow).SendMessage(RESET_INSTALL);
213 		return B_OK;
214 	}
215 
216 	// check not installing on boot volume
217 	if ((strncmp(BOOT_PATH, targetDirectory.Path(), strlen(BOOT_PATH)) == 0)
218 		&& ((new BAlert("", "Are you sure you want to install onto the "
219 			"current boot disk? The installer will have to reboot your "
220 			"machine if you proceed.", "OK", "Cancel", 0,
221 			B_WIDTH_AS_USUAL, B_STOP_ALERT))->Go() != 0)) {
222 		SetStatusMessage("Installation stopped.");
223 		BMessenger(fWindow).SendMessage(RESET_INSTALL);
224 		return B_OK;
225 	}
226 
227 	LaunchInitScript(targetDirectory);
228 
229 	// copy source volume
230 	BDirectory targetDir(targetDirectory.Path());
231 	BDirectory srcDir(srcDirectory.Path());
232 	CopyFolder(srcDir, targetDir);
233 
234 	// copy selected packages
235 	if (fPackages) {
236 		srcDirectory.Append(PACKAGES_DIRECTORY);
237 		srcDir.SetTo(srcDirectory.Path());
238 		BDirectory packageDir;
239 		int32 count = fPackages->CountItems();
240 		for (int32 i = 0; i < count; i++) {
241 			if (fControl->CheckUserCanceled())
242 				return B_OK;
243 			Package *p = static_cast<Package*>(fPackages->ItemAt(i));
244 			packageDir.SetTo(&srcDir, p->Folder());
245 			CopyFolder(packageDir, targetDir);
246 		}
247 	}
248 
249 	if (!fControl->CheckUserCanceled()) {
250 		LaunchFinishScript(targetDirectory);
251 
252 		BMessage msg(INSTALL_FINISHED);
253 		BMessenger(fWindow).SendMessage(&msg);
254 	}
255 
256 	return B_OK;
257 }
258 
259 
260 void
261 CopyEngine::CopyFolder(BDirectory &srcDir, BDirectory &targetDir)
262 {
263 	BEntry entry;
264 	status_t err;
265 	while (srcDir.GetNextEntry(&entry) == B_OK
266 		&& !fControl->CheckUserCanceled()) {
267 		StatStruct statbuf;
268 		entry.GetStat(&statbuf);
269 
270 		Undo undo;
271 		if (S_ISDIR(statbuf.st_mode)) {
272 			char name[B_FILE_NAME_LENGTH];
273 			if (entry.GetName(name) == B_OK
274 				&& strcmp(name, PACKAGES_DIRECTORY) == 0) {
275 				continue;
276 			}
277 			err = FSCopyFolder(&entry, &targetDir, fControl, NULL, false, undo);
278 		} else {
279 			err = FSCopyFile(&entry, &statbuf, &targetDir, fControl, NULL,
280 				false, undo);
281 		}
282 		if (err != B_OK) {
283 			BPath path;
284 			entry.GetPath(&path);
285 			ERR2("error while copying %s", path.Path());
286 		}
287 	}
288 }
289 
290 
291 void
292 CopyEngine::ScanDisksPartitions(BMenu *srcMenu, BMenu *targetMenu)
293 {
294 	BDiskDevice device;
295 	BPartition *partition = NULL;
296 
297 	printf("ScanDisksPartitions partitions begin\n");
298 	SourceVisitor srcVisitor(srcMenu);
299 	fDDRoster.VisitEachMountedPartition(&srcVisitor, &device, &partition);
300 
301 	printf("ScanDisksPartitions partitions begin\n");
302 	TargetVisitor targetVisitor(targetMenu);
303 	fDDRoster.VisitEachPartition(&targetVisitor, &device, &partition);
304 }
305 
306 
307 void
308 CopyEngine::SetPackagesList(BList *list)
309 {
310 	delete fPackages;
311 	fPackages = list;
312 }
313 
314 
315 bool
316 CopyEngine::Cancel()
317 {
318 	return fControl->Cancel();
319 }
320 
321 
322 // #pragma mark -
323 
324 
325 SourceVisitor::SourceVisitor(BMenu *menu)
326 	: fMenu(menu)
327 {
328 }
329 
330 bool
331 SourceVisitor::Visit(BDiskDevice *device)
332 {
333 	if (!device->ContentType()
334 		|| strcmp(device->ContentType(), kPartitionTypeBFS) != 0)
335 		return false;
336 	BPath path;
337 	if (device->GetPath(&path) == B_OK)
338 		printf("SourceVisitor::Visit(BDiskDevice *) : %s type:%s, "
339 			"contentType:%s\n", path.Path(), device->Type(),
340 			device->ContentType());
341 	PartitionMenuItem *item = new PartitionMenuItem(NULL, device->ContentName(), NULL, new BMessage(SRC_PARTITION), device->ID());
342 	if (device->IsMounted()) {
343 		BPath mountPoint;
344 		device->GetMountPoint(&mountPoint);
345 		if (strcmp(BOOT_PATH, mountPoint.Path()) == 0)
346 			item->SetMarked(true);
347 	}
348 	fMenu->AddItem(item);
349 	return false;
350 }
351 
352 
353 bool
354 SourceVisitor::Visit(BPartition *partition, int32 level)
355 {
356 	if (!partition->ContentType()
357 		|| strcmp(partition->ContentType(), kPartitionTypeBFS) != 0)
358 		return false;
359 	BPath path;
360 	if (partition->GetPath(&path) == B_OK)
361 		printf("SourceVisitor::Visit(BPartition *) : %s\n", path.Path());
362 	printf("SourceVisitor::Visit(BPartition *) : %s\n", partition->Name());
363 	PartitionMenuItem *item = new PartitionMenuItem(NULL,
364 		partition->ContentName(), NULL, new BMessage(SRC_PARTITION),
365 		partition->ID());
366 	if (partition->IsMounted()) {
367 		BPath mountPoint;
368 		partition->GetMountPoint(&mountPoint);
369 		if (strcmp(BOOT_PATH, mountPoint.Path()) == 0)
370 			item->SetMarked(true);
371 	}
372 	fMenu->AddItem(item);
373 	return false;
374 }
375 
376 
377 // #pragma mark -
378 
379 
380 TargetVisitor::TargetVisitor(BMenu *menu)
381 	: fMenu(menu)
382 {
383 }
384 
385 
386 bool
387 TargetVisitor::Visit(BDiskDevice *device)
388 {
389 	if (device->IsReadOnly() || device->IsReadOnlyMedia())
390 		return false;
391 	BPath path;
392 	if (device->GetPath(&path) == B_OK)
393 		printf("TargetVisitor::Visit(BDiskDevice *) : %s\n", path.Path());
394 	char label[255], menuLabel[255];
395 	_MakeLabel(device, label, menuLabel);
396 	fMenu->AddItem(new PartitionMenuItem(device->ContentName(), label,
397 		menuLabel, new BMessage(TARGET_PARTITION), device->ID()));
398 	return false;
399 }
400 
401 
402 bool
403 TargetVisitor::Visit(BPartition *partition, int32 level)
404 {
405 	if (partition->IsReadOnly())
406 		return false;
407 	BPath path;
408 	if (partition->GetPath(&path) == B_OK)
409 		printf("TargetVisitor::Visit(BPartition *) : %s\n", path.Path());
410 	printf("TargetVisitor::Visit(BPartition *) : %s\n", partition->Name());
411 	char label[255], menuLabel[255];
412 	_MakeLabel(partition, label, menuLabel);
413 	fMenu->AddItem(new PartitionMenuItem(partition->ContentName(), label,
414 		menuLabel, new BMessage(TARGET_PARTITION), partition->ID()));
415 	return false;
416 }
417 
418 
419 void
420 TargetVisitor::_MakeLabel(BPartition *partition, char *label, char *menuLabel)
421 {
422 	char size[15];
423 	SizeAsString(partition->ContentSize(), size);
424 	BPath path;
425 	if (partition->Parent())
426 		partition->Parent()->GetPath(&path);
427 
428 	sprintf(label, "%s - %s [%s] [%s partition:%li]", partition->ContentName(),
429 		size, partition->ContentType(), path.Path(), partition->ID());
430 	sprintf(menuLabel, "%s - %s [%s]", partition->ContentName(), size,
431 		partition->ContentType());
432 
433 }
434 
435