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