xref: /haiku/src/tests/kits/storage/virtualdrive/mkvirtualdrive.cpp (revision 24159a0c7d6d6dcba9f2a0c1a7c08d2c8167f21b)
1 // mkvirtualdrive.cpp
2 
3 #include <ctype.h>
4 #include <fcntl.h>
5 #include <errno.h>
6 #include <stdio.h>
7 #include <stdlib.h>
8 #include <string.h>
9 #include <unistd.h>
10 
11 #include <Path.h>
12 
13 #include "virtualdrive.h"
14 
15 extern "C" const char * const*__libc_argv;
16 
17 const char *kUsage =
18 "Usage: %s [ --install ] [ --size <size> ] <file>\n"
19 "       %s --uninstall  <device>\n"
20 "       %s --halt <device> (dangerous!)\n"
21 "       %s --info  <device>\n"
22 "       %s ( --help | -h )\n"
23 ;
24 
25 // print_usage
26 static
27 void
28 print_usage(bool error = false)
29 {
30 	const char *name = "mkvirtualdrive";
31 	BPath path;
32 	if (path.SetTo(__libc_argv[0]) == B_OK)
33 		name = path.Leaf();
34 	fprintf((error ? stderr : stdout), kUsage, name, name, name, name);
35 }
36 
37 
38 // parse_size
39 static
40 bool
41 parse_size(const char *str, off_t *_size)
42 {
43 	if (!str || !_size)
44 		return false;
45 	int32 len = strlen(str);
46 	char buffer[22];
47 	if (len == 0 || len + 1 > (int32)sizeof(buffer))
48 		return false;
49 	strcpy(buffer, str);
50 	// check the last char for one of the suffixes
51 	off_t suffix = 1;
52 	if (!isdigit(buffer[len - 1])) {
53 		switch (buffer[len - 1]) {
54 			case 'K':
55 				suffix = 1LL << 10;
56 				break;
57 			case 'M':
58 				suffix = 1LL << 20;
59 				break;
60 			case 'G':
61 				suffix = 1LL << 30;
62 				break;
63 			case 'T':
64 				suffix = 1LL << 40;
65 				break;
66 			case 'P':
67 				suffix = 1LL << 50;
68 				break;
69 			case 'E':
70 				suffix = 1LL << 60;
71 				break;
72 			default:
73 				return false;
74 		}
75 		buffer[len - 1] = '\0';
76 		len--;
77 		if (len == 0)
78 			return false;
79 	}
80 	// all other chars must be digits
81 	for (int i = 0; i < len; i++) {
82 		if (!isdigit(buffer[i]))
83 			return false;
84 	}
85 	off_t size = atoll(buffer);
86 	*_size = size * suffix;
87 	// check for overflow
88 	if (*_size / suffix != size)
89 		return false;
90 	return true;
91 }
92 
93 
94 // install_file
95 status_t
96 install_file(const char *file, off_t size)
97 {
98 	// open the control device
99 	int fd = open(VIRTUAL_DRIVE_CONTROL_DEVICE, O_RDONLY);
100 	if (fd < 0) {
101 		fprintf(stderr, "Failed to open control device: %s\n",
102 				strerror(errno));
103 		return errno;
104 	}
105 
106 	// set up the info
107 	virtual_drive_info info;
108 	info.magic = VIRTUAL_DRIVE_MAGIC;
109 	info.drive_info_size = sizeof(info);
110 
111 	strcpy(info.file_name, file);
112 	if (size >= 0) {
113 		info.use_geometry = true;
114 		// fill in the geometry
115 		// default to 512 bytes block size
116 		uint32 blockSize = 512;
117 		// Optimally we have only 1 block per sector and only one head.
118 		// Since we have only a uint32 for the cylinder count, this won't work
119 		// for files > 2TB. So, we set the head count to the minimally possible
120 		// value.
121 		off_t blocks = size / blockSize;
122 		uint32 heads = (blocks + ULONG_MAX - 1) / ULONG_MAX;
123 		if (heads == 0)
124 			heads = 1;
125 		info.geometry.bytes_per_sector = blockSize;
126 	    info.geometry.sectors_per_track = 1;
127 	    info.geometry.cylinder_count = blocks / heads;
128 	    info.geometry.head_count = heads;
129 	    info.geometry.device_type = B_DISK;	// TODO: Add a new constant.
130 	    info.geometry.removable = false;
131 	    info.geometry.read_only = false;
132 	    info.geometry.write_once = false;
133 	} else {
134 		info.use_geometry = false;
135 	}
136 
137 	// issue the ioctl
138 	status_t error = B_OK;
139 	if (ioctl(fd, VIRTUAL_DRIVE_REGISTER_FILE, &info) != 0) {
140 		error = errno;
141 		fprintf(stderr, "Failed to install device: %s\n", strerror(error));
142 	} else {
143 		printf("File `%s' registered as device `%s'.\n", file, info.device_name);
144 	}
145 	// close the control device
146 	close(fd);
147 	return error;
148 }
149 
150 
151 // uninstall_file
152 status_t
153 uninstall_file(const char *device, bool immediately)
154 {
155 	// open the device
156 	int fd = open(device, O_RDONLY);
157 	if (fd < 0) {
158 		fprintf(stderr, "Failed to open device `%s': %s\n",
159 				device, strerror(errno));
160 		return errno;
161 	}
162 
163 	// issue the ioctl
164 	status_t error = B_OK;
165 	if (ioctl(fd, VIRTUAL_DRIVE_UNREGISTER_FILE, immediately) != 0) {
166 		error = errno;
167 		fprintf(stderr, "Failed to uninstall device: %s\n", strerror(error));
168 	}
169 	// close the control device
170 	close(fd);
171 	return error;
172 }
173 
174 
175 status_t
176 info_for_device(const char *device)
177 {
178 	// open the device
179 	int fd = open(device, O_RDONLY);
180 	if (fd < 0) {
181 		fprintf(stderr, "Failed to open device `%s': %s\n",
182 				device, strerror(errno));
183 		return errno;
184 	}
185 
186 	// set up the info
187 	virtual_drive_info info;
188 	info.magic = VIRTUAL_DRIVE_MAGIC;
189 	info.drive_info_size = sizeof(info);
190 
191 	// issue the ioctl
192 	status_t error = B_OK;
193 	if (ioctl(fd, VIRTUAL_DRIVE_GET_INFO, &info) != 0) {
194 		error = errno;
195 		fprintf(stderr, "Failed to get device info: %s\n", strerror(error));
196 	} else {
197 		printf("Device `%s' points to file `%s'.\n", info.device_name, info.file_name);
198 		off_t size = (off_t)info.geometry.bytes_per_sector
199 				* info.geometry.sectors_per_track
200 				* info.geometry.cylinder_count
201 				* info.geometry.head_count;
202 		printf("\tdisk size is %Ld bytes (%g MB)\n", size, size / (1024.0 * 1024));
203 		if (info.halted)
204 			printf("\tdevice is currently halted.\n");
205 	}
206 	// close the control device
207 	close(fd);
208 	return error;
209 }
210 
211 
212 // main
213 int
214 main(int argc, const char **argv)
215 {
216 	status_t error = B_OK;
217 	int argIndex = 1;
218 	enum { INSTALL, UNINSTALL, INFO } mode = INSTALL;
219 	bool immediately = false;
220 	off_t size = -1;
221 
222 	// parse options
223 	for (; error == B_OK && argIndex < argc
224 		   && argv[argIndex][0] == '-'; argIndex++) {
225 		const char *arg = argv[argIndex];
226 		if (arg[1] == '-') {
227 			// "--" option
228 			arg += 2;
229 			if (!strcmp(arg, "install")) {
230 				mode = INSTALL;
231 			} else if (!strcmp(arg, "size")) {
232 				argIndex++;
233 				if (argIndex >= argc) {
234 					fprintf(stderr, "Parameter expected for `--%s'.\n", arg);
235 					print_usage(1);
236 					return 1;
237 				}
238 				if (!parse_size(argv[argIndex], &size)) {
239 					fprintf(stderr, "Parameter for `--%s' must be of the form "
240 							"`<number>[K|M|G|T|P|E]'.\n", arg);
241 					print_usage(1);
242 					return 1;
243 				}
244 			} else if (!strcmp(arg, "uninstall")) {
245 				mode = UNINSTALL;
246 			} else if (!strcmp(arg, "halt")) {
247 				mode = UNINSTALL;
248 				immediately = true;
249 			} else if (!strcmp(arg, "info")) {
250 				mode = INFO;
251 			} else if (!strcmp(arg, "help")) {
252 				print_usage();
253 				return 0;
254 			} else {
255 				fprintf(stderr, "Invalid option `-%s'.\n", arg);
256 				print_usage(true);
257 				return 1;
258 			}
259 		} else {
260 			// "-" options
261 			arg++;
262 			int32 count = strlen(arg);
263 			for (int i = 0; error == B_OK && i < count; i++) {
264 				switch (arg[i]) {
265 					case 'h':
266 						print_usage();
267 						return 0;
268 					default:
269 						fprintf(stderr, "Invalid option `-%c'.\n", arg[i]);
270 						print_usage(true);
271 						return 1;
272 				}
273 			}
274 		}
275 	}
276 
277 	// parse rest (the file name)
278 	if (argIndex != argc - 1) {
279 		print_usage(true);
280 		return 1;
281 	}
282 	const char *file = argv[argIndex];
283 
284 	// do the job
285 	switch (mode) {
286 		case INSTALL:
287 			error = install_file(file, size);
288 			break;
289 		case UNINSTALL:
290 			error = uninstall_file(file, immediately);
291 			break;
292 		case INFO:
293 			error = info_for_device(file);
294 			break;
295 	}
296 
297 	return (error == B_OK ? 0 : 1);
298 }
299 
300