xref: /illumos-gate/usr/src/lib/libbe/common/be_zones.c (revision dd72704bd9e794056c558153663c739e2012d721)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
24  */
25 
26 /*
27  * Copyright 2013 Nexenta Systems, Inc. All rights reserved.
28  * Copyright 2022 OmniOS Community Edition (OmniOSce) Association.
29  */
30 
31 /*
32  * System includes
33  */
34 #include <assert.h>
35 #include <errno.h>
36 #include <libintl.h>
37 #include <libnvpair.h>
38 #include <libzfs.h>
39 #include <stdio.h>
40 #include <stdlib.h>
41 #include <string.h>
42 #include <sys/mntent.h>
43 #include <sys/mnttab.h>
44 #include <sys/mount.h>
45 #include <sys/stat.h>
46 #include <sys/types.h>
47 #include <sys/vfstab.h>
48 #include <unistd.h>
49 
50 #include <libbe.h>
51 #include <libbe_priv.h>
52 
53 typedef struct active_zone_root_data {
54 	uuid_t	parent_uuid;
55 	char	*zoneroot_ds;
56 } active_zone_root_data_t;
57 
58 typedef struct mounted_zone_root_data {
59 	char	*zone_altroot;
60 	char	*zoneroot_ds;
61 } mounted_zone_root_data_t;
62 
63 /* Private function prototypes */
64 static int be_find_active_zone_root_callback(zfs_handle_t *, void *);
65 static int be_find_mounted_zone_root_callback(zfs_handle_t *, void *);
66 static boolean_t be_zone_get_active(zfs_handle_t *);
67 
68 
69 /* ******************************************************************** */
70 /*			Semi-Private Functions				*/
71 /* ******************************************************************** */
72 
73 /*
74  * Function:	be_make_zoneroot
75  * Description:	Generate a string for a zone's zoneroot given the
76  *		zone's zonepath.
77  * Parameters:
78  *		zonepath - pointer to zonepath
79  *		zoneroot - pointer to buffer to retrn zoneroot in.
80  *		zoneroot_size - size of zoneroot
81  * Returns:
82  *		None
83  * Scope:
84  *		Semi-private (library wise use only)
85  */
86 void
87 be_make_zoneroot(char *zonepath, char *zoneroot, int zoneroot_size)
88 {
89 	(void) snprintf(zoneroot, zoneroot_size, "%s/root", zonepath);
90 }
91 
92 /*
93  * Function:	be_find_active_zone_root
94  * Description:	This function will find the active zone root of a zone for
95  *		a given global BE.  It will iterate all of the zone roots
96  *		under a zonepath, find the zone roots that belong to the
97  *		specified global BE, and return the one that is active.
98  * Parameters:
99  *		be_zhp - zfs handle to global BE root dataset.
100  *		zonepath_ds - pointer to zone's zonepath dataset.
101  *		zoneroot_ds - pointer to a buffer to store the dataset name of
102  *			the zone's zoneroot that's currently active for this
103  *			given global BE..
104  *		zoneroot-ds_size - size of zoneroot_ds.
105  * Returns:
106  *		BE_SUCCESS - Success
107  *		be_errno_t - Failure
108  * Scope:
109  *		Semi-private (library wide use only)
110  */
111 int
112 be_find_active_zone_root(zfs_handle_t *be_zhp, char *zonepath_ds,
113     char *zoneroot_ds, int zoneroot_ds_size)
114 {
115 	active_zone_root_data_t		azr_data = { { 0 }, NULL };
116 	zfs_handle_t			*zhp;
117 	char				zone_container_ds[MAXPATHLEN];
118 	int				ret = BE_SUCCESS;
119 
120 	/* Get the uuid of the parent global BE */
121 	if (getzoneid() == GLOBAL_ZONEID) {
122 		if ((ret = be_get_uuid(zfs_get_name(be_zhp),
123 		    &azr_data.parent_uuid)) != BE_SUCCESS) {
124 			be_print_err(gettext("be_find_active_zone_root: failed "
125 			    "to get uuid for BE root dataset %s\n"),
126 			    zfs_get_name(be_zhp));
127 			return (ret);
128 		}
129 	} else {
130 		if ((ret = be_zone_get_parent_uuid(zfs_get_name(be_zhp),
131 		    &azr_data.parent_uuid)) != BE_SUCCESS) {
132 			be_print_err(gettext("be_find_active_zone_root: failed "
133 			    "to get parentbe uuid for zone root dataset %s\n"),
134 			    zfs_get_name(be_zhp));
135 			return (ret);
136 		}
137 	}
138 
139 	/* Generate string for the root container dataset for this zone. */
140 	if ((ret = be_make_container_ds(zonepath_ds, zone_container_ds,
141 	    sizeof (zone_container_ds))) != BE_SUCCESS) {
142 		be_print_err(gettext("%s: failed to get BE container dataset "
143 		    "for %s\n"), __func__, zonepath_ds);
144 		return (ret);
145 	}
146 
147 	/* Get handle to this zone's root container dataset */
148 	if ((zhp = zfs_open(g_zfs, zone_container_ds, ZFS_TYPE_FILESYSTEM))
149 	    == NULL) {
150 		be_print_err(gettext("be_find_active_zone_root: failed to "
151 		    "open zone root container dataset (%s): %s\n"),
152 		    zone_container_ds, libzfs_error_description(g_zfs));
153 		return (zfs_err_to_be_err(g_zfs));
154 	}
155 
156 	/*
157 	 * Iterate through all of this zone's BEs, looking for ones
158 	 * that belong to the parent global BE, and finding the one
159 	 * that is marked active.
160 	 */
161 	if ((ret = zfs_iter_filesystems(zhp, be_find_active_zone_root_callback,
162 	    &azr_data)) != 0) {
163 		be_print_err(gettext("be_find_active_zone_root: failed to "
164 		    "find active zone root in zonepath dataset %s: %s\n"),
165 		    zonepath_ds, be_err_to_str(ret));
166 		goto done;
167 	}
168 
169 	if (azr_data.zoneroot_ds != NULL) {
170 		(void) strlcpy(zoneroot_ds, azr_data.zoneroot_ds,
171 		    zoneroot_ds_size);
172 		free(azr_data.zoneroot_ds);
173 	} else {
174 		be_print_err(gettext("be_find_active_zone_root: failed to "
175 		    "find active zone root in zonepath dataset %s\n"),
176 		    zonepath_ds);
177 		ret = BE_ERR_ZONE_NO_ACTIVE_ROOT;
178 	}
179 
180 done:
181 	ZFS_CLOSE(zhp);
182 	return (ret);
183 }
184 
185 /*
186  * Function:	be_find_mounted_zone_root
187  * Description:	This function will find the dataset mounted as the zoneroot
188  *		of a zone for a given mounted global BE.
189  * Parameters:
190  *		zone_altroot - path of zoneroot wrt the mounted global BE.
191  *		zonepath_ds - dataset of the zone's zonepath.
192  *		zoneroot_ds - pointer to a buffer to store the dataset of
193  *			the zoneroot that currently mounted for this zone
194  *			in the mounted global BE.
195  *		zoneroot_ds_size - size of zoneroot_ds
196  * Returns:
197  *		BE_SUCCESS - Success
198  *		be_errno_t - Failure
199  * Scope:
200  *		Semi-private (library wide use only)
201  */
202 int
203 be_find_mounted_zone_root(char *zone_altroot, char *zonepath_ds,
204     char *zoneroot_ds, int zoneroot_ds_size)
205 {
206 	mounted_zone_root_data_t	mzr_data = { 0 };
207 	zfs_handle_t	*zhp = NULL;
208 	char		zone_container_ds[MAXPATHLEN];
209 	int		ret = BE_SUCCESS;
210 	int		zret = 0;
211 
212 	/* Generate string for the root container dataset for this zone. */
213 	if ((ret = be_make_container_ds(zonepath_ds, zone_container_ds,
214 	    sizeof (zone_container_ds))) != BE_SUCCESS) {
215 		be_print_err(gettext("%s: failed to get BE container dataset "
216 		    "for %s\n"), __func__, zonepath_ds);
217 		return (ret);
218 	}
219 
220 	/* Get handle to this zone's root container dataset. */
221 	if ((zhp = zfs_open(g_zfs, zone_container_ds, ZFS_TYPE_FILESYSTEM))
222 	    == NULL) {
223 		be_print_err(gettext("be_find_mounted_zone_root: failed to "
224 		    "open zone root container dataset (%s): %s\n"),
225 		    zone_container_ds, libzfs_error_description(g_zfs));
226 		return (zfs_err_to_be_err(g_zfs));
227 	}
228 
229 	mzr_data.zone_altroot = zone_altroot;
230 
231 	/*
232 	 * Iterate through all of the zone's BEs, looking for the one
233 	 * that is currently mounted at the zone altroot in the mounted
234 	 * global BE.
235 	 */
236 	if ((zret = zfs_iter_filesystems(zhp,
237 	    be_find_mounted_zone_root_callback, &mzr_data)) == 0) {
238 		be_print_err(gettext("be_find_mounted_zone_root: did not "
239 		    "find mounted zone under altroot zonepath %s\n"),
240 		    zonepath_ds);
241 		ret = BE_ERR_NO_MOUNTED_ZONE;
242 		goto done;
243 	} else if (zret < 0) {
244 		be_print_err(gettext("be_find_mounted_zone_root: "
245 		    "zfs_iter_filesystems failed: %s\n"),
246 		    libzfs_error_description(g_zfs));
247 		ret = zfs_err_to_be_err(g_zfs);
248 		goto done;
249 	}
250 
251 	if (mzr_data.zoneroot_ds != NULL) {
252 		(void) strlcpy(zoneroot_ds, mzr_data.zoneroot_ds,
253 		    zoneroot_ds_size);
254 		free(mzr_data.zoneroot_ds);
255 	}
256 
257 done:
258 	ZFS_CLOSE(zhp);
259 	return (ret);
260 }
261 
262 /*
263  * Function:	be_zone_supported
264  * Description:	This function will determine if a zone is supported
265  *		based on its zonepath dataset.  The zonepath dataset
266  *		must:
267  *		   - not be under any global BE root dataset.
268  *		   - have a root container dataset underneath it.
269  *
270  * Parameters:
271  *		zonepath_ds - name of dataset of the zonepath of the
272  *		zone to check.
273  * Returns:
274  *		B_TRUE - zone is supported
275  *		B_FALSE - zone is not supported
276  * Scope:
277  *		Semi-private (library wide use only)
278  */
279 boolean_t
280 be_zone_supported(char *zonepath_ds)
281 {
282 	char	zone_container_ds[MAXPATHLEN];
283 	int	ret = 0;
284 
285 	/*
286 	 * Make sure the dataset for the zonepath is not hierarchically
287 	 * under any reserved BE root container dataset of any pool.
288 	 */
289 	if ((ret = zpool_iter(g_zfs, be_check_be_roots_callback,
290 	    zonepath_ds)) > 0) {
291 		be_print_err(gettext("be_zone_supported: "
292 		    "zonepath dataset %s not supported\n"), zonepath_ds);
293 		return (B_FALSE);
294 	} else if (ret < 0) {
295 		be_print_err(gettext("be_zone_supported: "
296 		"zpool_iter failed: %s\n"),
297 		    libzfs_error_description(g_zfs));
298 		return (B_FALSE);
299 	}
300 
301 	/*
302 	 * Make sure the zonepath has a zone root container dataset
303 	 * underneath it.
304 	 */
305 	if ((ret = be_make_container_ds(zonepath_ds, zone_container_ds,
306 	    sizeof (zone_container_ds))) != BE_SUCCESS) {
307 		be_print_err(gettext("%s: failed to get BE container dataset "
308 		    "for %s\n"), __func__, zonepath_ds);
309 		return (B_FALSE);
310 	}
311 
312 	if (!zfs_dataset_exists(g_zfs, zone_container_ds,
313 	    ZFS_TYPE_FILESYSTEM)) {
314 		be_print_err(gettext("be_zone_supported: "
315 		    "zonepath dataset (%s) does not have a zone root container "
316 		    "dataset, zone is not supported, skipping ...\n"),
317 		    zonepath_ds);
318 		return (B_FALSE);
319 	}
320 
321 	return (B_TRUE);
322 }
323 
324 /*
325  * Function:	be_get_supported_brandlist
326  * Desciption:	This functions retuns a list of supported brands in
327  *		a zoneBrandList_t object.
328  * Parameters:
329  *		None
330  * Returns:
331  *		Failure - NULL if no supported brands found.
332  *		Success - pointer to zoneBrandList structure.
333  * Scope:
334  *		Semi-private (library wide use only)
335  */
336 zoneBrandList_t *
337 be_get_supported_brandlist(void)
338 {
339 	return (z_make_brand_list(BE_ZONE_SUPPORTED_BRANDS,
340 	    BE_ZONE_SUPPORTED_BRANDS_DELIM));
341 }
342 
343 /*
344  * Function:	be_zone_get_parent_uuid
345  * Description:	This function gets the parentbe property of a zone root
346  *		dataset, parsed it into internal uuid format, and returns
347  *		it in the uuid_t reference pointer passed in.
348  * Parameters:
349  *		root_ds - dataset name of a zone root dataset
350  *		uu - pointer to a uuid_t to return the parentbe uuid in
351  * Returns:
352  *		BE_SUCCESS - Success
353  *		be_errno_t - Failure
354  * Scope:
355  *		Private
356  */
357 int
358 be_zone_get_parent_uuid(const char *root_ds, uuid_t *uu)
359 {
360 	zfs_handle_t	*zhp = NULL;
361 	nvlist_t	*userprops = NULL;
362 	nvlist_t	*propname = NULL;
363 	char		*uu_string = NULL;
364 	int		ret = BE_SUCCESS;
365 
366 	/* Get handle to zone root dataset */
367 	if ((zhp = zfs_open(g_zfs, root_ds, ZFS_TYPE_FILESYSTEM)) == NULL) {
368 		be_print_err(gettext("be_zone_get_parent_uuid: failed to "
369 		    "open zone root dataset (%s): %s\n"), root_ds,
370 		    libzfs_error_description(g_zfs));
371 		return (zfs_err_to_be_err(g_zfs));
372 	}
373 
374 	/* Get user properties for zone root dataset */
375 	if ((userprops = zfs_get_user_props(zhp)) == NULL) {
376 		be_print_err(gettext("be_zone_get_parent_uuid: "
377 		    "failed to get user properties for zone root "
378 		    "dataset (%s): %s\n"), root_ds,
379 		    libzfs_error_description(g_zfs));
380 		ret = zfs_err_to_be_err(g_zfs);
381 		goto done;
382 	}
383 
384 	/* Get UUID string from zone's root dataset user properties */
385 	if (nvlist_lookup_nvlist(userprops, BE_ZONE_PARENTBE_PROPERTY,
386 	    &propname) != 0 || nvlist_lookup_string(propname, ZPROP_VALUE,
387 	    &uu_string) != 0) {
388 		be_print_err(gettext("be_zone_get_parent_uuid: failed to "
389 		    "get parent uuid property from zone root dataset user "
390 		    "properties.\n"));
391 		ret = BE_ERR_ZONE_NO_PARENTBE;
392 		goto done;
393 	}
394 
395 	/* Parse the uuid string into internal format */
396 	if (uuid_parse(uu_string, *uu) != 0 || uuid_is_null(*uu)) {
397 		be_print_err(gettext("be_zone_get_parent_uuid: failed to "
398 		    "parse parentuuid\n"));
399 		ret = BE_ERR_PARSE_UUID;
400 	}
401 
402 done:
403 	ZFS_CLOSE(zhp);
404 	return (ret);
405 }
406 
407 /*
408  * Function:	be_zone_set_parent_uuid
409  * Description:	This function sets parentbe uuid into
410  *		a zfs user property for a root zone dataset.
411  * Parameters:
412  *		root_ds - Root zone dataset of the BE to set a uuid on.
413  * Return:
414  *		be_errno_t - Failure
415  *		BE_SUCCESS - Success
416  * Scope:
417  *		Semi-private (library wide uses only)
418  */
419 int
420 be_zone_set_parent_uuid(char *root_ds, uuid_t uu)
421 {
422 	zfs_handle_t	*zhp = NULL;
423 	char		uu_string[UUID_PRINTABLE_STRING_LENGTH];
424 	int		ret = BE_SUCCESS;
425 
426 	uuid_unparse(uu, uu_string);
427 
428 	/* Get handle to the root zone dataset. */
429 	if ((zhp = zfs_open(g_zfs, root_ds, ZFS_TYPE_FILESYSTEM)) == NULL) {
430 		be_print_err(gettext("be_zone_set_parent_uuid: failed to "
431 		    "open root zone dataset (%s): %s\n"), root_ds,
432 		    libzfs_error_description(g_zfs));
433 		return (zfs_err_to_be_err(g_zfs));
434 	}
435 
436 	/* Set parentbe uuid property for the root zone dataset */
437 	if (zfs_prop_set(zhp, BE_ZONE_PARENTBE_PROPERTY, uu_string) != 0) {
438 		be_print_err(gettext("be_zone_set_parent_uuid: failed to "
439 		    "set parentbe uuid property for root zone dataset: %s\n"),
440 		    libzfs_error_description(g_zfs));
441 		ret = zfs_err_to_be_err(g_zfs);
442 	}
443 
444 	ZFS_CLOSE(zhp);
445 	return (ret);
446 }
447 
448 /*
449  * Function:	be_zone_compare_uuids
450  * Description:	This function compare the parentbe uuid of the
451  *		current running root zone dataset with the parentbe
452  *		uuid of the given root zone dataset.
453  * Parameters:
454  *		root_ds - Root zone dataset of the BE to compare.
455  * Return:
456  *		B_TRUE - root dataset has right parentbe uuid
457  *		B_FALSE - root dataset has wrong parentbe uuid
458  * Scope:
459  *		Semi-private (library wide uses only)
460  */
461 boolean_t
462 be_zone_compare_uuids(char *root_ds)
463 {
464 	char		*active_ds;
465 	uuid_t		parent_uuid = {0};
466 	uuid_t		cur_parent_uuid = {0};
467 
468 	/* Get parentbe uuid from given zone root dataset */
469 	if ((be_zone_get_parent_uuid(root_ds,
470 	    &parent_uuid)) != BE_SUCCESS) {
471 		be_print_err(gettext("be_zone_compare_uuids: failed to get "
472 		    "parentbe uuid from the given BE\n"));
473 		return (B_FALSE);
474 	}
475 
476 	/*
477 	 * Find current running zone root dataset and get it's parentbe
478 	 * uuid property.
479 	 */
480 	if ((active_ds = be_get_ds_from_dir("/")) != NULL) {
481 		if ((be_zone_get_parent_uuid(active_ds,
482 		    &cur_parent_uuid)) != BE_SUCCESS) {
483 			be_print_err(gettext("be_zone_compare_uuids: failed "
484 			"to get parentbe uuid from the current running zone "
485 			"root dataset\n"));
486 			return (B_FALSE);
487 		}
488 	} else {
489 		be_print_err(gettext("be_zone_compare_uuids: zone root dataset "
490 		    "is not mounted\n"));
491 		return (B_FALSE);
492 	}
493 
494 	if (uuid_compare(parent_uuid, cur_parent_uuid) != 0) {
495 		return (B_FALSE);
496 	}
497 
498 	return (B_TRUE);
499 }
500 
501 /* ******************************************************************** */
502 /*			Private Functions				*/
503 /* ******************************************************************** */
504 
505 /*
506  * Function:	be_find_active_zone_root_callback
507  * Description: This function is used as a callback to iterate over all of
508  *		a zone's root datasets, finding the one that is marked active
509  *		for the parent BE specified in the data passed in.  The name
510  *		of the zone's active root dataset is returned in heap storage
511  *		in the active_zone_root_data_t structure passed in, so the
512  *		caller is responsible for freeing it.
513  * Parameters:
514  *		zhp - zfs_handle_t pointer to current dataset being processed
515  *		data - active_zone_root_data_t pointer
516  * Returns:
517  *		0 - Success
518  *		>0 - Failure
519  * Scope:
520  *		Private
521  */
522 static int
523 be_find_active_zone_root_callback(zfs_handle_t *zhp, void *data)
524 {
525 	active_zone_root_data_t	*azr_data = data;
526 	uuid_t			parent_uuid = { 0 };
527 	int			iret = 0;
528 	int			ret = 0;
529 
530 	if ((iret = be_zone_get_parent_uuid(zfs_get_name(zhp), &parent_uuid))
531 	    != BE_SUCCESS) {
532 		be_print_err(gettext("be_find_active_zone_root_callback: "
533 		    "skipping zone root dataset (%s): %s\n"),
534 		    zfs_get_name(zhp), be_err_to_str(iret));
535 		goto done;
536 	}
537 
538 	if (uuid_compare(azr_data->parent_uuid, parent_uuid) == 0) {
539 		/*
540 		 * Found a zone root dataset belonging to the right parent,
541 		 * check if its active.
542 		 */
543 		if (be_zone_get_active(zhp)) {
544 			/*
545 			 * Found active zone root dataset, if its already
546 			 * set in the callback data, that means this
547 			 * is the second one we've found.  Return error.
548 			 */
549 			if (azr_data->zoneroot_ds != NULL) {
550 				ret = BE_ERR_ZONE_MULTIPLE_ACTIVE;
551 				goto done;
552 			}
553 
554 			azr_data->zoneroot_ds = strdup(zfs_get_name(zhp));
555 			if (azr_data->zoneroot_ds == NULL) {
556 				ret = BE_ERR_NOMEM;
557 			}
558 		}
559 	}
560 
561 done:
562 	ZFS_CLOSE(zhp);
563 	return (ret);
564 }
565 
566 /*
567  * Function:	be_find_mounted_zone_root_callback
568  * Description:	This function is used as a callback to iterate over all of
569  *		a zone's root datasets, find the one that is currently
570  *		mounted for the parent BE specified in the data passed in.
571  *		The name of the zone's mounted root dataset is returned in
572  *		heap storage the mounted_zone_data_t structure passed in,
573  *		so the caller is responsible for freeing it.
574  * Parameters:
575  *		zhp - zfs_handle_t pointer to the current dataset being
576  *			processed
577  *		data - mounted_zone_data_t pointer
578  * Returns:
579  *		0 - not mounted as zone's root
580  *		1 - this dataset is mounted as zone's root
581  * Scope:
582  *		Private
583  */
584 static int
585 be_find_mounted_zone_root_callback(zfs_handle_t *zhp, void *data)
586 {
587 	mounted_zone_root_data_t	*mzr_data = data;
588 	char				*mp = NULL;
589 
590 	if (zfs_is_mounted(zhp, &mp) && mp != NULL &&
591 	    strcmp(mp, mzr_data->zone_altroot) == 0) {
592 		mzr_data->zoneroot_ds = strdup(zfs_get_name(zhp));
593 		free(mp);
594 		return (1);
595 	}
596 
597 	free(mp);
598 	return (0);
599 }
600 
601 /*
602  * Function:	be_zone_get_active
603  * Description: This function gets the active property of a zone root
604  *		dataset, and returns true if active property is on.
605  * Parameters:
606  *		zfs - zfs_handle_t pointer to zone root dataset to check
607  * Returns:
608  *		B_TRUE - zone root dataset is active
609  *		B_FALSE - zone root dataset is not active
610  * Scope:
611  *		Private
612  */
613 static boolean_t
614 be_zone_get_active(zfs_handle_t *zhp)
615 {
616 	nvlist_t	*userprops = NULL;
617 	nvlist_t	*propname = NULL;
618 	char		*active_str = NULL;
619 
620 	/* Get user properties for the zone root dataset */
621 	if ((userprops = zfs_get_user_props(zhp)) == NULL) {
622 		be_print_err(gettext("be_zone_get_active: "
623 		    "failed to get user properties for zone root "
624 		    "dataset (%s): %s\n"), zfs_get_name(zhp),
625 		    libzfs_error_description(g_zfs));
626 		return (B_FALSE);
627 	}
628 
629 	/* Get active property from the zone root dataset user properties */
630 	if (nvlist_lookup_nvlist(userprops, BE_ZONE_ACTIVE_PROPERTY, &propname)
631 	    != 0 || nvlist_lookup_string(propname, ZPROP_VALUE, &active_str)
632 	    != 0) {
633 		return (B_FALSE);
634 	}
635 
636 	if (strcmp(active_str, "on") == 0)
637 		return (B_TRUE);
638 
639 	return (B_FALSE);
640 }
641