Skip to content

Commit

Permalink
Use libmount instead of invoking mount(8)/umount(8)
Browse files Browse the repository at this point in the history
ZoL has been relying on external commands mount(8)/umount(8) to
mount/unmount datasets. As the comment in `lib/libzfs/libzfs_mount.c`
suggests, use libmount to mount/unmount without fork(2)+exec(2) of
mount(8)/umount(8) if libmount exists.

mount(8)/umount(8) are nowadays libmount based (see util-linux code),
so there is 1:1 correspondence between the existing implementation
and this one.

Due to API instability, initially only support libmount v2.30 or above
which was released in June 2017. Distros which track upstream mostly
seem to meet this requirement at least in the latest version.

Signed-off-by: Tomohiro Kusumi <kusumi.tomohiro@osnexus.com>
  • Loading branch information
kusumi committed Jul 2, 2019
1 parent 681a85c commit e9e5f60
Show file tree
Hide file tree
Showing 4 changed files with 136 additions and 5 deletions.
12 changes: 12 additions & 0 deletions config/user-libmount.m4
Original file line number Diff line number Diff line change
@@ -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])
], [])
])
1 change: 1 addition & 0 deletions config/user.m4
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion lib/libzfs/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
126 changes: 122 additions & 4 deletions lib/libzfs/libzfs_mount.c
Original file line number Diff line number Diff line change
Expand Up @@ -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 <libmount/libmount.h>
#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)
{
Expand Down Expand Up @@ -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,
Expand Down

0 comments on commit e9e5f60

Please sign in to comment.