diff --git a/config/user-libmount.m4 b/config/user-libmount.m4 new file mode 100644 index 000000000000..d34f1bb08e37 --- /dev/null +++ b/config/user-libmount.m4 @@ -0,0 +1,12 @@ +dnl # +dnl # Check for libmount. libmount is a part of util-linux source code, +dnl # but requires libmount-devel (or something similar) on distros. +dnl # +AC_DEFUN([ZFS_AC_CONFIG_USER_LIBMOUNT], [ + LIBMOUNT= + + AC_CHECK_HEADER([libmount/libmount.h], [ + AC_SUBST([LIBMOUNT], ["-lmount"]) + AC_DEFINE([HAVE_LIBMOUNT], 1, [Define if you have libmount]) + ], []) +]) diff --git a/config/user.m4 b/config/user.m4 index 1ee9dbe263bc..a30e10077293 100644 --- a/config/user.m4 +++ b/config/user.m4 @@ -15,6 +15,7 @@ AC_DEFUN([ZFS_AC_CONFIG_USER], [ ZFS_AC_CONFIG_USER_LIBUDEV ZFS_AC_CONFIG_USER_LIBSSL ZFS_AC_CONFIG_USER_LIBAIO + ZFS_AC_CONFIG_USER_LIBMOUNT ZFS_AC_CONFIG_USER_RUNSTATEDIR ZFS_AC_CONFIG_USER_MAKEDEV_IN_SYSMACROS ZFS_AC_CONFIG_USER_MAKEDEV_IN_MKDEV diff --git a/lib/libzfs/Makefile.am b/lib/libzfs/Makefile.am index 421970413daf..631df6eb43db 100644 --- a/lib/libzfs/Makefile.am +++ b/lib/libzfs/Makefile.am @@ -61,7 +61,7 @@ libzfs_la_LIBADD = \ $(top_builddir)/lib/libzfs_core/libzfs_core.la \ $(top_builddir)/lib/libzutil/libzutil.la -libzfs_la_LIBADD += -lm $(LIBSSL) +libzfs_la_LIBADD += -lm $(LIBSSL) $(LIBMOUNT) libzfs_la_LDFLAGS = -version-info 2:0:0 EXTRA_DIST = $(libzfs_pc_DATA) $(USER_C) diff --git a/lib/libzfs/libzfs_mount.c b/lib/libzfs/libzfs_mount.c index 7497a72233ad..0faf51e2df27 100644 --- a/lib/libzfs/libzfs_mount.c +++ b/lib/libzfs/libzfs_mount.c @@ -351,14 +351,131 @@ zfs_is_mountable(zfs_handle_t *zhp, char *buf, size_t buflen, * in the case of a mount failure we do not have the exact errno. We must * make due with return value from the mount process. * - * In the long term a shared library called libmount is under development - * which provides a common API to address the locking and errno issues. - * Once the standard mount utility has been updated to use this library - * we can add an autoconf check to conditionally use it. + * If libmount exists, use libmount API rather than invoking external programs. + * libmount provides a common API to address the locking and errno issues. + * In modern distros, the system mount utilities are libmount based. * * http://www.kernel.org/pub/linux/utils/util-linux/libmount-docs/index.html */ +/* + * MNT_EX_* and related API didn't exist until util-linux v2.30 released in + * 2017, so initially only support v2.30 or above. + * XXX Ideally, this should be in config/user-libmount.m4, to avoid linking + * libmount if util-linux is < v2.30. + */ +#ifdef HAVE_LIBMOUNT +#include +#endif +#if (((LIBMOUNT_MAJOR_VERSION == 2) && (LIBMOUNT_MINOR_VERSION >= 30)) || \ + LIBMOUNT_MAJOR_VERSION > 2) +static int +xlate_excode_to_errno(int excode) +{ + /* Note that there is no MNT_EX_BUSY */ + switch (excode) { + case MNT_EX_SUCCESS: + return (0); + case MNT_EX_FILEIO: + return (EIO); + case MNT_EX_USER: + return (EINTR); + case MNT_EX_SOFTWARE: + return (EPIPE); + case MNT_EX_SYSERR: + return (EAGAIN); + case MNT_EX_USAGE: + return (EINVAL); + case MNT_EX_FAIL: + case MNT_EX_SOMEOK: + default: + return (ENXIO); /* Generic error */ + } + return (ENXIO); +} + +static int +do_mount(const char *src, const char *mntpt, char *opts) +{ + struct libmnt_context *cxt; + int rc; + + mnt_init_debug(0); + cxt = mnt_new_context(); + if (cxt == NULL) + return (ENOMEM); + + if (mnt_context_is_restricted(cxt) != 0) { + mnt_free_context(cxt); + return (EPERM); + } + if (mnt_context_disable_canonicalize(cxt, B_TRUE) != 0) { + mnt_free_context(cxt); + return (EINVAL); + } + if (mnt_context_append_options(cxt, opts) != 0) { + mnt_free_context(cxt); + return (EINVAL); + } + if (mnt_context_set_fstype(cxt, MNTTYPE_ZFS) != 0) { + mnt_free_context(cxt); + return (EINVAL); + } + if (mnt_context_set_source(cxt, src) != 0) { + mnt_free_context(cxt); + return (EINVAL); + } + if (mnt_context_set_target(cxt, mntpt) != 0) { + mnt_free_context(cxt); + return (EINVAL); + } + + rc = mnt_context_mount(cxt); + rc = mnt_context_get_excode(cxt, rc, NULL, 0); + mnt_free_context(cxt); + + return (xlate_excode_to_errno(rc)); +} + +static int +do_unmount(const char *mntpt, int flags) +{ + struct libmnt_context *cxt; + int rc; + + mnt_init_debug(0); + cxt = mnt_new_context(); + if (cxt == NULL) + return (ENOMEM); + + if (mnt_context_is_restricted(cxt) != 0) { + mnt_free_context(cxt); + return (EPERM); + } + if ((flags & MS_FORCE) && mnt_context_enable_force(cxt, B_TRUE) != 0) { + mnt_free_context(cxt); + return (EINVAL); + } + if ((flags & MS_DETACH) && mnt_context_enable_lazy(cxt, B_TRUE) != 0) { + mnt_free_context(cxt); + return (EINVAL); + } + if (mnt_context_set_fstype(cxt, MNTTYPE_ZFS) != 0) { + mnt_free_context(cxt); + return (EINVAL); + } + if (mnt_context_set_target(cxt, mntpt) != 0) { + mnt_free_context(cxt); + return (EINVAL); + } + + rc = mnt_context_umount(cxt); + rc = mnt_context_get_excode(cxt, rc, NULL, 0); + mnt_free_context(cxt); + + return (xlate_excode_to_errno(rc)); +} +#else static int do_mount(const char *src, const char *mntpt, char *opts) { @@ -420,6 +537,7 @@ do_unmount(const char *mntpt, int flags) return (rc ? EINVAL : 0); } +#endif static int zfs_add_option(zfs_handle_t *zhp, char *options, int len,