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