--- cddl/contrib/opensolaris/cmd/zpool/zpool.8 (revision 303655) +++ cddl/contrib/opensolaris/cmd/zpool/zpool.8 (working copy) @@ -119,6 +119,9 @@ .Ar ... .Nm .Cm labelclear +.Op Fl b | Fl e | Fl i Ar index +.Op Fl c +.Op Fl m .Op Fl f .Ar device .Nm @@ -1350,6 +1353,9 @@ .It Xo .Nm .Cm labelclear +.Op Fl b | Fl e | Fl i Ar index +.Op Fl c +.Op Fl m .Op Fl f .Ar device .Xc @@ -1360,8 +1366,29 @@ .Ar device . The .Ar device -must not be part of an active pool configuration. +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 indent +.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 . +.It Fl c +Check label integrity before doing anything. +.It Fl m +Perform minimal operation needed to invalidate a label instead of blindly +erasing every single byte. This reduces (but does not annihilate) the risks of +overriding important on-disk data. .It Fl f Treat exported or foreign devices as inactive. .El --- cddl/contrib/opensolaris/cmd/zpool/zpool_main.c (revision 303655) +++ cddl/contrib/opensolaris/cmd/zpool/zpool_main.c (working copy) @@ -38,6 +38,7 @@ #include #include #include +#include #include #include #include @@ -50,6 +51,7 @@ #include #include #include +#include #include #include @@ -236,7 +238,8 @@ return (gettext("\tiostat [-v] [-T d|u] [pool] ... [interval " "[count]]\n")); case HELP_LABELCLEAR: - return (gettext("\tlabelclear [-f] \n")); + return (gettext("\tlabelclear [-b | -e | -i index] [-c] [-m] " + "[-f] \n")); case HELP_LIST: return (gettext("\tlist [-Hpv] [-o property[,...]] " "[-T d|u] [pool] ... [interval [count]]\n")); @@ -645,10 +648,40 @@ pool_state_t state; boolean_t inuse = B_FALSE; boolean_t force = B_FALSE; + boolean_t check = B_FALSE; + boolean_t cherry = 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:cmf")) != -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 'c': + check = B_TRUE; + break; + case 'm': + cherry = B_TRUE; + break; case 'f': force = B_TRUE; break; @@ -705,8 +738,12 @@ } if (zpool_read_label(fd, &config) != 0 || config == NULL) { - (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); return (1); } nvlist_free(config); @@ -759,7 +796,7 @@ } wipe_label: - ret = zpool_clear_label(fd); + ret = zpool_clear_n_labels(fd, start, n, check, cherry); if (ret != 0) { (void) fprintf(stderr, gettext("failed to clear label for %s\n"), vdev); --- cddl/contrib/opensolaris/lib/libzfs/common/libzfs.h (revision 303655) +++ cddl/contrib/opensolaris/lib/libzfs/common/libzfs.h (working copy) @@ -769,6 +769,7 @@ * Label manipulation. */ extern int zpool_read_label(int, nvlist_t **); +extern int zpool_clear_n_labels(int, unsigned int, unsigned int, boolean_t, boolean_t); extern int zpool_clear_label(int); /* is this zvol valid for use as a dump device? */ --- cddl/contrib/opensolaris/lib/libzfs/common/libzfs_import.c (revision 303655) +++ cddl/contrib/opensolaris/lib/libzfs/common/libzfs_import.c (working copy) @@ -1099,36 +1099,68 @@ } /* - * Given a file descriptor, clear (zero) the label information. + * Given a file descriptor, a starting label and a number of labels to clear, + * clear (zero) the label information. */ int -zpool_clear_label(int fd) +zpool_clear_n_labels(int fd, unsigned int start, unsigned int n, + boolean_t check, boolean_t cherry) { struct stat64 statbuf; - int l; - vdev_label_t *label; + unsigned int l, end; + vdev_label_t label; uint64_t size; + char *buf = label.vl_vdev_phys.vp_nvlist; + size_t buflen = sizeof (label.vl_vdev_phys.vp_nvlist); + if (fstat64(fd, &statbuf) == -1) return (0); size = P2ALIGN_TYPED(statbuf.st_size, sizeof (vdev_label_t), uint64_t); - if ((label = calloc(sizeof (vdev_label_t), 1)) == NULL) + end = start + n; + if (end > VDEV_LABELS) return (-1); - for (l = 0; l < VDEV_LABELS; l++) { - if (pwrite64(fd, label, sizeof (vdev_label_t), - label_offset(size, l)) != sizeof (vdev_label_t)) { - free(label); + for (l = start; l < end; l++) { + if ((check == B_TRUE) || (cherry == B_TRUE)) { + if (pread64(fd, &label, sizeof (vdev_label_t), + label_offset(size, l)) != sizeof (vdev_label_t)) + return (-1); + + if (check == B_TRUE) { + nvlist_t *config = NULL; + if (nvlist_unpack(buf, buflen, &config, 0) != 0) + return (-1); + nvlist_free(config); + } + } + + if (cherry == B_TRUE) { + if (nvlist_invalidate(buf) != 0) + return (-1); + } else { + memset(&label, 0, sizeof (vdev_label_t)); + } + + if (pwrite64(fd, &label, sizeof (vdev_label_t), + label_offset(size, l)) != sizeof (vdev_label_t)) return (-1); - } } - free(label); 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_FALSE, B_FALSE)); +} + +/* * Given a list of directories to search, find all pools stored on disk. This * includes partial pools which are not available to import. If no args are * given (argc is 0), then the default directory (/dev/dsk) is searched. --- sys/cddl/contrib/opensolaris/common/nvpair/opensolaris_nvpair.c (revision 303655) +++ sys/cddl/contrib/opensolaris/common/nvpair/opensolaris_nvpair.c (working copy) @@ -2335,6 +2335,18 @@ } int +nvlist_invalidate(char *buf) +{ + if (buf == NULL) + 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) { return (nvlist_common(nvl, NULL, size, encoding, NVS_OP_GETSIZE)); --- sys/cddl/contrib/opensolaris/uts/common/sys/nvpair.h (revision 303655) +++ sys/cddl/contrib/opensolaris/uts/common/sys/nvpair.h (working copy) @@ -97,6 +97,7 @@ #define NV_VERSION 0 /* nvlist pack encoding */ +#define NV_ENCODE_INVALID (-1) #define NV_ENCODE_NATIVE 0 #define NV_ENCODE_XDR 1 @@ -152,6 +153,7 @@ /* list management */ int nvlist_alloc(nvlist_t **, uint_t, int); void nvlist_free(nvlist_t *); +int nvlist_invalidate(char *); 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);