1 /* 2 * Copyright 2005-2007 Ingo Weinhold, bonefish@users.sf.net 3 * Copyright 2005-2013 Axel Dörfler, axeld@pinc-software.de 4 * Copyright 2009 Jonas Sundström, jonas@kirilla.se 5 * 6 * All rights reserved. Distributed under the terms of the MIT License. 7 */ 8 9 10 #include <set> 11 #include <string> 12 13 #include <stdio.h> 14 #include <stdlib.h> 15 #include <string.h> 16 #include <termios.h> 17 18 #include <Application.h> 19 #include <Path.h> 20 #include <String.h> 21 #include <fs_volume.h> 22 23 #include <DiskDevice.h> 24 #include <DiskDevicePrivate.h> 25 #include <DiskDeviceRoster.h> 26 #include <DiskDeviceTypes.h> 27 #include <DiskDeviceList.h> 28 #include <Partition.h> 29 30 #include <tracker_private.h> 31 32 33 using std::set; 34 using std::string; 35 36 extern const char* __progname; 37 38 39 typedef set<string> StringSet; 40 41 // usage 42 static const char* kUsage = 43 "Usage: %s <options> [ <volume name> ... ]\n\n" 44 "Mounts the volume with name <volume name>, if given. Lists info about\n" 45 "mounted and mountable volumes and mounts/unmounts volumes.\n" 46 "\n" 47 "The terminology is actually not quite correct: By volumes only partitions\n" 48 "living on disk devices are meant.\n" 49 "\n" 50 "Options:\n" 51 "[general]\n" 52 " -s - silent; don't print info about (un)mounting\n" 53 " -h, --help - print this info text\n" 54 "\n" 55 "[mounting]\n" 56 " -all - mount all mountable volumes\n" 57 " -allbfs - mount all mountable BFS volumes\n" 58 " -allhfs - mount all mountable HFS volumes\n" 59 " -alldos - mount all mountable DOS volumes\n" 60 " -ro, -readonly - mount volumes read-only\n" 61 " -u, -unmount <volume> - unmount the volume with the name <volume>\n" 62 " -open - opens the mounted volumes in Tracker\n" 63 "\n" 64 "[info]\n" 65 " -p, -l - list all mounted and mountable volumes\n" 66 " -lh - list all existing volumes (incl. not-mountable " 67 "ones)\n" 68 " -dd - list all disk existing devices\n" 69 "\n" 70 "[obsolete]\n" 71 " -r - ignored\n" 72 " -publishall - ignored\n" 73 " -publishbfs - ignored\n" 74 " -publishhfs - ignored\n" 75 " -publishdos - ignored\n"; 76 77 78 const char* kAppName = __progname; 79 80 static int sVolumeNameWidth = B_OS_NAME_LENGTH; 81 static int sFSNameWidth = 25; 82 83 84 static void 85 print_usage(bool error) 86 { 87 fprintf(error ? stderr : stdout, kUsage, kAppName); 88 } 89 90 91 static void 92 print_usage_and_exit(bool error) 93 { 94 print_usage(error); 95 exit(error ? 0 : 1); 96 } 97 98 99 static const char* 100 size_string(int64 size) 101 { 102 double blocks = size; 103 static char string[64]; 104 105 if (size < 1024) 106 sprintf(string, "%" B_PRId64, size); 107 else { 108 const char* units[] = {"K", "M", "G", NULL}; 109 int32 i = -1; 110 111 do { 112 blocks /= 1024.0; 113 i++; 114 } while (blocks >= 1024 && units[i + 1]); 115 116 snprintf(string, sizeof(string), "%.1f%s", blocks, units[i]); 117 } 118 119 return string; 120 } 121 122 123 static status_t 124 open_in_tracker(BPartition* partition) 125 { 126 BPath mountPoint; 127 status_t status = partition->GetMountPoint(&mountPoint); 128 if (status != B_OK) 129 return status; 130 131 entry_ref ref; 132 status = get_ref_for_path(mountPoint.Path(), &ref); 133 if (status != B_OK) 134 return status; 135 136 BMessage refs(B_REFS_RECEIVED); 137 refs.AddRef("refs", &ref); 138 return BMessenger(kTrackerSignature).SendMessage(&refs); 139 } 140 141 142 // #pragma mark - 143 144 145 struct MountVisitor : public BDiskDeviceVisitor { 146 MountVisitor() 147 : 148 silent(false), 149 mountAll(false), 150 mountBFS(false), 151 mountHFS(false), 152 mountDOS(false), 153 readOnly(false), 154 openInTracker(false) 155 { 156 } 157 158 virtual bool Visit(BDiskDevice* device) 159 { 160 return Visit(device, 0); 161 } 162 163 virtual bool Visit(BPartition* partition, int32 level) 164 { 165 // get name and type 166 BString name = partition->ContentName(); 167 if (name.IsEmpty()) 168 name = partition->Name(); 169 const char* type = partition->ContentType(); 170 171 // check whether to mount 172 bool mount = false; 173 if (name && toMount.find(name.String()) != toMount.end()) { 174 toMount.erase(name.String()); 175 if (!partition->IsMounted()) 176 mount = true; 177 else if (!silent) 178 fprintf(stderr, "Volume `%s' already mounted.\n", name.String()); 179 } else if (mountAll) { 180 mount = true; 181 } else if (mountBFS && type != NULL 182 && strcmp(type, kPartitionTypeBFS) == 0) { 183 mount = true; 184 } else if (mountHFS && type != NULL 185 && strcmp(type, kPartitionTypeHFS) == 0) { 186 mount = true; 187 } else if (mountDOS && type != NULL 188 && (strcmp(type, kPartitionTypeFAT12) == 0 189 || strcmp(type, kPartitionTypeFAT32) == 0)) { 190 mount = true; 191 } 192 193 // don't try to mount a partition twice 194 if (partition->IsMounted()) 195 mount = false; 196 197 // check whether to unmount 198 bool unmount = false; 199 if (name && toUnmount.find(name.String()) != toUnmount.end()) { 200 toUnmount.erase(name.String()); 201 if (partition->IsMounted()) { 202 unmount = true; 203 mount = false; 204 } else if (!silent) 205 fprintf(stderr, "Volume `%s' not mounted.\n", name.String()); 206 } 207 208 // mount/unmount 209 if (mount) { 210 status_t error = partition->Mount(NULL, 211 readOnly ? B_MOUNT_READ_ONLY : 0); 212 if (!silent) { 213 if (error >= B_OK) { 214 BPath mountPoint; 215 partition->GetMountPoint(&mountPoint); 216 printf("Volume `%s' mounted successfully at '%s'.\n", name.String(), 217 mountPoint.Path()); 218 } else { 219 fprintf(stderr, "Failed to mount volume `%s': %s\n", 220 name.String(), strerror(error)); 221 } 222 } 223 if (openInTracker && error == B_OK) 224 open_in_tracker(partition); 225 } else if (unmount) { 226 status_t error = partition->Unmount(); 227 if (!silent) { 228 if (error == B_OK) { 229 printf("Volume `%s' unmounted successfully.\n", name.String()); 230 } else { 231 fprintf(stderr, "Failed to unmount volume `%s': %s\n", 232 name.String(), strerror(error)); 233 } 234 } 235 } 236 237 return false; 238 } 239 240 bool silent; 241 StringSet toMount; 242 StringSet toUnmount; 243 bool mountAll; 244 bool mountBFS; 245 bool mountHFS; 246 bool mountDOS; 247 bool readOnly; 248 bool openInTracker; 249 }; 250 251 252 struct PrintPartitionsVisitor : public BDiskDeviceVisitor { 253 PrintPartitionsVisitor() 254 : listMountablePartitions(false), 255 listAllPartitions(false) 256 { 257 } 258 259 bool IsUsed() 260 { 261 return listMountablePartitions || listAllPartitions; 262 } 263 264 virtual bool Visit(BDiskDevice* device) 265 { 266 return Visit(device, 0); 267 } 268 269 virtual bool Visit(BPartition* partition, int32 level) 270 { 271 // get name and type 272 BString name = partition->ContentName(); 273 if (name.IsEmpty()) { 274 name = partition->Name(); 275 if (name.IsEmpty()) { 276 if (partition->ContainsFileSystem()) 277 name = "<unnamed>"; 278 else 279 name = ""; 280 } 281 } 282 const char* type = partition->ContentType(); 283 if (type == NULL) 284 type = "<unknown>"; 285 286 // shorten known types for display 287 if (!strcmp(type, kPartitionTypeMultisession)) 288 type = "Multisession"; 289 else if (!strcmp(type, kPartitionTypeIntelExtended)) 290 type = "Intel Extended"; 291 292 BPath path; 293 partition->GetPath(&path); 294 295 // cut off beginning of the device path (if /dev/disk/) 296 int32 skip = strlen("/dev/disk/"); 297 if (strncmp(path.Path(), "/dev/disk/", skip)) 298 skip = 0; 299 300 BPath mountPoint; 301 if (partition->IsMounted()) 302 partition->GetMountPoint(&mountPoint); 303 304 printf("%-*s %-*s %8s %s%s(%s)\n", sVolumeNameWidth, name.String(), 305 sFSNameWidth, type, size_string(partition->Size()), 306 partition->IsMounted() ? mountPoint.Path() : "", 307 partition->IsMounted() ? " " : "", 308 path.Path() + skip); 309 return false; 310 } 311 312 bool listMountablePartitions; 313 bool listAllPartitions; 314 }; 315 316 317 // #pragma mark - 318 319 320 class MountVolume : public BApplication { 321 public: 322 MountVolume(); 323 virtual ~MountVolume(); 324 325 virtual void RefsReceived(BMessage* message); 326 virtual void ArgvReceived(int32 argc, char** argv); 327 virtual void ReadyToRun(); 328 }; 329 330 331 MountVolume::MountVolume() 332 : 333 BApplication("application/x-vnd.haiku-mountvolume") 334 { 335 } 336 337 338 MountVolume::~MountVolume() 339 { 340 } 341 342 343 void 344 MountVolume::RefsReceived(BMessage* message) 345 { 346 status_t status; 347 int32 refCount; 348 type_code typeFound; 349 350 status = message->GetInfo("refs", &typeFound, &refCount); 351 if (status != B_OK || refCount < 1) { 352 fprintf(stderr, "Failed to get info from entry_refs BMessage: %s\n", 353 strerror(status)); 354 exit(1); 355 } 356 357 entry_ref ref; 358 BPath path; 359 360 int32 argc = refCount + 2; 361 char** argv = new char*[argc + 1]; 362 argv[0] = strdup(kAppName); 363 argv[1] = strdup("-open"); 364 365 for (int32 i = 0; i < refCount; i++) { 366 message->FindRef("refs", i, &ref); 367 status = path.SetTo(&ref); 368 if (status != B_OK) { 369 fprintf(stderr, "Failed to get a path (%s) from entry (%s): %s\n", 370 path.Path(), ref.name, strerror(status)); 371 } 372 argv[2 + i] = strdup(path.Path()); 373 } 374 argv[argc] = NULL; 375 376 ArgvReceived(argc, argv); 377 } 378 379 380 void 381 MountVolume::ArgvReceived(int32 argc, char** argv) 382 { 383 MountVisitor mountVisitor; 384 PrintPartitionsVisitor printPartitionsVisitor; 385 bool listAllDevices = false; 386 387 if (argc < 2) 388 printPartitionsVisitor.listMountablePartitions = true; 389 390 // parse arguments 391 392 for (int argi = 1; argi < argc; argi++) { 393 const char* arg = argv[argi]; 394 395 if (arg[0] != '\0' && arg[0] != '-') { 396 mountVisitor.toMount.insert(arg); 397 } else if (strcmp(arg, "-s") == 0) { 398 mountVisitor.silent = true; 399 } else if (strcmp(arg, "-h") == 0 || strcmp(arg, "--help") == 0) { 400 print_usage_and_exit(false); 401 } else if (strcmp(arg, "-all") == 0) { 402 mountVisitor.mountAll = true; 403 } else if (strcmp(arg, "-allbfs") == 0) { 404 mountVisitor.mountBFS = true; 405 } else if (strcmp(arg, "-allhfs") == 0) { 406 mountVisitor.mountHFS = true; 407 } else if (strcmp(arg, "-alldos") == 0) { 408 mountVisitor.mountDOS = true; 409 } else if (strcmp(arg, "-ro") == 0 || strcmp(arg, "-readonly") == 0) { 410 mountVisitor.readOnly = true; 411 } else if (strcmp(arg, "-u") == 0 || strcmp(arg, "-unmount") == 0) { 412 argi++; 413 if (argi >= argc) 414 print_usage_and_exit(true); 415 mountVisitor.toUnmount.insert(argv[argi]); 416 } else if (strcmp(arg, "-open") == 0) { 417 mountVisitor.openInTracker = true; 418 } else if (strcmp(arg, "-p") == 0 || strcmp(arg, "-l") == 0) { 419 printPartitionsVisitor.listMountablePartitions = true; 420 } else if (strcmp(arg, "-lh") == 0) { 421 printPartitionsVisitor.listAllPartitions = true; 422 } else if (strcmp(arg, "-dd") == 0) { 423 listAllDevices = true; 424 } else if (strcmp(arg, "-r") == 0 || strcmp(arg, "-publishall") == 0 425 || strcmp(arg, "-publishbfs") == 0 426 || strcmp(arg, "-publishhfs") == 0 427 || strcmp(arg, "-publishdos") == 0) { 428 // obsolete: ignore 429 } else 430 print_usage_and_exit(true); 431 } 432 433 // get a disk device list 434 BDiskDeviceList deviceList; 435 status_t error = deviceList.Fetch(); 436 if (error != B_OK) { 437 fprintf(stderr, "Failed to get the list of disk devices: %s", 438 strerror(error)); 439 exit(1); 440 } 441 442 // mount/unmount volumes 443 deviceList.VisitEachMountablePartition(&mountVisitor); 444 445 BDiskDeviceRoster roster; 446 447 // try mount file images 448 for (StringSet::iterator iterator = mountVisitor.toMount.begin(); 449 iterator != mountVisitor.toMount.end();) { 450 const char* name = (*iterator).c_str(); 451 iterator++; 452 453 BEntry entry(name, true); 454 if (!entry.Exists()) 455 continue; 456 457 // TODO: improve error messages 458 BPath path; 459 if (entry.GetPath(&path) != B_OK) 460 continue; 461 462 partition_id id = -1; 463 BDiskDevice device; 464 BPartition* partition; 465 466 if (!strncmp(path.Path(), "/dev/", 5)) { 467 // seems to be a device path 468 if (roster.GetPartitionForPath(path.Path(), &device, &partition) 469 != B_OK) 470 continue; 471 } else { 472 // a file with this name exists, so try to mount it 473 id = roster.RegisterFileDevice(path.Path()); 474 if (id < 0) 475 continue; 476 477 if (roster.GetPartitionWithID(id, &device, &partition) != B_OK) { 478 roster.UnregisterFileDevice(id); 479 continue; 480 } 481 } 482 483 status_t status = partition->Mount(NULL, 484 mountVisitor.readOnly ? B_MOUNT_READ_ONLY : 0); 485 if (!mountVisitor.silent) { 486 if (status >= B_OK) { 487 BPath mountPoint; 488 partition->GetMountPoint(&mountPoint); 489 printf("%s \"%s\" mounted successfully at \"%s\".\n", 490 id < 0 ? "Device" : "Image", name, mountPoint.Path()); 491 } 492 } 493 if (status >= B_OK) { 494 if (mountVisitor.openInTracker) 495 open_in_tracker(partition); 496 497 // remove from list 498 mountVisitor.toMount.erase(name); 499 } else if (id >= 0) 500 roster.UnregisterFileDevice(id); 501 } 502 503 // TODO: support unmounting images by path! 504 505 // print errors for the volumes to mount/unmount, that weren't found 506 if (!mountVisitor.silent) { 507 for (StringSet::iterator it = mountVisitor.toMount.begin(); 508 it != mountVisitor.toMount.end(); it++) { 509 fprintf(stderr, "Failed to mount volume `%s': Volume not found.\n", 510 (*it).c_str()); 511 } 512 for (StringSet::iterator it = mountVisitor.toUnmount.begin(); 513 it != mountVisitor.toUnmount.end(); it++) { 514 fprintf(stderr, "Failed to unmount volume `%s': Volume not " 515 "found.\n", (*it).c_str()); 516 } 517 } 518 519 // update the disk device list 520 error = deviceList.Fetch(); 521 if (error != B_OK) { 522 fprintf(stderr, "Failed to update the list of disk devices: %s", 523 strerror(error)); 524 exit(1); 525 } 526 527 // print information 528 529 if (listAllDevices) { 530 // TODO 531 } 532 533 // determine width of the terminal in order to shrink the columns if needed 534 if (isatty(STDOUT_FILENO)) { 535 winsize size; 536 if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &size, sizeof(winsize)) == 0) { 537 if (size.ws_col < 95) { 538 sVolumeNameWidth -= (95 - size.ws_col) / 2; 539 sFSNameWidth -= (95 - size.ws_col) / 2; 540 } 541 } 542 } 543 544 if (printPartitionsVisitor.IsUsed()) { 545 printf("%-*s %-*s Size Mounted At (Device)\n", 546 sVolumeNameWidth, "Volume", sFSNameWidth, "File System"); 547 BString separator; 548 separator.SetTo('-', sVolumeNameWidth + sFSNameWidth + 35); 549 puts(separator.String()); 550 551 if (printPartitionsVisitor.listAllPartitions) 552 deviceList.VisitEachPartition(&printPartitionsVisitor); 553 else 554 deviceList.VisitEachMountablePartition(&printPartitionsVisitor); 555 } 556 557 exit(0); 558 } 559 560 561 void 562 MountVolume::ReadyToRun() 563 { 564 // We will only get here if we were launched without any arguments or 565 // startup messages 566 567 extern int __libc_argc; 568 extern char** __libc_argv; 569 570 ArgvReceived(__libc_argc, __libc_argv); 571 } 572 573 574 // #pragma mark - 575 576 577 int 578 main() 579 { 580 MountVolume mountVolume; 581 mountVolume.Run(); 582 return 0; 583 } 584 585