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("%6" B_PRId32 " ", 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 %" B_PRId32 ".\n", 108 path.Path(), id); 109 } else { 110 printf("Registered file as disk device with ID %" B_PRId32 ", " 111 "but failed to 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 %" B_PRId32 ": %s\n", id, strerror(error)); 132 return error; 133 } 134 135 printf("Unregistered file disk device with ID %" B_PRId32 ".\n", 136 id); 137 return B_OK; 138 } else { 139 fprintf(stderr, "No file disk device with ID %" B_PRId32 "," 140 "trying file \"%s\"\n", id, fileNameOrID); 141 } 142 } 143 144 // the parameter must be a file name -- stat() it 145 struct stat st; 146 if (lstat(fileNameOrID, &st) != 0) { 147 status_t error = errno; 148 fprintf(stderr, "Error: Failed to stat() \"%s\": %s\n", fileNameOrID, 149 strerror(error)); 150 return error; 151 } 152 153 // remember the volume and node ID, so we can identify the file 154 // NOTE: There's a race condition -- we would need to open the file and 155 // keep it open to avoid it. 156 dev_t volumeID = st.st_dev; 157 ino_t nodeID = st.st_ino; 158 159 // iterate through all file disk devices and try to find a match 160 BDiskDevice device; 161 while (roster.GetNextDevice(&device) == B_OK) { 162 if (!device.IsFile()) 163 continue; 164 165 // get file path and stat it, same for the device path 166 BPath path; 167 bool isFilePath = true; 168 if ((device.GetFilePath(&path) == B_OK && lstat(path.Path(), &st) == 0 169 && volumeID == st.st_dev && nodeID == st.st_ino) 170 || (isFilePath = false, false) 171 || (device.GetPath(&path) == B_OK && lstat(path.Path(), &st) == 0 172 && volumeID == st.st_dev && nodeID == st.st_ino)) { 173 status_t error = roster.UnregisterFileDevice(device.ID()); 174 if (error != B_OK) { 175 fprintf(stderr, "Error: Failed to unregister file disk device" 176 "%s \"%s\" (ID: %" B_PRId32 "): %s\n", 177 isFilePath ? " for file" : "", fileNameOrID, device.ID(), 178 strerror(error)); 179 return error; 180 } 181 182 printf("Unregistered file disk device%s \"%s\" " 183 "(ID: %" B_PRId32 ")\n", isFilePath ? " for file" : "", 184 fileNameOrID, device.ID()); 185 186 return B_OK; 187 } 188 } 189 190 fprintf(stderr, "Error: \"%s\" does not refer to a file disk device.\n", 191 fileNameOrID); 192 return B_BAD_VALUE; 193 } 194 195 196 int 197 main(int argc, const char* const* argv) 198 { 199 while (true) { 200 static struct option sLongOptions[] = { 201 { "help", no_argument, 0, 'h' }, 202 { 0, 0, 0, 0 } 203 }; 204 205 opterr = 0; // don't print errors 206 int c = getopt_long(argc, (char**)argv, "+h", sLongOptions, NULL); 207 if (c == -1) 208 break; 209 210 switch (c) { 211 case 'h': 212 print_usage_and_exit(false); 213 break; 214 215 default: 216 print_usage_and_exit(true); 217 break; 218 } 219 } 220 221 // Of the remaining arguments the next one should be the command. 222 if (optind >= argc) 223 print_usage_and_exit(true); 224 225 status_t error = B_OK; 226 const char* command = argv[optind++]; 227 228 if (strcmp(command, "list") == 0) { 229 if (optind < argc) 230 print_usage_and_exit(true); 231 232 list_file_disk_devices(); 233 } else if (strcmp(command, "register") == 0) { 234 if (optind + 1 != argc) 235 print_usage_and_exit(true); 236 237 const char* fileName = argv[optind++]; 238 register_file_disk_device(fileName); 239 } else if (strcmp(command, "unregister") == 0) { 240 if (optind + 1 != argc) 241 print_usage_and_exit(true); 242 243 const char* fileName = argv[optind++]; 244 unregister_file_disk_device(fileName); 245 } else 246 print_usage_and_exit(true); 247 248 return error == B_OK ? 0 : 1; 249 } 250