Skip to content

Commit

Permalink
Further improve 'zpool labelclear' command
Browse files Browse the repository at this point in the history
That patch follows:
  openzfs#8500
and is a port to ZoL of:
  openzfs/openzfs#424

It adds the following:
  - ability to clear a specific label (options -b, -e and -i)
  - label invalidation using a single-byte modification (wipe with option -w)
  - option -ff to force invalidation even for invalid labels

OpenZFS-issue: https://www.illumos.org/issues/7584
  • Loading branch information
martymac committed Mar 29, 2019
1 parent f94b3cb commit 7c62008
Show file tree
Hide file tree
Showing 8 changed files with 166 additions and 55 deletions.
65 changes: 56 additions & 9 deletions cmd/zpool/zpool_main.c
Expand Up @@ -347,7 +347,8 @@ get_usage(zpool_help_t idx)
"\t [[pool ...]|[pool vdev ...]|[vdev ...]]"
" [[-n] interval [count]]\n"));
case HELP_LABELCLEAR:
return (gettext("\tlabelclear [-f] <vdev>\n"));
return (gettext("\tlabelclear [-b | -e | -i index] [-w] "
"[-f] <vdev>\n"));
case HELP_LIST:
return (gettext("\tlist [-gHLpPv] [-o property[,...]] "
"[-T d|u] [pool] ... \n"
Expand Down Expand Up @@ -1038,12 +1039,22 @@ zpool_do_remove(int argc, char **argv)
}

/*
* zpool labelclear [-f] <vdev>
* zpool labelclear [-b | -e | -i index] [-w] [-f] <vdev>
*
* -f Force clearing the label for the vdevs which are members of
* the exported or foreign pools.
* -b Only work on labels located at the beginning of the
* device.
*
* Verifies that the vdev is not active and zeros out the label information
* -e Only work on labels located at the end of the device.
*
* -i index Only work on label located at specified index.
*
* -w Wipe label area entirely and replace it with zeroes.
*
* -f Force clearing the label for the vdevs which are
* members of the exported or foreign pools. If specified
* twice, clear even seemingly-invalid labels.
*
* Verifies that the vdev is not active and invalidates the label information
* on the device.
*/
int
Expand All @@ -1057,11 +1068,42 @@ zpool_do_labelclear(int argc, char **argv)
pool_state_t state;
boolean_t inuse = B_FALSE;
boolean_t force = B_FALSE;
boolean_t force_invalid = B_FALSE;
boolean_t wipe = B_FALSE;
unsigned int start = 0, n = VDEV_LABELS;
char *end = NULL;
long long index = 0;

/* check options */
while ((c = getopt(argc, argv, "f")) != -1) {
while ((c = getopt(argc, argv, "bei:wf")) != -1) {
switch (c) {
case 'b':
start = 0;
n = VDEV_LABELS / 2;
break;
case 'e':
start = VDEV_LABELS / 2;
n = VDEV_LABELS / 2;
break;
case 'i':
index = strtoll(optarg, &end, 10);
if((end == optarg) || (*end != '\0') ||
(index < 0) || (index >= VDEV_LABELS)) {
(void) fprintf(stderr,
gettext("Invalid index value provided\n"));
return (1);
}
start = (unsigned int)index;
n = 1;
break;
case 'w':
wipe = B_TRUE;
break;
case 'f':
if (force) {
/* -f specified twice */
force_invalid = B_TRUE;
}
force = B_TRUE;
break;
default:
Expand Down Expand Up @@ -1123,8 +1165,13 @@ zpool_do_labelclear(int argc, char **argv)
"cache for %s: %s\n"), vdev, strerror(errno));

if (zpool_read_label(fd, &config, NULL) != 0) {
(void) fprintf(stderr,
gettext("failed to read label from %s\n"), vdev);
if (force) {
goto wipe_label;
}
(void) fprintf(stderr, gettext(
"use '-f' to override the following error:\n"
"failed to read label from \"%s\"\n"),
vdev);
ret = 1;
goto errout;
}
Expand Down Expand Up @@ -1179,7 +1226,7 @@ zpool_do_labelclear(int argc, char **argv)
}

wipe_label:
ret = zpool_clear_label(fd);
ret = zpool_clear_n_labels(fd, start, n, force_invalid, wipe);
if (ret != 0) {
(void) fprintf(stderr,
gettext("failed to clear label for %s\n"), vdev);
Expand Down
2 changes: 2 additions & 0 deletions include/libzfs.h
Expand Up @@ -808,6 +808,8 @@ extern int zpool_in_use(libzfs_handle_t *, int, pool_state_t *, char **,
* Label manipulation.
*/
extern int zpool_clear_label(int);
extern int zpool_clear_n_labels(int, unsigned int, unsigned int, boolean_t,
boolean_t);

/*
* Management interfaces for SMB ACL files
Expand Down
2 changes: 2 additions & 0 deletions include/sys/fs/zfs.h
Expand Up @@ -748,6 +748,8 @@ typedef struct zpool_load_policy {
#define ZPOOL_CONFIG_LOAD_DATA_ERRORS "verify_data_errors"
#define ZPOOL_CONFIG_REWIND_TIME "seconds_of_rewind"

#define VDEV_LABELS 4

#define VDEV_TYPE_ROOT "root"
#define VDEV_TYPE_MIRROR "mirror"
#define VDEV_TYPE_REPLACING "replacing"
Expand Down
2 changes: 2 additions & 0 deletions include/sys/nvpair.h
Expand Up @@ -94,6 +94,7 @@ typedef struct nvlist {
#define NV_VERSION 0

/* nvlist pack encoding */
#define NV_ENCODE_INVALID (-1)
#define NV_ENCODE_NATIVE 0
#define NV_ENCODE_XDR 1

Expand Down Expand Up @@ -150,6 +151,7 @@ void nv_alloc_fini(nv_alloc_t *);
/* list management */
int nvlist_alloc(nvlist_t **, uint_t, int);
void nvlist_free(nvlist_t *);
int nvlist_invalidate(char *, size_t);
int nvlist_size(nvlist_t *, size_t *, int);
int nvlist_pack(nvlist_t *, char **, size_t *, int, int);
int nvlist_unpack(char *, size_t, nvlist_t **, int);
Expand Down
2 changes: 0 additions & 2 deletions include/sys/vdev_impl.h
Expand Up @@ -441,8 +441,6 @@ typedef struct vdev_label {
*/
#define VDEV_LABEL_START_SIZE (2 * sizeof (vdev_label_t) + VDEV_BOOT_SIZE)
#define VDEV_LABEL_END_SIZE (2 * sizeof (vdev_label_t))
#define VDEV_LABELS 4
#define VDEV_BEST_LABEL VDEV_LABELS

#define VDEV_ALLOC_LOAD 0
#define VDEV_ALLOC_ADD 1
Expand Down
107 changes: 66 additions & 41 deletions lib/libzfs/libzfs_import.c
Expand Up @@ -137,16 +137,17 @@ label_offset(uint64_t size, int l)
}

/*
* Given a file descriptor, clear (zero) the label information. This function
* is used in the appliance stack as part of the ZFS sysevent module and
* to implement the "zpool labelclear" command.
* Given a file descriptor, a starting label and a number of labels to clear,
* invalidate or clear (zero) the label information. This function is used in
* the appliance stack as part of the ZFS sysevent module and to implement the
* "zpool labelclear" command.
*/
int
zpool_clear_label(int fd)
zpool_clear_n_labels(int fd, unsigned int start, unsigned int n,
boolean_t force, boolean_t wipe)
{
struct stat64 statbuf;
int l;
vdev_label_t *label;
unsigned int l, end;
uint64_t size;
int labels_cleared = 0;

Expand All @@ -155,62 +156,86 @@ zpool_clear_label(int fd)

size = P2ALIGN_TYPED(statbuf.st_size, sizeof (vdev_label_t), uint64_t);

if ((label = calloc(1, sizeof (vdev_label_t))) == NULL)
return (-1);
end = start + n;
if (end > VDEV_LABELS)
return (-1);

for (l = start; l < end; l++) {
vdev_label_t label;
char *nvbuf = label.vl_vdev_phys.vp_nvlist;
size_t nvbuflen = sizeof (label.vl_vdev_phys.vp_nvlist);

for (l = 0; l < VDEV_LABELS; l++) {
uint64_t state, guid;
nvlist_t *config;

if (pread64(fd, label, sizeof (vdev_label_t),
label_offset(size, l)) != sizeof (vdev_label_t)) {
continue;
}

if (nvlist_unpack(label->vl_vdev_phys.vp_nvlist,
sizeof (label->vl_vdev_phys.vp_nvlist), &config, 0) != 0) {
continue;
}
if (!(force && wipe)) {
if (pread64(fd, &label, sizeof (vdev_label_t),
label_offset(size, l)) != sizeof (vdev_label_t)) {
continue;
}

/* Skip labels which do not have a valid guid. */
if (nvlist_lookup_uint64(config, ZPOOL_CONFIG_GUID,
&guid) != 0 || guid == 0) {
nvlist_free(config);
continue;
if (!force) {
if (nvlist_unpack(nvbuf, nvbuflen, &config, 0) != 0) {
continue;
}

/* Skip labels which do not have a valid guid. */
if (nvlist_lookup_uint64(config, ZPOOL_CONFIG_GUID,
&guid) != 0 || guid == 0) {
nvlist_free(config);
continue;
}

/* Skip labels which are not in a known valid state. */
if (nvlist_lookup_uint64(config, ZPOOL_CONFIG_POOL_STATE,
&state) != 0 || state > POOL_STATE_L2CACHE) {
nvlist_free(config);
continue;
}

nvlist_free(config);
}
}

/* Skip labels which are not in a known valid state. */
if (nvlist_lookup_uint64(config, ZPOOL_CONFIG_POOL_STATE,
&state) != 0 || state > POOL_STATE_L2CACHE) {
nvlist_free(config);
continue;
if (wipe) {
/*
* A valid label was found, overwrite this label's nvlist
* and uberblocks with zeros on disk. This is done to prevent
* system utilities, like blkid, from incorrectly detecting a
* partial label. The leading pad space is left untouched.
*/
memset(&label, 0, sizeof (vdev_label_t));
} else {
/*
* Perform minimal on-disk change
*/
if (nvlist_invalidate(nvbuf, nvbuflen) != 0)
continue;
}

nvlist_free(config);

/*
* A valid label was found, overwrite this label's nvlist
* and uberblocks with zeros on disk. This is done to prevent
* system utilities, like blkid, from incorrectly detecting a
* partial label. The leading pad space is left untouched.
*/
memset(label, 0, sizeof (vdev_label_t));
size_t label_size = sizeof (vdev_label_t) - (2 * VDEV_PAD_SIZE);

if (pwrite64(fd, label, label_size, label_offset(size, l) +
(2 * VDEV_PAD_SIZE)) == label_size) {
if (pwrite64(fd, (void*)&label + (2 * VDEV_PAD_SIZE), label_size,
label_offset(size, l) + (2 * VDEV_PAD_SIZE)) == label_size) {
labels_cleared++;
}
}

free(label);

if (labels_cleared == 0)
return (-1);

return (0);
}

/*
* Given a file descriptor, clear (zero) the label information.
*/
int
zpool_clear_label(int fd)
{
return (zpool_clear_n_labels(fd, 0, VDEV_LABELS, B_TRUE, B_TRUE));
}

static boolean_t
find_guid(nvlist_t *nv, uint64_t guid)
{
Expand Down
28 changes: 25 additions & 3 deletions man/man8/zpool.8
Expand Up @@ -128,7 +128,8 @@
.Op Ar interval Op Ar count
.Nm
.Cm labelclear
.Op Fl f
.Op Fl fw
.Op Fl Sy b Ns | Ns Sy e Ns | Ns Sy i Ar index
.Ar device
.Nm
.Cm list
Expand Down Expand Up @@ -1854,17 +1855,38 @@ will be sampled from the end of the interval.
.It Xo
.Nm
.Cm labelclear
.Op Fl f
.Op Fl fw
.Op Fl Sy b Ns | Ns Sy e Ns | Ns Sy i Ar index
.Ar device
.Xc
Removes ZFS label information from the specified
.Ar device .
The
.Ar device
must not be part of an active pool configuration.
Options
.Fl b ,
.Fl e
and
.Fl i
can be used for clearing specific labels.
They are mutually exclusive and the last one will supersede others, if any.
.Bl -tag -width Ds
.It Fl f
Treat exported or foreign devices as inactive.
Force mode: treat exported or foreign devices as inactive.
Specify twice to clear even seemingly-invalid labels.
.It Fl w
Instead of invalidating the label information, wipe label area entirely and
replace it with zeroes.
.It Fl b
Only remove ZFS labels located at the beginning of
.Ar device .
.It Fl e
Only remove ZFS labels located at the end of
.Ar device .
.It Fl i Ar index
Remove a single label entry located at position
.Ar index .
.El
.It Xo
.Nm
Expand Down
13 changes: 13 additions & 0 deletions module/nvpair/nvpair.c
Expand Up @@ -2629,6 +2629,18 @@ nvlist_common(nvlist_t *nvl, char *buf, size_t *buflen, int encoding,
return (err);
}

int
nvlist_invalidate(char *buf, size_t buflen)
{
if (buf == NULL || buflen < sizeof (nvs_header_t))
return (EINVAL);

nvs_header_t *nvh = (void *)buf;
nvh->nvh_encoding = NV_ENCODE_INVALID;

return (0);
}

int
nvlist_size(nvlist_t *nvl, size_t *size, int encoding)
{
Expand Down Expand Up @@ -3656,6 +3668,7 @@ EXPORT_SYMBOL(nvlist_prev_nvpair);
EXPORT_SYMBOL(nvlist_empty);
EXPORT_SYMBOL(nvlist_add_hrtime);

EXPORT_SYMBOL(nvlist_invalidate);
EXPORT_SYMBOL(nvlist_remove);
EXPORT_SYMBOL(nvlist_remove_nvpair);
EXPORT_SYMBOL(nvlist_remove_all);
Expand Down

0 comments on commit 7c62008

Please sign in to comment.