]> git.ipfire.org Git - thirdparty/util-linux.git/commitdiff
libmount: initial support for new FD based mount kernel API
authorKarel Zak <kzak@redhat.com>
Tue, 20 Sep 2022 11:53:40 +0000 (13:53 +0200)
committerKarel Zak <kzak@redhat.com>
Tue, 3 Jan 2023 11:58:42 +0000 (12:58 +0100)
For now it supports -obind and -obind,<flags> operations.

Signed-off-by: Karel Zak <kzak@redhat.com>
libmount/src/Makemodule.am
libmount/src/hook_mount.c [new file with mode: 0644]
libmount/src/hook_mount_legacy.c
libmount/src/hooks.c

index 1d38cc959c87f671e7098ae6ef77a1c090820471..437e47006de64edd38ca99c2b42d5e6af7664153 100644 (file)
@@ -31,6 +31,7 @@ libmount_la_SOURCES += \
        libmount/src/context.c \
        libmount/src/context_mount.c \
        libmount/src/context_umount.c \
+       libmount/src/hook_mount.c \
        libmount/src/hook_mount_legacy.c \
        libmount/src/hook_mkdir.c \
        libmount/src/hook_subdir.c \
diff --git a/libmount/src/hook_mount.c b/libmount/src/hook_mount.c
new file mode 100644 (file)
index 0000000..4452f38
--- /dev/null
@@ -0,0 +1,322 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * This file is part of libmount from util-linux project.
+ *
+ * Copyright (C) 2022 Karel Zak <kzak@redhat.com>
+ *
+ * libmount is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ *
+ * This is fsconfig/fsopen based mount.
+ *
+ * Please, see the comment in libmount/src/hooks.c to understand how hooks work.
+ *
+ * Operations: functions and STAGE, all is prepared in hook_prepare():
+ *
+ * mount:
+ *     - fsopen        PRE
+ *     - fsmount       MOUNT
+ *     - mount_setattr MOUNT (VFS flags)
+ *     - mount_move    POST
+ *     - mount_setattr POST (propagation)
+ *
+ * remount:
+ *     - open_tree     PRE
+ *     - reconfigure   MOUNT
+ *     - mount_setattr MOUNT (VFS flags)
+ *     - mount_setattr POST (propagation)
+ *
+ * propagation-only:
+ *     - open_tree     PRE
+ *     - mount_setattr POST (propagation)
+ *
+ * move:
+ *     - open_tree     PRE
+ *     - mount_move    POST
+ *
+ * bind:
+ *     - open_tree     PRE (clone)
+ *     - mount_setattr MOUNT (VFS flags)
+ *     - mount_move    POST
+ */
+
+#include "mountP.h"
+
+#include <inttypes.h>
+
+/*
+ * This hookset uses 'struct libmnt_sysapi' (mountP.h) as hookset data.
+ */
+static void free_hookset_data( struct libmnt_context *cxt,
+                               const struct libmnt_hookset *hs)
+{
+       struct libmnt_sysapi *api = mnt_context_get_hookset_data(cxt, hs);
+
+       if (!api)
+               return;
+       if (api->fd_fs >= 0)
+               close(api->fd_fs);
+       if (api->fd_tree >= 0)
+               close(api->fd_tree);
+
+       free(api);
+       mnt_context_set_hookset_data(cxt, hs, NULL);
+}
+
+/* global data, used by all callbacks */
+static struct libmnt_sysapi *new_hookset_data(
+                               struct libmnt_context *cxt,
+                               const struct libmnt_hookset *hs)
+{
+       struct libmnt_sysapi *api = calloc(1, sizeof(struct libmnt_sysapi));
+
+       if (!api)
+               return NULL;
+       api->fd_fs = api->fd_tree = -1;
+
+       if (mnt_context_set_hookset_data(cxt, hs, api) != 0) {
+               /* probably ENOMEM problem */
+               free(api);
+               api = NULL;
+       }
+       return api;
+}
+
+/* de-initiallize this module */
+static int hookset_deinit(struct libmnt_context *cxt, const struct libmnt_hookset *hs)
+{
+       DBG(HOOK, ul_debugobj(hs, "deinit '%s'", hs->name));
+
+       /* remove all our hooks */
+       while (mnt_context_remove_hook(cxt, hs, 0, NULL) == 0);
+
+       /* free and remove global hookset data */
+       free_hookset_data(cxt, hs);
+
+       return 0;
+}
+
+static inline struct libmnt_sysapi *get_sysapi(struct libmnt_context *cxt,
+                                       const struct libmnt_hookset *hs)
+{
+       return mnt_context_get_hookset_data(cxt, hs);
+}
+
+static int set_vfs_flags(int fd, struct libmnt_context *cxt, int recursive)
+{
+       struct libmnt_optlist *ol;
+       struct mount_attr attr = { .attr_clr = 0 };
+       unsigned int callflags = AT_EMPTY_PATH;
+       uint64_t mask = 0;
+       int rc;
+
+       ol= mnt_context_get_optlist(cxt);
+       if (!ol)
+               return -ENOMEM;
+
+       mnt_optlist_get_attrs(ol, &mask);
+       attr.attr_set = mask;
+       if (recursive)
+               callflags |= AT_RECURSIVE;
+
+       DBG(HOOK, ul_debug(" mount_setattr(set=0x%" PRIx64")", mask));
+       rc = mount_setattr(fd, "", callflags, &attr, sizeof(attr));
+       if (rc)
+               return -errno;
+
+       return 0;
+}
+
+static int is_recursive_bind(struct libmnt_context *cxt)
+{
+       struct libmnt_optlist *ol = mnt_context_get_optlist(cxt);
+
+       if (!ol)
+               return 0;
+
+       return mnt_optlist_is_rbind(ol);
+}
+
+static int hook_bind_attach(struct libmnt_context *cxt,
+               const struct libmnt_hookset *hs,
+               void *data __attribute__((__unused__)))
+{
+       struct libmnt_sysapi *api;
+       const char *target;
+
+       DBG(HOOK, ul_debugobj(hs, "bind: attach"));
+
+       target = mnt_fs_get_target(cxt->fs);
+       if (!target)
+               return -EINVAL;
+
+       api = get_sysapi(cxt, hs);
+       if (!api || api->fd_tree <= 0)
+               return -EINVAL;
+
+       DBG(HOOK, ul_debugobj(hs, " move_mount(to=%s)", target));
+       if (move_mount(api->fd_tree, "", AT_FDCWD, target, MOVE_MOUNT_F_EMPTY_PATH))
+               return -errno;
+
+       return 0;
+}
+
+static int hook_bind_setflags(struct libmnt_context *cxt,
+                       const struct libmnt_hookset *hs,
+                       void *data __attribute__((__unused__)))
+{
+       struct libmnt_sysapi *api;
+       int rc;
+
+       DBG(HOOK, ul_debugobj(hs, "bind: setting VFS"));
+
+       api = get_sysapi(cxt, hs);
+       if (!api || api->fd_tree <= 0)
+               return -EINVAL;
+
+       rc = set_vfs_flags(api->fd_tree, cxt, is_recursive_bind(cxt));
+       if (rc)
+               return rc;
+
+       return mnt_context_append_hook(cxt, hs,
+                       MNT_STAGE_MOUNT_POST, NULL, hook_bind_attach);
+}
+
+/*
+static int hook_remount(struct libmnt_context *cxt,
+                       const struct libmnt_hookset *hs, void *data)
+{
+       return 0;
+}
+
+
+static int hook_move(struct libmnt_context *cxt,
+                       const struct libmnt_hookset *hs, void *data)
+{
+       return 0;
+}
+
+static int hook_propagation(struct libmnt_context *cxt,
+                       const struct libmnt_hookset *hs, void *data)
+{
+       return 0;
+}
+
+static int hook_newmount(struct libmnt_context *cxt,
+                       const struct libmnt_hookset *hs, void *data)
+{
+       return 0;
+}
+*/
+
+/*
+ * analyze library context and register hook to call mount-like syscalls
+ */
+static int hook_prepare(struct libmnt_context *cxt,
+                       const struct libmnt_hookset *hs,
+                       void *data __attribute__((__unused__)))
+{
+       int rc = 0, next_stage = 0;
+       int (*next_hook)(struct libmnt_context *, const struct libmnt_hookset *, void *) = NULL;
+       unsigned long flags = 0, open_flags = 0;
+       const char *tree_path = NULL, *src = NULL, *target = NULL;
+       struct libmnt_sysapi *api;
+
+       assert(cxt);
+       assert(hs == &hookset_mount);
+
+       DBG(HOOK, ul_debugobj(hs, "prepare new API mount"));
+
+       target = mnt_fs_get_target(cxt->fs);
+       if (!target)
+               return -EINVAL;
+
+       src = mnt_fs_get_srcpath(cxt->fs);
+
+       rc = mnt_context_get_mflags(cxt, &flags);
+       if (rc)
+               return rc;
+
+       api = new_hookset_data(cxt, hs);
+       if (!api)
+               return -ENOMEM;
+
+       open_flags = OPEN_TREE_CLOEXEC;
+
+       if (flags & MS_REMOUNT) {
+               DBG(HOOK, ul_debugobj(hs, " prepare remount"));
+               tree_path = target;
+               next_stage = MNT_STAGE_MOUNT;
+               /*next_hook = hook_remount;*/
+
+       } else if (flags & MS_BIND) {
+               DBG(HOOK, ul_debugobj(hs, " prepare bind"));
+               tree_path = src;
+               next_stage = MNT_STAGE_MOUNT;
+               next_hook = (flags & MNT_BIND_SETTABLE) ? hook_bind_setflags :
+                                                         hook_bind_attach;
+               open_flags |= OPEN_TREE_CLONE;
+               if (is_recursive_bind(cxt))
+                       open_flags |= AT_RECURSIVE;
+
+       } else if (flags & MS_MOVE) {
+               DBG(HOOK, ul_debugobj(hs, " prepare move"));
+               tree_path = src;
+               next_stage = MNT_STAGE_MOUNT_POST;
+               /*next_hook = hook_move;*/
+
+       } else if (mnt_context_propagation_only(cxt)) {
+               DBG(HOOK, ul_debugobj(hs, " prepare propagation change"));
+               tree_path = target;
+               next_stage = MNT_STAGE_MOUNT_POST;
+               /*next_hook = hook_propagation;*/
+
+       } else {
+               const char *type;
+
+               DBG(HOOK, ul_debugobj(hs, " prepare mount"));
+               next_stage = MNT_STAGE_MOUNT;
+               /*next_hook = hook_newmount;*/
+
+               type = mnt_fs_get_fstype(cxt->fs);
+               if (!type)
+                       return -EINVAL;
+
+               DBG(HOOK, ul_debugobj(hs, "fsopen(%s)", type));
+               api->fd_fs = fsopen(type, FSOPEN_CLOEXEC);
+               if (api->fd_fs < 0)
+                       goto nothing;
+       }
+
+       if (api->fd_fs == -1) {
+               if (!tree_path) {
+                       DBG(HOOK, ul_debugobj(hs, "tree path undefined"));
+                       return -EINVAL;
+               }
+               DBG(HOOK, ul_debugobj(hs, "open_tree(path=%s, flags=0x%lx)",
+                                       tree_path, open_flags));
+               api->fd_tree = open_tree(AT_FDCWD, tree_path, open_flags);
+               if (api->fd_tree <= 0)
+                       goto nothing;
+       }
+
+       return mnt_context_append_hook(cxt, hs, next_stage, NULL, next_hook);
+nothing:
+       /* let's assume that fsopen/open_tree() is not supported */
+       DBG(HOOK, ul_debugobj(hs, " open fs/tree failed [errno=%d %m]", errno));
+       free_hookset_data(cxt, hs);
+       return 0;
+}
+
+const struct libmnt_hookset hookset_mount =
+{
+       .name = "__mount",
+
+       .firststage = MNT_STAGE_PREP,
+       .firstcall = hook_prepare,
+
+       .deinit = hookset_deinit
+};
index 8b082f2abe96c33fd2df8c8e7d8e148ba22607c6..a3b4b4347f5b7314e1b3caf69a23b8a74e9cc237 100644 (file)
@@ -280,7 +280,7 @@ static int hook_prepare(struct libmnt_context *cxt,
        assert(hs == &hookset_mount_legacy);
 
 #ifdef UL_HAVE_MOUNT_API
-       /* do nothing when __mount succesfully registred */
+       /* do nothing when a new __mount succesfully registred */
        if (mnt_context_has_hook(cxt, &hookset_mount, 0, NULL))
                return 0;
 #endif
index d0117e4562162134bc4b4b15929c75f567bac326..7ceff838ebc285d239b4cdc5ed94f2871b72796a 100644 (file)
@@ -39,6 +39,7 @@ static const struct libmnt_hookset *hooksets[] =
 #endif
        &hookset_mkdir,
        &hookset_subdir,
+       &hookset_mount,
        &hookset_mount_legacy,
 #ifdef UL_HAVE_MOUNT_API
        &hookset_idmap,
@@ -278,38 +279,39 @@ int mnt_context_call_hooks(struct libmnt_context *cxt, int stage)
 {
        struct list_head *p, *next;
        size_t i;
+       int rc = 0;
+
+       DBG(CXT, ul_debugobj(cxt, "---> ENTER-STAGE %s", stagenames[stage]));
 
        /* call initial hooks */
        for (i = 0; i <  ARRAY_SIZE(hooksets); i++) {
-               int rc;
                const struct libmnt_hookset *hs = hooksets[i];
 
                if (hs->firststage != stage)
                        continue;
 
-               DBG(CXT, ul_debugobj(cxt, "calling %s hook from %s",
-                        stagenames[hs->firststage], hs->name));
+               DBG(CXT, ul_debugobj(cxt, "calling %s hook", hs->name));
 
                rc = hs->firstcall(cxt, hs, NULL);
                if (rc < 0)
-                       return rc;
+                       goto done;
        }
 
        /* call already active hooks */
        list_for_each_safe(p, next, &cxt->hooksets_hooks) {
-               int rc;
                struct hookset_hook *x = list_entry(p, struct hookset_hook, hooks);
 
                if (x->stage != stage)
                        continue;
 
-               DBG(CXT, ul_debugobj(cxt, "calling %s hook from %s",
-                       stagenames[x->stage], x->hookset->name));
+               DBG(CXT, ul_debugobj(cxt, "calling %s hook", x->hookset->name));
 
                rc = x->func(cxt, x->hookset, x->data);
                if (rc < 0)
-                       return rc;
+                       goto done;
        }
 
-       return 0;
+done:
+       DBG(CXT, ul_debugobj(cxt, "<--- DONE-STAGE %s [rc=%d]", stagenames[stage], rc));
+       return rc;
 }