dmake: do not set MAKEFLAGS=k
[unleashed/tickless.git] / usr / src / lib / libbe / common / be_mount.c
blob1f3562b527a3912d95395159c72037d1ad50901f
1 /*
2 * CDDL HEADER START
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.
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.
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]
19 * CDDL HEADER END
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.
30 * System includes
32 #include <assert.h>
33 #include <errno.h>
34 #include <libgen.h>
35 #include <libintl.h>
36 #include <libnvpair.h>
37 #include <libzfs.h>
38 #include <stdio.h>
39 #include <stdlib.h>
40 #include <string.h>
41 #include <sys/mntent.h>
42 #include <sys/mnttab.h>
43 #include <sys/mount.h>
44 #include <sys/stat.h>
45 #include <sys/types.h>
46 #include <sys/vfstab.h>
47 #include <sys/zone.h>
48 #include <sys/mkdev.h>
49 #include <unistd.h>
51 #include <libbe.h>
52 #include <libbe_priv.h>
54 #define BE_TMP_MNTPNT "/tmp/.be.XXXXXX"
56 typedef struct dir_data {
57 char *dir;
58 char *ds;
59 } dir_data_t;
61 /* Private function prototypes */
62 static int be_mount_callback(zfs_handle_t *, void *);
63 static int be_unmount_callback(zfs_handle_t *, void *);
64 static int be_get_legacy_fs_callback(zfs_handle_t *, void *);
65 static int fix_mountpoint(zfs_handle_t *);
66 static int fix_mountpoint_callback(zfs_handle_t *, void *);
67 static int get_mountpoint_from_vfstab(char *, const char *, char *, size_t,
68 boolean_t);
69 static int loopback_mount_shared_fs(zfs_handle_t *, be_mount_data_t *);
70 static int loopback_mount_zonepath(const char *, be_mount_data_t *);
71 static int iter_shared_fs_callback(zfs_handle_t *, void *);
72 static int zpool_shared_fs_callback(zpool_handle_t *, void *);
73 static int unmount_shared_fs(be_unmount_data_t *);
74 static int add_to_fs_list(be_fs_list_data_t *, const char *);
75 static int be_mount_root(zfs_handle_t *, char *);
76 static int be_unmount_root(zfs_handle_t *, be_unmount_data_t *);
77 static int be_mount_zones(zfs_handle_t *, be_mount_data_t *);
78 static int be_unmount_zones(be_unmount_data_t *);
79 static int be_mount_one_zone(zfs_handle_t *, be_mount_data_t *, char *, char *,
80 char *);
81 static int be_unmount_one_zone(be_unmount_data_t *, char *, char *, char *);
82 static int be_get_ds_from_dir_callback(zfs_handle_t *, void *);
83 static int mount_zfs(zfs_handle_t *, char *);
86 /* ******************************************************************** */
87 /* Public Functions */
88 /* ******************************************************************** */
91 * Function: be_mount
92 * Description: Mounts a BE and its subordinate datasets at a given mountpoint.
93 * Parameters:
94 * be_attrs - pointer to nvlist_t of attributes being passed in.
95 * The following attributes are used by this function:
97 * BE_ATTR_ORIG_BE_NAME *required
98 * BE_ATTR_MOUNTPOINT *required
99 * BE_ATTR_MOUNT_FLAGS *optional
100 * Return:
101 * BE_SUCCESS - Success
102 * be_errno_t - Failure
103 * Scope:
104 * Public
107 be_mount(nvlist_t *be_attrs)
109 char *be_name = NULL;
110 char *mountpoint = NULL;
111 uint16_t flags = 0;
112 int ret = BE_SUCCESS;
114 /* Initialize libzfs handle */
115 if (!be_zfs_init())
116 return (BE_ERR_INIT);
118 /* Get original BE name */
119 if (nvlist_lookup_string(be_attrs, BE_ATTR_ORIG_BE_NAME, &be_name)
120 != 0) {
121 be_print_err(gettext("be_mount: failed to lookup "
122 "BE_ATTR_ORIG_BE_NAME attribute\n"));
123 return (BE_ERR_INVAL);
126 /* Validate original BE name */
127 if (!be_valid_be_name(be_name)) {
128 be_print_err(gettext("be_mount: invalid BE name %s\n"),
129 be_name);
130 return (BE_ERR_INVAL);
133 /* Get mountpoint */
134 if (nvlist_lookup_string(be_attrs, BE_ATTR_MOUNTPOINT, &mountpoint)
135 != 0) {
136 be_print_err(gettext("be_mount: failed to lookup "
137 "BE_ATTR_MOUNTPOINT attribute\n"));
138 return (BE_ERR_INVAL);
141 /* Get flags */
142 if (nvlist_lookup_pairs(be_attrs, NV_FLAG_NOENTOK,
143 BE_ATTR_MOUNT_FLAGS, DATA_TYPE_UINT16, &flags, NULL) != 0) {
144 be_print_err(gettext("be_mount: failed to lookup "
145 "BE_ATTR_MOUNT_FLAGS attribute\n"));
146 return (BE_ERR_INVAL);
149 ret = _be_mount(be_name, &mountpoint, flags);
151 be_zfs_fini();
153 return (ret);
157 * Function: be_unmount
158 * Description: Unmounts a BE and its subordinate datasets.
159 * Parameters:
160 * be_attrs - pointer to nvlist_t of attributes being passed in.
161 * The following attributes are used by this function:
163 * BE_ATTR_ORIG_BE_NAME *required
164 * BE_ATTR_UNMOUNT_FLAGS *optional
165 * Return:
166 * BE_SUCCESS - Success
167 * be_errno_t - Failure
168 * Scope:
169 * Public
172 be_unmount(nvlist_t *be_attrs)
174 char *be_name = NULL;
175 char *be_name_mnt = NULL;
176 char *ds = NULL;
177 uint16_t flags = 0;
178 int ret = BE_SUCCESS;
180 /* Initialize libzfs handle */
181 if (!be_zfs_init())
182 return (BE_ERR_INIT);
184 /* Get original BE name */
185 if (nvlist_lookup_string(be_attrs, BE_ATTR_ORIG_BE_NAME, &be_name)
186 != 0) {
187 be_print_err(gettext("be_unmount: failed to lookup "
188 "BE_ATTR_ORIG_BE_NAME attribute\n"));
189 return (BE_ERR_INVAL);
192 /* Check if we have mountpoint argument instead of BE name */
193 if (be_name[0] == '/') {
194 if ((ds = be_get_ds_from_dir(be_name)) != NULL) {
195 if ((be_name_mnt = strrchr(ds, '/')) != NULL) {
196 be_name = be_name_mnt + 1;
198 } else {
199 be_print_err(gettext("be_unmount: no datasets mounted "
200 "at '%s'\n"), be_name);
201 return (BE_ERR_INVAL);
205 /* Validate original BE name */
206 if (!be_valid_be_name(be_name)) {
207 be_print_err(gettext("be_unmount: invalid BE name %s\n"),
208 be_name);
209 return (BE_ERR_INVAL);
212 /* Get unmount flags */
213 if (nvlist_lookup_pairs(be_attrs, NV_FLAG_NOENTOK,
214 BE_ATTR_UNMOUNT_FLAGS, DATA_TYPE_UINT16, &flags, NULL) != 0) {
215 be_print_err(gettext("be_unmount: failed to loookup "
216 "BE_ATTR_UNMOUNT_FLAGS attribute\n"));
217 return (BE_ERR_INVAL);
220 ret = _be_unmount(be_name, flags);
222 be_zfs_fini();
224 return (ret);
227 /* ******************************************************************** */
228 /* Semi-Private Functions */
229 /* ******************************************************************** */
232 * Function: _be_mount
233 * Description: Mounts a BE. If the altroot is not provided, this function
234 * will generate a temporary mountpoint to mount the BE at. It
235 * will return this temporary mountpoint to the caller via the
236 * altroot reference pointer passed in. This returned value is
237 * allocated on heap storage and is the repsonsibility of the
238 * caller to free.
239 * Parameters:
240 * be_name - pointer to name of BE to mount.
241 * altroot - reference pointer to altroot of where to mount BE.
242 * flags - flag indicating special handling for mounting the BE
243 * Return:
244 * BE_SUCCESS - Success
245 * be_errno_t - Failure
246 * Scope:
247 * Semi-private (library wide use only)
250 _be_mount(char *be_name, char **altroot, int flags)
252 be_transaction_data_t bt = { 0 };
253 be_mount_data_t md = { 0 };
254 zfs_handle_t *zhp;
255 char obe_root_ds[MAXPATHLEN];
256 char *mp = NULL;
257 char *tmp_altroot = NULL;
258 int ret = BE_SUCCESS, err = 0;
259 uuid_t uu = { 0 };
260 boolean_t gen_tmp_altroot = B_FALSE;
262 if (be_name == NULL || altroot == NULL)
263 return (BE_ERR_INVAL);
265 /* Set be_name as obe_name in bt structure */
266 bt.obe_name = be_name;
268 /* Find which zpool obe_name lives in */
269 if ((err = zpool_iter(g_zfs, be_find_zpool_callback, &bt)) == 0) {
270 be_print_err(gettext("be_mount: failed to "
271 "find zpool for BE (%s)\n"), bt.obe_name);
272 return (BE_ERR_BE_NOENT);
273 } else if (err < 0) {
274 be_print_err(gettext("be_mount: zpool_iter failed: %s\n"),
275 libzfs_error_description(g_zfs));
276 return (zfs_err_to_be_err(g_zfs));
279 /* Generate string for obe_name's root dataset */
280 be_make_root_ds(bt.obe_zpool, bt.obe_name, obe_root_ds,
281 sizeof (obe_root_ds));
282 bt.obe_root_ds = obe_root_ds;
284 /* Get handle to BE's root dataset */
285 if ((zhp = zfs_open(g_zfs, bt.obe_root_ds, ZFS_TYPE_FILESYSTEM)) ==
286 NULL) {
287 be_print_err(gettext("be_mount: failed to "
288 "open BE root dataset (%s): %s\n"), bt.obe_root_ds,
289 libzfs_error_description(g_zfs));
290 return (zfs_err_to_be_err(g_zfs));
293 /* Make sure BE's root dataset isn't already mounted somewhere */
294 if (zfs_is_mounted(zhp, &mp)) {
295 ZFS_CLOSE(zhp);
296 be_print_err(gettext("be_mount: %s is already mounted "
297 "at %s\n"), bt.obe_name, mp != NULL ? mp : "");
298 free(mp);
299 return (BE_ERR_MOUNTED);
303 * Fix this BE's mountpoint if its root dataset isn't set to
304 * either 'legacy' or '/'.
306 if ((ret = fix_mountpoint(zhp)) != BE_SUCCESS) {
307 be_print_err(gettext("be_mount: mountpoint check "
308 "failed for %s\n"), bt.obe_root_ds);
309 ZFS_CLOSE(zhp);
310 return (ret);
314 * If altroot not provided, create a temporary alternate root
315 * to mount on
317 if (*altroot == NULL) {
318 if ((ret = be_make_tmp_mountpoint(&tmp_altroot))
319 != BE_SUCCESS) {
320 be_print_err(gettext("be_mount: failed to "
321 "make temporary mountpoint\n"));
322 ZFS_CLOSE(zhp);
323 return (ret);
325 gen_tmp_altroot = B_TRUE;
326 } else {
327 tmp_altroot = *altroot;
330 md.altroot = tmp_altroot;
331 md.shared_fs = flags & BE_MOUNT_FLAG_SHARED_FS;
332 md.shared_rw = flags & BE_MOUNT_FLAG_SHARED_RW;
334 /* Mount the BE's root file system */
335 if (getzoneid() == GLOBAL_ZONEID) {
336 if ((ret = be_mount_root(zhp, tmp_altroot)) != BE_SUCCESS) {
337 be_print_err(gettext("be_mount: failed to "
338 "mount BE root file system\n"));
339 if (gen_tmp_altroot)
340 free(tmp_altroot);
341 ZFS_CLOSE(zhp);
342 return (ret);
344 } else {
345 /* Legacy mount the zone root dataset */
346 if ((ret = be_mount_zone_root(zhp, &md)) != BE_SUCCESS) {
347 be_print_err(gettext("be_mount: failed to "
348 "mount BE zone root file system\n"));
349 free(md.altroot);
350 ZFS_CLOSE(zhp);
351 return (ret);
355 /* Iterate through BE's children filesystems */
356 if ((err = zfs_iter_filesystems(zhp, be_mount_callback,
357 tmp_altroot)) != 0) {
358 be_print_err(gettext("be_mount: failed to "
359 "mount BE (%s) on %s\n"), bt.obe_name, tmp_altroot);
360 if (gen_tmp_altroot)
361 free(tmp_altroot);
362 ZFS_CLOSE(zhp);
363 return (err);
367 * Mount shared file systems if mount flag says so.
369 if (md.shared_fs) {
371 * Mount all ZFS file systems not under the BE's root dataset
373 (void) zpool_iter(g_zfs, zpool_shared_fs_callback, &md);
375 /* TODO: Mount all non-ZFS file systems - Not supported yet */
379 * If we're in the global zone and the global zone has a valid uuid,
380 * mount all supported non-global zones.
382 if (getzoneid() == GLOBAL_ZONEID &&
383 !(flags & BE_MOUNT_FLAG_NO_ZONES) &&
384 be_get_uuid(bt.obe_root_ds, &uu) == BE_SUCCESS) {
385 if (be_mount_zones(zhp, &md) != BE_SUCCESS) {
386 ret = BE_ERR_NO_MOUNTED_ZONE;
390 ZFS_CLOSE(zhp);
393 * If a NULL altroot was passed in, pass the generated altroot
394 * back to the caller in altroot.
396 if (gen_tmp_altroot) {
397 if (ret == BE_SUCCESS || ret == BE_ERR_NO_MOUNTED_ZONE)
398 *altroot = tmp_altroot;
399 else
400 free(tmp_altroot);
403 return (ret);
407 * Function: _be_unmount
408 * Description: Unmount a BE.
409 * Parameters:
410 * be_name - pointer to name of BE to unmount.
411 * flags - flags for unmounting the BE.
412 * Returns:
413 * BE_SUCCESS - Success
414 * be_errno_t - Failure
415 * Scope:
416 * Semi-private (library wide use only)
419 _be_unmount(char *be_name, int flags)
421 be_transaction_data_t bt = { 0 };
422 be_unmount_data_t ud = { 0 };
423 zfs_handle_t *zhp;
424 uuid_t uu = { 0 };
425 char obe_root_ds[MAXPATHLEN];
426 char mountpoint[MAXPATHLEN];
427 char *mp = NULL;
428 int ret = BE_SUCCESS;
429 int zret = 0;
431 if (be_name == NULL)
432 return (BE_ERR_INVAL);
434 /* Set be_name as obe_name in bt structure */
435 bt.obe_name = be_name;
437 /* Find which zpool obe_name lives in */
438 if ((zret = zpool_iter(g_zfs, be_find_zpool_callback, &bt)) == 0) {
439 be_print_err(gettext("be_unmount: failed to "
440 "find zpool for BE (%s)\n"), bt.obe_name);
441 return (BE_ERR_BE_NOENT);
442 } else if (zret < 0) {
443 be_print_err(gettext("be_unmount: "
444 "zpool_iter failed: %s\n"),
445 libzfs_error_description(g_zfs));
446 ret = zfs_err_to_be_err(g_zfs);
447 return (ret);
450 /* Generate string for obe_name's root dataset */
451 be_make_root_ds(bt.obe_zpool, bt.obe_name, obe_root_ds,
452 sizeof (obe_root_ds));
453 bt.obe_root_ds = obe_root_ds;
455 /* Get handle to BE's root dataset */
456 if ((zhp = zfs_open(g_zfs, bt.obe_root_ds, ZFS_TYPE_FILESYSTEM)) ==
457 NULL) {
458 be_print_err(gettext("be_unmount: failed to "
459 "open BE root dataset (%s): %s\n"), bt.obe_root_ds,
460 libzfs_error_description(g_zfs));
461 ret = zfs_err_to_be_err(g_zfs);
462 return (ret);
465 /* Make sure BE's root dataset is mounted somewhere */
466 if (!zfs_is_mounted(zhp, &mp)) {
468 be_print_err(gettext("be_unmount: "
469 "(%s) not mounted\n"), bt.obe_name);
472 * BE is not mounted, fix this BE's mountpoint if its root
473 * dataset isn't set to either 'legacy' or '/'.
475 if ((ret = fix_mountpoint(zhp)) != BE_SUCCESS) {
476 be_print_err(gettext("be_unmount: mountpoint check "
477 "failed for %s\n"), bt.obe_root_ds);
478 ZFS_CLOSE(zhp);
479 return (ret);
482 ZFS_CLOSE(zhp);
483 return (BE_ERR_NOTMOUNTED);
487 * If we didn't get a mountpoint from the zfs_is_mounted call,
488 * try and get it from its property.
490 if (mp == NULL) {
491 if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mountpoint,
492 sizeof (mountpoint), NULL, NULL, 0, B_FALSE) != 0) {
493 be_print_err(gettext("be_unmount: failed to "
494 "get mountpoint of (%s)\n"), bt.obe_name);
495 ZFS_CLOSE(zhp);
496 return (BE_ERR_ZFS);
498 } else {
499 (void) strlcpy(mountpoint, mp, sizeof (mountpoint));
500 free(mp);
503 /* If BE mounted as current root, fail */
504 if (strcmp(mountpoint, "/") == 0) {
505 be_print_err(gettext("be_unmount: "
506 "cannot unmount currently running BE\n"));
507 ZFS_CLOSE(zhp);
508 return (BE_ERR_UMOUNT_CURR_BE);
511 ud.altroot = mountpoint;
512 ud.force = flags & BE_UNMOUNT_FLAG_FORCE;
514 /* Unmount all supported non-global zones if we're in the global zone */
515 if (getzoneid() == GLOBAL_ZONEID &&
516 be_get_uuid(bt.obe_root_ds, &uu) == BE_SUCCESS) {
517 if ((ret = be_unmount_zones(&ud)) != BE_SUCCESS) {
518 ZFS_CLOSE(zhp);
519 return (ret);
523 /* TODO: Unmount all non-ZFS file systems - Not supported yet */
525 /* Unmount all ZFS file systems not under the BE root dataset */
526 if ((ret = unmount_shared_fs(&ud)) != BE_SUCCESS) {
527 be_print_err(gettext("be_unmount: failed to "
528 "unmount shared file systems\n"));
529 ZFS_CLOSE(zhp);
530 return (ret);
533 /* Unmount all children datasets under the BE's root dataset */
534 if ((zret = zfs_iter_filesystems(zhp, be_unmount_callback,
535 &ud)) != 0) {
536 be_print_err(gettext("be_unmount: failed to "
537 "unmount BE (%s)\n"), bt.obe_name);
538 ZFS_CLOSE(zhp);
539 return (zret);
542 /* Unmount this BE's root filesystem */
543 if (getzoneid() == GLOBAL_ZONEID) {
544 if ((ret = be_unmount_root(zhp, &ud)) != BE_SUCCESS) {
545 ZFS_CLOSE(zhp);
546 return (ret);
548 } else {
549 if ((ret = be_unmount_zone_root(zhp, &ud)) != BE_SUCCESS) {
550 ZFS_CLOSE(zhp);
551 return (ret);
555 ZFS_CLOSE(zhp);
557 return (BE_SUCCESS);
561 * Function: be_mount_zone_root
562 * Description: Mounts the zone root dataset for a zone.
563 * Parameters:
564 * zfs - zfs_handle_t pointer to zone root dataset
565 * md - be_mount_data_t pointer to data for zone to be mounted
566 * Returns:
567 * BE_SUCCESS - Success
568 * be_errno_t - Failure
569 * Scope:
570 * Semi-private (library wide use only)
573 be_mount_zone_root(zfs_handle_t *zhp, be_mount_data_t *md)
575 struct stat buf;
576 char mountpoint[MAXPATHLEN];
577 int err = 0;
579 /* Get mountpoint property of dataset */
580 if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mountpoint,
581 sizeof (mountpoint), NULL, NULL, 0, B_FALSE) != 0) {
582 be_print_err(gettext("be_mount_zone_root: failed to "
583 "get mountpoint property for %s: %s\n"), zfs_get_name(zhp),
584 libzfs_error_description(g_zfs));
585 return (zfs_err_to_be_err(g_zfs));
589 * Make sure zone's root dataset is set to 'legacy'. This is
590 * currently a requirement in this implementation of zones
591 * support.
593 if (strcmp(mountpoint, ZFS_MOUNTPOINT_LEGACY) != 0) {
594 be_print_err(gettext("be_mount_zone_root: "
595 "zone root dataset mountpoint is not 'legacy'\n"));
596 return (BE_ERR_ZONE_ROOT_NOT_LEGACY);
599 /* Create the mountpoint if it doesn't exist */
600 if (lstat(md->altroot, &buf) != 0) {
601 if (mkdirp(md->altroot, 0755) != 0) {
602 err = errno;
603 be_print_err(gettext("be_mount_zone_root: failed "
604 "to create mountpoint %s\n"), md->altroot);
605 return (errno_to_be_err(err));
610 * Legacy mount the zone root dataset.
612 * As a workaround for 6176743, we mount the zone's root with the
613 * MS_OVERLAY option in case an alternate BE is mounted, and we're
614 * mounting the root for the zone from the current BE here. When an
615 * alternate BE is mounted, it ties up the zone's zoneroot directory
616 * for the current BE since the zone's zonepath is loopback mounted
617 * from the current BE.
619 * TODO: The MS_OVERLAY option needs to be removed when 6176743
620 * is fixed.
622 if (mount(zfs_get_name(zhp), md->altroot, MS_OVERLAY, MNTTYPE_ZFS,
623 NULL, 0, NULL, 0) != 0) {
624 err = errno;
625 be_print_err(gettext("be_mount_zone_root: failed to "
626 "legacy mount zone root dataset (%s) at %s\n"),
627 zfs_get_name(zhp), md->altroot);
628 return (errno_to_be_err(err));
631 return (BE_SUCCESS);
635 * Function: be_unmount_zone_root
636 * Description: Unmounts the zone root dataset for a zone.
637 * Parameters:
638 * zhp - zfs_handle_t pointer to zone root dataset
639 * ud - be_unmount_data_t pointer to data for zone to be unmounted
640 * Returns:
641 * BE_SUCCESS - Success
642 * be_errno_t - Failure
643 * Scope:
644 * Semi-private (library wise use only)
647 be_unmount_zone_root(zfs_handle_t *zhp, be_unmount_data_t *ud)
649 char mountpoint[MAXPATHLEN];
651 /* Unmount the dataset */
652 if (zfs_unmount(zhp, NULL, ud->force ? MS_FORCE : 0) != 0) {
653 be_print_err(gettext("be_unmount_zone_root: failed to "
654 "unmount zone root dataset %s: %s\n"), zfs_get_name(zhp),
655 libzfs_error_description(g_zfs));
656 return (zfs_err_to_be_err(g_zfs));
659 /* Get the current mountpoint property for the zone root dataset */
660 if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mountpoint,
661 sizeof (mountpoint), NULL, NULL, 0, B_FALSE) != 0) {
662 be_print_err(gettext("be_unmount_zone_root: failed to "
663 "get mountpoint property for zone root dataset (%s): %s\n"),
664 zfs_get_name(zhp), libzfs_error_description(g_zfs));
665 return (zfs_err_to_be_err(g_zfs));
668 /* If mountpoint not already set to 'legacy', set it to 'legacy' */
669 if (strcmp(mountpoint, ZFS_MOUNTPOINT_LEGACY) != 0) {
670 if (zfs_prop_set(zhp, zfs_prop_to_name(ZFS_PROP_MOUNTPOINT),
671 ZFS_MOUNTPOINT_LEGACY) != 0) {
672 be_print_err(gettext("be_unmount_zone_root: "
673 "failed to set mountpoint of zone root dataset "
674 "%s to 'legacy': %s\n"), zfs_get_name(zhp),
675 libzfs_error_description(g_zfs));
676 return (zfs_err_to_be_err(g_zfs));
680 return (BE_SUCCESS);
684 * Function: be_get_legacy_fs
685 * Description: This function iterates through all non-shared file systems
686 * of a BE and finds the ones with a legacy mountpoint. For
687 * those file systems, it reads the BE's vfstab to get the
688 * mountpoint. If found, it adds that file system to the
689 * be_fs_list_data_t passed in.
691 * This function can be used to gather legacy mounted file systems
692 * for both global BEs and non-global zone BEs. To get data for
693 * a non-global zone BE, the zoneroot_ds and zoneroot parameters
694 * will be specified, otherwise they should be set to NULL.
695 * Parameters:
696 * be_name - global BE name from which to get legacy file
697 * system list.
698 * be_root_ds - root dataset of global BE.
699 * zoneroot_ds - root dataset of zone.
700 * zoneroot - zoneroot path of zone.
701 * fld - be_fs_list_data_t pointer.
702 * Returns:
703 * BE_SUCCESS - Success
704 * be_errno_t - Failure
705 * Scope:
706 * Semi-private (library wide use only)
709 be_get_legacy_fs(char *be_name, char *be_root_ds, char *zoneroot_ds,
710 char *zoneroot, be_fs_list_data_t *fld)
712 zfs_handle_t *zhp = NULL;
713 char mountpoint[MAXPATHLEN];
714 boolean_t mounted_here = B_FALSE;
715 boolean_t zone_mounted_here = B_FALSE;
716 int ret = BE_SUCCESS, err = 0;
718 if (be_name == NULL || be_root_ds == NULL || fld == NULL)
719 return (BE_ERR_INVAL);
721 /* Get handle to BE's root dataset */
722 if ((zhp = zfs_open(g_zfs, be_root_ds, ZFS_TYPE_FILESYSTEM))
723 == NULL) {
724 be_print_err(gettext("be_get_legacy_fs: failed to "
725 "open BE root dataset (%s): %s\n"), be_root_ds,
726 libzfs_error_description(g_zfs));
727 ret = zfs_err_to_be_err(g_zfs);
728 return (ret);
731 /* If BE is not already mounted, mount it. */
732 if (!zfs_is_mounted(zhp, &fld->altroot)) {
733 if ((ret = _be_mount(be_name, &fld->altroot,
734 zoneroot_ds ? BE_MOUNT_FLAG_NULL :
735 BE_MOUNT_FLAG_NO_ZONES)) != BE_SUCCESS) {
736 be_print_err(gettext("be_get_legacy_fs: "
737 "failed to mount BE %s\n"), be_name);
738 goto cleanup;
741 mounted_here = B_TRUE;
742 } else if (fld->altroot == NULL) {
743 be_print_err(gettext("be_get_legacy_fs: failed to "
744 "get altroot of mounted BE %s: %s\n"),
745 be_name, libzfs_error_description(g_zfs));
746 ret = zfs_err_to_be_err(g_zfs);
747 goto cleanup;
751 * If a zone root dataset was passed in, we're wanting to get
752 * legacy mounted file systems for that zone, not the global
753 * BE.
755 if (zoneroot_ds != NULL) {
756 be_mount_data_t zone_md = { 0 };
758 /* Close off handle to global BE's root dataset */
759 ZFS_CLOSE(zhp);
761 /* Get handle to zone's root dataset */
762 if ((zhp = zfs_open(g_zfs, zoneroot_ds,
763 ZFS_TYPE_FILESYSTEM)) == NULL) {
764 be_print_err(gettext("be_get_legacy_fs: failed to "
765 "open zone BE root dataset (%s): %s\n"),
766 zoneroot_ds, libzfs_error_description(g_zfs));
767 ret = zfs_err_to_be_err(g_zfs);
768 goto cleanup;
771 /* Make sure the zone we're looking for is mounted */
772 if (!zfs_is_mounted(zhp, &zone_md.altroot)) {
773 char zone_altroot[MAXPATHLEN];
775 /* Generate alternate root path for zone */
776 (void) snprintf(zone_altroot, sizeof (zone_altroot),
777 "%s%s", fld->altroot, zoneroot);
778 if ((zone_md.altroot = strdup(zone_altroot)) == NULL) {
779 be_print_err(gettext("be_get_legacy_fs: "
780 "memory allocation failed\n"));
781 ret = BE_ERR_NOMEM;
782 goto cleanup;
785 if ((ret = be_mount_zone_root(zhp, &zone_md))
786 != BE_SUCCESS) {
787 be_print_err(gettext("be_get_legacy_fs: "
788 "failed to mount zone root %s\n"),
789 zoneroot_ds);
790 free(zone_md.altroot);
791 zone_md.altroot = NULL;
792 goto cleanup;
794 zone_mounted_here = B_TRUE;
797 free(fld->altroot);
798 fld->altroot = zone_md.altroot;
802 * If the root dataset is in the vfstab with a mountpoint of "/",
803 * add it to the list
805 if (get_mountpoint_from_vfstab(fld->altroot, zfs_get_name(zhp),
806 mountpoint, sizeof (mountpoint), B_FALSE) == BE_SUCCESS) {
807 if (strcmp(mountpoint, "/") == 0) {
808 if (add_to_fs_list(fld, zfs_get_name(zhp))
809 != BE_SUCCESS) {
810 be_print_err(gettext("be_get_legacy_fs: "
811 "failed to add %s to fs list\n"),
812 zfs_get_name(zhp));
813 ret = BE_ERR_INVAL;
814 goto cleanup;
819 /* Iterate subordinate file systems looking for legacy mounts */
820 if ((ret = zfs_iter_filesystems(zhp, be_get_legacy_fs_callback,
821 fld)) != 0) {
822 be_print_err(gettext("be_get_legacy_fs: "
823 "failed to iterate %s to get legacy mounts\n"),
824 zfs_get_name(zhp));
827 cleanup:
828 /* If we mounted the zone BE, unmount it */
829 if (zone_mounted_here) {
830 be_unmount_data_t zone_ud = { 0 };
832 zone_ud.altroot = fld->altroot;
833 zone_ud.force = B_TRUE;
834 if ((err = be_unmount_zone_root(zhp, &zone_ud)) != BE_SUCCESS) {
835 be_print_err(gettext("be_get_legacy_fs: "
836 "failed to unmount zone root %s\n"),
837 zoneroot_ds);
838 if (ret == BE_SUCCESS)
839 ret = err;
843 /* If we mounted this BE, unmount it */
844 if (mounted_here) {
845 if ((err = _be_unmount(be_name, 0)) != BE_SUCCESS) {
846 be_print_err(gettext("be_get_legacy_fs: "
847 "failed to unmount %s\n"), be_name);
848 if (ret == BE_SUCCESS)
849 ret = err;
853 ZFS_CLOSE(zhp);
855 free(fld->altroot);
856 fld->altroot = NULL;
858 return (ret);
862 * Function: be_free_fs_list
863 * Description: Function used to free the members of a be_fs_list_data_t
864 * structure.
865 * Parameters:
866 * fld - be_fs_list_data_t pointer to free.
867 * Returns:
868 * None
869 * Scope:
870 * Semi-private (library wide use only)
872 void
873 be_free_fs_list(be_fs_list_data_t *fld)
875 int i;
877 if (fld == NULL)
878 return;
880 free(fld->altroot);
882 if (fld->fs_list == NULL)
883 return;
885 for (i = 0; i < fld->fs_num; i++)
886 free(fld->fs_list[i]);
888 free(fld->fs_list);
892 * Function: be_get_ds_from_dir(char *dir)
893 * Description: Given a directory path, find the underlying dataset mounted
894 * at that directory path if there is one. The returned name
895 * is allocated in heap storage, so the caller is responsible
896 * for freeing it.
897 * Parameters:
898 * dir - char pointer of directory to find.
899 * Returns:
900 * NULL - if directory is not mounted from a dataset.
901 * name of dataset mounted at dir.
902 * Scope:
903 * Semi-private (library wide use only)
905 char *
906 be_get_ds_from_dir(char *dir)
908 dir_data_t dd = { 0 };
909 char resolved_dir[MAXPATHLEN];
911 /* Make sure length of dir is within the max length */
912 if (dir == NULL || strlen(dir) >= MAXPATHLEN)
913 return (NULL);
915 /* Resolve dir in case its lofs mounted */
916 (void) strlcpy(resolved_dir, dir, sizeof (resolved_dir));
918 dd.dir = resolved_dir;
920 (void) zfs_iter_root(g_zfs, be_get_ds_from_dir_callback, &dd);
922 return (dd.ds);
926 * Function: be_make_tmp_mountpoint
927 * Description: This function generates a random temporary mountpoint
928 * and creates that mountpoint directory. It returns the
929 * mountpoint in heap storage, so the caller is responsible
930 * for freeing it.
931 * Parameters:
932 * tmp_mp - reference to pointer of where to store generated
933 * temporary mountpoint.
934 * Returns:
935 * BE_SUCCESS - Success
936 * be_errno_t - Failure
937 * Scope:
938 * Semi-private (library wide use only)
941 be_make_tmp_mountpoint(char **tmp_mp)
943 int err = 0;
945 if ((*tmp_mp = (char *)calloc(1, sizeof (BE_TMP_MNTPNT) + 1)) == NULL) {
946 be_print_err(gettext("be_make_tmp_mountpoint: "
947 "malloc failed\n"));
948 return (BE_ERR_NOMEM);
950 (void) strlcpy(*tmp_mp, BE_TMP_MNTPNT, sizeof (BE_TMP_MNTPNT) + 1);
951 if (mkdtemp(*tmp_mp) == NULL) {
952 err = errno;
953 be_print_err(gettext("be_make_tmp_mountpoint: mkdtemp() failed "
954 "for %s: %s\n"), *tmp_mp, strerror(err));
955 free(*tmp_mp);
956 *tmp_mp = NULL;
957 return (errno_to_be_err(err));
960 return (BE_SUCCESS);
964 * Function: be_mount_pool
965 * Description: This function determines if the pool's datase is mounted
966 * and if not it is used to mount the pool's dataset. The
967 * function returns the current mountpoint if we are able
968 * to mount the dataset.
969 * Parameters:
970 * zhp - handle to the pool's dataset
971 * tmp_mntpnt - The temporary mountpoint that the pool's
972 * dataset is mounted on. This is set only
973 * if the attempt to mount the dataset at it's
974 * set mountpoint fails, and we've used a
975 * temporary mount point for this dataset. It
976 * is expected that the caller will free this
977 * memory.
978 * orig_mntpnt - The original mountpoint for the pool. If a
979 * temporary mount point was needed this will
980 * be used to reset the mountpoint property to
981 * it's original mountpoint. It is expected that
982 * the caller will free this memory.
983 * pool_mounted - This flag indicates that the pool was mounted
984 * in this function.
985 * Returns:
986 * BE_SUCCESS - Success
987 * be_errno_t - Failure
988 * Scope:
989 * Semi-private (library wide use only)
992 be_mount_pool(
993 zfs_handle_t *zhp,
994 char **tmp_mntpnt,
995 char **orig_mntpnt,
996 boolean_t *pool_mounted)
999 char mountpoint[MAXPATHLEN];
1000 int ret = 0;
1002 *tmp_mntpnt = NULL;
1003 *orig_mntpnt = NULL;
1004 *pool_mounted = B_FALSE;
1006 if (!zfs_is_mounted(zhp, NULL)) {
1007 if (zfs_mount(zhp, NULL, 0) != 0) {
1008 if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mountpoint,
1009 sizeof (mountpoint), NULL, NULL, 0, B_FALSE) != 0) {
1010 be_print_err(gettext("be_mount_pool: failed to "
1011 "get mountpoint of (%s): %s\n"),
1012 zfs_get_name(zhp),
1013 libzfs_error_description(g_zfs));
1014 return (zfs_err_to_be_err(g_zfs));
1016 if ((*orig_mntpnt = strdup(mountpoint)) == NULL) {
1017 be_print_err(gettext("be_mount_pool: memory "
1018 "allocation failed\n"));
1019 return (BE_ERR_NOMEM);
1022 * attempt to mount on a temp mountpoint
1024 if ((ret = be_make_tmp_mountpoint(tmp_mntpnt))
1025 != BE_SUCCESS) {
1026 be_print_err(gettext("be_mount_pool: failed "
1027 "to make temporary mountpoint\n"));
1028 free(*orig_mntpnt);
1029 *orig_mntpnt = NULL;
1030 return (ret);
1033 if (zfs_prop_set(zhp,
1034 zfs_prop_to_name(ZFS_PROP_MOUNTPOINT),
1035 *tmp_mntpnt) != 0) {
1036 be_print_err(gettext("be_mount_pool: failed "
1037 "to set mountpoint of pool dataset %s to "
1038 "%s: %s\n"), zfs_get_name(zhp),
1039 *orig_mntpnt,
1040 libzfs_error_description(g_zfs));
1041 free(*tmp_mntpnt);
1042 free(*orig_mntpnt);
1043 *orig_mntpnt = NULL;
1044 *tmp_mntpnt = NULL;
1045 return (zfs_err_to_be_err(g_zfs));
1048 if (zfs_mount(zhp, NULL, 0) != 0) {
1049 be_print_err(gettext("be_mount_pool: failed "
1050 "to mount dataset %s at %s: %s\n"),
1051 zfs_get_name(zhp), *tmp_mntpnt,
1052 libzfs_error_description(g_zfs));
1053 ret = zfs_err_to_be_err(g_zfs);
1054 if (zfs_prop_set(zhp,
1055 zfs_prop_to_name(ZFS_PROP_MOUNTPOINT),
1056 mountpoint) != 0) {
1057 be_print_err(gettext("be_mount_pool: "
1058 "failed to set mountpoint of pool "
1059 "dataset %s to %s: %s\n"),
1060 zfs_get_name(zhp), *tmp_mntpnt,
1061 libzfs_error_description(g_zfs));
1063 free(*tmp_mntpnt);
1064 free(*orig_mntpnt);
1065 *orig_mntpnt = NULL;
1066 *tmp_mntpnt = NULL;
1067 return (ret);
1070 *pool_mounted = B_TRUE;
1073 return (BE_SUCCESS);
1077 * Function: be_unmount_pool
1078 * Description: This function is used to unmount the pool's dataset if we
1079 * mounted it previously using be_mount_pool().
1080 * Parameters:
1081 * zhp - handle to the pool's dataset
1082 * tmp_mntpnt - If a temprary mount point was used this will
1083 * be set. Since this was created in be_mount_pool
1084 * we will need to clean it up here.
1085 * orig_mntpnt - The original mountpoint for the pool. This is
1086 * used to set the dataset mountpoint property
1087 * back to it's original value in the case where a
1088 * temporary mountpoint was used.
1089 * Returns:
1090 * BE_SUCCESS - Success
1091 * be_errno_t - Failure
1092 * Scope:
1093 * Semi-private (library wide use only)
1096 be_unmount_pool(
1097 zfs_handle_t *zhp,
1098 char *tmp_mntpnt,
1099 char *orig_mntpnt)
1101 if (zfs_unmount(zhp, NULL, 0) != 0) {
1102 be_print_err(gettext("be_unmount_pool: failed to "
1103 "unmount pool (%s): %s\n"), zfs_get_name(zhp),
1104 libzfs_error_description(g_zfs));
1105 return (zfs_err_to_be_err(g_zfs));
1107 if (orig_mntpnt != NULL) {
1108 if (tmp_mntpnt != NULL &&
1109 strcmp(orig_mntpnt, tmp_mntpnt) != 0) {
1110 (void) rmdir(tmp_mntpnt);
1112 if (zfs_prop_set(zhp,
1113 zfs_prop_to_name(ZFS_PROP_MOUNTPOINT),
1114 orig_mntpnt) != 0) {
1115 be_print_err(gettext("be_unmount_pool: failed "
1116 "to set the mountpoint for dataset (%s) to "
1117 "%s: %s\n"), zfs_get_name(zhp), orig_mntpnt,
1118 libzfs_error_description(g_zfs));
1119 return (zfs_err_to_be_err(g_zfs));
1123 return (BE_SUCCESS);
1126 /* ******************************************************************** */
1127 /* Private Functions */
1128 /* ******************************************************************** */
1131 * Function: be_mount_callback
1132 * Description: Callback function used to iterate through all of a BE's
1133 * subordinate file systems and to mount them accordingly.
1134 * Parameters:
1135 * zhp - zfs_handle_t pointer to current file system being
1136 * processed.
1137 * data - pointer to the altroot of where to mount BE.
1138 * Returns:
1139 * 0 - Success
1140 * be_errno_t - Failure
1141 * Scope:
1142 * Private
1144 static int
1145 be_mount_callback(zfs_handle_t *zhp, void *data)
1147 zprop_source_t sourcetype;
1148 const char *fs_name = zfs_get_name(zhp);
1149 char source[ZFS_MAX_DATASET_NAME_LEN];
1150 char *altroot = data;
1151 char zhp_mountpoint[MAXPATHLEN];
1152 char mountpoint[MAXPATHLEN];
1153 int ret = 0;
1155 /* Get dataset's mountpoint and source values */
1156 if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, zhp_mountpoint,
1157 sizeof (zhp_mountpoint), &sourcetype, source, sizeof (source),
1158 B_FALSE) != 0) {
1159 be_print_err(gettext("be_mount_callback: failed to "
1160 "get mountpoint and sourcetype for %s\n"),
1161 fs_name);
1162 ZFS_CLOSE(zhp);
1163 return (BE_ERR_ZFS);
1167 * Set this filesystem's 'canmount' property to 'noauto' just incase
1168 * it's been set 'on'.
1170 if (zfs_prop_set(zhp, zfs_prop_to_name(ZFS_PROP_CANMOUNT), "noauto")) {
1171 be_print_err(gettext("be_mount_callback: failed to "
1172 "set canmount to 'noauto' (%s)\n"), fs_name);
1173 ZFS_CLOSE(zhp);
1174 return (BE_ERR_ZFS);
1178 * If the mountpoint is none, there's nothing to do, goto next.
1179 * If the mountpoint is legacy, legacy mount it with mount(2).
1180 * If the mountpoint is inherited, its mountpoint should
1181 * already be set. If it's not, then explicitly fix-up
1182 * the mountpoint now by appending its explicitly set
1183 * mountpoint value to the BE mountpoint.
1185 if (strcmp(zhp_mountpoint, ZFS_MOUNTPOINT_NONE) == 0) {
1186 goto next;
1187 } else if (strcmp(zhp_mountpoint, ZFS_MOUNTPOINT_LEGACY) == 0) {
1189 * If the mountpoint is set to 'legacy', we need to
1190 * dig into this BE's vfstab to figure out where to
1191 * mount it, and just mount it via mount(2).
1193 if (get_mountpoint_from_vfstab(altroot, fs_name,
1194 mountpoint, sizeof (mountpoint), B_TRUE) == BE_SUCCESS) {
1196 /* Legacy mount the file system */
1197 if (mount(fs_name, mountpoint, MS_DATA,
1198 MNTTYPE_ZFS, NULL, 0, NULL, 0) != 0) {
1199 be_print_err(
1200 gettext("be_mount_callback: "
1201 "failed to mount %s on %s\n"),
1202 fs_name, mountpoint);
1204 } else {
1205 be_print_err(
1206 gettext("be_mount_callback: "
1207 "no entry for %s in vfstab, "
1208 "skipping ...\n"), fs_name);
1211 goto next;
1213 } else if ((sourcetype & (ZPROP_SRC_INHERITED|ZPROP_SRC_LOCAL)) == 0) {
1215 * Skip dataset if mountpoint is not inherited
1216 * or explicitly set.
1218 be_print_err(gettext("be_mount_callback: "
1219 "mountpoint sourcetype of %s is %d, skipping ...\n"),
1220 fs_name, sourcetype);
1222 goto next;
1225 /* Mount this filesystem */
1226 if (mount_zfs(zhp, altroot) != 0) {
1227 be_print_err(gettext("be_mount_callback: failed to "
1228 "mount dataset %s at %s: %s\n"), fs_name, mountpoint,
1229 strerror(errno));
1230 ZFS_CLOSE(zhp);
1231 return (BE_ERR_MOUNT);
1234 next:
1235 /* Iterate through this dataset's children and mount them */
1236 if ((ret = zfs_iter_filesystems(zhp, be_mount_callback,
1237 altroot)) != 0) {
1238 ZFS_CLOSE(zhp);
1239 return (ret);
1243 ZFS_CLOSE(zhp);
1244 return (0);
1248 * Function: be_unmount_callback
1249 * Description: Callback function used to iterate through all of a BE's
1250 * subordinate file systems and to unmount them.
1251 * Parameters:
1252 * zhp - zfs_handle_t pointer to current file system being
1253 * processed.
1254 * data - pointer to the mountpoint of where BE is mounted.
1255 * Returns:
1256 * 0 - Success
1257 * be_errno_t - Failure
1258 * Scope:
1259 * Private
1261 static int
1262 be_unmount_callback(zfs_handle_t *zhp, void *data)
1264 be_unmount_data_t *ud = data;
1265 zprop_source_t sourcetype;
1266 const char *fs_name = zfs_get_name(zhp);
1267 char source[ZFS_MAX_DATASET_NAME_LEN];
1268 char mountpoint[MAXPATHLEN];
1269 char *zhp_mountpoint;
1270 int ret = 0;
1272 /* Iterate down this dataset's children first */
1273 if (zfs_iter_filesystems(zhp, be_unmount_callback, ud)) {
1274 ret = BE_ERR_UMOUNT;
1275 goto done;
1278 /* Is dataset even mounted ? */
1279 if (!zfs_is_mounted(zhp, NULL))
1280 goto done;
1282 /* Unmount this file system */
1283 if (zfs_unmount(zhp, NULL, ud->force ? MS_FORCE : 0) != 0) {
1284 be_print_err(gettext("be_unmount_callback: "
1285 "failed to unmount %s: %s\n"), fs_name,
1286 libzfs_error_description(g_zfs));
1287 ret = zfs_err_to_be_err(g_zfs);
1288 goto done;
1291 /* Get dataset's current mountpoint and source value */
1292 if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mountpoint,
1293 sizeof (mountpoint), &sourcetype, source, sizeof (source),
1294 B_FALSE) != 0) {
1295 be_print_err(gettext("be_unmount_callback: "
1296 "failed to get mountpoint and sourcetype for %s: %s\n"),
1297 fs_name, libzfs_error_description(g_zfs));
1298 ret = zfs_err_to_be_err(g_zfs);
1299 goto done;
1302 if (sourcetype & ZPROP_SRC_INHERITED) {
1304 * If the mountpoint is inherited we don't need to
1305 * do anything. When its parent gets processed
1306 * its mountpoint will be set accordingly.
1308 goto done;
1309 } else if (sourcetype & ZPROP_SRC_LOCAL) {
1311 if (strcmp(mountpoint, ZFS_MOUNTPOINT_LEGACY) == 0) {
1313 * If the mountpoint is set to 'legacy', its already
1314 * been unmounted (from above call to zfs_unmount), and
1315 * we don't need to do anything else with it.
1317 goto done;
1319 } else {
1321 * Else process dataset with explicitly set mountpoint.
1325 * Get this dataset's mountpoint relative to
1326 * the BE's mountpoint.
1328 if ((strncmp(mountpoint, ud->altroot,
1329 strlen(ud->altroot)) == 0) &&
1330 (mountpoint[strlen(ud->altroot)] == '/')) {
1332 zhp_mountpoint = mountpoint +
1333 strlen(ud->altroot);
1335 /* Set this dataset's mountpoint value */
1336 if (zfs_prop_set(zhp,
1337 zfs_prop_to_name(ZFS_PROP_MOUNTPOINT),
1338 zhp_mountpoint)) {
1339 be_print_err(
1340 gettext("be_unmount_callback: "
1341 "failed to set mountpoint for "
1342 "%s to %s: %s\n"), fs_name,
1343 zhp_mountpoint,
1344 libzfs_error_description(g_zfs));
1345 ret = zfs_err_to_be_err(g_zfs);
1347 } else {
1349 * Nothing to do, mountpoint shouldn't be
1350 * corrected.
1352 goto done;
1355 } else {
1356 be_print_err(gettext("be_unmount_callback: "
1357 "mountpoint sourcetype of %s is %d, skipping ...\n"),
1358 fs_name, sourcetype);
1359 ret = BE_ERR_ZFS;
1362 done:
1363 /* Set this filesystem's 'canmount' property to 'noauto' */
1364 if (zfs_prop_set(zhp, zfs_prop_to_name(ZFS_PROP_CANMOUNT), "noauto")) {
1365 be_print_err(gettext("be_unmount_callback: "
1366 "failed to set canmount to 'noauto' (%s)\n"), fs_name);
1367 if (ret == 0)
1368 ret = BE_ERR_ZFS;
1371 ZFS_CLOSE(zhp);
1372 return (ret);
1376 * Function: be_get_legacy_fs_callback
1377 * Description: The callback function is used to iterate through all
1378 * non-shared file systems of a BE, finding ones that have
1379 * a legacy mountpoint and an entry in the BE's vfstab.
1380 * It adds these file systems to the callback data.
1381 * Parameters:
1382 * zhp - zfs_handle_t pointer to current file system being
1383 * processed.
1384 * data - be_fs_list_data_t pointer
1385 * Returns:
1386 * 0 - Success
1387 * be_errno_t - Failure
1388 * Scope:
1389 * Private
1391 static int
1392 be_get_legacy_fs_callback(zfs_handle_t *zhp, void *data)
1394 be_fs_list_data_t *fld = data;
1395 const char *fs_name = zfs_get_name(zhp);
1396 char zhp_mountpoint[MAXPATHLEN];
1397 char mountpoint[MAXPATHLEN];
1398 int ret = 0;
1400 /* Get this dataset's mountpoint property */
1401 if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, zhp_mountpoint,
1402 sizeof (zhp_mountpoint), NULL, NULL, 0, B_FALSE) != 0) {
1403 be_print_err(gettext("be_get_legacy_fs_callback: "
1404 "failed to get mountpoint for %s: %s\n"),
1405 fs_name, libzfs_error_description(g_zfs));
1406 ret = zfs_err_to_be_err(g_zfs);
1407 ZFS_CLOSE(zhp);
1408 return (ret);
1412 * If mountpoint is legacy, try to get its mountpoint from this BE's
1413 * vfstab. If it exists in the vfstab, add this file system to the
1414 * callback data.
1416 if (strcmp(zhp_mountpoint, ZFS_MOUNTPOINT_LEGACY) == 0) {
1417 if (get_mountpoint_from_vfstab(fld->altroot, fs_name,
1418 mountpoint, sizeof (mountpoint), B_FALSE) != BE_SUCCESS) {
1419 be_print_err(gettext("be_get_legacy_fs_callback: "
1420 "no entry for %s in vfstab, "
1421 "skipping ...\n"), fs_name);
1423 goto next;
1426 /* Record file system into the callback data. */
1427 if (add_to_fs_list(fld, zfs_get_name(zhp)) != BE_SUCCESS) {
1428 be_print_err(gettext("be_get_legacy_fs_callback: "
1429 "failed to add %s to fs list\n"), mountpoint);
1430 ZFS_CLOSE(zhp);
1431 return (BE_ERR_NOMEM);
1435 next:
1436 /* Iterate through this dataset's children file systems */
1437 if ((ret = zfs_iter_filesystems(zhp, be_get_legacy_fs_callback,
1438 fld)) != 0) {
1439 ZFS_CLOSE(zhp);
1440 return (ret);
1442 ZFS_CLOSE(zhp);
1443 return (0);
1447 * Function: add_to_fs_list
1448 * Description: Function used to add a file system to the fs_list array in
1449 * a be_fs_list_data_t structure.
1450 * Parameters:
1451 * fld - be_fs_list_data_t pointer
1452 * fs - file system to add
1453 * Returns:
1454 * BE_SUCCESS - Success
1455 * 1 - Failure
1456 * Scope:
1457 * Private
1459 static int
1460 add_to_fs_list(be_fs_list_data_t *fld, const char *fs)
1462 if (fld == NULL || fs == NULL)
1463 return (1);
1465 if ((fld->fs_list = reallocarray(fld->fs_list, fld->fs_num + 1,
1466 sizeof (char *))) == NULL) {
1467 be_print_err(gettext("add_to_fs_list: "
1468 "memory allocation failed\n"));
1469 return (1);
1472 if ((fld->fs_list[fld->fs_num++] = strdup(fs)) == NULL) {
1473 be_print_err(gettext("add_to_fs_list: "
1474 "memory allocation failed\n"));
1475 return (1);
1478 return (BE_SUCCESS);
1482 * Function: zpool_shared_fs_callback
1483 * Description: Callback function used to iterate through all existing pools
1484 * to find and mount all shared filesystems. This function
1485 * processes the pool's "pool data" dataset, then uses
1486 * iter_shared_fs_callback to iterate through the pool's
1487 * datasets.
1488 * Parameters:
1489 * zlp - zpool_handle_t pointer to the current pool being
1490 * looked at.
1491 * data - be_mount_data_t pointer
1492 * Returns:
1493 * 0 - Success
1494 * be_errno_t - Failure
1495 * Scope:
1496 * Private
1498 static int
1499 zpool_shared_fs_callback(zpool_handle_t *zlp, void *data)
1501 be_mount_data_t *md = data;
1502 zfs_handle_t *zhp = NULL;
1503 const char *zpool = zpool_get_name(zlp);
1504 int ret = 0;
1507 * Get handle to pool's "pool data" dataset
1509 if ((zhp = zfs_open(g_zfs, zpool, ZFS_TYPE_FILESYSTEM)) == NULL) {
1510 be_print_err(gettext("zpool_shared_fs: "
1511 "failed to open pool dataset %s: %s\n"), zpool,
1512 libzfs_error_description(g_zfs));
1513 ret = zfs_err_to_be_err(g_zfs);
1514 zpool_close(zlp);
1515 return (ret);
1518 /* Process this pool's "pool data" dataset */
1519 (void) loopback_mount_shared_fs(zhp, md);
1521 /* Interate through this pool's children */
1522 (void) zfs_iter_filesystems(zhp, iter_shared_fs_callback, md);
1524 ZFS_CLOSE(zhp);
1525 zpool_close(zlp);
1527 return (0);
1531 * Function: iter_shared_fs_callback
1532 * Description: Callback function used to iterate through a pool's datasets
1533 * to find and mount all shared filesystems. It makes sure to
1534 * find the BE container dataset of the pool, if it exists, and
1535 * does not process and iterate down that path.
1537 * Note - This function iterates linearly down the
1538 * hierarchical dataset paths and mounts things as it goes
1539 * along. It does not make sure that something deeper down
1540 * a dataset path has an interim mountpoint for something
1541 * processed earlier.
1543 * Parameters:
1544 * zhp - zfs_handle_t pointer to the current dataset being
1545 * processed.
1546 * data - be_mount_data_t pointer
1547 * Returns:
1548 * 0 - Success
1549 * be_errno_t - Failure
1550 * Scope:
1551 * Private
1553 static int
1554 iter_shared_fs_callback(zfs_handle_t *zhp, void *data)
1556 be_mount_data_t *md = data;
1557 const char *name = zfs_get_name(zhp);
1558 char container_ds[MAXPATHLEN];
1559 char tmp_name[MAXPATHLEN];
1560 char *pool;
1562 /* Get the pool's name */
1563 (void) strlcpy(tmp_name, name, sizeof (tmp_name));
1564 pool = strtok(tmp_name, "/");
1566 if (pool) {
1567 /* Get the name of this pool's container dataset */
1568 be_make_container_ds(pool, container_ds,
1569 sizeof (container_ds));
1572 * If what we're processing is this pool's BE container
1573 * dataset, skip it.
1575 if (strcmp(name, container_ds) == 0) {
1576 ZFS_CLOSE(zhp);
1577 return (0);
1579 } else {
1580 /* Getting the pool name failed, return error */
1581 be_print_err(gettext("iter_shared_fs_callback: "
1582 "failed to get pool name from %s\n"), name);
1583 ZFS_CLOSE(zhp);
1584 return (BE_ERR_POOL_NOENT);
1587 /* Mount this shared filesystem */
1588 (void) loopback_mount_shared_fs(zhp, md);
1590 /* Iterate this dataset's children file systems */
1591 (void) zfs_iter_filesystems(zhp, iter_shared_fs_callback, md);
1592 ZFS_CLOSE(zhp);
1594 return (0);
1598 * Function: loopback_mount_shared_fs
1599 * Description: This function loopback mounts a file system into the altroot
1600 * area of the BE being mounted. Since these are shared file
1601 * systems, they are expected to be already mounted for the
1602 * current BE, and this function just loopback mounts them into
1603 * the BE mountpoint. If they are not mounted for the current
1604 * live system, they are skipped and not mounted into the BE
1605 * we're mounting.
1606 * Parameters:
1607 * zhp - zfs_handle_t pointer to the dataset to loopback mount
1608 * md - be_mount_data_t pointer
1609 * Returns:
1610 * BE_SUCCESS - Success
1611 * be_errno_t - Failure
1612 * Scope:
1613 * Private
1615 static int
1616 loopback_mount_shared_fs(zfs_handle_t *zhp, be_mount_data_t *md)
1618 char zhp_mountpoint[MAXPATHLEN];
1619 char mountpoint[MAXPATHLEN];
1620 char *mp = NULL;
1621 char optstr[MAX_MNTOPT_STR];
1622 int mflag = MS_OPTIONSTR;
1623 int err;
1626 * Check if file system is currently mounted and not delegated
1627 * to a non-global zone (if we're in the global zone)
1629 if (zfs_is_mounted(zhp, &mp) && (getzoneid() != GLOBAL_ZONEID ||
1630 !zfs_prop_get_int(zhp, ZFS_PROP_ZONED))) {
1632 * If we didn't get a mountpoint from the zfs_is_mounted call,
1633 * get it from the mountpoint property.
1635 if (mp == NULL) {
1636 if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT,
1637 zhp_mountpoint, sizeof (zhp_mountpoint), NULL,
1638 NULL, 0, B_FALSE) != 0) {
1639 be_print_err(
1640 gettext("loopback_mount_shared_fs: "
1641 "failed to get mountpoint property\n"));
1642 return (BE_ERR_ZFS);
1644 } else {
1645 (void) strlcpy(zhp_mountpoint, mp,
1646 sizeof (zhp_mountpoint));
1647 free(mp);
1650 (void) snprintf(mountpoint, sizeof (mountpoint), "%s%s",
1651 md->altroot, zhp_mountpoint);
1653 /* Mount it read-only if read-write was not requested */
1654 if (!md->shared_rw) {
1655 mflag |= MS_RDONLY;
1658 /* Add the "nosub" option to the mount options string */
1659 (void) strlcpy(optstr, MNTOPT_NOSUB, sizeof (optstr));
1661 /* Loopback mount this dataset at the altroot */
1662 if (mount(zhp_mountpoint, mountpoint, mflag, MNTTYPE_LOFS,
1663 NULL, 0, optstr, sizeof (optstr)) != 0) {
1664 err = errno;
1665 be_print_err(gettext("loopback_mount_shared_fs: "
1666 "failed to loopback mount %s at %s: %s\n"),
1667 zhp_mountpoint, mountpoint, strerror(err));
1668 return (BE_ERR_MOUNT);
1672 return (BE_SUCCESS);
1676 * Function: loopback_mount_zonepath
1677 * Description: This function loopback mounts a zonepath into the altroot
1678 * area of the BE being mounted.
1679 * Parameters:
1680 * zonepath - pointer to zone path in the current BE
1681 * md - be_mount_data_t pointer
1682 * Returns:
1683 * BE_SUCCESS - Success
1684 * be_errno_t - Failure
1685 * Scope:
1686 * Private
1688 static int
1689 loopback_mount_zonepath(const char *zonepath, be_mount_data_t *md)
1691 FILE *fp = NULL;
1692 struct stat st;
1693 char *p;
1694 char *p1;
1695 char *parent_dir;
1696 struct extmnttab extmtab;
1697 dev_t dev = NODEV;
1698 char *parentmnt;
1699 char alt_parentmnt[MAXPATHLEN];
1700 struct mnttab mntref;
1701 char altzonepath[MAXPATHLEN];
1702 char optstr[MAX_MNTOPT_STR];
1703 int mflag = MS_OPTIONSTR;
1704 int ret;
1705 int err;
1707 fp = fopen(MNTTAB, "r");
1708 if (fp == NULL) {
1709 err = errno;
1710 be_print_err(gettext("loopback_mount_zonepath: "
1711 "failed to open /etc/mnttab\n"));
1712 return (errno_to_be_err(err));
1716 * before attempting the loopback mount of zonepath under altroot,
1717 * we need to make sure that all intermediate file systems in the
1718 * zone path are also mounted under altroot
1721 /* get the parent directory for zonepath */
1722 p = strrchr(zonepath, '/');
1723 if (p != NULL && p != zonepath) {
1724 if ((parent_dir = (char *)calloc(sizeof (char),
1725 p - zonepath + 1)) == NULL) {
1726 ret = BE_ERR_NOMEM;
1727 goto done;
1729 (void) strlcpy(parent_dir, zonepath, p - zonepath + 1);
1730 if (stat(parent_dir, &st) < 0) {
1731 ret = errno_to_be_err(errno);
1732 be_print_err(gettext("loopback_mount_zonepath: "
1733 "failed to stat %s"),
1734 parent_dir);
1735 free(parent_dir);
1736 goto done;
1738 free(parent_dir);
1741 * After the above stat call, st.st_dev contains ID of the
1742 * device over which parent dir resides.
1743 * Now, search mnttab and find mount point of parent dir device.
1746 resetmnttab(fp);
1747 while (getextmntent(fp, &extmtab, sizeof (extmtab)) == 0) {
1748 dev = makedev(extmtab.mnt_major, extmtab.mnt_minor);
1749 if (st.st_dev == dev && strcmp(extmtab.mnt_fstype,
1750 MNTTYPE_ZFS) == 0) {
1751 p1 = strchr(extmtab.mnt_special, '/');
1752 if (p1 == NULL || strncmp(p1 + 1,
1753 BE_CONTAINER_DS_NAME, 4) != 0 ||
1754 (*(p1 + 5) != '/' && *(p1 + 5) != '\0')) {
1756 * if parent dir is in a shared file
1757 * system, check whether it is already
1758 * loopback mounted under altroot or
1759 * not. It would have been mounted
1760 * already under altroot if it is in
1761 * a non-shared filesystem.
1763 parentmnt = strdup(extmtab.mnt_mountp);
1764 (void) snprintf(alt_parentmnt,
1765 sizeof (alt_parentmnt), "%s%s",
1766 md->altroot, parentmnt);
1767 mntref.mnt_mountp = alt_parentmnt;
1768 mntref.mnt_special = parentmnt;
1769 mntref.mnt_fstype = MNTTYPE_LOFS;
1770 mntref.mnt_mntopts = NULL;
1771 mntref.mnt_time = NULL;
1772 resetmnttab(fp);
1773 if (getmntany(fp, (struct mnttab *)
1774 &extmtab, &mntref) != 0) {
1775 ret = loopback_mount_zonepath(
1776 parentmnt, md);
1777 if (ret != BE_SUCCESS) {
1778 free(parentmnt);
1779 goto done;
1782 free(parentmnt);
1784 break;
1790 if (!md->shared_rw) {
1791 mflag |= MS_RDONLY;
1794 (void) snprintf(altzonepath, sizeof (altzonepath), "%s%s",
1795 md->altroot, zonepath);
1797 /* Add the "nosub" option to the mount options string */
1798 (void) strlcpy(optstr, MNTOPT_NOSUB, sizeof (optstr));
1800 /* Loopback mount this dataset at the altroot */
1801 if (mount(zonepath, altzonepath, mflag, MNTTYPE_LOFS,
1802 NULL, 0, optstr, sizeof (optstr)) != 0) {
1803 err = errno;
1804 be_print_err(gettext("loopback_mount_zonepath: "
1805 "failed to loopback mount %s at %s: %s\n"),
1806 zonepath, altzonepath, strerror(err));
1807 ret = BE_ERR_MOUNT;
1808 goto done;
1810 ret = BE_SUCCESS;
1812 done :
1813 (void) fclose(fp);
1814 return (ret);
1818 * Function: unmount_shared_fs
1819 * Description: This function iterates through the mnttab and finds all
1820 * loopback mount entries that reside within the altroot of
1821 * where the BE is mounted, and unmounts it.
1822 * Parameters:
1823 * ud - be_unmount_data_t pointer
1824 * Returns:
1825 * BE_SUCCESS - Success
1826 * be_errno_t - Failure
1827 * Scope:
1828 * Private
1830 static int
1831 unmount_shared_fs(be_unmount_data_t *ud)
1833 FILE *fp = NULL;
1834 struct mnttab *table = NULL;
1835 struct mnttab ent;
1836 struct mnttab *entp = NULL;
1837 size_t size = 0;
1838 int read_chunk = 32;
1839 int i;
1840 int altroot_len;
1841 int err = 0;
1843 errno = 0;
1845 /* Read in the mnttab into a table */
1846 if ((fp = fopen(MNTTAB, "r")) == NULL) {
1847 err = errno;
1848 be_print_err(gettext("unmount_shared_fs: "
1849 "failed to open mnttab\n"));
1850 return (errno_to_be_err(err));
1853 while (getmntent(fp, &ent) == 0) {
1854 if (size % read_chunk == 0) {
1855 table = reallocarray(table, size + read_chunk,
1856 sizeof (ent));
1858 entp = &table[size++];
1861 * Copy over the current mnttab entry into our table,
1862 * copying only the fields that we care about.
1864 (void) memset(entp, 0, sizeof (*entp));
1865 if ((entp->mnt_mountp = strdup(ent.mnt_mountp)) == NULL ||
1866 (entp->mnt_fstype = strdup(ent.mnt_fstype)) == NULL) {
1867 be_print_err(gettext("unmount_shared_fs: "
1868 "memory allocation failed\n"));
1869 return (BE_ERR_NOMEM);
1872 (void) fclose(fp);
1875 * Process the mnttab entries in reverse order, looking for
1876 * loopback mount entries mounted under our altroot.
1878 altroot_len = strlen(ud->altroot);
1879 for (i = size; i > 0; i--) {
1880 entp = &table[i - 1];
1882 /* If not of type lofs, skip */
1883 if (strcmp(entp->mnt_fstype, MNTTYPE_LOFS) != 0)
1884 continue;
1886 /* If inside the altroot, unmount it */
1887 if (strncmp(entp->mnt_mountp, ud->altroot, altroot_len) == 0 &&
1888 entp->mnt_mountp[altroot_len] == '/') {
1889 if (umount(entp->mnt_mountp) != 0) {
1890 err = errno;
1891 if (err == EBUSY) {
1892 (void) sleep(1);
1893 err = errno = 0;
1894 if (umount(entp->mnt_mountp) != 0)
1895 err = errno;
1897 if (err != 0) {
1898 be_print_err(gettext(
1899 "unmount_shared_fs: "
1900 "failed to unmount shared file "
1901 "system %s: %s\n"),
1902 entp->mnt_mountp, strerror(err));
1903 return (errno_to_be_err(err));
1909 return (BE_SUCCESS);
1913 * Function: get_mountpoint_from_vfstab
1914 * Description: This function digs into the vfstab in the given altroot,
1915 * and searches for an entry for the fs passed in. If found,
1916 * it returns the mountpoint of that fs in the mountpoint
1917 * buffer passed in. If the get_alt_mountpoint flag is set,
1918 * it returns the mountpoint with the altroot prepended.
1919 * Parameters:
1920 * altroot - pointer to the alternate root location
1921 * fs - pointer to the file system name to look for in the
1922 * vfstab in altroot
1923 * mountpoint - pointer to buffer of where the mountpoint of
1924 * fs will be returned.
1925 * size_mp - size of mountpoint argument
1926 * get_alt_mountpoint - flag to indicate whether or not the
1927 * mountpoint should be populated with the altroot
1928 * prepended.
1929 * Returns:
1930 * BE_SUCCESS - Success
1931 * 1 - Failure
1932 * Scope:
1933 * Private
1935 static int
1936 get_mountpoint_from_vfstab(char *altroot, const char *fs, char *mountpoint,
1937 size_t size_mp, boolean_t get_alt_mountpoint)
1939 struct vfstab vp;
1940 FILE *fp = NULL;
1941 char alt_vfstab[MAXPATHLEN];
1943 /* Generate path to alternate root vfstab */
1944 (void) snprintf(alt_vfstab, sizeof (alt_vfstab), "%s/etc/vfstab",
1945 altroot);
1947 /* Open alternate root vfstab */
1948 if ((fp = fopen(alt_vfstab, "r")) == NULL) {
1949 be_print_err(gettext("get_mountpoint_from_vfstab: "
1950 "failed to open vfstab (%s)\n"), alt_vfstab);
1951 return (1);
1954 if (getvfsspec(fp, &vp, (char *)fs) == 0) {
1956 * Found entry for fs, grab its mountpoint.
1957 * If the flag to prepend the altroot into the mountpoint
1958 * is set, prepend it. Otherwise, just return the mountpoint.
1960 if (get_alt_mountpoint) {
1961 (void) snprintf(mountpoint, size_mp, "%s%s", altroot,
1962 vp.vfs_mountp);
1963 } else {
1964 (void) strlcpy(mountpoint, vp.vfs_mountp, size_mp);
1966 } else {
1967 (void) fclose(fp);
1968 return (1);
1971 (void) fclose(fp);
1973 return (BE_SUCCESS);
1977 * Function: fix_mountpoint_callback
1978 * Description: This callback function is used to iterate through a BE's
1979 * children filesystems to check if its mountpoint is currently
1980 * set to be mounted at some specified altroot. If so, fix it by
1981 * removing altroot from the beginning of its mountpoint.
1983 * Note - There's no way to tell if a child filesystem's
1984 * mountpoint isn't broken, and just happens to begin with
1985 * the altroot we're looking for. In this case, this function
1986 * will errantly remove the altroot portion from the beginning
1987 * of this filesystem's mountpoint.
1989 * Parameters:
1990 * zhp - zfs_handle_t pointer to filesystem being processed.
1991 * data - altroot of where BE is to be mounted.
1992 * Returns:
1993 * 0 - Success
1994 * be_errno_t - Failure
1995 * Scope:
1996 * Private
1998 static int
1999 fix_mountpoint_callback(zfs_handle_t *zhp, void *data)
2001 zprop_source_t sourcetype;
2002 char source[ZFS_MAX_DATASET_NAME_LEN];
2003 char mountpoint[MAXPATHLEN];
2004 char *zhp_mountpoint = NULL;
2005 char *altroot = data;
2006 int ret = 0;
2008 /* Get dataset's mountpoint and source values */
2009 if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mountpoint,
2010 sizeof (mountpoint), &sourcetype, source, sizeof (source),
2011 B_FALSE) != 0) {
2012 be_print_err(gettext("fix_mountpoint_callback: "
2013 "failed to get mountpoint and sourcetype for %s\n"),
2014 zfs_get_name(zhp));
2015 ZFS_CLOSE(zhp);
2016 return (BE_ERR_ZFS);
2020 * If the mountpoint is not inherited and the mountpoint is not
2021 * 'legacy', this file system potentially needs its mountpoint
2022 * fixed.
2024 if (!(sourcetype & ZPROP_SRC_INHERITED) &&
2025 strcmp(mountpoint, ZFS_MOUNTPOINT_LEGACY) != 0) {
2028 * Check if this file system's current mountpoint is
2029 * under the altroot we're fixing it against.
2031 if (strncmp(mountpoint, altroot, strlen(altroot)) == 0 &&
2032 mountpoint[strlen(altroot)] == '/') {
2035 * Get this dataset's mountpoint relative to the
2036 * altroot.
2038 zhp_mountpoint = mountpoint + strlen(altroot);
2040 /* Fix this dataset's mountpoint value */
2041 if (zfs_prop_set(zhp,
2042 zfs_prop_to_name(ZFS_PROP_MOUNTPOINT),
2043 zhp_mountpoint)) {
2044 be_print_err(gettext("fix_mountpoint_callback: "
2045 "failed to set mountpoint for %s to "
2046 "%s: %s\n"), zfs_get_name(zhp),
2047 zhp_mountpoint,
2048 libzfs_error_description(g_zfs));
2049 ret = zfs_err_to_be_err(g_zfs);
2050 ZFS_CLOSE(zhp);
2051 return (ret);
2056 /* Iterate through this dataset's children and fix them */
2057 if ((ret = zfs_iter_filesystems(zhp, fix_mountpoint_callback,
2058 altroot)) != 0) {
2059 ZFS_CLOSE(zhp);
2060 return (ret);
2064 ZFS_CLOSE(zhp);
2065 return (0);
2069 * Function: be_mount_root
2070 * Description: This function mounts the root dataset of a BE at the
2071 * specified altroot.
2072 * Parameters:
2073 * zhp - zfs_handle_t pointer to root dataset of a BE that is
2074 * to be mounted at altroot.
2075 * altroot - location of where to mount the BE root.
2076 * Return:
2077 * BE_SUCCESS - Success
2078 * be_errno_t - Failure
2079 * Scope:
2080 * Private
2082 static int
2083 be_mount_root(zfs_handle_t *zhp, char *altroot)
2085 char mountpoint[MAXPATHLEN];
2087 /* Get mountpoint property of dataset */
2088 if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mountpoint,
2089 sizeof (mountpoint), NULL, NULL, 0, B_FALSE) != 0) {
2090 be_print_err(gettext("be_mount_root: failed to "
2091 "get mountpoint property for %s: %s\n"), zfs_get_name(zhp),
2092 libzfs_error_description(g_zfs));
2093 return (zfs_err_to_be_err(g_zfs));
2097 * Set the canmount property for the BE's root dataset to 'noauto' just
2098 * in case it's been set to 'on'.
2100 if (zfs_prop_set(zhp, zfs_prop_to_name(ZFS_PROP_CANMOUNT), "noauto")
2101 != 0) {
2102 be_print_err(gettext("be_mount_root: failed to "
2103 "set canmount property to 'noauto' (%s): %s\n"),
2104 zfs_get_name(zhp), libzfs_error_description(g_zfs));
2105 return (zfs_err_to_be_err(g_zfs));
2108 /* Mount the BE's root filesystem */
2109 if (mount_zfs(zhp, altroot) != 0) {
2110 be_print_err(gettext("be_mount_root: failed to "
2111 "mount dataset %s at %s: %s\n"), zfs_get_name(zhp),
2112 altroot, strerror(errno));
2113 return (BE_ERR_ZFS);
2116 return (BE_SUCCESS);
2120 * Function: be_unmount_root
2121 * Description: This function unmounts the root dataset of a BE, but before
2122 * unmounting, it looks at the BE's vfstab to determine
2123 * if the root dataset mountpoint should be left as 'legacy'
2124 * or '/'. If the vfstab contains an entry for this root
2125 * dataset with a mountpoint of '/', it sets the mountpoint
2126 * property to 'legacy'.
2128 * Parameters:
2129 * zhp - zfs_handle_t pointer of the BE root dataset that
2130 * is currently mounted.
2131 * ud - be_unmount_data_t pointer providing unmount data
2132 * for the given BE root dataset.
2133 * Returns:
2134 * BE_SUCCESS - Success
2135 * be_errno_t - Failure
2136 * Scope:
2137 * Private
2139 static int
2140 be_unmount_root(zfs_handle_t *zhp, be_unmount_data_t *ud)
2142 char mountpoint[MAXPATHLEN];
2143 boolean_t is_legacy = B_FALSE;
2145 /* See if this is a legacy mounted root */
2146 if (get_mountpoint_from_vfstab(ud->altroot, zfs_get_name(zhp),
2147 mountpoint, sizeof (mountpoint), B_FALSE) == BE_SUCCESS &&
2148 strcmp(mountpoint, "/") == 0) {
2149 is_legacy = B_TRUE;
2152 /* Unmount the dataset */
2153 if (zfs_unmount(zhp, NULL, ud->force ? MS_FORCE : 0) != 0) {
2154 be_print_err(gettext("be_unmount_root: failed to "
2155 "unmount BE root dataset %s: %s\n"), zfs_get_name(zhp),
2156 libzfs_error_description(g_zfs));
2157 return (zfs_err_to_be_err(g_zfs));
2160 /* Set canmount property for this BE's root filesystem to noauto */
2161 if (zfs_prop_set(zhp, zfs_prop_to_name(ZFS_PROP_CANMOUNT), "noauto")
2162 != 0) {
2163 be_print_err(gettext("be_unmount_root: failed to "
2164 "set canmount property for %s to 'noauto': %s\n"),
2165 zfs_get_name(zhp), libzfs_error_description(g_zfs));
2166 return (zfs_err_to_be_err(g_zfs));
2170 * Set mountpoint for BE's root dataset back to '/', or 'legacy'
2171 * if its a legacy mounted root.
2173 if (zfs_prop_set(zhp, zfs_prop_to_name(ZFS_PROP_MOUNTPOINT),
2174 is_legacy ? ZFS_MOUNTPOINT_LEGACY : "/") != 0) {
2175 be_print_err(gettext("be_unmount_root: failed to "
2176 "set mountpoint of %s to %s\n"), zfs_get_name(zhp),
2177 is_legacy ? ZFS_MOUNTPOINT_LEGACY : "/");
2178 return (zfs_err_to_be_err(g_zfs));
2181 return (BE_SUCCESS);
2185 * Function: fix_mountpoint
2186 * Description: This function checks the mountpoint of an unmounted BE to make
2187 * sure that it is set to either 'legacy' or '/'. If it's not,
2188 * then we're in a situation where an unmounted BE has some random
2189 * mountpoint set for it. (This could happen if the system was
2190 * rebooted while an inactive BE was mounted). This function
2191 * attempts to fix its mountpoints.
2192 * Parameters:
2193 * zhp - zfs_handle_t pointer to root dataset of the BE
2194 * whose mountpoint needs to be checked.
2195 * Return:
2196 * BE_SUCCESS - Success
2197 * be_errno_t - Failure
2198 * Scope:
2199 * Private
2201 static int
2202 fix_mountpoint(zfs_handle_t *zhp)
2204 be_unmount_data_t ud = { 0 };
2205 char *altroot = NULL;
2206 char mountpoint[MAXPATHLEN];
2207 int ret = BE_SUCCESS;
2210 * Record what this BE's root dataset mountpoint property is currently
2211 * set to.
2213 if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mountpoint,
2214 sizeof (mountpoint), NULL, NULL, 0, B_FALSE) != 0) {
2215 be_print_err(gettext("fix_mountpoint: failed to get "
2216 "mountpoint property of (%s): %s\n"), zfs_get_name(zhp),
2217 libzfs_error_description(g_zfs));
2218 return (BE_ERR_ZFS);
2222 * If the root dataset mountpoint is set to 'legacy' or '/', we're okay.
2224 if (strcmp(mountpoint, ZFS_MOUNTPOINT_LEGACY) == 0 ||
2225 strcmp(mountpoint, "/") == 0) {
2226 return (BE_SUCCESS);
2230 * Iterate through this BE's children datasets and fix
2231 * them if they need fixing.
2233 if (zfs_iter_filesystems(zhp, fix_mountpoint_callback, mountpoint)
2234 != 0) {
2235 return (BE_ERR_ZFS);
2239 * The process of mounting and unmounting the root file system
2240 * will fix its mountpoint to correctly be either 'legacy' or '/'
2241 * since be_unmount_root will do the right thing by looking at
2242 * its vfstab.
2245 /* Generate temporary altroot to mount the root file system */
2246 if ((ret = be_make_tmp_mountpoint(&altroot)) != BE_SUCCESS) {
2247 be_print_err(gettext("fix_mountpoint: failed to "
2248 "make temporary mountpoint\n"));
2249 return (ret);
2252 /* Mount and unmount the root. */
2253 if ((ret = be_mount_root(zhp, altroot)) != BE_SUCCESS) {
2254 be_print_err(gettext("fix_mountpoint: failed to "
2255 "mount BE root file system\n"));
2256 goto cleanup;
2258 ud.altroot = altroot;
2259 if ((ret = be_unmount_root(zhp, &ud)) != BE_SUCCESS) {
2260 be_print_err(gettext("fix_mountpoint: failed to "
2261 "unmount BE root file system\n"));
2262 goto cleanup;
2265 cleanup:
2266 free(altroot);
2268 return (ret);
2272 * Function: be_mount_zones
2273 * Description: This function finds all supported non-global zones in the
2274 * given global BE and mounts them with respect to where the
2275 * global BE is currently mounted. The global BE datasets
2276 * (including its shared datasets) are expected to already
2277 * be mounted.
2278 * Parameters:
2279 * be_zhp - zfs_handle_t pointer to the root dataset of the
2280 * global BE.
2281 * md - be_mount_data_t pointer to data for global BE.
2282 * Returns:
2283 * BE_SUCCESS - Success
2284 * be_errno_t - Failure
2285 * Scope:
2286 * Private
2288 static int
2289 be_mount_zones(zfs_handle_t *be_zhp, be_mount_data_t *md)
2291 char *zonepath_ds = NULL;
2292 int ret = BE_SUCCESS;
2293 FILE *cookie;
2294 struct zoneent *ze;
2296 zonecfg_set_root((const char *)md->altroot);
2298 cookie = setzoneent();
2299 while((ze = getzoneent_private(cookie)) != NULL) {
2301 if (strcmp(ze->zone_name, "global") == 0)
2302 continue;
2304 /* Skip zones that aren't at least installed */
2305 if (ze->zone_state == ZONE_STATE_INSTALLED) {
2307 if (((zonepath_ds = be_get_ds_from_dir(ze->zone_path)) == NULL) ||
2308 !be_zone_supported(zonepath_ds)) {
2309 free(zonepath_ds);
2310 free(ze);
2311 continue;
2315 * if BE's shared file systems are already mounted,
2316 * zone path dataset would have already been lofs
2317 * mounted under altroot. Otherwise, we need to do
2318 * it here.
2320 if (!md->shared_fs) {
2321 ret = loopback_mount_zonepath(ze->zone_path, md);
2322 if (ret != BE_SUCCESS)
2323 goto done;
2326 /* Mount this zone */
2327 ret = be_mount_one_zone(be_zhp, md, ze->zone_name,
2328 ze->zone_path, zonepath_ds);
2330 free(zonepath_ds);
2331 zonepath_ds = NULL;
2333 if (ret != BE_SUCCESS) {
2334 be_print_err(gettext("be_mount_zones: "
2335 "failed to mount zone %s under "
2336 "altroot %s\n"), ze->zone_name, md->altroot);
2337 goto done;
2340 free(ze);
2342 endzoneent(cookie);
2344 done:
2346 return (ret);
2350 * Function: be_unmount_zones
2351 * Description: This function finds all supported non-global zones in the
2352 * given mounted global BE and unmounts them.
2353 * Parameters:
2354 * ud - unmount_data_t pointer data for the global BE.
2355 * Returns:
2356 * BE_SUCCESS - Success
2357 * be_errno_t - Failure
2358 * Scope:
2359 * Private
2361 static int
2362 be_unmount_zones(be_unmount_data_t *ud)
2364 char alt_zonepath[MAXPATHLEN];
2365 char *zonepath_ds = NULL;
2366 int ret = BE_SUCCESS;
2367 FILE *cookie;
2368 struct zoneent *ze;
2370 zonecfg_set_root((const char *)ud->altroot);
2372 cookie = setzoneent();
2373 while((ze = getzoneent_private(cookie)) != NULL) {
2375 if (strcmp(ze->zone_name, "global") == 0)
2376 continue;
2378 if (ze->zone_state == ZONE_STATE_INSTALLED) {
2380 /* Build zone's zonepath wrt the global BE altroot */
2381 (void) snprintf(alt_zonepath, sizeof (alt_zonepath),
2382 "%s%s", ud->altroot, ze->zone_path);
2385 * Get the dataset of this zonepath. If its not
2386 * a dataset, skip it.
2388 if ((zonepath_ds = be_get_ds_from_dir(alt_zonepath))
2389 == NULL)
2390 continue;
2393 * Check if this zone is supported based on the
2394 * dataset of its zonepath.
2396 if (!be_zone_supported(zonepath_ds)) {
2397 free(zonepath_ds);
2398 zonepath_ds = NULL;
2399 free(ze);
2400 continue;
2403 /* Unmount this zone */
2404 ret = be_unmount_one_zone(ud, ze->zone_name, ze->zone_path,
2405 zonepath_ds);
2407 free(zonepath_ds);
2408 zonepath_ds = NULL;
2410 if (ret != BE_SUCCESS) {
2411 be_print_err(gettext("be_unmount_zones:"
2412 " failed to unmount zone %s from "
2413 "altroot %s\n"), ze->zone_name, ud->altroot);
2414 goto done;
2417 free(ze);
2419 endzoneent(cookie);
2421 done:
2423 return (ret);
2427 * Function: be_mount_one_zone
2428 * Description: This function is called to mount one zone for a given
2429 * global BE.
2430 * Parameters:
2431 * be_zhp - zfs_handle_t pointer to the root dataset of the
2432 * global BE
2433 * md - be_mount_data_t pointer to data for global BE
2434 * zonename - name of zone to mount
2435 * zonepath - zonepath of zone to mount
2436 * zonepath_ds - dataset for the zonepath
2437 * Returns:
2438 * BE_SUCCESS - Success
2439 * be_errno_t - Failure
2440 * Scope:
2441 * Private
2443 static int
2444 be_mount_one_zone(zfs_handle_t *be_zhp, be_mount_data_t *md, char *zonename,
2445 char *zonepath, char *zonepath_ds)
2447 be_mount_data_t zone_md = { 0 };
2448 zfs_handle_t *zone_zhp = NULL;
2449 char zone_altroot[MAXPATHLEN];
2450 char zoneroot[MAXPATHLEN];
2451 char zoneroot_ds[MAXPATHLEN];
2452 int ret = BE_SUCCESS;
2454 /* Find the active zone root dataset for this zone for this BE */
2455 if ((ret = be_find_active_zone_root(be_zhp, zonepath_ds, zoneroot_ds,
2456 sizeof (zoneroot_ds))) == BE_ERR_ZONE_NO_ACTIVE_ROOT) {
2457 be_print_err(gettext("be_mount_one_zone: did not "
2458 "find active zone root for zone %s, skipping ...\n"),
2459 zonename);
2460 return (BE_SUCCESS);
2461 } else if (ret != BE_SUCCESS) {
2462 be_print_err(gettext("be_mount_one_zone: failed to "
2463 "find active zone root for zone %s\n"), zonename);
2464 return (ret);
2467 /* Get handle to active zoneroot dataset */
2468 if ((zone_zhp = zfs_open(g_zfs, zoneroot_ds, ZFS_TYPE_FILESYSTEM))
2469 == NULL) {
2470 be_print_err(gettext("be_mount_one_zone: failed to "
2471 "open zone root dataset (%s): %s\n"), zoneroot_ds,
2472 libzfs_error_description(g_zfs));
2473 return (zfs_err_to_be_err(g_zfs));
2476 /* Generate string for zone's altroot path */
2477 be_make_zoneroot(zonepath, zoneroot, sizeof (zoneroot));
2478 (void) strlcpy(zone_altroot, md->altroot, sizeof (zone_altroot));
2479 (void) strlcat(zone_altroot, zoneroot, sizeof (zone_altroot));
2481 /* Build mount_data for the zone */
2482 zone_md.altroot = zone_altroot;
2483 zone_md.shared_fs = md->shared_fs;
2484 zone_md.shared_rw = md->shared_rw;
2486 /* Mount the zone's root file system */
2487 if ((ret = be_mount_zone_root(zone_zhp, &zone_md)) != BE_SUCCESS) {
2488 be_print_err(gettext("be_mount_one_zone: failed to "
2489 "mount zone root file system at %s\n"), zone_altroot);
2490 goto done;
2493 /* Iterate through zone's children filesystems */
2494 if ((ret = zfs_iter_filesystems(zone_zhp, be_mount_callback,
2495 zone_altroot)) != 0) {
2496 be_print_err(gettext("be_mount_one_zone: failed to "
2497 "mount zone subordinate file systems at %s\n"),
2498 zone_altroot);
2499 goto done;
2502 /* TODO: Mount all shared file systems for this zone */
2504 done:
2505 ZFS_CLOSE(zone_zhp);
2506 return (ret);
2510 * Function: be_unmount_one_zone
2511 * Description: This function unmount one zone for a give global BE.
2512 * Parameters:
2513 * ud - be_unmount_data_t pointer to data for global BE
2514 * zonename - name of zone to unmount
2515 * zonepath - zonepath of the zone to unmount
2516 * zonepath_ds - dataset for the zonepath
2517 * Returns:
2518 * BE_SUCCESS - Success
2519 * be_errno_t - Failure
2520 * Scope:
2521 * Private
2523 static int
2524 be_unmount_one_zone(be_unmount_data_t *ud, char *zonename, char *zonepath,
2525 char *zonepath_ds)
2527 be_unmount_data_t zone_ud = { 0 };
2528 zfs_handle_t *zone_zhp = NULL;
2529 char zone_altroot[MAXPATHLEN];
2530 char zoneroot[MAXPATHLEN];
2531 char zoneroot_ds[MAXPATHLEN];
2532 int ret = BE_SUCCESS;
2534 /* Generate string for zone's alternate root path */
2535 be_make_zoneroot(zonepath, zoneroot, sizeof (zoneroot));
2536 (void) strlcpy(zone_altroot, ud->altroot, sizeof (zone_altroot));
2537 (void) strlcat(zone_altroot, zoneroot, sizeof (zone_altroot));
2539 /* Build be_unmount_data for zone */
2540 zone_ud.altroot = zone_altroot;
2541 zone_ud.force = ud->force;
2543 /* Find the mounted zone root dataset for this zone for this BE */
2544 if ((ret = be_find_mounted_zone_root(zone_altroot, zonepath_ds,
2545 zoneroot_ds, sizeof (zoneroot_ds))) == BE_ERR_NO_MOUNTED_ZONE) {
2546 be_print_err(gettext("be_unmount_one_zone: did not "
2547 "find any zone root mounted for zone %s\n"), zonename);
2548 return (BE_SUCCESS);
2549 } else if (ret != BE_SUCCESS) {
2550 be_print_err(gettext("be_unmount_one_zone: failed to "
2551 "find mounted zone root for zone %s\n"), zonename);
2552 return (ret);
2555 /* Get handle to zoneroot dataset mounted for this BE */
2556 if ((zone_zhp = zfs_open(g_zfs, zoneroot_ds, ZFS_TYPE_FILESYSTEM))
2557 == NULL) {
2558 be_print_err(gettext("be_unmount_one_zone: failed to "
2559 "open mounted zone root dataset (%s): %s\n"), zoneroot_ds,
2560 libzfs_error_description(g_zfs));
2561 return (zfs_err_to_be_err(g_zfs));
2564 /* TODO: Unmount all shared file systems for this zone */
2566 /* Iterate through zone's children filesystems and unmount them */
2567 if ((ret = zfs_iter_filesystems(zone_zhp, be_unmount_callback,
2568 &zone_ud)) != 0) {
2569 be_print_err(gettext("be_unmount_one_zone: failed to "
2570 "unmount zone subordinate file systems at %s\n"),
2571 zone_altroot);
2572 goto done;
2575 /* Unmount the zone's root filesystem */
2576 if ((ret = be_unmount_zone_root(zone_zhp, &zone_ud)) != BE_SUCCESS) {
2577 be_print_err(gettext("be_unmount_one_zone: failed to "
2578 "unmount zone root file system at %s\n"), zone_altroot);
2579 goto done;
2582 done:
2583 ZFS_CLOSE(zone_zhp);
2584 return (ret);
2588 * Function: be_get_ds_from_dir_callback
2589 * Description: This is a callback function used to iterate all datasets
2590 * to find the one that is currently mounted at the directory
2591 * being searched for. If matched, the name of the dataset is
2592 * returned in heap storage, so the caller is responsible for
2593 * freeing it.
2594 * Parameters:
2595 * zhp - zfs_handle_t pointer to current dataset being processed.
2596 * data - dir_data_t pointer providing name of directory being
2597 * searched for.
2598 * Returns:
2599 * 1 - This dataset is mounted at directory being searched for.
2600 * 0 - This dataset is not mounted at directory being searched for.
2601 * Scope:
2602 * Private
2604 static int
2605 be_get_ds_from_dir_callback(zfs_handle_t *zhp, void *data)
2607 dir_data_t *dd = data;
2608 char *mp = NULL;
2609 int zret = 0;
2611 if (zfs_get_type(zhp) != ZFS_TYPE_FILESYSTEM) {
2612 ZFS_CLOSE(zhp);
2613 return (0);
2616 if (zfs_is_mounted(zhp, &mp) && mp != NULL &&
2617 strcmp(mp, dd->dir) == 0) {
2618 if ((dd->ds = strdup(zfs_get_name(zhp))) == NULL) {
2619 be_print_err(gettext("be_get_ds_from_dir_callback: "
2620 "memory allocation failed\n"));
2621 ZFS_CLOSE(zhp);
2622 return (0);
2624 ZFS_CLOSE(zhp);
2625 return (1);
2628 zret = zfs_iter_filesystems(zhp, be_get_ds_from_dir_callback, dd);
2630 ZFS_CLOSE(zhp);
2632 return (zret);
2636 * Function: mount_zfs
2637 * Description: This is a function to mount zfs filesystem to alternative
2638 * root without changing zfs mountpoint property. Logic is
2639 * similar to zfs_mount.
2640 * Parameters:
2641 * zhp - zfs_handle_t pointer to current dataset being processed.
2642 * altroot - char pointer to current alternative root.
2643 * Returns:
2644 * BE_SUCCESS - Success
2645 * be_errno_t - Failure
2646 * Scope:
2647 * Private
2649 static int
2650 mount_zfs(zfs_handle_t *zhp, char *altroot)
2652 int flags = 0;
2653 char mountpoint[MAXPATHLEN];
2654 char real_mountpoint[MAXPATHLEN];
2655 char source[MAXNAMELEN];
2656 char optstr[MAX_MNTOPT_STR];
2657 zprop_source_t sourcetype;
2658 struct stat buf;
2660 optstr[0] = '\0';
2662 /* Get dataset's mountpoint and source values */
2663 if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mountpoint,
2664 sizeof (mountpoint), &sourcetype, source, sizeof (source),
2665 B_FALSE) != 0) {
2666 be_print_err(gettext("mount_zfs: "
2667 "failed to get mountpoint and sourcetype for %s\n"),
2668 zfs_get_name(zhp));
2669 ZFS_CLOSE(zhp);
2670 return (BE_ERR_ZFS);
2673 if (strcmp(mountpoint, ZFS_MOUNTPOINT_LEGACY) == 0 ||
2674 strcmp(mountpoint, "/") == 0) {
2676 * We are called only from be_mount_root or be_mount_callback
2677 * when mountpoint != LEGACY
2679 mountpoint[0] = '\0';
2682 (void) snprintf(real_mountpoint, MAXPATHLEN, "%s%s", altroot,
2683 mountpoint);
2685 if (zpool_get_prop_int(zfs_get_pool_handle(zhp), ZPOOL_PROP_READONLY,
2686 NULL))
2687 flags |= MS_RDONLY;
2689 /* Create the directory if it doesn't already exist */
2690 if (lstat(real_mountpoint, &buf) != 0) {
2691 if (mkdirp(real_mountpoint, 0755) != 0) {
2692 be_print_err(gettext("mount_zfs: "
2693 "failed to create mountpoint for %s\n"),
2694 zfs_get_name(zhp));
2695 ZFS_CLOSE(zhp);
2696 return (BE_ERR_ZFS);
2700 if (mount(zfs_get_name(zhp), real_mountpoint, MS_OPTIONSTR | flags,
2701 MNTTYPE_ZFS, NULL, 0, optstr, sizeof (optstr))) {
2702 be_print_err(gettext("mount_zfs: failed to "
2703 "mount dataset %s at %s\n"), zfs_get_name(zhp),
2704 real_mountpoint);
2705 return (BE_ERR_ZFS);
2708 return (BE_SUCCESS);