summaryrefslogtreecommitdiffstats
path: root/libmount/src/hook_mount.c
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-14 19:10:49 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-14 19:10:49 +0000
commitcfe5e3905201349e9cf3f95d52ff4bd100bde37d (patch)
treed0baf160cbee3195249d095f85e52d20c21acf02 /libmount/src/hook_mount.c
parentInitial commit. (diff)
downloadutil-linux-cfe5e3905201349e9cf3f95d52ff4bd100bde37d.tar.xz
util-linux-cfe5e3905201349e9cf3f95d52ff4bd100bde37d.zip
Adding upstream version 2.39.3.upstream/2.39.3
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'libmount/src/hook_mount.c')
-rw-r--r--libmount/src/hook_mount.c790
1 files changed, 790 insertions, 0 deletions
diff --git a/libmount/src/hook_mount.c b/libmount/src/hook_mount.c
new file mode 100644
index 0000000..dc3dfa7
--- /dev/null
+++ b/libmount/src/hook_mount.c
@@ -0,0 +1,790 @@
+/* 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
+ * - fsconfig MOUNT (FS reconfigure)
+ * - 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 "fileutils.h" /* statx() fallback */
+#include "strutils.h"
+#include "mount-api-utils.h"
+#include "linux_version.h"
+
+#include <inttypes.h>
+
+#ifdef USE_LIBMOUNT_MOUNTFD_SUPPORT
+
+#define get_sysapi(_cxt) mnt_context_get_sysapi(_cxt)
+
+static void close_sysapi_fds(struct libmnt_sysapi *api)
+{
+ if (api->fd_fs >= 0)
+ close(api->fd_fs);
+ if (api->fd_tree >= 0)
+ close(api->fd_tree);
+
+ api->fd_tree = api->fd_fs = -1;
+}
+
+/*
+ * 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;
+
+ close_sysapi_fds(api);
+
+ 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 int fsconfig_set_value(
+ struct libmnt_context *cxt,
+ const struct libmnt_hookset *hs,
+ int fd,
+ const char *name, const char *value)
+{
+ int rc;
+ char *p = NULL;
+
+ if (value && strstr(value, "\\,")) {
+ p = strdup(value);
+ if (!p)
+ return -EINVAL;
+
+ strrem(p, '\\');
+ value = p;
+ }
+
+ DBG(HOOK, ul_debugobj(hs, " fsconfig(name=\"%s\" value=\"%s\")", name,
+ value ? : ""));
+ if (value) {
+ rc = fsconfig(fd, FSCONFIG_SET_STRING, name, value, 0);
+ free(p);
+ } else
+ rc = fsconfig(fd, FSCONFIG_SET_FLAG, name, NULL, 0);
+
+ set_syscall_status(cxt, "fsconfig", rc == 0);
+ return rc;
+}
+
+static int configure_superblock(struct libmnt_context *cxt,
+ const struct libmnt_hookset *hs,
+ int fd, int force_rwro)
+{
+ struct libmnt_optlist *ol;
+ struct libmnt_iter itr;
+ struct libmnt_opt *opt;
+ int rc = 0, has_rwro = 0;
+
+ DBG(HOOK, ul_debugobj(hs, " config FS"));
+
+ ol = mnt_context_get_optlist(cxt);
+ if (!ol)
+ return -ENOMEM;
+
+ mnt_reset_iter(&itr, MNT_ITER_FORWARD);
+
+ while (mnt_optlist_next_opt(ol, &itr, &opt) == 0) {
+ const char *name = mnt_opt_get_name(opt);
+ const char *value = mnt_opt_get_value(opt);
+ const struct libmnt_optmap *ent = mnt_opt_get_mapent(opt);
+ const int is_linux = ent && mnt_opt_get_map(opt) == cxt->map_linux;
+
+ if (is_linux && ent->id == MS_RDONLY) {
+ /* Use ro/rw for superblock (for backward compatibility) */
+ value = NULL;
+ has_rwro = 1;
+
+ } else if (is_linux && ent->mask & MNT_SUPERBLOCK) {
+ /* Use some old MS_* (VFS) flags as superblock flags */
+ ;
+
+ } else if (!name || mnt_opt_get_map(opt) || mnt_opt_is_external(opt))
+ /* Ignore VFS flags, userspace and external options */
+ continue;
+
+ rc = fsconfig_set_value(cxt, hs, fd, name, value);
+ if (rc != 0)
+ goto done;
+ }
+
+ if (force_rwro && !has_rwro)
+ rc = fsconfig_set_value(cxt, hs, fd, "rw", NULL);
+
+done:
+ DBG(HOOK, ul_debugobj(hs, " config done [rc=%d]", rc));
+ return rc != 0 && errno ? -errno : rc;
+}
+
+static int open_fs_configuration_context(struct libmnt_context *cxt,
+ struct libmnt_sysapi *api,
+ const char *type)
+{
+ DBG(HOOK, ul_debug(" new FS '%s'", type));
+
+ if (!type)
+ return -EINVAL;
+
+ DBG(HOOK, ul_debug(" fsopen(%s)", type));
+
+ api->fd_fs = fsopen(type, FSOPEN_CLOEXEC);
+ set_syscall_status(cxt, "fsopen", api->fd_fs >= 0);
+ if (api->fd_fs < 0)
+ return -errno;
+ api->is_new_fs = 1;
+ return api->fd_fs;
+}
+
+static int open_mount_tree(struct libmnt_context *cxt, const char *path, unsigned long mflg)
+{
+ unsigned long oflg = OPEN_TREE_CLOEXEC;
+ int rc = 0, fd = -1;
+
+ if (mflg == (unsigned long) -1) {
+ rc = mnt_optlist_get_flags(cxt->optlist, &mflg, cxt->map_linux, 0);
+ if (rc)
+ return rc;
+ }
+ if (!path) {
+ path = mnt_fs_get_target(cxt->fs);
+ if (!path)
+ return -EINVAL;
+ }
+
+ /* Classic -oremount,bind,ro is not bind operation, it's just
+ * VFS flags update only */
+ if ((mflg & MS_BIND) && !(mflg & MS_REMOUNT)) {
+ oflg |= OPEN_TREE_CLONE;
+
+ if (mnt_optlist_is_rbind(cxt->optlist))
+ oflg |= AT_RECURSIVE;
+ }
+
+ if (cxt->force_clone)
+ oflg |= OPEN_TREE_CLONE;
+
+ DBG(HOOK, ul_debug("open_tree(path=%s%s%s)", path,
+ oflg & OPEN_TREE_CLONE ? " clone" : "",
+ oflg & AT_RECURSIVE ? " recursive" : ""));
+ fd = open_tree(AT_FDCWD, path, oflg);
+ set_syscall_status(cxt, "open_tree", fd >= 0);
+
+ return fd;
+}
+
+static int hook_create_mount(struct libmnt_context *cxt,
+ const struct libmnt_hookset *hs,
+ void *data __attribute__((__unused__)))
+{
+ struct libmnt_sysapi *api;
+ const char *src;
+ int rc = 0;
+
+ assert(cxt);
+
+ if (mnt_context_helper_executed(cxt))
+ return 0;
+
+ assert(cxt->fs);
+
+ api = get_sysapi(cxt);
+ assert(api);
+
+ if (api->fd_fs < 0) {
+ const char *type = mnt_fs_get_fstype(cxt->fs);
+
+ rc = open_fs_configuration_context(cxt, api, type);
+ if (rc < 0) {
+ rc = api->fd_fs;
+ goto done;
+ }
+ }
+
+ src = mnt_fs_get_srcpath(cxt->fs);
+ if (!src)
+ return -EINVAL;
+
+ DBG(HOOK, ul_debugobj(hs, "init FS"));
+
+ rc = fsconfig(api->fd_fs, FSCONFIG_SET_STRING, "source", src, 0);
+ set_syscall_status(cxt, "fsconfig", rc == 0);
+
+ if (!rc)
+ rc = configure_superblock(cxt, hs, api->fd_fs, 0);
+ if (!rc) {
+ DBG(HOOK, ul_debugobj(hs, "create FS"));
+ rc = fsconfig(api->fd_fs, FSCONFIG_CMD_CREATE, NULL, NULL, 0);
+ set_syscall_status(cxt, "fsconfig", rc == 0);
+ }
+
+ if (!rc) {
+ api->fd_tree = fsmount(api->fd_fs, FSMOUNT_CLOEXEC, 0);
+ set_syscall_status(cxt, "fsmount", api->fd_tree >= 0);
+ if (api->fd_tree < 0)
+ rc = -errno;
+ }
+
+ if (rc)
+ /* cleanup after fail (libmount may only try the FS type) */
+ close_sysapi_fds(api);
+
+#if defined(HAVE_STATX) && defined(HAVE_STRUCT_STATX) && defined(HAVE_STRUCT_STATX_STX_MNT_ID)
+ if (!rc && cxt->fs) {
+ struct statx st;
+
+ rc = statx(api->fd_tree, "", AT_EMPTY_PATH, STATX_MNT_ID, &st);
+ if (rc == 0) {
+ cxt->fs->id = (int) st.stx_mnt_id;
+ if (cxt->update) {
+ struct libmnt_fs *fs = mnt_update_get_fs(cxt->update);
+ if (fs)
+ fs->id = cxt->fs->id;
+ }
+ }
+ }
+#endif
+
+done:
+ DBG(HOOK, ul_debugobj(hs, "create FS done [rc=%d, id=%d]", rc, cxt->fs ? cxt->fs->id : -1));
+ return rc;
+}
+
+static int hook_reconfigure_mount(struct libmnt_context *cxt,
+ const struct libmnt_hookset *hs,
+ void *data __attribute__((__unused__)))
+{
+ struct libmnt_sysapi *api;
+ int rc = 0;
+
+ assert(cxt);
+
+ if (mnt_context_helper_executed(cxt))
+ return 0;
+
+ api = get_sysapi(cxt);
+ assert(api);
+ assert(api->fd_tree >= 0);
+
+ if (api->fd_fs < 0) {
+ api->fd_fs = fspick(api->fd_tree, "", FSPICK_EMPTY_PATH |
+ FSPICK_NO_AUTOMOUNT);
+ set_syscall_status(cxt, "fspick", api->fd_fs >= 0);
+ if (api->fd_fs < 0)
+ return -errno;
+ }
+
+ rc = configure_superblock(cxt, hs, api->fd_fs, 1);
+ if (!rc) {
+ DBG(HOOK, ul_debugobj(hs, "re-configurate FS"));
+ rc = fsconfig(api->fd_fs, FSCONFIG_CMD_RECONFIGURE, NULL, NULL, 0);
+ set_syscall_status(cxt, "fsconfig", rc == 0);
+ }
+
+ DBG(HOOK, ul_debugobj(hs, "reconf FS done [rc=%d]", rc));
+ return rc;
+}
+
+static int set_vfsflags(struct libmnt_context *cxt,
+ const struct libmnt_hookset *hs,
+ uint64_t set, uint64_t clr, int recursive)
+{
+ struct libmnt_sysapi *api;
+ struct mount_attr attr = { .attr_clr = 0 };
+ unsigned int callflags = AT_EMPTY_PATH;
+ int rc;
+
+ api = get_sysapi(cxt);
+ assert(api);
+
+ /* fallback only; necessary when init_sysapi() during preparation
+ * cannot open the tree -- for example when we call /sbin/mount.<type> */
+ if (api->fd_tree < 0 && mnt_fs_get_target(cxt->fs)) {
+ rc = api->fd_tree = open_mount_tree(cxt, NULL, (unsigned long) -1);
+ if (rc < 0)
+ return rc;
+ rc = 0;
+ }
+
+ if (recursive)
+ callflags |= AT_RECURSIVE;
+
+ DBG(HOOK, ul_debugobj(hs,
+ "mount_setattr(set=0x%08" PRIx64" clr=0x%08" PRIx64")", set, clr));
+ attr.attr_set = set;
+ attr.attr_clr = clr;
+
+ errno = 0;
+ rc = mount_setattr(api->fd_tree, "", callflags, &attr, sizeof(attr));
+ set_syscall_status(cxt, "mount_setattr", rc == 0);
+
+ if (rc && errno == EINVAL)
+ return -MNT_ERR_APPLYFLAGS;
+
+ return rc == 0 ? 0 : -errno;
+}
+
+static int hook_set_vfsflags(struct libmnt_context *cxt,
+ const struct libmnt_hookset *hs,
+ void *data __attribute__((__unused__)))
+{
+ struct libmnt_optlist *ol;
+ uint64_t set = 0, clr = 0;
+ int rc = 0;
+
+ if (mnt_context_helper_executed(cxt))
+ return 0;
+
+ DBG(HOOK, ul_debugobj(hs, "setting VFS flags"));
+
+ ol = mnt_context_get_optlist(cxt);
+ if (!ol)
+ return -ENOMEM;
+
+ /* normal flags */
+ rc = mnt_optlist_get_attrs(ol, &set, &clr, MNT_OL_NOREC);
+ if (!rc && (set || clr))
+ rc = set_vfsflags(cxt, hs, set, clr, 0);
+
+ /* recursive flags */
+ set = clr = 0;
+ if (!rc)
+ rc = mnt_optlist_get_attrs(ol, &set, &clr, MNT_OL_REC);
+ if (!rc && (set || clr))
+ rc = set_vfsflags(cxt, hs, set, clr, 1);
+
+ return rc;
+}
+
+static int hook_set_propagation(struct libmnt_context *cxt,
+ const struct libmnt_hookset *hs,
+ void *data __attribute__((__unused__)))
+{
+ struct libmnt_sysapi *api;
+ struct libmnt_optlist *ol;
+ struct libmnt_iter itr;
+ struct libmnt_opt *opt;
+ int rc = 0;
+
+ DBG(HOOK, ul_debugobj(hs, "setting propagation"));
+
+ ol = mnt_context_get_optlist(cxt);
+ if (!ol)
+ return -ENOMEM;
+
+ api = get_sysapi(cxt);
+ assert(api);
+
+ /* fallback only; necessary when init_sysapi() during preparation
+ * cannot open the tree -- for example when we call /sbin/mount.<type> */
+ if (api->fd_tree < 0 && mnt_fs_get_target(cxt->fs)) {
+ rc = api->fd_tree = open_mount_tree(cxt, NULL, (unsigned long) -1);
+ if (rc < 0)
+ goto done;
+ rc = 0;
+ }
+
+ mnt_reset_iter(&itr, MNT_ITER_FORWARD);
+
+ while (mnt_optlist_next_opt(ol, &itr, &opt) == 0) {
+ const struct libmnt_optmap *map = mnt_opt_get_map(opt);
+ const struct libmnt_optmap *ent = mnt_opt_get_mapent(opt);
+ struct mount_attr attr = { .attr_clr = 0 };
+ unsigned int flgs = AT_EMPTY_PATH;
+
+ if (cxt->map_linux != map)
+ continue;
+ if (mnt_opt_is_external(opt))
+ continue;
+ if (!ent || !ent->id || !(ent->id & MS_PROPAGATION))
+ continue;
+
+ attr.propagation = ent->id & MS_PROPAGATION;
+ if (ent->id & MS_REC)
+ flgs |= AT_RECURSIVE;
+
+ DBG(HOOK, ul_debugobj(hs,
+ "mount_setattr(propagation=0x%08" PRIx64")",
+ (uint64_t) attr.propagation));
+
+ rc = mount_setattr(api->fd_tree, "", flgs, &attr, sizeof(attr));
+ set_syscall_status(cxt, "mount_setattr", rc == 0);
+
+ if (rc && errno == EINVAL)
+ return -MNT_ERR_APPLYFLAGS;
+ if (rc != 0)
+ break;
+ }
+done:
+ return rc == 0 ? 0 : -errno;
+}
+
+static int hook_attach_target(struct libmnt_context *cxt,
+ const struct libmnt_hookset *hs,
+ void *data __attribute__((__unused__)))
+{
+ struct libmnt_sysapi *api;
+ const char *target;
+ int rc = 0;
+
+ if (mnt_context_helper_executed(cxt))
+ return 0;
+
+ target = mnt_fs_get_target(cxt->fs);
+ if (!target)
+ return -EINVAL;
+
+ api = get_sysapi(cxt);
+ assert(api);
+ assert(api->fd_tree >= 0);
+
+ DBG(HOOK, ul_debugobj(hs, "move_mount(to=%s)", target));
+
+ /* umount old target if we created a clone */
+ if (cxt->force_clone
+ && !api->is_new_fs
+ && !mnt_optlist_is_bind(cxt->optlist)) {
+
+ DBG(HOOK, ul_debugobj(hs, "remove expired target"));
+ umount2(target, MNT_DETACH);
+ }
+
+ rc = move_mount(api->fd_tree, "", AT_FDCWD, target, MOVE_MOUNT_F_EMPTY_PATH);
+ set_syscall_status(cxt, "move_mount", rc == 0);
+
+ return rc == 0 ? 0 : -errno;
+}
+
+static inline int fsopen_is_supported(void)
+{
+ int dummy, rc = 1;
+
+ errno = 0;
+ dummy = fsopen(NULL, FSOPEN_CLOEXEC);
+
+ if (errno == ENOSYS)
+ rc = 0;
+ if (dummy >= 0)
+ close(dummy);
+ return rc;
+}
+
+static inline int mount_setattr_is_supported(void)
+{
+ int rc;
+
+ errno = 0;
+ rc = mount_setattr(-1, NULL, 0, NULL, 0);
+ return !(rc == -1 && errno == ENOSYS);
+}
+
+/*
+ * open_tree() and fsopen()
+ */
+static int init_sysapi(struct libmnt_context *cxt,
+ const struct libmnt_hookset *hs,
+ unsigned long flags)
+{
+ struct libmnt_sysapi *api;
+ const char *path = NULL;
+
+ assert(cxt);
+ assert(hs);
+
+ DBG(HOOK, ul_debugobj(hs, "initialize API fds"));
+
+ /* A) tree based operation -- the tree is mount point */
+ if ((flags & MS_REMOUNT)
+ || mnt_context_propagation_only(cxt)) {
+ DBG(HOOK, ul_debugobj(hs, " REMOUNT/propagation"));
+ path = mnt_fs_get_target(cxt->fs);
+ if (!path)
+ return -EINVAL;
+
+ /* B) tree based operation -- the tree is mount source */
+ } else if ((flags & MS_BIND)
+ || (flags & MS_MOVE)) {
+ DBG(HOOK, ul_debugobj(hs, " BIND/MOVE"));
+ path = mnt_fs_get_srcpath(cxt->fs);
+ if (!path)
+ return -EINVAL;
+ }
+
+ api = new_hookset_data(cxt, hs);
+ if (!api)
+ return -ENOMEM;
+
+ if (path) {
+ api->fd_tree = open_mount_tree(cxt, path, flags);
+ if (api->fd_tree < 0)
+ goto fail;
+
+ /* C) FS based operation
+ *
+ * Note, fstype is optinal and may be specified later if mount by
+ * list of FS types (mount -t foo,bar,ext4). In this case fsopen()
+ * is called later in hook_create_mount(). */
+ } else {
+ const char *type = mnt_fs_get_fstype(cxt->fs);
+ int rc = 0;
+
+ /* fsopen() to create a superblock */
+ if (cxt->helper == NULL && type && !strchr(type, ','))
+ rc = open_fs_configuration_context(cxt, api, type);
+
+ /* dummy fsopen() to test if API is available */
+ else if (!fsopen_is_supported()) {
+ errno = ENOSYS;
+ rc = -errno;
+ set_syscall_status(cxt, "fsopen", rc == 0);
+ }
+ if (rc < 0)
+ goto fail;
+ }
+
+ return 0;
+fail:
+ DBG(HOOK, ul_debugobj(hs, "init fs/tree failed [errno=%d %m]", errno));
+ return -errno;
+}
+
+static int force_classic_mount(struct libmnt_context *cxt)
+{
+ const char *env = getenv("LIBMOUNT_FORCE_MOUNT2");
+
+ if (env) {
+ if (strcmp(env, "always") == 0)
+ return 1;
+ if (strcmp(env, "never") == 0)
+ return 0;
+ }
+
+ /* "auto" (default) -- try to be smart */
+
+ /* For external /sbin/mount.<type> helpers we use the new API only for
+ * propagation setting. In this case, the usability of mount_setattr()
+ * will be verified later */
+ if (cxt->helper)
+ return 0;
+
+ /*
+ * The current kernel btrfs driver does not completely implement
+ * fsconfig() as it does not work with selinux stuff.
+ *
+ * Don't use the new mount API in this situation. Let's hope this issue
+ * is temporary.
+ */
+ {
+ const char *type = mnt_fs_get_fstype(cxt->fs);
+
+ if (type && strcmp(type, "btrfs") == 0 && cxt->has_selinux_opt)
+ return 1;
+ }
+
+ return 0;
+}
+
+
+/*
+ * Analyze library context and register hook to call mount-like syscalls.
+ *
+ * Note that this function interprets classic MS_* flags by new Linux mount FD
+ * based API.
+ *
+ * Returns: 0 on success, <0 on error, >0 on recover-able error
+ */
+static int hook_prepare(struct libmnt_context *cxt,
+ const struct libmnt_hookset *hs,
+ void *data __attribute__((__unused__)))
+{
+ struct libmnt_optlist *ol;
+ unsigned long flags = 0;
+ uint64_t set = 0, clr = 0;
+ int rc = 0;
+
+ assert(cxt);
+ assert(hs == &hookset_mount);
+
+ if (force_classic_mount(cxt)) {
+ DBG(HOOK, ul_debugobj(hs, "new API disabled"));
+ return 0;
+ }
+
+ DBG(HOOK, ul_debugobj(hs, "prepare mount"));
+
+ ol = mnt_context_get_optlist(cxt);
+ if (!ol)
+ return -ENOMEM;
+
+ /* classic MS_* flags (include oprations like MS_REMOUNT, etc) */
+ rc = mnt_optlist_get_flags(ol, &flags, cxt->map_linux, 0);
+
+ /* MOUNT_ATTR_* flags for mount_setattr() */
+ if (!rc)
+ rc = mnt_optlist_get_attrs(ol, &set, &clr, 0);
+
+ /* open_tree() or fsopen() */
+ if (!rc) {
+ rc = init_sysapi(cxt, hs, flags);
+ if (rc && cxt->syscall_status == -ENOSYS)
+ goto enosys;
+ }
+
+ /* check mutually exclusive operations */
+ if (!rc && (flags & MS_BIND) && (flags & MS_MOVE))
+ return -EINVAL;
+ if (!rc && (flags & MS_MOVE) && (flags & MS_REMOUNT))
+ return -EINVAL;
+
+ /* classic remount (note -oremount,bind,ro is not superblock reconfiguration) */
+ if (!rc
+ && cxt->helper == NULL
+ && (flags & MS_REMOUNT)
+ && !(flags & MS_BIND))
+ rc = mnt_context_append_hook(cxt, hs, MNT_STAGE_MOUNT, NULL,
+ hook_reconfigure_mount);
+
+ /* create a new FS instance */
+ else if (!rc
+ && cxt->helper == NULL
+ && !(flags & MS_BIND)
+ && !(flags & MS_MOVE)
+ && !(flags & MS_REMOUNT)
+ && !mnt_context_propagation_only(cxt))
+ rc = mnt_context_append_hook(cxt, hs, MNT_STAGE_MOUNT, NULL,
+ hook_create_mount);
+
+ /* call mount_setattr() */
+ if (!rc
+ && cxt->helper == NULL
+ && (set != 0 || clr != 0 || (flags & MS_REMOUNT))) {
+ /*
+ * mount_setattr() supported, but not usable for remount
+ * https://github.com/torvalds/linux/commit/dd8b477f9a3d8edb136207acb3652e1a34a661b7
+ */
+ if (get_linux_version() < KERNEL_VERSION(5, 14, 0))
+ goto enosys;
+
+ if (!mount_setattr_is_supported())
+ goto enosys;
+
+ rc = mnt_context_append_hook(cxt, hs, MNT_STAGE_MOUNT, NULL,
+ hook_set_vfsflags);
+ }
+
+ /* call move_mount() to attach target */
+ if (!rc
+ && cxt->helper == NULL
+ && (cxt->force_clone ||
+ (!(flags & MS_REMOUNT) && !mnt_context_propagation_only(cxt))))
+ rc = mnt_context_append_hook(cxt, hs, MNT_STAGE_MOUNT_POST, NULL,
+ hook_attach_target);
+
+ /* set propagation (has to be attached to VFS) */
+ if (!rc && mnt_optlist_get_propagation(ol)) {
+ if (!mount_setattr_is_supported())
+ goto enosys;
+
+ rc = mnt_context_append_hook(cxt, hs, MNT_STAGE_MOUNT_POST, NULL,
+ hook_set_propagation);
+ }
+
+ DBG(HOOK, ul_debugobj(hs, "prepare mount done [rc=%d]", rc));
+ return rc;
+
+enosys:
+ /* we need to recover from this error, so hook_mount_legacy.c
+ * can try to continue */
+ DBG(HOOK, ul_debugobj(hs, "failed to init new API"));
+ reset_syscall_status(cxt);
+ hookset_deinit(cxt, hs);
+ return 1;
+}
+
+const struct libmnt_hookset hookset_mount =
+{
+ .name = "__mount",
+
+ .firststage = MNT_STAGE_PREP,
+ .firstcall = hook_prepare,
+
+ .deinit = hookset_deinit
+};
+#endif /* USE_LIBMOUNT_MOUNTFD_SUPPORT */