From 6cc1f01511a4b2d8bdc3e72e20dd73f9fa0313f7 Mon Sep 17 00:00:00 2001 From: Ingo Weinhold Date: Thu, 1 Oct 2009 03:05:06 +0000 Subject: [PATCH] Implemented small tool diskimage, which allows to register/unregister files as disk devices. It also demonstrates that the respective devfs functionality is utterly broken. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@33384 a95241bf-73f2-0310-859d-f6bbb57e9c96 --- src/bin/Jamfile | 1 + src/bin/diskimage.cpp | 246 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 247 insertions(+) create mode 100644 src/bin/diskimage.cpp diff --git a/src/bin/Jamfile b/src/bin/Jamfile index 1689cac87e..81b31f33cf 100644 --- a/src/bin/Jamfile +++ b/src/bin/Jamfile @@ -80,6 +80,7 @@ StdBinCommands checkfs.cpp clipboard.cpp df.cpp + diskimage.cpp dpms.cpp draggers.cpp # factor.cpp diff --git a/src/bin/diskimage.cpp b/src/bin/diskimage.cpp new file mode 100644 index 0000000000..2fc4582302 --- /dev/null +++ b/src/bin/diskimage.cpp @@ -0,0 +1,246 @@ +/* + * Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de. + * Distributed under the terms of the MIT License. + */ + + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + + +extern const char* __progname; +const char* kCommandName = __progname; + + +static const char* kUsage = + "Usage: %s register \n" + " %s unregister ( | | )\n" + " %s list\n" + "Registers a regular file as disk device, unregisters an already " + "registered\n" + "one, or lists all file disk devices.\n" + "\n" + "Options:\n" + " -h, --help - Print this usage info.\n" +; + + +static void +print_usage_and_exit(bool error) +{ + fprintf(error ? stderr : stdout, kUsage, kCommandName, kCommandName, + kCommandName); + exit(error ? 1 : 0); +} + + +static status_t +list_file_disk_devices() +{ + printf(" ID File Device\n"); + printf("------------------------------------------------------------------" + "-------------\n"); + + BDiskDeviceRoster roster; + BDiskDevice device; + while (roster.GetNextDevice(&device) == B_OK) { + if (!device.IsFile()) + continue; + + // ID + printf("%6ld ", device.ID()); + + // file path + BPath path; + printf("%-35s", + device.GetFilePath(&path) == B_OK ? path.Path() : "???"); + + // device path + printf("%s", device.GetPath(&path) == B_OK ? path.Path() : "???"); + printf("\n"); + } + + return B_OK; +} + + +static status_t +register_file_disk_device(const char* fileName) +{ + // stat() the file to verify that it's a regular file + struct stat st; + if (lstat(fileName, &st) != 0) { + status_t error = errno; + fprintf(stderr, "Error: Failed to stat() \"%s\": %s\n", fileName, + strerror(error)); + return error; + } + + if (!S_ISREG(st.st_mode)) { + fprintf(stderr, "Error: \"%s\" is not a regular file.\n", fileName); + return B_BAD_VALUE; + } + + // register the file + BDiskDeviceRoster roster; + partition_id id = roster.RegisterFileDevice(fileName); + if (id < 0) { + fprintf(stderr, "Error: Failed to register file disk device: %s\n", + strerror(id)); + return id; + } + + // print the success message (get the device path) + BDiskDevice device; + BPath path; + if (roster.GetDeviceWithID(id, &device) == B_OK + && device.GetPath(&path) == B_OK) { + printf("Registered file as disk device \"%s\" with ID %ld.\n", + path.Path(), id); + } else { + printf("Registered file as disk device with ID %ld, but failed to " + "get the device path.\n", id); + } + + return B_OK; +} + + +static status_t +unregister_file_disk_device(const char* fileNameOrID) +{ + // try to parse the parameter as ID + char* numberEnd; + partition_id id = strtol(fileNameOrID, &numberEnd, 0); + BDiskDeviceRoster roster; + if (id >= 0 && numberEnd != fileNameOrID && *numberEnd == '\0') { + BDiskDevice device; + if (roster.GetDeviceWithID(id, &device) == B_OK && device.IsFile()) { + status_t error = roster.UnregisterFileDevice(id); + if (error != B_OK) { + fprintf(stderr, "Error: Failed to unregister file disk device " + "with ID %ld: %s\n", id, strerror(error)); + return error; + } + + printf("Unregistered file disk device with ID %ld.\n", id); + return B_OK; + } else { + fprintf(stderr, "No file disk device with ID %ld, trying file " + "\"%s\"\n", id, fileNameOrID); + } + } + + // the parameter must be a file name -- stat() it + struct stat st; + if (lstat(fileNameOrID, &st) != 0) { + status_t error = errno; + fprintf(stderr, "Error: Failed to stat() \"%s\": %s\n", fileNameOrID, + strerror(error)); + return error; + } + + // remember the volume and node ID, so we can identify the file + // NOTE: There's a race condition -- we would need to open the file and + // keep it open to avoid it. + dev_t volumeID = st.st_dev; + ino_t nodeID = st.st_ino; + + // iterate through all file disk devices and try to find a match + BDiskDevice device; + while (roster.GetNextDevice(&device) == B_OK) { + if (!device.IsFile()) + continue; + + // get file path and stat it, same for the device path + BPath path; + bool isFilePath = true; + if ((device.GetFilePath(&path) == B_OK && lstat(path.Path(), &st) == 0 + && volumeID == st.st_dev && nodeID == st.st_ino) + || (isFilePath = false, false) + || (device.GetPath(&path) == B_OK && lstat(path.Path(), &st) == 0 + && volumeID == st.st_dev && nodeID == st.st_ino)) { + status_t error = roster.UnregisterFileDevice(device.ID()); + if (error != B_OK) { + fprintf(stderr, "Error: Failed to unregister file disk device" + "%s \"%s\" (ID: %ld): %s\n", isFilePath ? " for file" : "", + fileNameOrID, device.ID(), strerror(error)); + return error; + } + + printf("Unregistered file disk device%s \"%s\" (ID: %ld)\n", + isFilePath ? " for file" : "", fileNameOrID, device.ID()); + + return B_OK; + } + } + + fprintf(stderr, "Error: \"%s\" does not refer to a file disk device.\n", + fileNameOrID); + return B_BAD_VALUE; +} + + +int +main(int argc, const char* const* argv) +{ + while (true) { + static struct option sLongOptions[] = { + { "help", no_argument, 0, 'h' }, + { 0, 0, 0, 0 } + }; + + opterr = 0; // don't print errors + int c = getopt_long(argc, (char**)argv, "+h", sLongOptions, NULL); + if (c == -1) + break; + + switch (c) { + case 'h': + print_usage_and_exit(false); + break; + + default: + print_usage_and_exit(true); + break; + } + } + + // Of the remaining arguments the next one should be the command. + if (optind >= argc) + print_usage_and_exit(true); + + status_t error = B_OK; + const char* command = argv[optind++]; + + if (strcmp(command, "list") == 0) { + if (optind < argc) + print_usage_and_exit(true); + + list_file_disk_devices(); + } else if (strcmp(command, "register") == 0) { + if (optind + 1 != argc) + print_usage_and_exit(true); + + const char* fileName = argv[optind++]; + register_file_disk_device(fileName); + } else if (strcmp(command, "unregister") == 0) { + if (optind + 1 != argc) + print_usage_and_exit(true); + + const char* fileName = argv[optind++]; + unregister_file_disk_device(fileName); + } else + print_usage_and_exit(true); + + return error == B_OK ? 0 : 1; +}