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  * Copyright 2013 Nexenta Systems, Inc. All rights reserved.
  27  */
  28 
  29 /*
  30  * System includes
  31  */
  32 #include <assert.h>
  33 #include <errno.h>
  34 #include <libgen.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 <sys/zone.h>
  48 #include <sys/mkdev.h>
  49 #include <unistd.h>
  50 
  51 #include <libbe.h>
  52 #include <libbe_priv.h>
  53 
  54 #define BE_TMP_MNTPNT           "/tmp/.be.XXXXXX"
  55 
  56 typedef struct dir_data {
  57         char *dir;
  58         char *ds;
  59 } dir_data_t;
  60 
  61 /* Private function prototypes */
  62 static int be_mount_callback(zfs_handle_t *, void *);
  63 static int be_unmount_callback(zfs_handle_t *, void *);
  64 static int be_get_legacy_fs_callback(zfs_handle_t *, void *);
  65 static int fix_mountpoint(zfs_handle_t *);
  66 static int fix_mountpoint_callback(zfs_handle_t *, void *);
  67 static int get_mountpoint_from_vfstab(char *, const char *, char *, size_t,
  68     boolean_t);
  69 static int loopback_mount_shared_fs(zfs_handle_t *, be_mount_data_t *);
  70 static int loopback_mount_zonepath(const char *, be_mount_data_t *);
  71 static int iter_shared_fs_callback(zfs_handle_t *, void *);
  72 static int zpool_shared_fs_callback(zpool_handle_t *, void *);
  73 static int unmount_shared_fs(be_unmount_data_t *);
  74 static int add_to_fs_list(be_fs_list_data_t *, const char *);
  75 static int be_mount_root(zfs_handle_t *, char *);
  76 static int be_unmount_root(zfs_handle_t *, be_unmount_data_t *);
  77 static int be_mount_zones(zfs_handle_t *, be_mount_data_t *);
  78 static int be_unmount_zones(be_unmount_data_t *);
  79 static int be_mount_one_zone(zfs_handle_t *, be_mount_data_t *, char *, char *,
  80     char *);
  81 static int be_unmount_one_zone(be_unmount_data_t *, char *, char *, char *);
  82 static int be_get_ds_from_dir_callback(zfs_handle_t *, void *);
  83 
  84 
  85 /* ******************************************************************** */
  86 /*                      Public Functions                                */
  87 /* ******************************************************************** */
  88 
  89 /*
  90  * Function:    be_mount
  91  * Description: Mounts a BE and its subordinate datasets at a given mountpoint.
  92  * Parameters:
  93  *              be_attrs - pointer to nvlist_t of attributes being passed in.
  94  *                      The following attributes are used by this function:
  95  *
  96  *                      BE_ATTR_ORIG_BE_NAME            *required
  97  *                      BE_ATTR_MOUNTPOINT              *required
  98  *                      BE_ATTR_MOUNT_FLAGS             *optional
  99  * Return:
 100  *              BE_SUCCESS - Success
 101  *              be_errno_t - Failure
 102  * Scope:
 103  *              Public
 104  */
 105 int
 106 be_mount(nvlist_t *be_attrs)
 107 {
 108         char            *be_name = NULL;
 109         char            *mountpoint = NULL;
 110         uint16_t        flags = 0;
 111         int             ret = BE_SUCCESS;
 112 
 113         /* Initialize libzfs handle */
 114         if (!be_zfs_init())
 115                 return (BE_ERR_INIT);
 116 
 117         /* Get original BE name */
 118         if (nvlist_lookup_string(be_attrs, BE_ATTR_ORIG_BE_NAME, &be_name)
 119             != 0) {
 120                 be_print_err(gettext("be_mount: failed to lookup "
 121                     "BE_ATTR_ORIG_BE_NAME attribute\n"));
 122                 return (BE_ERR_INVAL);
 123         }
 124 
 125         /* Validate original BE name */
 126         if (!be_valid_be_name(be_name)) {
 127                 be_print_err(gettext("be_mount: invalid BE name %s\n"),
 128                     be_name);
 129                 return (BE_ERR_INVAL);
 130         }
 131 
 132         /* Get mountpoint */
 133         if (nvlist_lookup_string(be_attrs, BE_ATTR_MOUNTPOINT, &mountpoint)
 134             != 0) {
 135                 be_print_err(gettext("be_mount: failed to lookup "
 136                     "BE_ATTR_MOUNTPOINT attribute\n"));
 137                 return (BE_ERR_INVAL);
 138         }
 139 
 140         /* Get flags */
 141         if (nvlist_lookup_pairs(be_attrs, NV_FLAG_NOENTOK,
 142             BE_ATTR_MOUNT_FLAGS, DATA_TYPE_UINT16, &flags, NULL) != 0) {
 143                 be_print_err(gettext("be_mount: failed to lookup "
 144                     "BE_ATTR_MOUNT_FLAGS attribute\n"));
 145                 return (BE_ERR_INVAL);
 146         }
 147 
 148         ret = _be_mount(be_name, &mountpoint, flags);
 149 
 150         be_zfs_fini();
 151 
 152         return (ret);
 153 }
 154 
 155 /*
 156  * Function:    be_unmount
 157  * Description: Unmounts a BE and its subordinate datasets.
 158  * Parameters:
 159  *              be_attrs - pointer to nvlist_t of attributes being passed in.
 160  *                      The following attributes are used by this function:
 161  *
 162  *                      BE_ATTR_ORIG_BE_NAME            *required
 163  *                      BE_ATTR_UNMOUNT_FLAGS           *optional
 164  * Return:
 165  *              BE_SUCCESS - Success
 166  *              be_errno_t - Failure
 167  * Scope:
 168  *              Public
 169  */
 170 int
 171 be_unmount(nvlist_t *be_attrs)
 172 {
 173         char            *be_name = NULL;
 174         char            *be_name_mnt = NULL;
 175         char            *ds = NULL;
 176         uint16_t        flags = 0;
 177         int             ret = BE_SUCCESS;
 178 
 179         /* Initialize libzfs handle */
 180         if (!be_zfs_init())
 181                 return (BE_ERR_INIT);
 182 
 183         /* Get original BE name */
 184         if (nvlist_lookup_string(be_attrs, BE_ATTR_ORIG_BE_NAME, &be_name)
 185             != 0) {
 186                 be_print_err(gettext("be_unmount: failed to lookup "
 187                     "BE_ATTR_ORIG_BE_NAME attribute\n"));
 188                 return (BE_ERR_INVAL);
 189         }
 190 
 191         /* Check if we have mountpoint argument instead of BE name */
 192         if (be_name[0] == '/') {
 193                 if ((ds = be_get_ds_from_dir(be_name)) != NULL) {
 194                         if ((be_name_mnt = strrchr(ds, '/')) != NULL) {
 195                                 free(be_name);
 196                                 be_name = be_name_mnt + 1;
 197                         }
 198                 } else {
 199                         be_print_err(gettext("be_unmount: no datasets mounted "
 200                             "at '%s'\n"), be_name);
 201                         return (BE_ERR_INVAL);
 202                 }
 203         }
 204 
 205         /* Validate original BE name */
 206         if (!be_valid_be_name(be_name)) {
 207                 be_print_err(gettext("be_unmount: invalid BE name %s\n"),
 208                     be_name);
 209                 return (BE_ERR_INVAL);
 210         }
 211 
 212         /* Get unmount flags */
 213         if (nvlist_lookup_pairs(be_attrs, NV_FLAG_NOENTOK,
 214             BE_ATTR_UNMOUNT_FLAGS, DATA_TYPE_UINT16, &flags, NULL) != 0) {
 215                 be_print_err(gettext("be_unmount: failed to loookup "
 216                     "BE_ATTR_UNMOUNT_FLAGS attribute\n"));
 217                 return (BE_ERR_INVAL);
 218         }
 219 
 220         ret = _be_unmount(be_name, flags);
 221 
 222         be_zfs_fini();
 223 
 224         return (ret);
 225 }
 226 
 227 /* ******************************************************************** */
 228 /*                      Semi-Private Functions                          */
 229 /* ******************************************************************** */
 230 
 231 /*
 232  * Function:    _be_mount
 233  * Description: Mounts a BE.  If the altroot is not provided, this function
 234  *              will generate a temporary mountpoint to mount the BE at.  It
 235  *              will return this temporary mountpoint to the caller via the
 236  *              altroot reference pointer passed in.  This returned value is
 237  *              allocated on heap storage and is the repsonsibility of the
 238  *              caller to free.
 239  * Parameters:
 240  *              be_name - pointer to name of BE to mount.
 241  *              altroot - reference pointer to altroot of where to mount BE.
 242  *              flags - flag indicating special handling for mounting the BE
 243  * Return:
 244  *              BE_SUCCESS - Success
 245  *              be_errno_t - Failure
 246  * Scope:
 247  *              Semi-private (library wide use only)
 248  */
 249 int
 250 _be_mount(char *be_name, char **altroot, int flags)
 251 {
 252         be_transaction_data_t   bt = { 0 };
 253         be_mount_data_t md = { 0 };
 254         zfs_handle_t    *zhp;
 255         char            obe_root_ds[MAXPATHLEN];
 256         char            *mp = NULL;
 257         char            *tmp_altroot = NULL;
 258         int             ret = BE_SUCCESS, err = 0;
 259         uuid_t          uu = { 0 };
 260         boolean_t       gen_tmp_altroot = B_FALSE;
 261 
 262         if (be_name == NULL || altroot == NULL)
 263                 return (BE_ERR_INVAL);
 264 
 265         /* Set be_name as obe_name in bt structure */
 266         bt.obe_name = be_name;
 267 
 268         /* Find which zpool obe_name lives in */
 269         if ((err = zpool_iter(g_zfs, be_find_zpool_callback, &bt)) == 0) {
 270                 be_print_err(gettext("be_mount: failed to "
 271                     "find zpool for BE (%s)\n"), bt.obe_name);
 272                 return (BE_ERR_BE_NOENT);
 273         } else if (err < 0) {
 274                 be_print_err(gettext("be_mount: zpool_iter failed: %s\n"),
 275                     libzfs_error_description(g_zfs));
 276                 return (zfs_err_to_be_err(g_zfs));
 277         }
 278 
 279         /* Generate string for obe_name's root dataset */
 280         be_make_root_ds(bt.obe_zpool, bt.obe_name, obe_root_ds,
 281             sizeof (obe_root_ds));
 282         bt.obe_root_ds = obe_root_ds;
 283 
 284         /* Get handle to BE's root dataset */
 285         if ((zhp = zfs_open(g_zfs, bt.obe_root_ds, ZFS_TYPE_FILESYSTEM)) ==
 286             NULL) {
 287                 be_print_err(gettext("be_mount: failed to "
 288                     "open BE root dataset (%s): %s\n"), bt.obe_root_ds,
 289                     libzfs_error_description(g_zfs));
 290                 return (zfs_err_to_be_err(g_zfs));
 291         }
 292 
 293         /* Make sure BE's root dataset isn't already mounted somewhere */
 294         if (zfs_is_mounted(zhp, &mp)) {
 295                 ZFS_CLOSE(zhp);
 296                 be_print_err(gettext("be_mount: %s is already mounted "
 297                     "at %s\n"), bt.obe_name, mp != NULL ? mp : "");
 298                 free(mp);
 299                 return (BE_ERR_MOUNTED);
 300         }
 301 
 302         /*
 303          * Fix this BE's mountpoint if its root dataset isn't set to
 304          * either 'legacy' or '/'.
 305          */
 306         if ((ret = fix_mountpoint(zhp)) != BE_SUCCESS) {
 307                 be_print_err(gettext("be_mount: mountpoint check "
 308                     "failed for %s\n"), bt.obe_root_ds);
 309                 ZFS_CLOSE(zhp);
 310                 return (ret);
 311         }
 312 
 313         /*
 314          * If altroot not provided, create a temporary alternate root
 315          * to mount on
 316          */
 317         if (*altroot == NULL) {
 318                 if ((ret = be_make_tmp_mountpoint(&tmp_altroot))
 319                     != BE_SUCCESS) {
 320                         be_print_err(gettext("be_mount: failed to "
 321                             "make temporary mountpoint\n"));
 322                         ZFS_CLOSE(zhp);
 323                         return (ret);
 324                 }
 325                 gen_tmp_altroot = B_TRUE;
 326         } else {
 327                 tmp_altroot = *altroot;
 328         }
 329 
 330         md.altroot = tmp_altroot;
 331         md.shared_fs = flags & BE_MOUNT_FLAG_SHARED_FS;
 332         md.shared_rw = flags & BE_MOUNT_FLAG_SHARED_RW;
 333 
 334         /* Mount the BE's root file system */
 335         if (getzoneid() == GLOBAL_ZONEID) {
 336                 if ((ret = be_mount_root(zhp, tmp_altroot)) != BE_SUCCESS) {
 337                         be_print_err(gettext("be_mount: failed to "
 338                             "mount BE root file system\n"));
 339                         if (gen_tmp_altroot)
 340                                 free(tmp_altroot);
 341                         ZFS_CLOSE(zhp);
 342                         return (ret);
 343                 }
 344         } else {
 345                 /* Legacy mount the zone root dataset */
 346                 if ((ret = be_mount_zone_root(zhp, &md)) != BE_SUCCESS) {
 347                         be_print_err(gettext("be_mount: failed to "
 348                             "mount BE zone root file system\n"));
 349                         free(md.altroot);
 350                         ZFS_CLOSE(zhp);
 351                         return (ret);
 352                 }
 353         }
 354 
 355         /* Iterate through BE's children filesystems */
 356         if ((err = zfs_iter_filesystems(zhp, be_mount_callback,
 357             tmp_altroot)) != 0) {
 358                 be_print_err(gettext("be_mount: failed to "
 359                     "mount BE (%s) on %s\n"), bt.obe_name, tmp_altroot);
 360                 if (gen_tmp_altroot)
 361                         free(tmp_altroot);
 362                 ZFS_CLOSE(zhp);
 363                 return (err);
 364         }
 365 
 366         /*
 367          * Mount shared file systems if mount flag says so.
 368          */
 369         if (md.shared_fs) {
 370                 /*
 371                  * Mount all ZFS file systems not under the BE's root dataset
 372                  */
 373                 (void) zpool_iter(g_zfs, zpool_shared_fs_callback, &md);
 374 
 375                 /* TODO: Mount all non-ZFS file systems - Not supported yet */
 376         }
 377 
 378         /*
 379          * If we're in the global zone and the global zone has a valid uuid,
 380          * mount all supported non-global zones.
 381          */
 382         if (getzoneid() == GLOBAL_ZONEID &&
 383             !(flags & BE_MOUNT_FLAG_NO_ZONES) &&
 384             be_get_uuid(bt.obe_root_ds, &uu) == BE_SUCCESS) {
 385                 if ((ret = be_mount_zones(zhp, &md)) != BE_SUCCESS) {
 386                         (void) _be_unmount(bt.obe_name, 0);
 387                         if (gen_tmp_altroot)
 388                                 free(tmp_altroot);
 389                         ZFS_CLOSE(zhp);
 390                         return (ret);
 391                 }
 392         }
 393 
 394         ZFS_CLOSE(zhp);
 395 
 396         /*
 397          * If a NULL altroot was passed in, pass the generated altroot
 398          * back to the caller in altroot.
 399          */
 400         if (gen_tmp_altroot)
 401                 *altroot = tmp_altroot;
 402 
 403         return (BE_SUCCESS);
 404 }
 405 
 406 /*
 407  * Function:    _be_unmount
 408  * Description: Unmount a BE.
 409  * Parameters:
 410  *              be_name - pointer to name of BE to unmount.
 411  *              flags - flags for unmounting the BE.
 412  * Returns:
 413  *              BE_SUCCESS - Success
 414  *              be_errno_t - Failure
 415  * Scope:
 416  *              Semi-private (library wide use only)
 417  */
 418 int
 419 _be_unmount(char *be_name, int flags)
 420 {
 421         be_transaction_data_t   bt = { 0 };
 422         be_unmount_data_t       ud = { 0 };
 423         zfs_handle_t    *zhp;
 424         uuid_t          uu = { 0 };
 425         char            obe_root_ds[MAXPATHLEN];
 426         char            mountpoint[MAXPATHLEN];
 427         char            *mp = NULL;
 428         int             ret = BE_SUCCESS;
 429         int             zret = 0;
 430 
 431         if (be_name == NULL)
 432                 return (BE_ERR_INVAL);
 433 
 434         /* Set be_name as obe_name in bt structure */
 435         bt.obe_name = be_name;
 436 
 437         /* Find which zpool obe_name lives in */
 438         if ((zret = zpool_iter(g_zfs, be_find_zpool_callback, &bt)) == 0) {
 439                 be_print_err(gettext("be_unmount: failed to "
 440                     "find zpool for BE (%s)\n"), bt.obe_name);
 441                 return (BE_ERR_BE_NOENT);
 442         } else if (zret < 0) {
 443                 be_print_err(gettext("be_unmount: "
 444                     "zpool_iter failed: %s\n"),
 445                     libzfs_error_description(g_zfs));
 446                 ret = zfs_err_to_be_err(g_zfs);
 447                 return (ret);
 448         }
 449 
 450         /* Generate string for obe_name's root dataset */
 451         be_make_root_ds(bt.obe_zpool, bt.obe_name, obe_root_ds,
 452             sizeof (obe_root_ds));
 453         bt.obe_root_ds = obe_root_ds;
 454 
 455         /* Get handle to BE's root dataset */
 456         if ((zhp = zfs_open(g_zfs, bt.obe_root_ds, ZFS_TYPE_FILESYSTEM)) ==
 457             NULL) {
 458                 be_print_err(gettext("be_unmount: failed to "
 459                     "open BE root dataset (%s): %s\n"), bt.obe_root_ds,
 460                     libzfs_error_description(g_zfs));
 461                 ret = zfs_err_to_be_err(g_zfs);
 462                 return (ret);
 463         }
 464 
 465         /* Make sure BE's root dataset is mounted somewhere */
 466         if (!zfs_is_mounted(zhp, &mp)) {
 467 
 468                 be_print_err(gettext("be_unmount: "
 469                     "(%s) not mounted\n"), bt.obe_name);
 470 
 471                 /*
 472                  * BE is not mounted, fix this BE's mountpoint if its root
 473                  * dataset isn't set to either 'legacy' or '/'.
 474                  */
 475                 if ((ret = fix_mountpoint(zhp)) != BE_SUCCESS) {
 476                         be_print_err(gettext("be_unmount: mountpoint check "
 477                             "failed for %s\n"), bt.obe_root_ds);
 478                         ZFS_CLOSE(zhp);
 479                         return (ret);
 480                 }
 481 
 482                 ZFS_CLOSE(zhp);
 483                 return (BE_ERR_NOTMOUNTED);
 484         }
 485 
 486         /*
 487          * If we didn't get a mountpoint from the zfs_is_mounted call,
 488          * try and get it from its property.
 489          */
 490         if (mp == NULL) {
 491                 if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mountpoint,
 492                     sizeof (mountpoint), NULL, NULL, 0, B_FALSE) != 0) {
 493                         be_print_err(gettext("be_unmount: failed to "
 494                             "get mountpoint of (%s)\n"), bt.obe_name);
 495                         ZFS_CLOSE(zhp);
 496                         return (BE_ERR_ZFS);
 497                 }
 498         } else {
 499                 (void) strlcpy(mountpoint, mp, sizeof (mountpoint));
 500                 free(mp);
 501         }
 502 
 503         /* If BE mounted as current root, fail */
 504         if (strcmp(mountpoint, "/") == 0) {
 505                 be_print_err(gettext("be_unmount: "
 506                     "cannot unmount currently running BE\n"));
 507                 ZFS_CLOSE(zhp);
 508                 return (BE_ERR_UMOUNT_CURR_BE);
 509         }
 510 
 511         ud.altroot = mountpoint;
 512         ud.force = flags & BE_UNMOUNT_FLAG_FORCE;
 513 
 514         /* Unmount all supported non-global zones if we're in the global zone */
 515         if (getzoneid() == GLOBAL_ZONEID &&
 516             be_get_uuid(bt.obe_root_ds, &uu) == BE_SUCCESS) {
 517                 if ((ret = be_unmount_zones(&ud)) != BE_SUCCESS) {
 518                         ZFS_CLOSE(zhp);
 519                         return (ret);
 520                 }
 521         }
 522 
 523         /* TODO: Unmount all non-ZFS file systems - Not supported yet */
 524 
 525         /* Unmount all ZFS file systems not under the BE root dataset */
 526         if ((ret = unmount_shared_fs(&ud)) != BE_SUCCESS) {
 527                 be_print_err(gettext("be_unmount: failed to "
 528                     "unmount shared file systems\n"));
 529                 ZFS_CLOSE(zhp);
 530                 return (ret);
 531         }
 532 
 533         /* Unmount all children datasets under the BE's root dataset */
 534         if ((zret = zfs_iter_filesystems(zhp, be_unmount_callback,
 535             &ud)) != 0) {
 536                 be_print_err(gettext("be_unmount: failed to "
 537                     "unmount BE (%s)\n"), bt.obe_name);
 538                 ZFS_CLOSE(zhp);
 539                 return (zret);
 540         }
 541 
 542         /* Unmount this BE's root filesystem */
 543         if (getzoneid() == GLOBAL_ZONEID) {
 544                 if ((ret = be_unmount_root(zhp, &ud)) != BE_SUCCESS) {
 545                         ZFS_CLOSE(zhp);
 546                         return (ret);
 547                 }
 548         } else {
 549                 if ((ret = be_unmount_zone_root(zhp, &ud)) != BE_SUCCESS) {
 550                         ZFS_CLOSE(zhp);
 551                         return (ret);
 552                 }
 553         }
 554 
 555         ZFS_CLOSE(zhp);
 556 
 557         return (BE_SUCCESS);
 558 }
 559 
 560 /*
 561  * Function:    be_mount_zone_root
 562  * Description: Mounts the zone root dataset for a zone.
 563  * Parameters:
 564  *              zfs - zfs_handle_t pointer to zone root dataset
 565  *              md - be_mount_data_t pointer to data for zone to be mounted
 566  * Returns:
 567  *              BE_SUCCESS - Success
 568  *              be_errno_t - Failure
 569  * Scope:
 570  *              Semi-private (library wide use only)
 571  */
 572 int
 573 be_mount_zone_root(zfs_handle_t *zhp, be_mount_data_t *md)
 574 {
 575         struct stat buf;
 576         char    mountpoint[MAXPATHLEN];
 577         int     err = 0;
 578 
 579         /* Get mountpoint property of dataset */
 580         if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mountpoint,
 581             sizeof (mountpoint), NULL, NULL, 0, B_FALSE) != 0) {
 582                 be_print_err(gettext("be_mount_zone_root: failed to "
 583                     "get mountpoint property for %s: %s\n"), zfs_get_name(zhp),
 584                     libzfs_error_description(g_zfs));
 585                 return (zfs_err_to_be_err(g_zfs));
 586         }
 587 
 588         /*
 589          * Make sure zone's root dataset is set to 'legacy'.  This is
 590          * currently a requirement in this implementation of zones
 591          * support.
 592          */
 593         if (strcmp(mountpoint, ZFS_MOUNTPOINT_LEGACY) != 0) {
 594                 be_print_err(gettext("be_mount_zone_root: "
 595                     "zone root dataset mountpoint is not 'legacy'\n"));
 596                 return (BE_ERR_ZONE_ROOT_NOT_LEGACY);
 597         }
 598 
 599         /* Create the mountpoint if it doesn't exist */
 600         if (lstat(md->altroot, &buf) != 0) {
 601                 if (mkdirp(md->altroot, 0755) != 0) {
 602                         err = errno;
 603                         be_print_err(gettext("be_mount_zone_root: failed "
 604                             "to create mountpoint %s\n"), md->altroot);
 605                         return (errno_to_be_err(err));
 606                 }
 607         }
 608 
 609         /*
 610          * Legacy mount the zone root dataset.
 611          *
 612          * As a workaround for 6176743, we mount the zone's root with the
 613          * MS_OVERLAY option in case an alternate BE is mounted, and we're
 614          * mounting the root for the zone from the current BE here.  When an
 615          * alternate BE is mounted, it ties up the zone's zoneroot directory
 616          * for the current BE since the zone's zonepath is loopback mounted
 617          * from the current BE.
 618          *
 619          * TODO: The MS_OVERLAY option needs to be removed when 6176743
 620          * is fixed.
 621          */
 622         if (mount(zfs_get_name(zhp), md->altroot, MS_OVERLAY, MNTTYPE_ZFS,
 623             NULL, 0, NULL, 0) != 0) {
 624                 err = errno;
 625                 be_print_err(gettext("be_mount_zone_root: failed to "
 626                     "legacy mount zone root dataset (%s) at %s\n"),
 627                     zfs_get_name(zhp), md->altroot);
 628                 return (errno_to_be_err(err));
 629         }
 630 
 631         return (BE_SUCCESS);
 632 }
 633 
 634 /*
 635  * Function:    be_unmount_zone_root
 636  * Description: Unmounts the zone root dataset for a zone.
 637  * Parameters:
 638  *              zhp - zfs_handle_t pointer to zone root dataset
 639  *              ud - be_unmount_data_t pointer to data for zone to be unmounted
 640  * Returns:
 641  *              BE_SUCCESS - Success
 642  *              be_errno_t - Failure
 643  * Scope:
 644  *              Semi-private (library wise use only)
 645  */
 646 int
 647 be_unmount_zone_root(zfs_handle_t *zhp, be_unmount_data_t *ud)
 648 {
 649         char    mountpoint[MAXPATHLEN];
 650 
 651         /* Unmount the dataset */
 652         if (zfs_unmount(zhp, NULL, ud->force ? MS_FORCE : 0) != 0) {
 653                 be_print_err(gettext("be_unmount_zone_root: failed to "
 654                     "unmount zone root dataset %s: %s\n"), zfs_get_name(zhp),
 655                     libzfs_error_description(g_zfs));
 656                 return (zfs_err_to_be_err(g_zfs));
 657         }
 658 
 659         /* Get the current mountpoint property for the zone root dataset */
 660         if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mountpoint,
 661             sizeof (mountpoint), NULL, NULL, 0, B_FALSE) != 0) {
 662                 be_print_err(gettext("be_unmount_zone_root: failed to "
 663                     "get mountpoint property for zone root dataset (%s): %s\n"),
 664                     zfs_get_name(zhp), libzfs_error_description(g_zfs));
 665                 return (zfs_err_to_be_err(g_zfs));
 666         }
 667 
 668         /* If mountpoint not already set to 'legacy', set it to 'legacy' */
 669         if (strcmp(mountpoint, ZFS_MOUNTPOINT_LEGACY) != 0) {
 670                 if (zfs_prop_set(zhp, zfs_prop_to_name(ZFS_PROP_MOUNTPOINT),
 671                     ZFS_MOUNTPOINT_LEGACY) != 0) {
 672                         be_print_err(gettext("be_unmount_zone_root: "
 673                             "failed to set mountpoint of zone root dataset "
 674                             "%s to 'legacy': %s\n"), zfs_get_name(zhp),
 675                             libzfs_error_description(g_zfs));
 676                         return (zfs_err_to_be_err(g_zfs));
 677                 }
 678         }
 679 
 680         return (BE_SUCCESS);
 681 }
 682 
 683 /*
 684  * Function:    be_get_legacy_fs
 685  * Description: This function iterates through all non-shared file systems
 686  *              of a BE and finds the ones with a legacy mountpoint.  For
 687  *              those file systems, it reads the BE's vfstab to get the
 688  *              mountpoint.  If found, it adds that file system to the
 689  *              be_fs_list_data_t passed in.
 690  *
 691  *              This function can be used to gather legacy mounted file systems
 692  *              for both global BEs and non-global zone BEs.  To get data for
 693  *              a non-global zone BE, the zoneroot_ds and zoneroot parameters
 694  *              will be specified, otherwise they should be set to NULL.
 695  * Parameters:
 696  *              be_name - global BE name from which to get legacy file
 697  *                      system list.
 698  *              be_root_ds - root dataset of global BE.
 699  *              zoneroot_ds - root dataset of zone.
 700  *              zoneroot - zoneroot path of zone.
 701  *              fld - be_fs_list_data_t pointer.
 702  * Returns:
 703  *              BE_SUCCESS - Success
 704  *              be_errno_t - Failure
 705  * Scope:
 706  *              Semi-private (library wide use only)
 707  */
 708 int
 709 be_get_legacy_fs(char *be_name, char *be_root_ds, char *zoneroot_ds,
 710     char *zoneroot, be_fs_list_data_t *fld)
 711 {
 712         zfs_handle_t            *zhp = NULL;
 713         char                    mountpoint[MAXPATHLEN];
 714         boolean_t               mounted_here = B_FALSE;
 715         boolean_t               zone_mounted_here = B_FALSE;
 716         int                     ret = BE_SUCCESS, err = 0;
 717 
 718         if (be_name == NULL || be_root_ds == NULL || fld == NULL)
 719                 return (BE_ERR_INVAL);
 720 
 721         /* Get handle to BE's root dataset */
 722         if ((zhp = zfs_open(g_zfs, be_root_ds, ZFS_TYPE_FILESYSTEM))
 723             == NULL) {
 724                 be_print_err(gettext("be_get_legacy_fs: failed to "
 725                     "open BE root dataset (%s): %s\n"), be_root_ds,
 726                     libzfs_error_description(g_zfs));
 727                 ret = zfs_err_to_be_err(g_zfs);
 728                 return (ret);
 729         }
 730 
 731         /* If BE is not already mounted, mount it. */
 732         if (!zfs_is_mounted(zhp, &fld->altroot)) {
 733                 if ((ret = _be_mount(be_name, &fld->altroot,
 734                     zoneroot_ds ? BE_MOUNT_FLAG_NULL :
 735                     BE_MOUNT_FLAG_NO_ZONES)) != BE_SUCCESS) {
 736                         be_print_err(gettext("be_get_legacy_fs: "
 737                             "failed to mount BE %s\n"), be_name);
 738                         goto cleanup;
 739                 }
 740 
 741                 mounted_here = B_TRUE;
 742         } else if (fld->altroot == NULL) {
 743                 be_print_err(gettext("be_get_legacy_fs: failed to "
 744                     "get altroot of mounted BE %s: %s\n"),
 745                     be_name, libzfs_error_description(g_zfs));
 746                 ret = zfs_err_to_be_err(g_zfs);
 747                 goto cleanup;
 748         }
 749 
 750         /*
 751          * If a zone root dataset was passed in, we're wanting to get
 752          * legacy mounted file systems for that zone, not the global
 753          * BE.
 754          */
 755         if (zoneroot_ds != NULL) {
 756                 be_mount_data_t         zone_md = { 0 };
 757 
 758                 /* Close off handle to global BE's root dataset */
 759                 ZFS_CLOSE(zhp);
 760 
 761                 /* Get handle to zone's root dataset */
 762                 if ((zhp = zfs_open(g_zfs, zoneroot_ds,
 763                     ZFS_TYPE_FILESYSTEM)) == NULL) {
 764                         be_print_err(gettext("be_get_legacy_fs: failed to "
 765                             "open zone BE root dataset (%s): %s\n"),
 766                             zoneroot_ds, libzfs_error_description(g_zfs));
 767                         ret = zfs_err_to_be_err(g_zfs);
 768                         goto cleanup;
 769                 }
 770 
 771                 /* Make sure the zone we're looking for is mounted */
 772                 if (!zfs_is_mounted(zhp, &zone_md.altroot)) {
 773                         char    zone_altroot[MAXPATHLEN];
 774 
 775                         /* Generate alternate root path for zone */
 776                         (void) snprintf(zone_altroot, sizeof (zone_altroot),
 777                             "%s%s", fld->altroot, zoneroot);
 778                         if ((zone_md.altroot = strdup(zone_altroot)) == NULL) {
 779                                 be_print_err(gettext("be_get_legacy_fs: "
 780                                     "memory allocation failed\n"));
 781                                 ret = BE_ERR_NOMEM;
 782                                 goto cleanup;
 783                         }
 784 
 785                         if ((ret = be_mount_zone_root(zhp, &zone_md))
 786                             != BE_SUCCESS) {
 787                                 be_print_err(gettext("be_get_legacy_fs: "
 788                                     "failed to mount zone root %s\n"),
 789                                     zoneroot_ds);
 790                                 free(zone_md.altroot);
 791                                 zone_md.altroot = NULL;
 792                                 goto cleanup;
 793                         }
 794                         zone_mounted_here = B_TRUE;
 795                 }
 796 
 797                 free(fld->altroot);
 798                 fld->altroot = zone_md.altroot;
 799         }
 800 
 801         /*
 802          * If the root dataset is in the vfstab with a mountpoint of "/",
 803          * add it to the list
 804          */
 805         if (get_mountpoint_from_vfstab(fld->altroot, zfs_get_name(zhp),
 806             mountpoint, sizeof (mountpoint), B_FALSE) == BE_SUCCESS) {
 807                 if (strcmp(mountpoint, "/") == 0) {
 808                         if (add_to_fs_list(fld, zfs_get_name(zhp))
 809                             != BE_SUCCESS) {
 810                                 be_print_err(gettext("be_get_legacy_fs: "
 811                                     "failed to add %s to fs list\n"),
 812                                     zfs_get_name(zhp));
 813                                 ret = BE_ERR_INVAL;
 814                                 goto cleanup;
 815                         }
 816                 }
 817         }
 818 
 819         /* Iterate subordinate file systems looking for legacy mounts */
 820         if ((ret = zfs_iter_filesystems(zhp, be_get_legacy_fs_callback,
 821             fld)) != 0) {
 822                 be_print_err(gettext("be_get_legacy_fs: "
 823                     "failed to iterate  %s to get legacy mounts\n"),
 824                     zfs_get_name(zhp));
 825         }
 826 
 827 cleanup:
 828         /* If we mounted the zone BE, unmount it */
 829         if (zone_mounted_here) {
 830                 be_unmount_data_t       zone_ud = { 0 };
 831 
 832                 zone_ud.altroot = fld->altroot;
 833                 zone_ud.force = B_TRUE;
 834                 if ((err = be_unmount_zone_root(zhp, &zone_ud)) != BE_SUCCESS) {
 835                         be_print_err(gettext("be_get_legacy_fs: "
 836                             "failed to unmount zone root %s\n"),
 837                             zoneroot_ds);
 838                         if (ret == BE_SUCCESS)
 839                                 ret = err;
 840                 }
 841         }
 842 
 843         /* If we mounted this BE, unmount it */
 844         if (mounted_here) {
 845                 if ((err = _be_unmount(be_name, 0)) != BE_SUCCESS) {
 846                         be_print_err(gettext("be_get_legacy_fs: "
 847                             "failed to unmount %s\n"), be_name);
 848                         if (ret == BE_SUCCESS)
 849                                 ret = err;
 850                 }
 851         }
 852 
 853         ZFS_CLOSE(zhp);
 854 
 855         free(fld->altroot);
 856         fld->altroot = NULL;
 857 
 858         return (ret);
 859 }
 860 
 861 /*
 862  * Function:    be_free_fs_list
 863  * Description: Function used to free the members of a be_fs_list_data_t
 864  *                      structure.
 865  * Parameters:
 866  *              fld - be_fs_list_data_t pointer to free.
 867  * Returns:
 868  *              None
 869  * Scope:
 870  *              Semi-private (library wide use only)
 871  */
 872 void
 873 be_free_fs_list(be_fs_list_data_t *fld)
 874 {
 875         int     i;
 876 
 877         if (fld == NULL)
 878                 return;
 879 
 880         free(fld->altroot);
 881 
 882         if (fld->fs_list == NULL)
 883                 return;
 884 
 885         for (i = 0; i < fld->fs_num; i++)
 886                 free(fld->fs_list[i]);
 887 
 888         free(fld->fs_list);
 889 }
 890 
 891 /*
 892  * Function:    be_get_ds_from_dir(char *dir)
 893  * Description: Given a directory path, find the underlying dataset mounted
 894  *              at that directory path if there is one.   The returned name
 895  *              is allocated in heap storage, so the caller is responsible
 896  *              for freeing it.
 897  * Parameters:
 898  *              dir - char pointer of directory to find.
 899  * Returns:
 900  *              NULL - if directory is not mounted from a dataset.
 901  *              name of dataset mounted at dir.
 902  * Scope:
 903  *              Semi-private (library wide use only)
 904  */
 905 char *
 906 be_get_ds_from_dir(char *dir)
 907 {
 908         dir_data_t      dd = { 0 };
 909         char            resolved_dir[MAXPATHLEN];
 910 
 911         /* Make sure length of dir is within the max length */
 912         if (dir == NULL || strlen(dir) >= MAXPATHLEN)
 913                 return (NULL);
 914 
 915         /* Resolve dir in case its lofs mounted */
 916         (void) strlcpy(resolved_dir, dir, sizeof (resolved_dir));
 917         z_resolve_lofs(resolved_dir, sizeof (resolved_dir));
 918 
 919         dd.dir = resolved_dir;
 920 
 921         (void) zfs_iter_root(g_zfs, be_get_ds_from_dir_callback, &dd);
 922 
 923         return (dd.ds);
 924 }
 925 
 926 /*
 927  * Function:    be_make_tmp_mountpoint
 928  * Description: This function generates a random temporary mountpoint
 929  *              and creates that mountpoint directory.  It returns the
 930  *              mountpoint in heap storage, so the caller is responsible
 931  *              for freeing it.
 932  * Parameters:
 933  *              tmp_mp - reference to pointer of where to store generated
 934  *                      temporary mountpoint.
 935  * Returns:
 936  *              BE_SUCCESS - Success
 937  *              be_errno_t - Failure
 938  * Scope:
 939  *              Semi-private (library wide use only)
 940  */
 941 int
 942 be_make_tmp_mountpoint(char **tmp_mp)
 943 {
 944         int     err = 0;
 945 
 946         if ((*tmp_mp = (char *)calloc(1, sizeof (BE_TMP_MNTPNT) + 1)) == NULL) {
 947                 be_print_err(gettext("be_make_tmp_mountpoint: "
 948                     "malloc failed\n"));
 949                 return (BE_ERR_NOMEM);
 950         }
 951         (void) strlcpy(*tmp_mp, BE_TMP_MNTPNT, sizeof (BE_TMP_MNTPNT) + 1);
 952         if (mkdtemp(*tmp_mp) == NULL) {
 953                 err = errno;
 954                 be_print_err(gettext("be_make_tmp_mountpoint: mkdtemp() failed "
 955                     "for %s: %s\n"), *tmp_mp, strerror(err));
 956                 free(*tmp_mp);
 957                 *tmp_mp = NULL;
 958                 return (errno_to_be_err(err));
 959         }
 960 
 961         return (BE_SUCCESS);
 962 }
 963 
 964 /*
 965  * Function:    be_mount_pool
 966  * Description: This function determines if the pool's datase is mounted
 967  *              and if not it is used to mount the pool's dataset. The
 968  *              function returns the current mountpoint if we are able
 969  *              to mount the dataset.
 970  * Parameters:
 971  *              zhp - handle to the pool's dataset
 972  *              tmp_mntpnt - The temporary mountpoint that the pool's
 973  *                            dataset is mounted on. This is set only
 974  *                            if the attempt to mount the dataset at it's
 975  *                            set mountpoint fails, and we've used a
 976  *                            temporary mount point for this dataset. It
 977  *                            is expected that the caller will free this
 978  *                            memory.
 979  *              orig_mntpnt - The original mountpoint for the pool. If a
 980  *                            temporary mount point was needed this will
 981  *                            be used to reset the mountpoint property to
 982  *                            it's original mountpoint. It is expected that
 983  *                            the caller will free this memory.
 984  *              pool_mounted - This flag indicates that the pool was mounted
 985  *                             in this function.
 986  * Returns:
 987  *              BE_SUCCESS - Success
 988  *              be_errno_t - Failure
 989  * Scope:
 990  *              Semi-private (library wide use only)
 991  */
 992 int
 993 be_mount_pool(
 994         zfs_handle_t *zhp,
 995         char **tmp_mntpnt,
 996         char **orig_mntpnt,
 997         boolean_t *pool_mounted)
 998 {
 999 
1000         char            mountpoint[MAXPATHLEN];
1001         int             ret = 0;
1002 
1003         *tmp_mntpnt = NULL;
1004         *orig_mntpnt = NULL;
1005         *pool_mounted = B_FALSE;
1006 
1007         if (!zfs_is_mounted(zhp, NULL)) {
1008                 if (zfs_mount(zhp, NULL, 0) != 0) {
1009                         if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mountpoint,
1010                             sizeof (mountpoint), NULL, NULL, 0, B_FALSE) != 0) {
1011                                 be_print_err(gettext("be_mount_pool: failed to "
1012                                     "get mountpoint of (%s): %s\n"),
1013                                     zfs_get_name(zhp),
1014                                     libzfs_error_description(g_zfs));
1015                                 return (zfs_err_to_be_err(g_zfs));
1016                         }
1017                         if ((*orig_mntpnt = strdup(mountpoint)) == NULL) {
1018                                 be_print_err(gettext("be_mount_pool: memory "
1019                                     "allocation failed\n"));
1020                                 return (BE_ERR_NOMEM);
1021                         }
1022                         /*
1023                          * attempt to mount on a temp mountpoint
1024                          */
1025                         if ((ret = be_make_tmp_mountpoint(tmp_mntpnt))
1026                             != BE_SUCCESS) {
1027                                 be_print_err(gettext("be_mount_pool: failed "
1028                                     "to make temporary mountpoint\n"));
1029                                 free(*orig_mntpnt);
1030                                 *orig_mntpnt = NULL;
1031                                 return (ret);
1032                         }
1033 
1034                         if (zfs_prop_set(zhp,
1035                             zfs_prop_to_name(ZFS_PROP_MOUNTPOINT),
1036                             *tmp_mntpnt) != 0) {
1037                                 be_print_err(gettext("be_mount_pool: failed "
1038                                     "to set mountpoint of pool dataset %s to "
1039                                     "%s: %s\n"), zfs_get_name(zhp),
1040                                     *orig_mntpnt,
1041                                     libzfs_error_description(g_zfs));
1042                                 free(*tmp_mntpnt);
1043                                 free(*orig_mntpnt);
1044                                 *orig_mntpnt = NULL;
1045                                 *tmp_mntpnt = NULL;
1046                                 return (zfs_err_to_be_err(g_zfs));
1047                         }
1048 
1049                         if (zfs_mount(zhp, NULL, 0) != 0) {
1050                                 be_print_err(gettext("be_mount_pool: failed "
1051                                     "to mount dataset %s at %s: %s\n"),
1052                                     zfs_get_name(zhp), *tmp_mntpnt,
1053                                     libzfs_error_description(g_zfs));
1054                                 ret = zfs_err_to_be_err(g_zfs);
1055                                 if (zfs_prop_set(zhp,
1056                                     zfs_prop_to_name(ZFS_PROP_MOUNTPOINT),
1057                                     mountpoint) != 0) {
1058                                         be_print_err(gettext("be_mount_pool: "
1059                                             "failed to set mountpoint of pool "
1060                                             "dataset %s to %s: %s\n"),
1061                                             zfs_get_name(zhp), *tmp_mntpnt,
1062                                             libzfs_error_description(g_zfs));
1063                                 }
1064                                 free(*tmp_mntpnt);
1065                                 free(*orig_mntpnt);
1066                                 *orig_mntpnt = NULL;
1067                                 *tmp_mntpnt = NULL;
1068                                 return (ret);
1069                         }
1070                 }
1071                 *pool_mounted = B_TRUE;
1072         }
1073 
1074         return (BE_SUCCESS);
1075 }
1076 
1077 /*
1078  * Function:    be_unmount_pool
1079  * Description: This function is used to unmount the pool's dataset if we
1080  *              mounted it previously using be_mount_pool().
1081  * Parameters:
1082  *              zhp - handle to the pool's dataset
1083  *              tmp_mntpnt - If a temprary mount point was used this will
1084  *                           be set. Since this was created in be_mount_pool
1085  *                           we will need to clean it up here.
1086  *              orig_mntpnt - The original mountpoint for the pool. This is
1087  *                            used to set the dataset mountpoint property
1088  *                            back to it's original value in the case where a
1089  *                            temporary mountpoint was used.
1090  * Returns:
1091  *              BE_SUCCESS - Success
1092  *              be_errno_t - Failure
1093  * Scope:
1094  *              Semi-private (library wide use only)
1095  */
1096 int
1097 be_unmount_pool(
1098         zfs_handle_t *zhp,
1099         char *tmp_mntpnt,
1100         char *orig_mntpnt)
1101 {
1102         if (zfs_unmount(zhp, NULL, 0) != 0) {
1103                 be_print_err(gettext("be_unmount_pool: failed to "
1104                     "unmount pool (%s): %s\n"), zfs_get_name(zhp),
1105                     libzfs_error_description(g_zfs));
1106                 return (zfs_err_to_be_err(g_zfs));
1107         }
1108         if (orig_mntpnt != NULL) {
1109                 if (tmp_mntpnt != NULL &&
1110                     strcmp(orig_mntpnt, tmp_mntpnt) != 0) {
1111                         (void) rmdir(tmp_mntpnt);
1112                 }
1113                 if (zfs_prop_set(zhp,
1114                     zfs_prop_to_name(ZFS_PROP_MOUNTPOINT),
1115                     orig_mntpnt) != 0) {
1116                         be_print_err(gettext("be_unmount_pool: failed "
1117                             "to set the mountpoint for dataset (%s) to "
1118                             "%s: %s\n"), zfs_get_name(zhp), orig_mntpnt,
1119                             libzfs_error_description(g_zfs));
1120                         return (zfs_err_to_be_err(g_zfs));
1121                 }
1122         }
1123 
1124         return (BE_SUCCESS);
1125 }
1126 
1127 /* ******************************************************************** */
1128 /*                      Private Functions                               */
1129 /* ******************************************************************** */
1130 
1131 /*
1132  * Function:    be_mount_callback
1133  * Description: Callback function used to iterate through all of a BE's
1134  *              subordinate file systems and to mount them accordingly.
1135  * Parameters:
1136  *              zhp - zfs_handle_t pointer to current file system being
1137  *                      processed.
1138  *              data - pointer to the altroot of where to mount BE.
1139  * Returns:
1140  *              0 - Success
1141  *              be_errno_t - Failure
1142  * Scope:
1143  *              Private
1144  */
1145 static int
1146 be_mount_callback(zfs_handle_t *zhp, void *data)
1147 {
1148         zprop_source_t  sourcetype;
1149         const char      *fs_name = zfs_get_name(zhp);
1150         char            source[ZFS_MAXNAMELEN];
1151         char            *altroot = data;
1152         char            zhp_mountpoint[MAXPATHLEN];
1153         char            mountpoint[MAXPATHLEN];
1154         int             ret = 0;
1155 
1156         /* Get dataset's mountpoint and source values */
1157         if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, zhp_mountpoint,
1158             sizeof (zhp_mountpoint), &sourcetype, source, sizeof (source),
1159             B_FALSE) != 0) {
1160                 be_print_err(gettext("be_mount_callback: failed to "
1161                     "get mountpoint and sourcetype for %s\n"),
1162                     fs_name);
1163                 ZFS_CLOSE(zhp);
1164                 return (BE_ERR_ZFS);
1165         }
1166 
1167         /*
1168          * Set this filesystem's 'canmount' property to 'noauto' just incase
1169          * it's been set 'on'.  We do this so that when we change its
1170          * mountpoint zfs won't immediately try to mount it.
1171          */
1172         if (zfs_prop_set(zhp, zfs_prop_to_name(ZFS_PROP_CANMOUNT), "noauto")) {
1173                 be_print_err(gettext("be_mount_callback: failed to "
1174                     "set canmount to 'noauto' (%s)\n"), fs_name);
1175                 ZFS_CLOSE(zhp);
1176                 return (BE_ERR_ZFS);
1177         }
1178 
1179         /*
1180          * If the mountpoint is none, there's nothing to do, goto next.
1181          * If the mountpoint is legacy, legacy mount it with mount(2).
1182          * If the mountpoint is inherited, its mountpoint should
1183          * already be set.  If it's not, then explicitly fix-up
1184          * the mountpoint now by appending its explicitly set
1185          * mountpoint value to the BE mountpoint.
1186          */
1187         if (strcmp(zhp_mountpoint, ZFS_MOUNTPOINT_NONE) == 0) {
1188                 goto next;
1189         } else if (strcmp(zhp_mountpoint, ZFS_MOUNTPOINT_LEGACY) == 0) {
1190                 /*
1191                  * If the mountpoint is set to 'legacy', we need to
1192                  * dig into this BE's vfstab to figure out where to
1193                  * mount it, and just mount it via mount(2).
1194                  */
1195                 if (get_mountpoint_from_vfstab(altroot, fs_name,
1196                     mountpoint, sizeof (mountpoint), B_TRUE) == BE_SUCCESS) {
1197 
1198                         /* Legacy mount the file system */
1199                         if (mount(fs_name, mountpoint, MS_DATA,
1200                             MNTTYPE_ZFS, NULL, 0, NULL, 0) != 0) {
1201                                 be_print_err(
1202                                     gettext("be_mount_callback: "
1203                                     "failed to mount %s on %s\n"),
1204                                     fs_name, mountpoint);
1205                         }
1206                 } else {
1207                         be_print_err(
1208                             gettext("be_mount_callback: "
1209                             "no entry for %s in vfstab, "
1210                             "skipping ...\n"), fs_name);
1211                 }
1212 
1213                 goto next;
1214 
1215         } else if (sourcetype & ZPROP_SRC_INHERITED) {
1216                 /*
1217                  * If the mountpoint is inherited, its parent should have
1218                  * already been processed so its current mountpoint value
1219                  * is what its mountpoint ought to be.
1220                  */
1221                 (void) strlcpy(mountpoint, zhp_mountpoint, sizeof (mountpoint));
1222         } else if (sourcetype & ZPROP_SRC_LOCAL) {
1223                 /*
1224                  * Else process dataset with explicitly set mountpoint.
1225                  */
1226                 (void) snprintf(mountpoint, sizeof (mountpoint),
1227                     "%s%s", altroot, zhp_mountpoint);
1228 
1229                 /* Set the new mountpoint for the dataset */
1230                 if (zfs_prop_set(zhp,
1231                     zfs_prop_to_name(ZFS_PROP_MOUNTPOINT),
1232                     mountpoint)) {
1233                         be_print_err(gettext("be_mount_callback: "
1234                             "failed to set mountpoint for %s to "
1235                             "%s\n"), fs_name, mountpoint);
1236                         ZFS_CLOSE(zhp);
1237                         return (BE_ERR_ZFS);
1238                 }
1239         } else {
1240                 be_print_err(gettext("be_mount_callback: "
1241                     "mountpoint sourcetype of %s is %d, skipping ...\n"),
1242                     fs_name, sourcetype);
1243 
1244                 goto next;
1245         }
1246 
1247         /* Mount this filesystem */
1248         if (zfs_mount(zhp, NULL, 0) != 0) {
1249                 be_print_err(gettext("be_mount_callback: failed to "
1250                     "mount dataset %s at %s: %s\n"), fs_name, mountpoint,
1251                     libzfs_error_description(g_zfs));
1252                 /*
1253                  * Set this filesystem's 'mountpoint' property back to what
1254                  * it was
1255                  */
1256                 if (sourcetype & ZPROP_SRC_LOCAL &&
1257                     strcmp(zhp_mountpoint, ZFS_MOUNTPOINT_LEGACY) != 0) {
1258                         (void) zfs_prop_set(zhp,
1259                             zfs_prop_to_name(ZFS_PROP_MOUNTPOINT),
1260                             zhp_mountpoint);
1261                 }
1262 
1263                 ZFS_CLOSE(zhp);
1264                 return (BE_ERR_MOUNT);
1265         }
1266 
1267 next:
1268         /* Iterate through this dataset's children and mount them */
1269         if ((ret = zfs_iter_filesystems(zhp, be_mount_callback,
1270             altroot)) != 0) {
1271                 ZFS_CLOSE(zhp);
1272                 return (ret);
1273         }
1274 
1275 
1276         ZFS_CLOSE(zhp);
1277         return (0);
1278 }
1279 
1280 /*
1281  * Function:    be_unmount_callback
1282  * Description: Callback function used to iterate through all of a BE's
1283  *              subordinate file systems and to unmount them.
1284  * Parameters:
1285  *              zhp - zfs_handle_t pointer to current file system being
1286  *                      processed.
1287  *              data - pointer to the mountpoint of where BE is mounted.
1288  * Returns:
1289  *              0 - Success
1290  *              be_errno_t - Failure
1291  * Scope:
1292  *              Private
1293  */
1294 static int
1295 be_unmount_callback(zfs_handle_t *zhp, void *data)
1296 {
1297         be_unmount_data_t       *ud = data;
1298         zprop_source_t  sourcetype;
1299         const char      *fs_name = zfs_get_name(zhp);
1300         char            source[ZFS_MAXNAMELEN];
1301         char            mountpoint[MAXPATHLEN];
1302         char            *zhp_mountpoint;
1303         int             ret = 0;
1304 
1305         /* Iterate down this dataset's children first */
1306         if (zfs_iter_filesystems(zhp, be_unmount_callback, ud)) {
1307                 ret = BE_ERR_UMOUNT;
1308                 goto done;
1309         }
1310 
1311         /* Is dataset even mounted ? */
1312         if (!zfs_is_mounted(zhp, NULL))
1313                 goto done;
1314 
1315         /* Unmount this file system */
1316         if (zfs_unmount(zhp, NULL, ud->force ? MS_FORCE : 0) != 0) {
1317                 be_print_err(gettext("be_unmount_callback: "
1318                     "failed to unmount %s: %s\n"), fs_name,
1319                     libzfs_error_description(g_zfs));
1320                 ret = zfs_err_to_be_err(g_zfs);
1321                 goto done;
1322         }
1323 
1324         /* Get dataset's current mountpoint and source value */
1325         if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mountpoint,
1326             sizeof (mountpoint), &sourcetype, source, sizeof (source),
1327             B_FALSE) != 0) {
1328                 be_print_err(gettext("be_unmount_callback: "
1329                     "failed to get mountpoint and sourcetype for %s: %s\n"),
1330                     fs_name, libzfs_error_description(g_zfs));
1331                 ret = zfs_err_to_be_err(g_zfs);
1332                 goto done;
1333         }
1334 
1335         if (sourcetype & ZPROP_SRC_INHERITED) {
1336                 /*
1337                  * If the mountpoint is inherited we don't need to
1338                  * do anything.  When its parent gets processed
1339                  * its mountpoint will be set accordingly.
1340                  */
1341                 goto done;
1342         } else if (sourcetype & ZPROP_SRC_LOCAL) {
1343 
1344                 if (strcmp(mountpoint, ZFS_MOUNTPOINT_LEGACY) == 0) {
1345                         /*
1346                          * If the mountpoint is set to 'legacy', its already
1347                          * been unmounted (from above call to zfs_unmount), and
1348                          * we don't need to do anything else with it.
1349                          */
1350                         goto done;
1351 
1352                 } else {
1353                         /*
1354                          * Else process dataset with explicitly set mountpoint.
1355                          */
1356 
1357                         /*
1358                          * Get this dataset's mountpoint relative to
1359                          * the BE's mountpoint.
1360                          */
1361                         if ((strncmp(mountpoint, ud->altroot,
1362                             strlen(ud->altroot)) == 0) &&
1363                             (mountpoint[strlen(ud->altroot)] == '/')) {
1364 
1365                                 zhp_mountpoint = mountpoint +
1366                                     strlen(ud->altroot);
1367 
1368                                 /* Set this dataset's mountpoint value */
1369                                 if (zfs_prop_set(zhp,
1370                                     zfs_prop_to_name(ZFS_PROP_MOUNTPOINT),
1371                                     zhp_mountpoint)) {
1372                                         be_print_err(
1373                                             gettext("be_unmount_callback: "
1374                                             "failed to set mountpoint for "
1375                                             "%s to %s: %s\n"), fs_name,
1376                                             zhp_mountpoint,
1377                                             libzfs_error_description(g_zfs));
1378                                         ret = zfs_err_to_be_err(g_zfs);
1379                                 }
1380                         } else {
1381                                 be_print_err(
1382                                     gettext("be_unmount_callback: "
1383                                     "%s not mounted under BE's altroot %s, "
1384                                     "skipping ...\n"), fs_name, ud->altroot);
1385                                 /*
1386                                  * fs_name is mounted but not under the
1387                                  * root for this BE.
1388                                  */
1389                                 ret = BE_ERR_INVALMOUNTPOINT;
1390                         }
1391                 }
1392         } else {
1393                 be_print_err(gettext("be_unmount_callback: "
1394                     "mountpoint sourcetype of %s is %d, skipping ...\n"),
1395                     fs_name, sourcetype);
1396                 ret = BE_ERR_ZFS;
1397         }
1398 
1399 done:
1400         /* Set this filesystem's 'canmount' property to 'noauto' */
1401         if (zfs_prop_set(zhp, zfs_prop_to_name(ZFS_PROP_CANMOUNT), "noauto")) {
1402                 be_print_err(gettext("be_unmount_callback: "
1403                     "failed to set canmount to 'noauto' (%s)\n"), fs_name);
1404                 if (ret == 0)
1405                         ret = BE_ERR_ZFS;
1406         }
1407 
1408         ZFS_CLOSE(zhp);
1409         return (ret);
1410 }
1411 
1412 /*
1413  * Function:    be_get_legacy_fs_callback
1414  * Description: The callback function is used to iterate through all
1415  *              non-shared file systems of a BE, finding ones that have
1416  *              a legacy mountpoint and an entry in the BE's vfstab.
1417  *              It adds these file systems to the callback data.
1418  * Parameters:
1419  *              zhp - zfs_handle_t pointer to current file system being
1420  *                      processed.
1421  *              data - be_fs_list_data_t pointer
1422  * Returns:
1423  *              0 - Success
1424  *              be_errno_t - Failure
1425  * Scope:
1426  *              Private
1427  */
1428 static int
1429 be_get_legacy_fs_callback(zfs_handle_t *zhp, void *data)
1430 {
1431         be_fs_list_data_t       *fld = data;
1432         const char              *fs_name = zfs_get_name(zhp);
1433         char                    zhp_mountpoint[MAXPATHLEN];
1434         char                    mountpoint[MAXPATHLEN];
1435         int                     ret = 0;
1436 
1437         /* Get this dataset's mountpoint property */
1438         if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, zhp_mountpoint,
1439             sizeof (zhp_mountpoint), NULL, NULL, 0, B_FALSE) != 0) {
1440                 be_print_err(gettext("be_get_legacy_fs_callback: "
1441                     "failed to get mountpoint for %s: %s\n"),
1442                     fs_name, libzfs_error_description(g_zfs));
1443                 ret = zfs_err_to_be_err(g_zfs);
1444                 ZFS_CLOSE(zhp);
1445                 return (ret);
1446         }
1447 
1448         /*
1449          * If mountpoint is legacy, try to get its mountpoint from this BE's
1450          * vfstab.  If it exists in the vfstab, add this file system to the
1451          * callback data.
1452          */
1453         if (strcmp(zhp_mountpoint, ZFS_MOUNTPOINT_LEGACY) == 0) {
1454                 if (get_mountpoint_from_vfstab(fld->altroot, fs_name,
1455                     mountpoint, sizeof (mountpoint), B_FALSE) != BE_SUCCESS) {
1456                         be_print_err(gettext("be_get_legacy_fs_callback: "
1457                             "no entry for %s in vfstab, "
1458                             "skipping ...\n"), fs_name);
1459 
1460                         goto next;
1461                 }
1462 
1463                 /* Record file system into the callback data. */
1464                 if (add_to_fs_list(fld, zfs_get_name(zhp)) != BE_SUCCESS) {
1465                         be_print_err(gettext("be_get_legacy_fs_callback: "
1466                             "failed to add %s to fs list\n"), mountpoint);
1467                         ZFS_CLOSE(zhp);
1468                         return (BE_ERR_NOMEM);
1469                 }
1470         }
1471 
1472 next:
1473         /* Iterate through this dataset's children file systems */
1474         if ((ret = zfs_iter_filesystems(zhp, be_get_legacy_fs_callback,
1475             fld)) != 0) {
1476                 ZFS_CLOSE(zhp);
1477                 return (ret);
1478         }
1479         ZFS_CLOSE(zhp);
1480         return (0);
1481 }
1482 
1483 /*
1484  * Function:    add_to_fs_list
1485  * Description: Function used to add a file system to the fs_list array in
1486  *                      a be_fs_list_data_t structure.
1487  * Parameters:
1488  *              fld - be_fs_list_data_t pointer
1489  *              fs - file system to add
1490  * Returns:
1491  *              BE_SUCCESS - Success
1492  *              1 - Failure
1493  * Scope:
1494  *              Private
1495  */
1496 static int
1497 add_to_fs_list(be_fs_list_data_t *fld, const char *fs)
1498 {
1499         if (fld == NULL || fs == NULL)
1500                 return (1);
1501 
1502         if ((fld->fs_list = (char **)realloc(fld->fs_list,
1503             sizeof (char *)*(fld->fs_num + 1))) == NULL) {
1504                 be_print_err(gettext("add_to_fs_list: "
1505                     "memory allocation failed\n"));
1506                 return (1);
1507         }
1508 
1509         if ((fld->fs_list[fld->fs_num++] = strdup(fs)) == NULL) {
1510                 be_print_err(gettext("add_to_fs_list: "
1511                     "memory allocation failed\n"));
1512                 return (1);
1513         }
1514 
1515         return (BE_SUCCESS);
1516 }
1517 
1518 /*
1519  * Function:    zpool_shared_fs_callback
1520  * Description: Callback function used to iterate through all existing pools
1521  *              to find and mount all shared filesystems.  This function
1522  *              processes the pool's "pool data" dataset, then uses
1523  *              iter_shared_fs_callback to iterate through the pool's
1524  *              datasets.
1525  * Parameters:
1526  *              zlp - zpool_handle_t pointer to the current pool being
1527  *                      looked at.
1528  *              data - be_mount_data_t pointer
1529  * Returns:
1530  *              0 - Success
1531  *              be_errno_t - Failure
1532  * Scope:
1533  *              Private
1534  */
1535 static int
1536 zpool_shared_fs_callback(zpool_handle_t *zlp, void *data)
1537 {
1538         be_mount_data_t *md = data;
1539         zfs_handle_t    *zhp = NULL;
1540         const char      *zpool = zpool_get_name(zlp);
1541         int             ret = 0;
1542 
1543         /*
1544          * Get handle to pool's "pool data" dataset
1545          */
1546         if ((zhp = zfs_open(g_zfs, zpool, ZFS_TYPE_FILESYSTEM)) == NULL) {
1547                 be_print_err(gettext("zpool_shared_fs: "
1548                     "failed to open pool dataset %s: %s\n"), zpool,
1549                     libzfs_error_description(g_zfs));
1550                 ret = zfs_err_to_be_err(g_zfs);
1551                 zpool_close(zlp);
1552                 return (ret);
1553         }
1554 
1555         /* Process this pool's "pool data" dataset */
1556         (void) loopback_mount_shared_fs(zhp, md);
1557 
1558         /* Interate through this pool's children */
1559         (void) zfs_iter_filesystems(zhp, iter_shared_fs_callback, md);
1560 
1561         ZFS_CLOSE(zhp);
1562         zpool_close(zlp);
1563 
1564         return (0);
1565 }
1566 
1567 /*
1568  * Function:    iter_shared_fs_callback
1569  * Description: Callback function used to iterate through a pool's datasets
1570  *              to find and mount all shared filesystems.  It makes sure to
1571  *              find the BE container dataset of the pool, if it exists, and
1572  *              does not process and iterate down that path.
1573  *
1574  *              Note - This function iterates linearly down the
1575  *              hierarchical dataset paths and mounts things as it goes
1576  *              along.  It does not make sure that something deeper down
1577  *              a dataset path has an interim mountpoint for something
1578  *              processed earlier.
1579  *
1580  * Parameters:
1581  *              zhp - zfs_handle_t pointer to the current dataset being
1582  *                      processed.
1583  *              data - be_mount_data_t pointer
1584  * Returns:
1585  *              0 - Success
1586  *              be_errno_t - Failure
1587  * Scope:
1588  *              Private
1589  */
1590 static int
1591 iter_shared_fs_callback(zfs_handle_t *zhp, void *data)
1592 {
1593         be_mount_data_t *md = data;
1594         const char      *name = zfs_get_name(zhp);
1595         char            container_ds[MAXPATHLEN];
1596         char            tmp_name[MAXPATHLEN];
1597         char            *pool;
1598 
1599         /* Get the pool's name */
1600         (void) strlcpy(tmp_name, name, sizeof (tmp_name));
1601         pool = strtok(tmp_name, "/");
1602 
1603         if (pool) {
1604                 /* Get the name of this pool's container dataset */
1605                 be_make_container_ds(pool, container_ds,
1606                     sizeof (container_ds));
1607 
1608                 /*
1609                  * If what we're processing is this pool's BE container
1610                  * dataset, skip it.
1611                  */
1612                 if (strcmp(name, container_ds) == 0) {
1613                         ZFS_CLOSE(zhp);
1614                         return (0);
1615                 }
1616         } else {
1617                 /* Getting the pool name failed, return error */
1618                 be_print_err(gettext("iter_shared_fs_callback: "
1619                     "failed to get pool name from %s\n"), name);
1620                 ZFS_CLOSE(zhp);
1621                 return (BE_ERR_POOL_NOENT);
1622         }
1623 
1624         /* Mount this shared filesystem */
1625         (void) loopback_mount_shared_fs(zhp, md);
1626 
1627         /* Iterate this dataset's children file systems */
1628         (void) zfs_iter_filesystems(zhp, iter_shared_fs_callback, md);
1629         ZFS_CLOSE(zhp);
1630 
1631         return (0);
1632 }
1633 
1634 /*
1635  * Function:    loopback_mount_shared_fs
1636  * Description: This function loopback mounts a file system into the altroot
1637  *              area of the BE being mounted.  Since these are shared file
1638  *              systems, they are expected to be already mounted for the
1639  *              current BE, and this function just loopback mounts them into
1640  *              the BE mountpoint.  If they are not mounted for the current
1641  *              live system, they are skipped and not mounted into the BE
1642  *              we're mounting.
1643  * Parameters:
1644  *              zhp - zfs_handle_t pointer to the dataset to loopback mount
1645  *              md - be_mount_data_t pointer
1646  * Returns:
1647  *              BE_SUCCESS - Success
1648  *              be_errno_t - Failure
1649  * Scope:
1650  *              Private
1651  */
1652 static int
1653 loopback_mount_shared_fs(zfs_handle_t *zhp, be_mount_data_t *md)
1654 {
1655         char            zhp_mountpoint[MAXPATHLEN];
1656         char            mountpoint[MAXPATHLEN];
1657         char            *mp = NULL;
1658         char            optstr[MAX_MNTOPT_STR];
1659         int             mflag = MS_OPTIONSTR;
1660         int             err;
1661 
1662         /*
1663          * Check if file system is currently mounted and not delegated
1664          * to a non-global zone (if we're in the global zone)
1665          */
1666         if (zfs_is_mounted(zhp, &mp) && (getzoneid() != GLOBAL_ZONEID ||
1667             !zfs_prop_get_int(zhp, ZFS_PROP_ZONED))) {
1668                 /*
1669                  * If we didn't get a mountpoint from the zfs_is_mounted call,
1670                  * get it from the mountpoint property.
1671                  */
1672                 if (mp == NULL) {
1673                         if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT,
1674                             zhp_mountpoint, sizeof (zhp_mountpoint), NULL,
1675                             NULL, 0, B_FALSE) != 0) {
1676                                 be_print_err(
1677                                     gettext("loopback_mount_shared_fs: "
1678                                     "failed to get mountpoint property\n"));
1679                                 return (BE_ERR_ZFS);
1680                         }
1681                 } else {
1682                         (void) strlcpy(zhp_mountpoint, mp,
1683                             sizeof (zhp_mountpoint));
1684                         free(mp);
1685                 }
1686 
1687                 (void) snprintf(mountpoint, sizeof (mountpoint), "%s%s",
1688                     md->altroot, zhp_mountpoint);
1689 
1690                 /* Mount it read-only if read-write was not requested */
1691                 if (!md->shared_rw) {
1692                         mflag |= MS_RDONLY;
1693                 }
1694 
1695                 /* Add the "nosub" option to the mount options string */
1696                 (void) strlcpy(optstr, MNTOPT_NOSUB, sizeof (optstr));
1697 
1698                 /* Loopback mount this dataset at the altroot */
1699                 if (mount(zhp_mountpoint, mountpoint, mflag, MNTTYPE_LOFS,
1700                     NULL, 0, optstr, sizeof (optstr)) != 0) {
1701                         err = errno;
1702                         be_print_err(gettext("loopback_mount_shared_fs: "
1703                             "failed to loopback mount %s at %s: %s\n"),
1704                             zhp_mountpoint, mountpoint, strerror(err));
1705                         return (BE_ERR_MOUNT);
1706                 }
1707         }
1708 
1709         return (BE_SUCCESS);
1710 }
1711 
1712 /*
1713  * Function:    loopback_mount_zonepath
1714  * Description: This function loopback mounts a zonepath into the altroot
1715  *              area of the BE being mounted.  Since these are shared file
1716  *              systems, they are expected to be already mounted for the
1717  *              current BE, and this function just loopback mounts them into
1718  *              the BE mountpoint.
1719  * Parameters:
1720  *              zonepath - pointer to zone path in the current BE
1721  *              md - be_mount_data_t pointer
1722  * Returns:
1723  *              BE_SUCCESS - Success
1724  *              be_errno_t - Failure
1725  * Scope:
1726  *              Private
1727  */
1728 static int
1729 loopback_mount_zonepath(const char *zonepath, be_mount_data_t *md)
1730 {
1731         FILE            *fp = (FILE *)NULL;
1732         struct stat     st;
1733         char            *p;
1734         char            *p1;
1735         char            *parent_dir;
1736         struct extmnttab        extmtab;
1737         dev_t           dev = NODEV;
1738         char            *parentmnt;
1739         char            alt_parentmnt[MAXPATHLEN];
1740         struct mnttab   mntref;
1741         char            altzonepath[MAXPATHLEN];
1742         char            optstr[MAX_MNTOPT_STR];
1743         int             mflag = MS_OPTIONSTR;
1744         int             ret;
1745         int             err;
1746 
1747         fp = fopen(MNTTAB, "r");
1748         if (fp == NULL) {
1749                 err = errno;
1750                 be_print_err(gettext("loopback_mount_zonepath: "
1751                     "failed to open /etc/mnttab\n"));
1752                 return (errno_to_be_err(err));
1753         }
1754 
1755         /*
1756          * before attempting the loopback mount of zonepath under altroot,
1757          * we need to make sure that all intermediate file systems in the
1758          * zone path are also mounted under altroot
1759          */
1760 
1761         /* get the parent directory for zonepath */
1762         p = strrchr(zonepath, '/');
1763         if (p != NULL && p != zonepath) {
1764                 if ((parent_dir = (char *)calloc(sizeof (char),
1765                     p - zonepath + 1)) == NULL) {
1766                         ret = BE_ERR_NOMEM;
1767                         goto done;
1768                 }
1769                 (void) strlcpy(parent_dir, zonepath, p - zonepath + 1);
1770                 if (stat(parent_dir, &st) < 0) {
1771                         ret = errno_to_be_err(errno);
1772                         be_print_err(gettext("loopback_mount_zonepath: "
1773                             "failed to stat %s"),
1774                             parent_dir);
1775                         free(parent_dir);
1776                         goto done;
1777                 }
1778                 free(parent_dir);
1779 
1780                 /*
1781                  * After the above stat call, st.st_dev contains ID of the
1782                  * device over which parent dir resides.
1783                  * Now, search mnttab and find mount point of parent dir device.
1784                  */
1785 
1786                 resetmnttab(fp);
1787                 while (getextmntent(fp, &extmtab, sizeof (extmtab)) == 0) {
1788                         dev = makedev(extmtab.mnt_major, extmtab.mnt_minor);
1789                         if (st.st_dev == dev && strcmp(extmtab.mnt_fstype,
1790                             MNTTYPE_ZFS) == 0) {
1791                                 p1 = strchr(extmtab.mnt_special, '/');
1792                                 if (p1 == NULL || strncmp(p1 + 1,
1793                                     BE_CONTAINER_DS_NAME, 4) != 0 ||
1794                                     (*(p1 + 5) != '/' && *(p1 + 5) != '\0')) {
1795                                         /*
1796                                          * if parent dir is in a shared file
1797                                          * system, check whether it is already
1798                                          * loopback mounted under altroot or
1799                                          * not.  It would have been mounted
1800                                          * already under altroot if it is in
1801                                          * a non-shared filesystem.
1802                                          */
1803                                         parentmnt = strdup(extmtab.mnt_mountp);
1804                                         (void) snprintf(alt_parentmnt,
1805                                             sizeof (alt_parentmnt), "%s%s",
1806                                             md->altroot, parentmnt);
1807                                         mntref.mnt_mountp = alt_parentmnt;
1808                                         mntref.mnt_special = parentmnt;
1809                                         mntref.mnt_fstype = MNTTYPE_LOFS;
1810                                         mntref.mnt_mntopts = NULL;
1811                                         mntref.mnt_time = NULL;
1812                                         resetmnttab(fp);
1813                                         if (getmntany(fp, (struct mnttab *)
1814                                             &extmtab, &mntref) != 0) {
1815                                                 ret = loopback_mount_zonepath(
1816                                                     parentmnt, md);
1817                                                 if (ret != BE_SUCCESS) {
1818                                                         free(parentmnt);
1819                                                         goto done;
1820                                                 }
1821                                         }
1822                                         free(parentmnt);
1823                                 }
1824                                 break;
1825                         }
1826                 }
1827         }
1828 
1829 
1830         if (!md->shared_rw) {
1831                 mflag |= MS_RDONLY;
1832         }
1833 
1834         (void) snprintf(altzonepath, sizeof (altzonepath), "%s%s",
1835             md->altroot, zonepath);
1836 
1837         /* Add the "nosub" option to the mount options string */
1838         (void) strlcpy(optstr, MNTOPT_NOSUB, sizeof (optstr));
1839 
1840         /* Loopback mount this dataset at the altroot */
1841         if (mount(zonepath, altzonepath, mflag, MNTTYPE_LOFS,
1842             NULL, 0, optstr, sizeof (optstr)) != 0) {
1843                 err = errno;
1844                 be_print_err(gettext("loopback_mount_zonepath: "
1845                     "failed to loopback mount %s at %s: %s\n"),
1846                     zonepath, altzonepath, strerror(err));
1847                 ret = BE_ERR_MOUNT;
1848                 goto done;
1849         }
1850         ret = BE_SUCCESS;
1851 
1852 done :
1853         (void) fclose(fp);
1854         return (ret);
1855 }
1856 
1857 /*
1858  * Function:    unmount_shared_fs
1859  * Description: This function iterates through the mnttab and finds all
1860  *              loopback mount entries that reside within the altroot of
1861  *              where the BE is mounted, and unmounts it.
1862  * Parameters:
1863  *              ud - be_unmount_data_t pointer
1864  * Returns:
1865  *              BE_SUCCESS - Success
1866  *              be_errno_t - Failure
1867  * Scope:
1868  *              Private
1869  */
1870 static int
1871 unmount_shared_fs(be_unmount_data_t *ud)
1872 {
1873         FILE            *fp = NULL;
1874         struct mnttab   *table = NULL;
1875         struct mnttab   ent;
1876         struct mnttab   *entp = NULL;
1877         size_t          size = 0;
1878         int             read_chunk = 32;
1879         int             i;
1880         int             altroot_len;
1881         int             err = 0;
1882 
1883         errno = 0;
1884 
1885         /* Read in the mnttab into a table */
1886         if ((fp = fopen(MNTTAB, "r")) == NULL) {
1887                 err = errno;
1888                 be_print_err(gettext("unmount_shared_fs: "
1889                     "failed to open mnttab\n"));
1890                 return (errno_to_be_err(err));
1891         }
1892 
1893         while (getmntent(fp, &ent) == 0) {
1894                 if (size % read_chunk == 0) {
1895                         table = (struct mnttab *)realloc(table,
1896                             (size + read_chunk) * sizeof (ent));
1897                 }
1898                 entp = &table[size++];
1899 
1900                 /*
1901                  * Copy over the current mnttab entry into our table,
1902                  * copying only the fields that we care about.
1903                  */
1904                 (void) memset(entp, 0, sizeof (*entp));
1905                 if ((entp->mnt_mountp = strdup(ent.mnt_mountp)) == NULL ||
1906                     (entp->mnt_fstype = strdup(ent.mnt_fstype)) == NULL) {
1907                         be_print_err(gettext("unmount_shared_fs: "
1908                             "memory allocation failed\n"));
1909                         return (BE_ERR_NOMEM);
1910                 }
1911         }
1912         (void) fclose(fp);
1913 
1914         /*
1915          * Process the mnttab entries in reverse order, looking for
1916          * loopback mount entries mounted under our altroot.
1917          */
1918         altroot_len = strlen(ud->altroot);
1919         for (i = size; i > 0; i--) {
1920                 entp = &table[i - 1];
1921 
1922                 /* If not of type lofs, skip */
1923                 if (strcmp(entp->mnt_fstype, MNTTYPE_LOFS) != 0)
1924                         continue;
1925 
1926                 /* If inside the altroot, unmount it */
1927                 if (strncmp(entp->mnt_mountp, ud->altroot, altroot_len) == 0 &&
1928                     entp->mnt_mountp[altroot_len] == '/') {
1929                         if (umount(entp->mnt_mountp) != 0) {
1930                                 err = errno;
1931                                 if (err == EBUSY) {
1932                                         (void) sleep(1);
1933                                         err = errno = 0;
1934                                         if (umount(entp->mnt_mountp) != 0)
1935                                                 err = errno;
1936                                 }
1937                                 if (err != 0) {
1938                                         be_print_err(gettext(
1939                                             "unmount_shared_fs: "
1940                                             "failed to unmount shared file "
1941                                             "system %s: %s\n"),
1942                                             entp->mnt_mountp, strerror(err));
1943                                         return (errno_to_be_err(err));
1944                                 }
1945                         }
1946                 }
1947         }
1948 
1949         return (BE_SUCCESS);
1950 }
1951 
1952 /*
1953  * Function:    get_mountpoint_from_vfstab
1954  * Description: This function digs into the vfstab in the given altroot,
1955  *              and searches for an entry for the fs passed in.  If found,
1956  *              it returns the mountpoint of that fs in the mountpoint
1957  *              buffer passed in.  If the get_alt_mountpoint flag is set,
1958  *              it returns the mountpoint with the altroot prepended.
1959  * Parameters:
1960  *              altroot - pointer to the alternate root location
1961  *              fs - pointer to the file system name to look for in the
1962  *                      vfstab in altroot
1963  *              mountpoint - pointer to buffer of where the mountpoint of
1964  *                      fs will be returned.
1965  *              size_mp - size of mountpoint argument
1966  *              get_alt_mountpoint - flag to indicate whether or not the
1967  *                      mountpoint should be populated with the altroot
1968  *                      prepended.
1969  * Returns:
1970  *              BE_SUCCESS - Success
1971  *              1 - Failure
1972  * Scope:
1973  *              Private
1974  */
1975 static int
1976 get_mountpoint_from_vfstab(char *altroot, const char *fs, char *mountpoint,
1977     size_t size_mp, boolean_t get_alt_mountpoint)
1978 {
1979         struct vfstab   vp;
1980         FILE            *fp = NULL;
1981         char            alt_vfstab[MAXPATHLEN];
1982 
1983         /* Generate path to alternate root vfstab */
1984         (void) snprintf(alt_vfstab, sizeof (alt_vfstab), "%s/etc/vfstab",
1985             altroot);
1986 
1987         /* Open alternate root vfstab */
1988         if ((fp = fopen(alt_vfstab, "r")) == NULL) {
1989                 be_print_err(gettext("get_mountpoint_from_vfstab: "
1990                     "failed to open vfstab (%s)\n"), alt_vfstab);
1991                 return (1);
1992         }
1993 
1994         if (getvfsspec(fp, &vp, (char *)fs) == 0) {
1995                 /*
1996                  * Found entry for fs, grab its mountpoint.
1997                  * If the flag to prepend the altroot into the mountpoint
1998                  * is set, prepend it.  Otherwise, just return the mountpoint.
1999                  */
2000                 if (get_alt_mountpoint) {
2001                         (void) snprintf(mountpoint, size_mp, "%s%s", altroot,
2002                             vp.vfs_mountp);
2003                 } else {
2004                         (void) strlcpy(mountpoint, vp.vfs_mountp, size_mp);
2005                 }
2006         } else {
2007                 (void) fclose(fp);
2008                 return (1);
2009         }
2010 
2011         (void) fclose(fp);
2012 
2013         return (BE_SUCCESS);
2014 }
2015 
2016 /*
2017  * Function:    fix_mountpoint_callback
2018  * Description: This callback function is used to iterate through a BE's
2019  *              children filesystems to check if its mountpoint is currently
2020  *              set to be mounted at some specified altroot.  If so, fix it by
2021  *              removing altroot from the beginning of its mountpoint.
2022  *
2023  *              Note - There's no way to tell if a child filesystem's
2024  *              mountpoint isn't broken, and just happens to begin with
2025  *              the altroot we're looking for.  In this case, this function
2026  *              will errantly remove the altroot portion from the beginning
2027  *              of this filesystem's mountpoint.
2028  *
2029  * Parameters:
2030  *              zhp - zfs_handle_t pointer to filesystem being processed.
2031  *              data - altroot of where BE is to be mounted.
2032  * Returns:
2033  *              0 - Success
2034  *              be_errno_t - Failure
2035  * Scope:
2036  *              Private
2037  */
2038 static int
2039 fix_mountpoint_callback(zfs_handle_t *zhp, void *data)
2040 {
2041         zprop_source_t  sourcetype;
2042         char            source[ZFS_MAXNAMELEN];
2043         char            mountpoint[MAXPATHLEN];
2044         char            *zhp_mountpoint = NULL;
2045         char            *altroot = data;
2046         int             ret = 0;
2047 
2048         /* Get dataset's mountpoint and source values */
2049         if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mountpoint,
2050             sizeof (mountpoint), &sourcetype, source, sizeof (source),
2051             B_FALSE) != 0) {
2052                 be_print_err(gettext("fix_mountpoint_callback: "
2053                     "failed to get mountpoint and sourcetype for %s\n"),
2054                     zfs_get_name(zhp));
2055                 ZFS_CLOSE(zhp);
2056                 return (BE_ERR_ZFS);
2057         }
2058 
2059         /*
2060          * If the mountpoint is not inherited and the mountpoint is not
2061          * 'legacy', this file system potentially needs its mountpoint
2062          * fixed.
2063          */
2064         if (!(sourcetype & ZPROP_SRC_INHERITED) &&
2065             strcmp(mountpoint, ZFS_MOUNTPOINT_LEGACY) != 0) {
2066 
2067                 /*
2068                  * Check if this file system's current mountpoint is
2069                  * under the altroot we're fixing it against.
2070                  */
2071                 if (strncmp(mountpoint, altroot, strlen(altroot)) == 0 &&
2072                     mountpoint[strlen(altroot)] == '/') {
2073 
2074                         /*
2075                          * Get this dataset's mountpoint relative to the
2076                          * altroot.
2077                          */
2078                         zhp_mountpoint = mountpoint + strlen(altroot);
2079 
2080                         /* Fix this dataset's mountpoint value */
2081                         if (zfs_prop_set(zhp,
2082                             zfs_prop_to_name(ZFS_PROP_MOUNTPOINT),
2083                             zhp_mountpoint)) {
2084                                 be_print_err(gettext("fix_mountpoint_callback: "
2085                                     "failed to set mountpoint for %s to "
2086                                     "%s: %s\n"), zfs_get_name(zhp),
2087                                     zhp_mountpoint,
2088                                     libzfs_error_description(g_zfs));
2089                                 ret = zfs_err_to_be_err(g_zfs);
2090                                 ZFS_CLOSE(zhp);
2091                                 return (ret);
2092                         }
2093                 }
2094         }
2095 
2096         /* Iterate through this dataset's children and fix them */
2097         if ((ret = zfs_iter_filesystems(zhp, fix_mountpoint_callback,
2098             altroot)) != 0) {
2099                 ZFS_CLOSE(zhp);
2100                 return (ret);
2101         }
2102 
2103 
2104         ZFS_CLOSE(zhp);
2105         return (0);
2106 }
2107 
2108 /*
2109  * Function:    be_mount_root
2110  * Description: This function mounts the root dataset of a BE at the
2111  *              specified altroot.
2112  * Parameters:
2113  *              zhp - zfs_handle_t pointer to root dataset of a BE that is
2114  *              to be mounted at altroot.
2115  *              altroot - location of where to mount the BE root.
2116  * Return:
2117  *              BE_SUCCESS - Success
2118  *              be_errno_t - Failure
2119  * Scope:
2120  *              Private
2121  */
2122 static int
2123 be_mount_root(zfs_handle_t *zhp, char *altroot)
2124 {
2125         char            mountpoint[MAXPATHLEN];
2126 
2127         /* Get mountpoint property of dataset */
2128         if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mountpoint,
2129             sizeof (mountpoint), NULL, NULL, 0, B_FALSE) != 0) {
2130                 be_print_err(gettext("be_mount_root: failed to "
2131                     "get mountpoint property for %s: %s\n"), zfs_get_name(zhp),
2132                     libzfs_error_description(g_zfs));
2133                 return (zfs_err_to_be_err(g_zfs));
2134         }
2135 
2136         /*
2137          * Set the canmount property for the BE's root dataset to 'noauto' just
2138          * in case it's been set to 'on'.  We do this so that when we change its
2139          * mountpoint, zfs won't immediately try to mount it.
2140          */
2141         if (zfs_prop_set(zhp, zfs_prop_to_name(ZFS_PROP_CANMOUNT), "noauto")
2142             != 0) {
2143                 be_print_err(gettext("be_mount_root: failed to "
2144                     "set canmount property to 'noauto' (%s): %s\n"),
2145                     zfs_get_name(zhp), libzfs_error_description(g_zfs));
2146                 return (zfs_err_to_be_err(g_zfs));
2147         }
2148 
2149         /* Set mountpoint for BE's root filesystem */
2150         if (zfs_prop_set(zhp, zfs_prop_to_name(ZFS_PROP_MOUNTPOINT), altroot)
2151             != 0) {
2152                 be_print_err(gettext("be_mount_root: failed to "
2153                     "set mountpoint of %s to %s: %s\n"),
2154                     zfs_get_name(zhp), altroot,
2155                     libzfs_error_description(g_zfs));
2156                 return (zfs_err_to_be_err(g_zfs));
2157         }
2158 
2159         /* Mount the BE's root filesystem */
2160         if (zfs_mount(zhp, NULL, 0) != 0) {
2161                 be_print_err(gettext("be_mount_root: failed to "
2162                     "mount dataset %s at %s: %s\n"), zfs_get_name(zhp),
2163                     altroot, libzfs_error_description(g_zfs));
2164                 /*
2165                  * Set this BE's root filesystem 'mountpoint' property
2166                  * back to what it was before.
2167                  */
2168                 (void) zfs_prop_set(zhp, zfs_prop_to_name(ZFS_PROP_MOUNTPOINT),
2169                     mountpoint);
2170                 return (zfs_err_to_be_err(g_zfs));
2171         }
2172 
2173         return (BE_SUCCESS);
2174 }
2175 
2176 /*
2177  * Function:    be_unmount_root
2178  * Description: This function unmounts the root dataset of a BE, but before
2179  *              unmounting, it looks at the BE's vfstab to determine
2180  *              if the root dataset mountpoint should be left as 'legacy'
2181  *              or '/'.  If the vfstab contains an entry for this root
2182  *              dataset with a mountpoint of '/', it sets the mountpoint
2183  *              property to 'legacy'.
2184  *
2185  * Parameters:
2186  *              zhp - zfs_handle_t pointer of the BE root dataset that
2187  *              is currently mounted.
2188  *              ud - be_unmount_data_t pointer providing unmount data
2189  *              for the given BE root dataset.
2190  * Returns:
2191  *              BE_SUCCESS - Success
2192  *              be_errno_t - Failure
2193  * Scope:
2194  *              Private
2195  */
2196 static int
2197 be_unmount_root(zfs_handle_t *zhp, be_unmount_data_t *ud)
2198 {
2199         char            mountpoint[MAXPATHLEN];
2200         boolean_t       is_legacy = B_FALSE;
2201 
2202         /* See if this is a legacy mounted root */
2203         if (get_mountpoint_from_vfstab(ud->altroot, zfs_get_name(zhp),
2204             mountpoint, sizeof (mountpoint), B_FALSE) == BE_SUCCESS &&
2205             strcmp(mountpoint, "/") == 0) {
2206                 is_legacy = B_TRUE;
2207         }
2208 
2209         /* Unmount the dataset */
2210         if (zfs_unmount(zhp, NULL, ud->force ? MS_FORCE : 0) != 0) {
2211                 be_print_err(gettext("be_unmount_root: failed to "
2212                     "unmount BE root dataset %s: %s\n"), zfs_get_name(zhp),
2213                     libzfs_error_description(g_zfs));
2214                 return (zfs_err_to_be_err(g_zfs));
2215         }
2216 
2217         /* Set canmount property for this BE's root filesystem to noauto */
2218         if (zfs_prop_set(zhp, zfs_prop_to_name(ZFS_PROP_CANMOUNT), "noauto")
2219             != 0) {
2220                 be_print_err(gettext("be_unmount_root: failed to "
2221                     "set canmount property for %s to 'noauto': %s\n"),
2222                     zfs_get_name(zhp), libzfs_error_description(g_zfs));
2223                 return (zfs_err_to_be_err(g_zfs));
2224         }
2225 
2226         /*
2227          * Set mountpoint for BE's root dataset back to '/', or 'legacy'
2228          * if its a legacy mounted root.
2229          */
2230         if (zfs_prop_set(zhp, zfs_prop_to_name(ZFS_PROP_MOUNTPOINT),
2231             is_legacy ? ZFS_MOUNTPOINT_LEGACY : "/") != 0) {
2232                 be_print_err(gettext("be_unmount_root: failed to "
2233                     "set mountpoint of %s to %s\n"), zfs_get_name(zhp),
2234                     is_legacy ? ZFS_MOUNTPOINT_LEGACY : "/");
2235                 return (zfs_err_to_be_err(g_zfs));
2236         }
2237 
2238         return (BE_SUCCESS);
2239 }
2240 
2241 /*
2242  * Function:    fix_mountpoint
2243  * Description: This function checks the mountpoint of an unmounted BE to make
2244  *              sure that it is set to either 'legacy' or '/'.  If it's not,
2245  *              then we're in a situation where an unmounted BE has some random
2246  *              mountpoint set for it.  (This could happen if the system was
2247  *              rebooted while an inactive BE was mounted).  This function
2248  *              attempts to fix its mountpoints.
2249  * Parameters:
2250  *              zhp - zfs_handle_t pointer to root dataset of the BE
2251  *              whose mountpoint needs to be checked.
2252  * Return:
2253  *              BE_SUCCESS - Success
2254  *              be_errno_t - Failure
2255  * Scope:
2256  *              Private
2257  */
2258 static int
2259 fix_mountpoint(zfs_handle_t *zhp)
2260 {
2261         be_unmount_data_t       ud = { 0 };
2262         char    *altroot = NULL;
2263         char    mountpoint[MAXPATHLEN];
2264         int     ret = BE_SUCCESS;
2265 
2266         /*
2267          * Record what this BE's root dataset mountpoint property is currently
2268          * set to.
2269          */
2270         if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mountpoint,
2271             sizeof (mountpoint), NULL, NULL, 0, B_FALSE) != 0) {
2272                 be_print_err(gettext("fix_mountpoint: failed to get "
2273                     "mountpoint property of (%s): %s\n"), zfs_get_name(zhp),
2274                     libzfs_error_description(g_zfs));
2275                 return (BE_ERR_ZFS);
2276         }
2277 
2278         /*
2279          * If the root dataset mountpoint is set to 'legacy' or '/', we're okay.
2280          */
2281         if (strcmp(mountpoint, ZFS_MOUNTPOINT_LEGACY) == 0 ||
2282             strcmp(mountpoint, "/") == 0) {
2283                 return (BE_SUCCESS);
2284         }
2285 
2286         /*
2287          * Iterate through this BE's children datasets and fix
2288          * them if they need fixing.
2289          */
2290         if (zfs_iter_filesystems(zhp, fix_mountpoint_callback, mountpoint)
2291             != 0) {
2292                 return (BE_ERR_ZFS);
2293         }
2294 
2295         /*
2296          * The process of mounting and unmounting the root file system
2297          * will fix its mountpoint to correctly be either 'legacy' or '/'
2298          * since be_unmount_root will do the right thing by looking at
2299          * its vfstab.
2300          */
2301 
2302         /* Generate temporary altroot to mount the root file system */
2303         if ((ret = be_make_tmp_mountpoint(&altroot)) != BE_SUCCESS) {
2304                 be_print_err(gettext("fix_mountpoint: failed to "
2305                     "make temporary mountpoint\n"));
2306                 return (ret);
2307         }
2308 
2309         /* Mount and unmount the root. */
2310         if ((ret = be_mount_root(zhp, altroot)) != BE_SUCCESS) {
2311                 be_print_err(gettext("fix_mountpoint: failed to "
2312                     "mount BE root file system\n"));
2313                 goto cleanup;
2314         }
2315         ud.altroot = altroot;
2316         if ((ret = be_unmount_root(zhp, &ud)) != BE_SUCCESS) {
2317                 be_print_err(gettext("fix_mountpoint: failed to "
2318                     "unmount BE root file system\n"));
2319                 goto cleanup;
2320         }
2321 
2322 cleanup:
2323         free(altroot);
2324 
2325         return (ret);
2326 }
2327 
2328 /*
2329  * Function:    be_mount_zones
2330  * Description: This function finds all supported non-global zones in the
2331  *              given global BE and mounts them with respect to where the
2332  *              global BE is currently mounted.  The global BE datasets
2333  *              (including its shared datasets) are expected to already
2334  *              be mounted.
2335  * Parameters:
2336  *              be_zhp - zfs_handle_t pointer to the root dataset of the
2337  *                      global BE.
2338  *              md - be_mount_data_t pointer to data for global BE.
2339  * Returns:
2340  *              BE_SUCCESS - Success
2341  *              be_errno_t - Failure
2342  * Scope:
2343  *              Private
2344  */
2345 static int
2346 be_mount_zones(zfs_handle_t *be_zhp, be_mount_data_t *md)
2347 {
2348         zoneBrandList_t *brands = NULL;
2349         zoneList_t      zlst = NULL;
2350         char            *zonename = NULL;
2351         char            *zonepath = NULL;
2352         char            *zonepath_ds = NULL;
2353         int             k;
2354         int             ret = BE_SUCCESS;
2355 
2356         z_set_zone_root(md->altroot);
2357 
2358         if ((brands = be_get_supported_brandlist()) == NULL) {
2359                 be_print_err(gettext("be_mount_zones: "
2360                     "no supported brands\n"));
2361                 return (BE_SUCCESS);
2362         }
2363 
2364         zlst = z_get_nonglobal_zone_list_by_brand(brands);
2365         if (zlst == NULL) {
2366                 z_free_brand_list(brands);
2367                 return (BE_SUCCESS);
2368         }
2369 
2370         for (k = 0; (zonename = z_zlist_get_zonename(zlst, k)) != NULL; k++) {
2371                 if (z_zlist_get_current_state(zlst, k) ==
2372                     ZONE_STATE_INSTALLED) {
2373                         zonepath = z_zlist_get_zonepath(zlst, k);
2374 
2375                         /*
2376                          * Get the dataset of this zonepath in current BE.
2377                          * If its not a dataset, skip it.
2378                          */
2379                         if ((zonepath_ds = be_get_ds_from_dir(zonepath))
2380                             == NULL)
2381                                 continue;
2382 
2383                         /*
2384                          * Check if this zone is supported based on
2385                          * the dataset of its zonepath
2386                          */
2387                         if (!be_zone_supported(zonepath_ds)) {
2388                                 free(zonepath_ds);
2389                                 zonepath_ds = NULL;
2390                                 continue;
2391                         }
2392 
2393                         /*
2394                          * if BE's shared file systems are already mounted,
2395                          * zone path dataset would have already been lofs
2396                          * mounted under altroot. Otherwise, we need to do
2397                          * it here.
2398                          */
2399                         if (!md->shared_fs) {
2400                                 ret = loopback_mount_zonepath(zonepath, md);
2401                                 if (ret != BE_SUCCESS)
2402                                         goto done;
2403                         }
2404 
2405 
2406                         /* Mount this zone */
2407                         ret = be_mount_one_zone(be_zhp, md, zonename,
2408                             zonepath, zonepath_ds);
2409 
2410                         free(zonepath_ds);
2411                         zonepath_ds = NULL;
2412 
2413                         if (ret != BE_SUCCESS) {
2414                                 be_print_err(gettext("be_mount_zones: "
2415                                     "failed to mount zone %s under "
2416                                     "altroot %s\n"), zonename, md->altroot);
2417                                 goto done;
2418                         }
2419                 }
2420         }
2421 
2422 done:
2423         z_free_brand_list(brands);
2424         z_free_zone_list(zlst);
2425         /*
2426          * libinstzones caches mnttab and uses cached version for resolving lofs
2427          * mounts when we call z_resolve_lofs. It creates the cached version
2428          * when the first call to z_resolve_lofs happens. So, library's cached
2429          * mnttab doesn't contain entries for lofs mounts created in the above
2430          * loop. Because of this, subsequent calls to z_resolve_lofs would fail
2431          * to resolve these lofs mounts. So, here we destroy library's cached
2432          * mnttab to force its recreation when the next call to z_resolve_lofs
2433          * happens.
2434          */
2435         z_destroyMountTable();
2436         return (ret);
2437 }
2438 
2439 /*
2440  * Function:    be_unmount_zones
2441  * Description: This function finds all supported non-global zones in the
2442  *              given mounted global BE and unmounts them.
2443  * Parameters:
2444  *              ud - unmount_data_t pointer data for the global BE.
2445  * Returns:
2446  *              BE_SUCCESS - Success
2447  *              be_errno_t - Failure
2448  * Scope:
2449  *              Private
2450  */
2451 static int
2452 be_unmount_zones(be_unmount_data_t *ud)
2453 {
2454         zoneBrandList_t         *brands = NULL;
2455         zoneList_t              zlst = NULL;
2456         char                    *zonename = NULL;
2457         char                    *zonepath = NULL;
2458         char                    alt_zonepath[MAXPATHLEN];
2459         char                    *zonepath_ds = NULL;
2460         int                     k;
2461         int                     ret = BE_SUCCESS;
2462 
2463         z_set_zone_root(ud->altroot);
2464 
2465         if ((brands = be_get_supported_brandlist()) == NULL) {
2466                 be_print_err(gettext("be_unmount_zones: "
2467                     "no supported brands\n"));
2468                 return (BE_SUCCESS);
2469         }
2470 
2471         zlst = z_get_nonglobal_zone_list_by_brand(brands);
2472         if (zlst == NULL) {
2473                 z_free_brand_list(brands);
2474                 return (BE_SUCCESS);
2475         }
2476 
2477         for (k = 0; (zonename = z_zlist_get_zonename(zlst, k)) != NULL; k++) {
2478                 if (z_zlist_get_current_state(zlst, k) ==
2479                     ZONE_STATE_INSTALLED) {
2480                         zonepath = z_zlist_get_zonepath(zlst, k);
2481 
2482                         /* Build zone's zonepath wrt the global BE altroot */
2483                         (void) snprintf(alt_zonepath, sizeof (alt_zonepath),
2484                             "%s%s", ud->altroot, zonepath);
2485 
2486                         /*
2487                          * Get the dataset of this zonepath.  If its not
2488                          * a dataset, skip it.
2489                          */
2490                         if ((zonepath_ds = be_get_ds_from_dir(alt_zonepath))
2491                             == NULL)
2492                                 continue;
2493 
2494                         /*
2495                          * Check if this zone is supported based on the
2496                          * dataset of its zonepath.
2497                          */
2498                         if (!be_zone_supported(zonepath_ds)) {
2499                                 free(zonepath_ds);
2500                                 zonepath_ds = NULL;
2501                                 continue;
2502                         }
2503 
2504                         /* Unmount this zone */
2505                         ret = be_unmount_one_zone(ud, zonename, zonepath,
2506                             zonepath_ds);
2507 
2508                         free(zonepath_ds);
2509                         zonepath_ds = NULL;
2510 
2511                         if (ret != BE_SUCCESS) {
2512                                 be_print_err(gettext("be_unmount_zones:"
2513                                     " failed to unmount zone %s from "
2514                                     "altroot %s\n"), zonename, ud->altroot);
2515                                 goto done;
2516                         }
2517                 }
2518         }
2519 
2520 done:
2521         z_free_brand_list(brands);
2522         z_free_zone_list(zlst);
2523         return (ret);
2524 }
2525 
2526 /*
2527  * Function:    be_mount_one_zone
2528  * Description: This function is called to mount one zone for a given
2529  *              global BE.
2530  * Parameters:
2531  *              be_zhp - zfs_handle_t pointer to the root dataset of the
2532  *                      global BE
2533  *              md - be_mount_data_t pointer to data for global BE
2534  *              zonename - name of zone to mount
2535  *              zonepath - zonepath of zone to mount
2536  *              zonepath_ds - dataset for the zonepath
2537  * Returns:
2538  *              BE_SUCCESS - Success
2539  *              be_errno_t - Failure
2540  * Scope:
2541  *              Private
2542  */
2543 static int
2544 be_mount_one_zone(zfs_handle_t *be_zhp, be_mount_data_t *md, char *zonename,
2545     char *zonepath, char *zonepath_ds)
2546 {
2547         be_mount_data_t zone_md = { 0 };
2548         zfs_handle_t    *zone_zhp = NULL;
2549         char            zone_altroot[MAXPATHLEN];
2550         char            zoneroot[MAXPATHLEN];
2551         char            zoneroot_ds[MAXPATHLEN];
2552         int             ret = BE_SUCCESS;
2553 
2554         /* Find the active zone root dataset for this zone for this BE */
2555         if ((ret = be_find_active_zone_root(be_zhp, zonepath_ds, zoneroot_ds,
2556             sizeof (zoneroot_ds))) == BE_ERR_ZONE_NO_ACTIVE_ROOT) {
2557                 be_print_err(gettext("be_mount_one_zone: did not "
2558                     "find active zone root for zone %s, skipping ...\n"),
2559                     zonename);
2560                 return (BE_SUCCESS);
2561         } else if (ret != BE_SUCCESS) {
2562                 be_print_err(gettext("be_mount_one_zone: failed to "
2563                     "find active zone root for zone %s\n"), zonename);
2564                 return (ret);
2565         }
2566 
2567         /* Get handle to active zoneroot dataset */
2568         if ((zone_zhp = zfs_open(g_zfs, zoneroot_ds, ZFS_TYPE_FILESYSTEM))
2569             == NULL) {
2570                 be_print_err(gettext("be_mount_one_zone: failed to "
2571                     "open zone root dataset (%s): %s\n"), zoneroot_ds,
2572                     libzfs_error_description(g_zfs));
2573                 return (zfs_err_to_be_err(g_zfs));
2574         }
2575 
2576         /* Generate string for zone's altroot path */
2577         be_make_zoneroot(zonepath, zoneroot, sizeof (zoneroot));
2578         (void) strlcpy(zone_altroot, md->altroot, sizeof (zone_altroot));
2579         (void) strlcat(zone_altroot, zoneroot, sizeof (zone_altroot));
2580 
2581         /* Build mount_data for the zone */
2582         zone_md.altroot = zone_altroot;
2583         zone_md.shared_fs = md->shared_fs;
2584         zone_md.shared_rw = md->shared_rw;
2585 
2586         /* Mount the zone's root file system */
2587         if ((ret = be_mount_zone_root(zone_zhp, &zone_md)) != BE_SUCCESS) {
2588                 be_print_err(gettext("be_mount_one_zone: failed to "
2589                     "mount zone root file system at %s\n"), zone_altroot);
2590                 goto done;
2591         }
2592 
2593         /* Iterate through zone's children filesystems */
2594         if ((ret = zfs_iter_filesystems(zone_zhp, be_mount_callback,
2595             zone_altroot)) != 0) {
2596                 be_print_err(gettext("be_mount_one_zone: failed to "
2597                     "mount zone subordinate file systems at %s\n"),
2598                     zone_altroot);
2599                 goto done;
2600         }
2601 
2602         /* TODO: Mount all shared file systems for this zone */
2603 
2604 done:
2605         ZFS_CLOSE(zone_zhp);
2606         return (ret);
2607 }
2608 
2609 /*
2610  * Function:    be_unmount_one_zone
2611  * Description: This function unmount one zone for a give global BE.
2612  * Parameters:
2613  *              ud - be_unmount_data_t pointer to data for global BE
2614  *              zonename - name of zone to unmount
2615  *              zonepath - zonepath of the zone to unmount
2616  *              zonepath_ds - dataset for the zonepath
2617  * Returns:
2618  *              BE_SUCCESS - Success
2619  *              be_errno_t - Failure
2620  * Scope:
2621  *              Private
2622  */
2623 static int
2624 be_unmount_one_zone(be_unmount_data_t *ud, char *zonename, char *zonepath,
2625     char *zonepath_ds)
2626 {
2627         be_unmount_data_t       zone_ud = { 0 };
2628         zfs_handle_t    *zone_zhp = NULL;
2629         char            zone_altroot[MAXPATHLEN];
2630         char            zoneroot[MAXPATHLEN];
2631         char            zoneroot_ds[MAXPATHLEN];
2632         int             ret = BE_SUCCESS;
2633 
2634         /* Generate string for zone's alternate root path */
2635         be_make_zoneroot(zonepath, zoneroot, sizeof (zoneroot));
2636         (void) strlcpy(zone_altroot, ud->altroot, sizeof (zone_altroot));
2637         (void) strlcat(zone_altroot, zoneroot, sizeof (zone_altroot));
2638 
2639         /* Build be_unmount_data for zone */
2640         zone_ud.altroot = zone_altroot;
2641         zone_ud.force = ud->force;
2642 
2643         /* Find the mounted zone root dataset for this zone for this BE */
2644         if ((ret = be_find_mounted_zone_root(zone_altroot, zonepath_ds,
2645             zoneroot_ds, sizeof (zoneroot_ds))) == BE_ERR_NO_MOUNTED_ZONE) {
2646                 be_print_err(gettext("be_unmount_one_zone: did not "
2647                     "find any zone root mounted for zone %s\n"), zonename);
2648                 return (BE_SUCCESS);
2649         } else if (ret != BE_SUCCESS) {
2650                 be_print_err(gettext("be_unmount_one_zone: failed to "
2651                     "find mounted zone root for zone %s\n"), zonename);
2652                 return (ret);
2653         }
2654 
2655         /* Get handle to zoneroot dataset mounted for this BE */
2656         if ((zone_zhp = zfs_open(g_zfs, zoneroot_ds, ZFS_TYPE_FILESYSTEM))
2657             == NULL) {
2658                 be_print_err(gettext("be_unmount_one_zone: failed to "
2659                     "open mounted zone root dataset (%s): %s\n"), zoneroot_ds,
2660                     libzfs_error_description(g_zfs));
2661                 return (zfs_err_to_be_err(g_zfs));
2662         }
2663 
2664         /* TODO: Unmount all shared file systems for this zone */
2665 
2666         /* Iterate through zone's children filesystems and unmount them */
2667         if ((ret = zfs_iter_filesystems(zone_zhp, be_unmount_callback,
2668             &zone_ud)) != 0) {
2669                 be_print_err(gettext("be_unmount_one_zone: failed to "
2670                     "unmount zone subordinate file systems at %s\n"),
2671                     zone_altroot);
2672                 goto done;
2673         }
2674 
2675         /* Unmount the zone's root filesystem */
2676         if ((ret = be_unmount_zone_root(zone_zhp, &zone_ud)) != BE_SUCCESS) {
2677                 be_print_err(gettext("be_unmount_one_zone: failed to "
2678                     "unmount zone root file system at %s\n"), zone_altroot);
2679                 goto done;
2680         }
2681 
2682 done:
2683         ZFS_CLOSE(zone_zhp);
2684         return (ret);
2685 }
2686 
2687 /*
2688  * Function:    be_get_ds_from_dir_callback
2689  * Description: This is a callback function used to iterate all datasets
2690  *              to find the one that is currently mounted at the directory
2691  *              being searched for.  If matched, the name of the dataset is
2692  *              returned in heap storage, so the caller is responsible for
2693  *              freeing it.
2694  * Parameters:
2695  *              zhp - zfs_handle_t pointer to current dataset being processed.
2696  *              data - dir_data_t pointer providing name of directory being
2697  *                      searched for.
2698  * Returns:
2699  *              1 - This dataset is mounted at directory being searched for.
2700  *              0 - This dataset is not mounted at directory being searched for.
2701  * Scope:
2702  *              Private
2703  */
2704 static int
2705 be_get_ds_from_dir_callback(zfs_handle_t *zhp, void *data)
2706 {
2707         dir_data_t      *dd = data;
2708         char            *mp = NULL;
2709         int             zret = 0;
2710 
2711         if (zfs_get_type(zhp) != ZFS_TYPE_FILESYSTEM) {
2712                 ZFS_CLOSE(zhp);
2713                 return (0);
2714         }
2715 
2716         if (zfs_is_mounted(zhp, &mp) && mp != NULL &&
2717             strcmp(mp, dd->dir) == 0) {
2718                 if ((dd->ds = strdup(zfs_get_name(zhp))) == NULL) {
2719                         be_print_err(gettext("be_get_ds_from_dir_callback: "
2720                             "memory allocation failed\n"));
2721                         ZFS_CLOSE(zhp);
2722                         return (0);
2723                 }
2724                 ZFS_CLOSE(zhp);
2725                 return (1);
2726         }
2727 
2728         zret = zfs_iter_filesystems(zhp, be_get_ds_from_dir_callback, dd);
2729 
2730         ZFS_CLOSE(zhp);
2731 
2732         return (zret);
2733 }