]> www.infradead.org Git - users/sagi/nvme-cli.git/commitdiff
nvme-cli: Warn and delay before formatting
authorKeith Busch <kbusch@kernel.org>
Wed, 21 Aug 2019 22:28:19 +0000 (16:28 -0600)
committerKeith Busch <keith.busch@intel.com>
Thu, 22 Aug 2019 16:07:35 +0000 (10:07 -0600)
By popular demand, have format print a warning that shows the relatives of
the device the format was sent to.

  If this command was issued to a block device, its relative are the
  parent controllers.

  If this command was issued to a controller device, its relative children
  are namespace block devices.

This provides a visual clue that /dev/nvme0 may or may not be the parent
controller to namespace /dev/nvme0n1, and provides ample time for the
user to abort the operation if they didn't mean to do this.

Here are some examples of what this change provides sending this command
to a controller first, then to a multipathed namespace:

  # nvme format /dev/nvme0 -n 1
  You are about to format nvme0, namespace 0x1.
  Controller nvme0 has child namespace(s):nvme2n1

  # nvme format /dev/nvme4n1
  You are about to format nvme4n1, namespace 0x1.
  Namespace nvme4n1 has parent controller(s):nvme4, nvme5

Link: https://github.com/linux-nvme/nvme-cli/issues/501
Signed-off-by: Keith Busch <kbusch@kernel.org>
Documentation/nvme-format.txt
nvme.c

index 7030808e9ff45dcb42b7cab6d8ee1c9adc76ba09..9ef788ae0c7a98117dcc8a7dd98a471b0a0e8a65 100644 (file)
@@ -16,6 +16,7 @@ SYNOPSIS
                    [--pi=<pi> | -i <pi>]
                    [--ms=<ms> | -m <ms>]
                    [--reset | -r ]
+                   [--force | -f ]
                    [--timeout=<timeout> | -t <timeout> ]
 
 DESCRIPTION
@@ -127,6 +128,10 @@ cryptographically. This is accomplished by deleting the encryption key.
        Issue a reset after successful format. Must use the character
        device for this.
 
+-f::
+--force::
+       Just send the command immediately without warning of the implications.
+
 -t <timeout>::
 --timeout=<timeout>::
        Override default timeout value. In milliseconds.
diff --git a/nvme.c b/nvme.c
index 3d9085b4057437d404b6b4daf1ae1e45951265f1..7411aad1932c03fb19ad9711ef922ac20bd67121 100644 (file)
--- a/nvme.c
+++ b/nvme.c
@@ -3491,6 +3491,78 @@ ret:
        return nvme_status_to_errno(err, false);
 }
 
+/* Requires global 'devicename' was set */
+static void print_relatives()
+{
+       unsigned id, i, nsid = NVME_NSID_ALL;
+       char *path = NULL;
+       bool block = true;
+       int ret;
+
+       ret = sscanf(devicename, "nvme%dn%d", &id, &nsid);
+       switch (ret) {
+       case 1:
+               asprintf(&path, "/sys/class/nvme/%s", devicename);
+               block = false;
+               break;
+       case 2:
+               asprintf(&path, "/sys/block/%s/device", devicename);
+               break;
+       default:
+               return;
+       }
+       if (!path)
+               return;
+
+       if (block) {
+               char *subsysnqn;
+               struct subsys_list_item *slist;
+               int subcnt = 0;
+
+               subsysnqn = get_nvme_subsnqn(path);
+               if (!subsysnqn)
+                       return;
+               slist = get_subsys_list(&subcnt, subsysnqn, nsid);
+               if (subcnt != 1) {
+                       free(subsysnqn);
+                       free(path);
+                       return;
+               }
+
+               fprintf(stderr, "Namespace %s has parent controller(s):", devicename);
+               for (i = 0; i < slist[0].nctrls; i++)
+                       fprintf(stderr, "%s%s", i ? ", " : "", slist[0].ctrls[i].name);
+               fprintf(stderr, "\n\n");
+               free(subsysnqn);
+       } else {
+               struct dirent **paths;
+               bool comma = false;
+               int n, ns, cntlid;
+
+               n = scandir(path, &paths, scan_ctrl_paths_filter, alphasort);
+               if (n < 0) {
+                       free(path);
+                       return;
+               }
+
+               fprintf(stderr, "Controller %s has child namespace(s):", devicename);
+               for (i = 0; i < n; i++) {
+                       if (sscanf(paths[i]->d_name, "nvme%dc%dn%d",
+                                  &id, &cntlid, &ns) != 3) {
+                               if (sscanf(paths[i]->d_name, "nvme%dn%d",
+                                          &id, &ns) != 2) {
+                                       continue;
+                               }
+                       }
+                       fprintf(stderr, "%snvme%dn%d", comma ? ", " : "", id, ns);
+                       comma = true;
+               }
+               fprintf(stderr, "\n\n");
+               free(paths);
+       }
+       free(path);
+}
+
 static int format(int argc, char **argv, struct command *cmd, struct plugin *plugin)
 {
        const char *desc = "Re-format a specified namespace on the "\
@@ -3506,6 +3578,7 @@ static int format(int argc, char **argv, struct command *cmd, struct plugin *plu
        const char *reset = "Automatically reset the controller after successful format";
        const char *timeout = "timeout value, in milliseconds";
        const char *bs = "target block size";
+       const char *force = "The \"I know what I'm doing\" flag, skip confirmation before sending command";
        struct nvme_id_ns ns;
        int err, fd, i;
        __u8 prev_lbaf = 0;
@@ -3521,6 +3594,7 @@ static int format(int argc, char **argv, struct command *cmd, struct plugin *plu
                __u8  ms;
                __u64 bs;
                int reset;
+               int force;
        };
 
        struct config cfg = {
@@ -3530,6 +3604,7 @@ static int format(int argc, char **argv, struct command *cmd, struct plugin *plu
                .ses          = 0,
                .pi           = 0,
                .reset        = 0,
+               .force        = 0,
                .bs           = 0,
        };
 
@@ -3542,6 +3617,7 @@ static int format(int argc, char **argv, struct command *cmd, struct plugin *plu
                {"pil",          'p', "NUM",  CFG_BYTE,     &cfg.pil,          required_argument, pil},
                {"ms",           'm', "NUM",  CFG_BYTE,     &cfg.ms,           required_argument, ms},
                {"reset",        'r', "",     CFG_NONE,     &cfg.reset,        no_argument,       reset},
+               {"force",        'f', "NUM",  CFG_NONE,     &cfg.force,        no_argument,       force},
                {"block-size",   'b', "NUM",  CFG_LONG_SUFFIX, &cfg.bs,        required_argument, bs},
                {NULL}
        };
@@ -3567,6 +3643,7 @@ static int format(int argc, char **argv, struct command *cmd, struct plugin *plu
                        goto close_fd;
                }
        }
+
        if (S_ISBLK(nvme_stat.st_mode)) {
                cfg.namespace_id = get_nsid(fd);
                if (cfg.namespace_id == 0) {
@@ -3656,6 +3733,17 @@ static int format(int argc, char **argv, struct command *cmd, struct plugin *plu
                goto close_fd;
        }
 
+       if (!cfg.force) {
+               fprintf(stderr, "You are about to format %s, namespace %#x.\n",
+                       devicename, cfg.namespace_id);
+               print_relatives();
+               fprintf(stderr, "WARNING: Format may irrevocably delete this device's data.\n"
+                       "You have 10 seconds to press Ctrl-C to cancel this operation.\n\n"
+                       "Use the force [--force|-f] option to suppress this warning.\n");
+               sleep(10);
+               fprintf(stderr, "Sending format operation ... \n");
+       }
+
        err = nvme_format(fd, cfg.namespace_id, cfg.lbaf, cfg.ses, cfg.pi,
                                cfg.pil, cfg.ms, cfg.timeout);
        if (err < 0)