Skip to content

Commit f7cc369

Browse files
keithbuschKeith Busch
authored andcommitted
nvme-cli: Warn and delay before formatting
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: #501 Signed-off-by: Keith Busch <[email protected]>
1 parent 995b62d commit f7cc369

2 files changed

Lines changed: 93 additions & 0 deletions

File tree

Documentation/nvme-format.txt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ SYNOPSIS
1616
[--pi=<pi> | -i <pi>]
1717
[--ms=<ms> | -m <ms>]
1818
[--reset | -r ]
19+
[--force | -f ]
1920
[--timeout=<timeout> | -t <timeout> ]
2021

2122
DESCRIPTION
@@ -127,6 +128,10 @@ cryptographically. This is accomplished by deleting the encryption key.
127128
Issue a reset after successful format. Must use the character
128129
device for this.
129130

131+
-f::
132+
--force::
133+
Just send the command immediately without warning of the implications.
134+
130135
-t <timeout>::
131136
--timeout=<timeout>::
132137
Override default timeout value. In milliseconds.

nvme.c

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3491,6 +3491,78 @@ static int set_property(int argc, char **argv, struct command *cmd, struct plugi
34913491
return nvme_status_to_errno(err, false);
34923492
}
34933493

3494+
/* Requires global 'devicename' was set */
3495+
static void print_relatives()
3496+
{
3497+
unsigned id, i, nsid = NVME_NSID_ALL;
3498+
char *path = NULL;
3499+
bool block = true;
3500+
int ret;
3501+
3502+
ret = sscanf(devicename, "nvme%dn%d", &id, &nsid);
3503+
switch (ret) {
3504+
case 1:
3505+
asprintf(&path, "/sys/class/nvme/%s", devicename);
3506+
block = false;
3507+
break;
3508+
case 2:
3509+
asprintf(&path, "/sys/block/%s/device", devicename);
3510+
break;
3511+
default:
3512+
return;
3513+
}
3514+
if (!path)
3515+
return;
3516+
3517+
if (block) {
3518+
char *subsysnqn;
3519+
struct subsys_list_item *slist;
3520+
int subcnt = 0;
3521+
3522+
subsysnqn = get_nvme_subsnqn(path);
3523+
if (!subsysnqn)
3524+
return;
3525+
slist = get_subsys_list(&subcnt, subsysnqn, nsid);
3526+
if (subcnt != 1) {
3527+
free(subsysnqn);
3528+
free(path);
3529+
return;
3530+
}
3531+
3532+
fprintf(stderr, "Namespace %s has parent controller(s):", devicename);
3533+
for (i = 0; i < slist[0].nctrls; i++)
3534+
fprintf(stderr, "%s%s", i ? ", " : "", slist[0].ctrls[i].name);
3535+
fprintf(stderr, "\n\n");
3536+
free(subsysnqn);
3537+
} else {
3538+
struct dirent **paths;
3539+
bool comma = false;
3540+
int n, ns, cntlid;
3541+
3542+
n = scandir(path, &paths, scan_ctrl_paths_filter, alphasort);
3543+
if (n < 0) {
3544+
free(path);
3545+
return;
3546+
}
3547+
3548+
fprintf(stderr, "Controller %s has child namespace(s):", devicename);
3549+
for (i = 0; i < n; i++) {
3550+
if (sscanf(paths[i]->d_name, "nvme%dc%dn%d",
3551+
&id, &cntlid, &ns) != 3) {
3552+
if (sscanf(paths[i]->d_name, "nvme%dn%d",
3553+
&id, &ns) != 2) {
3554+
continue;
3555+
}
3556+
}
3557+
fprintf(stderr, "%snvme%dn%d", comma ? ", " : "", id, ns);
3558+
comma = true;
3559+
}
3560+
fprintf(stderr, "\n\n");
3561+
free(paths);
3562+
}
3563+
free(path);
3564+
}
3565+
34943566
static int format(int argc, char **argv, struct command *cmd, struct plugin *plugin)
34953567
{
34963568
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
35063578
const char *reset = "Automatically reset the controller after successful format";
35073579
const char *timeout = "timeout value, in milliseconds";
35083580
const char *bs = "target block size";
3581+
const char *force = "The \"I know what I'm doing\" flag, skip confirmation before sending command";
35093582
struct nvme_id_ns ns;
35103583
int err, fd, i;
35113584
__u8 prev_lbaf = 0;
@@ -3521,6 +3594,7 @@ static int format(int argc, char **argv, struct command *cmd, struct plugin *plu
35213594
__u8 ms;
35223595
__u64 bs;
35233596
int reset;
3597+
int force;
35243598
};
35253599

35263600
struct config cfg = {
@@ -3530,6 +3604,7 @@ static int format(int argc, char **argv, struct command *cmd, struct plugin *plu
35303604
.ses = 0,
35313605
.pi = 0,
35323606
.reset = 0,
3607+
.force = 0,
35333608
.bs = 0,
35343609
};
35353610

@@ -3542,6 +3617,7 @@ static int format(int argc, char **argv, struct command *cmd, struct plugin *plu
35423617
{"pil", 'p', "NUM", CFG_BYTE, &cfg.pil, required_argument, pil},
35433618
{"ms", 'm', "NUM", CFG_BYTE, &cfg.ms, required_argument, ms},
35443619
{"reset", 'r', "", CFG_NONE, &cfg.reset, no_argument, reset},
3620+
{"force", 'f', "NUM", CFG_NONE, &cfg.force, no_argument, force},
35453621
{"block-size", 'b', "NUM", CFG_LONG_SUFFIX, &cfg.bs, required_argument, bs},
35463622
{NULL}
35473623
};
@@ -3567,6 +3643,7 @@ static int format(int argc, char **argv, struct command *cmd, struct plugin *plu
35673643
goto close_fd;
35683644
}
35693645
}
3646+
35703647
if (S_ISBLK(nvme_stat.st_mode)) {
35713648
cfg.namespace_id = get_nsid(fd);
35723649
if (cfg.namespace_id == 0) {
@@ -3656,6 +3733,17 @@ static int format(int argc, char **argv, struct command *cmd, struct plugin *plu
36563733
goto close_fd;
36573734
}
36583735

3736+
if (!cfg.force) {
3737+
fprintf(stderr, "You are about to format %s, namespace %#x.\n",
3738+
devicename, cfg.namespace_id);
3739+
print_relatives();
3740+
fprintf(stderr, "WARNING: Format may irrevocably delete this device's data.\n"
3741+
"You have 10 seconds to press Ctrl-C to cancel this operation.\n\n"
3742+
"Use the force [--force|-f] option to suppress this warning.\n");
3743+
sleep(10);
3744+
fprintf(stderr, "Sending format operation ... \n");
3745+
}
3746+
36593747
err = nvme_format(fd, cfg.namespace_id, cfg.lbaf, cfg.ses, cfg.pi,
36603748
cfg.pil, cfg.ms, cfg.timeout);
36613749
if (err < 0)

0 commit comments

Comments
 (0)