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
52typedef struct dir_data {
53	char *dir;
54	char *ds;
55} dir_data_t;
56
57/* Private function prototypes */
58static int be_mount_callback(zfs_handle_t *, void *);
59static int be_unmount_callback(zfs_handle_t *, void *);
60static int be_get_legacy_fs_callback(zfs_handle_t *, void *);
61static int fix_mountpoint(zfs_handle_t *);
62static int fix_mountpoint_callback(zfs_handle_t *, void *);
63static int get_mountpoint_from_vfstab(char *, const char *, char *, size_t,
64    boolean_t);
65static int loopback_mount_shared_fs(zfs_handle_t *, be_mount_data_t *);
66static int loopback_mount_zonepath(const char *, be_mount_data_t *);
67static int iter_shared_fs_callback(zfs_handle_t *, void *);
68static int zpool_shared_fs_callback(zpool_handle_t *, void *);
69static int unmount_shared_fs(be_unmount_data_t *);
70static int add_to_fs_list(be_fs_list_data_t *, const char *);
71static int be_mount_root(zfs_handle_t *, char *);
72static int be_unmount_root(zfs_handle_t *, be_unmount_data_t *);
73static int be_mount_zones(zfs_handle_t *, be_mount_data_t *);
74static int be_unmount_zones(be_unmount_data_t *);
75static int be_mount_one_zone(zfs_handle_t *, be_mount_data_t *, char *, char *,
76    char *);
77static int be_unmount_one_zone(be_unmount_data_t *, char *, char *, char *);
78static 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 */
101int
102be_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 */
166int
167be_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 */
229int
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 */
387int
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 */
534int
535be_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 */
597int
598be_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 */
659int
660be_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
778cleanup:
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 */
823void
824be_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 */
856char *
857be_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 */
892int
893be_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 */
943int
944be_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 */
1047int
1048be_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 */
1096static int
1097be_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
1218next:
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 */
1245static int
1246be_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
1350done:
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 */
1379static int
1380be_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
1423next:
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 */
1447static int
1448add_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 */
1486static int
1487zpool_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 */
1541static int
1542iter_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 */
1603static int
1604loopback_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 */
1679static int
1680loopback_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
1803done :
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 */
1821static int
1822unmount_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 */
1926static int
1927get_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 */
1989static int
1990fix_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 */
2073static int
2074be_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 */
2147static int
2148be_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 */
2209static int
2210fix_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
2273cleanup:
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 */
2296static int
2297be_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
2373done:
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 */
2402static int
2403be_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
2471done:
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 */
2494static int
2495be_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
2555done:
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 */
2574static int
2575be_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
2633done:
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 */
2655static int
2656be_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