xref: /haiku/src/tests/kits/storage/virtualdrive/mkvirtualdrive.cpp (revision d5cd5d63ff0ad395989db6cf4841a64d5b545d1d)
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 ( --help | -h )\n"
21 ;
22 
23 // print_usage
24 static
25 void
26 print_usage(bool error = false)
27 {
28 	const char *name = "mkvirtualdrive";
29 	BPath path;
30 	if (path.SetTo(__libc_argv[0]) == B_OK)
31 		name = path.Leaf();
32 	fprintf((error ? stderr : stdout), kUsage, name, name, name);
33 }
34 
35 // parse_size
36 static
37 bool
38 parse_size(const char *str, off_t *_size)
39 {
40 	if (!str || !_size)
41 		return false;
42 	int32 len = strlen(str);
43 	char buffer[22];
44 	if (len == 0 || len + 1 > (int32)sizeof(buffer))
45 		return false;
46 	strcpy(buffer, str);
47 	// check the last char for one of the suffixes
48 	off_t suffix = 1;
49 	if (!isdigit(buffer[len - 1])) {
50 		switch (buffer[len - 1]) {
51 			case 'K':
52 				suffix = 1LL << 10;
53 				break;
54 			case 'M':
55 				suffix = 1LL << 20;
56 				break;
57 			case 'G':
58 				suffix = 1LL << 30;
59 				break;
60 			case 'T':
61 				suffix = 1LL << 40;
62 				break;
63 			case 'P':
64 				suffix = 1LL << 50;
65 				break;
66 			case 'E':
67 				suffix = 1LL << 60;
68 				break;
69 			default:
70 				return false;
71 		}
72 		buffer[len - 1] = '\0';
73 		len--;
74 		if (len == 0)
75 			return false;
76 	}
77 	// all other chars must be digits
78 	for (int i = 0; i < len; i++) {
79 		if (!isdigit(buffer[i]))
80 			return false;
81 	}
82 	off_t size = atoll(buffer);
83 	*_size = size * suffix;
84 	// check for overflow
85 	if (*_size / suffix != size)
86 		return false;
87 	return true;
88 }
89 
90 // install_file
91 status_t
92 install_file(const char *file, off_t size)
93 {
94 	// open the control device
95 	int fd = open(VIRTUAL_DRIVE_CONTROL_DEVICE, O_RDONLY);
96 	if (fd < 0) {
97 		fprintf(stderr, "Failed to open control device: %s\n",
98 				strerror(errno));
99 		return errno;
100 	}
101 	// set up the info
102 	virtual_drive_info info;
103 	strcpy(info.file_name, file);
104 	if (size >= 0) {
105 		info.use_geometry = true;
106 		// fill in the geometry
107 		// default to 512 bytes block size
108 		uint32 blockSize = 512;
109 		// Optimally we have only 1 block per sector and only one head.
110 		// Since we have only a uint32 for the cylinder count, this won't work
111 		// for files > 2TB. So, we set the head count to the minimally possible
112 		// value.
113 		off_t blocks = size / blockSize;
114 		uint32 heads = (blocks + ULONG_MAX - 1) / ULONG_MAX;
115 		if (heads == 0)
116 			heads = 1;
117 		info.geometry.bytes_per_sector = blockSize;
118 	    info.geometry.sectors_per_track = 1;
119 	    info.geometry.cylinder_count = blocks / heads;
120 	    info.geometry.head_count = heads;
121 	    info.geometry.device_type = B_DISK;	// TODO: Add a new constant.
122 	    info.geometry.removable = false;
123 	    info.geometry.read_only = false;
124 	    info.geometry.write_once = false;
125 	} else {
126 		info.use_geometry = false;
127 	}
128 	// issue the ioctl
129 	status_t error = B_OK;
130 	if (ioctl(fd, VIRTUAL_DRIVE_REGISTER_FILE, &info) != 0) {
131 		error = errno;
132 		fprintf(stderr, "Failed to install device: %s\n", strerror(error));
133 	} else {
134 		printf("File `%s' registered as device `%s'.\n", file,
135 			   info.device_name);
136 	}
137 	// close the control device
138 	close(fd);
139 	return error;
140 }
141 
142 // uninstall_file
143 status_t
144 uninstall_file(const char *device)
145 {
146 	// open the device
147 	int fd = open(device, O_RDONLY);
148 	if (fd < 0) {
149 		fprintf(stderr, "Failed to open device `%s': %s\n",
150 				device, strerror(errno));
151 		return errno;
152 	}
153 	// issue the ioctl
154 	status_t error = B_OK;
155 	if (ioctl(fd, VIRTUAL_DRIVE_UNREGISTER_FILE) != 0) {
156 		error = errno;
157 		fprintf(stderr, "Failed to uninstall device: %s\n", strerror(error));
158 	}
159 	// close the control device
160 	close(fd);
161 	return error;
162 }
163 
164 // main
165 int
166 main(int argc, const char **argv)
167 {
168 	status_t error = B_OK;
169 	int argIndex = 1;
170 	bool install = true;
171 	off_t size = -1;
172 	// parse options
173 	for (; error == B_OK && argIndex < argc
174 		   && argv[argIndex][0] == '-'; argIndex++) {
175 		const char *arg = argv[argIndex];
176 		if (arg[1] == '-') {
177 			// "--" option
178 			arg += 2;
179 			if (!strcmp(arg, "install")) {
180 				install = true;
181 			} else if (!strcmp(arg, "size")) {
182 				argIndex++;
183 				if (argIndex >= argc) {
184 					fprintf(stderr, "Parameter expected for `--%s'.\n", arg);
185 					print_usage(1);
186 					return 1;
187 				}
188 				if (!parse_size(argv[argIndex], &size)) {
189 					fprintf(stderr, "Parameter for `--%s' must be of the form "
190 							"`<number>[K|M|G|T|P|E]'.\n", arg);
191 					print_usage(1);
192 					return 1;
193 				}
194 			} else if (!strcmp(arg, "uninstall")) {
195 				install = false;
196 			} else if (!strcmp(arg, "help")) {
197 				print_usage();
198 				return 0;
199 			} else {
200 				fprintf(stderr, "Invalid option `-%s'.\n", arg);
201 				print_usage(true);
202 				return 1;
203 			}
204 		} else {
205 			// "-" options
206 			arg++;
207 			int32 count = strlen(arg);
208 			for (int i = 0; error == B_OK && i < count; i++) {
209 				switch (arg[i]) {
210 					case 'h':
211 						print_usage();
212 						return 0;
213 					default:
214 						fprintf(stderr, "Invalid option `-%c'.\n", arg[i]);
215 						print_usage(true);
216 						return 1;
217 				}
218 			}
219 		}
220 	}
221 	// parse rest (the file name)
222 	if (argIndex != argc - 1) {
223 		print_usage(true);
224 		return 1;
225 	}
226 	const char *file = argv[argIndex];
227 	// do the job
228 	if (install)
229 		error = install_file(file, size);
230 	else
231 		error = uninstall_file(file);
232 	return (error == B_OK ? 0 : 1);
233 }
234 
235