1 /* 2 * Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de. 3 * Distributed under the terms of the MIT License. 4 */ 5 6 7 #include <errno.h> 8 #include <getopt.h> 9 #include <stdio.h> 10 #include <stdlib.h> 11 #include <string.h> 12 #include <sys/stat.h> 13 #include <unistd.h> 14 15 #include <DiskDevice.h> 16 #include <DiskDeviceRoster.h> 17 #include <Path.h> 18 19 20 extern const char* __progname; 21 const char* kCommandName = __progname; 22 23 24 static const char* kUsage = 25 "Usage: %s register <file>\n" 26 " %s unregister ( <file> | <device path> | <device ID> )\n" 27 " %s list\n" 28 "Registers a regular file as disk device, unregisters an already " 29 "registered\n" 30 "one, or lists all file disk devices.\n" 31 "\n" 32 "Options:\n" 33 " -h, --help - Print this usage info.\n" 34 ; 35 36 37 static void 38 print_usage_and_exit(bool error) 39 { 40 fprintf(error ? stderr : stdout, kUsage, kCommandName, kCommandName, 41 kCommandName); 42 exit(error ? 1 : 0); 43 } 44 45 46 static status_t 47 list_file_disk_devices() 48 { 49 printf(" ID File Device\n"); 50 printf("------------------------------------------------------------------" 51 "-------------\n"); 52 53 BDiskDeviceRoster roster; 54 BDiskDevice device; 55 while (roster.GetNextDevice(&device) == B_OK) { 56 if (!device.IsFile()) 57 continue; 58 59 // ID 60 printf("%6ld ", device.ID()); 61 62 // file path 63 BPath path; 64 printf("%-35s", 65 device.GetFilePath(&path) == B_OK ? path.Path() : "???"); 66 67 // device path 68 printf("%s", device.GetPath(&path) == B_OK ? path.Path() : "???"); 69 printf("\n"); 70 } 71 72 return B_OK; 73 } 74 75 76 static status_t 77 register_file_disk_device(const char* fileName) 78 { 79 // stat() the file to verify that it's a regular file 80 struct stat st; 81 if (lstat(fileName, &st) != 0) { 82 status_t error = errno; 83 fprintf(stderr, "Error: Failed to stat() \"%s\": %s\n", fileName, 84 strerror(error)); 85 return error; 86 } 87 88 if (!S_ISREG(st.st_mode)) { 89 fprintf(stderr, "Error: \"%s\" is not a regular file.\n", fileName); 90 return B_BAD_VALUE; 91 } 92 93 // register the file 94 BDiskDeviceRoster roster; 95 partition_id id = roster.RegisterFileDevice(fileName); 96 if (id < 0) { 97 fprintf(stderr, "Error: Failed to register file disk device: %s\n", 98 strerror(id)); 99 return id; 100 } 101 102 // print the success message (get the device path) 103 BDiskDevice device; 104 BPath path; 105 if (roster.GetDeviceWithID(id, &device) == B_OK 106 && device.GetPath(&path) == B_OK) { 107 printf("Registered file as disk device \"%s\" with ID %ld.\n", 108 path.Path(), id); 109 } else { 110 printf("Registered file as disk device with ID %ld, but failed to " 111 "get the device path.\n", id); 112 } 113 114 return B_OK; 115 } 116 117 118 static status_t 119 unregister_file_disk_device(const char* fileNameOrID) 120 { 121 // try to parse the parameter as ID 122 char* numberEnd; 123 partition_id id = strtol(fileNameOrID, &numberEnd, 0); 124 BDiskDeviceRoster roster; 125 if (id >= 0 && numberEnd != fileNameOrID && *numberEnd == '\0') { 126 BDiskDevice device; 127 if (roster.GetDeviceWithID(id, &device) == B_OK && device.IsFile()) { 128 status_t error = roster.UnregisterFileDevice(id); 129 if (error != B_OK) { 130 fprintf(stderr, "Error: Failed to unregister file disk device " 131 "with ID %ld: %s\n", id, strerror(error)); 132 return error; 133 } 134 135 printf("Unregistered file disk device with ID %ld.\n", id); 136 return B_OK; 137 } else { 138 fprintf(stderr, "No file disk device with ID %ld, trying file " 139 "\"%s\"\n", id, fileNameOrID); 140 } 141 } 142 143 // the parameter must be a file name -- stat() it 144 struct stat st; 145 if (lstat(fileNameOrID, &st) != 0) { 146 status_t error = errno; 147 fprintf(stderr, "Error: Failed to stat() \"%s\": %s\n", fileNameOrID, 148 strerror(error)); 149 return error; 150 } 151 152 // remember the volume and node ID, so we can identify the file 153 // NOTE: There's a race condition -- we would need to open the file and 154 // keep it open to avoid it. 155 dev_t volumeID = st.st_dev; 156 ino_t nodeID = st.st_ino; 157 158 // iterate through all file disk devices and try to find a match 159 BDiskDevice device; 160 while (roster.GetNextDevice(&device) == B_OK) { 161 if (!device.IsFile()) 162 continue; 163 164 // get file path and stat it, same for the device path 165 BPath path; 166 bool isFilePath = true; 167 if ((device.GetFilePath(&path) == B_OK && lstat(path.Path(), &st) == 0 168 && volumeID == st.st_dev && nodeID == st.st_ino) 169 || (isFilePath = false, false) 170 || (device.GetPath(&path) == B_OK && lstat(path.Path(), &st) == 0 171 && volumeID == st.st_dev && nodeID == st.st_ino)) { 172 status_t error = roster.UnregisterFileDevice(device.ID()); 173 if (error != B_OK) { 174 fprintf(stderr, "Error: Failed to unregister file disk device" 175 "%s \"%s\" (ID: %ld): %s\n", isFilePath ? " for file" : "", 176 fileNameOrID, device.ID(), strerror(error)); 177 return error; 178 } 179 180 printf("Unregistered file disk device%s \"%s\" (ID: %ld)\n", 181 isFilePath ? " for file" : "", fileNameOrID, device.ID()); 182 183 return B_OK; 184 } 185 } 186 187 fprintf(stderr, "Error: \"%s\" does not refer to a file disk device.\n", 188 fileNameOrID); 189 return B_BAD_VALUE; 190 } 191 192 193 int 194 main(int argc, const char* const* argv) 195 { 196 while (true) { 197 static struct option sLongOptions[] = { 198 { "help", no_argument, 0, 'h' }, 199 { 0, 0, 0, 0 } 200 }; 201 202 opterr = 0; // don't print errors 203 int c = getopt_long(argc, (char**)argv, "+h", sLongOptions, NULL); 204 if (c == -1) 205 break; 206 207 switch (c) { 208 case 'h': 209 print_usage_and_exit(false); 210 break; 211 212 default: 213 print_usage_and_exit(true); 214 break; 215 } 216 } 217 218 // Of the remaining arguments the next one should be the command. 219 if (optind >= argc) 220 print_usage_and_exit(true); 221 222 status_t error = B_OK; 223 const char* command = argv[optind++]; 224 225 if (strcmp(command, "list") == 0) { 226 if (optind < argc) 227 print_usage_and_exit(true); 228 229 list_file_disk_devices(); 230 } else if (strcmp(command, "register") == 0) { 231 if (optind + 1 != argc) 232 print_usage_and_exit(true); 233 234 const char* fileName = argv[optind++]; 235 register_file_disk_device(fileName); 236 } else if (strcmp(command, "unregister") == 0) { 237 if (optind + 1 != argc) 238 print_usage_and_exit(true); 239 240 const char* fileName = argv[optind++]; 241 unregister_file_disk_device(fileName); 242 } else 243 print_usage_and_exit(true); 244 245 return error == B_OK ? 0 : 1; 246 } 247