8322 nl: misleading-indentation
[unleashed/tickless.git] / usr / src / lib / libbe / common / be_mount.c
blob98c861fca46573cb9dbe4514081e564a09ad6c90
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));
917 z_resolve_lofs(resolved_dir, sizeof (resolved_dir));
919 dd.dir = resolved_dir;
921 (void) zfs_iter_root(g_zfs, be_get_ds_from_dir_callback, &dd);
923 return (dd.ds);
927 * Function: be_make_tmp_mountpoint
928 * Description: This function generates a random temporary mountpoint
929 * and creates that mountpoint directory. It returns the
930 * mountpoint in heap storage, so the caller is responsible
931 * for freeing it.
932 * Parameters:
933 * tmp_mp - reference to pointer of where to store generated
934 * temporary mountpoint.
935 * Returns:
936 * BE_SUCCESS - Success
937 * be_errno_t - Failure
938 * Scope:
939 * Semi-private (library wide use only)
942 be_make_tmp_mountpoint(char **tmp_mp)
944 int err = 0;
946 if ((*tmp_mp = (char *)calloc(1, sizeof (BE_TMP_MNTPNT) + 1)) == NULL) {
947 be_print_err(gettext("be_make_tmp_mountpoint: "
948 "malloc failed\n"));
949 return (BE_ERR_NOMEM);
951 (void) strlcpy(*tmp_mp, BE_TMP_MNTPNT, sizeof (BE_TMP_MNTPNT) + 1);
952 if (mkdtemp(*tmp_mp) == NULL) {
953 err = errno;
954 be_print_err(gettext("be_make_tmp_mountpoint: mkdtemp() failed "
955 "for %s: %s\n"), *tmp_mp, strerror(err));
956 free(*tmp_mp);
957 *tmp_mp = NULL;
958 return (errno_to_be_err(err));
961 return (BE_SUCCESS);
965 * Function: be_mount_pool
966 * Description: This function determines if the pool's datase is mounted
967 * and if not it is used to mount the pool's dataset. The
968 * function returns the current mountpoint if we are able
969 * to mount the dataset.
970 * Parameters:
971 * zhp - handle to the pool's dataset
972 * tmp_mntpnt - The temporary mountpoint that the pool's
973 * dataset is mounted on. This is set only
974 * if the attempt to mount the dataset at it's
975 * set mountpoint fails, and we've used a
976 * temporary mount point for this dataset. It
977 * is expected that the caller will free this
978 * memory.
979 * orig_mntpnt - The original mountpoint for the pool. If a
980 * temporary mount point was needed this will
981 * be used to reset the mountpoint property to
982 * it's original mountpoint. It is expected that
983 * the caller will free this memory.
984 * pool_mounted - This flag indicates that the pool was mounted
985 * in this function.
986 * Returns:
987 * BE_SUCCESS - Success
988 * be_errno_t - Failure
989 * Scope:
990 * Semi-private (library wide use only)
993 be_mount_pool(
994 zfs_handle_t *zhp,
995 char **tmp_mntpnt,
996 char **orig_mntpnt,
997 boolean_t *pool_mounted)
1000 char mountpoint[MAXPATHLEN];
1001 int ret = 0;
1003 *tmp_mntpnt = NULL;
1004 *orig_mntpnt = NULL;
1005 *pool_mounted = B_FALSE;
1007 if (!zfs_is_mounted(zhp, NULL)) {
1008 if (zfs_mount(zhp, NULL, 0) != 0) {
1009 if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mountpoint,
1010 sizeof (mountpoint), NULL, NULL, 0, B_FALSE) != 0) {
1011 be_print_err(gettext("be_mount_pool: failed to "
1012 "get mountpoint of (%s): %s\n"),
1013 zfs_get_name(zhp),
1014 libzfs_error_description(g_zfs));
1015 return (zfs_err_to_be_err(g_zfs));
1017 if ((*orig_mntpnt = strdup(mountpoint)) == NULL) {
1018 be_print_err(gettext("be_mount_pool: memory "
1019 "allocation failed\n"));
1020 return (BE_ERR_NOMEM);
1023 * attempt to mount on a temp mountpoint
1025 if ((ret = be_make_tmp_mountpoint(tmp_mntpnt))
1026 != BE_SUCCESS) {
1027 be_print_err(gettext("be_mount_pool: failed "
1028 "to make temporary mountpoint\n"));
1029 free(*orig_mntpnt);
1030 *orig_mntpnt = NULL;
1031 return (ret);
1034 if (zfs_prop_set(zhp,
1035 zfs_prop_to_name(ZFS_PROP_MOUNTPOINT),
1036 *tmp_mntpnt) != 0) {
1037 be_print_err(gettext("be_mount_pool: failed "
1038 "to set mountpoint of pool dataset %s to "
1039 "%s: %s\n"), zfs_get_name(zhp),
1040 *orig_mntpnt,
1041 libzfs_error_description(g_zfs));
1042 free(*tmp_mntpnt);
1043 free(*orig_mntpnt);
1044 *orig_mntpnt = NULL;
1045 *tmp_mntpnt = NULL;
1046 return (zfs_err_to_be_err(g_zfs));
1049 if (zfs_mount(zhp, NULL, 0) != 0) {
1050 be_print_err(gettext("be_mount_pool: failed "
1051 "to mount dataset %s at %s: %s\n"),
1052 zfs_get_name(zhp), *tmp_mntpnt,
1053 libzfs_error_description(g_zfs));
1054 ret = zfs_err_to_be_err(g_zfs);
1055 if (zfs_prop_set(zhp,
1056 zfs_prop_to_name(ZFS_PROP_MOUNTPOINT),
1057 mountpoint) != 0) {
1058 be_print_err(gettext("be_mount_pool: "
1059 "failed to set mountpoint of pool "
1060 "dataset %s to %s: %s\n"),
1061 zfs_get_name(zhp), *tmp_mntpnt,
1062 libzfs_error_description(g_zfs));
1064 free(*tmp_mntpnt);
1065 free(*orig_mntpnt);
1066 *orig_mntpnt = NULL;
1067 *tmp_mntpnt = NULL;
1068 return (ret);
1071 *pool_mounted = B_TRUE;
1074 return (BE_SUCCESS);
1078 * Function: be_unmount_pool
1079 * Description: This function is used to unmount the pool's dataset if we
1080 * mounted it previously using be_mount_pool().
1081 * Parameters:
1082 * zhp - handle to the pool's dataset
1083 * tmp_mntpnt - If a temprary mount point was used this will
1084 * be set. Since this was created in be_mount_pool
1085 * we will need to clean it up here.
1086 * orig_mntpnt - The original mountpoint for the pool. This is
1087 * used to set the dataset mountpoint property
1088 * back to it's original value in the case where a
1089 * temporary mountpoint was used.
1090 * Returns:
1091 * BE_SUCCESS - Success
1092 * be_errno_t - Failure
1093 * Scope:
1094 * Semi-private (library wide use only)
1097 be_unmount_pool(
1098 zfs_handle_t *zhp,
1099 char *tmp_mntpnt,
1100 char *orig_mntpnt)
1102 if (zfs_unmount(zhp, NULL, 0) != 0) {
1103 be_print_err(gettext("be_unmount_pool: failed to "
1104 "unmount pool (%s): %s\n"), zfs_get_name(zhp),
1105 libzfs_error_description(g_zfs));
1106 return (zfs_err_to_be_err(g_zfs));
1108 if (orig_mntpnt != NULL) {
1109 if (tmp_mntpnt != NULL &&
1110 strcmp(orig_mntpnt, tmp_mntpnt) != 0) {
1111 (void) rmdir(tmp_mntpnt);
1113 if (zfs_prop_set(zhp,
1114 zfs_prop_to_name(ZFS_PROP_MOUNTPOINT),
1115 orig_mntpnt) != 0) {
1116 be_print_err(gettext("be_unmount_pool: failed "
1117 "to set the mountpoint for dataset (%s) to "
1118 "%s: %s\n"), zfs_get_name(zhp), orig_mntpnt,
1119 libzfs_error_description(g_zfs));
1120 return (zfs_err_to_be_err(g_zfs));
1124 return (BE_SUCCESS);
1127 /* ******************************************************************** */
1128 /* Private Functions */
1129 /* ******************************************************************** */
1132 * Function: be_mount_callback
1133 * Description: Callback function used to iterate through all of a BE's
1134 * subordinate file systems and to mount them accordingly.
1135 * Parameters:
1136 * zhp - zfs_handle_t pointer to current file system being
1137 * processed.
1138 * data - pointer to the altroot of where to mount BE.
1139 * Returns:
1140 * 0 - Success
1141 * be_errno_t - Failure
1142 * Scope:
1143 * Private
1145 static int
1146 be_mount_callback(zfs_handle_t *zhp, void *data)
1148 zprop_source_t sourcetype;
1149 const char *fs_name = zfs_get_name(zhp);
1150 char source[ZFS_MAX_DATASET_NAME_LEN];
1151 char *altroot = data;
1152 char zhp_mountpoint[MAXPATHLEN];
1153 char mountpoint[MAXPATHLEN];
1154 int ret = 0;
1156 /* Get dataset's mountpoint and source values */
1157 if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, zhp_mountpoint,
1158 sizeof (zhp_mountpoint), &sourcetype, source, sizeof (source),
1159 B_FALSE) != 0) {
1160 be_print_err(gettext("be_mount_callback: failed to "
1161 "get mountpoint and sourcetype for %s\n"),
1162 fs_name);
1163 ZFS_CLOSE(zhp);
1164 return (BE_ERR_ZFS);
1168 * Set this filesystem's 'canmount' property to 'noauto' just incase
1169 * it's been set 'on'.
1171 if (zfs_prop_set(zhp, zfs_prop_to_name(ZFS_PROP_CANMOUNT), "noauto")) {
1172 be_print_err(gettext("be_mount_callback: failed to "
1173 "set canmount to 'noauto' (%s)\n"), fs_name);
1174 ZFS_CLOSE(zhp);
1175 return (BE_ERR_ZFS);
1179 * If the mountpoint is none, there's nothing to do, goto next.
1180 * If the mountpoint is legacy, legacy mount it with mount(2).
1181 * If the mountpoint is inherited, its mountpoint should
1182 * already be set. If it's not, then explicitly fix-up
1183 * the mountpoint now by appending its explicitly set
1184 * mountpoint value to the BE mountpoint.
1186 if (strcmp(zhp_mountpoint, ZFS_MOUNTPOINT_NONE) == 0) {
1187 goto next;
1188 } else if (strcmp(zhp_mountpoint, ZFS_MOUNTPOINT_LEGACY) == 0) {
1190 * If the mountpoint is set to 'legacy', we need to
1191 * dig into this BE's vfstab to figure out where to
1192 * mount it, and just mount it via mount(2).
1194 if (get_mountpoint_from_vfstab(altroot, fs_name,
1195 mountpoint, sizeof (mountpoint), B_TRUE) == BE_SUCCESS) {
1197 /* Legacy mount the file system */
1198 if (mount(fs_name, mountpoint, MS_DATA,
1199 MNTTYPE_ZFS, NULL, 0, NULL, 0) != 0) {
1200 be_print_err(
1201 gettext("be_mount_callback: "
1202 "failed to mount %s on %s\n"),
1203 fs_name, mountpoint);
1205 } else {
1206 be_print_err(
1207 gettext("be_mount_callback: "
1208 "no entry for %s in vfstab, "
1209 "skipping ...\n"), fs_name);
1212 goto next;
1214 } else if ((sourcetype & (ZPROP_SRC_INHERITED|ZPROP_SRC_LOCAL)) == 0) {
1216 * Skip dataset if mountpoint is not inherited
1217 * or explicitly set.
1219 be_print_err(gettext("be_mount_callback: "
1220 "mountpoint sourcetype of %s is %d, skipping ...\n"),
1221 fs_name, sourcetype);
1223 goto next;
1226 /* Mount this filesystem */
1227 if (mount_zfs(zhp, altroot) != 0) {
1228 be_print_err(gettext("be_mount_callback: failed to "
1229 "mount dataset %s at %s: %s\n"), fs_name, mountpoint,
1230 strerror(errno));
1231 ZFS_CLOSE(zhp);
1232 return (BE_ERR_MOUNT);
1235 next:
1236 /* Iterate through this dataset's children and mount them */
1237 if ((ret = zfs_iter_filesystems(zhp, be_mount_callback,
1238 altroot)) != 0) {
1239 ZFS_CLOSE(zhp);
1240 return (ret);
1244 ZFS_CLOSE(zhp);
1245 return (0);
1249 * Function: be_unmount_callback
1250 * Description: Callback function used to iterate through all of a BE's
1251 * subordinate file systems and to unmount them.
1252 * Parameters:
1253 * zhp - zfs_handle_t pointer to current file system being
1254 * processed.
1255 * data - pointer to the mountpoint of where BE is mounted.
1256 * Returns:
1257 * 0 - Success
1258 * be_errno_t - Failure
1259 * Scope:
1260 * Private
1262 static int
1263 be_unmount_callback(zfs_handle_t *zhp, void *data)
1265 be_unmount_data_t *ud = data;
1266 zprop_source_t sourcetype;
1267 const char *fs_name = zfs_get_name(zhp);
1268 char source[ZFS_MAX_DATASET_NAME_LEN];
1269 char mountpoint[MAXPATHLEN];
1270 char *zhp_mountpoint;
1271 int ret = 0;
1273 /* Iterate down this dataset's children first */
1274 if (zfs_iter_filesystems(zhp, be_unmount_callback, ud)) {
1275 ret = BE_ERR_UMOUNT;
1276 goto done;
1279 /* Is dataset even mounted ? */
1280 if (!zfs_is_mounted(zhp, NULL))
1281 goto done;
1283 /* Unmount this file system */
1284 if (zfs_unmount(zhp, NULL, ud->force ? MS_FORCE : 0) != 0) {
1285 be_print_err(gettext("be_unmount_callback: "
1286 "failed to unmount %s: %s\n"), fs_name,
1287 libzfs_error_description(g_zfs));
1288 ret = zfs_err_to_be_err(g_zfs);
1289 goto done;
1292 /* Get dataset's current mountpoint and source value */
1293 if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mountpoint,
1294 sizeof (mountpoint), &sourcetype, source, sizeof (source),
1295 B_FALSE) != 0) {
1296 be_print_err(gettext("be_unmount_callback: "
1297 "failed to get mountpoint and sourcetype for %s: %s\n"),
1298 fs_name, libzfs_error_description(g_zfs));
1299 ret = zfs_err_to_be_err(g_zfs);
1300 goto done;
1303 if (sourcetype & ZPROP_SRC_INHERITED) {
1305 * If the mountpoint is inherited we don't need to
1306 * do anything. When its parent gets processed
1307 * its mountpoint will be set accordingly.
1309 goto done;
1310 } else if (sourcetype & ZPROP_SRC_LOCAL) {
1312 if (strcmp(mountpoint, ZFS_MOUNTPOINT_LEGACY) == 0) {
1314 * If the mountpoint is set to 'legacy', its already
1315 * been unmounted (from above call to zfs_unmount), and
1316 * we don't need to do anything else with it.
1318 goto done;
1320 } else {
1322 * Else process dataset with explicitly set mountpoint.
1326 * Get this dataset's mountpoint relative to
1327 * the BE's mountpoint.
1329 if ((strncmp(mountpoint, ud->altroot,
1330 strlen(ud->altroot)) == 0) &&
1331 (mountpoint[strlen(ud->altroot)] == '/')) {
1333 zhp_mountpoint = mountpoint +
1334 strlen(ud->altroot);
1336 /* Set this dataset's mountpoint value */
1337 if (zfs_prop_set(zhp,
1338 zfs_prop_to_name(ZFS_PROP_MOUNTPOINT),
1339 zhp_mountpoint)) {
1340 be_print_err(
1341 gettext("be_unmount_callback: "
1342 "failed to set mountpoint for "
1343 "%s to %s: %s\n"), fs_name,
1344 zhp_mountpoint,
1345 libzfs_error_description(g_zfs));
1346 ret = zfs_err_to_be_err(g_zfs);
1348 } else {
1350 * Nothing to do, mountpoint shouldn't be
1351 * corrected.
1353 goto done;
1356 } else {
1357 be_print_err(gettext("be_unmount_callback: "
1358 "mountpoint sourcetype of %s is %d, skipping ...\n"),
1359 fs_name, sourcetype);
1360 ret = BE_ERR_ZFS;
1363 done:
1364 /* Set this filesystem's 'canmount' property to 'noauto' */
1365 if (zfs_prop_set(zhp, zfs_prop_to_name(ZFS_PROP_CANMOUNT), "noauto")) {
1366 be_print_err(gettext("be_unmount_callback: "
1367 "failed to set canmount to 'noauto' (%s)\n"), fs_name);
1368 if (ret == 0)
1369 ret = BE_ERR_ZFS;
1372 ZFS_CLOSE(zhp);
1373 return (ret);
1377 * Function: be_get_legacy_fs_callback
1378 * Description: The callback function is used to iterate through all
1379 * non-shared file systems of a BE, finding ones that have
1380 * a legacy mountpoint and an entry in the BE's vfstab.
1381 * It adds these file systems to the callback data.
1382 * Parameters:
1383 * zhp - zfs_handle_t pointer to current file system being
1384 * processed.
1385 * data - be_fs_list_data_t pointer
1386 * Returns:
1387 * 0 - Success
1388 * be_errno_t - Failure
1389 * Scope:
1390 * Private
1392 static int
1393 be_get_legacy_fs_callback(zfs_handle_t *zhp, void *data)
1395 be_fs_list_data_t *fld = data;
1396 const char *fs_name = zfs_get_name(zhp);
1397 char zhp_mountpoint[MAXPATHLEN];
1398 char mountpoint[MAXPATHLEN];
1399 int ret = 0;
1401 /* Get this dataset's mountpoint property */
1402 if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, zhp_mountpoint,
1403 sizeof (zhp_mountpoint), NULL, NULL, 0, B_FALSE) != 0) {
1404 be_print_err(gettext("be_get_legacy_fs_callback: "
1405 "failed to get mountpoint for %s: %s\n"),
1406 fs_name, libzfs_error_description(g_zfs));
1407 ret = zfs_err_to_be_err(g_zfs);
1408 ZFS_CLOSE(zhp);
1409 return (ret);
1413 * If mountpoint is legacy, try to get its mountpoint from this BE's
1414 * vfstab. If it exists in the vfstab, add this file system to the
1415 * callback data.
1417 if (strcmp(zhp_mountpoint, ZFS_MOUNTPOINT_LEGACY) == 0) {
1418 if (get_mountpoint_from_vfstab(fld->altroot, fs_name,
1419 mountpoint, sizeof (mountpoint), B_FALSE) != BE_SUCCESS) {
1420 be_print_err(gettext("be_get_legacy_fs_callback: "
1421 "no entry for %s in vfstab, "
1422 "skipping ...\n"), fs_name);
1424 goto next;
1427 /* Record file system into the callback data. */
1428 if (add_to_fs_list(fld, zfs_get_name(zhp)) != BE_SUCCESS) {
1429 be_print_err(gettext("be_get_legacy_fs_callback: "
1430 "failed to add %s to fs list\n"), mountpoint);
1431 ZFS_CLOSE(zhp);
1432 return (BE_ERR_NOMEM);
1436 next:
1437 /* Iterate through this dataset's children file systems */
1438 if ((ret = zfs_iter_filesystems(zhp, be_get_legacy_fs_callback,
1439 fld)) != 0) {
1440 ZFS_CLOSE(zhp);
1441 return (ret);
1443 ZFS_CLOSE(zhp);
1444 return (0);
1448 * Function: add_to_fs_list
1449 * Description: Function used to add a file system to the fs_list array in
1450 * a be_fs_list_data_t structure.
1451 * Parameters:
1452 * fld - be_fs_list_data_t pointer
1453 * fs - file system to add
1454 * Returns:
1455 * BE_SUCCESS - Success
1456 * 1 - Failure
1457 * Scope:
1458 * Private
1460 static int
1461 add_to_fs_list(be_fs_list_data_t *fld, const char *fs)
1463 if (fld == NULL || fs == NULL)
1464 return (1);
1466 if ((fld->fs_list = (char **)realloc(fld->fs_list,
1467 sizeof (char *)*(fld->fs_num + 1))) == NULL) {
1468 be_print_err(gettext("add_to_fs_list: "
1469 "memory allocation failed\n"));
1470 return (1);
1473 if ((fld->fs_list[fld->fs_num++] = strdup(fs)) == NULL) {
1474 be_print_err(gettext("add_to_fs_list: "
1475 "memory allocation failed\n"));
1476 return (1);
1479 return (BE_SUCCESS);
1483 * Function: zpool_shared_fs_callback
1484 * Description: Callback function used to iterate through all existing pools
1485 * to find and mount all shared filesystems. This function
1486 * processes the pool's "pool data" dataset, then uses
1487 * iter_shared_fs_callback to iterate through the pool's
1488 * datasets.
1489 * Parameters:
1490 * zlp - zpool_handle_t pointer to the current pool being
1491 * looked at.
1492 * data - be_mount_data_t pointer
1493 * Returns:
1494 * 0 - Success
1495 * be_errno_t - Failure
1496 * Scope:
1497 * Private
1499 static int
1500 zpool_shared_fs_callback(zpool_handle_t *zlp, void *data)
1502 be_mount_data_t *md = data;
1503 zfs_handle_t *zhp = NULL;
1504 const char *zpool = zpool_get_name(zlp);
1505 int ret = 0;
1508 * Get handle to pool's "pool data" dataset
1510 if ((zhp = zfs_open(g_zfs, zpool, ZFS_TYPE_FILESYSTEM)) == NULL) {
1511 be_print_err(gettext("zpool_shared_fs: "
1512 "failed to open pool dataset %s: %s\n"), zpool,
1513 libzfs_error_description(g_zfs));
1514 ret = zfs_err_to_be_err(g_zfs);
1515 zpool_close(zlp);
1516 return (ret);
1519 /* Process this pool's "pool data" dataset */
1520 (void) loopback_mount_shared_fs(zhp, md);
1522 /* Interate through this pool's children */
1523 (void) zfs_iter_filesystems(zhp, iter_shared_fs_callback, md);
1525 ZFS_CLOSE(zhp);
1526 zpool_close(zlp);
1528 return (0);
1532 * Function: iter_shared_fs_callback
1533 * Description: Callback function used to iterate through a pool's datasets
1534 * to find and mount all shared filesystems. It makes sure to
1535 * find the BE container dataset of the pool, if it exists, and
1536 * does not process and iterate down that path.
1538 * Note - This function iterates linearly down the
1539 * hierarchical dataset paths and mounts things as it goes
1540 * along. It does not make sure that something deeper down
1541 * a dataset path has an interim mountpoint for something
1542 * processed earlier.
1544 * Parameters:
1545 * zhp - zfs_handle_t pointer to the current dataset being
1546 * processed.
1547 * data - be_mount_data_t pointer
1548 * Returns:
1549 * 0 - Success
1550 * be_errno_t - Failure
1551 * Scope:
1552 * Private
1554 static int
1555 iter_shared_fs_callback(zfs_handle_t *zhp, void *data)
1557 be_mount_data_t *md = data;
1558 const char *name = zfs_get_name(zhp);
1559 char container_ds[MAXPATHLEN];
1560 char tmp_name[MAXPATHLEN];
1561 char *pool;
1563 /* Get the pool's name */
1564 (void) strlcpy(tmp_name, name, sizeof (tmp_name));
1565 pool = strtok(tmp_name, "/");
1567 if (pool) {
1568 /* Get the name of this pool's container dataset */
1569 be_make_container_ds(pool, container_ds,
1570 sizeof (container_ds));
1573 * If what we're processing is this pool's BE container
1574 * dataset, skip it.
1576 if (strcmp(name, container_ds) == 0) {
1577 ZFS_CLOSE(zhp);
1578 return (0);
1580 } else {
1581 /* Getting the pool name failed, return error */
1582 be_print_err(gettext("iter_shared_fs_callback: "
1583 "failed to get pool name from %s\n"), name);
1584 ZFS_CLOSE(zhp);
1585 return (BE_ERR_POOL_NOENT);
1588 /* Mount this shared filesystem */
1589 (void) loopback_mount_shared_fs(zhp, md);
1591 /* Iterate this dataset's children file systems */
1592 (void) zfs_iter_filesystems(zhp, iter_shared_fs_callback, md);
1593 ZFS_CLOSE(zhp);
1595 return (0);
1599 * Function: loopback_mount_shared_fs
1600 * Description: This function loopback mounts a file system into the altroot
1601 * area of the BE being mounted. Since these are shared file
1602 * systems, they are expected to be already mounted for the
1603 * current BE, and this function just loopback mounts them into
1604 * the BE mountpoint. If they are not mounted for the current
1605 * live system, they are skipped and not mounted into the BE
1606 * we're mounting.
1607 * Parameters:
1608 * zhp - zfs_handle_t pointer to the dataset to loopback mount
1609 * md - be_mount_data_t pointer
1610 * Returns:
1611 * BE_SUCCESS - Success
1612 * be_errno_t - Failure
1613 * Scope:
1614 * Private
1616 static int
1617 loopback_mount_shared_fs(zfs_handle_t *zhp, be_mount_data_t *md)
1619 char zhp_mountpoint[MAXPATHLEN];
1620 char mountpoint[MAXPATHLEN];
1621 char *mp = NULL;
1622 char optstr[MAX_MNTOPT_STR];
1623 int mflag = MS_OPTIONSTR;
1624 int err;
1627 * Check if file system is currently mounted and not delegated
1628 * to a non-global zone (if we're in the global zone)
1630 if (zfs_is_mounted(zhp, &mp) && (getzoneid() != GLOBAL_ZONEID ||
1631 !zfs_prop_get_int(zhp, ZFS_PROP_ZONED))) {
1633 * If we didn't get a mountpoint from the zfs_is_mounted call,
1634 * get it from the mountpoint property.
1636 if (mp == NULL) {
1637 if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT,
1638 zhp_mountpoint, sizeof (zhp_mountpoint), NULL,
1639 NULL, 0, B_FALSE) != 0) {
1640 be_print_err(
1641 gettext("loopback_mount_shared_fs: "
1642 "failed to get mountpoint property\n"));
1643 return (BE_ERR_ZFS);
1645 } else {
1646 (void) strlcpy(zhp_mountpoint, mp,
1647 sizeof (zhp_mountpoint));
1648 free(mp);
1651 (void) snprintf(mountpoint, sizeof (mountpoint), "%s%s",
1652 md->altroot, zhp_mountpoint);
1654 /* Mount it read-only if read-write was not requested */
1655 if (!md->shared_rw) {
1656 mflag |= MS_RDONLY;
1659 /* Add the "nosub" option to the mount options string */
1660 (void) strlcpy(optstr, MNTOPT_NOSUB, sizeof (optstr));
1662 /* Loopback mount this dataset at the altroot */
1663 if (mount(zhp_mountpoint, mountpoint, mflag, MNTTYPE_LOFS,
1664 NULL, 0, optstr, sizeof (optstr)) != 0) {
1665 err = errno;
1666 be_print_err(gettext("loopback_mount_shared_fs: "
1667 "failed to loopback mount %s at %s: %s\n"),
1668 zhp_mountpoint, mountpoint, strerror(err));
1669 return (BE_ERR_MOUNT);
1673 return (BE_SUCCESS);
1677 * Function: loopback_mount_zonepath
1678 * Description: This function loopback mounts a zonepath into the altroot
1679 * area of the BE being mounted.
1680 * Parameters:
1681 * zonepath - pointer to zone path in the current BE
1682 * md - be_mount_data_t pointer
1683 * Returns:
1684 * BE_SUCCESS - Success
1685 * be_errno_t - Failure
1686 * Scope:
1687 * Private
1689 static int
1690 loopback_mount_zonepath(const char *zonepath, be_mount_data_t *md)
1692 FILE *fp = (FILE *)NULL;
1693 struct stat st;
1694 char *p;
1695 char *p1;
1696 char *parent_dir;
1697 struct extmnttab extmtab;
1698 dev_t dev = NODEV;
1699 char *parentmnt;
1700 char alt_parentmnt[MAXPATHLEN];
1701 struct mnttab mntref;
1702 char altzonepath[MAXPATHLEN];
1703 char optstr[MAX_MNTOPT_STR];
1704 int mflag = MS_OPTIONSTR;
1705 int ret;
1706 int err;
1708 fp = fopen(MNTTAB, "r");
1709 if (fp == NULL) {
1710 err = errno;
1711 be_print_err(gettext("loopback_mount_zonepath: "
1712 "failed to open /etc/mnttab\n"));
1713 return (errno_to_be_err(err));
1717 * before attempting the loopback mount of zonepath under altroot,
1718 * we need to make sure that all intermediate file systems in the
1719 * zone path are also mounted under altroot
1722 /* get the parent directory for zonepath */
1723 p = strrchr(zonepath, '/');
1724 if (p != NULL && p != zonepath) {
1725 if ((parent_dir = (char *)calloc(sizeof (char),
1726 p - zonepath + 1)) == NULL) {
1727 ret = BE_ERR_NOMEM;
1728 goto done;
1730 (void) strlcpy(parent_dir, zonepath, p - zonepath + 1);
1731 if (stat(parent_dir, &st) < 0) {
1732 ret = errno_to_be_err(errno);
1733 be_print_err(gettext("loopback_mount_zonepath: "
1734 "failed to stat %s"),
1735 parent_dir);
1736 free(parent_dir);
1737 goto done;
1739 free(parent_dir);
1742 * After the above stat call, st.st_dev contains ID of the
1743 * device over which parent dir resides.
1744 * Now, search mnttab and find mount point of parent dir device.
1747 resetmnttab(fp);
1748 while (getextmntent(fp, &extmtab, sizeof (extmtab)) == 0) {
1749 dev = makedev(extmtab.mnt_major, extmtab.mnt_minor);
1750 if (st.st_dev == dev && strcmp(extmtab.mnt_fstype,
1751 MNTTYPE_ZFS) == 0) {
1752 p1 = strchr(extmtab.mnt_special, '/');
1753 if (p1 == NULL || strncmp(p1 + 1,
1754 BE_CONTAINER_DS_NAME, 4) != 0 ||
1755 (*(p1 + 5) != '/' && *(p1 + 5) != '\0')) {
1757 * if parent dir is in a shared file
1758 * system, check whether it is already
1759 * loopback mounted under altroot or
1760 * not. It would have been mounted
1761 * already under altroot if it is in
1762 * a non-shared filesystem.
1764 parentmnt = strdup(extmtab.mnt_mountp);
1765 (void) snprintf(alt_parentmnt,
1766 sizeof (alt_parentmnt), "%s%s",
1767 md->altroot, parentmnt);
1768 mntref.mnt_mountp = alt_parentmnt;
1769 mntref.mnt_special = parentmnt;
1770 mntref.mnt_fstype = MNTTYPE_LOFS;
1771 mntref.mnt_mntopts = NULL;
1772 mntref.mnt_time = NULL;
1773 resetmnttab(fp);
1774 if (getmntany(fp, (struct mnttab *)
1775 &extmtab, &mntref) != 0) {
1776 ret = loopback_mount_zonepath(
1777 parentmnt, md);
1778 if (ret != BE_SUCCESS) {
1779 free(parentmnt);
1780 goto done;
1783 free(parentmnt);
1785 break;
1791 if (!md->shared_rw) {
1792 mflag |= MS_RDONLY;
1795 (void) snprintf(altzonepath, sizeof (altzonepath), "%s%s",
1796 md->altroot, zonepath);
1798 /* Add the "nosub" option to the mount options string */
1799 (void) strlcpy(optstr, MNTOPT_NOSUB, sizeof (optstr));
1801 /* Loopback mount this dataset at the altroot */
1802 if (mount(zonepath, altzonepath, mflag, MNTTYPE_LOFS,
1803 NULL, 0, optstr, sizeof (optstr)) != 0) {
1804 err = errno;
1805 be_print_err(gettext("loopback_mount_zonepath: "
1806 "failed to loopback mount %s at %s: %s\n"),
1807 zonepath, altzonepath, strerror(err));
1808 ret = BE_ERR_MOUNT;
1809 goto done;
1811 ret = BE_SUCCESS;
1813 done :
1814 (void) fclose(fp);
1815 return (ret);
1819 * Function: unmount_shared_fs
1820 * Description: This function iterates through the mnttab and finds all
1821 * loopback mount entries that reside within the altroot of
1822 * where the BE is mounted, and unmounts it.
1823 * Parameters:
1824 * ud - be_unmount_data_t pointer
1825 * Returns:
1826 * BE_SUCCESS - Success
1827 * be_errno_t - Failure
1828 * Scope:
1829 * Private
1831 static int
1832 unmount_shared_fs(be_unmount_data_t *ud)
1834 FILE *fp = NULL;
1835 struct mnttab *table = NULL;
1836 struct mnttab ent;
1837 struct mnttab *entp = NULL;
1838 size_t size = 0;
1839 int read_chunk = 32;
1840 int i;
1841 int altroot_len;
1842 int err = 0;
1844 errno = 0;
1846 /* Read in the mnttab into a table */
1847 if ((fp = fopen(MNTTAB, "r")) == NULL) {
1848 err = errno;
1849 be_print_err(gettext("unmount_shared_fs: "
1850 "failed to open mnttab\n"));
1851 return (errno_to_be_err(err));
1854 while (getmntent(fp, &ent) == 0) {
1855 if (size % read_chunk == 0) {
1856 table = (struct mnttab *)realloc(table,
1857 (size + read_chunk) * sizeof (ent));
1859 entp = &table[size++];
1862 * Copy over the current mnttab entry into our table,
1863 * copying only the fields that we care about.
1865 (void) memset(entp, 0, sizeof (*entp));
1866 if ((entp->mnt_mountp = strdup(ent.mnt_mountp)) == NULL ||
1867 (entp->mnt_fstype = strdup(ent.mnt_fstype)) == NULL) {
1868 be_print_err(gettext("unmount_shared_fs: "
1869 "memory allocation failed\n"));
1870 return (BE_ERR_NOMEM);
1873 (void) fclose(fp);
1876 * Process the mnttab entries in reverse order, looking for
1877 * loopback mount entries mounted under our altroot.
1879 altroot_len = strlen(ud->altroot);
1880 for (i = size; i > 0; i--) {
1881 entp = &table[i - 1];
1883 /* If not of type lofs, skip */
1884 if (strcmp(entp->mnt_fstype, MNTTYPE_LOFS) != 0)
1885 continue;
1887 /* If inside the altroot, unmount it */
1888 if (strncmp(entp->mnt_mountp, ud->altroot, altroot_len) == 0 &&
1889 entp->mnt_mountp[altroot_len] == '/') {
1890 if (umount(entp->mnt_mountp) != 0) {
1891 err = errno;
1892 if (err == EBUSY) {
1893 (void) sleep(1);
1894 err = errno = 0;
1895 if (umount(entp->mnt_mountp) != 0)
1896 err = errno;
1898 if (err != 0) {
1899 be_print_err(gettext(
1900 "unmount_shared_fs: "
1901 "failed to unmount shared file "
1902 "system %s: %s\n"),
1903 entp->mnt_mountp, strerror(err));
1904 return (errno_to_be_err(err));
1910 return (BE_SUCCESS);
1914 * Function: get_mountpoint_from_vfstab
1915 * Description: This function digs into the vfstab in the given altroot,
1916 * and searches for an entry for the fs passed in. If found,
1917 * it returns the mountpoint of that fs in the mountpoint
1918 * buffer passed in. If the get_alt_mountpoint flag is set,
1919 * it returns the mountpoint with the altroot prepended.
1920 * Parameters:
1921 * altroot - pointer to the alternate root location
1922 * fs - pointer to the file system name to look for in the
1923 * vfstab in altroot
1924 * mountpoint - pointer to buffer of where the mountpoint of
1925 * fs will be returned.
1926 * size_mp - size of mountpoint argument
1927 * get_alt_mountpoint - flag to indicate whether or not the
1928 * mountpoint should be populated with the altroot
1929 * prepended.
1930 * Returns:
1931 * BE_SUCCESS - Success
1932 * 1 - Failure
1933 * Scope:
1934 * Private
1936 static int
1937 get_mountpoint_from_vfstab(char *altroot, const char *fs, char *mountpoint,
1938 size_t size_mp, boolean_t get_alt_mountpoint)
1940 struct vfstab vp;
1941 FILE *fp = NULL;
1942 char alt_vfstab[MAXPATHLEN];
1944 /* Generate path to alternate root vfstab */
1945 (void) snprintf(alt_vfstab, sizeof (alt_vfstab), "%s/etc/vfstab",
1946 altroot);
1948 /* Open alternate root vfstab */
1949 if ((fp = fopen(alt_vfstab, "r")) == NULL) {
1950 be_print_err(gettext("get_mountpoint_from_vfstab: "
1951 "failed to open vfstab (%s)\n"), alt_vfstab);
1952 return (1);
1955 if (getvfsspec(fp, &vp, (char *)fs) == 0) {
1957 * Found entry for fs, grab its mountpoint.
1958 * If the flag to prepend the altroot into the mountpoint
1959 * is set, prepend it. Otherwise, just return the mountpoint.
1961 if (get_alt_mountpoint) {
1962 (void) snprintf(mountpoint, size_mp, "%s%s", altroot,
1963 vp.vfs_mountp);
1964 } else {
1965 (void) strlcpy(mountpoint, vp.vfs_mountp, size_mp);
1967 } else {
1968 (void) fclose(fp);
1969 return (1);
1972 (void) fclose(fp);
1974 return (BE_SUCCESS);
1978 * Function: fix_mountpoint_callback
1979 * Description: This callback function is used to iterate through a BE's
1980 * children filesystems to check if its mountpoint is currently
1981 * set to be mounted at some specified altroot. If so, fix it by
1982 * removing altroot from the beginning of its mountpoint.
1984 * Note - There's no way to tell if a child filesystem's
1985 * mountpoint isn't broken, and just happens to begin with
1986 * the altroot we're looking for. In this case, this function
1987 * will errantly remove the altroot portion from the beginning
1988 * of this filesystem's mountpoint.
1990 * Parameters:
1991 * zhp - zfs_handle_t pointer to filesystem being processed.
1992 * data - altroot of where BE is to be mounted.
1993 * Returns:
1994 * 0 - Success
1995 * be_errno_t - Failure
1996 * Scope:
1997 * Private
1999 static int
2000 fix_mountpoint_callback(zfs_handle_t *zhp, void *data)
2002 zprop_source_t sourcetype;
2003 char source[ZFS_MAX_DATASET_NAME_LEN];
2004 char mountpoint[MAXPATHLEN];
2005 char *zhp_mountpoint = NULL;
2006 char *altroot = data;
2007 int ret = 0;
2009 /* Get dataset's mountpoint and source values */
2010 if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mountpoint,
2011 sizeof (mountpoint), &sourcetype, source, sizeof (source),
2012 B_FALSE) != 0) {
2013 be_print_err(gettext("fix_mountpoint_callback: "
2014 "failed to get mountpoint and sourcetype for %s\n"),
2015 zfs_get_name(zhp));
2016 ZFS_CLOSE(zhp);
2017 return (BE_ERR_ZFS);
2021 * If the mountpoint is not inherited and the mountpoint is not
2022 * 'legacy', this file system potentially needs its mountpoint
2023 * fixed.
2025 if (!(sourcetype & ZPROP_SRC_INHERITED) &&
2026 strcmp(mountpoint, ZFS_MOUNTPOINT_LEGACY) != 0) {
2029 * Check if this file system's current mountpoint is
2030 * under the altroot we're fixing it against.
2032 if (strncmp(mountpoint, altroot, strlen(altroot)) == 0 &&
2033 mountpoint[strlen(altroot)] == '/') {
2036 * Get this dataset's mountpoint relative to the
2037 * altroot.
2039 zhp_mountpoint = mountpoint + strlen(altroot);
2041 /* Fix this dataset's mountpoint value */
2042 if (zfs_prop_set(zhp,
2043 zfs_prop_to_name(ZFS_PROP_MOUNTPOINT),
2044 zhp_mountpoint)) {
2045 be_print_err(gettext("fix_mountpoint_callback: "
2046 "failed to set mountpoint for %s to "
2047 "%s: %s\n"), zfs_get_name(zhp),
2048 zhp_mountpoint,
2049 libzfs_error_description(g_zfs));
2050 ret = zfs_err_to_be_err(g_zfs);
2051 ZFS_CLOSE(zhp);
2052 return (ret);
2057 /* Iterate through this dataset's children and fix them */
2058 if ((ret = zfs_iter_filesystems(zhp, fix_mountpoint_callback,
2059 altroot)) != 0) {
2060 ZFS_CLOSE(zhp);
2061 return (ret);
2065 ZFS_CLOSE(zhp);
2066 return (0);
2070 * Function: be_mount_root
2071 * Description: This function mounts the root dataset of a BE at the
2072 * specified altroot.
2073 * Parameters:
2074 * zhp - zfs_handle_t pointer to root dataset of a BE that is
2075 * to be mounted at altroot.
2076 * altroot - location of where to mount the BE root.
2077 * Return:
2078 * BE_SUCCESS - Success
2079 * be_errno_t - Failure
2080 * Scope:
2081 * Private
2083 static int
2084 be_mount_root(zfs_handle_t *zhp, char *altroot)
2086 char mountpoint[MAXPATHLEN];
2088 /* Get mountpoint property of dataset */
2089 if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mountpoint,
2090 sizeof (mountpoint), NULL, NULL, 0, B_FALSE) != 0) {
2091 be_print_err(gettext("be_mount_root: failed to "
2092 "get mountpoint property for %s: %s\n"), zfs_get_name(zhp),
2093 libzfs_error_description(g_zfs));
2094 return (zfs_err_to_be_err(g_zfs));
2098 * Set the canmount property for the BE's root dataset to 'noauto' just
2099 * in case it's been set to 'on'.
2101 if (zfs_prop_set(zhp, zfs_prop_to_name(ZFS_PROP_CANMOUNT), "noauto")
2102 != 0) {
2103 be_print_err(gettext("be_mount_root: failed to "
2104 "set canmount property to 'noauto' (%s): %s\n"),
2105 zfs_get_name(zhp), libzfs_error_description(g_zfs));
2106 return (zfs_err_to_be_err(g_zfs));
2109 /* Mount the BE's root filesystem */
2110 if (mount_zfs(zhp, altroot) != 0) {
2111 be_print_err(gettext("be_mount_root: failed to "
2112 "mount dataset %s at %s: %s\n"), zfs_get_name(zhp),
2113 altroot, strerror(errno));
2114 return (BE_ERR_ZFS);
2117 return (BE_SUCCESS);
2121 * Function: be_unmount_root
2122 * Description: This function unmounts the root dataset of a BE, but before
2123 * unmounting, it looks at the BE's vfstab to determine
2124 * if the root dataset mountpoint should be left as 'legacy'
2125 * or '/'. If the vfstab contains an entry for this root
2126 * dataset with a mountpoint of '/', it sets the mountpoint
2127 * property to 'legacy'.
2129 * Parameters:
2130 * zhp - zfs_handle_t pointer of the BE root dataset that
2131 * is currently mounted.
2132 * ud - be_unmount_data_t pointer providing unmount data
2133 * for the given BE root dataset.
2134 * Returns:
2135 * BE_SUCCESS - Success
2136 * be_errno_t - Failure
2137 * Scope:
2138 * Private
2140 static int
2141 be_unmount_root(zfs_handle_t *zhp, be_unmount_data_t *ud)
2143 char mountpoint[MAXPATHLEN];
2144 boolean_t is_legacy = B_FALSE;
2146 /* See if this is a legacy mounted root */
2147 if (get_mountpoint_from_vfstab(ud->altroot, zfs_get_name(zhp),
2148 mountpoint, sizeof (mountpoint), B_FALSE) == BE_SUCCESS &&
2149 strcmp(mountpoint, "/") == 0) {
2150 is_legacy = B_TRUE;
2153 /* Unmount the dataset */
2154 if (zfs_unmount(zhp, NULL, ud->force ? MS_FORCE : 0) != 0) {
2155 be_print_err(gettext("be_unmount_root: failed to "
2156 "unmount BE root dataset %s: %s\n"), zfs_get_name(zhp),
2157 libzfs_error_description(g_zfs));
2158 return (zfs_err_to_be_err(g_zfs));
2161 /* Set canmount property for this BE's root filesystem to noauto */
2162 if (zfs_prop_set(zhp, zfs_prop_to_name(ZFS_PROP_CANMOUNT), "noauto")
2163 != 0) {
2164 be_print_err(gettext("be_unmount_root: failed to "
2165 "set canmount property for %s to 'noauto': %s\n"),
2166 zfs_get_name(zhp), libzfs_error_description(g_zfs));
2167 return (zfs_err_to_be_err(g_zfs));
2171 * Set mountpoint for BE's root dataset back to '/', or 'legacy'
2172 * if its a legacy mounted root.
2174 if (zfs_prop_set(zhp, zfs_prop_to_name(ZFS_PROP_MOUNTPOINT),
2175 is_legacy ? ZFS_MOUNTPOINT_LEGACY : "/") != 0) {
2176 be_print_err(gettext("be_unmount_root: failed to "
2177 "set mountpoint of %s to %s\n"), zfs_get_name(zhp),
2178 is_legacy ? ZFS_MOUNTPOINT_LEGACY : "/");
2179 return (zfs_err_to_be_err(g_zfs));
2182 return (BE_SUCCESS);
2186 * Function: fix_mountpoint
2187 * Description: This function checks the mountpoint of an unmounted BE to make
2188 * sure that it is set to either 'legacy' or '/'. If it's not,
2189 * then we're in a situation where an unmounted BE has some random
2190 * mountpoint set for it. (This could happen if the system was
2191 * rebooted while an inactive BE was mounted). This function
2192 * attempts to fix its mountpoints.
2193 * Parameters:
2194 * zhp - zfs_handle_t pointer to root dataset of the BE
2195 * whose mountpoint needs to be checked.
2196 * Return:
2197 * BE_SUCCESS - Success
2198 * be_errno_t - Failure
2199 * Scope:
2200 * Private
2202 static int
2203 fix_mountpoint(zfs_handle_t *zhp)
2205 be_unmount_data_t ud = { 0 };
2206 char *altroot = NULL;
2207 char mountpoint[MAXPATHLEN];
2208 int ret = BE_SUCCESS;
2211 * Record what this BE's root dataset mountpoint property is currently
2212 * set to.
2214 if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mountpoint,
2215 sizeof (mountpoint), NULL, NULL, 0, B_FALSE) != 0) {
2216 be_print_err(gettext("fix_mountpoint: failed to get "
2217 "mountpoint property of (%s): %s\n"), zfs_get_name(zhp),
2218 libzfs_error_description(g_zfs));
2219 return (BE_ERR_ZFS);
2223 * If the root dataset mountpoint is set to 'legacy' or '/', we're okay.
2225 if (strcmp(mountpoint, ZFS_MOUNTPOINT_LEGACY) == 0 ||
2226 strcmp(mountpoint, "/") == 0) {
2227 return (BE_SUCCESS);
2231 * Iterate through this BE's children datasets and fix
2232 * them if they need fixing.
2234 if (zfs_iter_filesystems(zhp, fix_mountpoint_callback, mountpoint)
2235 != 0) {
2236 return (BE_ERR_ZFS);
2240 * The process of mounting and unmounting the root file system
2241 * will fix its mountpoint to correctly be either 'legacy' or '/'
2242 * since be_unmount_root will do the right thing by looking at
2243 * its vfstab.
2246 /* Generate temporary altroot to mount the root file system */
2247 if ((ret = be_make_tmp_mountpoint(&altroot)) != BE_SUCCESS) {
2248 be_print_err(gettext("fix_mountpoint: failed to "
2249 "make temporary mountpoint\n"));
2250 return (ret);
2253 /* Mount and unmount the root. */
2254 if ((ret = be_mount_root(zhp, altroot)) != BE_SUCCESS) {
2255 be_print_err(gettext("fix_mountpoint: failed to "
2256 "mount BE root file system\n"));
2257 goto cleanup;
2259 ud.altroot = altroot;
2260 if ((ret = be_unmount_root(zhp, &ud)) != BE_SUCCESS) {
2261 be_print_err(gettext("fix_mountpoint: failed to "
2262 "unmount BE root file system\n"));
2263 goto cleanup;
2266 cleanup:
2267 free(altroot);
2269 return (ret);
2273 * Function: be_mount_zones
2274 * Description: This function finds all supported non-global zones in the
2275 * given global BE and mounts them with respect to where the
2276 * global BE is currently mounted. The global BE datasets
2277 * (including its shared datasets) are expected to already
2278 * be mounted.
2279 * Parameters:
2280 * be_zhp - zfs_handle_t pointer to the root dataset of the
2281 * global BE.
2282 * md - be_mount_data_t pointer to data for global BE.
2283 * Returns:
2284 * BE_SUCCESS - Success
2285 * be_errno_t - Failure
2286 * Scope:
2287 * Private
2289 static int
2290 be_mount_zones(zfs_handle_t *be_zhp, be_mount_data_t *md)
2292 zoneBrandList_t *brands = NULL;
2293 zoneList_t zlst = NULL;
2294 char *zonename = NULL;
2295 char *zonepath = NULL;
2296 char *zonepath_ds = NULL;
2297 int k;
2298 int ret = BE_SUCCESS;
2300 z_set_zone_root(md->altroot);
2302 if ((brands = be_get_supported_brandlist()) == NULL) {
2303 be_print_err(gettext("be_mount_zones: "
2304 "no supported brands\n"));
2305 return (BE_SUCCESS);
2308 zlst = z_get_nonglobal_zone_list_by_brand(brands);
2309 if (zlst == NULL) {
2310 z_free_brand_list(brands);
2311 return (BE_SUCCESS);
2314 for (k = 0; (zonename = z_zlist_get_zonename(zlst, k)) != NULL; k++) {
2315 if (z_zlist_get_current_state(zlst, k) ==
2316 ZONE_STATE_INSTALLED) {
2317 zonepath = z_zlist_get_zonepath(zlst, k);
2320 * Get the dataset of this zonepath in current BE.
2321 * If its not a dataset, skip it.
2323 if ((zonepath_ds = be_get_ds_from_dir(zonepath))
2324 == NULL)
2325 continue;
2328 * Check if this zone is supported based on
2329 * the dataset of its zonepath
2331 if (!be_zone_supported(zonepath_ds)) {
2332 free(zonepath_ds);
2333 zonepath_ds = NULL;
2334 continue;
2338 * if BE's shared file systems are already mounted,
2339 * zone path dataset would have already been lofs
2340 * mounted under altroot. Otherwise, we need to do
2341 * it here.
2343 if (!md->shared_fs) {
2344 ret = loopback_mount_zonepath(zonepath, md);
2345 if (ret != BE_SUCCESS)
2346 goto done;
2350 /* Mount this zone */
2351 ret = be_mount_one_zone(be_zhp, md, zonename,
2352 zonepath, zonepath_ds);
2354 free(zonepath_ds);
2355 zonepath_ds = NULL;
2357 if (ret != BE_SUCCESS) {
2358 be_print_err(gettext("be_mount_zones: "
2359 "failed to mount zone %s under "
2360 "altroot %s\n"), zonename, md->altroot);
2361 goto done;
2366 done:
2367 z_free_brand_list(brands);
2368 z_free_zone_list(zlst);
2370 * libinstzones caches mnttab and uses cached version for resolving lofs
2371 * mounts when we call z_resolve_lofs. It creates the cached version
2372 * when the first call to z_resolve_lofs happens. So, library's cached
2373 * mnttab doesn't contain entries for lofs mounts created in the above
2374 * loop. Because of this, subsequent calls to z_resolve_lofs would fail
2375 * to resolve these lofs mounts. So, here we destroy library's cached
2376 * mnttab to force its recreation when the next call to z_resolve_lofs
2377 * happens.
2379 z_destroyMountTable();
2380 return (ret);
2384 * Function: be_unmount_zones
2385 * Description: This function finds all supported non-global zones in the
2386 * given mounted global BE and unmounts them.
2387 * Parameters:
2388 * ud - unmount_data_t pointer data for the global BE.
2389 * Returns:
2390 * BE_SUCCESS - Success
2391 * be_errno_t - Failure
2392 * Scope:
2393 * Private
2395 static int
2396 be_unmount_zones(be_unmount_data_t *ud)
2398 zoneBrandList_t *brands = NULL;
2399 zoneList_t zlst = NULL;
2400 char *zonename = NULL;
2401 char *zonepath = NULL;
2402 char alt_zonepath[MAXPATHLEN];
2403 char *zonepath_ds = NULL;
2404 int k;
2405 int ret = BE_SUCCESS;
2407 z_set_zone_root(ud->altroot);
2409 if ((brands = be_get_supported_brandlist()) == NULL) {
2410 be_print_err(gettext("be_unmount_zones: "
2411 "no supported brands\n"));
2412 return (BE_SUCCESS);
2415 zlst = z_get_nonglobal_zone_list_by_brand(brands);
2416 if (zlst == NULL) {
2417 z_free_brand_list(brands);
2418 return (BE_SUCCESS);
2421 for (k = 0; (zonename = z_zlist_get_zonename(zlst, k)) != NULL; k++) {
2422 if (z_zlist_get_current_state(zlst, k) ==
2423 ZONE_STATE_INSTALLED) {
2424 zonepath = z_zlist_get_zonepath(zlst, k);
2426 /* Build zone's zonepath wrt the global BE altroot */
2427 (void) snprintf(alt_zonepath, sizeof (alt_zonepath),
2428 "%s%s", ud->altroot, zonepath);
2431 * Get the dataset of this zonepath. If its not
2432 * a dataset, skip it.
2434 if ((zonepath_ds = be_get_ds_from_dir(alt_zonepath))
2435 == NULL)
2436 continue;
2439 * Check if this zone is supported based on the
2440 * dataset of its zonepath.
2442 if (!be_zone_supported(zonepath_ds)) {
2443 free(zonepath_ds);
2444 zonepath_ds = NULL;
2445 continue;
2448 /* Unmount this zone */
2449 ret = be_unmount_one_zone(ud, zonename, zonepath,
2450 zonepath_ds);
2452 free(zonepath_ds);
2453 zonepath_ds = NULL;
2455 if (ret != BE_SUCCESS) {
2456 be_print_err(gettext("be_unmount_zones:"
2457 " failed to unmount zone %s from "
2458 "altroot %s\n"), zonename, ud->altroot);
2459 goto done;
2464 done:
2465 z_free_brand_list(brands);
2466 z_free_zone_list(zlst);
2467 return (ret);
2471 * Function: be_mount_one_zone
2472 * Description: This function is called to mount one zone for a given
2473 * global BE.
2474 * Parameters:
2475 * be_zhp - zfs_handle_t pointer to the root dataset of the
2476 * global BE
2477 * md - be_mount_data_t pointer to data for global BE
2478 * zonename - name of zone to mount
2479 * zonepath - zonepath of zone to mount
2480 * zonepath_ds - dataset for the zonepath
2481 * Returns:
2482 * BE_SUCCESS - Success
2483 * be_errno_t - Failure
2484 * Scope:
2485 * Private
2487 static int
2488 be_mount_one_zone(zfs_handle_t *be_zhp, be_mount_data_t *md, char *zonename,
2489 char *zonepath, char *zonepath_ds)
2491 be_mount_data_t zone_md = { 0 };
2492 zfs_handle_t *zone_zhp = NULL;
2493 char zone_altroot[MAXPATHLEN];
2494 char zoneroot[MAXPATHLEN];
2495 char zoneroot_ds[MAXPATHLEN];
2496 int ret = BE_SUCCESS;
2498 /* Find the active zone root dataset for this zone for this BE */
2499 if ((ret = be_find_active_zone_root(be_zhp, zonepath_ds, zoneroot_ds,
2500 sizeof (zoneroot_ds))) == BE_ERR_ZONE_NO_ACTIVE_ROOT) {
2501 be_print_err(gettext("be_mount_one_zone: did not "
2502 "find active zone root for zone %s, skipping ...\n"),
2503 zonename);
2504 return (BE_SUCCESS);
2505 } else if (ret != BE_SUCCESS) {
2506 be_print_err(gettext("be_mount_one_zone: failed to "
2507 "find active zone root for zone %s\n"), zonename);
2508 return (ret);
2511 /* Get handle to active zoneroot dataset */
2512 if ((zone_zhp = zfs_open(g_zfs, zoneroot_ds, ZFS_TYPE_FILESYSTEM))
2513 == NULL) {
2514 be_print_err(gettext("be_mount_one_zone: failed to "
2515 "open zone root dataset (%s): %s\n"), zoneroot_ds,
2516 libzfs_error_description(g_zfs));
2517 return (zfs_err_to_be_err(g_zfs));
2520 /* Generate string for zone's altroot path */
2521 be_make_zoneroot(zonepath, zoneroot, sizeof (zoneroot));
2522 (void) strlcpy(zone_altroot, md->altroot, sizeof (zone_altroot));
2523 (void) strlcat(zone_altroot, zoneroot, sizeof (zone_altroot));
2525 /* Build mount_data for the zone */
2526 zone_md.altroot = zone_altroot;
2527 zone_md.shared_fs = md->shared_fs;
2528 zone_md.shared_rw = md->shared_rw;
2530 /* Mount the zone's root file system */
2531 if ((ret = be_mount_zone_root(zone_zhp, &zone_md)) != BE_SUCCESS) {
2532 be_print_err(gettext("be_mount_one_zone: failed to "
2533 "mount zone root file system at %s\n"), zone_altroot);
2534 goto done;
2537 /* Iterate through zone's children filesystems */
2538 if ((ret = zfs_iter_filesystems(zone_zhp, be_mount_callback,
2539 zone_altroot)) != 0) {
2540 be_print_err(gettext("be_mount_one_zone: failed to "
2541 "mount zone subordinate file systems at %s\n"),
2542 zone_altroot);
2543 goto done;
2546 /* TODO: Mount all shared file systems for this zone */
2548 done:
2549 ZFS_CLOSE(zone_zhp);
2550 return (ret);
2554 * Function: be_unmount_one_zone
2555 * Description: This function unmount one zone for a give global BE.
2556 * Parameters:
2557 * ud - be_unmount_data_t pointer to data for global BE
2558 * zonename - name of zone to unmount
2559 * zonepath - zonepath of the zone to unmount
2560 * zonepath_ds - dataset for the zonepath
2561 * Returns:
2562 * BE_SUCCESS - Success
2563 * be_errno_t - Failure
2564 * Scope:
2565 * Private
2567 static int
2568 be_unmount_one_zone(be_unmount_data_t *ud, char *zonename, char *zonepath,
2569 char *zonepath_ds)
2571 be_unmount_data_t zone_ud = { 0 };
2572 zfs_handle_t *zone_zhp = NULL;
2573 char zone_altroot[MAXPATHLEN];
2574 char zoneroot[MAXPATHLEN];
2575 char zoneroot_ds[MAXPATHLEN];
2576 int ret = BE_SUCCESS;
2578 /* Generate string for zone's alternate root path */
2579 be_make_zoneroot(zonepath, zoneroot, sizeof (zoneroot));
2580 (void) strlcpy(zone_altroot, ud->altroot, sizeof (zone_altroot));
2581 (void) strlcat(zone_altroot, zoneroot, sizeof (zone_altroot));
2583 /* Build be_unmount_data for zone */
2584 zone_ud.altroot = zone_altroot;
2585 zone_ud.force = ud->force;
2587 /* Find the mounted zone root dataset for this zone for this BE */
2588 if ((ret = be_find_mounted_zone_root(zone_altroot, zonepath_ds,
2589 zoneroot_ds, sizeof (zoneroot_ds))) == BE_ERR_NO_MOUNTED_ZONE) {
2590 be_print_err(gettext("be_unmount_one_zone: did not "
2591 "find any zone root mounted for zone %s\n"), zonename);
2592 return (BE_SUCCESS);
2593 } else if (ret != BE_SUCCESS) {
2594 be_print_err(gettext("be_unmount_one_zone: failed to "
2595 "find mounted zone root for zone %s\n"), zonename);
2596 return (ret);
2599 /* Get handle to zoneroot dataset mounted for this BE */
2600 if ((zone_zhp = zfs_open(g_zfs, zoneroot_ds, ZFS_TYPE_FILESYSTEM))
2601 == NULL) {
2602 be_print_err(gettext("be_unmount_one_zone: failed to "
2603 "open mounted zone root dataset (%s): %s\n"), zoneroot_ds,
2604 libzfs_error_description(g_zfs));
2605 return (zfs_err_to_be_err(g_zfs));
2608 /* TODO: Unmount all shared file systems for this zone */
2610 /* Iterate through zone's children filesystems and unmount them */
2611 if ((ret = zfs_iter_filesystems(zone_zhp, be_unmount_callback,
2612 &zone_ud)) != 0) {
2613 be_print_err(gettext("be_unmount_one_zone: failed to "
2614 "unmount zone subordinate file systems at %s\n"),
2615 zone_altroot);
2616 goto done;
2619 /* Unmount the zone's root filesystem */
2620 if ((ret = be_unmount_zone_root(zone_zhp, &zone_ud)) != BE_SUCCESS) {
2621 be_print_err(gettext("be_unmount_one_zone: failed to "
2622 "unmount zone root file system at %s\n"), zone_altroot);
2623 goto done;
2626 done:
2627 ZFS_CLOSE(zone_zhp);
2628 return (ret);
2632 * Function: be_get_ds_from_dir_callback
2633 * Description: This is a callback function used to iterate all datasets
2634 * to find the one that is currently mounted at the directory
2635 * being searched for. If matched, the name of the dataset is
2636 * returned in heap storage, so the caller is responsible for
2637 * freeing it.
2638 * Parameters:
2639 * zhp - zfs_handle_t pointer to current dataset being processed.
2640 * data - dir_data_t pointer providing name of directory being
2641 * searched for.
2642 * Returns:
2643 * 1 - This dataset is mounted at directory being searched for.
2644 * 0 - This dataset is not mounted at directory being searched for.
2645 * Scope:
2646 * Private
2648 static int
2649 be_get_ds_from_dir_callback(zfs_handle_t *zhp, void *data)
2651 dir_data_t *dd = data;
2652 char *mp = NULL;
2653 int zret = 0;
2655 if (zfs_get_type(zhp) != ZFS_TYPE_FILESYSTEM) {
2656 ZFS_CLOSE(zhp);
2657 return (0);
2660 if (zfs_is_mounted(zhp, &mp) && mp != NULL &&
2661 strcmp(mp, dd->dir) == 0) {
2662 if ((dd->ds = strdup(zfs_get_name(zhp))) == NULL) {
2663 be_print_err(gettext("be_get_ds_from_dir_callback: "
2664 "memory allocation failed\n"));
2665 ZFS_CLOSE(zhp);
2666 return (0);
2668 ZFS_CLOSE(zhp);
2669 return (1);
2672 zret = zfs_iter_filesystems(zhp, be_get_ds_from_dir_callback, dd);
2674 ZFS_CLOSE(zhp);
2676 return (zret);
2680 * Function: mount_zfs
2681 * Description: This is a function to mount zfs filesystem to alternative
2682 * root without changing zfs mountpoint property. Logic is
2683 * similar to zfs_mount.
2684 * Parameters:
2685 * zhp - zfs_handle_t pointer to current dataset being processed.
2686 * altroot - char pointer to current alternative root.
2687 * Returns:
2688 * BE_SUCCESS - Success
2689 * be_errno_t - Failure
2690 * Scope:
2691 * Private
2693 static int
2694 mount_zfs(zfs_handle_t *zhp, char *altroot)
2696 int flags = 0;
2697 char mountpoint[MAXPATHLEN];
2698 char real_mountpoint[MAXPATHLEN];
2699 char source[MAXNAMELEN];
2700 char optstr[MAX_MNTOPT_STR];
2701 zprop_source_t sourcetype;
2702 struct stat buf;
2704 optstr[0] = '\0';
2706 /* Get dataset's mountpoint and source values */
2707 if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mountpoint,
2708 sizeof (mountpoint), &sourcetype, source, sizeof (source),
2709 B_FALSE) != 0) {
2710 be_print_err(gettext("mount_zfs: "
2711 "failed to get mountpoint and sourcetype for %s\n"),
2712 zfs_get_name(zhp));
2713 ZFS_CLOSE(zhp);
2714 return (BE_ERR_ZFS);
2717 if (strcmp(mountpoint, ZFS_MOUNTPOINT_LEGACY) == 0 ||
2718 strcmp(mountpoint, "/") == 0) {
2720 * We are called only from be_mount_root or be_mount_callback
2721 * when mountpoint != LEGACY
2723 mountpoint[0] = '\0';
2726 (void) snprintf(real_mountpoint, MAXPATHLEN, "%s%s", altroot,
2727 mountpoint);
2729 if (zpool_get_prop_int(zfs_get_pool_handle(zhp), ZPOOL_PROP_READONLY,
2730 NULL))
2731 flags |= MS_RDONLY;
2733 /* Create the directory if it doesn't already exist */
2734 if (lstat(real_mountpoint, &buf) != 0) {
2735 if (mkdirp(real_mountpoint, 0755) != 0) {
2736 be_print_err(gettext("mount_zfs: "
2737 "failed to create mountpoint for %s\n"),
2738 zfs_get_name(zhp));
2739 ZFS_CLOSE(zhp);
2740 return (BE_ERR_ZFS);
2744 if (mount(zfs_get_name(zhp), real_mountpoint, MS_OPTIONSTR | flags,
2745 MNTTYPE_ZFS, NULL, 0, optstr, sizeof (optstr))) {
2746 be_print_err(gettext("mount_zfs: failed to "
2747 "mount dataset %s at %s\n"), zfs_get_name(zhp),
2748 real_mountpoint);
2749 return (BE_ERR_ZFS);
2752 return (BE_SUCCESS);