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