summaryrefslogtreecommitdiffstats
path: root/sys-utils/blkpr.c
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-14 19:10:49 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-14 19:10:49 +0000
commitcfe5e3905201349e9cf3f95d52ff4bd100bde37d (patch)
treed0baf160cbee3195249d095f85e52d20c21acf02 /sys-utils/blkpr.c
parentInitial commit. (diff)
downloadutil-linux-cfe5e3905201349e9cf3f95d52ff4bd100bde37d.tar.xz
util-linux-cfe5e3905201349e9cf3f95d52ff4bd100bde37d.zip
Adding upstream version 2.39.3.upstream/2.39.3
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'sys-utils/blkpr.c')
-rw-r--r--sys-utils/blkpr.c314
1 files changed, 314 insertions, 0 deletions
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 <pizhenwei@bytedance.com>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ *
+ * This program uses IOC_PR_XXX ioctl to run persistent reservations
+ * command on a block device if the device supports it.
+ */
+#include <string.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <getopt.h>
+#include <locale.h>
+#include <sys/ioctl.h>
+#include <linux/pr.h>
+
+#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] <device>\n"), program_invocation_short_name);
+
+ fputs(USAGE_SEPARATOR, out);
+ fputs(_("Persistent reservations on a device.\n"), out);
+
+ fputs(USAGE_OPTIONS, out);
+ fputs(_(" -c, --command <cmd> command of persistent reservations\n"), out);
+ fputs(_(" -k, --key <num> key to operate\n"), out);
+ fputs(_(" -K, --oldkey <num> old key to operate\n"), out);
+ fputs(_(" -f, --flag <flag> command flag\n"), out);
+ fputs(_(" -t, --type <type> command type\n"), out);
+
+ fputs(USAGE_SEPARATOR, out);
+ printf(USAGE_HELP_OPTIONS(26));
+
+ fputs(USAGE_ARGUMENTS, out);
+
+ fputs(_(" <cmd> is an command, available command:\n"), out);
+ print_pr_command(out);
+
+ fputs(_(" <flag> is a command flag, available flags:\n"), out);
+ print_pr_flag(out);
+
+ fputs(_(" <type> 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;
+}