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, only support util-linux v2.30 or above which
was released in June 2017. Distros which track upstream mostly seem
to meet this requirement with releases after 2017.

(It's technically possible to support < v2.30 using low level API's
which are still public interface, but need to draw the line. v2.30 is
reasonably old with stable enough API to maintain builds now and in
the future.)

Signed-off-by: Tomohiro Kusumi <kusumi.tomohiro@osnexus.com>
  • Loading branch information
kusumi committed Jul 22, 2019
1 parent 09276fd commit 6f7046f
Show file tree
Hide file tree
Showing 4 changed files with 140 additions and 7 deletions.
13 changes: 13 additions & 0 deletions config/user-libmount.m4
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
dnl #
dnl # Check for libmount. libmount is a part of util-linux source code,
dnl # but requires libmount-devel (or similar name) for <libmount/libmount.h>
dnl # 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
131 changes: 125 additions & 6 deletions lib/libzfs/libzfs_mount.c
Original file line number Diff line number Diff line change
Expand Up @@ -351,14 +351,132 @@ 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.
*
* http://www.kernel.org/pub/linux/utils/util-linux/libmount-docs/index.html
* 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.
* https://www.kernel.org/pub/linux/utils/util-linux/
*/

/*
* MNT_EX_* and related API didn't exist until util-linux v2.30 released in
* 2017, so libzfs only supports v2.30 or above.
*/
#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)
{
/*
* There is no MNT_EX_BUSY, i.e. difference in interpretation of errno
* in comparison with invoking mount(8).
*/
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
/* HAVE_LIBMOUNT undefined or unsupported util-linux version */
static int
do_mount(const char *src, const char *mntpt, char *opts)
{
Expand Down Expand Up @@ -420,6 +538,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 6f7046f

Please sign in to comment.