btrfs: Attempt to fix GCC2 build.
[haiku.git] / src / bin / mountvolume.cpp
blob43c4c5aebc08259479cf32ad80b63f65b400eb54
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
6 * All rights reserved. Distributed under the terms of the MIT License.
7 */
10 #include <set>
11 #include <string>
13 #include <stdio.h>
14 #include <stdlib.h>
15 #include <string.h>
16 #include <termios.h>
18 #include <Application.h>
19 #include <Path.h>
20 #include <String.h>
21 #include <fs_volume.h>
23 #include <DiskDevice.h>
24 #include <DiskDevicePrivate.h>
25 #include <DiskDeviceRoster.h>
26 #include <DiskDeviceTypes.h>
27 #include <DiskDeviceList.h>
28 #include <Partition.h>
30 #include <tracker_private.h>
33 using std::set;
34 using std::string;
36 extern const char* __progname;
39 typedef set<string> StringSet;
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";
78 const char* kAppName = __progname;
80 static int sVolumeNameWidth = B_OS_NAME_LENGTH;
81 static int sFSNameWidth = 25;
84 static void
85 print_usage(bool error)
87 fprintf(error ? stderr : stdout, kUsage, kAppName);
91 static void
92 print_usage_and_exit(bool error)
94 print_usage(error);
95 exit(error ? 0 : 1);
99 static const char*
100 size_string(int64 size)
102 double blocks = size;
103 static char string[64];
105 if (size < 1024)
106 sprintf(string, "%" B_PRId64, size);
107 else {
108 const char* units[] = {"K", "M", "G", NULL};
109 int32 i = -1;
111 do {
112 blocks /= 1024.0;
113 i++;
114 } while (blocks >= 1024 && units[i + 1]);
116 snprintf(string, sizeof(string), "%.1f%s", blocks, units[i]);
119 return string;
123 static status_t
124 open_in_tracker(BPartition* partition)
126 BPath mountPoint;
127 status_t status = partition->GetMountPoint(&mountPoint);
128 if (status != B_OK)
129 return status;
131 entry_ref ref;
132 status = get_ref_for_path(mountPoint.Path(), &ref);
133 if (status != B_OK)
134 return status;
136 BMessage refs(B_REFS_RECEIVED);
137 refs.AddRef("refs", &ref);
138 return BMessenger(kTrackerSignature).SendMessage(&refs);
142 // #pragma mark -
145 struct MountVisitor : public BDiskDeviceVisitor {
146 MountVisitor()
148 silent(false),
149 mountAll(false),
150 mountBFS(false),
151 mountHFS(false),
152 mountDOS(false),
153 readOnly(false),
154 openInTracker(false)
158 virtual bool Visit(BDiskDevice* device)
160 return Visit(device, 0);
163 virtual bool Visit(BPartition* partition, int32 level)
165 // get name and type
166 const char* name = partition->ContentName();
167 if (!name)
168 name = partition->Name();
169 const char* type = partition->ContentType();
171 // check whether to mount
172 bool mount = false;
173 if (name && toMount.find(name) != toMount.end()) {
174 toMount.erase(name);
175 if (!partition->IsMounted())
176 mount = true;
177 else if (!silent)
178 fprintf(stderr, "Volume `%s' already mounted.\n", name);
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;
193 // don't try to mount a partition twice
194 if (partition->IsMounted())
195 mount = false;
197 // check whether to unmount
198 bool unmount = false;
199 if (name && toUnmount.find(name) != toUnmount.end()) {
200 toUnmount.erase(name);
201 if (partition->IsMounted()) {
202 unmount = true;
203 mount = false;
204 } else if (!silent)
205 fprintf(stderr, "Volume `%s' not mounted.\n", name);
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,
217 mountPoint.Path());
218 } else {
219 fprintf(stderr, "Failed to mount volume `%s': %s\n",
220 name, strerror(error));
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);
230 } else {
231 fprintf(stderr, "Failed to unmount volume `%s': %s\n",
232 name, strerror(error));
237 return false;
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;
252 struct PrintPartitionsVisitor : public BDiskDeviceVisitor {
253 PrintPartitionsVisitor()
254 : listMountablePartitions(false),
255 listAllPartitions(false)
259 bool IsUsed()
261 return listMountablePartitions || listAllPartitions;
264 virtual bool Visit(BDiskDevice* device)
266 return Visit(device, 0);
269 virtual bool Visit(BPartition* partition, int32 level)
271 // get name and type
272 const char* name = partition->ContentName();
273 if (name == NULL || name[0] == '\0') {
274 name = partition->Name();
275 if (name == NULL || name[0] == '\0') {
276 if (partition->ContainsFileSystem())
277 name = "<unnamed>";
278 else
279 name = "";
282 const char* type = partition->ContentType();
283 if (type == NULL)
284 type = "<unknown>";
286 // shorten known types for display
287 if (!strcmp(type, kPartitionTypeMultisession))
288 type = "Multisession";
289 else if (!strcmp(type, kPartitionTypeIntelExtended))
290 type = "Intel Extended";
292 BPath path;
293 partition->GetPath(&path);
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;
300 BPath mountPoint;
301 if (partition->IsMounted())
302 partition->GetMountPoint(&mountPoint);
304 printf("%-*s %-*s %8s %s%s(%s)\n", sVolumeNameWidth, name,
305 sFSNameWidth, type, size_string(partition->Size()),
306 partition->IsMounted() ? mountPoint.Path() : "",
307 partition->IsMounted() ? " " : "",
308 path.Path() + skip);
309 return false;
312 bool listMountablePartitions;
313 bool listAllPartitions;
317 // #pragma mark -
320 class MountVolume : public BApplication {
321 public:
322 MountVolume();
323 virtual ~MountVolume();
325 virtual void RefsReceived(BMessage* message);
326 virtual void ArgvReceived(int32 argc, char** argv);
327 virtual void ReadyToRun();
331 MountVolume::MountVolume()
333 BApplication("application/x-vnd.haiku-mountvolume")
338 MountVolume::~MountVolume()
343 void
344 MountVolume::RefsReceived(BMessage* message)
346 status_t status;
347 int32 refCount;
348 type_code typeFound;
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);
357 entry_ref ref;
358 BPath path;
360 int32 argc = refCount + 2;
361 char** argv = new char*[argc + 1];
362 argv[0] = strdup(kAppName);
363 argv[1] = strdup("-open");
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));
372 argv[2 + i] = strdup(path.Path());
374 argv[argc] = NULL;
376 ArgvReceived(argc, argv);
380 void
381 MountVolume::ArgvReceived(int32 argc, char** argv)
383 MountVisitor mountVisitor;
384 PrintPartitionsVisitor printPartitionsVisitor;
385 bool listAllDevices = false;
387 if (argc < 2)
388 printPartitionsVisitor.listMountablePartitions = true;
390 // parse arguments
392 for (int argi = 1; argi < argc; argi++) {
393 const char* arg = argv[argi];
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);
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);
442 // mount/unmount volumes
443 deviceList.VisitEachMountablePartition(&mountVisitor);
445 BDiskDeviceRoster roster;
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++;
453 BEntry entry(name, true);
454 if (!entry.Exists())
455 continue;
457 // TODO: improve error messages
458 BPath path;
459 if (entry.GetPath(&path) != B_OK)
460 continue;
462 partition_id id = -1;
463 BDiskDevice device;
464 BPartition* partition;
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;
477 if (roster.GetPartitionWithID(id, &device, &partition) != B_OK) {
478 roster.UnregisterFileDevice(id);
479 continue;
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());
493 if (status >= B_OK) {
494 if (mountVisitor.openInTracker)
495 open_in_tracker(partition);
497 // remove from list
498 mountVisitor.toMount.erase(name);
499 } else if (id >= 0)
500 roster.UnregisterFileDevice(id);
503 // TODO: support unmounting images by path!
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());
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());
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);
527 // print information
529 if (listAllDevices) {
530 // TODO
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;
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());
551 if (printPartitionsVisitor.listAllPartitions)
552 deviceList.VisitEachPartition(&printPartitionsVisitor);
553 else
554 deviceList.VisitEachMountablePartition(&printPartitionsVisitor);
557 exit(0);
561 void
562 MountVolume::ReadyToRun()
564 // We will only get here if we were launched without any arguments or
565 // startup messages
567 extern int __libc_argc;
568 extern char** __libc_argv;
570 ArgvReceived(__libc_argc, __libc_argv);
574 // #pragma mark -
578 main()
580 MountVolume mountVolume;
581 mountVolume.Run();
582 return 0;