changeset 14163:712ede127bb4

3845 beadm doesn't work in non-global zones Reviewed by: Hans Rosenfeld <hans.rosenfeld@nexenta.com> Reviewed by: Andrew Stormont <andyjstormont@gmail.com> Reviewed by: Richard Lowe <richlowe@richlowe.net> Approved by: Dan McDonald <danmcd@nexenta.com>
author Alexander Eremin <a.eremin@nexenta.com>
date Mon, 26 Aug 2013 13:52:20 -0400
parents b876bad2811a
children dceb17481b99
files usr/src/cmd/beadm/beadm.c usr/src/lib/libbe/common/be_activate.c usr/src/lib/libbe/common/be_create.c usr/src/lib/libbe/common/be_list.c usr/src/lib/libbe/common/be_mount.c usr/src/lib/libbe/common/be_snapshot.c usr/src/lib/libbe/common/be_utils.c usr/src/lib/libbe/common/be_zones.c usr/src/lib/libbe/common/libbe.h usr/src/lib/libbe/common/libbe_priv.h usr/src/man/man1m/beadm.1m
diffstat 11 files changed, 491 insertions(+), 112 deletions(-) [+]
line wrap: on
line diff
--- a/usr/src/cmd/beadm/beadm.c	Sat Aug 17 17:54:47 2013 -0400
+++ b/usr/src/cmd/beadm/beadm.c	Mon Aug 26 13:52:20 2013 -0400
@@ -24,7 +24,7 @@
  */
 
 /*
- * Copyright 2012 Nexenta Systems, Inc. All rights reserved.
+ * Copyright 2013 Nexenta Systems, Inc. All rights reserved.
  */
 
 /*
@@ -386,10 +386,17 @@
 		    snap = snap->be_next_snapshot)
 			used += snap->be_snapshot_space_used;
 
+		if (!cur_be->be_global_active)
+			active[ai++] = 'x';
+
 		if (cur_be->be_active)
 			active[ai++] = 'N';
-		if (cur_be->be_active_on_boot)
-			active[ai] = 'R';
+		if (cur_be->be_active_on_boot) {
+			if (!cur_be->be_global_active)
+				active[ai] = 'b';
+			else
+				active[ai] = 'R';
+		}
 
 		nicenum(used, buf, sizeof (buf));
 		if (parsable)
--- a/usr/src/lib/libbe/common/be_activate.c	Sat Aug 17 17:54:47 2013 -0400
+++ b/usr/src/lib/libbe/common/be_activate.c	Mon Aug 26 13:52:20 2013 -0400
@@ -23,6 +23,10 @@
  * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
  */
 
+/*
+ * Copyright 2013 Nexenta Systems, Inc. All rights reserved.
+ */
+
 #include <assert.h>
 #include <libintl.h>
 #include <libnvpair.h>
@@ -131,6 +135,7 @@
 	be_transaction_data_t cb = { 0 };
 	zfs_handle_t	*zhp = NULL;
 	char		root_ds[MAXPATHLEN];
+	char		active_ds[MAXPATHLEN];
 	char		*cur_vers = NULL, *new_vers = NULL;
 	be_node_list_t	*be_nodes = NULL;
 	uuid_t		uu = {0};
@@ -224,10 +229,13 @@
 		goto done;
 	}
 
-	if ((ret = set_bootfs(be_nodes->be_rpool, root_ds)) != BE_SUCCESS) {
-		be_print_err(gettext("be_activate: failed to set "
-		    "bootfs pool property for %s\n"), root_ds);
-		goto done;
+	if (getzoneid() == GLOBAL_ZONEID) {
+		if ((ret = set_bootfs(be_nodes->be_rpool,
+		    root_ds)) != BE_SUCCESS) {
+			be_print_err(gettext("be_activate: failed to set "
+			    "bootfs pool property for %s\n"), root_ds);
+			goto done;
+		}
 	}
 
 	if ((zhp = zfs_open(g_zfs, root_ds, ZFS_TYPE_FILESYSTEM)) != NULL) {
@@ -246,7 +254,7 @@
 			goto done;
 		}
 	} else {
-		be_print_err(gettext("be_activate:: failed to open "
+		be_print_err(gettext("be_activate: failed to open "
 		    "dataset (%s): %s\n"), root_ds,
 		    libzfs_error_description(g_zfs));
 		ret = zfs_err_to_be_err(g_zfs);
@@ -262,6 +270,67 @@
 		    cb.obe_name);
 	}
 
+	if (getzoneid() != GLOBAL_ZONEID) {
+		if (!be_zone_compare_uuids(root_ds)) {
+			be_print_err(gettext("be_activate: activating zone "
+			    "root dataset from non-active global BE is not "
+			    "supported\n"));
+			ret = BE_ERR_NOTSUP;
+			goto done;
+		}
+		if ((zhp = zfs_open(g_zfs, root_ds,
+		    ZFS_TYPE_FILESYSTEM)) == NULL) {
+			be_print_err(gettext("be_activate: failed to open "
+			    "dataset (%s): %s\n"), root_ds,
+			    libzfs_error_description(g_zfs));
+			ret = zfs_err_to_be_err(g_zfs);
+			goto done;
+		}
+		/* Find current active zone root dataset */
+		if ((ret = be_find_active_zone_root(zhp, cb.obe_zpool,
+		    active_ds, sizeof (active_ds))) != BE_SUCCESS) {
+			be_print_err(gettext("be_activate: failed to find "
+			    "active zone root dataset\n"));
+			ZFS_CLOSE(zhp);
+			goto done;
+		}
+		/* Do nothing if requested BE is already active */
+		if (strcmp(root_ds, active_ds) == 0) {
+			ret = BE_SUCCESS;
+			ZFS_CLOSE(zhp);
+			goto done;
+		}
+
+		/* Set active property for BE */
+		if (zfs_prop_set(zhp, BE_ZONE_ACTIVE_PROPERTY, "on") != 0) {
+			be_print_err(gettext("be_activate: failed to set "
+			    "active property (%s): %s\n"), root_ds,
+			    libzfs_error_description(g_zfs));
+			ret = zfs_err_to_be_err(g_zfs);
+			ZFS_CLOSE(zhp);
+			goto done;
+		}
+		ZFS_CLOSE(zhp);
+
+		/* Unset active property for old active root dataset */
+		if ((zhp = zfs_open(g_zfs, active_ds,
+		    ZFS_TYPE_FILESYSTEM)) == NULL) {
+			be_print_err(gettext("be_activate: failed to open "
+			    "dataset (%s): %s\n"), active_ds,
+			    libzfs_error_description(g_zfs));
+			ret = zfs_err_to_be_err(g_zfs);
+			goto done;
+		}
+		if (zfs_prop_set(zhp, BE_ZONE_ACTIVE_PROPERTY, "off") != 0) {
+			be_print_err(gettext("be_activate: failed to unset "
+			    "active property (%s): %s\n"), active_ds,
+			    libzfs_error_description(g_zfs));
+			ret = zfs_err_to_be_err(g_zfs);
+			ZFS_CLOSE(zhp);
+			goto done;
+		}
+		ZFS_CLOSE(zhp);
+	}
 done:
 	be_free_list(be_nodes);
 	return (ret);
--- a/usr/src/lib/libbe/common/be_create.c	Sat Aug 17 17:54:47 2013 -0400
+++ b/usr/src/lib/libbe/common/be_create.c	Mon Aug 26 13:52:20 2013 -0400
@@ -24,7 +24,7 @@
  */
 
 /*
- * Copyright 2011 Nexenta Systems, Inc. All rights reserved.
+ * Copyright 2013 Nexenta Systems, Inc. All rights reserved.
  */
 
 /*
@@ -450,6 +450,17 @@
 	    sizeof (obe_root_ds));
 	bt.obe_root_ds = obe_root_ds;
 
+	if (getzoneid() != GLOBAL_ZONEID) {
+		if (!be_zone_compare_uuids(bt.obe_root_ds)) {
+			if (be_is_active_on_boot(bt.obe_name)) {
+				be_print_err(gettext("be_destroy: destroying "
+				    "active zone root dataset from non-active "
+				    "global BE is not supported\n"));
+				return (BE_ERR_NOTSUP);
+			}
+		}
+	}
+
 	/*
 	 * Detect if the BE to destroy has the 'active on boot' property set.
 	 * If so, set the 'active on boot' property on the the 'active' BE.
@@ -482,9 +493,12 @@
 	}
 
 	/* Get the UUID of the global BE */
-	if (be_get_uuid(zfs_get_name(zhp), &dd.gz_be_uuid) != BE_SUCCESS) {
-		be_print_err(gettext("be_destroy: BE has no UUID (%s)\n"),
-		    zfs_get_name(zhp));
+	if (getzoneid() == GLOBAL_ZONEID) {
+		if (be_get_uuid(zfs_get_name(zhp),
+		    &dd.gz_be_uuid) != BE_SUCCESS) {
+			be_print_err(gettext("be_destroy: BE has no "
+			"UUID (%s)\n"), zfs_get_name(zhp));
+		}
 	}
 
 	/*
@@ -595,6 +609,7 @@
 	zpool_handle_t	*zphp = NULL;
 	nvlist_t	*zfs_props = NULL;
 	uuid_t		uu = { 0 };
+	uuid_t		parent_uu = { 0 };
 	char		obe_root_ds[MAXPATHLEN];
 	char		nbe_root_ds[MAXPATHLEN];
 	char		ss[MAXPATHLEN];
@@ -753,17 +768,30 @@
 		}
 
 		/* Verify it doesn't already exist */
-		if ((zret = zpool_iter(g_zfs, be_exists_callback, bt.nbe_name))
-		    > 0) {
-			be_print_err(gettext("be_copy: BE (%s) already "
-			    "exists\n"), bt.nbe_name);
-			ret = BE_ERR_BE_EXISTS;
-			goto done;
-		} else if (zret < 0) {
-			be_print_err(gettext("be_copy: zpool_iter failed: "
-			    "%s\n"), libzfs_error_description(g_zfs));
-			ret = zfs_err_to_be_err(g_zfs);
-			goto done;
+		if (getzoneid() == GLOBAL_ZONEID) {
+			if ((zret = zpool_iter(g_zfs, be_exists_callback,
+			    bt.nbe_name)) > 0) {
+				be_print_err(gettext("be_copy: BE (%s) already "
+				    "exists\n"), bt.nbe_name);
+				ret = BE_ERR_BE_EXISTS;
+				goto done;
+			} else if (zret < 0) {
+				be_print_err(gettext("be_copy: zpool_iter "
+				    "failed: %s\n"),
+				    libzfs_error_description(g_zfs));
+				ret = zfs_err_to_be_err(g_zfs);
+				goto done;
+			}
+		} else {
+			be_make_root_ds(bt.nbe_zpool, bt.nbe_name, nbe_root_ds,
+			    sizeof (nbe_root_ds));
+			if (zfs_dataset_exists(g_zfs, nbe_root_ds,
+			    ZFS_TYPE_FILESYSTEM)) {
+				be_print_err(gettext("be_copy: BE (%s) already "
+				    "exists\n"), bt.nbe_name);
+				ret = BE_ERR_BE_EXISTS;
+				goto done;
+			}
 		}
 	} else {
 		/*
@@ -1016,9 +1044,24 @@
 	}
 
 	/* Set UUID for new BE */
-	if (be_set_uuid(bt.nbe_root_ds) != BE_SUCCESS) {
-		be_print_err(gettext("be_copy: failed to "
-		    "set uuid for new BE\n"));
+	if (getzoneid() == GLOBAL_ZONEID) {
+		if (be_set_uuid(bt.nbe_root_ds) != BE_SUCCESS) {
+			be_print_err(gettext("be_copy: failed to "
+			    "set uuid for new BE\n"));
+		}
+	} else {
+		if ((ret = be_zone_get_parent_uuid(bt.obe_root_ds,
+		    &parent_uu)) != BE_SUCCESS) {
+			be_print_err(gettext("be_copy: failed to get "
+			    "parentbe uuid from orig BE\n"));
+			ret = BE_ERR_ZONE_NO_PARENTBE;
+			goto done;
+		} else if ((ret = be_zone_set_parent_uuid(bt.nbe_root_ds,
+		    parent_uu)) != BE_SUCCESS) {
+			be_print_err(gettext("be_copy: failed to set "
+			    "parentbe uuid for newly created BE\n"));
+			goto done;
+		}
 	}
 
 	/*
--- a/usr/src/lib/libbe/common/be_list.c	Sat Aug 17 17:54:47 2013 -0400
+++ b/usr/src/lib/libbe/common/be_list.c	Mon Aug 26 13:52:20 2013 -0400
@@ -24,7 +24,7 @@
  */
 
 /*
- * Copyright 2011 Nexenta Systems, Inc. All rights reserved.
+ * Copyright 2013 Nexenta Systems, Inc. All rights reserved.
  */
 
 #include <assert.h>
@@ -188,7 +188,7 @@
 
 	if (be_defaults.be_deflt_rpool_container && rpool != NULL) {
 		if ((zphp = zpool_open(g_zfs, rpool)) == NULL) {
-			be_print_err(gettext("be_get_node_data: failed to "
+			be_print_err(gettext("be_list: failed to "
 			    "open rpool (%s): %s\n"), rpool,
 			    libzfs_error_description(g_zfs));
 			free(cb.be_name);
@@ -824,7 +824,9 @@
 	char prop_buf[MAXPATHLEN];
 	nvlist_t *userprops = NULL;
 	nvlist_t *propval = NULL;
+	nvlist_t *zone_propval = NULL;
 	char *prop_str = NULL;
+	char *zone_prop_str = NULL;
 	char *grub_default_bootfs = NULL;
 	zpool_handle_t *zphp = NULL;
 	int err = 0;
@@ -865,27 +867,38 @@
 
 	be_node->be_space_used = zfs_prop_get_int(zhp, ZFS_PROP_USED);
 
-	if ((zphp = zpool_open(g_zfs, rpool)) == NULL) {
-		be_print_err(gettext("be_get_node_data: failed to open pool "
-		    "(%s): %s\n"), rpool, libzfs_error_description(g_zfs));
-		return (zfs_err_to_be_err(g_zfs));
-	}
+	if (getzoneid() == GLOBAL_ZONEID) {
+		if ((zphp = zpool_open(g_zfs, rpool)) == NULL) {
+			be_print_err(gettext("be_get_node_data: failed to open "
+			    "pool (%s): %s\n"), rpool,
+			    libzfs_error_description(g_zfs));
+			return (zfs_err_to_be_err(g_zfs));
+		}
 
-	(void) zpool_get_prop(zphp, ZPOOL_PROP_BOOTFS, prop_buf, ZFS_MAXPROPLEN,
-	    NULL);
-	if (be_has_grub() &&
-	    (be_default_grub_bootfs(rpool, &grub_default_bootfs)
-	    == BE_SUCCESS) && grub_default_bootfs != NULL)
-		if (strcmp(grub_default_bootfs, be_ds) == 0)
+		(void) zpool_get_prop(zphp, ZPOOL_PROP_BOOTFS, prop_buf,
+		    ZFS_MAXPROPLEN, NULL);
+		if (be_has_grub() && (be_default_grub_bootfs(rpool,
+		    &grub_default_bootfs) == BE_SUCCESS) &&
+		    grub_default_bootfs != NULL)
+			if (strcmp(grub_default_bootfs, be_ds) == 0)
+				be_node->be_active_on_boot = B_TRUE;
+			else
+				be_node->be_active_on_boot = B_FALSE;
+		else if (prop_buf != NULL && strcmp(prop_buf, be_ds) == 0)
 			be_node->be_active_on_boot = B_TRUE;
 		else
 			be_node->be_active_on_boot = B_FALSE;
-	else if (prop_buf != NULL && strcmp(prop_buf, be_ds) == 0)
-		be_node->be_active_on_boot = B_TRUE;
-	else
-		be_node->be_active_on_boot = B_FALSE;
-	free(grub_default_bootfs);
-	zpool_close(zphp);
+
+		be_node->be_global_active = B_TRUE;
+
+		free(grub_default_bootfs);
+		zpool_close(zphp);
+	} else {
+		if (be_zone_compare_uuids(be_node->be_root_ds))
+			be_node->be_global_active = B_TRUE;
+		else
+			be_node->be_global_active = B_FALSE;
+	}
 
 	/*
 	 * If the dataset is mounted use the mount point
@@ -910,6 +923,22 @@
 	if ((userprops = zfs_get_user_props(zhp)) == NULL) {
 		be_node->be_policy_type = strdup(be_default_policy());
 	} else {
+		if (getzoneid() != GLOBAL_ZONEID) {
+			if (nvlist_lookup_nvlist(userprops,
+			    BE_ZONE_ACTIVE_PROPERTY, &zone_propval) != 0 ||
+			    zone_propval == NULL) {
+				be_node->be_active_on_boot = B_FALSE;
+			} else {
+				verify(nvlist_lookup_string(zone_propval,
+				    ZPROP_VALUE, &zone_prop_str) == 0);
+				if (strcmp(zone_prop_str, "on") == 0) {
+					be_node->be_active_on_boot = B_TRUE;
+				} else {
+					be_node->be_active_on_boot = B_FALSE;
+				}
+			}
+		}
+
 		if (nvlist_lookup_nvlist(userprops, BE_POLICY_PROPERTY,
 		    &propval) != 0 || propval == NULL) {
 			be_node->be_policy_type =
@@ -924,11 +953,19 @@
 			else
 				be_node->be_policy_type = strdup(prop_str);
 		}
-
-		if (nvlist_lookup_nvlist(userprops, BE_UUID_PROPERTY, &propval)
-		    == 0 && nvlist_lookup_string(propval, ZPROP_VALUE,
-		    &prop_str) == 0) {
-			be_node->be_uuid_str = strdup(prop_str);
+		if (getzoneid() != GLOBAL_ZONEID) {
+			if (nvlist_lookup_nvlist(userprops,
+			    BE_ZONE_PARENTBE_PROPERTY, &propval) != 0 &&
+			    nvlist_lookup_string(propval, ZPROP_VALUE,
+			    &prop_str) == 0) {
+				be_node->be_uuid_str = strdup(prop_str);
+			}
+		} else {
+			if (nvlist_lookup_nvlist(userprops, BE_UUID_PROPERTY,
+			    &propval) == 0 && nvlist_lookup_string(propval,
+			    ZPROP_VALUE, &prop_str) == 0) {
+				be_node->be_uuid_str = strdup(prop_str);
+			}
 		}
 	}
 
--- a/usr/src/lib/libbe/common/be_mount.c	Sat Aug 17 17:54:47 2013 -0400
+++ b/usr/src/lib/libbe/common/be_mount.c	Mon Aug 26 13:52:20 2013 -0400
@@ -23,7 +23,7 @@
  * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
  */
 /*
- * Copyright 2012 Nexenta Systems, Inc. All rights reserved.
+ * Copyright 2013 Nexenta Systems, Inc. All rights reserved.
  */
 
 /*
@@ -31,6 +31,7 @@
  */
 #include <assert.h>
 #include <errno.h>
+#include <libgen.h>
 #include <libintl.h>
 #include <libnvpair.h>
 #include <libzfs.h>
@@ -326,14 +327,29 @@
 		tmp_altroot = *altroot;
 	}
 
+	md.altroot = tmp_altroot;
+	md.shared_fs = flags & BE_MOUNT_FLAG_SHARED_FS;
+	md.shared_rw = flags & BE_MOUNT_FLAG_SHARED_RW;
+
 	/* Mount the BE's root file system */
-	if ((ret = be_mount_root(zhp, tmp_altroot)) != BE_SUCCESS) {
-		be_print_err(gettext("be_mount: failed to "
-		    "mount BE root file system\n"));
-		if (gen_tmp_altroot)
-			free(tmp_altroot);
-		ZFS_CLOSE(zhp);
-		return (ret);
+	if (getzoneid() == GLOBAL_ZONEID) {
+		if ((ret = be_mount_root(zhp, tmp_altroot)) != BE_SUCCESS) {
+			be_print_err(gettext("be_mount: failed to "
+			    "mount BE root file system\n"));
+			if (gen_tmp_altroot)
+				free(tmp_altroot);
+			ZFS_CLOSE(zhp);
+			return (ret);
+		}
+	} else {
+		/* Legacy mount the zone root dataset */
+		if ((ret = be_mount_zone_root(zhp, &md)) != BE_SUCCESS) {
+			be_print_err(gettext("be_mount: failed to "
+			    "mount BE zone root file system\n"));
+			free(md.altroot);
+			ZFS_CLOSE(zhp);
+			return (ret);
+		}
 	}
 
 	/* Iterate through BE's children filesystems */
@@ -347,10 +363,6 @@
 		return (err);
 	}
 
-	md.altroot = tmp_altroot;
-	md.shared_fs = flags & BE_MOUNT_FLAG_SHARED_FS;
-	md.shared_rw = flags & BE_MOUNT_FLAG_SHARED_RW;
-
 	/*
 	 * Mount shared file systems if mount flag says so.
 	 */
@@ -528,9 +540,16 @@
 	}
 
 	/* Unmount this BE's root filesystem */
-	if ((ret = be_unmount_root(zhp, &ud)) != BE_SUCCESS) {
-		ZFS_CLOSE(zhp);
-		return (ret);
+	if (getzoneid() == GLOBAL_ZONEID) {
+		if ((ret = be_unmount_root(zhp, &ud)) != BE_SUCCESS) {
+			ZFS_CLOSE(zhp);
+			return (ret);
+		}
+	} else {
+		if ((ret = be_unmount_zone_root(zhp, &ud)) != BE_SUCCESS) {
+			ZFS_CLOSE(zhp);
+			return (ret);
+		}
 	}
 
 	ZFS_CLOSE(zhp);
@@ -553,6 +572,7 @@
 int
 be_mount_zone_root(zfs_handle_t *zhp, be_mount_data_t *md)
 {
+	struct stat buf;
 	char	mountpoint[MAXPATHLEN];
 	int	err = 0;
 
@@ -576,6 +596,16 @@
 		return (BE_ERR_ZONE_ROOT_NOT_LEGACY);
 	}
 
+	/* Create the mountpoint if it doesn't exist */
+	if (lstat(md->altroot, &buf) != 0) {
+		if (mkdirp(md->altroot, 0755) != 0) {
+			err = errno;
+			be_print_err(gettext("be_mount_zone_root: failed "
+			    "to create mountpoint %s\n"), md->altroot);
+			return (errno_to_be_err(err));
+		}
+	}
+
 	/*
 	 * Legacy mount the zone root dataset.
 	 *
--- a/usr/src/lib/libbe/common/be_snapshot.c	Sat Aug 17 17:54:47 2013 -0400
+++ b/usr/src/lib/libbe/common/be_snapshot.c	Mon Aug 26 13:52:20 2013 -0400
@@ -24,7 +24,7 @@
  */
 
 /*
- * Copyright 2011 Nexenta Systems, Inc. All rights reserved.
+ * Copyright 2013 Nexenta Systems, Inc. All rights reserved.
  */
 
 /*
@@ -323,6 +323,15 @@
 	    sizeof (obe_root_ds));
 	bt.obe_root_ds = obe_root_ds;
 
+	if (getzoneid() != GLOBAL_ZONEID) {
+		if (!be_zone_compare_uuids(bt.obe_root_ds)) {
+			be_print_err(gettext("be_rollback: rolling back zone "
+			    "root dataset from non-active global BE is not "
+			    "supported\n"));
+			return (BE_ERR_NOTSUP);
+		}
+	}
+
 	/* Get handle to BE's root dataset */
 	if ((zhp = zfs_open(g_zfs, bt.obe_root_ds, ZFS_TYPE_DATASET)) == NULL) {
 		be_print_err(gettext("be_rollback: "
@@ -429,6 +438,16 @@
 	    sizeof (root_ds));
 	bt.obe_root_ds = root_ds;
 
+	if (getzoneid() != GLOBAL_ZONEID) {
+		if (!be_zone_compare_uuids(bt.obe_root_ds)) {
+			be_print_err(gettext("be_create_snapshot: creating "
+			    "snapshot for the zone root dataset from "
+			    "non-active global BE is not "
+			    "supported\n"));
+			return (BE_ERR_NOTSUP);
+		}
+	}
+
 	/* If BE policy not specified, use the default policy */
 	if (bt.policy == NULL) {
 		bt.policy = be_default_policy();
@@ -482,27 +501,30 @@
 	 * cleanup policy there.  Otherwise don't set one - this snapshot
 	 * will always inherit the cleanup policy from its parent.
 	 */
-	if (pool_version >= SPA_VERSION_SNAP_PROPS) {
-		if (nvlist_alloc(&ss_props, NV_UNIQUE_NAME, 0) != 0) {
-			be_print_err(gettext("be_create_snapshot: internal "
-			    "error: out of memory\n"));
-			return (BE_ERR_NOMEM);
+	if (getzoneid() == GLOBAL_ZONEID) {
+		if (pool_version >= SPA_VERSION_SNAP_PROPS) {
+			if (nvlist_alloc(&ss_props, NV_UNIQUE_NAME, 0) != 0) {
+				be_print_err(gettext("be_create_snapshot: "
+				    "internal error: out of memory\n"));
+				return (BE_ERR_NOMEM);
+			}
+			if (nvlist_add_string(ss_props, BE_POLICY_PROPERTY,
+			    bt.policy) != 0) {
+				be_print_err(gettext("be_create_snapshot: "
+				    "internal error: out of memory\n"));
+				nvlist_free(ss_props);
+				return (BE_ERR_NOMEM);
+			}
+		} else if (policy != NULL) {
+			/*
+			 * If an explicit cleanup policy was requested
+			 * by the caller and we don't support it, error out.
+			 */
+			be_print_err(gettext("be_create_snapshot: cannot set "
+			    "cleanup policy: ZFS pool version is %d\n"),
+			    pool_version);
+			return (BE_ERR_NOTSUP);
 		}
-		if (nvlist_add_string(ss_props, BE_POLICY_PROPERTY, bt.policy)
-		    != 0) {
-			be_print_err(gettext("be_create_snapshot: internal "
-			    "error: out of memory\n"));
-			nvlist_free(ss_props);
-			return (BE_ERR_NOMEM);
-		}
-	} else if (policy != NULL) {
-		/*
-		 * If an explicit cleanup policy was requested
-		 * by the caller and we don't support it, error out.
-		 */
-		be_print_err(gettext("be_create_snapshot: cannot set "
-		    "cleanup policy: ZFS pool version is %d\n"), pool_version);
-		return (BE_ERR_NOTSUP);
 	}
 
 	/* Create the snapshots recursively */
--- a/usr/src/lib/libbe/common/be_utils.c	Sat Aug 17 17:54:47 2013 -0400
+++ b/usr/src/lib/libbe/common/be_utils.c	Mon Aug 26 13:52:20 2013 -0400
@@ -24,7 +24,7 @@
  */
 
 /*
- * Copyright 2011 Nexenta Systems, Inc. All rights reserved.
+ * Copyright 2013 Nexenta Systems, Inc. All rights reserved.
  */
 
 
@@ -53,6 +53,7 @@
 #include <deflt.h>
 #include <wait.h>
 #include <libdevinfo.h>
+#include <libgen.h>
 
 #include <libbe.h>
 #include <libbe_priv.h>
@@ -230,13 +231,30 @@
 {
 	struct be_defaults be_defaults;
 	be_get_defaults(&be_defaults);
-
-	if (be_defaults.be_deflt_rpool_container)
-		(void) snprintf(be_root_ds, be_root_ds_size, "%s/%s", zpool,
-		    be_name);
-	else
-		(void) snprintf(be_root_ds, be_root_ds_size, "%s/%s/%s", zpool,
-		    BE_CONTAINER_DS_NAME, be_name);
+	char	*root_ds = NULL;
+
+	if (getzoneid() == GLOBAL_ZONEID) {
+		if (be_defaults.be_deflt_rpool_container) {
+			(void) snprintf(be_root_ds, be_root_ds_size,
+			    "%s/%s", zpool, be_name);
+		} else {
+			(void) snprintf(be_root_ds, be_root_ds_size,
+			    "%s/%s/%s", zpool, BE_CONTAINER_DS_NAME, be_name);
+		}
+	} else {
+		/*
+		 * In non-global zone we can use path from mounted root dataset
+		 * to generate BE's root dataset string.
+		 */
+		if ((root_ds = be_get_ds_from_dir("/")) != NULL) {
+			(void) snprintf(be_root_ds, be_root_ds_size, "%s/%s",
+			    dirname(root_ds), be_name);
+		} else {
+			be_print_err(gettext("be_make_root_ds: zone root "
+			    "dataset is not mounted\n"));
+			return;
+		}
+	}
 }
 
 /*
@@ -258,12 +276,26 @@
 {
 	struct be_defaults be_defaults;
 	be_get_defaults(&be_defaults);
-
-	if (be_defaults.be_deflt_rpool_container)
-		(void) snprintf(container_ds, container_ds_size, "%s", zpool);
-	else
-		(void) snprintf(container_ds, container_ds_size, "%s/%s", zpool,
-		    BE_CONTAINER_DS_NAME);
+	char	*root_ds = NULL;
+
+	if (getzoneid() == GLOBAL_ZONEID) {
+		if (be_defaults.be_deflt_rpool_container) {
+			(void) snprintf(container_ds, container_ds_size,
+			    "%s", zpool);
+		} else {
+			(void) snprintf(container_ds, container_ds_size,
+			    "%s/%s", zpool, BE_CONTAINER_DS_NAME);
+		}
+	} else {
+		if ((root_ds = be_get_ds_from_dir("/")) != NULL) {
+			(void) strlcpy(container_ds, dirname(root_ds),
+			    container_ds_size);
+		} else {
+			be_print_err(gettext("be_make_container_ds: zone root "
+			    "dataset is not mounted\n"));
+			return;
+		}
+	}
 }
 
 /*
@@ -2436,11 +2468,25 @@
 	zfs_handle_t		*zhp = NULL;
 	const char		*zpool =  zpool_get_name(zlp);
 	char			be_container_ds[MAXPATHLEN];
+	char			*zpath = NULL;
 
 	/*
 	 * Generate string for BE container dataset
 	 */
-	be_make_container_ds(zpool, be_container_ds, sizeof (be_container_ds));
+	if (getzoneid() != GLOBAL_ZONEID) {
+		if ((zpath = be_get_ds_from_dir("/")) != NULL) {
+			(void) strlcpy(be_container_ds, dirname(zpath),
+			    sizeof (be_container_ds));
+		} else {
+			be_print_err(gettext(
+			    "be_zpool_find_current_be_callback: "
+			    "zone root dataset is not mounted\n"));
+			return (0);
+		}
+	} else {
+		be_make_container_ds(zpool, be_container_ds,
+		    sizeof (be_container_ds));
+	}
 
 	/*
 	 * Check if a BE container dataset exists in this pool.
--- a/usr/src/lib/libbe/common/be_zones.c	Sat Aug 17 17:54:47 2013 -0400
+++ b/usr/src/lib/libbe/common/be_zones.c	Mon Aug 26 13:52:20 2013 -0400
@@ -24,6 +24,10 @@
  */
 
 /*
+ * Copyright 2013 Nexenta Systems, Inc. All rights reserved.
+ */
+
+/*
  * System includes
  */
 #include <assert.h>
@@ -113,14 +117,25 @@
 	int				ret = BE_SUCCESS;
 
 	/* Get the uuid of the parent global BE */
-	if ((ret = be_get_uuid(zfs_get_name(be_zhp), &azr_data.parent_uuid))
-	    != BE_SUCCESS) {
-		be_print_err(gettext("be_find_active_zone_root: failed to "
-		    "get uuid for BE root dataset %s\n"), zfs_get_name(be_zhp));
-		return (ret);
+	if (getzoneid() == GLOBAL_ZONEID) {
+		if ((ret = be_get_uuid(zfs_get_name(be_zhp),
+		    &azr_data.parent_uuid)) != BE_SUCCESS) {
+			be_print_err(gettext("be_find_active_zone_root: failed "
+			    "to get uuid for BE root dataset %s\n"),
+			    zfs_get_name(be_zhp));
+			return (ret);
+		}
+	} else {
+		if ((ret = be_zone_get_parent_uuid(zfs_get_name(be_zhp),
+		    &azr_data.parent_uuid)) != BE_SUCCESS) {
+			be_print_err(gettext("be_find_active_zone_root: failed "
+			    "to get parentbe uuid for zone root dataset %s\n"),
+			    zfs_get_name(be_zhp));
+			return (ret);
+		}
 	}
 
-	/* Generate string for the root container dataset for this zone. */
+	/* Generate string for the root container dataset  for this zone. */
 	be_make_container_ds(zonepath_ds, zone_container_ds,
 	    sizeof (zone_container_ds));
 
@@ -376,6 +391,100 @@
 	return (ret);
 }
 
+/*
+ * Function:	be_zone_set_parent_uuid
+ * Description:	This function sets parentbe uuid into
+ *		a zfs user property for a root zone dataset.
+ * Parameters:
+ *		root_ds - Root zone dataset of the BE to set a uuid on.
+ * Return:
+ *		be_errno_t - Failure
+ *		BE_SUCCESS - Success
+ * Scope:
+ *		Semi-private (library wide uses only)
+ */
+int
+be_zone_set_parent_uuid(char *root_ds, uuid_t uu)
+{
+	zfs_handle_t	*zhp = NULL;
+	char		uu_string[UUID_PRINTABLE_STRING_LENGTH];
+	int		ret = BE_SUCCESS;
+
+	uuid_unparse(uu, uu_string);
+
+	/* Get handle to the root zone dataset. */
+	if ((zhp = zfs_open(g_zfs, root_ds, ZFS_TYPE_FILESYSTEM)) == NULL) {
+		be_print_err(gettext("be_zone_set_parent_uuid: failed to "
+		    "open root zone dataset (%s): %s\n"), root_ds,
+		    libzfs_error_description(g_zfs));
+		return (zfs_err_to_be_err(g_zfs));
+	}
+
+	/* Set parentbe uuid property for the root zone dataset */
+	if (zfs_prop_set(zhp, BE_ZONE_PARENTBE_PROPERTY, uu_string) != 0) {
+		be_print_err(gettext("be_zone_set_parent_uuid: failed to "
+		    "set parentbe uuid property for root zone dataset: %s\n"),
+		    libzfs_error_description(g_zfs));
+		ret = zfs_err_to_be_err(g_zfs);
+	}
+
+	ZFS_CLOSE(zhp);
+	return (ret);
+}
+
+/*
+ * Function:	be_zone_compare_uuids
+ * Description:	This function compare the parentbe uuid of the
+ *		current running root zone dataset with the parentbe
+ *		uuid of the given root zone dataset.
+ * Parameters:
+ *		root_ds - Root zone dataset of the BE to compare.
+ * Return:
+ *		B_TRUE - root dataset has right parentbe uuid
+ *		B_FALSE - root dataset has wrong parentbe uuid
+ * Scope:
+ *		Semi-private (library wide uses only)
+ */
+boolean_t
+be_zone_compare_uuids(char *root_ds)
+{
+	char		*active_ds;
+	uuid_t		parent_uuid = {0};
+	uuid_t		cur_parent_uuid = {0};
+
+	/* Get parentbe uuid from given zone root dataset */
+	if ((be_zone_get_parent_uuid(root_ds,
+	    &parent_uuid)) != BE_SUCCESS) {
+		be_print_err(gettext("be_zone_compare_uuids: failed to get "
+		    "parentbe uuid from the given BE\n"));
+		return (B_FALSE);
+	}
+
+	/*
+	 * Find current running zone root dataset and get it's parentbe
+	 * uuid property.
+	 */
+	if ((active_ds = be_get_ds_from_dir("/")) != NULL) {
+		if ((be_zone_get_parent_uuid(active_ds,
+		    &cur_parent_uuid)) != BE_SUCCESS) {
+			be_print_err(gettext("be_zone_compare_uuids: failed "
+			"to get parentbe uuid from the current running zone "
+			"root dataset\n"));
+			return (B_FALSE);
+		}
+	} else {
+		be_print_err(gettext("be_zone_compare_uuids: zone root dataset "
+		    "is not mounted\n"));
+		return (B_FALSE);
+	}
+
+	if (uuid_compare(parent_uuid, cur_parent_uuid) != 0) {
+		return (B_FALSE);
+	}
+
+	return (B_TRUE);
+}
+
 /* ******************************************************************** */
 /*			Private Functions				*/
 /* ******************************************************************** */
--- a/usr/src/lib/libbe/common/libbe.h	Sat Aug 17 17:54:47 2013 -0400
+++ b/usr/src/lib/libbe/common/libbe.h	Mon Aug 26 13:52:20 2013 -0400
@@ -23,6 +23,10 @@
  * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
  */
 
+/*
+ * Copyright 2013 Nexenta Systems, Inc. All rights reserved.
+ */
+
 #ifndef _LIBBE_H
 #define	_LIBBE_H
 
@@ -58,6 +62,7 @@
 
 #define	BE_ATTR_ACTIVE		"active"
 #define	BE_ATTR_ACTIVE_ON_BOOT	"active_boot"
+#define	BE_ATTR_GLOBAL_ACTIVE	"global_active"
 #define	BE_ATTR_SPACE		"space_used"
 #define	BE_ATTR_DATASET		"dataset"
 #define	BE_ATTR_STATUS		"status"
@@ -166,6 +171,8 @@
 	boolean_t be_mounted;		/* is BE currently mounted */
 	boolean_t be_active_on_boot;	/* is this BE active on boot */
 	boolean_t be_active;		/* is this BE active currently */
+	boolean_t be_global_active;	/* is zone's BE associated with */
+					/* an active global BE */
 	uint64_t be_space_used;
 	char *be_node_name;
 	char *be_rpool;
--- a/usr/src/lib/libbe/common/libbe_priv.h	Sat Aug 17 17:54:47 2013 -0400
+++ b/usr/src/lib/libbe/common/libbe_priv.h	Mon Aug 26 13:52:20 2013 -0400
@@ -24,7 +24,7 @@
  */
 
 /*
- * Copyright 2011 Nexenta Systems, Inc. All rights reserved.
+ * Copyright 2013 Nexenta Systems, Inc. All rights reserved.
  */
 
 #ifndef	_LIBBE_PRIV_H
@@ -205,6 +205,8 @@
 boolean_t be_zone_supported(char *);
 zoneBrandList_t *be_get_supported_brandlist(void);
 int be_zone_get_parent_uuid(const char *, uuid_t *);
+int be_zone_set_parent_uuid(char *, uuid_t);
+boolean_t be_zone_compare_uuids(char *);
 
 /* check architecture functions */
 char *be_get_default_isa(void);
--- a/usr/src/man/man1m/beadm.1m	Sat Aug 17 17:54:47 2013 -0400
+++ b/usr/src/man/man1m/beadm.1m	Mon Aug 26 13:52:20 2013 -0400
@@ -1,6 +1,6 @@
 '\" te
-.\" Copyright 2012 Nexenta Systems, Inc. All rights reserved.
-.TH BEADM 1M "Feb 26, 2011"
+.\" Copyright 2013 Nexenta Systems, Inc. All rights reserved.
+.TH BEADM 1M "Jul 25, 2013"
 .SH NAME
 beadm \- utility for managing zfs boot environments
 .SH SYNOPSIS
@@ -239,6 +239,7 @@
 Create the new BE in the specified zpool.  If this is not provided, the
 default
 behavior is to create the new BE in the same pool as as the origin BE.
+This option is not supported in non-global zone.
 .RE
 .sp
 .ne 2
@@ -338,7 +339,12 @@
 field indicates whether the boot environment is active now,
 represented
 by 'N'; active on reboot, represented by 'R'; or both, represented
-by 'NR'.
+by 'NR'. In non-global zone the 'Active' field also indicates whether the
+boot environment has a non-active parent BE, represented by 'x'; is active
+on boot in a non-active parent BE, represented by 'b'. Activate, rollback
+and snapshot operations for boot environments from non-active global parent
+BE aren't supported, destroy is allowed if these boot environments aren't
+active on boot.
 .sp
 Each line in the machine parasable output has the boot environment name as the
 first field.  The 'Space' field is displayed in bytes and the 'Created' field
@@ -346,7 +352,8 @@
 gives
 the boot environment's uuid in the second field.  This field will be
 blank if
-the boot environment does not have a uuid.  See the EXAMPLES section.
+the boot environment does not have a uuid. See the EXAMPLES section.
+In non-global zones, this field shows the uuid of the parent BE.
 .sp
 .ne 2
 .na