From cfe5e3905201349e9cf3f95d52ff4bd100bde37d Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 14 Apr 2024 21:10:49 +0200 Subject: Adding upstream version 2.39.3. Signed-off-by: Daniel Baumann --- sys-utils/blkpr.c | 314 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 314 insertions(+) create mode 100644 sys-utils/blkpr.c (limited to 'sys-utils/blkpr.c') diff --git a/sys-utils/blkpr.c b/sys-utils/blkpr.c new file mode 100644 index 0000000..90763db --- /dev/null +++ b/sys-utils/blkpr.c @@ -0,0 +1,314 @@ +/* + * blkpr.c -- persistent reservations on a block device. + * + * Copyright (C) 2021-2022 zhenwei pi + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * This program uses IOC_PR_XXX ioctl to run persistent reservations + * command on a block device if the device supports it. + */ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "nls.h" +#include "c.h" +#include "closestream.h" +#include "strutils.h" +#include "xalloc.h" + +struct type_string { + int type; + char *str; + char *desc; +}; + +/* This array should keep align with enum pr_type of linux/types.h */ +static const struct type_string pr_type[] = { + {PR_WRITE_EXCLUSIVE, "write-exclusive", + " * write-exclusive: Only the initiator that owns the reservation can\n" + " write to the device. Any initiator can read from the device.\n"}, + + {PR_EXCLUSIVE_ACCESS, "exclusive-access", + " * exclusive-access: Only the initiator that owns the reservation can\n" + " access the device.\n"}, + + {PR_WRITE_EXCLUSIVE_REG_ONLY, "write-exclusive-reg-only", + " * write-exclusive-reg-only: Only initiators with a registered key can\n" + " write to the device, any initiator can read from the device.\n"}, + + {PR_EXCLUSIVE_ACCESS_REG_ONLY, "exclusive-access-reg-only", + " * exclusive-access-reg-only: Only initiators with a registered key can\n" + " access the device.\n"}, + + {PR_WRITE_EXCLUSIVE_ALL_REGS, "write-exclusive-all-regs", + " * write-exclusive-all-regs: Only initiators with a registered key can\n" + " write to the device. Any initiator can read from the device. All\n" + " initiators with a registered key are considered reservation holders.\n" + " Please reference the SPC spec on the meaning of a reservation holder\n" + " if you want to use this type.\n"}, + + {PR_EXCLUSIVE_ACCESS_ALL_REGS, "exclusive-access-all-regs", + " * exclusive-access-all-regs: Only initiators with a registered key can\n" + " access the device. All initiators with a registered key are considered\n" + " reservation holders. Please reference the SPC spec on the meaning of\n" + " a reservation holder if you want to use this type.\n"} +}; + +static const struct type_string pr_command[] = { + {IOC_PR_REGISTER, "register", + " * register: This command registers a new reservation if the key argument\n" + " is non-null. If no existing reservation exists oldkey must be zero, if\n" + " an existing reservation should be replaced oldkey must contain the old\n" + " reservation key. If the key argument is 0 it unregisters the existing\n" + " reservation passed in oldkey.\n" + }, + + {IOC_PR_RESERVE, "reserve", + " * reserve: This command reserves the device and thus restricts access for\n" + " other devices based on the type argument. The key argument must be\n" + " the existing reservation key for the device as acquired by the register,\n" + " preempt, preempt-abort commands.\n"}, + + {IOC_PR_RELEASE, "release", + " * release: This command releases the reservation specified by key and flags\n" + " and thus removes any access restriction implied by it.\n"}, + + {IOC_PR_PREEMPT, "preempt", + " * preempt: This command releases the existing reservation referred to by\n" + " old_key and replaces it with a new reservation of type for the\n" + " reservation key key.\n"}, + + {IOC_PR_PREEMPT_ABORT, "preempt-abort", + " * preempt-abort: This command works like preempt except that it also aborts\n" + " any outstanding command sent over a connection identified by oldkey.\n"}, + + {IOC_PR_CLEAR, "clear", + " * clear: This command unregisters both key and any other reservation\n" + " key registered with the device and drops any existing reservation.\n"}, +}; + +static const struct type_string pr_flag[] = { + {PR_FL_IGNORE_KEY, "ignore-key", + " * ignore-key: Ignore the existing reservation key. This is commonly\n" + " supported for register command, and some implementation may support\n" + " the flag for reserve command.\n"} +}; + +static void print_type(FILE *out, const struct type_string *ts, size_t nmem) +{ + size_t i; + + for (i = 0; i < nmem; i++) { + fprintf(out, "%s\n", ts[i].desc); + } +} + + +static int parse_type_by_str(const struct type_string *ts, int nmem, char *pattern) +{ + int i; + + for (i = 0; i < nmem; i++) { + if (!strcmp(ts[i].str, pattern)) + return ts[i].type; + } + + return -1; +} + + +#define PRINT_SUPPORTED(XX) \ + static void print_##XX(FILE *out) \ + { print_type(out, XX, ARRAY_SIZE(XX)); } + +#define PARSE(XX) \ + static int parse_##XX(char *pattern) \ + { return parse_type_by_str(XX, ARRAY_SIZE(XX), pattern); } + +PRINT_SUPPORTED(pr_type) +PRINT_SUPPORTED(pr_command) +PRINT_SUPPORTED(pr_flag) + +PARSE(pr_type) +PARSE(pr_command) +PARSE(pr_flag) + +static int do_pr(char *path, uint64_t key, uint64_t oldkey, int op, int type, int flag) +{ + struct pr_registration pr_reg; + struct pr_reservation pr_res; + struct pr_preempt pr_prt; + struct pr_clear pr_clr; + int fd, ret; + + fd = open(path, O_RDWR); + if (fd < 0) + err(EXIT_FAILURE, _("cannot open %s"), path); + + switch (op) { + case IOC_PR_REGISTER: + pr_reg.old_key = oldkey; + pr_reg.new_key = key; + pr_reg.flags = flag; + ret = ioctl(fd, op, &pr_reg); + break; + case IOC_PR_RESERVE: + case IOC_PR_RELEASE: + pr_res.key = key; + pr_res.type = type; + pr_res.flags = flag; + ret = ioctl(fd, op, &pr_res); + break; + case IOC_PR_PREEMPT: + case IOC_PR_PREEMPT_ABORT: + pr_prt.old_key = oldkey; + pr_prt.new_key = key; + pr_prt.type = type; + pr_prt.flags = flag; + ret = ioctl(fd, op, &pr_prt); + break; + case IOC_PR_CLEAR: + pr_clr.key = key; + pr_clr.flags = flag; + ret = ioctl(fd, op, &pr_clr); + break; + default: + errno = EINVAL; + err(EXIT_FAILURE, _("unknown command")); + } + + close(fd); + if (ret < 0) + err(EXIT_FAILURE, _("pr ioctl failed")); + if (ret > 0) + errx(EXIT_FAILURE, _("error code 0x%x, for more detailed information see specification of device model."), ret); + + return ret; +} + +static void __attribute__((__noreturn__)) usage(void) +{ + FILE *out = stdout; + + fputs(USAGE_HEADER, out); + fprintf(out, + _(" %s [options] \n"), program_invocation_short_name); + + fputs(USAGE_SEPARATOR, out); + fputs(_("Persistent reservations on a device.\n"), out); + + fputs(USAGE_OPTIONS, out); + fputs(_(" -c, --command command of persistent reservations\n"), out); + fputs(_(" -k, --key key to operate\n"), out); + fputs(_(" -K, --oldkey old key to operate\n"), out); + fputs(_(" -f, --flag command flag\n"), out); + fputs(_(" -t, --type command type\n"), out); + + fputs(USAGE_SEPARATOR, out); + printf(USAGE_HELP_OPTIONS(26)); + + fputs(USAGE_ARGUMENTS, out); + + fputs(_(" is an command, available command:\n"), out); + print_pr_command(out); + + fputs(_(" is a command flag, available flags:\n"), out); + print_pr_flag(out); + + fputs(_(" is a command type, available types:\n"), out); + print_pr_type(out); + + printf(USAGE_MAN_TAIL("blkpr(8)")); + exit(EXIT_SUCCESS); +} + +int main(int argc, char **argv) +{ + int c; + char *path; + uint64_t key = 0, oldkey = 0; + int command = -1, type = -1, flag = 0; + + static const struct option longopts[] = { + { "help", no_argument, NULL, 'h' }, + { "version", no_argument, NULL, 'V' }, + { "command", required_argument, NULL, 'c' }, + { "key", required_argument, NULL, 'k' }, + { "oldkey", required_argument, NULL, 'K' }, + { "flag", required_argument, NULL, 'f' }, + { "type", required_argument, NULL, 't' }, + { NULL, 0, NULL, 0 } + }; + + setlocale(LC_ALL, ""); + bindtextdomain(PACKAGE, LOCALEDIR); + textdomain(PACKAGE); + close_stdout_atexit(); + + errno = EINVAL; + while ((c = getopt_long(argc, argv, "hVc:k:K:f:t:", longopts, NULL)) != -1) { + switch(c) { + case 'k': + key = strtosize_or_err(optarg, + _("failed to parse key")); + break; + case 'K': + oldkey = strtosize_or_err(optarg, + _("failed to parse old key")); + break; + case 'c': + command = parse_pr_command(optarg); + if (command < 0) + err(EXIT_FAILURE, _("unknown command")); + break; + case 't': + type = parse_pr_type(optarg); + if (type < 0) + err(EXIT_FAILURE, _("unknown type")); + break; + case 'f': + flag = parse_pr_flag(optarg); + if (flag < 0) + err(EXIT_FAILURE, _("unknown flag")); + break; + + case 'h': + usage(); + case 'V': + print_version(EXIT_SUCCESS); + default: + errtryhelp(EXIT_FAILURE); + } + } + + if (optind == argc) + errx(EXIT_FAILURE, _("no device specified")); + + path = argv[optind++]; + if (optind != argc) { + warnx(_("unexpected number of arguments")); + errtryhelp(EXIT_FAILURE); + } + + do_pr(path, key, oldkey, command, type, flag); + + return EXIT_SUCCESS; +} -- cgit v1.2.3