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