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