xref: /haiku/src/bin/mountvolume.cpp (revision 1e36cfc2721ef13a187c6f7354dc9cbc485e89d3)
1 /*
2  * Copyright 2005-2006, 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 // MountVisitor
87 struct MountVisitor : public BDiskDeviceVisitor {
88 	MountVisitor()
89 		: silent(false),
90 		  mountAll(false),
91 		  mountBFS(false),
92 		  mountHFS(false),
93 		  mountDOS(false),
94 		  readOnly(false)
95 	{
96 	}
97 
98 	virtual bool Visit(BDiskDevice *device)
99 	{
100 		return Visit(device, 0);
101 	}
102 
103 	virtual bool Visit(BPartition *partition, int32 level)
104 	{
105 		// get name and type
106 		const char *name = partition->ContentName();
107 		if (!name)
108 			name = partition->Name();
109 		const char *type = partition->ContentType();
110 
111 		// check whether to mount
112 		bool mount = false;
113 		if (name && toMount.find(name) != toMount.end()) {
114 			toMount.erase(name);
115 			if (!partition->IsMounted())
116 				mount = true;
117 			else if (!silent)
118 				fprintf(stderr, "Volume `%s' already mounted.\n", name);
119 		} else if (mountAll) {
120 			mount = true;
121 		} else if (mountBFS && type != NULL
122 			&& strcmp(type, kPartitionTypeBFS) == 0) {
123 			mount = true;
124 		} else if (mountHFS && type != NULL
125 			&& strcmp(type, kPartitionTypeHFS) == 0) {
126 			mount = true;
127 		} else if (mountDOS && type != NULL
128 			&& (strcmp(type, kPartitionTypeFAT12) == 0
129 				|| strcmp(type, kPartitionTypeFAT32) == 0)) {
130 			mount = true;
131 		}
132 
133 		// don't try to mount a partition twice
134 		if (partition->IsMounted())
135 			mount = false;
136 
137 		// check whether to unmount
138 		bool unmount = false;
139 		if (name && toUnmount.find(name) != toUnmount.end()) {
140 			toUnmount.erase(name);
141 			if (!partition->IsMounted()) {
142 				unmount = true;
143 				mount = false;
144 			} else if (!silent)
145 				fprintf(stderr, "Volume `%s' not mounted.\n", name);
146 		}
147 
148 		// mount/unmount
149 		if (mount) {
150 			status_t error = partition->Mount(NULL,
151 				(readOnly ? B_MOUNT_READ_ONLY : 0));
152 			if (!silent) {
153 				if (error >= B_OK) {
154 					BPath mountPoint;
155 					partition->GetMountPoint(&mountPoint);
156 					printf("Volume `%s' mounted successfully at '%s'.\n", name, mountPoint.Path());
157 				} else {
158 					fprintf(stderr, "Failed to mount volume `%s': %s\n",
159 						name, strerror(error));
160 				}
161 			}
162 		} else if (unmount) {
163 			status_t error = partition->Unmount();
164 			if (!silent) {
165 				if (error == B_OK) {
166 					printf("Volume `%s' unmounted successfully.\n", name);
167 				} else {
168 					fprintf(stderr, "Failed to unmount volume `%s': %s\n",
169 						name, strerror(error));
170 				}
171 			}
172 		}
173 
174 		return false;
175 	}
176 
177 	bool		silent;
178 	StringSet	toMount;
179 	StringSet	toUnmount;
180 	bool		mountAll;
181 	bool		mountBFS;
182 	bool		mountHFS;
183 	bool		mountDOS;
184 	bool		readOnly;
185 };
186 
187 // PrintPartitionsVisitor
188 struct PrintPartitionsVisitor : public BDiskDeviceVisitor {
189 	PrintPartitionsVisitor()
190 		: listMountablePartitions(false),
191 		  listAllPartitions(false)
192 	{
193 	}
194 
195 	bool IsUsed()
196 	{
197 		return listMountablePartitions || listAllPartitions;
198 	}
199 
200 	virtual bool Visit(BDiskDevice *device)
201 	{
202 		return Visit(device, 0);
203 	}
204 
205 	virtual bool Visit(BPartition *partition, int32 level)
206 	{
207 		// get name and type
208 		const char *name = partition->ContentName();
209 		if (name == NULL || name[0] == '\0') {
210 			name = partition->Name();
211 			if (name == NULL || name[0] == '\0') {
212 				if (partition->ContainsFileSystem())
213 					name = "<unnamed>";
214 				else
215 					name = "";
216 			}
217 		}
218 		const char *type = partition->ContentType();
219 		if (type == NULL)
220 			type = "<unknown>";
221 
222 		BPath path;
223 		if (partition->IsMounted())
224 			partition->GetMountPoint(&path);
225 
226 		printf("%-16s %-20s %s\n",
227 			name, type, partition->IsMounted() ? path.Path() : "");
228 		return false;
229 	}
230 
231 	bool listMountablePartitions;
232 	bool listAllPartitions;
233 };
234 
235 
236 // main
237 int
238 main(int argc, char **argv)
239 {
240 	if (argc < 2)
241 		print_usage_and_exit(true);
242 
243 	MountVisitor mountVisitor;
244 	PrintPartitionsVisitor printPartitionsVisitor;
245 	bool listAllDevices = false;
246 
247 	// parse arguments
248 
249 	for (int argi = 1; argi < argc; argi++) {
250 		const char *arg = argv[argi];
251 
252 		if (arg[0] != '\0' && arg[0] != '-') {
253 			mountVisitor.toMount.insert(arg);
254 		} else if (strcmp(arg, "-s") == 0) {
255 			mountVisitor.silent = true;
256 		} else if (strcmp(arg, "-h") == 0 || strcmp(arg, "--help") == 0) {
257 			print_usage_and_exit(false);
258 		} else if (strcmp(arg, "-all") == 0) {
259 			mountVisitor.mountAll = true;
260 		} else if (strcmp(arg, "-allbfs") == 0) {
261 			mountVisitor.mountBFS = true;
262 		} else if (strcmp(arg, "-allhfs") == 0) {
263 			mountVisitor.mountHFS = true;
264 		} else if (strcmp(arg, "-alldos") == 0) {
265 			mountVisitor.mountDOS = true;
266 		} else if (strcmp(arg, "-ro") == 0) {
267 			mountVisitor.readOnly = true;
268 		} else if (strcmp(arg, "-unmount") == 0) {
269 			argi++;
270 			if (argi >= argc)
271 				print_usage_and_exit(true);
272 			mountVisitor.toUnmount.insert(argv[argi]);
273 		} else if (strcmp(arg, "-p") == 0 || strcmp(arg, "-l") == 0) {
274 			printPartitionsVisitor.listMountablePartitions = true;
275 		} else if (strcmp(arg, "-lh") == 0) {
276 			printPartitionsVisitor.listAllPartitions = true;
277 		} else if (strcmp(arg, "-dd") == 0) {
278 			listAllDevices = true;
279 		} else if (strcmp(arg, "-r") == 0 || strcmp(arg, "-publishall") == 0
280 			|| strcmp(arg, "-publishbfs") == 0
281 			|| strcmp(arg, "-publishhfs") == 0
282 			|| strcmp(arg, "-publishdos") == 0) {
283 			// obsolete: ignore
284 		} else
285 			print_usage_and_exit(true);
286 	}
287 
288 	// get a disk device list
289 	BDiskDeviceList deviceList;
290 	status_t error = deviceList.Fetch();
291 	if (error != B_OK) {
292 		fprintf(stderr, "Failed to get the list of disk devices: %s",
293 			strerror(error));
294 		exit(1);
295 	}
296 
297 	// mount/unmount volumes
298 	deviceList.VisitEachMountablePartition(&mountVisitor);
299 
300 	// print errors for the volumes to mount/unmount, that weren't found
301 	if (!mountVisitor.silent) {
302 		for (StringSet::iterator it = mountVisitor.toMount.begin();
303 			 it != mountVisitor.toMount.end();
304 			 it++) {
305 			fprintf(stderr, "Failed to mount volume `%s': Volume not found.\n",
306 				(*it).c_str());
307 		}
308 		for (StringSet::iterator it = mountVisitor.toUnmount.begin();
309 			 it != mountVisitor.toUnmount.end();
310 			 it++) {
311 			fprintf(stderr, "Failed to unmount volume `%s': Volume not found.\n",
312 				(*it).c_str());
313 		}
314 	}
315 
316 	// update the disk device list
317 	error = deviceList.Fetch();
318 	if (error != B_OK) {
319 		fprintf(stderr, "Failed to update the list of disk devices: %s",
320 			strerror(error));
321 		exit(1);
322 	}
323 
324 	// print information
325 
326 	if (listAllDevices) {
327 		// TODO
328 	}
329 
330 	if (printPartitionsVisitor.IsUsed()) {
331 		puts("Volume           File System          Mounted At");
332 		puts("----------------------------------------------------------");
333 
334 		if (printPartitionsVisitor.listAllPartitions)
335 			deviceList.VisitEachPartition(&printPartitionsVisitor);
336 		else
337 			deviceList.VisitEachMountablePartition(&printPartitionsVisitor);
338 	}
339 
340 	return 0;
341 }
342