[CFR][ZFS] Add "zpool labelclear" command.

Justin T. Gibbs gibbs at scsiguy.com
Tue Jun 14 21:08:38 UTC 2011


ZFS rightfully has a lot of safety belts in place to ward off unintended
data loss.  But in some scenarios, the safety belts are so restrictive,
the only way to proceed is to wipe the label information off of a drive.

Here's an example:

Pull a drive that is active in a pool on one system and stick it into
another system.  ZFS will correctly reject this drive as a member of
a new pool or as the argument of a replace command.  But if you really
want to use that drive, how do you clear it's "potentially active"
status?  If the pool were imported, you could destroy it, but ZFS wont
allow you to import a pool unless there are sufficient members for it
to serve I/O (I know about the undocumented -F option for import,
but users aren't going to find that).  You can use dd to wipe the label
data off, but where exactly does ZFS keep its for copies of the label?

"zpool labelclear" allows the user to unbuckle a few seatbelts in a
controlled manner.  Without the "-f" flag, it will allow you to wipe
label data from a destroyed pool.  With the "-f" flag, it will allow
you to wipe metadata from "potentially active" vdevs - vdevs that are
part of an exported pool, or are listed as active in a pool that is not
mounted.  It will not allow you to clear the label data from a vdev
that is active in the system.

Note: there are some const correctness fixes to zpool_state_to_name()
in these patches.  I added a zpool_pool_state_to_name() method with
a const correct signature, and it made sense to me to update the sibling
method at the same time.

--
Justin

-------------- next part --------------
diff -u -r -x cscope.out -x out -x ctl -x compile vendor/FreeBSD/head/cddl/contrib/opensolaris/cmd/zpool/zpool.8 SpectraBSD/head/cddl/contrib/opensolaris/cmd/zpool/zpool.8
--- vendor/FreeBSD/head/cddl/contrib/opensolaris/cmd/zpool/zpool.8	2011-02-28 13:51:22.118582660 -0700
+++ SpectraBSD/head/cddl/contrib/opensolaris/cmd/zpool/zpool.8	2011-06-06 17:28:33.578173928 -0600
@@ -82,6 +82,11 @@
 
 .LP
 .nf
+\fBzpool labelclear\fR [\fB-f\fR] \fIdevice\fR
+.fi
+
+.LP
+.nf
 \fBzpool list\fR [\fB-H\fR] [\fB-o\fR \fIproperty\fR[,...]] [\fIpool\fR] ...
 .fi
 
@@ -1205,6 +1210,28 @@
 .ne 2
 .mk
 .na
+\fB\fBzpool labelclear\fR [\fB-f\fR] \fIdevice\fR
+.ad
+.sp .6
+.RS 4n
+Removes ZFS label information from the specified device. The device must not be part of an active pool configuration.
+.sp
+.ne 2
+.mk
+.na
+\fB\fB-f\fR\fR
+.ad
+.RS 12n
+.rt  
+Treat exported or foreign devices as inactive.
+.RE
+
+.RE
+
+.sp
+.ne 2
+.mk
+.na
 \fB\fBzpool list\fR [\fB-H\fR] [\fB-o\fR \fIprops\fR[,...]] [\fIpool\fR] ...\fR
 .ad
 .sp .6
diff -u -r -x cscope.out -x out -x ctl -x compile vendor/FreeBSD/head/cddl/contrib/opensolaris/cmd/zpool/zpool_main.c SpectraBSD/head/cddl/contrib/opensolaris/cmd/zpool/zpool_main.c
--- vendor/FreeBSD/head/cddl/contrib/opensolaris/cmd/zpool/zpool_main.c	2011-02-28 13:51:22.120585187 -0700
+++ SpectraBSD/head/cddl/contrib/opensolaris/cmd/zpool/zpool_main.c	2011-06-08 17:22:53.450540438 -0600
@@ -57,6 +57,7 @@
 
 static int zpool_do_add(int, char **);
 static int zpool_do_remove(int, char **);
+static int zpool_do_labelclear(int, char **);
 
 static int zpool_do_list(int, char **);
 static int zpool_do_iostat(int, char **);
@@ -113,6 +114,7 @@
 	HELP_HISTORY,
 	HELP_IMPORT,
 	HELP_IOSTAT,
+	HELP_LABELCLEAR,
 	HELP_LIST,
 	HELP_OFFLINE,
 	HELP_ONLINE,
@@ -149,6 +151,8 @@
 	{ "add",	zpool_do_add,		HELP_ADD		},
 	{ "remove",	zpool_do_remove,	HELP_REMOVE		},
 	{ NULL },
+	{ "labelclear",	zpool_do_labelclear,	HELP_LABELCLEAR		},
+	{ NULL },
 	{ "list",	zpool_do_list,		HELP_LIST		},
 	{ "iostat",	zpool_do_iostat,	HELP_IOSTAT		},
 	{ "status",	zpool_do_status,	HELP_STATUS		},
@@ -215,6 +219,8 @@
 	case HELP_IOSTAT:
 		return (gettext("\tiostat [-v] [-T d|u] [pool] ... [interval "
 		    "[count]]\n"));
+	case HELP_LABELCLEAR:
+		return (gettext("\tlabelclear [-f] <vdev>\n"));
 	case HELP_LIST:
 		return (gettext("\tlist [-H] [-o property[,...]] "
 		    "[-T d|u] [pool] ... [interval [count]]\n"));
@@ -561,6 +567,125 @@
 }
 
 /*
+ * zpool labelclear <vdev>
+ *
+ * Verifies that the vdev is not active and zeros out the label information
+ * on the device.
+ */
+int
+zpool_do_labelclear(int argc, char **argv)
+{
+	char *vdev, *name;
+	int c, fd = -1, ret = 0;
+	pool_state_t state;
+	boolean_t inuse = B_FALSE;
+	boolean_t force = B_FALSE;
+
+	/* check options */
+	while ((c = getopt(argc, argv, "f")) != -1) {
+		switch (c) {
+		case 'f':
+			force = B_TRUE;
+			break;
+		default:
+			(void) fprintf(stderr, gettext("invalid option '%c'\n"),
+			    optopt);
+			usage(B_FALSE);
+		}
+	}
+
+	argc -= optind;
+	argv += optind;
+
+	/* get vdev name */
+	if (argc < 1) {
+		(void) fprintf(stderr, gettext("missing vdev device name\n"));
+		usage(B_FALSE);
+	}
+
+	vdev = argv[0];
+	if ((fd = open(vdev, O_RDWR)) < 0) {
+		(void) fprintf(stderr, gettext("Unable to open %s\n"), vdev);
+		return (B_FALSE);
+	}
+
+	name = NULL;
+	if (zpool_in_use(g_zfs, fd, &state, &name, &inuse) != 0) {
+		if (force)
+			goto wipe_label;
+		
+		(void) fprintf(stderr,
+		    gettext("Unable to determine pool state for %s\n"
+		    "Use -f to force the clearing any label data\n"), vdev);
+
+		return (1);
+	}
+
+	if (inuse) {
+		switch (state) {
+		default:
+		case POOL_STATE_ACTIVE:
+		case POOL_STATE_SPARE:
+		case POOL_STATE_L2CACHE:
+			(void) fprintf(stderr,
+gettext("labelclear operation failed.\n"
+	"\tVdev %s is a member (%s), of pool \"%s\".\n"
+	"\tTo remove label information from this device, export or destroy\n"
+	"\tthe pool, or remove %s from the configuration of this pool\n"
+	"\tand retry the labelclear operation\n"),
+			    vdev, zpool_pool_state_to_name(state), name, vdev);
+			ret = 1;
+			goto errout;
+
+		case POOL_STATE_EXPORTED:
+			if (force)
+				break;
+
+			(void) fprintf(stderr,
+gettext("labelclear operation failed.\n"
+	"\tVdev %s is a member of the exported pool \"%s\".\n"
+	"\tUse \"zpool labelclear -f %s\" to force the removal of label\n"
+	"\tinformation.\n"),
+			    vdev, name, vdev);
+			ret = 1;
+			goto errout;
+
+		case POOL_STATE_POTENTIALLY_ACTIVE:
+			if (force)
+				break;
+
+			(void) fprintf(stderr,
+gettext("labelclear operation failed.\n"
+	"\tVdev %s is a member of the pool \"%s\".\n"
+	"\tThis pool is unknown to this system, but may be active on\n"
+	"\tanother system. Use \'zpool labelclear -f %s\' to force the\n"
+	"\tremoval of label information.\n"),
+			    vdev, name, vdev);
+			ret = 1;
+			goto errout;
+
+		case POOL_STATE_DESTROYED:
+			/* inuse should never be set for a destoryed pool... */
+			break;
+		}
+	}
+
+wipe_label:
+	if (zpool_clear_label(fd) != 0) {
+		(void) fprintf(stderr,
+		    gettext("Label clear failed on vdev %s\n"), vdev);
+		ret = 1;
+	}
+
+errout:
+	close(fd);
+	if (name != NULL)
+		free(name);
+
+	return (ret);
+}
+
+/*
  * zpool create [-fn] [-o property=value] ...
  *		[-O file-system-property=value] ...
  *		[-R root] [-m mountpoint] <pool> <dev> ...
@@ -1052,7 +1177,7 @@
 	char *vname;
 	uint64_t notpresent;
 	spare_cbdata_t cb;
-	char *state;
+	const char *state;
 
 	if (nvlist_lookup_nvlist_array(nv, ZPOOL_CONFIG_CHILDREN,
 	    &child, &children) != 0)
diff -u -r -x cscope.out -x out -x ctl -x compile vendor/FreeBSD/head/cddl/contrib/opensolaris/lib/libzfs/common/libzfs.h SpectraBSD/head/cddl/contrib/opensolaris/lib/libzfs/common/libzfs.h
--- vendor/FreeBSD/head/cddl/contrib/opensolaris/lib/libzfs/common/libzfs.h	2011-02-28 13:51:22.250862280 -0700
+++ SpectraBSD/head/cddl/contrib/opensolaris/lib/libzfs/common/libzfs.h	2011-06-07 13:59:20.387329174 -0600
@@ -200,7 +200,8 @@
 extern void zpool_close(zpool_handle_t *);
 extern const char *zpool_get_name(zpool_handle_t *);
 extern int zpool_get_state(zpool_handle_t *);
-extern char *zpool_state_to_name(vdev_state_t, vdev_aux_t);
+extern const char *zpool_state_to_name(vdev_state_t, vdev_aux_t);
+extern const char *zpool_pool_state_to_name(pool_state_t);
 extern void zpool_free_handles(libzfs_handle_t *);
 
 /*
@@ -249,7 +250,7 @@
     boolean_t *, boolean_t *);
 extern nvlist_t *zpool_find_vdev_by_physpath(zpool_handle_t *, const char *,
     boolean_t *, boolean_t *, boolean_t *);
-extern int zpool_label_disk(libzfs_handle_t *, zpool_handle_t *, char *);
+extern int zpool_label_disk(libzfs_handle_t *, zpool_handle_t *, const char *);
 
 /*
  * Functions to manage pool properties
diff -u -r -x cscope.out -x out -x ctl -x compile vendor/FreeBSD/head/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_import.c SpectraBSD/head/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_import.c
--- vendor/FreeBSD/head/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_import.c	2011-02-28 13:51:22.254931589 -0700
+++ SpectraBSD/head/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_import.c	2011-06-08 17:16:38.396640386 -0600
@@ -1084,8 +1084,8 @@
 
 /*
  * Given a file descriptor, clear (zero) the label information.  This function
- * is currently only used in the appliance stack as part of the ZFS sysevent
- * module.
+ * 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)
diff -u -r -x cscope.out -x out -x ctl -x compile vendor/FreeBSD/head/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_pool.c SpectraBSD/head/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_pool.c
--- vendor/FreeBSD/head/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_pool.c	2011-02-28 13:51:22.272292499 -0700
+++ SpectraBSD/head/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_pool.c	2011-06-06 17:32:19.581512402 -0600
@@ -174,7 +174,7 @@
 /*
  * Map VDEV STATE to printed strings.
  */
-char *
+const char *
 zpool_state_to_name(vdev_state_t state, vdev_aux_t aux)
 {
 	switch (state) {
@@ -202,6 +202,34 @@
 }
 
 /*
+ * Map POOL STATE to printed strings.
+ */
+const char *
+zpool_pool_state_to_name(pool_state_t state)
+{
+	switch (state) {
+	case POOL_STATE_ACTIVE:
+		return (gettext("ACTIVE"));
+	case POOL_STATE_EXPORTED:
+		return (gettext("EXPORTED"));
+	case POOL_STATE_DESTROYED:
+		return (gettext("DESTROYED"));
+	case POOL_STATE_SPARE:
+		return (gettext("SPARE"));
+	case POOL_STATE_L2CACHE:
+		return (gettext("L2CACHE"));
+	case POOL_STATE_UNINITIALIZED:
+		return (gettext("UNINITIALIZED"));
+	case POOL_STATE_UNAVAIL:
+		return (gettext("UNAVAIL"));
+	case POOL_STATE_POTENTIALLY_ACTIVE:
+		return (gettext("POTENTIALLY_ACTIVE"));
+	}
+
+	return (gettext("UNKNOWN"));
+}
+
+/*
  * Get a zpool property value for 'prop' and return the value in
  * a pre-allocated buffer.
  */
@@ -3605,7 +3642,7 @@
  * stripped of any leading /dev path.
  */
 int
-zpool_label_disk(libzfs_handle_t *hdl, zpool_handle_t *zhp, char *name)
+zpool_label_disk(libzfs_handle_t *hdl, zpool_handle_t *zhp, const char *name)
 {
 #ifdef sun
 	char path[MAXPATHLEN];


More information about the freebsd-fs mailing list