1 /* 2 * Copyright 2013, Ingo Weinhold, ingo_weinhold@gmx.de. 3 * Distributed under the terms of the MIT License. 4 */ 5 6 7 #include <dirent.h> 8 #include <errno.h> 9 #include <fcntl.h> 10 #include <getopt.h> 11 #include <stdio.h> 12 #include <stdlib.h> 13 #include <string.h> 14 15 #include <Entry.h> 16 #include <Path.h> 17 #include <String.h> 18 19 #include <AutoDeleter.h> 20 #include <AutoDeleterPosix.h> 21 #include <StringForSize.h> 22 #include <TextTable.h> 23 24 #include <file_systems/ram_disk/ram_disk.h> 25 26 27 extern const char* __progname; 28 static const char* kProgramName = __progname; 29 30 static const char* const kUsage = 31 "Usage: %s <command> [ <options> ]\n" 32 "Controls RAM disk devices.\n" 33 "\n" 34 "Commands:\n" 35 " create (-s <size> | <path>)\n" 36 " Creates a new RAM disk.\n" 37 " delete <id>\n" 38 " Deletes an existing RAM disk.\n" 39 " flush <id>\n" 40 " Writes modified data of an existing RAM disk back to its file.\n" 41 " help\n" 42 " Print this usage info.\n" 43 " list\n" 44 " List all RAM disks.\n" 45 ; 46 47 static const char* const kCreateUsage = 48 "Usage: %s %s (-s <size> | <path>)\n" 49 "Creates a new RAM disk device. If the <size> argument is specified, a\n" 50 "new zeroed RAM disk with that size (in bytes, suffixes 'k', 'm', 'g' are\n" 51 "interpreted as KiB, MiB, GiB) is registered.\n" 52 "Alternatively a file path can be specified. In that case the RAM disk \n" 53 "data are initially read from that file and at any later point the\n" 54 "modified RAM disk data can be written back to the same file upon request\n" 55 "(via the \"flush\" command). The size of the RAM disk is implied by that\n" 56 "of the file.\n" 57 ; 58 59 static const char* const kDeleteUsage = 60 "Usage: %s %s <id>\n" 61 "Deletes the existing RAM disk with ID <id>. All modified data will be\n" 62 "lost.\n" 63 ; 64 65 static const char* const kFlushUsage = 66 "Usage: %s %s <id>\n" 67 "Writes all modified data of the RAM disk with ID <id> back to the file\n" 68 "specified when the RAM disk was created. Fails, if the RAM disk had been\n" 69 "created without an associated file.\n" 70 ; 71 72 static const char* const kListUsage = 73 "Usage: %s %s\n" 74 "Lists all existing RAM disks.\n" 75 ; 76 77 static const char* const kRamDiskControlDevicePath 78 = "/dev/" RAM_DISK_CONTROL_DEVICE_NAME; 79 static const char* const kRamDiskRawDeviceBasePath 80 = "/dev/" RAM_DISK_RAW_DEVICE_BASE_NAME; 81 82 static const char* sCommandName = NULL; 83 static const char* sCommandUsage = NULL; 84 85 86 static void 87 print_usage_and_exit(bool error) 88 { 89 if (sCommandUsage != NULL) { 90 fprintf(error ? stderr : stdout, sCommandUsage, kProgramName, 91 sCommandName); 92 } else 93 fprintf(error ? stderr : stdout, kUsage, kProgramName); 94 exit(error ? 1 : 0); 95 } 96 97 98 static status_t 99 execute_control_device_ioctl(int operation, void* request) 100 { 101 // open the ram disk control device 102 int fd = open(kRamDiskControlDevicePath, O_RDONLY); 103 if (fd < 0) { 104 fprintf(stderr, "Error: Failed to open RAM disk control device \"%s\": " 105 "%s\n", kRamDiskControlDevicePath, strerror(errno)); 106 return errno; 107 } 108 FileDescriptorCloser fdCloser(fd); 109 110 // issue the request 111 if (ioctl(fd, operation, request) < 0) 112 return errno; 113 114 return B_OK; 115 } 116 117 118 static int 119 command_register(int argc, const char* const* argv) 120 { 121 sCommandUsage = kCreateUsage; 122 123 int64 deviceSize = -1; 124 125 while (true) { 126 static struct option sLongOptions[] = { 127 { "size", required_argument, 0, 's' }, 128 { "help", no_argument, 0, 'h' }, 129 { 0, 0, 0, 0 } 130 }; 131 132 opterr = 0; // don't print errors 133 int c = getopt_long(argc, (char**)argv, "+s:h", sLongOptions, NULL); 134 if (c == -1) 135 break; 136 137 switch (c) { 138 case 'h': 139 print_usage_and_exit(false); 140 break; 141 142 case 's': 143 { 144 const char* sizeString = optarg; 145 deviceSize = parse_size(sizeString); 146 147 if (deviceSize <= 0) { 148 fprintf(stderr, "Error: Invalid size argument: \"%s\"\n", 149 sizeString); 150 return 1; 151 } 152 153 // check maximum size 154 system_info info; 155 get_system_info(&info); 156 if (deviceSize / B_PAGE_SIZE > (int64)info.max_pages * 2 / 3) { 157 fprintf(stderr, "Error: Given RAM disk size too large.\n"); 158 return 1; 159 } 160 161 break; 162 } 163 164 default: 165 print_usage_and_exit(true); 166 break; 167 } 168 } 169 170 // The remaining optional argument is the file path. It may only be 171 // specified, if no size has been specified. 172 const char* path = optind < argc ? argv[optind++] : NULL; 173 if (optind < argc || (deviceSize >= 0) == (path != NULL)) 174 print_usage_and_exit(true); 175 176 // prepare the request 177 ram_disk_ioctl_register request; 178 request.size = (uint64)deviceSize; 179 request.path[0] = '\0'; 180 request.id = -1; 181 182 if (path != NULL) { 183 // verify the path 184 BEntry entry; 185 status_t error = entry.SetTo(path, true); 186 if (error == B_OK && !entry.Exists()) 187 error = B_ENTRY_NOT_FOUND; 188 if (error != B_OK) { 189 fprintf(stderr, "Error: Failed to resolve path \"%s\": %s\n", 190 path, strerror(error)); 191 return 1; 192 } 193 194 if (!entry.IsFile()) { 195 fprintf(stderr, "Error: \"%s\" is not a file.\n", path); 196 return 1; 197 } 198 199 BPath normalizedPath; 200 error = entry.GetPath(&normalizedPath); 201 if (error != B_OK) { 202 fprintf(stderr, "Error: Failed to normalize path \"%s\": %s\n", 203 path, strerror(error)); 204 return 1; 205 } 206 207 if (strlcpy(request.path, normalizedPath.Path(), sizeof(request.path)) 208 >= sizeof(request.path)) { 209 fprintf(stderr, "Error: Normalized path too long.\n"); 210 return 1; 211 } 212 } 213 214 status_t error = execute_control_device_ioctl(RAM_DISK_IOCTL_REGISTER, 215 &request); 216 if (error != B_OK) { 217 fprintf(stderr, "Error: Failed to create RAM disk device: %s\n", 218 strerror(error)); 219 return 1; 220 } 221 222 printf("RAM disk device created as \"%s/%" B_PRId32 "/raw\"\n", 223 kRamDiskRawDeviceBasePath, request.id); 224 return 0; 225 } 226 227 228 static int 229 command_unregister(int argc, const char* const* argv) 230 { 231 sCommandUsage = kDeleteUsage; 232 233 while (true) { 234 static struct option sLongOptions[] = { 235 { "help", no_argument, 0, 'h' }, 236 { 0, 0, 0, 0 } 237 }; 238 239 opterr = 0; // don't print errors 240 int c = getopt_long(argc, (char**)argv, "+h", sLongOptions, NULL); 241 if (c == -1) 242 break; 243 244 switch (c) { 245 case 'h': 246 print_usage_and_exit(false); 247 break; 248 249 default: 250 print_usage_and_exit(true); 251 break; 252 } 253 } 254 255 // The remaining argument is the device ID. 256 if (optind + 1 != argc) 257 print_usage_and_exit(true); 258 259 const char* idString = argv[optind++]; 260 char* end; 261 long long id = strtol(idString, &end, 0); 262 if (end == idString || *end != '\0' || id < 0 || id > INT32_MAX) { 263 fprintf(stderr, "Error: Invalid ID \"%s\".\n", idString); 264 return 1; 265 } 266 267 // check whether the raw device for that ID exists 268 BString path; 269 path.SetToFormat("%s/%s/raw", kRamDiskRawDeviceBasePath, idString); 270 struct stat st; 271 if (lstat(path, &st) != 0) { 272 fprintf(stderr, "Error: No RAM disk with ID %s.\n", idString); 273 return 1; 274 } 275 276 // issue the request 277 ram_disk_ioctl_unregister request; 278 request.id = (int32)id; 279 280 status_t error = execute_control_device_ioctl(RAM_DISK_IOCTL_UNREGISTER, 281 &request); 282 if (error != B_OK) { 283 fprintf(stderr, "Error: Failed to delete RAM disk device: %s\n", 284 strerror(error)); 285 return 1; 286 } 287 288 return 0; 289 } 290 291 292 static int 293 command_flush(int argc, const char* const* argv) 294 { 295 sCommandUsage = kFlushUsage; 296 297 while (true) { 298 static struct option sLongOptions[] = { 299 { "help", no_argument, 0, 'h' }, 300 { 0, 0, 0, 0 } 301 }; 302 303 opterr = 0; // don't print errors 304 int c = getopt_long(argc, (char**)argv, "+h", sLongOptions, NULL); 305 if (c == -1) 306 break; 307 308 switch (c) { 309 case 'h': 310 print_usage_and_exit(false); 311 break; 312 313 default: 314 print_usage_and_exit(true); 315 break; 316 } 317 } 318 319 // The remaining argument is the device ID. 320 if (optind + 1 != argc) 321 print_usage_and_exit(true); 322 323 const char* idString = argv[optind++]; 324 char* end; 325 long long id = strtol(idString, &end, 0); 326 if (end == idString || *end != '\0' || id < 0 || id > INT32_MAX) { 327 fprintf(stderr, "Error: Invalid ID \"%s\".\n", idString); 328 return 1; 329 } 330 331 // open the raw device 332 BString path; 333 path.SetToFormat("%s/%s/raw", kRamDiskRawDeviceBasePath, idString); 334 int fd = open(path, O_RDONLY); 335 if (fd < 0) { 336 fprintf(stderr, "Error: Failed to open RAM disk device \"%s\"\n", 337 path.String()); 338 return 1; 339 } 340 FileDescriptorCloser fdCloser(fd); 341 342 // issue the request 343 if (ioctl(fd, RAM_DISK_IOCTL_FLUSH, NULL) < 0) { 344 fprintf(stderr, "Error: Failed to flush RAM disk device: %s\n", 345 strerror(errno)); 346 return 1; 347 } 348 349 return 0; 350 } 351 352 353 static int 354 command_list(int argc, const char* const* argv) 355 { 356 sCommandUsage = kListUsage; 357 358 while (true) { 359 static struct option sLongOptions[] = { 360 { "help", no_argument, 0, 'h' }, 361 { 0, 0, 0, 0 } 362 }; 363 364 opterr = 0; // don't print errors 365 int c = getopt_long(argc, (char**)argv, "+h", sLongOptions, NULL); 366 if (c == -1) 367 break; 368 369 switch (c) { 370 case 'h': 371 print_usage_and_exit(false); 372 break; 373 374 default: 375 print_usage_and_exit(true); 376 break; 377 } 378 } 379 380 // There shouldn't be any remaining arguments. 381 if (optind != argc) 382 print_usage_and_exit(true); 383 384 // iterate through the RAM disk device directory and search for raw devices 385 DIR* dir = opendir(kRamDiskRawDeviceBasePath); 386 if (dir == NULL) { 387 fprintf(stderr, "Error: Failed to open RAM disk device directory: %s\n", 388 strerror(errno)); 389 return 1; 390 } 391 DirCloser dirCloser(dir); 392 393 TextTable table; 394 table.AddColumn("ID", B_ALIGN_RIGHT); 395 table.AddColumn("Size", B_ALIGN_RIGHT); 396 table.AddColumn("Associated file"); 397 398 while (dirent* entry = readdir(dir)) { 399 // check, if the entry name could be an ID 400 const char* idString = entry->d_name; 401 char* end; 402 long long id = strtol(idString, &end, 0); 403 if (end == idString || *end != '\0' || id < 0 || id > INT32_MAX) 404 continue; 405 406 // open the raw device 407 BString path; 408 path.SetToFormat("%s/%s/raw", kRamDiskRawDeviceBasePath, idString); 409 int fd = open(path, O_RDONLY); 410 if (fd < 0) 411 continue; 412 FileDescriptorCloser fdCloser(fd); 413 414 // issue the request 415 ram_disk_ioctl_info request; 416 if (ioctl(fd, RAM_DISK_IOCTL_INFO, &request, sizeof(request)) < 0) 417 continue; 418 419 int32 rowIndex = table.CountRows(); 420 table.SetTextAt(rowIndex, 0, BString() << request.id); 421 table.SetTextAt(rowIndex, 1, BString() << request.size); 422 table.SetTextAt(rowIndex, 2, request.path); 423 } 424 425 if (table.CountRows() > 0) 426 table.Print(INT32_MAX); 427 else 428 printf("No RAM disks.\n"); 429 430 return 0; 431 } 432 433 434 int 435 main(int argc, const char* const* argv) 436 { 437 if (argc < 2) 438 print_usage_and_exit(true); 439 440 if (strcmp(argv[1], "help") == 0 || strcmp(argv[1], "--help") == 0 441 || strcmp(argv[1], "-h") == 0) { 442 print_usage_and_exit(false); 443 } 444 445 sCommandName = argv[1]; 446 447 if (strcmp(sCommandName, "create") == 0) 448 return command_register(argc - 1, argv + 1); 449 if (strcmp(sCommandName, "delete") == 0) 450 return command_unregister(argc - 1, argv + 1); 451 if (strcmp(sCommandName, "flush") == 0) 452 return command_flush(argc - 1, argv + 1); 453 if (strcmp(sCommandName, "list") == 0) 454 return command_list(argc - 1, argv + 1); 455 456 print_usage_and_exit(true); 457 } 458