2451 beadm umount should report if there is nothing to unmount
[unleashed.git] / usr / src / lib / libbe / common / be_mount.c
blob6c631da67d64ed42520b41839aa36e2f8a59b88d
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.
26 * Copyright 2012 Nexenta Systems, Inc. All rights reserved.
30 * System includes
32 #include <assert.h>
33 #include <errno.h>
34 #include <libintl.h>
35 #include <libnvpair.h>
36 #include <libzfs.h>
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <string.h>
40 #include <sys/mntent.h>
41 #include <sys/mnttab.h>
42 #include <sys/mount.h>
43 #include <sys/stat.h>
44 #include <sys/types.h>
45 #include <sys/vfstab.h>
46 #include <sys/zone.h>
47 #include <sys/mkdev.h>
48 #include <unistd.h>
50 #include <libbe.h>
51 #include <libbe_priv.h>
53 #define BE_TMP_MNTPNT "/tmp/.be.XXXXXX"
55 typedef struct dir_data {
56 char *dir;
57 char *ds;
58 } dir_data_t;
60 /* Private function prototypes */
61 static int be_mount_callback(zfs_handle_t *, void *);
62 static int be_unmount_callback(zfs_handle_t *, void *);
63 static int be_get_legacy_fs_callback(zfs_handle_t *, void *);
64 static int fix_mountpoint(zfs_handle_t *);
65 static int fix_mountpoint_callback(zfs_handle_t *, void *);
66 static int get_mountpoint_from_vfstab(char *, const char *, char *, size_t,
67 boolean_t);
68 static int loopback_mount_shared_fs(zfs_handle_t *, be_mount_data_t *);
69 static int loopback_mount_zonepath(const char *, be_mount_data_t *);
70 static int iter_shared_fs_callback(zfs_handle_t *, void *);
71 static int zpool_shared_fs_callback(zpool_handle_t *, void *);
72 static int unmount_shared_fs(be_unmount_data_t *);
73 static int add_to_fs_list(be_fs_list_data_t *, const char *);
74 static int be_mount_root(zfs_handle_t *, char *);
75 static int be_unmount_root(zfs_handle_t *, be_unmount_data_t *);
76 static int be_mount_zones(zfs_handle_t *, be_mount_data_t *);
77 static int be_unmount_zones(be_unmount_data_t *);
78 static int be_mount_one_zone(zfs_handle_t *, be_mount_data_t *, char *, char *,
79 char *);
80 static int be_unmount_one_zone(be_unmount_data_t *, char *, char *, char *);
81 static int be_get_ds_from_dir_callback(zfs_handle_t *, void *);
84 /* ******************************************************************** */
85 /* Public Functions */
86 /* ******************************************************************** */
89 * Function: be_mount
90 * Description: Mounts a BE and its subordinate datasets at a given mountpoint.
91 * Parameters:
92 * be_attrs - pointer to nvlist_t of attributes being passed in.
93 * The following attributes are used by this function:
95 * BE_ATTR_ORIG_BE_NAME *required
96 * BE_ATTR_MOUNTPOINT *required
97 * BE_ATTR_MOUNT_FLAGS *optional
98 * Return:
99 * BE_SUCCESS - Success
100 * be_errno_t - Failure
101 * Scope:
102 * Public
105 be_mount(nvlist_t *be_attrs)
107 char *be_name = NULL;
108 char *mountpoint = NULL;
109 uint16_t flags = 0;
110 int ret = BE_SUCCESS;
112 /* Initialize libzfs handle */
113 if (!be_zfs_init())
114 return (BE_ERR_INIT);
116 /* Get original BE name */
117 if (nvlist_lookup_string(be_attrs, BE_ATTR_ORIG_BE_NAME, &be_name)
118 != 0) {
119 be_print_err(gettext("be_mount: failed to lookup "
120 "BE_ATTR_ORIG_BE_NAME attribute\n"));
121 return (BE_ERR_INVAL);
124 /* Validate original BE name */
125 if (!be_valid_be_name(be_name)) {
126 be_print_err(gettext("be_mount: invalid BE name %s\n"),
127 be_name);
128 return (BE_ERR_INVAL);
131 /* Get mountpoint */
132 if (nvlist_lookup_string(be_attrs, BE_ATTR_MOUNTPOINT, &mountpoint)
133 != 0) {
134 be_print_err(gettext("be_mount: failed to lookup "
135 "BE_ATTR_MOUNTPOINT attribute\n"));
136 return (BE_ERR_INVAL);
139 /* Get flags */
140 if (nvlist_lookup_pairs(be_attrs, NV_FLAG_NOENTOK,
141 BE_ATTR_MOUNT_FLAGS, DATA_TYPE_UINT16, &flags, NULL) != 0) {
142 be_print_err(gettext("be_mount: failed to lookup "
143 "BE_ATTR_MOUNT_FLAGS attribute\n"));
144 return (BE_ERR_INVAL);
147 ret = _be_mount(be_name, &mountpoint, flags);
149 be_zfs_fini();
151 return (ret);
155 * Function: be_unmount
156 * Description: Unmounts a BE and its subordinate datasets.
157 * Parameters:
158 * be_attrs - pointer to nvlist_t of attributes being passed in.
159 * The following attributes are used by this function:
161 * BE_ATTR_ORIG_BE_NAME *required
162 * BE_ATTR_UNMOUNT_FLAGS *optional
163 * Return:
164 * BE_SUCCESS - Success
165 * be_errno_t - Failure
166 * Scope:
167 * Public
170 be_unmount(nvlist_t *be_attrs)
172 char *be_name = NULL;
173 char *be_name_mnt = NULL;
174 char *ds = NULL;
175 uint16_t flags = 0;
176 int ret = BE_SUCCESS;
178 /* Initialize libzfs handle */
179 if (!be_zfs_init())
180 return (BE_ERR_INIT);
182 /* Get original BE name */
183 if (nvlist_lookup_string(be_attrs, BE_ATTR_ORIG_BE_NAME, &be_name)
184 != 0) {
185 be_print_err(gettext("be_unmount: failed to lookup "
186 "BE_ATTR_ORIG_BE_NAME attribute\n"));
187 return (BE_ERR_INVAL);
190 /* Check if we have mountpoint argument instead of BE name */
191 if (be_name[0] == '/') {
192 if ((ds = be_get_ds_from_dir(be_name)) != NULL) {
193 if ((be_name_mnt = strrchr(ds, '/')) != NULL) {
194 free(be_name);
195 be_name = be_name_mnt + 1;
197 } else {
198 be_print_err(gettext("be_unmount: no datasets mounted "
199 "at '%s'\n"), be_name);
200 return (BE_ERR_INVAL);
204 /* Validate original BE name */
205 if (!be_valid_be_name(be_name)) {
206 be_print_err(gettext("be_unmount: invalid BE name %s\n"),
207 be_name);
208 return (BE_ERR_INVAL);
211 /* Get unmount flags */
212 if (nvlist_lookup_pairs(be_attrs, NV_FLAG_NOENTOK,
213 BE_ATTR_UNMOUNT_FLAGS, DATA_TYPE_UINT16, &flags, NULL) != 0) {
214 be_print_err(gettext("be_unmount: failed to loookup "
215 "BE_ATTR_UNMOUNT_FLAGS attribute\n"));
216 return (BE_ERR_INVAL);
219 ret = _be_unmount(be_name, flags);
221 be_zfs_fini();
223 return (ret);
226 /* ******************************************************************** */
227 /* Semi-Private Functions */
228 /* ******************************************************************** */
231 * Function: _be_mount
232 * Description: Mounts a BE. If the altroot is not provided, this function
233 * will generate a temporary mountpoint to mount the BE at. It
234 * will return this temporary mountpoint to the caller via the
235 * altroot reference pointer passed in. This returned value is
236 * allocated on heap storage and is the repsonsibility of the
237 * caller to free.
238 * Parameters:
239 * be_name - pointer to name of BE to mount.
240 * altroot - reference pointer to altroot of where to mount BE.
241 * flags - flag indicating special handling for mounting the BE
242 * Return:
243 * BE_SUCCESS - Success
244 * be_errno_t - Failure
245 * Scope:
246 * Semi-private (library wide use only)
249 _be_mount(char *be_name, char **altroot, int flags)
251 be_transaction_data_t bt = { 0 };
252 be_mount_data_t md = { 0 };
253 zfs_handle_t *zhp;
254 char obe_root_ds[MAXPATHLEN];
255 char *mp = NULL;
256 char *tmp_altroot = NULL;
257 int ret = BE_SUCCESS, err = 0;
258 uuid_t uu = { 0 };
259 boolean_t gen_tmp_altroot = B_FALSE;
261 if (be_name == NULL || altroot == NULL)
262 return (BE_ERR_INVAL);
264 /* Set be_name as obe_name in bt structure */
265 bt.obe_name = be_name;
267 /* Find which zpool obe_name lives in */
268 if ((err = zpool_iter(g_zfs, be_find_zpool_callback, &bt)) == 0) {
269 be_print_err(gettext("be_mount: failed to "
270 "find zpool for BE (%s)\n"), bt.obe_name);
271 return (BE_ERR_BE_NOENT);
272 } else if (err < 0) {
273 be_print_err(gettext("be_mount: zpool_iter failed: %s\n"),
274 libzfs_error_description(g_zfs));
275 return (zfs_err_to_be_err(g_zfs));
278 /* Generate string for obe_name's root dataset */
279 be_make_root_ds(bt.obe_zpool, bt.obe_name, obe_root_ds,
280 sizeof (obe_root_ds));
281 bt.obe_root_ds = obe_root_ds;
283 /* Get handle to BE's root dataset */
284 if ((zhp = zfs_open(g_zfs, bt.obe_root_ds, ZFS_TYPE_FILESYSTEM)) ==
285 NULL) {
286 be_print_err(gettext("be_mount: failed to "
287 "open BE root dataset (%s): %s\n"), bt.obe_root_ds,
288 libzfs_error_description(g_zfs));
289 return (zfs_err_to_be_err(g_zfs));
292 /* Make sure BE's root dataset isn't already mounted somewhere */
293 if (zfs_is_mounted(zhp, &mp)) {
294 ZFS_CLOSE(zhp);
295 be_print_err(gettext("be_mount: %s is already mounted "
296 "at %s\n"), bt.obe_name, mp != NULL ? mp : "");
297 free(mp);
298 return (BE_ERR_MOUNTED);
302 * Fix this BE's mountpoint if its root dataset isn't set to
303 * either 'legacy' or '/'.
305 if ((ret = fix_mountpoint(zhp)) != BE_SUCCESS) {
306 be_print_err(gettext("be_mount: mountpoint check "
307 "failed for %s\n"), bt.obe_root_ds);
308 ZFS_CLOSE(zhp);
309 return (ret);
313 * If altroot not provided, create a temporary alternate root
314 * to mount on
316 if (*altroot == NULL) {
317 if ((ret = be_make_tmp_mountpoint(&tmp_altroot))
318 != BE_SUCCESS) {
319 be_print_err(gettext("be_mount: failed to "
320 "make temporary mountpoint\n"));
321 ZFS_CLOSE(zhp);
322 return (ret);
324 gen_tmp_altroot = B_TRUE;
325 } else {
326 tmp_altroot = *altroot;
329 /* Mount the BE's root file system */
330 if ((ret = be_mount_root(zhp, tmp_altroot)) != BE_SUCCESS) {
331 be_print_err(gettext("be_mount: failed to "
332 "mount BE root file system\n"));
333 if (gen_tmp_altroot)
334 free(tmp_altroot);
335 ZFS_CLOSE(zhp);
336 return (ret);
339 /* Iterate through BE's children filesystems */
340 if ((err = zfs_iter_filesystems(zhp, be_mount_callback,
341 tmp_altroot)) != 0) {
342 be_print_err(gettext("be_mount: failed to "
343 "mount BE (%s) on %s\n"), bt.obe_name, tmp_altroot);
344 if (gen_tmp_altroot)
345 free(tmp_altroot);
346 ZFS_CLOSE(zhp);
347 return (err);
350 md.altroot = tmp_altroot;
351 md.shared_fs = flags & BE_MOUNT_FLAG_SHARED_FS;
352 md.shared_rw = flags & BE_MOUNT_FLAG_SHARED_RW;
355 * Mount shared file systems if mount flag says so.
357 if (md.shared_fs) {
359 * Mount all ZFS file systems not under the BE's root dataset
361 (void) zpool_iter(g_zfs, zpool_shared_fs_callback, &md);
363 /* TODO: Mount all non-ZFS file systems - Not supported yet */
367 * If we're in the global zone and the global zone has a valid uuid,
368 * mount all supported non-global zones.
370 if (getzoneid() == GLOBAL_ZONEID &&
371 !(flags & BE_MOUNT_FLAG_NO_ZONES) &&
372 be_get_uuid(bt.obe_root_ds, &uu) == BE_SUCCESS) {
373 if ((ret = be_mount_zones(zhp, &md)) != BE_SUCCESS) {
374 (void) _be_unmount(bt.obe_name, 0);
375 if (gen_tmp_altroot)
376 free(tmp_altroot);
377 ZFS_CLOSE(zhp);
378 return (ret);
382 ZFS_CLOSE(zhp);
385 * If a NULL altroot was passed in, pass the generated altroot
386 * back to the caller in altroot.
388 if (gen_tmp_altroot)
389 *altroot = tmp_altroot;
391 return (BE_SUCCESS);
395 * Function: _be_unmount
396 * Description: Unmount a BE.
397 * Parameters:
398 * be_name - pointer to name of BE to unmount.
399 * flags - flags for unmounting the BE.
400 * Returns:
401 * BE_SUCCESS - Success
402 * be_errno_t - Failure
403 * Scope:
404 * Semi-private (library wide use only)
407 _be_unmount(char *be_name, int flags)
409 be_transaction_data_t bt = { 0 };
410 be_unmount_data_t ud = { 0 };
411 zfs_handle_t *zhp;
412 uuid_t uu = { 0 };
413 char obe_root_ds[MAXPATHLEN];
414 char mountpoint[MAXPATHLEN];
415 char *mp = NULL;
416 int ret = BE_SUCCESS;
417 int zret = 0;
419 if (be_name == NULL)
420 return (BE_ERR_INVAL);
422 /* Set be_name as obe_name in bt structure */
423 bt.obe_name = be_name;
425 /* Find which zpool obe_name lives in */
426 if ((zret = zpool_iter(g_zfs, be_find_zpool_callback, &bt)) == 0) {
427 be_print_err(gettext("be_unmount: failed to "
428 "find zpool for BE (%s)\n"), bt.obe_name);
429 return (BE_ERR_BE_NOENT);
430 } else if (zret < 0) {
431 be_print_err(gettext("be_unmount: "
432 "zpool_iter failed: %s\n"),
433 libzfs_error_description(g_zfs));
434 ret = zfs_err_to_be_err(g_zfs);
435 return (ret);
438 /* Generate string for obe_name's root dataset */
439 be_make_root_ds(bt.obe_zpool, bt.obe_name, obe_root_ds,
440 sizeof (obe_root_ds));
441 bt.obe_root_ds = obe_root_ds;
443 /* Get handle to BE's root dataset */
444 if ((zhp = zfs_open(g_zfs, bt.obe_root_ds, ZFS_TYPE_FILESYSTEM)) ==
445 NULL) {
446 be_print_err(gettext("be_unmount: failed to "
447 "open BE root dataset (%s): %s\n"), bt.obe_root_ds,
448 libzfs_error_description(g_zfs));
449 ret = zfs_err_to_be_err(g_zfs);
450 return (ret);
453 /* Make sure BE's root dataset is mounted somewhere */
454 if (!zfs_is_mounted(zhp, &mp)) {
456 be_print_err(gettext("be_unmount: "
457 "(%s) not mounted\n"), bt.obe_name);
460 * BE is not mounted, fix this BE's mountpoint if its root
461 * dataset isn't set to either 'legacy' or '/'.
463 if ((ret = fix_mountpoint(zhp)) != BE_SUCCESS) {
464 be_print_err(gettext("be_unmount: mountpoint check "
465 "failed for %s\n"), bt.obe_root_ds);
466 ZFS_CLOSE(zhp);
467 return (ret);
470 ZFS_CLOSE(zhp);
471 return (BE_ERR_NOTMOUNTED);
475 * If we didn't get a mountpoint from the zfs_is_mounted call,
476 * try and get it from its property.
478 if (mp == NULL) {
479 if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mountpoint,
480 sizeof (mountpoint), NULL, NULL, 0, B_FALSE) != 0) {
481 be_print_err(gettext("be_unmount: failed to "
482 "get mountpoint of (%s)\n"), bt.obe_name);
483 ZFS_CLOSE(zhp);
484 return (BE_ERR_ZFS);
486 } else {
487 (void) strlcpy(mountpoint, mp, sizeof (mountpoint));
488 free(mp);
491 /* If BE mounted as current root, fail */
492 if (strcmp(mountpoint, "/") == 0) {
493 be_print_err(gettext("be_unmount: "
494 "cannot unmount currently running BE\n"));
495 ZFS_CLOSE(zhp);
496 return (BE_ERR_UMOUNT_CURR_BE);
499 ud.altroot = mountpoint;
500 ud.force = flags & BE_UNMOUNT_FLAG_FORCE;
502 /* Unmount all supported non-global zones if we're in the global zone */
503 if (getzoneid() == GLOBAL_ZONEID &&
504 be_get_uuid(bt.obe_root_ds, &uu) == BE_SUCCESS) {
505 if ((ret = be_unmount_zones(&ud)) != BE_SUCCESS) {
506 ZFS_CLOSE(zhp);
507 return (ret);
511 /* TODO: Unmount all non-ZFS file systems - Not supported yet */
513 /* Unmount all ZFS file systems not under the BE root dataset */
514 if ((ret = unmount_shared_fs(&ud)) != BE_SUCCESS) {
515 be_print_err(gettext("be_unmount: failed to "
516 "unmount shared file systems\n"));
517 ZFS_CLOSE(zhp);
518 return (ret);
521 /* Unmount all children datasets under the BE's root dataset */
522 if ((zret = zfs_iter_filesystems(zhp, be_unmount_callback,
523 &ud)) != 0) {
524 be_print_err(gettext("be_unmount: failed to "
525 "unmount BE (%s)\n"), bt.obe_name);
526 ZFS_CLOSE(zhp);
527 return (zret);
530 /* Unmount this BE's root filesystem */
531 if ((ret = be_unmount_root(zhp, &ud)) != BE_SUCCESS) {
532 ZFS_CLOSE(zhp);
533 return (ret);
536 ZFS_CLOSE(zhp);
538 return (BE_SUCCESS);
542 * Function: be_mount_zone_root
543 * Description: Mounts the zone root dataset for a zone.
544 * Parameters:
545 * zfs - zfs_handle_t pointer to zone root dataset
546 * md - be_mount_data_t pointer to data for zone to be mounted
547 * Returns:
548 * BE_SUCCESS - Success
549 * be_errno_t - Failure
550 * Scope:
551 * Semi-private (library wide use only)
554 be_mount_zone_root(zfs_handle_t *zhp, be_mount_data_t *md)
556 char mountpoint[MAXPATHLEN];
557 int err = 0;
559 /* Get mountpoint property of dataset */
560 if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mountpoint,
561 sizeof (mountpoint), NULL, NULL, 0, B_FALSE) != 0) {
562 be_print_err(gettext("be_mount_zone_root: failed to "
563 "get mountpoint property for %s: %s\n"), zfs_get_name(zhp),
564 libzfs_error_description(g_zfs));
565 return (zfs_err_to_be_err(g_zfs));
569 * Make sure zone's root dataset is set to 'legacy'. This is
570 * currently a requirement in this implementation of zones
571 * support.
573 if (strcmp(mountpoint, ZFS_MOUNTPOINT_LEGACY) != 0) {
574 be_print_err(gettext("be_mount_zone_root: "
575 "zone root dataset mountpoint is not 'legacy'\n"));
576 return (BE_ERR_ZONE_ROOT_NOT_LEGACY);
580 * Legacy mount the zone root dataset.
582 * As a workaround for 6176743, we mount the zone's root with the
583 * MS_OVERLAY option in case an alternate BE is mounted, and we're
584 * mounting the root for the zone from the current BE here. When an
585 * alternate BE is mounted, it ties up the zone's zoneroot directory
586 * for the current BE since the zone's zonepath is loopback mounted
587 * from the current BE.
589 * TODO: The MS_OVERLAY option needs to be removed when 6176743
590 * is fixed.
592 if (mount(zfs_get_name(zhp), md->altroot, MS_OVERLAY, MNTTYPE_ZFS,
593 NULL, 0, NULL, 0) != 0) {
594 err = errno;
595 be_print_err(gettext("be_mount_zone_root: failed to "
596 "legacy mount zone root dataset (%s) at %s\n"),
597 zfs_get_name(zhp), md->altroot);
598 return (errno_to_be_err(err));
601 return (BE_SUCCESS);
605 * Function: be_unmount_zone_root
606 * Description: Unmounts the zone root dataset for a zone.
607 * Parameters:
608 * zhp - zfs_handle_t pointer to zone root dataset
609 * ud - be_unmount_data_t pointer to data for zone to be unmounted
610 * Returns:
611 * BE_SUCCESS - Success
612 * be_errno_t - Failure
613 * Scope:
614 * Semi-private (library wise use only)
617 be_unmount_zone_root(zfs_handle_t *zhp, be_unmount_data_t *ud)
619 char mountpoint[MAXPATHLEN];
621 /* Unmount the dataset */
622 if (zfs_unmount(zhp, NULL, ud->force ? MS_FORCE : 0) != 0) {
623 be_print_err(gettext("be_unmount_zone_root: failed to "
624 "unmount zone root dataset %s: %s\n"), zfs_get_name(zhp),
625 libzfs_error_description(g_zfs));
626 return (zfs_err_to_be_err(g_zfs));
629 /* Get the current mountpoint property for the zone root dataset */
630 if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mountpoint,
631 sizeof (mountpoint), NULL, NULL, 0, B_FALSE) != 0) {
632 be_print_err(gettext("be_unmount_zone_root: failed to "
633 "get mountpoint property for zone root dataset (%s): %s\n"),
634 zfs_get_name(zhp), libzfs_error_description(g_zfs));
635 return (zfs_err_to_be_err(g_zfs));
638 /* If mountpoint not already set to 'legacy', set it to 'legacy' */
639 if (strcmp(mountpoint, ZFS_MOUNTPOINT_LEGACY) != 0) {
640 if (zfs_prop_set(zhp, zfs_prop_to_name(ZFS_PROP_MOUNTPOINT),
641 ZFS_MOUNTPOINT_LEGACY) != 0) {
642 be_print_err(gettext("be_unmount_zone_root: "
643 "failed to set mountpoint of zone root dataset "
644 "%s to 'legacy': %s\n"), zfs_get_name(zhp),
645 libzfs_error_description(g_zfs));
646 return (zfs_err_to_be_err(g_zfs));
650 return (BE_SUCCESS);
654 * Function: be_get_legacy_fs
655 * Description: This function iterates through all non-shared file systems
656 * of a BE and finds the ones with a legacy mountpoint. For
657 * those file systems, it reads the BE's vfstab to get the
658 * mountpoint. If found, it adds that file system to the
659 * be_fs_list_data_t passed in.
661 * This function can be used to gather legacy mounted file systems
662 * for both global BEs and non-global zone BEs. To get data for
663 * a non-global zone BE, the zoneroot_ds and zoneroot parameters
664 * will be specified, otherwise they should be set to NULL.
665 * Parameters:
666 * be_name - global BE name from which to get legacy file
667 * system list.
668 * be_root_ds - root dataset of global BE.
669 * zoneroot_ds - root dataset of zone.
670 * zoneroot - zoneroot path of zone.
671 * fld - be_fs_list_data_t pointer.
672 * Returns:
673 * BE_SUCCESS - Success
674 * be_errno_t - Failure
675 * Scope:
676 * Semi-private (library wide use only)
679 be_get_legacy_fs(char *be_name, char *be_root_ds, char *zoneroot_ds,
680 char *zoneroot, be_fs_list_data_t *fld)
682 zfs_handle_t *zhp = NULL;
683 char mountpoint[MAXPATHLEN];
684 boolean_t mounted_here = B_FALSE;
685 boolean_t zone_mounted_here = B_FALSE;
686 int ret = BE_SUCCESS, err = 0;
688 if (be_name == NULL || be_root_ds == NULL || fld == NULL)
689 return (BE_ERR_INVAL);
691 /* Get handle to BE's root dataset */
692 if ((zhp = zfs_open(g_zfs, be_root_ds, ZFS_TYPE_FILESYSTEM))
693 == NULL) {
694 be_print_err(gettext("be_get_legacy_fs: failed to "
695 "open BE root dataset (%s): %s\n"), be_root_ds,
696 libzfs_error_description(g_zfs));
697 ret = zfs_err_to_be_err(g_zfs);
698 return (ret);
701 /* If BE is not already mounted, mount it. */
702 if (!zfs_is_mounted(zhp, &fld->altroot)) {
703 if ((ret = _be_mount(be_name, &fld->altroot,
704 zoneroot_ds ? BE_MOUNT_FLAG_NULL :
705 BE_MOUNT_FLAG_NO_ZONES)) != BE_SUCCESS) {
706 be_print_err(gettext("be_get_legacy_fs: "
707 "failed to mount BE %s\n"), be_name);
708 goto cleanup;
711 mounted_here = B_TRUE;
712 } else if (fld->altroot == NULL) {
713 be_print_err(gettext("be_get_legacy_fs: failed to "
714 "get altroot of mounted BE %s: %s\n"),
715 be_name, libzfs_error_description(g_zfs));
716 ret = zfs_err_to_be_err(g_zfs);
717 goto cleanup;
721 * If a zone root dataset was passed in, we're wanting to get
722 * legacy mounted file systems for that zone, not the global
723 * BE.
725 if (zoneroot_ds != NULL) {
726 be_mount_data_t zone_md = { 0 };
728 /* Close off handle to global BE's root dataset */
729 ZFS_CLOSE(zhp);
731 /* Get handle to zone's root dataset */
732 if ((zhp = zfs_open(g_zfs, zoneroot_ds,
733 ZFS_TYPE_FILESYSTEM)) == NULL) {
734 be_print_err(gettext("be_get_legacy_fs: failed to "
735 "open zone BE root dataset (%s): %s\n"),
736 zoneroot_ds, libzfs_error_description(g_zfs));
737 ret = zfs_err_to_be_err(g_zfs);
738 goto cleanup;
741 /* Make sure the zone we're looking for is mounted */
742 if (!zfs_is_mounted(zhp, &zone_md.altroot)) {
743 char zone_altroot[MAXPATHLEN];
745 /* Generate alternate root path for zone */
746 (void) snprintf(zone_altroot, sizeof (zone_altroot),
747 "%s%s", fld->altroot, zoneroot);
748 if ((zone_md.altroot = strdup(zone_altroot)) == NULL) {
749 be_print_err(gettext("be_get_legacy_fs: "
750 "memory allocation failed\n"));
751 ret = BE_ERR_NOMEM;
752 goto cleanup;
755 if ((ret = be_mount_zone_root(zhp, &zone_md))
756 != BE_SUCCESS) {
757 be_print_err(gettext("be_get_legacy_fs: "
758 "failed to mount zone root %s\n"),
759 zoneroot_ds);
760 free(zone_md.altroot);
761 zone_md.altroot = NULL;
762 goto cleanup;
764 zone_mounted_here = B_TRUE;
767 free(fld->altroot);
768 fld->altroot = zone_md.altroot;
772 * If the root dataset is in the vfstab with a mountpoint of "/",
773 * add it to the list
775 if (get_mountpoint_from_vfstab(fld->altroot, zfs_get_name(zhp),
776 mountpoint, sizeof (mountpoint), B_FALSE) == BE_SUCCESS) {
777 if (strcmp(mountpoint, "/") == 0) {
778 if (add_to_fs_list(fld, zfs_get_name(zhp))
779 != BE_SUCCESS) {
780 be_print_err(gettext("be_get_legacy_fs: "
781 "failed to add %s to fs list\n"),
782 zfs_get_name(zhp));
783 ret = BE_ERR_INVAL;
784 goto cleanup;
789 /* Iterate subordinate file systems looking for legacy mounts */
790 if ((ret = zfs_iter_filesystems(zhp, be_get_legacy_fs_callback,
791 fld)) != 0) {
792 be_print_err(gettext("be_get_legacy_fs: "
793 "failed to iterate %s to get legacy mounts\n"),
794 zfs_get_name(zhp));
797 cleanup:
798 /* If we mounted the zone BE, unmount it */
799 if (zone_mounted_here) {
800 be_unmount_data_t zone_ud = { 0 };
802 zone_ud.altroot = fld->altroot;
803 zone_ud.force = B_TRUE;
804 if ((err = be_unmount_zone_root(zhp, &zone_ud)) != BE_SUCCESS) {
805 be_print_err(gettext("be_get_legacy_fs: "
806 "failed to unmount zone root %s\n"),
807 zoneroot_ds);
808 if (ret == BE_SUCCESS)
809 ret = err;
813 /* If we mounted this BE, unmount it */
814 if (mounted_here) {
815 if ((err = _be_unmount(be_name, 0)) != BE_SUCCESS) {
816 be_print_err(gettext("be_get_legacy_fs: "
817 "failed to unmount %s\n"), be_name);
818 if (ret == BE_SUCCESS)
819 ret = err;
823 ZFS_CLOSE(zhp);
825 free(fld->altroot);
826 fld->altroot = NULL;
828 return (ret);
832 * Function: be_free_fs_list
833 * Description: Function used to free the members of a be_fs_list_data_t
834 * structure.
835 * Parameters:
836 * fld - be_fs_list_data_t pointer to free.
837 * Returns:
838 * None
839 * Scope:
840 * Semi-private (library wide use only)
842 void
843 be_free_fs_list(be_fs_list_data_t *fld)
845 int i;
847 if (fld == NULL)
848 return;
850 free(fld->altroot);
852 if (fld->fs_list == NULL)
853 return;
855 for (i = 0; i < fld->fs_num; i++)
856 free(fld->fs_list[i]);
858 free(fld->fs_list);
862 * Function: be_get_ds_from_dir(char *dir)
863 * Description: Given a directory path, find the underlying dataset mounted
864 * at that directory path if there is one. The returned name
865 * is allocated in heap storage, so the caller is responsible
866 * for freeing it.
867 * Parameters:
868 * dir - char pointer of directory to find.
869 * Returns:
870 * NULL - if directory is not mounted from a dataset.
871 * name of dataset mounted at dir.
872 * Scope:
873 * Semi-private (library wide use only)
875 char *
876 be_get_ds_from_dir(char *dir)
878 dir_data_t dd = { 0 };
879 char resolved_dir[MAXPATHLEN];
881 /* Make sure length of dir is within the max length */
882 if (dir == NULL || strlen(dir) >= MAXPATHLEN)
883 return (NULL);
885 /* Resolve dir in case its lofs mounted */
886 (void) strlcpy(resolved_dir, dir, sizeof (resolved_dir));
887 z_resolve_lofs(resolved_dir, sizeof (resolved_dir));
889 dd.dir = resolved_dir;
891 (void) zfs_iter_root(g_zfs, be_get_ds_from_dir_callback, &dd);
893 return (dd.ds);
897 * Function: be_make_tmp_mountpoint
898 * Description: This function generates a random temporary mountpoint
899 * and creates that mountpoint directory. It returns the
900 * mountpoint in heap storage, so the caller is responsible
901 * for freeing it.
902 * Parameters:
903 * tmp_mp - reference to pointer of where to store generated
904 * temporary mountpoint.
905 * Returns:
906 * BE_SUCCESS - Success
907 * be_errno_t - Failure
908 * Scope:
909 * Semi-private (library wide use only)
912 be_make_tmp_mountpoint(char **tmp_mp)
914 int err = 0;
916 if ((*tmp_mp = (char *)calloc(1, sizeof (BE_TMP_MNTPNT) + 1)) == NULL) {
917 be_print_err(gettext("be_make_tmp_mountpoint: "
918 "malloc failed\n"));
919 return (BE_ERR_NOMEM);
921 (void) strlcpy(*tmp_mp, BE_TMP_MNTPNT, sizeof (BE_TMP_MNTPNT) + 1);
922 if (mkdtemp(*tmp_mp) == NULL) {
923 err = errno;
924 be_print_err(gettext("be_make_tmp_mountpoint: mkdtemp() failed "
925 "for %s: %s\n"), *tmp_mp, strerror(err));
926 free(*tmp_mp);
927 *tmp_mp = NULL;
928 return (errno_to_be_err(err));
931 return (BE_SUCCESS);
935 * Function: be_mount_pool
936 * Description: This function determines if the pool's datase is mounted
937 * and if not it is used to mount the pool's dataset. The
938 * function returns the current mountpoint if we are able
939 * to mount the dataset.
940 * Parameters:
941 * zhp - handle to the pool's dataset
942 * tmp_mntpnt - The temporary mountpoint that the pool's
943 * dataset is mounted on. This is set only
944 * if the attempt to mount the dataset at it's
945 * set mountpoint fails, and we've used a
946 * temporary mount point for this dataset. It
947 * is expected that the caller will free this
948 * memory.
949 * orig_mntpnt - The original mountpoint for the pool. If a
950 * temporary mount point was needed this will
951 * be used to reset the mountpoint property to
952 * it's original mountpoint. It is expected that
953 * the caller will free this memory.
954 * pool_mounted - This flag indicates that the pool was mounted
955 * in this function.
956 * Returns:
957 * BE_SUCCESS - Success
958 * be_errno_t - Failure
959 * Scope:
960 * Semi-private (library wide use only)
963 be_mount_pool(
964 zfs_handle_t *zhp,
965 char **tmp_mntpnt,
966 char **orig_mntpnt,
967 boolean_t *pool_mounted)
970 char mountpoint[MAXPATHLEN];
971 int ret = 0;
973 *tmp_mntpnt = NULL;
974 *orig_mntpnt = NULL;
975 *pool_mounted = B_FALSE;
977 if (!zfs_is_mounted(zhp, NULL)) {
978 if (zfs_mount(zhp, NULL, 0) != 0) {
979 if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mountpoint,
980 sizeof (mountpoint), NULL, NULL, 0, B_FALSE) != 0) {
981 be_print_err(gettext("be_mount_pool: failed to "
982 "get mountpoint of (%s): %s\n"),
983 zfs_get_name(zhp),
984 libzfs_error_description(g_zfs));
985 return (zfs_err_to_be_err(g_zfs));
987 if ((*orig_mntpnt = strdup(mountpoint)) == NULL) {
988 be_print_err(gettext("be_mount_pool: memory "
989 "allocation failed\n"));
990 return (BE_ERR_NOMEM);
993 * attempt to mount on a temp mountpoint
995 if ((ret = be_make_tmp_mountpoint(tmp_mntpnt))
996 != BE_SUCCESS) {
997 be_print_err(gettext("be_mount_pool: failed "
998 "to make temporary mountpoint\n"));
999 free(*orig_mntpnt);
1000 *orig_mntpnt = NULL;
1001 return (ret);
1004 if (zfs_prop_set(zhp,
1005 zfs_prop_to_name(ZFS_PROP_MOUNTPOINT),
1006 *tmp_mntpnt) != 0) {
1007 be_print_err(gettext("be_mount_pool: failed "
1008 "to set mountpoint of pool dataset %s to "
1009 "%s: %s\n"), zfs_get_name(zhp),
1010 *orig_mntpnt,
1011 libzfs_error_description(g_zfs));
1012 free(*tmp_mntpnt);
1013 free(*orig_mntpnt);
1014 *orig_mntpnt = NULL;
1015 *tmp_mntpnt = NULL;
1016 return (zfs_err_to_be_err(g_zfs));
1019 if (zfs_mount(zhp, NULL, 0) != 0) {
1020 be_print_err(gettext("be_mount_pool: failed "
1021 "to mount dataset %s at %s: %s\n"),
1022 zfs_get_name(zhp), *tmp_mntpnt,
1023 libzfs_error_description(g_zfs));
1024 ret = zfs_err_to_be_err(g_zfs);
1025 if (zfs_prop_set(zhp,
1026 zfs_prop_to_name(ZFS_PROP_MOUNTPOINT),
1027 mountpoint) != 0) {
1028 be_print_err(gettext("be_mount_pool: "
1029 "failed to set mountpoint of pool "
1030 "dataset %s to %s: %s\n"),
1031 zfs_get_name(zhp), *tmp_mntpnt,
1032 libzfs_error_description(g_zfs));
1034 free(*tmp_mntpnt);
1035 free(*orig_mntpnt);
1036 *orig_mntpnt = NULL;
1037 *tmp_mntpnt = NULL;
1038 return (ret);
1041 *pool_mounted = B_TRUE;
1044 return (BE_SUCCESS);
1048 * Function: be_unmount_pool
1049 * Description: This function is used to unmount the pool's dataset if we
1050 * mounted it previously using be_mount_pool().
1051 * Parameters:
1052 * zhp - handle to the pool's dataset
1053 * tmp_mntpnt - If a temprary mount point was used this will
1054 * be set. Since this was created in be_mount_pool
1055 * we will need to clean it up here.
1056 * orig_mntpnt - The original mountpoint for the pool. This is
1057 * used to set the dataset mountpoint property
1058 * back to it's original value in the case where a
1059 * temporary mountpoint was used.
1060 * Returns:
1061 * BE_SUCCESS - Success
1062 * be_errno_t - Failure
1063 * Scope:
1064 * Semi-private (library wide use only)
1067 be_unmount_pool(
1068 zfs_handle_t *zhp,
1069 char *tmp_mntpnt,
1070 char *orig_mntpnt)
1072 if (zfs_unmount(zhp, NULL, 0) != 0) {
1073 be_print_err(gettext("be_unmount_pool: failed to "
1074 "unmount pool (%s): %s\n"), zfs_get_name(zhp),
1075 libzfs_error_description(g_zfs));
1076 return (zfs_err_to_be_err(g_zfs));
1078 if (orig_mntpnt != NULL) {
1079 if (tmp_mntpnt != NULL &&
1080 strcmp(orig_mntpnt, tmp_mntpnt) != 0) {
1081 (void) rmdir(tmp_mntpnt);
1083 if (zfs_prop_set(zhp,
1084 zfs_prop_to_name(ZFS_PROP_MOUNTPOINT),
1085 orig_mntpnt) != 0) {
1086 be_print_err(gettext("be_unmount_pool: failed "
1087 "to set the mountpoint for dataset (%s) to "
1088 "%s: %s\n"), zfs_get_name(zhp), orig_mntpnt,
1089 libzfs_error_description(g_zfs));
1090 return (zfs_err_to_be_err(g_zfs));
1094 return (BE_SUCCESS);
1097 /* ******************************************************************** */
1098 /* Private Functions */
1099 /* ******************************************************************** */
1102 * Function: be_mount_callback
1103 * Description: Callback function used to iterate through all of a BE's
1104 * subordinate file systems and to mount them accordingly.
1105 * Parameters:
1106 * zhp - zfs_handle_t pointer to current file system being
1107 * processed.
1108 * data - pointer to the altroot of where to mount BE.
1109 * Returns:
1110 * 0 - Success
1111 * be_errno_t - Failure
1112 * Scope:
1113 * Private
1115 static int
1116 be_mount_callback(zfs_handle_t *zhp, void *data)
1118 zprop_source_t sourcetype;
1119 const char *fs_name = zfs_get_name(zhp);
1120 char source[ZFS_MAXNAMELEN];
1121 char *altroot = data;
1122 char zhp_mountpoint[MAXPATHLEN];
1123 char mountpoint[MAXPATHLEN];
1124 int ret = 0;
1126 /* Get dataset's mountpoint and source values */
1127 if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, zhp_mountpoint,
1128 sizeof (zhp_mountpoint), &sourcetype, source, sizeof (source),
1129 B_FALSE) != 0) {
1130 be_print_err(gettext("be_mount_callback: failed to "
1131 "get mountpoint and sourcetype for %s\n"),
1132 fs_name);
1133 ZFS_CLOSE(zhp);
1134 return (BE_ERR_ZFS);
1138 * Set this filesystem's 'canmount' property to 'noauto' just incase
1139 * it's been set 'on'. We do this so that when we change its
1140 * mountpoint zfs won't immediately try to mount it.
1142 if (zfs_prop_set(zhp, zfs_prop_to_name(ZFS_PROP_CANMOUNT), "noauto")) {
1143 be_print_err(gettext("be_mount_callback: failed to "
1144 "set canmount to 'noauto' (%s)\n"), fs_name);
1145 ZFS_CLOSE(zhp);
1146 return (BE_ERR_ZFS);
1150 * If the mountpoint is none, there's nothing to do, goto next.
1151 * If the mountpoint is legacy, legacy mount it with mount(2).
1152 * If the mountpoint is inherited, its mountpoint should
1153 * already be set. If it's not, then explicitly fix-up
1154 * the mountpoint now by appending its explicitly set
1155 * mountpoint value to the BE mountpoint.
1157 if (strcmp(zhp_mountpoint, ZFS_MOUNTPOINT_NONE) == 0) {
1158 goto next;
1159 } else if (strcmp(zhp_mountpoint, ZFS_MOUNTPOINT_LEGACY) == 0) {
1161 * If the mountpoint is set to 'legacy', we need to
1162 * dig into this BE's vfstab to figure out where to
1163 * mount it, and just mount it via mount(2).
1165 if (get_mountpoint_from_vfstab(altroot, fs_name,
1166 mountpoint, sizeof (mountpoint), B_TRUE) == BE_SUCCESS) {
1168 /* Legacy mount the file system */
1169 if (mount(fs_name, mountpoint, MS_DATA,
1170 MNTTYPE_ZFS, NULL, 0, NULL, 0) != 0) {
1171 be_print_err(
1172 gettext("be_mount_callback: "
1173 "failed to mount %s on %s\n"),
1174 fs_name, mountpoint);
1176 } else {
1177 be_print_err(
1178 gettext("be_mount_callback: "
1179 "no entry for %s in vfstab, "
1180 "skipping ...\n"), fs_name);
1183 goto next;
1185 } else if (sourcetype & ZPROP_SRC_INHERITED) {
1187 * If the mountpoint is inherited, its parent should have
1188 * already been processed so its current mountpoint value
1189 * is what its mountpoint ought to be.
1191 (void) strlcpy(mountpoint, zhp_mountpoint, sizeof (mountpoint));
1192 } else if (sourcetype & ZPROP_SRC_LOCAL) {
1194 * Else process dataset with explicitly set mountpoint.
1196 (void) snprintf(mountpoint, sizeof (mountpoint),
1197 "%s%s", altroot, zhp_mountpoint);
1199 /* Set the new mountpoint for the dataset */
1200 if (zfs_prop_set(zhp,
1201 zfs_prop_to_name(ZFS_PROP_MOUNTPOINT),
1202 mountpoint)) {
1203 be_print_err(gettext("be_mount_callback: "
1204 "failed to set mountpoint for %s to "
1205 "%s\n"), fs_name, mountpoint);
1206 ZFS_CLOSE(zhp);
1207 return (BE_ERR_ZFS);
1209 } else {
1210 be_print_err(gettext("be_mount_callback: "
1211 "mountpoint sourcetype of %s is %d, skipping ...\n"),
1212 fs_name, sourcetype);
1214 goto next;
1217 /* Mount this filesystem */
1218 if (zfs_mount(zhp, NULL, 0) != 0) {
1219 be_print_err(gettext("be_mount_callback: failed to "
1220 "mount dataset %s at %s: %s\n"), fs_name, mountpoint,
1221 libzfs_error_description(g_zfs));
1223 * Set this filesystem's 'mountpoint' property back to what
1224 * it was
1226 if (sourcetype & ZPROP_SRC_LOCAL &&
1227 strcmp(zhp_mountpoint, ZFS_MOUNTPOINT_LEGACY) != 0) {
1228 (void) zfs_prop_set(zhp,
1229 zfs_prop_to_name(ZFS_PROP_MOUNTPOINT),
1230 zhp_mountpoint);
1233 ZFS_CLOSE(zhp);
1234 return (BE_ERR_MOUNT);
1237 next:
1238 /* Iterate through this dataset's children and mount them */
1239 if ((ret = zfs_iter_filesystems(zhp, be_mount_callback,
1240 altroot)) != 0) {
1241 ZFS_CLOSE(zhp);
1242 return (ret);
1246 ZFS_CLOSE(zhp);
1247 return (0);
1251 * Function: be_unmount_callback
1252 * Description: Callback function used to iterate through all of a BE's
1253 * subordinate file systems and to unmount them.
1254 * Parameters:
1255 * zhp - zfs_handle_t pointer to current file system being
1256 * processed.
1257 * data - pointer to the mountpoint of where BE is mounted.
1258 * Returns:
1259 * 0 - Success
1260 * be_errno_t - Failure
1261 * Scope:
1262 * Private
1264 static int
1265 be_unmount_callback(zfs_handle_t *zhp, void *data)
1267 be_unmount_data_t *ud = data;
1268 zprop_source_t sourcetype;
1269 const char *fs_name = zfs_get_name(zhp);
1270 char source[ZFS_MAXNAMELEN];
1271 char mountpoint[MAXPATHLEN];
1272 char *zhp_mountpoint;
1273 int ret = 0;
1275 /* Iterate down this dataset's children first */
1276 if (zfs_iter_filesystems(zhp, be_unmount_callback, ud)) {
1277 ret = BE_ERR_UMOUNT;
1278 goto done;
1281 /* Is dataset even mounted ? */
1282 if (!zfs_is_mounted(zhp, NULL))
1283 goto done;
1285 /* Unmount this file system */
1286 if (zfs_unmount(zhp, NULL, ud->force ? MS_FORCE : 0) != 0) {
1287 be_print_err(gettext("be_unmount_callback: "
1288 "failed to unmount %s: %s\n"), fs_name,
1289 libzfs_error_description(g_zfs));
1290 ret = zfs_err_to_be_err(g_zfs);
1291 goto done;
1294 /* Get dataset's current mountpoint and source value */
1295 if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mountpoint,
1296 sizeof (mountpoint), &sourcetype, source, sizeof (source),
1297 B_FALSE) != 0) {
1298 be_print_err(gettext("be_unmount_callback: "
1299 "failed to get mountpoint and sourcetype for %s: %s\n"),
1300 fs_name, libzfs_error_description(g_zfs));
1301 ret = zfs_err_to_be_err(g_zfs);
1302 goto done;
1305 if (sourcetype & ZPROP_SRC_INHERITED) {
1307 * If the mountpoint is inherited we don't need to
1308 * do anything. When its parent gets processed
1309 * its mountpoint will be set accordingly.
1311 goto done;
1312 } else if (sourcetype & ZPROP_SRC_LOCAL) {
1314 if (strcmp(mountpoint, ZFS_MOUNTPOINT_LEGACY) == 0) {
1316 * If the mountpoint is set to 'legacy', its already
1317 * been unmounted (from above call to zfs_unmount), and
1318 * we don't need to do anything else with it.
1320 goto done;
1322 } else {
1324 * Else process dataset with explicitly set mountpoint.
1328 * Get this dataset's mountpoint relative to
1329 * the BE's mountpoint.
1331 if ((strncmp(mountpoint, ud->altroot,
1332 strlen(ud->altroot)) == 0) &&
1333 (mountpoint[strlen(ud->altroot)] == '/')) {
1335 zhp_mountpoint = mountpoint +
1336 strlen(ud->altroot);
1338 /* Set this dataset's mountpoint value */
1339 if (zfs_prop_set(zhp,
1340 zfs_prop_to_name(ZFS_PROP_MOUNTPOINT),
1341 zhp_mountpoint)) {
1342 be_print_err(
1343 gettext("be_unmount_callback: "
1344 "failed to set mountpoint for "
1345 "%s to %s: %s\n"), fs_name,
1346 zhp_mountpoint,
1347 libzfs_error_description(g_zfs));
1348 ret = zfs_err_to_be_err(g_zfs);
1350 } else {
1351 be_print_err(
1352 gettext("be_unmount_callback: "
1353 "%s not mounted under BE's altroot %s, "
1354 "skipping ...\n"), fs_name, ud->altroot);
1356 * fs_name is mounted but not under the
1357 * root for this BE.
1359 ret = BE_ERR_INVALMOUNTPOINT;
1362 } else {
1363 be_print_err(gettext("be_unmount_callback: "
1364 "mountpoint sourcetype of %s is %d, skipping ...\n"),
1365 fs_name, sourcetype);
1366 ret = BE_ERR_ZFS;
1369 done:
1370 /* Set this filesystem's 'canmount' property to 'noauto' */
1371 if (zfs_prop_set(zhp, zfs_prop_to_name(ZFS_PROP_CANMOUNT), "noauto")) {
1372 be_print_err(gettext("be_unmount_callback: "
1373 "failed to set canmount to 'noauto' (%s)\n"), fs_name);
1374 if (ret == 0)
1375 ret = BE_ERR_ZFS;
1378 ZFS_CLOSE(zhp);
1379 return (ret);
1383 * Function: be_get_legacy_fs_callback
1384 * Description: The callback function is used to iterate through all
1385 * non-shared file systems of a BE, finding ones that have
1386 * a legacy mountpoint and an entry in the BE's vfstab.
1387 * It adds these file systems to the callback data.
1388 * Parameters:
1389 * zhp - zfs_handle_t pointer to current file system being
1390 * processed.
1391 * data - be_fs_list_data_t pointer
1392 * Returns:
1393 * 0 - Success
1394 * be_errno_t - Failure
1395 * Scope:
1396 * Private
1398 static int
1399 be_get_legacy_fs_callback(zfs_handle_t *zhp, void *data)
1401 be_fs_list_data_t *fld = data;
1402 const char *fs_name = zfs_get_name(zhp);
1403 char zhp_mountpoint[MAXPATHLEN];
1404 char mountpoint[MAXPATHLEN];
1405 int ret = 0;
1407 /* Get this dataset's mountpoint property */
1408 if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, zhp_mountpoint,
1409 sizeof (zhp_mountpoint), NULL, NULL, 0, B_FALSE) != 0) {
1410 be_print_err(gettext("be_get_legacy_fs_callback: "
1411 "failed to get mountpoint for %s: %s\n"),
1412 fs_name, libzfs_error_description(g_zfs));
1413 ret = zfs_err_to_be_err(g_zfs);
1414 ZFS_CLOSE(zhp);
1415 return (ret);
1419 * If mountpoint is legacy, try to get its mountpoint from this BE's
1420 * vfstab. If it exists in the vfstab, add this file system to the
1421 * callback data.
1423 if (strcmp(zhp_mountpoint, ZFS_MOUNTPOINT_LEGACY) == 0) {
1424 if (get_mountpoint_from_vfstab(fld->altroot, fs_name,
1425 mountpoint, sizeof (mountpoint), B_FALSE) != BE_SUCCESS) {
1426 be_print_err(gettext("be_get_legacy_fs_callback: "
1427 "no entry for %s in vfstab, "
1428 "skipping ...\n"), fs_name);
1430 goto next;
1433 /* Record file system into the callback data. */
1434 if (add_to_fs_list(fld, zfs_get_name(zhp)) != BE_SUCCESS) {
1435 be_print_err(gettext("be_get_legacy_fs_callback: "
1436 "failed to add %s to fs list\n"), mountpoint);
1437 ZFS_CLOSE(zhp);
1438 return (BE_ERR_NOMEM);
1442 next:
1443 /* Iterate through this dataset's children file systems */
1444 if ((ret = zfs_iter_filesystems(zhp, be_get_legacy_fs_callback,
1445 fld)) != 0) {
1446 ZFS_CLOSE(zhp);
1447 return (ret);
1449 ZFS_CLOSE(zhp);
1450 return (0);
1454 * Function: add_to_fs_list
1455 * Description: Function used to add a file system to the fs_list array in
1456 * a be_fs_list_data_t structure.
1457 * Parameters:
1458 * fld - be_fs_list_data_t pointer
1459 * fs - file system to add
1460 * Returns:
1461 * BE_SUCCESS - Success
1462 * 1 - Failure
1463 * Scope:
1464 * Private
1466 static int
1467 add_to_fs_list(be_fs_list_data_t *fld, const char *fs)
1469 if (fld == NULL || fs == NULL)
1470 return (1);
1472 if ((fld->fs_list = (char **)realloc(fld->fs_list,
1473 sizeof (char *)*(fld->fs_num + 1))) == NULL) {
1474 be_print_err(gettext("add_to_fs_list: "
1475 "memory allocation failed\n"));
1476 return (1);
1479 if ((fld->fs_list[fld->fs_num++] = strdup(fs)) == NULL) {
1480 be_print_err(gettext("add_to_fs_list: "
1481 "memory allocation failed\n"));
1482 return (1);
1485 return (BE_SUCCESS);
1489 * Function: zpool_shared_fs_callback
1490 * Description: Callback function used to iterate through all existing pools
1491 * to find and mount all shared filesystems. This function
1492 * processes the pool's "pool data" dataset, then uses
1493 * iter_shared_fs_callback to iterate through the pool's
1494 * datasets.
1495 * Parameters:
1496 * zlp - zpool_handle_t pointer to the current pool being
1497 * looked at.
1498 * data - be_mount_data_t pointer
1499 * Returns:
1500 * 0 - Success
1501 * be_errno_t - Failure
1502 * Scope:
1503 * Private
1505 static int
1506 zpool_shared_fs_callback(zpool_handle_t *zlp, void *data)
1508 be_mount_data_t *md = data;
1509 zfs_handle_t *zhp = NULL;
1510 const char *zpool = zpool_get_name(zlp);
1511 int ret = 0;
1514 * Get handle to pool's "pool data" dataset
1516 if ((zhp = zfs_open(g_zfs, zpool, ZFS_TYPE_FILESYSTEM)) == NULL) {
1517 be_print_err(gettext("zpool_shared_fs: "
1518 "failed to open pool dataset %s: %s\n"), zpool,
1519 libzfs_error_description(g_zfs));
1520 ret = zfs_err_to_be_err(g_zfs);
1521 zpool_close(zlp);
1522 return (ret);
1525 /* Process this pool's "pool data" dataset */
1526 (void) loopback_mount_shared_fs(zhp, md);
1528 /* Interate through this pool's children */
1529 (void) zfs_iter_filesystems(zhp, iter_shared_fs_callback, md);
1531 ZFS_CLOSE(zhp);
1532 zpool_close(zlp);
1534 return (0);
1538 * Function: iter_shared_fs_callback
1539 * Description: Callback function used to iterate through a pool's datasets
1540 * to find and mount all shared filesystems. It makes sure to
1541 * find the BE container dataset of the pool, if it exists, and
1542 * does not process and iterate down that path.
1544 * Note - This function iterates linearly down the
1545 * hierarchical dataset paths and mounts things as it goes
1546 * along. It does not make sure that something deeper down
1547 * a dataset path has an interim mountpoint for something
1548 * processed earlier.
1550 * Parameters:
1551 * zhp - zfs_handle_t pointer to the current dataset being
1552 * processed.
1553 * data - be_mount_data_t pointer
1554 * Returns:
1555 * 0 - Success
1556 * be_errno_t - Failure
1557 * Scope:
1558 * Private
1560 static int
1561 iter_shared_fs_callback(zfs_handle_t *zhp, void *data)
1563 be_mount_data_t *md = data;
1564 const char *name = zfs_get_name(zhp);
1565 char container_ds[MAXPATHLEN];
1566 char tmp_name[MAXPATHLEN];
1567 char *pool;
1569 /* Get the pool's name */
1570 (void) strlcpy(tmp_name, name, sizeof (tmp_name));
1571 pool = strtok(tmp_name, "/");
1573 if (pool) {
1574 /* Get the name of this pool's container dataset */
1575 be_make_container_ds(pool, container_ds,
1576 sizeof (container_ds));
1579 * If what we're processing is this pool's BE container
1580 * dataset, skip it.
1582 if (strcmp(name, container_ds) == 0) {
1583 ZFS_CLOSE(zhp);
1584 return (0);
1586 } else {
1587 /* Getting the pool name failed, return error */
1588 be_print_err(gettext("iter_shared_fs_callback: "
1589 "failed to get pool name from %s\n"), name);
1590 ZFS_CLOSE(zhp);
1591 return (BE_ERR_POOL_NOENT);
1594 /* Mount this shared filesystem */
1595 (void) loopback_mount_shared_fs(zhp, md);
1597 /* Iterate this dataset's children file systems */
1598 (void) zfs_iter_filesystems(zhp, iter_shared_fs_callback, md);
1599 ZFS_CLOSE(zhp);
1601 return (0);
1605 * Function: loopback_mount_shared_fs
1606 * Description: This function loopback mounts a file system into the altroot
1607 * area of the BE being mounted. Since these are shared file
1608 * systems, they are expected to be already mounted for the
1609 * current BE, and this function just loopback mounts them into
1610 * the BE mountpoint. If they are not mounted for the current
1611 * live system, they are skipped and not mounted into the BE
1612 * we're mounting.
1613 * Parameters:
1614 * zhp - zfs_handle_t pointer to the dataset to loopback mount
1615 * md - be_mount_data_t pointer
1616 * Returns:
1617 * BE_SUCCESS - Success
1618 * be_errno_t - Failure
1619 * Scope:
1620 * Private
1622 static int
1623 loopback_mount_shared_fs(zfs_handle_t *zhp, be_mount_data_t *md)
1625 char zhp_mountpoint[MAXPATHLEN];
1626 char mountpoint[MAXPATHLEN];
1627 char *mp = NULL;
1628 char optstr[MAX_MNTOPT_STR];
1629 int mflag = MS_OPTIONSTR;
1630 int err;
1633 * Check if file system is currently mounted and not delegated
1634 * to a non-global zone (if we're in the global zone)
1636 if (zfs_is_mounted(zhp, &mp) && (getzoneid() != GLOBAL_ZONEID ||
1637 !zfs_prop_get_int(zhp, ZFS_PROP_ZONED))) {
1639 * If we didn't get a mountpoint from the zfs_is_mounted call,
1640 * get it from the mountpoint property.
1642 if (mp == NULL) {
1643 if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT,
1644 zhp_mountpoint, sizeof (zhp_mountpoint), NULL,
1645 NULL, 0, B_FALSE) != 0) {
1646 be_print_err(
1647 gettext("loopback_mount_shared_fs: "
1648 "failed to get mountpoint property\n"));
1649 return (BE_ERR_ZFS);
1651 } else {
1652 (void) strlcpy(zhp_mountpoint, mp,
1653 sizeof (zhp_mountpoint));
1654 free(mp);
1657 (void) snprintf(mountpoint, sizeof (mountpoint), "%s%s",
1658 md->altroot, zhp_mountpoint);
1660 /* Mount it read-only if read-write was not requested */
1661 if (!md->shared_rw) {
1662 mflag |= MS_RDONLY;
1665 /* Add the "nosub" option to the mount options string */
1666 (void) strlcpy(optstr, MNTOPT_NOSUB, sizeof (optstr));
1668 /* Loopback mount this dataset at the altroot */
1669 if (mount(zhp_mountpoint, mountpoint, mflag, MNTTYPE_LOFS,
1670 NULL, 0, optstr, sizeof (optstr)) != 0) {
1671 err = errno;
1672 be_print_err(gettext("loopback_mount_shared_fs: "
1673 "failed to loopback mount %s at %s: %s\n"),
1674 zhp_mountpoint, mountpoint, strerror(err));
1675 return (BE_ERR_MOUNT);
1679 return (BE_SUCCESS);
1683 * Function: loopback_mount_zonepath
1684 * Description: This function loopback mounts a zonepath into the altroot
1685 * area of the BE being mounted. Since these are shared file
1686 * systems, they are expected to be already mounted for the
1687 * current BE, and this function just loopback mounts them into
1688 * the BE mountpoint.
1689 * Parameters:
1690 * zonepath - pointer to zone path in the current BE
1691 * md - be_mount_data_t pointer
1692 * Returns:
1693 * BE_SUCCESS - Success
1694 * be_errno_t - Failure
1695 * Scope:
1696 * Private
1698 static int
1699 loopback_mount_zonepath(const char *zonepath, be_mount_data_t *md)
1701 FILE *fp = (FILE *)NULL;
1702 struct stat st;
1703 char *p;
1704 char *p1;
1705 char *parent_dir;
1706 struct extmnttab extmtab;
1707 dev_t dev = NODEV;
1708 char *parentmnt;
1709 char alt_parentmnt[MAXPATHLEN];
1710 struct mnttab mntref;
1711 char altzonepath[MAXPATHLEN];
1712 char optstr[MAX_MNTOPT_STR];
1713 int mflag = MS_OPTIONSTR;
1714 int ret;
1715 int err;
1717 fp = fopen(MNTTAB, "r");
1718 if (fp == NULL) {
1719 err = errno;
1720 be_print_err(gettext("loopback_mount_zonepath: "
1721 "failed to open /etc/mnttab\n"));
1722 return (errno_to_be_err(err));
1726 * before attempting the loopback mount of zonepath under altroot,
1727 * we need to make sure that all intermediate file systems in the
1728 * zone path are also mounted under altroot
1731 /* get the parent directory for zonepath */
1732 p = strrchr(zonepath, '/');
1733 if (p != NULL && p != zonepath) {
1734 if ((parent_dir = (char *)calloc(sizeof (char),
1735 p - zonepath + 1)) == NULL) {
1736 ret = BE_ERR_NOMEM;
1737 goto done;
1739 (void) strlcpy(parent_dir, zonepath, p - zonepath + 1);
1740 if (stat(parent_dir, &st) < 0) {
1741 ret = errno_to_be_err(errno);
1742 be_print_err(gettext("loopback_mount_zonepath: "
1743 "failed to stat %s"),
1744 parent_dir);
1745 free(parent_dir);
1746 goto done;
1748 free(parent_dir);
1751 * After the above stat call, st.st_dev contains ID of the
1752 * device over which parent dir resides.
1753 * Now, search mnttab and find mount point of parent dir device.
1756 resetmnttab(fp);
1757 while (getextmntent(fp, &extmtab, sizeof (extmtab)) == 0) {
1758 dev = makedev(extmtab.mnt_major, extmtab.mnt_minor);
1759 if (st.st_dev == dev && strcmp(extmtab.mnt_fstype,
1760 MNTTYPE_ZFS) == 0) {
1761 p1 = strchr(extmtab.mnt_special, '/');
1762 if (p1 == NULL || strncmp(p1 + 1,
1763 BE_CONTAINER_DS_NAME, 4) != 0 ||
1764 (*(p1 + 5) != '/' && *(p1 + 5) != '\0')) {
1766 * if parent dir is in a shared file
1767 * system, check whether it is already
1768 * loopback mounted under altroot or
1769 * not. It would have been mounted
1770 * already under altroot if it is in
1771 * a non-shared filesystem.
1773 parentmnt = strdup(extmtab.mnt_mountp);
1774 (void) snprintf(alt_parentmnt,
1775 sizeof (alt_parentmnt), "%s%s",
1776 md->altroot, parentmnt);
1777 mntref.mnt_mountp = alt_parentmnt;
1778 mntref.mnt_special = parentmnt;
1779 mntref.mnt_fstype = MNTTYPE_LOFS;
1780 mntref.mnt_mntopts = NULL;
1781 mntref.mnt_time = NULL;
1782 resetmnttab(fp);
1783 if (getmntany(fp, (struct mnttab *)
1784 &extmtab, &mntref) != 0) {
1785 ret = loopback_mount_zonepath(
1786 parentmnt, md);
1787 if (ret != BE_SUCCESS) {
1788 free(parentmnt);
1789 goto done;
1792 free(parentmnt);
1794 break;
1800 if (!md->shared_rw) {
1801 mflag |= MS_RDONLY;
1804 (void) snprintf(altzonepath, sizeof (altzonepath), "%s%s",
1805 md->altroot, zonepath);
1807 /* Add the "nosub" option to the mount options string */
1808 (void) strlcpy(optstr, MNTOPT_NOSUB, sizeof (optstr));
1810 /* Loopback mount this dataset at the altroot */
1811 if (mount(zonepath, altzonepath, mflag, MNTTYPE_LOFS,
1812 NULL, 0, optstr, sizeof (optstr)) != 0) {
1813 err = errno;
1814 be_print_err(gettext("loopback_mount_zonepath: "
1815 "failed to loopback mount %s at %s: %s\n"),
1816 zonepath, altzonepath, strerror(err));
1817 ret = BE_ERR_MOUNT;
1818 goto done;
1820 ret = BE_SUCCESS;
1822 done :
1823 (void) fclose(fp);
1824 return (ret);
1828 * Function: unmount_shared_fs
1829 * Description: This function iterates through the mnttab and finds all
1830 * loopback mount entries that reside within the altroot of
1831 * where the BE is mounted, and unmounts it.
1832 * Parameters:
1833 * ud - be_unmount_data_t pointer
1834 * Returns:
1835 * BE_SUCCESS - Success
1836 * be_errno_t - Failure
1837 * Scope:
1838 * Private
1840 static int
1841 unmount_shared_fs(be_unmount_data_t *ud)
1843 FILE *fp = NULL;
1844 struct mnttab *table = NULL;
1845 struct mnttab ent;
1846 struct mnttab *entp = NULL;
1847 size_t size = 0;
1848 int read_chunk = 32;
1849 int i;
1850 int altroot_len;
1851 int err = 0;
1853 errno = 0;
1855 /* Read in the mnttab into a table */
1856 if ((fp = fopen(MNTTAB, "r")) == NULL) {
1857 err = errno;
1858 be_print_err(gettext("unmount_shared_fs: "
1859 "failed to open mnttab\n"));
1860 return (errno_to_be_err(err));
1863 while (getmntent(fp, &ent) == 0) {
1864 if (size % read_chunk == 0) {
1865 table = (struct mnttab *)realloc(table,
1866 (size + read_chunk) * sizeof (ent));
1868 entp = &table[size++];
1871 * Copy over the current mnttab entry into our table,
1872 * copying only the fields that we care about.
1874 (void) memset(entp, 0, sizeof (*entp));
1875 if ((entp->mnt_mountp = strdup(ent.mnt_mountp)) == NULL ||
1876 (entp->mnt_fstype = strdup(ent.mnt_fstype)) == NULL) {
1877 be_print_err(gettext("unmount_shared_fs: "
1878 "memory allocation failed\n"));
1879 return (BE_ERR_NOMEM);
1882 (void) fclose(fp);
1885 * Process the mnttab entries in reverse order, looking for
1886 * loopback mount entries mounted under our altroot.
1888 altroot_len = strlen(ud->altroot);
1889 for (i = size; i > 0; i--) {
1890 entp = &table[i - 1];
1892 /* If not of type lofs, skip */
1893 if (strcmp(entp->mnt_fstype, MNTTYPE_LOFS) != 0)
1894 continue;
1896 /* If inside the altroot, unmount it */
1897 if (strncmp(entp->mnt_mountp, ud->altroot, altroot_len) == 0 &&
1898 entp->mnt_mountp[altroot_len] == '/') {
1899 if (umount(entp->mnt_mountp) != 0) {
1900 err = errno;
1901 if (err == EBUSY) {
1902 (void) sleep(1);
1903 err = errno = 0;
1904 if (umount(entp->mnt_mountp) != 0)
1905 err = errno;
1907 if (err != 0) {
1908 be_print_err(gettext(
1909 "unmount_shared_fs: "
1910 "failed to unmount shared file "
1911 "system %s: %s\n"),
1912 entp->mnt_mountp, strerror(err));
1913 return (errno_to_be_err(err));
1919 return (BE_SUCCESS);
1923 * Function: get_mountpoint_from_vfstab
1924 * Description: This function digs into the vfstab in the given altroot,
1925 * and searches for an entry for the fs passed in. If found,
1926 * it returns the mountpoint of that fs in the mountpoint
1927 * buffer passed in. If the get_alt_mountpoint flag is set,
1928 * it returns the mountpoint with the altroot prepended.
1929 * Parameters:
1930 * altroot - pointer to the alternate root location
1931 * fs - pointer to the file system name to look for in the
1932 * vfstab in altroot
1933 * mountpoint - pointer to buffer of where the mountpoint of
1934 * fs will be returned.
1935 * size_mp - size of mountpoint argument
1936 * get_alt_mountpoint - flag to indicate whether or not the
1937 * mountpoint should be populated with the altroot
1938 * prepended.
1939 * Returns:
1940 * BE_SUCCESS - Success
1941 * 1 - Failure
1942 * Scope:
1943 * Private
1945 static int
1946 get_mountpoint_from_vfstab(char *altroot, const char *fs, char *mountpoint,
1947 size_t size_mp, boolean_t get_alt_mountpoint)
1949 struct vfstab vp;
1950 FILE *fp = NULL;
1951 char alt_vfstab[MAXPATHLEN];
1953 /* Generate path to alternate root vfstab */
1954 (void) snprintf(alt_vfstab, sizeof (alt_vfstab), "%s/etc/vfstab",
1955 altroot);
1957 /* Open alternate root vfstab */
1958 if ((fp = fopen(alt_vfstab, "r")) == NULL) {
1959 be_print_err(gettext("get_mountpoint_from_vfstab: "
1960 "failed to open vfstab (%s)\n"), alt_vfstab);
1961 return (1);
1964 if (getvfsspec(fp, &vp, (char *)fs) == 0) {
1966 * Found entry for fs, grab its mountpoint.
1967 * If the flag to prepend the altroot into the mountpoint
1968 * is set, prepend it. Otherwise, just return the mountpoint.
1970 if (get_alt_mountpoint) {
1971 (void) snprintf(mountpoint, size_mp, "%s%s", altroot,
1972 vp.vfs_mountp);
1973 } else {
1974 (void) strlcpy(mountpoint, vp.vfs_mountp, size_mp);
1976 } else {
1977 (void) fclose(fp);
1978 return (1);
1981 (void) fclose(fp);
1983 return (BE_SUCCESS);
1987 * Function: fix_mountpoint_callback
1988 * Description: This callback function is used to iterate through a BE's
1989 * children filesystems to check if its mountpoint is currently
1990 * set to be mounted at some specified altroot. If so, fix it by
1991 * removing altroot from the beginning of its mountpoint.
1993 * Note - There's no way to tell if a child filesystem's
1994 * mountpoint isn't broken, and just happens to begin with
1995 * the altroot we're looking for. In this case, this function
1996 * will errantly remove the altroot portion from the beginning
1997 * of this filesystem's mountpoint.
1999 * Parameters:
2000 * zhp - zfs_handle_t pointer to filesystem being processed.
2001 * data - altroot of where BE is to be mounted.
2002 * Returns:
2003 * 0 - Success
2004 * be_errno_t - Failure
2005 * Scope:
2006 * Private
2008 static int
2009 fix_mountpoint_callback(zfs_handle_t *zhp, void *data)
2011 zprop_source_t sourcetype;
2012 char source[ZFS_MAXNAMELEN];
2013 char mountpoint[MAXPATHLEN];
2014 char *zhp_mountpoint = NULL;
2015 char *altroot = data;
2016 int ret = 0;
2018 /* Get dataset's mountpoint and source values */
2019 if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mountpoint,
2020 sizeof (mountpoint), &sourcetype, source, sizeof (source),
2021 B_FALSE) != 0) {
2022 be_print_err(gettext("fix_mountpoint_callback: "
2023 "failed to get mountpoint and sourcetype for %s\n"),
2024 zfs_get_name(zhp));
2025 ZFS_CLOSE(zhp);
2026 return (BE_ERR_ZFS);
2030 * If the mountpoint is not inherited and the mountpoint is not
2031 * 'legacy', this file system potentially needs its mountpoint
2032 * fixed.
2034 if (!(sourcetype & ZPROP_SRC_INHERITED) &&
2035 strcmp(mountpoint, ZFS_MOUNTPOINT_LEGACY) != 0) {
2038 * Check if this file system's current mountpoint is
2039 * under the altroot we're fixing it against.
2041 if (strncmp(mountpoint, altroot, strlen(altroot)) == 0 &&
2042 mountpoint[strlen(altroot)] == '/') {
2045 * Get this dataset's mountpoint relative to the
2046 * altroot.
2048 zhp_mountpoint = mountpoint + strlen(altroot);
2050 /* Fix this dataset's mountpoint value */
2051 if (zfs_prop_set(zhp,
2052 zfs_prop_to_name(ZFS_PROP_MOUNTPOINT),
2053 zhp_mountpoint)) {
2054 be_print_err(gettext("fix_mountpoint_callback: "
2055 "failed to set mountpoint for %s to "
2056 "%s: %s\n"), zfs_get_name(zhp),
2057 zhp_mountpoint,
2058 libzfs_error_description(g_zfs));
2059 ret = zfs_err_to_be_err(g_zfs);
2060 ZFS_CLOSE(zhp);
2061 return (ret);
2066 /* Iterate through this dataset's children and fix them */
2067 if ((ret = zfs_iter_filesystems(zhp, fix_mountpoint_callback,
2068 altroot)) != 0) {
2069 ZFS_CLOSE(zhp);
2070 return (ret);
2074 ZFS_CLOSE(zhp);
2075 return (0);
2079 * Function: be_mount_root
2080 * Description: This function mounts the root dataset of a BE at the
2081 * specified altroot.
2082 * Parameters:
2083 * zhp - zfs_handle_t pointer to root dataset of a BE that is
2084 * to be mounted at altroot.
2085 * altroot - location of where to mount the BE root.
2086 * Return:
2087 * BE_SUCCESS - Success
2088 * be_errno_t - Failure
2089 * Scope:
2090 * Private
2092 static int
2093 be_mount_root(zfs_handle_t *zhp, char *altroot)
2095 char mountpoint[MAXPATHLEN];
2097 /* Get mountpoint property of dataset */
2098 if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mountpoint,
2099 sizeof (mountpoint), NULL, NULL, 0, B_FALSE) != 0) {
2100 be_print_err(gettext("be_mount_root: failed to "
2101 "get mountpoint property for %s: %s\n"), zfs_get_name(zhp),
2102 libzfs_error_description(g_zfs));
2103 return (zfs_err_to_be_err(g_zfs));
2107 * Set the canmount property for the BE's root dataset to 'noauto' just
2108 * in case it's been set to 'on'. We do this so that when we change its
2109 * mountpoint, zfs won't immediately try to mount it.
2111 if (zfs_prop_set(zhp, zfs_prop_to_name(ZFS_PROP_CANMOUNT), "noauto")
2112 != 0) {
2113 be_print_err(gettext("be_mount_root: failed to "
2114 "set canmount property to 'noauto' (%s): %s\n"),
2115 zfs_get_name(zhp), libzfs_error_description(g_zfs));
2116 return (zfs_err_to_be_err(g_zfs));
2119 /* Set mountpoint for BE's root filesystem */
2120 if (zfs_prop_set(zhp, zfs_prop_to_name(ZFS_PROP_MOUNTPOINT), altroot)
2121 != 0) {
2122 be_print_err(gettext("be_mount_root: failed to "
2123 "set mountpoint of %s to %s: %s\n"),
2124 zfs_get_name(zhp), altroot,
2125 libzfs_error_description(g_zfs));
2126 return (zfs_err_to_be_err(g_zfs));
2129 /* Mount the BE's root filesystem */
2130 if (zfs_mount(zhp, NULL, 0) != 0) {
2131 be_print_err(gettext("be_mount_root: failed to "
2132 "mount dataset %s at %s: %s\n"), zfs_get_name(zhp),
2133 altroot, libzfs_error_description(g_zfs));
2135 * Set this BE's root filesystem 'mountpoint' property
2136 * back to what it was before.
2138 (void) zfs_prop_set(zhp, zfs_prop_to_name(ZFS_PROP_MOUNTPOINT),
2139 mountpoint);
2140 return (zfs_err_to_be_err(g_zfs));
2143 return (BE_SUCCESS);
2147 * Function: be_unmount_root
2148 * Description: This function unmounts the root dataset of a BE, but before
2149 * unmounting, it looks at the BE's vfstab to determine
2150 * if the root dataset mountpoint should be left as 'legacy'
2151 * or '/'. If the vfstab contains an entry for this root
2152 * dataset with a mountpoint of '/', it sets the mountpoint
2153 * property to 'legacy'.
2155 * Parameters:
2156 * zhp - zfs_handle_t pointer of the BE root dataset that
2157 * is currently mounted.
2158 * ud - be_unmount_data_t pointer providing unmount data
2159 * for the given BE root dataset.
2160 * Returns:
2161 * BE_SUCCESS - Success
2162 * be_errno_t - Failure
2163 * Scope:
2164 * Private
2166 static int
2167 be_unmount_root(zfs_handle_t *zhp, be_unmount_data_t *ud)
2169 char mountpoint[MAXPATHLEN];
2170 boolean_t is_legacy = B_FALSE;
2172 /* See if this is a legacy mounted root */
2173 if (get_mountpoint_from_vfstab(ud->altroot, zfs_get_name(zhp),
2174 mountpoint, sizeof (mountpoint), B_FALSE) == BE_SUCCESS &&
2175 strcmp(mountpoint, "/") == 0) {
2176 is_legacy = B_TRUE;
2179 /* Unmount the dataset */
2180 if (zfs_unmount(zhp, NULL, ud->force ? MS_FORCE : 0) != 0) {
2181 be_print_err(gettext("be_unmount_root: failed to "
2182 "unmount BE root dataset %s: %s\n"), zfs_get_name(zhp),
2183 libzfs_error_description(g_zfs));
2184 return (zfs_err_to_be_err(g_zfs));
2187 /* Set canmount property for this BE's root filesystem to noauto */
2188 if (zfs_prop_set(zhp, zfs_prop_to_name(ZFS_PROP_CANMOUNT), "noauto")
2189 != 0) {
2190 be_print_err(gettext("be_unmount_root: failed to "
2191 "set canmount property for %s to 'noauto': %s\n"),
2192 zfs_get_name(zhp), libzfs_error_description(g_zfs));
2193 return (zfs_err_to_be_err(g_zfs));
2197 * Set mountpoint for BE's root dataset back to '/', or 'legacy'
2198 * if its a legacy mounted root.
2200 if (zfs_prop_set(zhp, zfs_prop_to_name(ZFS_PROP_MOUNTPOINT),
2201 is_legacy ? ZFS_MOUNTPOINT_LEGACY : "/") != 0) {
2202 be_print_err(gettext("be_unmount_root: failed to "
2203 "set mountpoint of %s to %s\n"), zfs_get_name(zhp),
2204 is_legacy ? ZFS_MOUNTPOINT_LEGACY : "/");
2205 return (zfs_err_to_be_err(g_zfs));
2208 return (BE_SUCCESS);
2212 * Function: fix_mountpoint
2213 * Description: This function checks the mountpoint of an unmounted BE to make
2214 * sure that it is set to either 'legacy' or '/'. If it's not,
2215 * then we're in a situation where an unmounted BE has some random
2216 * mountpoint set for it. (This could happen if the system was
2217 * rebooted while an inactive BE was mounted). This function
2218 * attempts to fix its mountpoints.
2219 * Parameters:
2220 * zhp - zfs_handle_t pointer to root dataset of the BE
2221 * whose mountpoint needs to be checked.
2222 * Return:
2223 * BE_SUCCESS - Success
2224 * be_errno_t - Failure
2225 * Scope:
2226 * Private
2228 static int
2229 fix_mountpoint(zfs_handle_t *zhp)
2231 be_unmount_data_t ud = { 0 };
2232 char *altroot = NULL;
2233 char mountpoint[MAXPATHLEN];
2234 int ret = BE_SUCCESS;
2237 * Record what this BE's root dataset mountpoint property is currently
2238 * set to.
2240 if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mountpoint,
2241 sizeof (mountpoint), NULL, NULL, 0, B_FALSE) != 0) {
2242 be_print_err(gettext("fix_mountpoint: failed to get "
2243 "mountpoint property of (%s): %s\n"), zfs_get_name(zhp),
2244 libzfs_error_description(g_zfs));
2245 return (BE_ERR_ZFS);
2249 * If the root dataset mountpoint is set to 'legacy' or '/', we're okay.
2251 if (strcmp(mountpoint, ZFS_MOUNTPOINT_LEGACY) == 0 ||
2252 strcmp(mountpoint, "/") == 0) {
2253 return (BE_SUCCESS);
2257 * Iterate through this BE's children datasets and fix
2258 * them if they need fixing.
2260 if (zfs_iter_filesystems(zhp, fix_mountpoint_callback, mountpoint)
2261 != 0) {
2262 return (BE_ERR_ZFS);
2266 * The process of mounting and unmounting the root file system
2267 * will fix its mountpoint to correctly be either 'legacy' or '/'
2268 * since be_unmount_root will do the right thing by looking at
2269 * its vfstab.
2272 /* Generate temporary altroot to mount the root file system */
2273 if ((ret = be_make_tmp_mountpoint(&altroot)) != BE_SUCCESS) {
2274 be_print_err(gettext("fix_mountpoint: failed to "
2275 "make temporary mountpoint\n"));
2276 return (ret);
2279 /* Mount and unmount the root. */
2280 if ((ret = be_mount_root(zhp, altroot)) != BE_SUCCESS) {
2281 be_print_err(gettext("fix_mountpoint: failed to "
2282 "mount BE root file system\n"));
2283 goto cleanup;
2285 ud.altroot = altroot;
2286 if ((ret = be_unmount_root(zhp, &ud)) != BE_SUCCESS) {
2287 be_print_err(gettext("fix_mountpoint: failed to "
2288 "unmount BE root file system\n"));
2289 goto cleanup;
2292 cleanup:
2293 free(altroot);
2295 return (ret);
2299 * Function: be_mount_zones
2300 * Description: This function finds all supported non-global zones in the
2301 * given global BE and mounts them with respect to where the
2302 * global BE is currently mounted. The global BE datasets
2303 * (including its shared datasets) are expected to already
2304 * be mounted.
2305 * Parameters:
2306 * be_zhp - zfs_handle_t pointer to the root dataset of the
2307 * global BE.
2308 * md - be_mount_data_t pointer to data for global BE.
2309 * Returns:
2310 * BE_SUCCESS - Success
2311 * be_errno_t - Failure
2312 * Scope:
2313 * Private
2315 static int
2316 be_mount_zones(zfs_handle_t *be_zhp, be_mount_data_t *md)
2318 zoneBrandList_t *brands = NULL;
2319 zoneList_t zlst = NULL;
2320 char *zonename = NULL;
2321 char *zonepath = NULL;
2322 char *zonepath_ds = NULL;
2323 int k;
2324 int ret = BE_SUCCESS;
2326 z_set_zone_root(md->altroot);
2328 if ((brands = be_get_supported_brandlist()) == NULL) {
2329 be_print_err(gettext("be_mount_zones: "
2330 "no supported brands\n"));
2331 return (BE_SUCCESS);
2334 zlst = z_get_nonglobal_zone_list_by_brand(brands);
2335 if (zlst == NULL) {
2336 z_free_brand_list(brands);
2337 return (BE_SUCCESS);
2340 for (k = 0; (zonename = z_zlist_get_zonename(zlst, k)) != NULL; k++) {
2341 if (z_zlist_get_current_state(zlst, k) ==
2342 ZONE_STATE_INSTALLED) {
2343 zonepath = z_zlist_get_zonepath(zlst, k);
2346 * Get the dataset of this zonepath in current BE.
2347 * If its not a dataset, skip it.
2349 if ((zonepath_ds = be_get_ds_from_dir(zonepath))
2350 == NULL)
2351 continue;
2354 * Check if this zone is supported based on
2355 * the dataset of its zonepath
2357 if (!be_zone_supported(zonepath_ds)) {
2358 free(zonepath_ds);
2359 zonepath_ds = NULL;
2360 continue;
2364 * if BE's shared file systems are already mounted,
2365 * zone path dataset would have already been lofs
2366 * mounted under altroot. Otherwise, we need to do
2367 * it here.
2369 if (!md->shared_fs) {
2370 ret = loopback_mount_zonepath(zonepath, md);
2371 if (ret != BE_SUCCESS)
2372 goto done;
2376 /* Mount this zone */
2377 ret = be_mount_one_zone(be_zhp, md, zonename,
2378 zonepath, zonepath_ds);
2380 free(zonepath_ds);
2381 zonepath_ds = NULL;
2383 if (ret != BE_SUCCESS) {
2384 be_print_err(gettext("be_mount_zones: "
2385 "failed to mount zone %s under "
2386 "altroot %s\n"), zonename, md->altroot);
2387 goto done;
2392 done:
2393 z_free_brand_list(brands);
2394 z_free_zone_list(zlst);
2396 * libinstzones caches mnttab and uses cached version for resolving lofs
2397 * mounts when we call z_resolve_lofs. It creates the cached version
2398 * when the first call to z_resolve_lofs happens. So, library's cached
2399 * mnttab doesn't contain entries for lofs mounts created in the above
2400 * loop. Because of this, subsequent calls to z_resolve_lofs would fail
2401 * to resolve these lofs mounts. So, here we destroy library's cached
2402 * mnttab to force its recreation when the next call to z_resolve_lofs
2403 * happens.
2405 z_destroyMountTable();
2406 return (ret);
2410 * Function: be_unmount_zones
2411 * Description: This function finds all supported non-global zones in the
2412 * given mounted global BE and unmounts them.
2413 * Parameters:
2414 * ud - unmount_data_t pointer data for the global BE.
2415 * Returns:
2416 * BE_SUCCESS - Success
2417 * be_errno_t - Failure
2418 * Scope:
2419 * Private
2421 static int
2422 be_unmount_zones(be_unmount_data_t *ud)
2424 zoneBrandList_t *brands = NULL;
2425 zoneList_t zlst = NULL;
2426 char *zonename = NULL;
2427 char *zonepath = NULL;
2428 char alt_zonepath[MAXPATHLEN];
2429 char *zonepath_ds = NULL;
2430 int k;
2431 int ret = BE_SUCCESS;
2433 z_set_zone_root(ud->altroot);
2435 if ((brands = be_get_supported_brandlist()) == NULL) {
2436 be_print_err(gettext("be_unmount_zones: "
2437 "no supported brands\n"));
2438 return (BE_SUCCESS);
2441 zlst = z_get_nonglobal_zone_list_by_brand(brands);
2442 if (zlst == NULL) {
2443 z_free_brand_list(brands);
2444 return (BE_SUCCESS);
2447 for (k = 0; (zonename = z_zlist_get_zonename(zlst, k)) != NULL; k++) {
2448 if (z_zlist_get_current_state(zlst, k) ==
2449 ZONE_STATE_INSTALLED) {
2450 zonepath = z_zlist_get_zonepath(zlst, k);
2452 /* Build zone's zonepath wrt the global BE altroot */
2453 (void) snprintf(alt_zonepath, sizeof (alt_zonepath),
2454 "%s%s", ud->altroot, zonepath);
2457 * Get the dataset of this zonepath. If its not
2458 * a dataset, skip it.
2460 if ((zonepath_ds = be_get_ds_from_dir(alt_zonepath))
2461 == NULL)
2462 continue;
2465 * Check if this zone is supported based on the
2466 * dataset of its zonepath.
2468 if (!be_zone_supported(zonepath_ds)) {
2469 free(zonepath_ds);
2470 zonepath_ds = NULL;
2471 continue;
2474 /* Unmount this zone */
2475 ret = be_unmount_one_zone(ud, zonename, zonepath,
2476 zonepath_ds);
2478 free(zonepath_ds);
2479 zonepath_ds = NULL;
2481 if (ret != BE_SUCCESS) {
2482 be_print_err(gettext("be_unmount_zones:"
2483 " failed to unmount zone %s from "
2484 "altroot %s\n"), zonename, ud->altroot);
2485 goto done;
2490 done:
2491 z_free_brand_list(brands);
2492 z_free_zone_list(zlst);
2493 return (ret);
2497 * Function: be_mount_one_zone
2498 * Description: This function is called to mount one zone for a given
2499 * global BE.
2500 * Parameters:
2501 * be_zhp - zfs_handle_t pointer to the root dataset of the
2502 * global BE
2503 * md - be_mount_data_t pointer to data for global BE
2504 * zonename - name of zone to mount
2505 * zonepath - zonepath of zone to mount
2506 * zonepath_ds - dataset for the zonepath
2507 * Returns:
2508 * BE_SUCCESS - Success
2509 * be_errno_t - Failure
2510 * Scope:
2511 * Private
2513 static int
2514 be_mount_one_zone(zfs_handle_t *be_zhp, be_mount_data_t *md, char *zonename,
2515 char *zonepath, char *zonepath_ds)
2517 be_mount_data_t zone_md = { 0 };
2518 zfs_handle_t *zone_zhp = NULL;
2519 char zone_altroot[MAXPATHLEN];
2520 char zoneroot[MAXPATHLEN];
2521 char zoneroot_ds[MAXPATHLEN];
2522 int ret = BE_SUCCESS;
2524 /* Find the active zone root dataset for this zone for this BE */
2525 if ((ret = be_find_active_zone_root(be_zhp, zonepath_ds, zoneroot_ds,
2526 sizeof (zoneroot_ds))) == BE_ERR_ZONE_NO_ACTIVE_ROOT) {
2527 be_print_err(gettext("be_mount_one_zone: did not "
2528 "find active zone root for zone %s, skipping ...\n"),
2529 zonename);
2530 return (BE_SUCCESS);
2531 } else if (ret != BE_SUCCESS) {
2532 be_print_err(gettext("be_mount_one_zone: failed to "
2533 "find active zone root for zone %s\n"), zonename);
2534 return (ret);
2537 /* Get handle to active zoneroot dataset */
2538 if ((zone_zhp = zfs_open(g_zfs, zoneroot_ds, ZFS_TYPE_FILESYSTEM))
2539 == NULL) {
2540 be_print_err(gettext("be_mount_one_zone: failed to "
2541 "open zone root dataset (%s): %s\n"), zoneroot_ds,
2542 libzfs_error_description(g_zfs));
2543 return (zfs_err_to_be_err(g_zfs));
2546 /* Generate string for zone's altroot path */
2547 be_make_zoneroot(zonepath, zoneroot, sizeof (zoneroot));
2548 (void) strlcpy(zone_altroot, md->altroot, sizeof (zone_altroot));
2549 (void) strlcat(zone_altroot, zoneroot, sizeof (zone_altroot));
2551 /* Build mount_data for the zone */
2552 zone_md.altroot = zone_altroot;
2553 zone_md.shared_fs = md->shared_fs;
2554 zone_md.shared_rw = md->shared_rw;
2556 /* Mount the zone's root file system */
2557 if ((ret = be_mount_zone_root(zone_zhp, &zone_md)) != BE_SUCCESS) {
2558 be_print_err(gettext("be_mount_one_zone: failed to "
2559 "mount zone root file system at %s\n"), zone_altroot);
2560 goto done;
2563 /* Iterate through zone's children filesystems */
2564 if ((ret = zfs_iter_filesystems(zone_zhp, be_mount_callback,
2565 zone_altroot)) != 0) {
2566 be_print_err(gettext("be_mount_one_zone: failed to "
2567 "mount zone subordinate file systems at %s\n"),
2568 zone_altroot);
2569 goto done;
2572 /* TODO: Mount all shared file systems for this zone */
2574 done:
2575 ZFS_CLOSE(zone_zhp);
2576 return (ret);
2580 * Function: be_unmount_one_zone
2581 * Description: This function unmount one zone for a give global BE.
2582 * Parameters:
2583 * ud - be_unmount_data_t pointer to data for global BE
2584 * zonename - name of zone to unmount
2585 * zonepath - zonepath of the zone to unmount
2586 * zonepath_ds - dataset for the zonepath
2587 * Returns:
2588 * BE_SUCCESS - Success
2589 * be_errno_t - Failure
2590 * Scope:
2591 * Private
2593 static int
2594 be_unmount_one_zone(be_unmount_data_t *ud, char *zonename, char *zonepath,
2595 char *zonepath_ds)
2597 be_unmount_data_t zone_ud = { 0 };
2598 zfs_handle_t *zone_zhp = NULL;
2599 char zone_altroot[MAXPATHLEN];
2600 char zoneroot[MAXPATHLEN];
2601 char zoneroot_ds[MAXPATHLEN];
2602 int ret = BE_SUCCESS;
2604 /* Generate string for zone's alternate root path */
2605 be_make_zoneroot(zonepath, zoneroot, sizeof (zoneroot));
2606 (void) strlcpy(zone_altroot, ud->altroot, sizeof (zone_altroot));
2607 (void) strlcat(zone_altroot, zoneroot, sizeof (zone_altroot));
2609 /* Build be_unmount_data for zone */
2610 zone_ud.altroot = zone_altroot;
2611 zone_ud.force = ud->force;
2613 /* Find the mounted zone root dataset for this zone for this BE */
2614 if ((ret = be_find_mounted_zone_root(zone_altroot, zonepath_ds,
2615 zoneroot_ds, sizeof (zoneroot_ds))) == BE_ERR_NO_MOUNTED_ZONE) {
2616 be_print_err(gettext("be_unmount_one_zone: did not "
2617 "find any zone root mounted for zone %s\n"), zonename);
2618 return (BE_SUCCESS);
2619 } else if (ret != BE_SUCCESS) {
2620 be_print_err(gettext("be_unmount_one_zone: failed to "
2621 "find mounted zone root for zone %s\n"), zonename);
2622 return (ret);
2625 /* Get handle to zoneroot dataset mounted for this BE */
2626 if ((zone_zhp = zfs_open(g_zfs, zoneroot_ds, ZFS_TYPE_FILESYSTEM))
2627 == NULL) {
2628 be_print_err(gettext("be_unmount_one_zone: failed to "
2629 "open mounted zone root dataset (%s): %s\n"), zoneroot_ds,
2630 libzfs_error_description(g_zfs));
2631 return (zfs_err_to_be_err(g_zfs));
2634 /* TODO: Unmount all shared file systems for this zone */
2636 /* Iterate through zone's children filesystems and unmount them */
2637 if ((ret = zfs_iter_filesystems(zone_zhp, be_unmount_callback,
2638 &zone_ud)) != 0) {
2639 be_print_err(gettext("be_unmount_one_zone: failed to "
2640 "unmount zone subordinate file systems at %s\n"),
2641 zone_altroot);
2642 goto done;
2645 /* Unmount the zone's root filesystem */
2646 if ((ret = be_unmount_zone_root(zone_zhp, &zone_ud)) != BE_SUCCESS) {
2647 be_print_err(gettext("be_unmount_one_zone: failed to "
2648 "unmount zone root file system at %s\n"), zone_altroot);
2649 goto done;
2652 done:
2653 ZFS_CLOSE(zone_zhp);
2654 return (ret);
2658 * Function: be_get_ds_from_dir_callback
2659 * Description: This is a callback function used to iterate all datasets
2660 * to find the one that is currently mounted at the directory
2661 * being searched for. If matched, the name of the dataset is
2662 * returned in heap storage, so the caller is responsible for
2663 * freeing it.
2664 * Parameters:
2665 * zhp - zfs_handle_t pointer to current dataset being processed.
2666 * data - dir_data_t pointer providing name of directory being
2667 * searched for.
2668 * Returns:
2669 * 1 - This dataset is mounted at directory being searched for.
2670 * 0 - This dataset is not mounted at directory being searched for.
2671 * Scope:
2672 * Private
2674 static int
2675 be_get_ds_from_dir_callback(zfs_handle_t *zhp, void *data)
2677 dir_data_t *dd = data;
2678 char *mp = NULL;
2679 int zret = 0;
2681 if (zfs_get_type(zhp) != ZFS_TYPE_FILESYSTEM) {
2682 ZFS_CLOSE(zhp);
2683 return (0);
2686 if (zfs_is_mounted(zhp, &mp) && mp != NULL &&
2687 strcmp(mp, dd->dir) == 0) {
2688 if ((dd->ds = strdup(zfs_get_name(zhp))) == NULL) {
2689 be_print_err(gettext("be_get_ds_from_dir_callback: "
2690 "memory allocation failed\n"));
2691 ZFS_CLOSE(zhp);
2692 return (0);
2694 ZFS_CLOSE(zhp);
2695 return (1);
2698 zret = zfs_iter_filesystems(zhp, be_get_ds_from_dir_callback, dd);
2700 ZFS_CLOSE(zhp);
2702 return (zret);