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