16cc1f015SIngo Weinhold /*
26cc1f015SIngo Weinhold * Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de.
36cc1f015SIngo Weinhold * Distributed under the terms of the MIT License.
46cc1f015SIngo Weinhold */
56cc1f015SIngo Weinhold
66cc1f015SIngo Weinhold
76cc1f015SIngo Weinhold #include <errno.h>
86cc1f015SIngo Weinhold #include <getopt.h>
96cc1f015SIngo Weinhold #include <stdio.h>
106cc1f015SIngo Weinhold #include <stdlib.h>
116cc1f015SIngo Weinhold #include <string.h>
126cc1f015SIngo Weinhold #include <sys/stat.h>
136cc1f015SIngo Weinhold #include <unistd.h>
146cc1f015SIngo Weinhold
156cc1f015SIngo Weinhold #include <DiskDevice.h>
166cc1f015SIngo Weinhold #include <DiskDeviceRoster.h>
176cc1f015SIngo Weinhold #include <Path.h>
186cc1f015SIngo Weinhold
196cc1f015SIngo Weinhold
206cc1f015SIngo Weinhold extern const char* __progname;
216cc1f015SIngo Weinhold const char* kCommandName = __progname;
226cc1f015SIngo Weinhold
236cc1f015SIngo Weinhold
246cc1f015SIngo Weinhold static const char* kUsage =
256cc1f015SIngo Weinhold "Usage: %s register <file>\n"
266cc1f015SIngo Weinhold " %s unregister ( <file> | <device path> | <device ID> )\n"
276cc1f015SIngo Weinhold " %s list\n"
286cc1f015SIngo Weinhold "Registers a regular file as disk device, unregisters an already "
296cc1f015SIngo Weinhold "registered\n"
306cc1f015SIngo Weinhold "one, or lists all file disk devices.\n"
316cc1f015SIngo Weinhold "\n"
326cc1f015SIngo Weinhold "Options:\n"
336cc1f015SIngo Weinhold " -h, --help - Print this usage info.\n"
346cc1f015SIngo Weinhold ;
356cc1f015SIngo Weinhold
366cc1f015SIngo Weinhold
376cc1f015SIngo Weinhold static void
print_usage_and_exit(bool error)386cc1f015SIngo Weinhold print_usage_and_exit(bool error)
396cc1f015SIngo Weinhold {
406cc1f015SIngo Weinhold fprintf(error ? stderr : stdout, kUsage, kCommandName, kCommandName,
416cc1f015SIngo Weinhold kCommandName);
426cc1f015SIngo Weinhold exit(error ? 1 : 0);
436cc1f015SIngo Weinhold }
446cc1f015SIngo Weinhold
456cc1f015SIngo Weinhold
466cc1f015SIngo Weinhold static status_t
list_file_disk_devices()476cc1f015SIngo Weinhold list_file_disk_devices()
486cc1f015SIngo Weinhold {
496cc1f015SIngo Weinhold printf(" ID File Device\n");
506cc1f015SIngo Weinhold printf("------------------------------------------------------------------"
516cc1f015SIngo Weinhold "-------------\n");
526cc1f015SIngo Weinhold
536cc1f015SIngo Weinhold BDiskDeviceRoster roster;
546cc1f015SIngo Weinhold BDiskDevice device;
556cc1f015SIngo Weinhold while (roster.GetNextDevice(&device) == B_OK) {
566cc1f015SIngo Weinhold if (!device.IsFile())
576cc1f015SIngo Weinhold continue;
586cc1f015SIngo Weinhold
596cc1f015SIngo Weinhold // ID
60*f783542cSAlexander von Gluck IV printf("%6" B_PRId32 " ", device.ID());
616cc1f015SIngo Weinhold
626cc1f015SIngo Weinhold // file path
636cc1f015SIngo Weinhold BPath path;
646cc1f015SIngo Weinhold printf("%-35s",
656cc1f015SIngo Weinhold device.GetFilePath(&path) == B_OK ? path.Path() : "???");
666cc1f015SIngo Weinhold
676cc1f015SIngo Weinhold // device path
686cc1f015SIngo Weinhold printf("%s", device.GetPath(&path) == B_OK ? path.Path() : "???");
696cc1f015SIngo Weinhold printf("\n");
706cc1f015SIngo Weinhold }
716cc1f015SIngo Weinhold
726cc1f015SIngo Weinhold return B_OK;
736cc1f015SIngo Weinhold }
746cc1f015SIngo Weinhold
756cc1f015SIngo Weinhold
766cc1f015SIngo Weinhold static status_t
register_file_disk_device(const char * fileName)776cc1f015SIngo Weinhold register_file_disk_device(const char* fileName)
786cc1f015SIngo Weinhold {
796cc1f015SIngo Weinhold // stat() the file to verify that it's a regular file
806cc1f015SIngo Weinhold struct stat st;
816cc1f015SIngo Weinhold if (lstat(fileName, &st) != 0) {
826cc1f015SIngo Weinhold status_t error = errno;
836cc1f015SIngo Weinhold fprintf(stderr, "Error: Failed to stat() \"%s\": %s\n", fileName,
846cc1f015SIngo Weinhold strerror(error));
856cc1f015SIngo Weinhold return error;
866cc1f015SIngo Weinhold }
876cc1f015SIngo Weinhold
886cc1f015SIngo Weinhold if (!S_ISREG(st.st_mode)) {
896cc1f015SIngo Weinhold fprintf(stderr, "Error: \"%s\" is not a regular file.\n", fileName);
906cc1f015SIngo Weinhold return B_BAD_VALUE;
916cc1f015SIngo Weinhold }
926cc1f015SIngo Weinhold
936cc1f015SIngo Weinhold // register the file
946cc1f015SIngo Weinhold BDiskDeviceRoster roster;
956cc1f015SIngo Weinhold partition_id id = roster.RegisterFileDevice(fileName);
966cc1f015SIngo Weinhold if (id < 0) {
976cc1f015SIngo Weinhold fprintf(stderr, "Error: Failed to register file disk device: %s\n",
986cc1f015SIngo Weinhold strerror(id));
996cc1f015SIngo Weinhold return id;
1006cc1f015SIngo Weinhold }
1016cc1f015SIngo Weinhold
1026cc1f015SIngo Weinhold // print the success message (get the device path)
1036cc1f015SIngo Weinhold BDiskDevice device;
1046cc1f015SIngo Weinhold BPath path;
1056cc1f015SIngo Weinhold if (roster.GetDeviceWithID(id, &device) == B_OK
1066cc1f015SIngo Weinhold && device.GetPath(&path) == B_OK) {
107*f783542cSAlexander von Gluck IV printf("Registered file as disk device \"%s\" with ID %" B_PRId32 ".\n",
1086cc1f015SIngo Weinhold path.Path(), id);
1096cc1f015SIngo Weinhold } else {
110*f783542cSAlexander von Gluck IV printf("Registered file as disk device with ID %" B_PRId32 ", "
111*f783542cSAlexander von Gluck IV "but failed to get the device path.\n", id);
1126cc1f015SIngo Weinhold }
1136cc1f015SIngo Weinhold
1146cc1f015SIngo Weinhold return B_OK;
1156cc1f015SIngo Weinhold }
1166cc1f015SIngo Weinhold
1176cc1f015SIngo Weinhold
1186cc1f015SIngo Weinhold static status_t
unregister_file_disk_device(const char * fileNameOrID)1196cc1f015SIngo Weinhold unregister_file_disk_device(const char* fileNameOrID)
1206cc1f015SIngo Weinhold {
1216cc1f015SIngo Weinhold // try to parse the parameter as ID
1226cc1f015SIngo Weinhold char* numberEnd;
1236cc1f015SIngo Weinhold partition_id id = strtol(fileNameOrID, &numberEnd, 0);
1246cc1f015SIngo Weinhold BDiskDeviceRoster roster;
1256cc1f015SIngo Weinhold if (id >= 0 && numberEnd != fileNameOrID && *numberEnd == '\0') {
1266cc1f015SIngo Weinhold BDiskDevice device;
1276cc1f015SIngo Weinhold if (roster.GetDeviceWithID(id, &device) == B_OK && device.IsFile()) {
1286cc1f015SIngo Weinhold status_t error = roster.UnregisterFileDevice(id);
1296cc1f015SIngo Weinhold if (error != B_OK) {
1306cc1f015SIngo Weinhold fprintf(stderr, "Error: Failed to unregister file disk device "
131*f783542cSAlexander von Gluck IV "with ID %" B_PRId32 ": %s\n", id, strerror(error));
1326cc1f015SIngo Weinhold return error;
1336cc1f015SIngo Weinhold }
1346cc1f015SIngo Weinhold
135*f783542cSAlexander von Gluck IV printf("Unregistered file disk device with ID %" B_PRId32 ".\n",
136*f783542cSAlexander von Gluck IV id);
1376cc1f015SIngo Weinhold return B_OK;
1386cc1f015SIngo Weinhold } else {
139*f783542cSAlexander von Gluck IV fprintf(stderr, "No file disk device with ID %" B_PRId32 ","
140*f783542cSAlexander von Gluck IV "trying file \"%s\"\n", id, fileNameOrID);
1416cc1f015SIngo Weinhold }
1426cc1f015SIngo Weinhold }
1436cc1f015SIngo Weinhold
1446cc1f015SIngo Weinhold // the parameter must be a file name -- stat() it
1456cc1f015SIngo Weinhold struct stat st;
1466cc1f015SIngo Weinhold if (lstat(fileNameOrID, &st) != 0) {
1476cc1f015SIngo Weinhold status_t error = errno;
1486cc1f015SIngo Weinhold fprintf(stderr, "Error: Failed to stat() \"%s\": %s\n", fileNameOrID,
1496cc1f015SIngo Weinhold strerror(error));
1506cc1f015SIngo Weinhold return error;
1516cc1f015SIngo Weinhold }
1526cc1f015SIngo Weinhold
1536cc1f015SIngo Weinhold // remember the volume and node ID, so we can identify the file
1546cc1f015SIngo Weinhold // NOTE: There's a race condition -- we would need to open the file and
1556cc1f015SIngo Weinhold // keep it open to avoid it.
1566cc1f015SIngo Weinhold dev_t volumeID = st.st_dev;
1576cc1f015SIngo Weinhold ino_t nodeID = st.st_ino;
1586cc1f015SIngo Weinhold
1596cc1f015SIngo Weinhold // iterate through all file disk devices and try to find a match
1606cc1f015SIngo Weinhold BDiskDevice device;
1616cc1f015SIngo Weinhold while (roster.GetNextDevice(&device) == B_OK) {
1626cc1f015SIngo Weinhold if (!device.IsFile())
1636cc1f015SIngo Weinhold continue;
1646cc1f015SIngo Weinhold
1656cc1f015SIngo Weinhold // get file path and stat it, same for the device path
1666cc1f015SIngo Weinhold BPath path;
1676cc1f015SIngo Weinhold bool isFilePath = true;
1686cc1f015SIngo Weinhold if ((device.GetFilePath(&path) == B_OK && lstat(path.Path(), &st) == 0
1696cc1f015SIngo Weinhold && volumeID == st.st_dev && nodeID == st.st_ino)
1706cc1f015SIngo Weinhold || (isFilePath = false, false)
1716cc1f015SIngo Weinhold || (device.GetPath(&path) == B_OK && lstat(path.Path(), &st) == 0
1726cc1f015SIngo Weinhold && volumeID == st.st_dev && nodeID == st.st_ino)) {
1736cc1f015SIngo Weinhold status_t error = roster.UnregisterFileDevice(device.ID());
1746cc1f015SIngo Weinhold if (error != B_OK) {
1756cc1f015SIngo Weinhold fprintf(stderr, "Error: Failed to unregister file disk device"
176*f783542cSAlexander von Gluck IV "%s \"%s\" (ID: %" B_PRId32 "): %s\n",
177*f783542cSAlexander von Gluck IV isFilePath ? " for file" : "", fileNameOrID, device.ID(),
178*f783542cSAlexander von Gluck IV strerror(error));
1796cc1f015SIngo Weinhold return error;
1806cc1f015SIngo Weinhold }
1816cc1f015SIngo Weinhold
182*f783542cSAlexander von Gluck IV printf("Unregistered file disk device%s \"%s\" "
183*f783542cSAlexander von Gluck IV "(ID: %" B_PRId32 ")\n", isFilePath ? " for file" : "",
184*f783542cSAlexander von Gluck IV fileNameOrID, device.ID());
1856cc1f015SIngo Weinhold
1866cc1f015SIngo Weinhold return B_OK;
1876cc1f015SIngo Weinhold }
1886cc1f015SIngo Weinhold }
1896cc1f015SIngo Weinhold
1906cc1f015SIngo Weinhold fprintf(stderr, "Error: \"%s\" does not refer to a file disk device.\n",
1916cc1f015SIngo Weinhold fileNameOrID);
1926cc1f015SIngo Weinhold return B_BAD_VALUE;
1936cc1f015SIngo Weinhold }
1946cc1f015SIngo Weinhold
1956cc1f015SIngo Weinhold
1966cc1f015SIngo Weinhold int
main(int argc,const char * const * argv)1976cc1f015SIngo Weinhold main(int argc, const char* const* argv)
1986cc1f015SIngo Weinhold {
1996cc1f015SIngo Weinhold while (true) {
2006cc1f015SIngo Weinhold static struct option sLongOptions[] = {
2016cc1f015SIngo Weinhold { "help", no_argument, 0, 'h' },
2026cc1f015SIngo Weinhold { 0, 0, 0, 0 }
2036cc1f015SIngo Weinhold };
2046cc1f015SIngo Weinhold
2056cc1f015SIngo Weinhold opterr = 0; // don't print errors
2066cc1f015SIngo Weinhold int c = getopt_long(argc, (char**)argv, "+h", sLongOptions, NULL);
2076cc1f015SIngo Weinhold if (c == -1)
2086cc1f015SIngo Weinhold break;
2096cc1f015SIngo Weinhold
2106cc1f015SIngo Weinhold switch (c) {
2116cc1f015SIngo Weinhold case 'h':
2126cc1f015SIngo Weinhold print_usage_and_exit(false);
2136cc1f015SIngo Weinhold break;
2146cc1f015SIngo Weinhold
2156cc1f015SIngo Weinhold default:
2166cc1f015SIngo Weinhold print_usage_and_exit(true);
2176cc1f015SIngo Weinhold break;
2186cc1f015SIngo Weinhold }
2196cc1f015SIngo Weinhold }
2206cc1f015SIngo Weinhold
2216cc1f015SIngo Weinhold // Of the remaining arguments the next one should be the command.
2226cc1f015SIngo Weinhold if (optind >= argc)
2236cc1f015SIngo Weinhold print_usage_and_exit(true);
2246cc1f015SIngo Weinhold
2256cc1f015SIngo Weinhold status_t error = B_OK;
2266cc1f015SIngo Weinhold const char* command = argv[optind++];
2276cc1f015SIngo Weinhold
2286cc1f015SIngo Weinhold if (strcmp(command, "list") == 0) {
2296cc1f015SIngo Weinhold if (optind < argc)
2306cc1f015SIngo Weinhold print_usage_and_exit(true);
2316cc1f015SIngo Weinhold
2326cc1f015SIngo Weinhold list_file_disk_devices();
2336cc1f015SIngo Weinhold } else if (strcmp(command, "register") == 0) {
2346cc1f015SIngo Weinhold if (optind + 1 != argc)
2356cc1f015SIngo Weinhold print_usage_and_exit(true);
2366cc1f015SIngo Weinhold
2376cc1f015SIngo Weinhold const char* fileName = argv[optind++];
2386cc1f015SIngo Weinhold register_file_disk_device(fileName);
2396cc1f015SIngo Weinhold } else if (strcmp(command, "unregister") == 0) {
2406cc1f015SIngo Weinhold if (optind + 1 != argc)
2416cc1f015SIngo Weinhold print_usage_and_exit(true);
2426cc1f015SIngo Weinhold
2436cc1f015SIngo Weinhold const char* fileName = argv[optind++];
2446cc1f015SIngo Weinhold unregister_file_disk_device(fileName);
2456cc1f015SIngo Weinhold } else
2466cc1f015SIngo Weinhold print_usage_and_exit(true);
2476cc1f015SIngo Weinhold
2486cc1f015SIngo Weinhold return error == B_OK ? 0 : 1;
2496cc1f015SIngo Weinhold }
250