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