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 FileDescriptorCloser fd(open(kRamDiskControlDevicePath, O_RDONLY)); 103 if (!fd.IsSet()) { 104 fprintf(stderr, "Error: Failed to open RAM disk control device \"%s\": " 105 "%s\n", kRamDiskControlDevicePath, strerror(errno)); 106 return errno; 107 } 108 109 // issue the request 110 if (ioctl(fd.Get(), operation, request) < 0) 111 return errno; 112 113 return B_OK; 114 } 115 116 117 static int 118 command_register(int argc, const char* const* argv) 119 { 120 sCommandUsage = kCreateUsage; 121 122 int64 deviceSize = -1; 123 124 while (true) { 125 static struct option sLongOptions[] = { 126 { "size", required_argument, 0, 's' }, 127 { "help", no_argument, 0, 'h' }, 128 { 0, 0, 0, 0 } 129 }; 130 131 opterr = 0; // don't print errors 132 int c = getopt_long(argc, (char**)argv, "+s:h", sLongOptions, NULL); 133 if (c == -1) 134 break; 135 136 switch (c) { 137 case 'h': 138 print_usage_and_exit(false); 139 break; 140 141 case 's': 142 { 143 const char* sizeString = optarg; 144 deviceSize = parse_size(sizeString); 145 146 if (deviceSize <= 0) { 147 fprintf(stderr, "Error: Invalid size argument: \"%s\"\n", 148 sizeString); 149 return 1; 150 } 151 152 // check maximum size 153 system_info info; 154 get_system_info(&info); 155 if (deviceSize / B_PAGE_SIZE > (int64)info.max_pages * 2 / 3) { 156 fprintf(stderr, "Error: Given RAM disk size too large.\n"); 157 return 1; 158 } 159 160 break; 161 } 162 163 default: 164 print_usage_and_exit(true); 165 break; 166 } 167 } 168 169 // The remaining optional argument is the file path. It may only be 170 // specified, if no size has been specified. 171 const char* path = optind < argc ? argv[optind++] : NULL; 172 if (optind < argc || (deviceSize >= 0) == (path != NULL)) 173 print_usage_and_exit(true); 174 175 // prepare the request 176 ram_disk_ioctl_register request; 177 request.size = (uint64)deviceSize; 178 request.path[0] = '\0'; 179 request.id = -1; 180 181 if (path != NULL) { 182 // verify the path 183 BEntry entry; 184 status_t error = entry.SetTo(path, true); 185 if (error == B_OK && !entry.Exists()) 186 error = B_ENTRY_NOT_FOUND; 187 if (error != B_OK) { 188 fprintf(stderr, "Error: Failed to resolve path \"%s\": %s\n", 189 path, strerror(error)); 190 return 1; 191 } 192 193 if (!entry.IsFile()) { 194 fprintf(stderr, "Error: \"%s\" is not a file.\n", path); 195 return 1; 196 } 197 198 BPath normalizedPath; 199 error = entry.GetPath(&normalizedPath); 200 if (error != B_OK) { 201 fprintf(stderr, "Error: Failed to normalize path \"%s\": %s\n", 202 path, strerror(error)); 203 return 1; 204 } 205 206 if (strlcpy(request.path, normalizedPath.Path(), sizeof(request.path)) 207 >= sizeof(request.path)) { 208 fprintf(stderr, "Error: Normalized path too long.\n"); 209 return 1; 210 } 211 } 212 213 status_t error = execute_control_device_ioctl(RAM_DISK_IOCTL_REGISTER, 214 &request); 215 if (error != B_OK) { 216 fprintf(stderr, "Error: Failed to create RAM disk device: %s\n", 217 strerror(error)); 218 return 1; 219 } 220 221 printf("RAM disk device created as \"%s/%" B_PRId32 "/raw\"\n", 222 kRamDiskRawDeviceBasePath, request.id); 223 return 0; 224 } 225 226 227 static int 228 command_unregister(int argc, const char* const* argv) 229 { 230 sCommandUsage = kDeleteUsage; 231 232 while (true) { 233 static struct option sLongOptions[] = { 234 { "help", no_argument, 0, 'h' }, 235 { 0, 0, 0, 0 } 236 }; 237 238 opterr = 0; // don't print errors 239 int c = getopt_long(argc, (char**)argv, "+h", sLongOptions, NULL); 240 if (c == -1) 241 break; 242 243 switch (c) { 244 case 'h': 245 print_usage_and_exit(false); 246 break; 247 248 default: 249 print_usage_and_exit(true); 250 break; 251 } 252 } 253 254 // The remaining argument is the device ID. 255 if (optind + 1 != argc) 256 print_usage_and_exit(true); 257 258 const char* idString = argv[optind++]; 259 char* end; 260 long long id = strtol(idString, &end, 0); 261 if (end == idString || *end != '\0' || id < 0 || id > INT32_MAX) { 262 fprintf(stderr, "Error: Invalid ID \"%s\".\n", idString); 263 return 1; 264 } 265 266 // check whether the raw device for that ID exists 267 BString path; 268 path.SetToFormat("%s/%s/raw", kRamDiskRawDeviceBasePath, idString); 269 struct stat st; 270 if (lstat(path, &st) != 0) { 271 fprintf(stderr, "Error: No RAM disk with ID %s.\n", idString); 272 return 1; 273 } 274 275 // issue the request 276 ram_disk_ioctl_unregister request; 277 request.id = (int32)id; 278 279 status_t error = execute_control_device_ioctl(RAM_DISK_IOCTL_UNREGISTER, 280 &request); 281 if (error != B_OK) { 282 fprintf(stderr, "Error: Failed to delete RAM disk device: %s\n", 283 strerror(error)); 284 return 1; 285 } 286 287 return 0; 288 } 289 290 291 static int 292 command_flush(int argc, const char* const* argv) 293 { 294 sCommandUsage = kFlushUsage; 295 296 while (true) { 297 static struct option sLongOptions[] = { 298 { "help", no_argument, 0, 'h' }, 299 { 0, 0, 0, 0 } 300 }; 301 302 opterr = 0; // don't print errors 303 int c = getopt_long(argc, (char**)argv, "+h", sLongOptions, NULL); 304 if (c == -1) 305 break; 306 307 switch (c) { 308 case 'h': 309 print_usage_and_exit(false); 310 break; 311 312 default: 313 print_usage_and_exit(true); 314 break; 315 } 316 } 317 318 // The remaining argument is the device ID. 319 if (optind + 1 != argc) 320 print_usage_and_exit(true); 321 322 const char* idString = argv[optind++]; 323 char* end; 324 long long id = strtol(idString, &end, 0); 325 if (end == idString || *end != '\0' || id < 0 || id > INT32_MAX) { 326 fprintf(stderr, "Error: Invalid ID \"%s\".\n", idString); 327 return 1; 328 } 329 330 // open the raw device 331 BString path; 332 path.SetToFormat("%s/%s/raw", kRamDiskRawDeviceBasePath, idString); 333 FileDescriptorCloser fd(open(path, O_RDONLY)); 334 if (!fd.IsSet()) { 335 fprintf(stderr, "Error: Failed to open RAM disk device \"%s\"\n", 336 path.String()); 337 return 1; 338 } 339 340 // issue the request 341 if (ioctl(fd.Get(), RAM_DISK_IOCTL_FLUSH, NULL) < 0) { 342 fprintf(stderr, "Error: Failed to flush RAM disk device: %s\n", 343 strerror(errno)); 344 return 1; 345 } 346 347 return 0; 348 } 349 350 351 static int 352 command_list(int argc, const char* const* argv) 353 { 354 sCommandUsage = kListUsage; 355 356 while (true) { 357 static struct option sLongOptions[] = { 358 { "help", no_argument, 0, 'h' }, 359 { 0, 0, 0, 0 } 360 }; 361 362 opterr = 0; // don't print errors 363 int c = getopt_long(argc, (char**)argv, "+h", sLongOptions, NULL); 364 if (c == -1) 365 break; 366 367 switch (c) { 368 case 'h': 369 print_usage_and_exit(false); 370 break; 371 372 default: 373 print_usage_and_exit(true); 374 break; 375 } 376 } 377 378 // There shouldn't be any remaining arguments. 379 if (optind != argc) 380 print_usage_and_exit(true); 381 382 // iterate through the RAM disk device directory and search for raw devices 383 DirCloser dir(opendir(kRamDiskRawDeviceBasePath)); 384 if (!dir.IsSet()) { 385 fprintf(stderr, "Error: Failed to open RAM disk device directory: %s\n", 386 strerror(errno)); 387 return 1; 388 } 389 390 TextTable table; 391 table.AddColumn("ID", B_ALIGN_RIGHT); 392 table.AddColumn("Size", B_ALIGN_RIGHT); 393 table.AddColumn("Associated file"); 394 395 while (dirent* entry = readdir(dir.Get())) { 396 // check, if the entry name could be an ID 397 const char* idString = entry->d_name; 398 char* end; 399 long long id = strtol(idString, &end, 0); 400 if (end == idString || *end != '\0' || id < 0 || id > INT32_MAX) 401 continue; 402 403 // open the raw device 404 BString path; 405 path.SetToFormat("%s/%s/raw", kRamDiskRawDeviceBasePath, idString); 406 FileDescriptorCloser fd(open(path, O_RDONLY)); 407 if (!fd.IsSet()) 408 continue; 409 410 // issue the request 411 ram_disk_ioctl_info request; 412 if (ioctl(fd.Get(), RAM_DISK_IOCTL_INFO, &request, sizeof(request)) 413 < 0) 414 continue; 415 416 int32 rowIndex = table.CountRows(); 417 table.SetTextAt(rowIndex, 0, BString() << request.id); 418 table.SetTextAt(rowIndex, 1, BString() << request.size); 419 table.SetTextAt(rowIndex, 2, request.path); 420 } 421 422 if (table.CountRows() > 0) 423 table.Print(INT32_MAX); 424 else 425 printf("No RAM disks.\n"); 426 427 return 0; 428 } 429 430 431 int 432 main(int argc, const char* const* argv) 433 { 434 if (argc < 2) 435 print_usage_and_exit(true); 436 437 if (strcmp(argv[1], "help") == 0 || strcmp(argv[1], "--help") == 0 438 || strcmp(argv[1], "-h") == 0) { 439 print_usage_and_exit(false); 440 } 441 442 sCommandName = argv[1]; 443 444 if (strcmp(sCommandName, "create") == 0) 445 return command_register(argc - 1, argv + 1); 446 if (strcmp(sCommandName, "delete") == 0) 447 return command_unregister(argc - 1, argv + 1); 448 if (strcmp(sCommandName, "flush") == 0) 449 return command_flush(argc - 1, argv + 1); 450 if (strcmp(sCommandName, "list") == 0) 451 return command_list(argc - 1, argv + 1); 452 453 print_usage_and_exit(true); 454 } 455