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