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