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.
18 #include <Application.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>
36 extern const char* __progname
;
39 typedef set
<string
> StringSet
;
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"
47 "The terminology is actually not quite correct: By volumes only partitions\n"
48 "living on disk devices are meant.\n"
52 " -s - silent; don't print info about (un)mounting\n"
53 " -h, --help - print this info text\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"
65 " -p, -l - list all mounted and mountable volumes\n"
66 " -lh - list all existing volumes (incl. not-mountable "
68 " -dd - list all disk existing devices\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;
85 print_usage(bool error
)
87 fprintf(error
? stderr
: stdout
, kUsage
, kAppName
);
92 print_usage_and_exit(bool error
)
100 size_string(int64 size
)
102 double blocks
= size
;
103 static char string
[64];
106 sprintf(string
, "%" B_PRId64
, size
);
108 const char* units
[] = {"K", "M", "G", NULL
};
114 } while (blocks
>= 1024 && units
[i
+ 1]);
116 snprintf(string
, sizeof(string
), "%.1f%s", blocks
, units
[i
]);
124 open_in_tracker(BPartition
* partition
)
127 status_t status
= partition
->GetMountPoint(&mountPoint
);
132 status
= get_ref_for_path(mountPoint
.Path(), &ref
);
136 BMessage
refs(B_REFS_RECEIVED
);
137 refs
.AddRef("refs", &ref
);
138 return BMessenger(kTrackerSignature
).SendMessage(&refs
);
145 struct MountVisitor
: public BDiskDeviceVisitor
{
158 virtual bool Visit(BDiskDevice
* device
)
160 return Visit(device
, 0);
163 virtual bool Visit(BPartition
* partition
, int32 level
)
166 const char* name
= partition
->ContentName();
168 name
= partition
->Name();
169 const char* type
= partition
->ContentType();
171 // check whether to mount
173 if (name
&& toMount
.find(name
) != toMount
.end()) {
175 if (!partition
->IsMounted())
178 fprintf(stderr
, "Volume `%s' already mounted.\n", name
);
179 } else if (mountAll
) {
181 } else if (mountBFS
&& type
!= NULL
182 && strcmp(type
, kPartitionTypeBFS
) == 0) {
184 } else if (mountHFS
&& type
!= NULL
185 && strcmp(type
, kPartitionTypeHFS
) == 0) {
187 } else if (mountDOS
&& type
!= NULL
188 && (strcmp(type
, kPartitionTypeFAT12
) == 0
189 || strcmp(type
, kPartitionTypeFAT32
) == 0)) {
193 // don't try to mount a partition twice
194 if (partition
->IsMounted())
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()) {
205 fprintf(stderr
, "Volume `%s' not mounted.\n", name
);
210 status_t error
= partition
->Mount(NULL
,
211 readOnly
? B_MOUNT_READ_ONLY
: 0);
215 partition
->GetMountPoint(&mountPoint
);
216 printf("Volume `%s' mounted successfully at '%s'.\n", name
,
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();
229 printf("Volume `%s' unmounted successfully.\n", name
);
231 fprintf(stderr
, "Failed to unmount volume `%s': %s\n",
232 name
, strerror(error
));
252 struct PrintPartitionsVisitor
: public BDiskDeviceVisitor
{
253 PrintPartitionsVisitor()
254 : listMountablePartitions(false),
255 listAllPartitions(false)
261 return listMountablePartitions
|| listAllPartitions
;
264 virtual bool Visit(BDiskDevice
* device
)
266 return Visit(device
, 0);
269 virtual bool Visit(BPartition
* partition
, int32 level
)
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())
282 const char* type
= partition
->ContentType();
286 // shorten known types for display
287 if (!strcmp(type
, kPartitionTypeMultisession
))
288 type
= "Multisession";
289 else if (!strcmp(type
, kPartitionTypeIntelExtended
))
290 type
= "Intel Extended";
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
))
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() ? " " : "",
312 bool listMountablePartitions
;
313 bool listAllPartitions
;
320 class MountVolume
: public BApplication
{
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()
344 MountVolume::RefsReceived(BMessage
* message
)
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",
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());
376 ArgvReceived(argc
, argv
);
381 MountVolume::ArgvReceived(int32 argc
, char** argv
)
383 MountVisitor mountVisitor
;
384 PrintPartitionsVisitor printPartitionsVisitor
;
385 bool listAllDevices
= false;
388 printPartitionsVisitor
.listMountablePartitions
= true;
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) {
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) {
430 print_usage_and_exit(true);
433 // get a disk device list
434 BDiskDeviceList deviceList
;
435 status_t error
= deviceList
.Fetch();
437 fprintf(stderr
, "Failed to get the list of disk devices: %s",
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();
453 BEntry
entry(name
, true);
457 // TODO: improve error messages
459 if (entry
.GetPath(&path
) != B_OK
)
462 partition_id id
= -1;
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
)
472 // a file with this name exists, so try to mount it
473 id
= roster
.RegisterFileDevice(path
.Path());
477 if (roster
.GetPartitionWithID(id
, &device
, &partition
) != B_OK
) {
478 roster
.UnregisterFileDevice(id
);
483 status_t status
= partition
->Mount(NULL
,
484 mountVisitor
.readOnly
? B_MOUNT_READ_ONLY
: 0);
485 if (!mountVisitor
.silent
) {
486 if (status
>= B_OK
) {
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
);
498 mountVisitor
.toMount
.erase(name
);
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",
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();
522 fprintf(stderr
, "Failed to update the list of disk devices: %s",
529 if (listAllDevices
) {
533 // determine width of the terminal in order to shrink the columns if needed
534 if (isatty(STDOUT_FILENO
)) {
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");
548 separator
.SetTo('-', sVolumeNameWidth
+ sFSNameWidth
+ 35);
549 puts(separator
.String());
551 if (printPartitionsVisitor
.listAllPartitions
)
552 deviceList
.VisitEachPartition(&printPartitionsVisitor
);
554 deviceList
.VisitEachMountablePartition(&printPartitionsVisitor
);
562 MountVolume::ReadyToRun()
564 // We will only get here if we were launched without any arguments or
567 extern int __libc_argc
;
568 extern char** __libc_argv
;
570 ArgvReceived(__libc_argc
, __libc_argv
);
580 MountVolume mountVolume
;