From 68a192e4b7627f8f00d412ba68c291fcc90d3de9 Mon Sep 17 00:00:00 2001 From: Kyle Evans Date: Tue, 14 Jan 2020 10:49:54 -0600 Subject: [PATCH] libzfs: add zfs_mount_at() function zfs_mount_at() mounts a dataset at an arbitrary mountpoint rather than at the configured mountpoint. This may be used by consumers that wish to temporarily expose a dataset at another mountpoint without altering dataset/pool properties. This will be used by FreeBSD's libbe be_mount(), which mounts a boot environment at an arbitrary mountpoint. Reviewed-by: Brian Behlendorf Reviewed-by: Ryan Moeller Signed-off-by: Kyle Evans Closes #9833 --- include/libzfs.h | 1 + lib/libzfs/libzfs_mount.c | 45 ++++++++++++++++++++++++++++++++------- 2 files changed, 38 insertions(+), 8 deletions(-) diff --git a/include/libzfs.h b/include/libzfs.h index 8069d4cd4dc2..05abfdf89ece 100644 --- a/include/libzfs.h +++ b/include/libzfs.h @@ -788,6 +788,7 @@ extern boolean_t zfs_bookmark_exists(const char *path); extern boolean_t is_mounted(libzfs_handle_t *, const char *special, char **); extern boolean_t zfs_is_mounted(zfs_handle_t *, char **); extern int zfs_mount(zfs_handle_t *, const char *, int); +extern int zfs_mount_at(zfs_handle_t *, const char *, int, const char *); extern int zfs_unmount(zfs_handle_t *, const char *, int); extern int zfs_unmountall(zfs_handle_t *, int); diff --git a/lib/libzfs/libzfs_mount.c b/lib/libzfs/libzfs_mount.c index c2955ccfaba5..c3fe97d5573e 100644 --- a/lib/libzfs/libzfs_mount.c +++ b/lib/libzfs/libzfs_mount.c @@ -37,6 +37,7 @@ * * zfs_is_mounted() * zfs_mount() + * zfs_mount_at() * zfs_unmount() * zfs_unmountall() * @@ -239,6 +240,23 @@ zfs_is_mounted(zfs_handle_t *zhp, char **where) return (is_mounted(zhp->zfs_hdl, zfs_get_name(zhp), where)); } +/* + * Checks any higher order concerns about whether the given dataset is + * mountable, false otherwise. zfs_is_mountable_internal specifically assumes + * that the caller has verified the sanity of mounting the dataset at + * mountpoint to the extent the caller wants. + */ +static boolean_t +zfs_is_mountable_internal(zfs_handle_t *zhp, const char *mountpoint) +{ + + if (zfs_prop_get_int(zhp, ZFS_PROP_ZONED) && + getzoneid() == GLOBAL_ZONEID) + return (B_FALSE); + + return (B_TRUE); +} + /* * Returns true if the given dataset is mountable, false otherwise. Returns the * mountpoint in 'buf'. @@ -264,8 +282,7 @@ zfs_is_mountable(zfs_handle_t *zhp, char *buf, size_t buflen, if (zfs_prop_get_int(zhp, ZFS_PROP_CANMOUNT) == ZFS_CANMOUNT_OFF) return (B_FALSE); - if (zfs_prop_get_int(zhp, ZFS_PROP_ZONED) && - getzoneid() == GLOBAL_ZONEID) + if (!zfs_is_mountable_internal(zhp, buf)) return (B_FALSE); if (zfs_prop_get_int(zhp, ZFS_PROP_REDACTED) && !(flags & MS_FORCE)) @@ -346,14 +363,26 @@ zfs_add_options(zfs_handle_t *zhp, char *options, int len) return (error); } +int +zfs_mount(zfs_handle_t *zhp, const char *options, int flags) +{ + char mountpoint[ZFS_MAXPROPLEN]; + + if (!zfs_is_mountable(zhp, mountpoint, sizeof (mountpoint), NULL, + flags)) + return (0); + + return (zfs_mount_at(zhp, options, flags, mountpoint)); +} + /* * Mount the given filesystem. */ int -zfs_mount(zfs_handle_t *zhp, const char *options, int flags) +zfs_mount_at(zfs_handle_t *zhp, const char *options, int flags, + const char *mountpoint) { struct stat buf; - char mountpoint[ZFS_MAXPROPLEN]; char mntopts[MNT_LINE_MAX]; char overlay[ZFS_MAXPROPLEN]; libzfs_handle_t *hdl = zhp->zfs_hdl; @@ -369,16 +398,16 @@ zfs_mount(zfs_handle_t *zhp, const char *options, int flags) if (strstr(mntopts, MNTOPT_REMOUNT) != NULL) remount = 1; + /* Potentially duplicates some checks if invoked by zfs_mount(). */ + if (!zfs_is_mountable_internal(zhp, mountpoint)) + return (0); + /* * If the pool is imported read-only then all mounts must be read-only */ if (zpool_get_prop_int(zhp->zpool_hdl, ZPOOL_PROP_READONLY, NULL)) (void) strlcat(mntopts, "," MNTOPT_RO, sizeof (mntopts)); - if (!zfs_is_mountable(zhp, mountpoint, sizeof (mountpoint), NULL, - flags)) - return (0); - /* * Append default mount options which apply to the mount point. * This is done because under Linux (unlike Solaris) multiple mount