diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-27 13:14:44 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-27 13:14:44 +0000 |
commit | 30ff6afe596eddafacf22b1a5b2d1a3d6254ea15 (patch) | |
tree | 9b788335f92174baf7ee18f03ca8330b8c19ce2b /disk-utils/raw.c | |
parent | Initial commit. (diff) | |
download | util-linux-upstream.tar.xz util-linux-upstream.zip |
Adding upstream version 2.36.1.upstream/2.36.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'disk-utils/raw.c')
-rw-r--r-- | disk-utils/raw.c | 276 |
1 files changed, 276 insertions, 0 deletions
diff --git a/disk-utils/raw.c b/disk-utils/raw.c new file mode 100644 index 0000000..b44a581 --- /dev/null +++ b/disk-utils/raw.c @@ -0,0 +1,276 @@ +/* + * raw.c: User mode tool to bind and query raw character devices. + * + * Stephen Tweedie, 1999, 2000 + * + * This file may be redistributed under the terms of the GNU General + * Public License, version 2. + * + * Copyright Red Hat Software, 1999, 2000 + * + */ + +#include <errno.h> +#include <fcntl.h> +#include <getopt.h> +#include <linux/major.h> +#include <linux/raw.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/ioctl.h> +#include <sys/stat.h> +#include <unistd.h> + +#include "c.h" +#include "closestream.h" +#include "nls.h" +#include "pathnames.h" + +#define EXIT_RAW_ACCESS 3 +#define EXIT_RAW_IOCTL 4 + +#define RAW_NR_MINORS 8192 + +static int do_query; +static int do_query_all; + +static int master_fd; +static int raw_minor; + +void open_raw_ctl(void); +static int query(int minor_raw, const char *raw_name, int quiet); +static int bind(int minor_raw, int block_major, int block_minor); + +static void __attribute__((__noreturn__)) usage(void) +{ + FILE *out = stdout; + fputs(USAGE_HEADER, out); + fprintf(out, + _(" %1$s %2$srawN <major> <minor>\n" + " %1$s %2$srawN /dev/<blockdevice>\n" + " %1$s -q %2$srawN\n" + " %1$s -qa\n"), program_invocation_short_name, + _PATH_RAWDEVDIR); + + fputs(USAGE_SEPARATOR, out); + fputs(_("Bind a raw character device to a block device.\n"), out); + + fputs(USAGE_OPTIONS, out); + fputs(_(" -q, --query set query mode\n"), out); + fputs(_(" -a, --all query all raw devices\n"), out); + printf(USAGE_HELP_OPTIONS(16)); + printf(USAGE_MAN_TAIL("raw(8)")); + exit(EXIT_SUCCESS); +} + +static long strtol_octal_or_err(const char *str, const char *errmesg) +{ + long num; + char *end = NULL; + + if (str == NULL || *str == '\0') + goto err; + errno = 0; + num = strtol(str, &end, 0); + + if (errno || str == end || (end && *end)) + goto err; + + return num; + err: + if (errno) + err(EXIT_FAILURE, "%s: '%s'", errmesg, str); + else + errx(EXIT_FAILURE, "%s: '%s'", errmesg, str); + return 0; +} + +int main(int argc, char *argv[]) +{ + int c; + char *raw_name; + char *block_name; + int retval; + int block_major, block_minor; + int i, rc; + + struct stat statbuf; + + static const struct option longopts[] = { + {"query", no_argument, NULL, 'q'}, + {"all", no_argument, NULL, 'a'}, + {"version", no_argument, NULL, 'V'}, + {"help", no_argument, NULL, 'h'}, + {NULL, 0, NULL, '0'}, + }; + + setlocale(LC_ALL, ""); + bindtextdomain(PACKAGE, LOCALEDIR); + textdomain(PACKAGE); + close_stdout_atexit(); + + while ((c = getopt_long(argc, argv, "qaVh", longopts, NULL)) != -1) + switch (c) { + case 'q': + do_query = 1; + break; + case 'a': + do_query_all = 1; + break; + case 'V': + print_version(EXIT_SUCCESS); + case 'h': + usage(); + default: + errtryhelp(EXIT_FAILURE); + } + + /* + * Check for, and open, the master raw device, /dev/raw + */ + open_raw_ctl(); + + if (do_query_all) { + if (optind < argc) { + warnx(_("bad usage")); + errtryhelp(EXIT_FAILURE); + } + for (i = 1; i < RAW_NR_MINORS; i++) + query(i, NULL, 1); + exit(EXIT_SUCCESS); + } + + /* + * It's a bind or a single query. Either way we need a raw device. + */ + + if (optind >= argc) { + warnx(_("bad usage")); + errtryhelp(EXIT_FAILURE); + } + raw_name = argv[optind++]; + + /* + * try to check the device name before stat(), because on systems with + * udev the raw0 causes a create udev event for char 162/0, which + * causes udev to *remove* /dev/rawctl + */ + rc = sscanf(raw_name, _PATH_RAWDEVDIR "raw%d", &raw_minor); + if (rc != 1) { + warnx(_("bad usage")); + errtryhelp(EXIT_FAILURE); + } + if (raw_minor == 0) + errx(EXIT_RAW_ACCESS, + _("Device '%s' is the control raw device " + "(use raw<N> where <N> is greater than zero)"), + raw_name); + + if (do_query) + return query(raw_minor, raw_name, 0); + + /* + * It's not a query, so we still have some parsing to do. Have we been + * given a block device filename or a major/minor pair? + */ + switch (argc - optind) { + case 1: + block_name = argv[optind]; + retval = stat(block_name, &statbuf); + if (retval) + err(EXIT_RAW_ACCESS, + _("Cannot locate block device '%s'"), block_name); + if (!S_ISBLK(statbuf.st_mode)) + errx(EXIT_RAW_ACCESS, + _("Device '%s' is not a block device"), + block_name); + block_major = major(statbuf.st_rdev); + block_minor = minor(statbuf.st_rdev); + break; + + case 2: + block_major = + strtol_octal_or_err(argv[optind], + _("failed to parse argument")); + block_minor = + strtol_octal_or_err(argv[optind + 1], + _("failed to parse argument")); + break; + + default: + warnx(_("bad usage")); + errtryhelp(EXIT_FAILURE); + } + + return bind(raw_minor, block_major, block_minor); +} + +void open_raw_ctl(void) +{ + master_fd = open(_PATH_RAWDEVCTL, O_RDWR, 0); + if (master_fd < 0) { + master_fd = open(_PATH_RAWDEVCTL_OLD, O_RDWR, 0); + if (master_fd < 0) + err(EXIT_RAW_ACCESS, + _("Cannot open master raw device '%s'"), + _PATH_RAWDEVCTL); + } +} + +static int query(int minor_raw, const char *raw_name, int quiet) +{ + struct raw_config_request rq; + static int has_worked = 0; + + if (raw_name) { + struct stat statbuf; + + if (stat(raw_name, &statbuf) != 0) + err(EXIT_RAW_ACCESS, + _("Cannot locate raw device '%s'"), raw_name); + if (!S_ISCHR(statbuf.st_mode)) + errx(EXIT_RAW_ACCESS, + _("Raw device '%s' is not a character dev"), + raw_name); + if (major(statbuf.st_rdev) != RAW_MAJOR) + errx(EXIT_RAW_ACCESS, + _("Device '%s' is not a raw dev"), raw_name); + minor_raw = minor(statbuf.st_rdev); + } + + rq.raw_minor = minor_raw; + if (ioctl(master_fd, RAW_GETBIND, &rq) < 0) { + if (quiet && errno == ENODEV) + return 3; + if (has_worked && errno == EINVAL) + return 0; + err(EXIT_RAW_IOCTL, _("Error querying raw device")); + } + + /* If one query has worked, mark that fact so that we don't report + * spurious fatal errors if raw(8) has been built to support more raw + * minor numbers than the kernel has. */ + has_worked = 1; + if (quiet && !rq.block_major && !rq.block_minor) + return 0; + printf(_("%sraw%d: bound to major %d, minor %d\n"), + _PATH_RAWDEVDIR, minor_raw, (int)rq.block_major, + (int)rq.block_minor); + return 0; +} + +static int bind(int minor_raw, int block_major, int block_minor) +{ + struct raw_config_request rq; + + rq.raw_minor = minor_raw; + rq.block_major = block_major; + rq.block_minor = block_minor; + if (ioctl(master_fd, RAW_SETBIND, &rq) < 0) + err(EXIT_RAW_IOCTL, _("Error setting raw device")); + printf(_("%sraw%d: bound to major %d, minor %d\n"), + _PATH_RAWDEVDIR, raw_minor, (int)rq.block_major, + (int)rq.block_minor); + return 0; +} |