xref: /haiku/src/bin/mountvolume.cpp (revision b028e77473189065f2baefc6f5e10d451cf591e2)
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