xref: /titanic_50/usr/src/lib/libbe/common/be_mount.c (revision 7386f4319a95797d8397f24d609435b2c0e027a6)
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 }
2704