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