1 /* 2 * Copyright 2005-2007, Ingo Weinhold, bonefish@users.sf.net. All rights reserved. 3 * Distributed under the terms of the MIT License. 4 */ 5 6 7 #include <stdio.h> 8 #include <string.h> 9 10 #include <set> 11 #include <string> 12 13 #include <DiskDevice.h> 14 #include <DiskDevicePrivate.h> 15 #include <DiskDeviceRoster.h> 16 #include <DiskDeviceTypes.h> 17 #include <DiskDeviceList.h> 18 #include <Partition.h> 19 20 #include <Path.h> 21 #include <fs_volume.h> 22 23 using std::set; 24 using std::string; 25 26 extern const char *__progname; 27 28 29 typedef set<string> StringSet; 30 31 // usage 32 static const char *kUsage = 33 "%s <options> [ <volume name> ... ]\n" 34 "Mounts the volume with name <volume name>, if given. Lists info about\n" 35 "mounted and mountable volumes and mounts/unmounts volumes.\n" 36 "\n" 37 "The terminology is actually not quite correct: By volumes only partitions\n" 38 "living on disk devices are meant.\n" 39 "\n" 40 "Options:\n" 41 "[general]\n" 42 " -s - silent; don't print info about (un)mounting\n" 43 " -h, --help - print this info text\n" 44 "\n" 45 "[mounting]\n" 46 " -all - mount all mountable volumes\n" 47 " -allbfs - mount all mountable BFS volumes\n" 48 " -allhfs - mount all mountable HFS volumes\n" 49 " -alldos - mount all mountable DOS volumes\n" 50 " -ro - mount volumes read-only\n" 51 " -unmount <volume> - unmount the volume with the name <volume>\n" 52 "\n" 53 "[info]\n" 54 " -p, -l - list all mounted and mountable volumes\n" 55 " -lh - list all existing volumes (incl. not-mountable ones)\n" 56 " -dd - list all disk existing devices\n" 57 "\n" 58 "[obsolete]\n" 59 " -r - ignored\n" 60 " -publishall - ignored\n" 61 " -publishbfs - ignored\n" 62 " -publishhfs - ignored\n" 63 " -publishdos - ignored\n"; 64 65 // application name 66 const char *kAppName = __progname; 67 68 69 // print_usage 70 static 71 void 72 print_usage(bool error) 73 { 74 fprintf(error ? stderr : stdout, kUsage, kAppName); 75 } 76 77 // print_usage_and_exit 78 static 79 void 80 print_usage_and_exit(bool error) 81 { 82 print_usage(error); 83 exit(error ? 0 : 1); 84 } 85 86 static const char * 87 size_string(int64 size) 88 { 89 double blocks = size; 90 static char string[64]; 91 92 if (size < 1024) 93 sprintf(string, "%Ld", size); 94 else { 95 char *units[] = {"K", "M", "G", NULL}; 96 int32 i = -1; 97 98 do { 99 blocks /= 1024.0; 100 i++; 101 } while (blocks >= 1024 && units[i + 1]); 102 103 snprintf(string, sizeof(string), "%.1f%s", blocks, units[i]); 104 } 105 106 return string; 107 } 108 109 110 // #pragma mark - 111 112 113 struct MountVisitor : public BDiskDeviceVisitor { 114 MountVisitor() 115 : silent(false), 116 mountAll(false), 117 mountBFS(false), 118 mountHFS(false), 119 mountDOS(false), 120 readOnly(false) 121 { 122 } 123 124 virtual bool Visit(BDiskDevice *device) 125 { 126 return Visit(device, 0); 127 } 128 129 virtual bool Visit(BPartition *partition, int32 level) 130 { 131 // get name and type 132 const char *name = partition->ContentName(); 133 if (!name) 134 name = partition->Name(); 135 const char *type = partition->ContentType(); 136 137 // check whether to mount 138 bool mount = false; 139 if (name && toMount.find(name) != toMount.end()) { 140 toMount.erase(name); 141 if (!partition->IsMounted()) 142 mount = true; 143 else if (!silent) 144 fprintf(stderr, "Volume `%s' already mounted.\n", name); 145 } else if (mountAll) { 146 mount = true; 147 } else if (mountBFS && type != NULL 148 && strcmp(type, kPartitionTypeBFS) == 0) { 149 mount = true; 150 } else if (mountHFS && type != NULL 151 && strcmp(type, kPartitionTypeHFS) == 0) { 152 mount = true; 153 } else if (mountDOS && type != NULL 154 && (strcmp(type, kPartitionTypeFAT12) == 0 155 || strcmp(type, kPartitionTypeFAT32) == 0)) { 156 mount = true; 157 } 158 159 // don't try to mount a partition twice 160 if (partition->IsMounted()) 161 mount = false; 162 163 // check whether to unmount 164 bool unmount = false; 165 if (name && toUnmount.find(name) != toUnmount.end()) { 166 toUnmount.erase(name); 167 if (!partition->IsMounted()) { 168 unmount = true; 169 mount = false; 170 } else if (!silent) 171 fprintf(stderr, "Volume `%s' not mounted.\n", name); 172 } 173 174 // mount/unmount 175 if (mount) { 176 status_t error = partition->Mount(NULL, 177 (readOnly ? B_MOUNT_READ_ONLY : 0)); 178 if (!silent) { 179 if (error >= B_OK) { 180 BPath mountPoint; 181 partition->GetMountPoint(&mountPoint); 182 printf("Volume `%s' mounted successfully at '%s'.\n", name, mountPoint.Path()); 183 } else { 184 fprintf(stderr, "Failed to mount volume `%s': %s\n", 185 name, strerror(error)); 186 } 187 } 188 } else if (unmount) { 189 status_t error = partition->Unmount(); 190 if (!silent) { 191 if (error == B_OK) { 192 printf("Volume `%s' unmounted successfully.\n", name); 193 } else { 194 fprintf(stderr, "Failed to unmount volume `%s': %s\n", 195 name, strerror(error)); 196 } 197 } 198 } 199 200 return false; 201 } 202 203 bool silent; 204 StringSet toMount; 205 StringSet toUnmount; 206 bool mountAll; 207 bool mountBFS; 208 bool mountHFS; 209 bool mountDOS; 210 bool readOnly; 211 }; 212 213 // PrintPartitionsVisitor 214 struct PrintPartitionsVisitor : public BDiskDeviceVisitor { 215 PrintPartitionsVisitor() 216 : listMountablePartitions(false), 217 listAllPartitions(false) 218 { 219 } 220 221 bool IsUsed() 222 { 223 return listMountablePartitions || listAllPartitions; 224 } 225 226 virtual bool Visit(BDiskDevice *device) 227 { 228 return Visit(device, 0); 229 } 230 231 virtual bool Visit(BPartition *partition, int32 level) 232 { 233 // get name and type 234 const char *name = partition->ContentName(); 235 if (name == NULL || name[0] == '\0') { 236 name = partition->Name(); 237 if (name == NULL || name[0] == '\0') { 238 if (partition->ContainsFileSystem()) 239 name = "<unnamed>"; 240 else 241 name = ""; 242 } 243 } 244 const char *type = partition->ContentType(); 245 if (type == NULL) 246 type = "<unknown>"; 247 248 // shorten known types for display 249 if (!strcmp(type, kPartitionTypeMultisession)) 250 type = "Multisession"; 251 else if (!strcmp(type, kPartitionTypeIntelExtended)) 252 type = "Intel Extended"; 253 254 BPath path; 255 partition->GetPath(&path); 256 257 // cut off beginning of the device path (if /dev/disk/) 258 int32 skip = strlen("/dev/disk/"); 259 if (strncmp(path.Path(), "/dev/disk/", skip)) 260 skip = 0; 261 262 BPath mountPoint; 263 if (partition->IsMounted()) 264 partition->GetMountPoint(&mountPoint); 265 266 printf("%-14s %-20s %8s %s (%s)\n", 267 name, type, size_string(partition->Size()), 268 partition->IsMounted() ? mountPoint.Path() : "", 269 path.Path() + skip); 270 return false; 271 } 272 273 bool listMountablePartitions; 274 bool listAllPartitions; 275 }; 276 277 278 // #pragma mark - 279 280 281 int 282 main(int argc, char **argv) 283 { 284 if (argc < 2) 285 print_usage_and_exit(true); 286 287 MountVisitor mountVisitor; 288 PrintPartitionsVisitor printPartitionsVisitor; 289 bool listAllDevices = false; 290 291 // parse arguments 292 293 for (int argi = 1; argi < argc; argi++) { 294 const char *arg = argv[argi]; 295 296 if (arg[0] != '\0' && arg[0] != '-') { 297 mountVisitor.toMount.insert(arg); 298 } else if (strcmp(arg, "-s") == 0) { 299 mountVisitor.silent = true; 300 } else if (strcmp(arg, "-h") == 0 || strcmp(arg, "--help") == 0) { 301 print_usage_and_exit(false); 302 } else if (strcmp(arg, "-all") == 0) { 303 mountVisitor.mountAll = true; 304 } else if (strcmp(arg, "-allbfs") == 0) { 305 mountVisitor.mountBFS = true; 306 } else if (strcmp(arg, "-allhfs") == 0) { 307 mountVisitor.mountHFS = true; 308 } else if (strcmp(arg, "-alldos") == 0) { 309 mountVisitor.mountDOS = true; 310 } else if (strcmp(arg, "-ro") == 0) { 311 mountVisitor.readOnly = true; 312 } else if (strcmp(arg, "-unmount") == 0) { 313 argi++; 314 if (argi >= argc) 315 print_usage_and_exit(true); 316 mountVisitor.toUnmount.insert(argv[argi]); 317 } else if (strcmp(arg, "-p") == 0 || strcmp(arg, "-l") == 0) { 318 printPartitionsVisitor.listMountablePartitions = true; 319 } else if (strcmp(arg, "-lh") == 0) { 320 printPartitionsVisitor.listAllPartitions = true; 321 } else if (strcmp(arg, "-dd") == 0) { 322 listAllDevices = true; 323 } else if (strcmp(arg, "-r") == 0 || strcmp(arg, "-publishall") == 0 324 || strcmp(arg, "-publishbfs") == 0 325 || strcmp(arg, "-publishhfs") == 0 326 || strcmp(arg, "-publishdos") == 0) { 327 // obsolete: ignore 328 } else 329 print_usage_and_exit(true); 330 } 331 332 // get a disk device list 333 BDiskDeviceList deviceList; 334 status_t error = deviceList.Fetch(); 335 if (error != B_OK) { 336 fprintf(stderr, "Failed to get the list of disk devices: %s", 337 strerror(error)); 338 exit(1); 339 } 340 341 // mount/unmount volumes 342 deviceList.VisitEachMountablePartition(&mountVisitor); 343 344 // print errors for the volumes to mount/unmount, that weren't found 345 if (!mountVisitor.silent) { 346 for (StringSet::iterator it = mountVisitor.toMount.begin(); 347 it != mountVisitor.toMount.end(); 348 it++) { 349 fprintf(stderr, "Failed to mount volume `%s': Volume not found.\n", 350 (*it).c_str()); 351 } 352 for (StringSet::iterator it = mountVisitor.toUnmount.begin(); 353 it != mountVisitor.toUnmount.end(); 354 it++) { 355 fprintf(stderr, "Failed to unmount volume `%s': Volume not found.\n", 356 (*it).c_str()); 357 } 358 } 359 360 // update the disk device list 361 error = deviceList.Fetch(); 362 if (error != B_OK) { 363 fprintf(stderr, "Failed to update the list of disk devices: %s", 364 strerror(error)); 365 exit(1); 366 } 367 368 // print information 369 370 if (listAllDevices) { 371 // TODO 372 } 373 374 if (printPartitionsVisitor.IsUsed()) { 375 puts("Volume File System Size Mounted At (Device)"); 376 puts("---------------------------------------------------------------------"); 377 378 if (printPartitionsVisitor.listAllPartitions) 379 deviceList.VisitEachPartition(&printPartitionsVisitor); 380 else 381 deviceList.VisitEachMountablePartition(&printPartitionsVisitor); 382 } 383 384 return 0; 385 } 386