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 2015 Josef 'Jeff' Sipek <jeffpc@josefsipek.net>
  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 };
 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         be_make_container_ds(zonepath_ds, zone_container_ds,
 141             sizeof (zone_container_ds));
 142 
 143         /* Get handle to this zone's root container dataset */
 144         if ((zhp = zfs_open(g_zfs, zone_container_ds, ZFS_TYPE_FILESYSTEM))
 145             == NULL) {
 146                 be_print_err(gettext("be_find_active_zone_root: failed to "
 147                     "open zone root container dataset (%s): %s\n"),
 148                     zone_container_ds, libzfs_error_description(g_zfs));
 149                 return (zfs_err_to_be_err(g_zfs));
 150         }
 151 
 152         /*
 153          * Iterate through all of this zone's BEs, looking for ones
 154          * that belong to the parent global BE, and finding the one
 155          * that is marked active.
 156          */
 157         if ((ret = zfs_iter_filesystems(zhp, be_find_active_zone_root_callback,
 158             &azr_data)) != 0) {
 159                 be_print_err(gettext("be_find_active_zone_root: failed to "
 160                     "find active zone root in zonepath dataset %s: %s\n"),
 161                     zonepath_ds, be_err_to_str(ret));
 162                 goto done;
 163         }
 164 
 165         if (azr_data.zoneroot_ds != NULL) {
 166                 (void) strlcpy(zoneroot_ds, azr_data.zoneroot_ds,
 167                     zoneroot_ds_size);
 168                 free(azr_data.zoneroot_ds);
 169         } else {
 170                 be_print_err(gettext("be_find_active_zone_root: failed to "
 171                     "find active zone root in zonepath dataset %s\n"),
 172                     zonepath_ds);
 173                 ret = BE_ERR_ZONE_NO_ACTIVE_ROOT;
 174         }
 175 
 176 done:
 177         ZFS_CLOSE(zhp);
 178         return (ret);
 179 }
 180 
 181 /*
 182  * Function:    be_find_mounted_zone_root
 183  * Description: This function will find the dataset mounted as the zoneroot
 184  *              of a zone for a given mounted global BE.
 185  * Parameters:
 186  *              zone_altroot - path of zoneroot wrt the mounted global BE.
 187  *              zonepath_ds - dataset of the zone's zonepath.
 188  *              zoneroot_ds - pointer to a buffer to store the dataset of
 189  *                      the zoneroot that currently mounted for this zone
 190  *                      in the mounted global BE.
 191  *              zoneroot_ds_size - size of zoneroot_ds
 192  * Returns:
 193  *              BE_SUCCESS - Success
 194  *              be_errno_t - Failure
 195  * Scope:
 196  *              Semi-private (library wide use only)
 197  */
 198 int
 199 be_find_mounted_zone_root(char *zone_altroot, char *zonepath_ds,
 200     char *zoneroot_ds, int zoneroot_ds_size)
 201 {
 202         mounted_zone_root_data_t        mzr_data = { 0 };
 203         zfs_handle_t    *zhp = NULL;
 204         char            zone_container_ds[MAXPATHLEN];
 205         int             ret = BE_SUCCESS;
 206         int             zret = 0;
 207 
 208         /* Generate string for the root container dataset for this zone. */
 209         be_make_container_ds(zonepath_ds, zone_container_ds,
 210             sizeof (zone_container_ds));
 211 
 212         /* Get handle to this zone's root container dataset. */
 213         if ((zhp = zfs_open(g_zfs, zone_container_ds, ZFS_TYPE_FILESYSTEM))
 214             == NULL) {
 215                 be_print_err(gettext("be_find_mounted_zone_root: failed to "
 216                     "open zone root container dataset (%s): %s\n"),
 217                     zone_container_ds, libzfs_error_description(g_zfs));
 218                 return (zfs_err_to_be_err(g_zfs));
 219         }
 220 
 221         mzr_data.zone_altroot = zone_altroot;
 222 
 223         /*
 224          * Iterate through all of the zone's BEs, looking for the one
 225          * that is currently mounted at the zone altroot in the mounted
 226          * global BE.
 227          */
 228         if ((zret = zfs_iter_filesystems(zhp,
 229             be_find_mounted_zone_root_callback, &mzr_data)) == 0) {
 230                 be_print_err(gettext("be_find_mounted_zone_root: did not "
 231                     "find mounted zone under altroot zonepath %s\n"),
 232                     zonepath_ds);
 233                 ret = BE_ERR_NO_MOUNTED_ZONE;
 234                 goto done;
 235         } else if (zret < 0) {
 236                 be_print_err(gettext("be_find_mounted_zone_root: "
 237                     "zfs_iter_filesystems failed: %s\n"),
 238                     libzfs_error_description(g_zfs));
 239                 ret = zfs_err_to_be_err(g_zfs);
 240                 goto done;
 241         }
 242 
 243         if (mzr_data.zoneroot_ds != NULL) {
 244                 (void) strlcpy(zoneroot_ds, mzr_data.zoneroot_ds,
 245                     zoneroot_ds_size);
 246                 free(mzr_data.zoneroot_ds);
 247         }
 248 
 249 done:
 250         ZFS_CLOSE(zhp);
 251         return (ret);
 252 }
 253 
 254 /*
 255  * Function:    be_zone_supported
 256  * Description: This function will determine if a zone is supported
 257  *              based on its zonepath dataset.  The zonepath dataset
 258  *              must:
 259  *                 - not be under any global BE root dataset.
 260  *                 - have a root container dataset underneath it.
 261  *
 262  * Parameters:
 263  *              zonepath_ds - name of dataset of the zonepath of the
 264  *              zone to check.
 265  * Returns:
 266  *              B_TRUE - zone is supported
 267  *              B_FALSE - zone is not supported
 268  * Scope:
 269  *              Semi-private (library wide use only)
 270  */
 271 boolean_t
 272 be_zone_supported(char *zonepath_ds)
 273 {
 274         char    zone_container_ds[MAXPATHLEN];
 275         int     ret = 0;
 276 
 277         /*
 278          * Make sure the dataset for the zonepath is not hierarchically
 279          * under any reserved BE root container dataset of any pool.
 280          */
 281         if ((ret = zpool_iter(g_zfs, be_check_be_roots_callback,
 282             zonepath_ds)) > 0) {
 283                 be_print_err(gettext("be_zone_supported: "
 284                     "zonepath dataset %s not supported\n"), zonepath_ds);
 285                 return (B_FALSE);
 286         } else if (ret < 0) {
 287                 be_print_err(gettext("be_zone_supported: "
 288                 "zpool_iter failed: %s\n"),
 289                     libzfs_error_description(g_zfs));
 290                 return (B_FALSE);
 291         }
 292 
 293         /*
 294          * Make sure the zonepath has a zone root container dataset
 295          * underneath it.
 296          */
 297         be_make_container_ds(zonepath_ds, zone_container_ds,
 298             sizeof (zone_container_ds));
 299 
 300         if (!zfs_dataset_exists(g_zfs, zone_container_ds,
 301             ZFS_TYPE_FILESYSTEM)) {
 302                 be_print_err(gettext("be_zone_supported: "
 303                     "zonepath dataset (%s) does not have a zone root container "
 304                     "dataset, zone is not supported, skipping ...\n"),
 305                     zonepath_ds);
 306                 return (B_FALSE);
 307         }
 308 
 309         return (B_TRUE);
 310 }
 311 
 312 /*
 313  * Function:    be_zone_get_parent_uuid
 314  * Description: This function gets the parentbe property of a zone root
 315  *              dataset, parsed it into internal uuid format, and returns
 316  *              it in the uuid_t reference pointer passed in.
 317  * Parameters:
 318  *              root_ds - dataset name of a zone root dataset
 319  *              uu - pointer to a uuid_t to return the parentbe uuid in
 320  * Returns:
 321  *              BE_SUCCESS - Success
 322  *              be_errno_t - Failure
 323  * Scope:
 324  *              Private
 325  */
 326 int
 327 be_zone_get_parent_uuid(const char *root_ds, uuid_t *uu)
 328 {
 329         zfs_handle_t    *zhp = NULL;
 330         nvlist_t        *userprops = NULL;
 331         nvlist_t        *propname = NULL;
 332         char            *uu_string = NULL;
 333         int             ret = BE_SUCCESS;
 334 
 335         /* Get handle to zone root dataset */
 336         if ((zhp = zfs_open(g_zfs, root_ds, ZFS_TYPE_FILESYSTEM)) == NULL) {
 337                 be_print_err(gettext("be_zone_get_parent_uuid: failed to "
 338                     "open zone root dataset (%s): %s\n"), root_ds,
 339                     libzfs_error_description(g_zfs));
 340                 return (zfs_err_to_be_err(g_zfs));
 341         }
 342 
 343         /* Get user properties for zone root dataset */
 344         if ((userprops = zfs_get_user_props(zhp)) == NULL) {
 345                 be_print_err(gettext("be_zone_get_parent_uuid: "
 346                     "failed to get user properties for zone root "
 347                     "dataset (%s): %s\n"), root_ds,
 348                     libzfs_error_description(g_zfs));
 349                 ret = zfs_err_to_be_err(g_zfs);
 350                 goto done;
 351         }
 352 
 353         /* Get UUID string from zone's root dataset user properties */
 354         if (nvlist_lookup_nvlist(userprops, BE_ZONE_PARENTBE_PROPERTY,
 355             &propname) != 0 || nvlist_lookup_string(propname, ZPROP_VALUE,
 356             &uu_string) != 0) {
 357                 be_print_err(gettext("be_zone_get_parent_uuid: failed to "
 358                     "get parent uuid property from zone root dataset user "
 359                     "properties.\n"));
 360                 ret = BE_ERR_ZONE_NO_PARENTBE;
 361                 goto done;
 362         }
 363 
 364         /* Parse the uuid string into internal format */
 365         if (uuid_parse(uu_string, *uu) != 0 || uuid_is_null(*uu)) {
 366                 be_print_err(gettext("be_zone_get_parent_uuid: failed to "
 367                     "parse parentuuid\n"));
 368                 ret = BE_ERR_PARSE_UUID;
 369         }
 370 
 371 done:
 372         ZFS_CLOSE(zhp);
 373         return (ret);
 374 }
 375 
 376 /*
 377  * Function:    be_zone_set_parent_uuid
 378  * Description: This function sets parentbe uuid into
 379  *              a zfs user property for a root zone dataset.
 380  * Parameters:
 381  *              root_ds - Root zone dataset of the BE to set a uuid on.
 382  * Return:
 383  *              be_errno_t - Failure
 384  *              BE_SUCCESS - Success
 385  * Scope:
 386  *              Semi-private (library wide uses only)
 387  */
 388 int
 389 be_zone_set_parent_uuid(char *root_ds, uuid_t uu)
 390 {
 391         zfs_handle_t    *zhp = NULL;
 392         char            uu_string[UUID_PRINTABLE_STRING_LENGTH];
 393         int             ret = BE_SUCCESS;
 394 
 395         uuid_unparse(uu, uu_string);
 396 
 397         /* Get handle to the root zone dataset. */
 398         if ((zhp = zfs_open(g_zfs, root_ds, ZFS_TYPE_FILESYSTEM)) == NULL) {
 399                 be_print_err(gettext("be_zone_set_parent_uuid: failed to "
 400                     "open root zone dataset (%s): %s\n"), root_ds,
 401                     libzfs_error_description(g_zfs));
 402                 return (zfs_err_to_be_err(g_zfs));
 403         }
 404 
 405         /* Set parentbe uuid property for the root zone dataset */
 406         if (zfs_prop_set(zhp, BE_ZONE_PARENTBE_PROPERTY, uu_string) != 0) {
 407                 be_print_err(gettext("be_zone_set_parent_uuid: failed to "
 408                     "set parentbe uuid property for root zone dataset: %s\n"),
 409                     libzfs_error_description(g_zfs));
 410                 ret = zfs_err_to_be_err(g_zfs);
 411         }
 412 
 413         ZFS_CLOSE(zhp);
 414         return (ret);
 415 }
 416 
 417 /*
 418  * Function:    be_zone_compare_uuids
 419  * Description: This function compare the parentbe uuid of the
 420  *              current running root zone dataset with the parentbe
 421  *              uuid of the given root zone dataset.
 422  * Parameters:
 423  *              root_ds - Root zone dataset of the BE to compare.
 424  * Return:
 425  *              B_TRUE - root dataset has right parentbe uuid
 426  *              B_FALSE - root dataset has wrong parentbe uuid
 427  * Scope:
 428  *              Semi-private (library wide uses only)
 429  */
 430 boolean_t
 431 be_zone_compare_uuids(char *root_ds)
 432 {
 433         char            *active_ds;
 434         uuid_t          parent_uuid = {0};
 435         uuid_t          cur_parent_uuid = {0};
 436 
 437         /* Get parentbe uuid from given zone root dataset */
 438         if ((be_zone_get_parent_uuid(root_ds,
 439             &parent_uuid)) != BE_SUCCESS) {
 440                 be_print_err(gettext("be_zone_compare_uuids: failed to get "
 441                     "parentbe uuid from the given BE\n"));
 442                 return (B_FALSE);
 443         }
 444 
 445         /*
 446          * Find current running zone root dataset and get it's parentbe
 447          * uuid property.
 448          */
 449         if ((active_ds = be_get_ds_from_dir("/")) != NULL) {
 450                 if ((be_zone_get_parent_uuid(active_ds,
 451                     &cur_parent_uuid)) != BE_SUCCESS) {
 452                         be_print_err(gettext("be_zone_compare_uuids: failed "
 453                         "to get parentbe uuid from the current running zone "
 454                         "root dataset\n"));
 455                         return (B_FALSE);
 456                 }
 457         } else {
 458                 be_print_err(gettext("be_zone_compare_uuids: zone root dataset "
 459                     "is not mounted\n"));
 460                 return (B_FALSE);
 461         }
 462 
 463         if (uuid_compare(parent_uuid, cur_parent_uuid) != 0) {
 464                 return (B_FALSE);
 465         }
 466 
 467         return (B_TRUE);
 468 }
 469 
 470 /* ******************************************************************** */
 471 /*                      Private Functions                               */
 472 /* ******************************************************************** */
 473 
 474 /*
 475  * Function:    be_find_active_zone_root_callback
 476  * Description: This function is used as a callback to iterate over all of
 477  *              a zone's root datasets, finding the one that is marked active
 478  *              for the parent BE specified in the data passed in.  The name
 479  *              of the zone's active root dataset is returned in heap storage
 480  *              in the active_zone_root_data_t structure passed in, so the
 481  *              caller is responsible for freeing it.
 482  * Parameters:
 483  *              zhp - zfs_handle_t pointer to current dataset being processed
 484  *              data - active_zone_root_data_t pointer
 485  * Returns:
 486  *              0 - Success
 487  *              >0 - Failure
 488  * Scope:
 489  *              Private
 490  */
 491 static int
 492 be_find_active_zone_root_callback(zfs_handle_t *zhp, void *data)
 493 {
 494         active_zone_root_data_t *azr_data = data;
 495         uuid_t                  parent_uuid = { 0 };
 496         int                     iret = 0;
 497         int                     ret = 0;
 498 
 499         if ((iret = be_zone_get_parent_uuid(zfs_get_name(zhp), &parent_uuid))
 500             != BE_SUCCESS) {
 501                 be_print_err(gettext("be_find_active_zone_root_callback: "
 502                     "skipping zone root dataset (%s): %s\n"),
 503                     zfs_get_name(zhp), be_err_to_str(iret));
 504                 goto done;
 505         }
 506 
 507         if (uuid_compare(azr_data->parent_uuid, parent_uuid) == 0) {
 508                 /*
 509                  * Found a zone root dataset belonging to the right parent,
 510                  * check if its active.
 511                  */
 512                 if (be_zone_get_active(zhp)) {
 513                         /*
 514                          * Found active zone root dataset, if its already
 515                          * set in the callback data, that means this
 516                          * is the second one we've found.  Return error.
 517                          */
 518                         if (azr_data->zoneroot_ds != NULL) {
 519                                 ret = BE_ERR_ZONE_MULTIPLE_ACTIVE;
 520                                 goto done;
 521                         }
 522 
 523                         azr_data->zoneroot_ds = strdup(zfs_get_name(zhp));
 524                         if (azr_data->zoneroot_ds == NULL) {
 525                                 ret = BE_ERR_NOMEM;
 526                         }
 527                 }
 528         }
 529 
 530 done:
 531         ZFS_CLOSE(zhp);
 532         return (ret);
 533 }
 534 
 535 /*
 536  * Function:    be_find_mounted_zone_root_callback
 537  * Description: This function is used as a callback to iterate over all of
 538  *              a zone's root datasets, find the one that is currently
 539  *              mounted for the parent BE specified in the data passed in.
 540  *              The name of the zone's mounted root dataset is returned in
 541  *              heap storage the mounted_zone_data_t structure passed in,
 542  *              so the caller is responsible for freeing it.
 543  * Parameters:
 544  *              zhp - zfs_handle_t pointer to the current dataset being
 545  *                      processed
 546  *              data - mounted_zone_data_t pointer
 547  * Returns:
 548  *              0 - not mounted as zone's root
 549  *              1 - this dataset is mounted as zone's root
 550  * Scope:
 551  *              Private
 552  */
 553 static int
 554 be_find_mounted_zone_root_callback(zfs_handle_t *zhp, void *data)
 555 {
 556         mounted_zone_root_data_t        *mzr_data = data;
 557         char                            *mp = NULL;
 558 
 559         if (zfs_is_mounted(zhp, &mp) && mp != NULL &&
 560             strcmp(mp, mzr_data->zone_altroot) == 0) {
 561                 mzr_data->zoneroot_ds = strdup(zfs_get_name(zhp));
 562                 free(mp);
 563                 return (1);
 564         }
 565 
 566         free(mp);
 567         return (0);
 568 }
 569 
 570 /*
 571  * Function:    be_zone_get_active
 572  * Description: This function gets the active property of a zone root
 573  *              dataset, and returns true if active property is on.
 574  * Parameters:
 575  *              zfs - zfs_handle_t pointer to zone root dataset to check
 576  * Returns:
 577  *              B_TRUE - zone root dataset is active
 578  *              B_FALSE - zone root dataset is not active
 579  * Scope:
 580  *              Private
 581  */
 582 static boolean_t
 583 be_zone_get_active(zfs_handle_t *zhp)
 584 {
 585         nvlist_t        *userprops = NULL;
 586         nvlist_t        *propname = NULL;
 587         char            *active_str = NULL;
 588 
 589         /* Get user properties for the zone root dataset */
 590         if ((userprops = zfs_get_user_props(zhp)) == NULL) {
 591                 be_print_err(gettext("be_zone_get_active: "
 592                     "failed to get user properties for zone root "
 593                     "dataset (%s): %s\n"), zfs_get_name(zhp),
 594                     libzfs_error_description(g_zfs));
 595                 return (B_FALSE);
 596         }
 597 
 598         /* Get active property from the zone root dataset user properties */
 599         if (nvlist_lookup_nvlist(userprops, BE_ZONE_ACTIVE_PROPERTY, &propname)
 600             != 0 || nvlist_lookup_string(propname, ZPROP_VALUE, &active_str)
 601             != 0) {
 602                 return (B_FALSE);
 603         }
 604 
 605         if (strcmp(active_str, "on") == 0)
 606                 return (B_TRUE);
 607 
 608         return (B_FALSE);
 609 }