6721094 Setting certain properties on root pools should not be allowed
authorgw25295
Mon, 07 Jul 2008 10:11:14 -0700
changeset 7042 46fc4b6db23e
parent 7041 b4c5fe87fad8
child 7043 22affca31e0f
6721094 Setting certain properties on root pools should not be allowed
usr/src/grub/grub-0.95/stage2/fsys_zfs.c
usr/src/lib/libzfs/common/libzfs.h
usr/src/lib/libzfs/common/libzfs_dataset.c
usr/src/lib/libzfs/common/libzfs_pool.c
usr/src/lib/libzfs/common/libzfs_util.c
usr/src/uts/common/fs/zfs/spa.c
usr/src/uts/common/fs/zfs/sys/spa_impl.h
usr/src/uts/common/fs/zfs/sys/vdev.h
usr/src/uts/common/fs/zfs/vdev.c
usr/src/uts/common/fs/zfs/zfs_ioctl.c
--- a/usr/src/grub/grub-0.95/stage2/fsys_zfs.c	Fri Jul 04 13:08:26 2008 -0700
+++ b/usr/src/grub/grub-0.95/stage2/fsys_zfs.c	Mon Jul 07 10:11:14 2008 -0700
@@ -64,10 +64,11 @@
 
 decomp_entry_t decomp_table[ZIO_COMPRESS_FUNCTIONS] =
 {
-	{"noop", 0},
+	{"inherit", 0},			/* ZIO_COMPRESS_INHERIT */
 	{"on", lzjb_decompress}, 	/* ZIO_COMPRESS_ON */
-	{"off", 0},
-	{"lzjb", lzjb_decompress}	/* ZIO_COMPRESS_LZJB */
+	{"off", 0},			/* ZIO_COMPRESS_OFF */
+	{"lzjb", lzjb_decompress},	/* ZIO_COMPRESS_LZJB */
+	{"empty", 0}			/* ZIO_COMPRESS_EMPTY */
 };
 
 /*
--- a/usr/src/lib/libzfs/common/libzfs.h	Fri Jul 04 13:08:26 2008 -0700
+++ b/usr/src/lib/libzfs/common/libzfs.h	Mon Jul 07 10:11:14 2008 -0700
@@ -115,6 +115,7 @@
 	EZFS_BADCACHE,		/* bad cache file */
 	EZFS_ISL2CACHE,		/* device is for the level 2 ARC */
 	EZFS_VDEVNOTSUP,	/* unsupported vdev type */
+	EZFS_NOTSUP,		/* ops not supported on this dataset */
 	EZFS_UNKNOWN
 };
 
--- a/usr/src/lib/libzfs/common/libzfs_dataset.c	Fri Jul 04 13:08:26 2008 -0700
+++ b/usr/src/lib/libzfs/common/libzfs_dataset.c	Mon Jul 07 10:11:14 2008 -0700
@@ -1868,6 +1868,17 @@
 			(void) zfs_error(hdl, EZFS_BADVERSION, errbuf);
 			break;
 
+		case ERANGE:
+			if (prop == ZFS_PROP_COMPRESSION) {
+				(void) zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+				    "property setting is not allowed on "
+				    "bootable datasets"));
+				(void) zfs_error(hdl, EZFS_NOTSUP, errbuf);
+			} else {
+				(void) zfs_standard_error(hdl, errno, errbuf);
+			}
+			break;
+
 		case EOVERFLOW:
 			/*
 			 * This platform can't address a volume this big.
--- a/usr/src/lib/libzfs/common/libzfs_pool.c	Fri Jul 04 13:08:26 2008 -0700
+++ b/usr/src/lib/libzfs/common/libzfs_pool.c	Mon Jul 07 10:11:14 2008 -0700
@@ -48,6 +48,7 @@
 #include "zfs_prop.h"
 #include "libzfs_impl.h"
 
+static int read_efi_label(nvlist_t *config, diskaddr_t *sb);
 
 /*
  * ====================================================================
@@ -284,6 +285,27 @@
 }
 
 /*
+ * Inspect the configuration to determine if any of the devices contain
+ * an EFI label.
+ */
+static boolean_t
+pool_uses_efi(nvlist_t *config)
+{
+	nvlist_t **child;
+	uint_t c, children;
+
+	if (nvlist_lookup_nvlist_array(config, ZPOOL_CONFIG_CHILDREN,
+	    &child, &children) != 0)
+		return (read_efi_label(config, NULL) >= 0);
+
+	for (c = 0; c < children; c++) {
+		if (pool_uses_efi(child[c]))
+			return (B_TRUE);
+	}
+	return (B_FALSE);
+}
+
+/*
  * Given an nvlist of zpool properties to be set, validate that they are
  * correct, and parse any numeric properties (index, boolean, etc) if they are
  * specified as strings.
@@ -299,6 +321,8 @@
 	uint64_t intval;
 	char *slash;
 	struct stat64 statbuf;
+	zpool_handle_t *zhp;
+	nvlist_t *nvroot;
 
 	if (nvlist_alloc(&retprops, NV_UNIQUE_NAME, 0) != 0) {
 		(void) no_memory(hdl);
@@ -372,6 +396,29 @@
 				(void) zfs_error(hdl, EZFS_INVALIDNAME, errbuf);
 				goto error;
 			}
+
+			if ((zhp = zpool_open_canfail(hdl, poolname)) == NULL) {
+				zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+				    "could not open pool '%s'"), poolname);
+				(void) zfs_error(hdl, EZFS_OPENFAILED, errbuf);
+				goto error;
+			}
+			verify(nvlist_lookup_nvlist(zpool_get_config(zhp, NULL),
+			    ZPOOL_CONFIG_VDEV_TREE, &nvroot) == 0);
+
+			/*
+			 * bootfs property cannot be set on a disk which has
+			 * been EFI labeled.
+			 */
+			if (pool_uses_efi(nvroot)) {
+				zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+				    "property '%s' not supported on "
+				    "EFI labeled devices"), propname);
+				(void) zfs_error(hdl, EZFS_POOL_NOTSUP, errbuf);
+				zpool_close(zhp);
+				goto error;
+			}
+			zpool_close(zhp);
 			break;
 
 		case ZPOOL_PROP_ALTROOT:
@@ -2502,6 +2549,38 @@
 #define	NEW_START_BLOCK	256
 
 /*
+ * Read the EFI label from the config, if a label does not exist then
+ * pass back the error to the caller. If the caller has passed a non-NULL
+ * diskaddr argument then we set it to the starting address of the EFI
+ * partition.
+ */
+static int
+read_efi_label(nvlist_t *config, diskaddr_t *sb)
+{
+	char *path;
+	int fd;
+	char diskname[MAXPATHLEN];
+	int err = -1;
+
+	if (nvlist_lookup_string(config, ZPOOL_CONFIG_PATH, &path) != 0)
+		return (err);
+
+	(void) snprintf(diskname, sizeof (diskname), "%s%s", RDISK_ROOT,
+	    strrchr(path, '/'));
+	if ((fd = open(diskname, O_RDONLY|O_NDELAY)) >= 0) {
+		struct dk_gpt *vtoc;
+
+		if ((err = efi_alloc_and_read(fd, &vtoc)) >= 0) {
+			if (sb != NULL)
+				*sb = vtoc->efi_parts[0].p_start;
+			efi_free(vtoc);
+		}
+		(void) close(fd);
+	}
+	return (err);
+}
+
+/*
  * determine where a partition starts on a disk in the current
  * configuration
  */
@@ -2510,10 +2589,7 @@
 {
 	nvlist_t **child;
 	uint_t c, children;
-	char *path;
 	diskaddr_t sb = MAXOFFSET_T;
-	int fd;
-	char diskname[MAXPATHLEN];
 	uint64_t wholedisk;
 
 	if (nvlist_lookup_nvlist_array(config,
@@ -2523,21 +2599,8 @@
 		    &wholedisk) != 0 || !wholedisk) {
 			return (MAXOFFSET_T);
 		}
-		if (nvlist_lookup_string(config,
-		    ZPOOL_CONFIG_PATH, &path) != 0) {
-			return (MAXOFFSET_T);
-		}
-
-		(void) snprintf(diskname, sizeof (diskname), "%s%s",
-		    RDISK_ROOT, strrchr(path, '/'));
-		if ((fd = open(diskname, O_RDONLY|O_NDELAY)) >= 0) {
-			struct dk_gpt *vtoc;
-			if (efi_alloc_and_read(fd, &vtoc) >= 0) {
-				sb = vtoc->efi_parts[0].p_start;
-				efi_free(vtoc);
-			}
-			(void) close(fd);
-		}
+		if (read_efi_label(config, &sb) < 0)
+			sb = MAXOFFSET_T;
 		return (sb);
 	}
 
--- a/usr/src/lib/libzfs/common/libzfs_util.c	Fri Jul 04 13:08:26 2008 -0700
+++ b/usr/src/lib/libzfs/common/libzfs_util.c	Mon Jul 07 10:11:14 2008 -0700
@@ -206,6 +206,9 @@
 	case EZFS_VDEVNOTSUP:
 		return (dgettext(TEXT_DOMAIN, "vdev specification is not "
 		    "supported"));
+	case EZFS_NOTSUP:
+		return (dgettext(TEXT_DOMAIN, "operation not supported "
+		    "on this dataset"));
 	case EZFS_UNKNOWN:
 		return (dgettext(TEXT_DOMAIN, "unknown error"));
 	default:
--- a/usr/src/uts/common/fs/zfs/spa.c	Fri Jul 04 13:08:26 2008 -0700
+++ b/usr/src/uts/common/fs/zfs/spa.c	Mon Jul 07 10:11:14 2008 -0700
@@ -271,8 +271,6 @@
 		zpool_prop_t prop;
 		char *propname, *strval;
 		uint64_t intval;
-		vdev_t *rvdev;
-		char *vdev_type;
 		objset_t *os;
 		char *slash;
 
@@ -303,15 +301,9 @@
 			}
 
 			/*
-			 * A bootable filesystem can not be on a RAIDZ pool
-			 * nor a striped pool with more than 1 device.
+			 * Make sure the vdev config is bootable
 			 */
-			rvdev = spa->spa_root_vdev;
-			vdev_type =
-			    rvdev->vdev_child[0]->vdev_ops->vdev_op_type;
-			if (rvdev->vdev_children > 1 ||
-			    strcmp(vdev_type, VDEV_TYPE_RAIDZ) == 0 ||
-			    strcmp(vdev_type, VDEV_TYPE_MISSING) == 0) {
+			if (!vdev_is_bootable(spa->spa_root_vdev)) {
 				error = ENOTSUP;
 				break;
 			}
@@ -321,6 +313,8 @@
 			error = nvpair_value_string(elem, &strval);
 
 			if (!error) {
+				uint64_t compress;
+
 				if (strval == NULL || strval[0] == '\0') {
 					objnum = zpool_prop_default_numeric(
 					    ZPOOL_PROP_BOOTFS);
@@ -330,7 +324,16 @@
 				if (error = dmu_objset_open(strval, DMU_OST_ZFS,
 				    DS_MODE_USER | DS_MODE_READONLY, &os))
 					break;
-				objnum = dmu_objset_id(os);
+
+				/* We don't support gzip bootable datasets */
+				if ((error = dsl_prop_get_integer(strval,
+				    zfs_prop_to_name(ZFS_PROP_COMPRESSION),
+				    &compress, NULL)) == 0 &&
+				    !BOOTFS_COMPRESS_VALID(compress)) {
+					error = ENOTSUP;
+				} else {
+					objnum = dmu_objset_id(os);
+				}
 				dmu_objset_close(os);
 			}
 			break;
--- a/usr/src/uts/common/fs/zfs/sys/spa_impl.h	Fri Jul 04 13:08:26 2008 -0700
+++ b/usr/src/uts/common/fs/zfs/sys/spa_impl.h	Mon Jul 07 10:11:14 2008 -0700
@@ -176,6 +176,12 @@
 
 extern const char *spa_config_path;
 
+#define	BOOTFS_COMPRESS_VALID(compress) \
+	((compress) == ZIO_COMPRESS_LZJB || \
+	((compress) == ZIO_COMPRESS_ON && \
+	ZIO_COMPRESS_ON_VALUE == ZIO_COMPRESS_LZJB) || \
+	(compress) == ZIO_COMPRESS_OFF)
+
 #ifdef	__cplusplus
 }
 #endif
--- a/usr/src/uts/common/fs/zfs/sys/vdev.h	Fri Jul 04 13:08:26 2008 -0700
+++ b/usr/src/uts/common/fs/zfs/sys/vdev.h	Mon Jul 07 10:11:14 2008 -0700
@@ -56,6 +56,7 @@
 extern int vdev_validate_aux(vdev_t *vd);
 extern int vdev_probe(vdev_t *);
 
+extern boolean_t vdev_is_bootable(vdev_t *vd);
 extern vdev_t *vdev_lookup_top(spa_t *spa, uint64_t vdev);
 extern vdev_t *vdev_lookup_by_guid(vdev_t *vd, uint64_t guid);
 extern void vdev_dtl_dirty(space_map_t *sm, uint64_t txg, uint64_t size);
--- a/usr/src/uts/common/fs/zfs/vdev.c	Fri Jul 04 13:08:26 2008 -0700
+++ b/usr/src/uts/common/fs/zfs/vdev.c	Mon Jul 07 10:11:14 2008 -0700
@@ -2264,3 +2264,35 @@
 	if (!isopen)
 		vdev_propagate_state(vd);
 }
+
+/*
+ * Check the vdev configuration to ensure that it's capable of supporting
+ * a root pool. Currently, we do not support RAID-Z or partial configuration.
+ * In addition, only a single top-level vdev is allowed and none of the leaves
+ * can be wholedisks.
+ */
+boolean_t
+vdev_is_bootable(vdev_t *vd)
+{
+	int c;
+
+	if (!vd->vdev_ops->vdev_op_leaf) {
+		char *vdev_type = vd->vdev_ops->vdev_op_type;
+
+		if (strcmp(vdev_type, VDEV_TYPE_ROOT) == 0 &&
+		    vd->vdev_children > 1) {
+			return (B_FALSE);
+		} else if (strcmp(vdev_type, VDEV_TYPE_RAIDZ) == 0 ||
+		    strcmp(vdev_type, VDEV_TYPE_MISSING) == 0) {
+			return (B_FALSE);
+		}
+	} else if (vd->vdev_wholedisk == 1) {
+		return (B_FALSE);
+	}
+
+	for (c = 0; c < vd->vdev_children; c++) {
+		if (!vdev_is_bootable(vd->vdev_child[c]))
+			return (B_FALSE);
+	}
+	return (B_TRUE);
+}
--- a/usr/src/uts/common/fs/zfs/zfs_ioctl.c	Fri Jul 04 13:08:26 2008 -0700
+++ b/usr/src/uts/common/fs/zfs/zfs_ioctl.c	Mon Jul 07 10:11:14 2008 -0700
@@ -155,6 +155,30 @@
 }
 
 /*
+ * Check to see if the named dataset is currently defined as bootable
+ */
+static boolean_t
+zfs_is_bootfs(const char *name)
+{
+	spa_t *spa;
+	boolean_t ret = B_FALSE;
+
+	if (spa_open(name, &spa, FTAG) == 0) {
+		if (spa->spa_bootfs) {
+			objset_t *os;
+
+			if (dmu_objset_open(name, DMU_OST_ZFS,
+			    DS_MODE_USER | DS_MODE_READONLY, &os) == 0) {
+				ret = (dmu_objset_id(os) == spa->spa_bootfs);
+				dmu_objset_close(os);
+			}
+		}
+		spa_close(spa, FTAG);
+	}
+	return (ret);
+}
+
+/*
  * zfs_check_version
  *
  *	Return non-zero if the spa version is less than requested version.
@@ -1370,12 +1394,23 @@
 			 * we'll catch them later.
 			 */
 			if (nvpair_type(elem) == DATA_TYPE_UINT64 &&
-			    nvpair_value_uint64(elem, &intval) == 0 &&
-			    intval >= ZIO_COMPRESS_GZIP_1 &&
-			    intval <= ZIO_COMPRESS_GZIP_9) {
-				if (zfs_check_version(name,
+			    nvpair_value_uint64(elem, &intval) == 0) {
+				if (intval >= ZIO_COMPRESS_GZIP_1 &&
+				    intval <= ZIO_COMPRESS_GZIP_9 &&
+				    zfs_check_version(name,
 				    SPA_VERSION_GZIP_COMPRESSION))
 					return (ENOTSUP);
+
+				/*
+				 * If this is a bootable dataset then
+				 * verify that the compression algorithm
+				 * is supported for booting. We must return
+				 * something other than ENOTSUP since it
+				 * implies a downrev pool version.
+				 */
+				if (zfs_is_bootfs(name) &&
+				    !BOOTFS_COMPRESS_VALID(intval))
+					return (ERANGE);
 			}
 			break;