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