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