lib_mount_sources = '''
src/mountP.h
src/cache.c
+ src/hooks.c
src/fs.c
src/init.c
src/iter.c
src/context_veritydev.c
src/context_mount.c
src/context_umount.c
+ src/hook_mount_legacy.c
src/monitor.c
'''.split()
endif
libmount/src/mountP.h \
libmount/src/cache.c \
libmount/src/fs.c \
+ libmount/src/hooks.c \
libmount/src/init.c \
libmount/src/iter.c \
libmount/src/lock.c \
libmount/src/context_veritydev.c \
libmount/src/context_mount.c \
libmount/src/context_umount.c \
+ libmount/src/hook_mount_legacy.c \
libmount/src/monitor.c
if HAVE_BTRFS
cxt->tgt_group = (gid_t) -1;
cxt->tgt_mode = (mode_t) -1;
- INIT_LIST_HEAD(&cxt->addmounts);
-
ruid = getuid();
euid = geteuid();
cxt->ns_tgt.fd = -1;
cxt->ns_cur = &cxt->ns_orig;
+ INIT_LIST_HEAD(&cxt->hooksets_hooks);
+ INIT_LIST_HEAD(&cxt->hooksets_datas);
+
/* if we're really root and aren't running setuid */
cxt->restricted = (uid_t) 0 == ruid && ruid == euid ? 0 : 1;
mnt_free_update(cxt->update);
mnt_context_set_target_ns(cxt, NULL);
+ mnt_context_deinit_hooksets(cxt);
free(cxt->children);
cxt->orig_user = NULL;
cxt->mountflags = 0;
cxt->user_mountflags = 0;
+ cxt->orig_mountflags = 0;
cxt->mountdata = NULL;
cxt->subdir = NULL;
cxt->flags = MNT_FL_DEFAULT;
cxt->noautofs = 1;
-
- /* free additional mounts list */
- while (!list_empty(&cxt->addmounts)) {
- struct libmnt_addmount *ad = list_entry(cxt->addmounts.next,
- struct libmnt_addmount,
- mounts);
- mnt_free_addmount(ad);
- }
+ cxt->is_propagation_only = 0;
mnt_context_reset_status(cxt);
+ mnt_context_deinit_hooksets(cxt);
if (cxt->table_fltrcb)
mnt_context_set_tabfilter(cxt, NULL, NULL);
n->mountflags = o->mountflags;
n->mountdata = o->mountdata;
+ n->is_propagation_only = o->is_propagation_only;
mnt_context_reset_status(n);
int mnt_context_get_mflags(struct libmnt_context *cxt, unsigned long *flags)
{
int rc = 0;
- struct list_head *p;
if (!cxt || !flags)
return -EINVAL;
*flags = 0;
- if (!(cxt->flags & MNT_FL_MOUNTFLAGS_MERGED) && cxt->fs) {
- const char *o = mnt_fs_get_options(cxt->fs);
- if (o)
- rc = mnt_optstr_get_flags(o, flags,
- mnt_get_builtin_optmap(MNT_LINUX_MAP));
- }
-
- list_for_each(p, &cxt->addmounts) {
- struct libmnt_addmount *ad =
- list_entry(p, struct libmnt_addmount, mounts);
- *flags |= ad->mountflags;
- }
+ if (cxt->flags & MNT_FL_MOUNTFLAGS_MERGED)
+ /*
+ * Mount options already merged to the flags
+ */
+ *flags |= cxt->orig_mountflags;
+ else {
+ /*
+ * Mount options not yet processed by library, generate the
+ * flags on-the fly
+ */
+ if (cxt->fs) {
+ const char *o = mnt_fs_get_options(cxt->fs);
- if (!rc)
+ if (o)
+ rc = mnt_optstr_get_flags(o, flags,
+ mnt_get_builtin_optmap(MNT_LINUX_MAP));
+ }
+ /* Add flags defined by mnt_context_set_mflags */
*flags |= cxt->mountflags;
+ }
return rc;
}
return rc;
}
+/*
+ * Create cxt->mountflags from options and already defined flags. Note that the
+ * flags may be later modified, the original is stored in cxt->orig_mountflags.
+ */
int mnt_context_merge_mflags(struct libmnt_context *cxt)
{
unsigned long fl = 0;
rc = mnt_context_get_mflags(cxt, &fl);
if (rc)
return rc;
- cxt->mountflags = fl;
+ cxt->orig_mountflags = cxt->mountflags = fl;
fl = 0;
rc = mnt_context_get_user_mflags(cxt, &fl);
cxt->mountflags, cxt->user_mountflags));
cxt->flags |= MNT_FL_MOUNTFLAGS_MERGED;
+
+ if (cxt->mountflags & MS_PROPAGATION) {
+ unsigned long rest = cxt->mountflags & ~MS_PROPAGATION;
+
+ if (rest == 0 || rest == MS_SILENT)
+ cxt->is_propagation_only = 1;
+ }
+
return 0;
}
if (cxt->action != MNT_ACT_MOUNT)
return 0;
- /* has to be called after context_mount.c: fix_opts() */
- assert((cxt->flags & MNT_FL_MOUNTOPTS_FIXED));
+ assert((cxt->flags & MNT_FL_MOUNTFLAGS_MERGED));
- /* all propagation mounts are in cxt->addmount */
- return !list_empty(&cxt->addmounts)
- && (cxt->mountflags == 0 || cxt->mountflags == MS_SILENT)
+ return cxt->is_propagation_only
+ && cxt->mountdata == NULL
&& cxt->fs
+ && cxt->fs->fs_optstr == NULL
&& (!cxt->fs->fstype || strcmp(cxt->fs->fstype, "none") == 0)
&& (!cxt->fs->source || strcmp(cxt->fs->source, "none") == 0);
}
#include "mountP.h"
#include "strutils.h"
-/*
- * Kernel supports only one MS_PROPAGATION flag change by one mount(2) syscall,
- * to bypass this restriction we call mount(2) per flag. It's really not a perfect
- * solution, but it's the same like to execute multiple mount(8) commands.
- *
- * We use cxt->addmounts (additional mounts) list to keep order of the requested
- * flags changes.
- */
-struct libmnt_addmount *mnt_new_addmount(void)
-{
- struct libmnt_addmount *ad = calloc(1, sizeof(*ad));
- if (!ad)
- return NULL;
-
- INIT_LIST_HEAD(&ad->mounts);
- return ad;
-}
-
-void mnt_free_addmount(struct libmnt_addmount *ad)
-{
- if (!ad)
- return;
- list_del(&ad->mounts);
- free(ad);
-}
-
-static int mnt_context_append_additional_mount(struct libmnt_context *cxt,
- struct libmnt_addmount *ad)
-{
- assert(cxt);
- assert(ad);
-
- if (!list_empty(&ad->mounts))
- return -EINVAL;
-
- DBG(CXT, ul_debugobj(cxt,
- "mount: add additional flag: 0x%08lx",
- ad->mountflags));
-
- list_add_tail(&ad->mounts, &cxt->addmounts);
- return 0;
-}
-
-/*
- * add additional mount(2) syscall requests when necessary to set propagation flags
- * after regular mount(2).
- */
-static int init_propagation(struct libmnt_context *cxt)
-{
- char *name;
- char *opts = (char *) mnt_fs_get_vfs_options(cxt->fs);
- size_t namesz;
- struct libmnt_optmap const *maps[1];
- int rec_count = 0;
-
- if (!opts)
- return 0;
-
- DBG(CXT, ul_debugobj(cxt, "mount: initialize additional propagation mounts"));
-
- maps[0] = mnt_get_builtin_optmap(MNT_LINUX_MAP);
-
- while (!mnt_optstr_next_option(&opts, &name, &namesz, NULL, NULL)) {
- const struct libmnt_optmap *ent;
- struct libmnt_addmount *ad;
- int rc;
-
- if (!mnt_optmap_get_entry(maps, 1, name, namesz, &ent) || !ent)
- continue;
-
- DBG(CXT, ul_debugobj(cxt, " checking %s", ent->name));
-
- /* Note that MS_REC may be used for more flags, so we have to keep
- * track about number of recursive options to keep the MS_REC in the
- * mountflags if necessary.
- */
- if (ent->id & MS_REC)
- rec_count++;
-
- if (!(ent->id & MS_PROPAGATION))
- continue;
-
- ad = mnt_new_addmount();
- if (!ad)
- return -ENOMEM;
-
- ad->mountflags = ent->id;
- DBG(CXT, ul_debugobj(cxt, " adding extra mount(2) call for %s", ent->name));
- rc = mnt_context_append_additional_mount(cxt, ad);
- if (rc)
- return rc;
-
- DBG(CXT, ul_debugobj(cxt, " removing %s from primary mount(2) call", ent->name));
- cxt->mountflags &= ~ent->id;
-
- if (ent->id & MS_REC)
- rec_count--;
- }
-
- if (rec_count)
- cxt->mountflags |= MS_REC;
-
- return 0;
-}
-
-/*
- * add additional mount(2) syscall request to implement "bind,<flags>", the first regular
- * mount(2) is the "bind" operation, the second is "remount,bind,<flags>" call.
- */
-static int init_bind_remount(struct libmnt_context *cxt)
-{
- struct libmnt_addmount *ad;
- int rc;
-
- assert(cxt);
- assert(cxt->mountflags & MS_BIND);
- assert(!(cxt->mountflags & MS_REMOUNT));
-
- DBG(CXT, ul_debugobj(cxt, "mount: initialize additional ro,bind mount"));
-
- ad = mnt_new_addmount();
- if (!ad)
- return -ENOMEM;
-
- ad->mountflags = cxt->mountflags;
- ad->mountflags |= (MS_REMOUNT | MS_BIND);
-
- rc = mnt_context_append_additional_mount(cxt, ad);
- if (rc)
- return rc;
-
- return 0;
-}
-
#if defined(HAVE_LIBSELINUX) || defined(HAVE_SMACK)
struct libmnt_optname {
const char *name;
free(fs->user_optstr);
fs->user_optstr = NULL;
}
- if (cxt->mountflags & MS_PROPAGATION) {
- rc = init_propagation(cxt);
- if (rc)
- return rc;
- }
- if ((cxt->mountflags & MS_BIND)
- && (cxt->mountflags & MNT_BIND_SETTABLE)
- && !(cxt->mountflags & MS_REMOUNT)) {
- rc = init_bind_remount(cxt);
- if (rc)
- return rc;
- }
+
next = fs->fs_optstr;
goto done;
}
-
if (!rc && mnt_context_is_restricted(cxt) && (cxt->user_mountflags & MNT_MS_USER)) {
ns_old = mnt_context_switch_origin_ns(cxt);
if (!ns_old)
return -MNT_ERR_NAMESPACE;
}
+ mnt_context_call_hooks(cxt, MNT_STAGE_PREP_OPTIONS);
+
/* refresh merged optstr */
free(fs->optstr);
fs->optstr = NULL;
return rc;
}
-static int do_mount_additional(struct libmnt_context *cxt,
- const char *target,
- unsigned long flags,
- int *syserr)
-{
- struct list_head *p;
-
- assert(cxt);
- assert(target);
-
- if (syserr)
- *syserr = 0;
-
- list_for_each(p, &cxt->addmounts) {
- int rc;
- struct libmnt_addmount *ad =
- list_entry(p, struct libmnt_addmount, mounts);
-
- DBG(CXT, ul_debugobj(cxt, "mount(2) changing flag: 0x%08lx %s",
- ad->mountflags,
- ad->mountflags & MS_REC ? " (recursive)" : ""));
-
- rc = mount("none", target, NULL,
- ad->mountflags | (flags & MS_SILENT), NULL);
- if (rc) {
- if (syserr)
- *syserr = -errno;
- DBG(CXT, ul_debugobj(cxt,
- "mount(2) failed [errno=%d %m]",
- errno));
- return rc;
- }
- }
-
- return 0;
-}
-
static int do_mount_subdir(struct libmnt_context *cxt,
const char *root,
const char *subdir,
static int do_mount(struct libmnt_context *cxt, const char *try_type)
{
int rc = 0, old_ns_fd = -1;
- const char *src, *target, *type;
- unsigned long flags;
+ char *org_target = NULL, *org_type = NULL;
assert(cxt);
assert(cxt->fs);
return rc;
}
- flags = cxt->mountflags;
- src = mnt_fs_get_srcpath(cxt->fs);
- target = mnt_fs_get_target(cxt->fs);
-
if (cxt->helper) {
rc = exec_helper(cxt);
if (mnt_context_helper_executed(cxt)
&& mnt_context_get_helper_status(cxt) == 0
- && !list_empty(&cxt->addmounts)
- && do_mount_additional(cxt, target, flags, NULL))
-
+ && mnt_context_call_hooks(cxt, MNT_STAGE_MOUNT_POST))
return -MNT_ERR_APPLYFLAGS;
+
return rc;
}
- if (!target)
- return -EINVAL;
- if (!src) {
- /* unnecessary, should be already resolved in
- * mnt_context_prepare_srcpath(), but to be sure... */
- DBG(CXT, ul_debugobj(cxt, "WARNING: source is NULL -- using \"none\"!"));
- src = "none";
+ if (try_type) {
+ cxt->mountflags |= MS_SILENT;
+ if (mnt_fs_get_fstype(cxt->fs)) {
+ org_type = strdup(mnt_fs_get_fstype(cxt->fs));
+ if (!org_type) {
+ rc = -ENOMEM;
+ goto done;
+ }
+ }
+ mnt_fs_set_fstype(cxt->fs, try_type);
}
- type = try_type ? : mnt_fs_get_fstype(cxt->fs);
-
- if (try_type)
- flags |= MS_SILENT;
-
-
- if (mnt_context_is_fake(cxt)) {
- /*
- * fake
- */
- cxt->syscall_status = 0;
-
- DBG(CXT, ul_debugobj(cxt, "FAKE mount(2) "
- "[source=%s, target=%s, type=%s,"
- " mountflags=0x%08lx, mountdata=%s]",
- src, target, type,
- flags, cxt->mountdata ? "yes" : "<none>"));
-
- } else if (mnt_context_propagation_only(cxt)) {
- /*
- * propagation flags *only*
- */
- if (do_mount_additional(cxt, target, flags, &cxt->syscall_status))
- return -MNT_ERR_APPLYFLAGS;
- } else {
- /*
- * regular mount
- */
- /* create unhared temporary target */
- if (cxt->subdir) {
- rc = mnt_tmptgt_unshare(&old_ns_fd);
- if (rc)
- return rc;
- target = MNT_PATH_TMPTGT;
- }
- DBG(CXT, ul_debugobj(cxt, "mount(2) "
- "[source=%s, target=%s, type=%s,"
- " mountflags=0x%08lx, mountdata=%s]",
- src, target, type,
- flags, cxt->mountdata ? "yes" : "<none>"));
-
- if (mount(src, target, type, flags, cxt->mountdata)) {
- cxt->syscall_status = -errno;
- DBG(CXT, ul_debugobj(cxt, "mount(2) failed [errno=%d %m]",
- -cxt->syscall_status));
- rc = -cxt->syscall_status;
+ /* create unhared temporary target (TODO: use MOUNT_PRE hook) */
+ if (cxt->subdir) {
+ org_target = strdup(mnt_fs_get_target(cxt->fs));
+ if (!org_target) {
+ rc = -ENOMEM;
goto done;
}
- DBG(CXT, ul_debugobj(cxt, " mount(2) success"));
- cxt->syscall_status = 0;
+ rc = mnt_tmptgt_unshare(&old_ns_fd);
+ if (rc)
+ goto done;
+ mnt_fs_set_target(cxt->fs, MNT_PATH_TMPTGT);
+ }
- /*
- * additional mounts for extra propagation flags
- */
- if (!list_empty(&cxt->addmounts)
- && do_mount_additional(cxt, target, flags, NULL)) {
+ /*
+ * mount(2) or others syscalls
+ */
+ rc = mnt_context_call_hooks(cxt, MNT_STAGE_MOUNT_PRE);
+ if (!rc)
+ rc = mnt_context_call_hooks(cxt, MNT_STAGE_MOUNT);
+ if (!rc)
+ rc = mnt_context_call_hooks(cxt, MNT_STAGE_MOUNT_POST);
- /* TODO: call umount? */
- rc = -MNT_ERR_APPLYFLAGS;
- goto done;
- }
+ if (org_target)
+ __mnt_fs_set_target_ptr(cxt->fs, org_target);
+ if (org_type && rc != 0)
+ __mnt_fs_set_fstype_ptr(cxt->fs, org_type);
+ org_target = org_type = NULL;
- /*
- * bind subdir to the real target, umount temporary target
- */
- if (cxt->subdir) {
- target = mnt_fs_get_target(cxt->fs);
- rc = do_mount_subdir(cxt, MNT_PATH_TMPTGT, cxt->subdir, target);
- if (rc)
- goto done;
- mnt_tmptgt_cleanup(old_ns_fd);
- old_ns_fd = -1;
- }
+ /*
+ * bind subdir to the real target, umount temporary target
+ * (TODO: use MOUNT_POST hook)
+ */
+ if (rc == 0 && cxt->subdir) {
+ rc = do_mount_subdir(cxt, MNT_PATH_TMPTGT, cxt->subdir,
+ mnt_fs_get_target(cxt->fs));
+ if (rc)
+ goto done;
+ mnt_tmptgt_cleanup(old_ns_fd);
+ old_ns_fd = -1;
}
- if (try_type && cxt->update) {
+ if (rc == 0 && try_type && cxt->update) {
struct libmnt_fs *fs = mnt_update_get_fs(cxt->update);
if (fs)
rc = mnt_fs_set_fstype(fs, try_type);
done:
if (old_ns_fd >= 0)
mnt_tmptgt_cleanup(old_ns_fd);
-
+ if (try_type)
+ cxt->mountflags &= ~MS_SILENT;
+ free(org_target);
+ free(org_type);
return rc;
}
rc = mnt_context_merge_mflags(cxt);
if (!rc)
rc = evaluate_permissions(cxt);
+ if (!rc)
+ rc = mnt_context_init_hooksets(cxt);
if (!rc)
rc = fix_optstr(cxt);
if (!rc)
goto again;
}
}
+
+ mnt_context_deinit_hooksets(cxt);
+
if (!mnt_context_switch_ns(cxt, ns_old))
return -MNT_ERR_NAMESPACE;
+
return rc;
}
return strdup_to_struct_member(fs, target, tgt);
}
+int __mnt_fs_set_target_ptr(struct libmnt_fs *fs, char *tgt)
+{
+ free(fs->target);
+ fs->target = tgt;
+ return 0;
+}
+
static int mnt_fs_get_flags(struct libmnt_fs *fs)
{
return fs ? fs->flags : 0;
--- /dev/null
+/* 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.
+ */
+
+#include "mountP.h"
+
+static int hook_prepare(struct libmnt_context *cxt, const struct libmnt_hookset *hs, void *data);
+
+struct hook_data {
+ unsigned long mountflags;
+};
+
+static int hookset_init(struct libmnt_context *cxt, const struct libmnt_hookset *hs)
+{
+#ifdef UL_HAVE_MOUNT_API
+ /* do nothing when __builtin-mount succesfully registred */
+ if (mnt_context_has_hook(cxt, &hookset_mount, 0, NULL))
+ return 0;
+#endif
+
+ DBG(HOOK, ul_debugobj(hs, "init '%s'", hs->name));
+
+ /* add very basic callback */
+ return mnt_context_append_hook(cxt, hs,
+ MNT_STAGE_PREP_OPTIONS, NULL, hook_prepare);
+}
+
+static int hookset_deinit(struct libmnt_context *cxt, const struct libmnt_hookset *hs)
+{
+ void *data = NULL;
+
+ DBG(HOOK, ul_debugobj(hs, "deinit '%s'", hs->name));
+
+ /* remove all our hooks and free hook data */
+ while (mnt_context_remove_hook(cxt, hs, 0, &data) == 0) {
+ if (data)
+ free(data);
+ data = NULL;
+ }
+
+ return 0;
+}
+
+static struct hook_data *new_hook_data(void)
+{
+ return calloc(1, sizeof(struct hook_data));
+}
+
+/* call mount(2) for propagation flags */
+static int hook_propagation(struct libmnt_context *cxt,
+ const struct libmnt_hookset *hs,
+ void *data)
+{
+ int rc;
+ struct hook_data *hd = (struct hook_data *) data;
+
+ assert(hd);
+ assert(cxt);
+ assert(cxt->fs);
+
+ DBG(HOOK, ul_debugobj(hs, " calling mount(2) for propagation: 0x%08lx %s",
+ hd->mountflags,
+ hd->mountflags & MS_REC ? " (recursive)" : ""));
+
+ if (mnt_context_is_fake(cxt)) {
+ DBG(CXT, ul_debugobj(cxt, " FAKE (-f)"));
+ cxt->syscall_status = 0;
+ return 0;
+ }
+
+ /*
+ * hd->mountflags are propagation flags as set in prepare_propagation()
+ *
+ * cxt->mountflags are global mount flags, may be modified after
+ * preparation stage (for example when libmount blindly tries FS type then
+ * it uses MS_SILENT)
+ */
+ rc = mount("none", mnt_fs_get_target(cxt->fs), NULL,
+ hd->mountflags | (cxt->mountflags & MS_SILENT), NULL);
+
+ if (rc) {
+ /* Update global syscall status if only this function called */
+ if (mnt_context_propagation_only(cxt))
+ cxt->syscall_status = -errno;
+
+ DBG(HOOK, ul_debugobj(hs, " mount(2) failed [errno=%d %m]", errno));
+ rc = -MNT_ERR_APPLYFLAGS;
+ }
+ return rc;
+}
+
+/*
+ * add additional mount(2) syscalls to set propagation flags after regular mount(2).
+ */
+static int prepare_propagation(struct libmnt_context *cxt,
+ const struct libmnt_hookset *hs)
+{
+ char *name;
+ char *opts;
+ size_t namesz;
+ struct libmnt_optmap const *maps[1];
+ int rec_count = 0;
+
+ assert(cxt);
+ assert(cxt->fs);
+
+ opts = (char *) mnt_fs_get_vfs_options(cxt->fs);
+ if (!opts)
+ return 0;
+
+ maps[0] = mnt_get_builtin_optmap(MNT_LINUX_MAP);
+
+ while (!mnt_optstr_next_option(&opts, &name, &namesz, NULL, NULL)) {
+ struct hook_data *data;
+ const struct libmnt_optmap *ent;
+ int rc;
+
+ if (!mnt_optmap_get_entry(maps, 1, name, namesz, &ent) || !ent)
+ continue;
+
+ /* Note that MS_REC may be used for more flags, so we have to keep
+ * track about number of recursive options to keep the MS_REC in the
+ * mountflags if necessary.
+ */
+ if (ent->id & MS_REC)
+ rec_count++;
+
+ if (!(ent->id & MS_PROPAGATION))
+ continue;
+
+ data = new_hook_data();
+ if (!data)
+ return -ENOMEM;
+ data->mountflags = ent->id;
+
+ DBG(HOOK, ul_debugobj(hs, " adding mount(2) call for %s", ent->name));
+ rc = mnt_context_append_hook(cxt, hs,
+ MNT_STAGE_MOUNT_POST,
+ data,
+ hook_propagation);
+ if (rc)
+ return rc;
+
+ DBG(HOOK, ul_debugobj(hs, " removing '%s' flag from primary mount(2)", ent->name));
+ cxt->mountflags &= ~ent->id;
+
+ if (ent->id & MS_REC)
+ rec_count--;
+ }
+
+ if (rec_count)
+ cxt->mountflags |= MS_REC;
+
+ return 0;
+}
+
+/* call mount(2) for bind,remount */
+static int hook_bindremount(struct libmnt_context *cxt,
+ const struct libmnt_hookset *hs, void *data)
+{
+ int rc;
+ struct hook_data *hd = (struct hook_data *) data;
+
+ DBG(HOOK, ul_debugobj(hs, " mount(2) for bind-remount: 0x%08lx %s",
+ hd->mountflags,
+ hd->mountflags & MS_REC ? " (recursive)" : ""));
+
+ if (mnt_context_is_fake(cxt)) {
+ DBG(CXT, ul_debugobj(cxt, " FAKE (-f)"));
+ cxt->syscall_status = 0;
+ return 0;
+ }
+
+ /* for the flags see comment in hook_propagation() */
+ rc = mount("none", mnt_fs_get_target(cxt->fs), NULL,
+ hd->mountflags | (cxt->mountflags & MS_SILENT), NULL);
+
+ if (rc)
+ DBG(HOOK, ul_debugobj(hs, " mount(2) failed"
+ " [rc=%d errno=%d %m]", rc, errno));
+ return rc;
+}
+
+/*
+ * add additional mount(2) syscall request to implement "bind,<flags>", the first regular
+ * mount(2) is the "bind" operation, the second is "remount,bind,<flags>" call.
+ */
+static int prepare_bindremount(struct libmnt_context *cxt,
+ const struct libmnt_hookset *hs)
+{
+ struct hook_data *data;
+ int rc;
+
+ assert(cxt);
+ assert(cxt->mountflags & MS_BIND);
+ assert(!(cxt->mountflags & MS_REMOUNT));
+
+ DBG(HOOK, ul_debugobj(hs, " adding mount(2) call for bint-remount"));
+
+ data = new_hook_data();
+ if (!data)
+ return -ENOMEM;
+
+ data->mountflags = cxt->mountflags;
+ data->mountflags |= (MS_REMOUNT | MS_BIND);
+
+ rc = mnt_context_append_hook(cxt, hs,
+ MNT_STAGE_MOUNT_POST, data, hook_bindremount);
+ return rc;
+}
+
+
+
+
+/* call mount(2) for regular FS mount, mount flags and options are read from
+ * library context struct. There are no private hook data.
+ */
+static int hook_mount(struct libmnt_context *cxt,
+ const struct libmnt_hookset *hs,
+ void *data __attribute__((__unused__)))
+{
+ int rc = 0;
+ const char *src, *target, *type;
+
+ src = mnt_fs_get_srcpath(cxt->fs);
+ target = mnt_fs_get_target(cxt->fs);
+ type = mnt_fs_get_fstype(cxt->fs);
+
+ if (!target)
+ return -EINVAL;
+ if (!src)
+ src = "none";
+
+ DBG(HOOK, ul_debugobj(hs, " mount(2) "
+ "[source=%s, target=%s, type=%s,"
+ " mountflags=0x%08lx, mountdata=%s]",
+ src, target, type,
+ cxt->mountflags, cxt->mountdata ? "yes" : "<none>"));
+
+ if (mnt_context_is_fake(cxt)) {
+ DBG(HOOK, ul_debugobj(hs, " FAKE (-f)"));
+ cxt->syscall_status = 0;
+ return 0;
+ }
+
+ if (mount(src, target, type, cxt->mountflags, cxt->mountdata)) {
+ cxt->syscall_status = -errno;
+ DBG(HOOK, ul_debugobj(hs, " mount(2) failed [errno=%d %m]",
+ -cxt->syscall_status));
+ rc = -cxt->syscall_status;
+ return rc;
+ }
+
+ cxt->syscall_status = 0;
+ return rc;
+}
+
+/*
+ * analyze library context and register hooks to call one or more mount(2) syscalls
+ */
+static int hook_prepare(struct libmnt_context *cxt,
+ const struct libmnt_hookset *hs,
+ void *data __attribute__((__unused__)))
+{
+ int rc = 0;
+
+ assert(cxt);
+ assert(hs == &hookset_mount_legacy);
+
+ /* add extra mount(2) calls for each propagation flag */
+ if (cxt->mountflags & MS_PROPAGATION) {
+ rc = prepare_propagation(cxt, hs);
+ if (rc)
+ return rc;
+ }
+
+ /* add extra mount(2) call to implement "bind,remount,<flags>" */
+ if ((cxt->mountflags & MS_BIND)
+ && (cxt->mountflags & MNT_BIND_SETTABLE)
+ && !(cxt->mountflags & MS_REMOUNT)) {
+ rc = prepare_bindremount(cxt, hs);
+ if (rc)
+ return rc;
+ }
+
+ /* append regual FS mount(2) */
+ if (!mnt_context_propagation_only(cxt))
+ rc = mnt_context_append_hook(cxt, hs,
+ MNT_STAGE_MOUNT, NULL, hook_mount);
+
+ return rc;
+}
+
+
+const struct libmnt_hookset hookset_mount_legacy =
+{
+ .name = "__legacy-mount",
+ .init = hookset_init,
+ .deinit = hookset_deinit
+};
--- /dev/null
+/* 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.
+ */
+
+/**
+ * SECTION: hookset
+ * @title: Mount context hooks
+ * @short_description: extensions to mount process
+ */
+
+#include "mountP.h"
+
+/* built-in hooksets */
+static const struct libmnt_hookset *hooksets[] = {
+#ifdef __linux__
+ &hookset_mount_legacy
+#endif
+};
+
+/* hooksets data */
+struct hookset_data {
+ const struct libmnt_hookset *hookset;
+ void *data;
+
+ struct list_head datas;
+};
+
+/* individial callback */
+struct hookset_hook {
+ const struct libmnt_hookset *hookset;
+ int stage;
+ void *data;
+
+ int (*func)(struct libmnt_context *, const struct libmnt_hookset *, void *);
+
+ struct list_head hooks;
+};
+
+static const char *stagenames[] = {
+ /* prepare */
+ [MNT_STAGE_PREP_SOURCE] = "prep-source",
+ [MNT_STAGE_PREP_TARGET] = "prep-target",
+ [MNT_STAGE_PREP_OPTIONS] = "prep-options",
+ /* mount */
+ [MNT_STAGE_MOUNT_PRE] = "pre-mount",
+ [MNT_STAGE_MOUNT] = "mount",
+ [MNT_STAGE_MOUNT_POST] = "post-mount",
+ /* post */
+ [MNT_STAGE_POST] = "post",
+};
+
+int mnt_context_init_hooksets(struct libmnt_context *cxt)
+{
+ size_t i;
+ int rc = 0;
+
+ assert(cxt);
+
+ for (i = 0; i < ARRAY_SIZE(hooksets); i++) {
+ const struct libmnt_hookset *hs = hooksets[i];
+
+ rc = hs->init(cxt, hs);
+ if (rc < 0)
+ break;
+ }
+
+ if (rc < 0)
+ mnt_context_deinit_hooksets(cxt);
+ return rc;
+}
+
+int mnt_context_deinit_hooksets(struct libmnt_context *cxt)
+{
+ size_t i;
+ int rc = 0;
+
+ assert(cxt);
+
+ if (list_empty(&cxt->hooksets_hooks))
+ return 0;
+
+ for (i = 0; i < ARRAY_SIZE(hooksets); i++) {
+ const struct libmnt_hookset *hs = hooksets[i];
+
+ rc += hs->deinit(cxt, hs);
+ }
+
+ assert(list_empty(&cxt->hooksets_datas));
+ assert(list_empty(&cxt->hooksets_hooks));
+
+ INIT_LIST_HEAD(&cxt->hooksets_datas);
+ INIT_LIST_HEAD(&cxt->hooksets_hooks);
+
+ return rc;
+}
+
+const struct libmnt_hookset *mnt_context_get_hookset(
+ struct libmnt_context *cxt, const char *name)
+{
+ size_t i;
+
+ assert(cxt);
+ assert(name);
+
+ for (i = 0; i < ARRAY_SIZE(hooksets); i++) {
+ const struct libmnt_hookset *hs = hooksets[i];
+
+ if (strcmp(name, hs->name) == 0)
+ return hs;
+ }
+
+ return NULL;
+}
+
+static struct hookset_data *get_hookset_data(
+ struct libmnt_context *cxt,
+ const struct libmnt_hookset *hs)
+{
+ struct list_head *p;
+
+ assert(cxt);
+ assert(hs);
+
+ list_for_each(p, &cxt->hooksets_datas) {
+ struct hookset_data *x = list_entry(p, struct hookset_data, datas);
+
+ if (x->hookset == hs)
+ return x;
+ }
+ return 0;
+}
+
+int mnt_context_set_hookset_data(struct libmnt_context *cxt,
+ const struct libmnt_hookset *hs,
+ void *data)
+{
+ struct hookset_data *hd = NULL;
+
+ hd = get_hookset_data(cxt, hs);
+
+ /* deallocate old data */
+ if (data == NULL) {
+ if (hd) {
+ list_del(&hd->datas);
+ free(hd);
+ }
+ return 0;
+ }
+
+ /* create and append new data */
+ if (!hd) {
+ hd = calloc(1, sizeof(*hd));
+ if (!hd)
+ return -ENOMEM;
+
+ DBG(CXT, ul_debugobj(cxt, "alloc '%s' data", hs->name));
+ INIT_LIST_HEAD(&hd->datas);
+ hd->hookset = hs;
+ list_add_tail(&hd->datas, &cxt->hooksets_datas);
+
+ }
+ hd->data = data;
+ return 0;
+}
+
+void *mnt_context_get_hookset_data(struct libmnt_context *cxt,
+ const struct libmnt_hookset *hs)
+{
+ struct hookset_data *hd = get_hookset_data(cxt, hs);
+
+ return hd ? hd->data : NULL;
+}
+
+int mnt_context_append_hook(struct libmnt_context *cxt,
+ const struct libmnt_hookset *hs,
+ int stage,
+ void *data,
+ int (*func)(struct libmnt_context *,
+ const struct libmnt_hookset *,
+ void *))
+{
+ struct hookset_hook *hook;
+
+ assert(cxt);
+ assert(hs);
+ assert(stage);
+
+ hook = calloc(1, sizeof(*hook));
+ if (!hook)
+ return -ENOMEM;
+
+ DBG(CXT, ul_debugobj(cxt, " appending %s hook from %s",
+ stagenames[stage], hs->name));
+
+ INIT_LIST_HEAD(&hook->hooks);
+
+ hook->hookset = hs;
+ hook->data = data;
+ hook->func = func;
+ hook->stage = stage;
+
+ list_add_tail(&hook->hooks, &cxt->hooksets_hooks);
+ return 0;
+}
+
+static struct hookset_hook *get_hookset_hook(struct libmnt_context *cxt,
+ const struct libmnt_hookset *hs,
+ int stage,
+ void *data)
+{
+ struct list_head *p, *next;
+
+ assert(cxt);
+
+ list_for_each_safe(p, next, &cxt->hooksets_hooks) {
+ struct hookset_hook *x = list_entry(p, struct hookset_hook, hooks);
+
+ if (hs && x->hookset != hs)
+ continue;
+ if (stage && x->stage != stage)
+ continue;
+ if (data && x->data != data)
+ continue;
+ return x;
+ }
+
+ return NULL;
+}
+
+int mnt_context_remove_hook(struct libmnt_context *cxt,
+ const struct libmnt_hookset *hs,
+ int stage,
+ void **data)
+{
+ struct hookset_hook *hook;
+
+ assert(cxt);
+
+ hook = get_hookset_hook(cxt, hs, stage, NULL);
+ if (hook) {
+ DBG(CXT, ul_debugobj(cxt, " removing %s hook from %s",
+ stagenames[hook->stage], hook->hookset->name));
+
+ if (data)
+ *data = hook->data;
+
+ list_del(&hook->hooks);
+ free(hook);
+ return 0;
+ }
+
+ return 1;
+}
+
+int mnt_context_has_hook(struct libmnt_context *cxt,
+ const struct libmnt_hookset *hs,
+ int stage,
+ void *data)
+{
+ return get_hookset_hook(cxt, hs, stage, data) ? 1 : 0;
+}
+
+int mnt_context_call_hooks(struct libmnt_context *cxt, int stage)
+{
+ struct list_head *p, *next;
+
+ 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));
+
+ rc = x->func(cxt, x->hookset, x->data);
+ if (rc < 0)
+ return rc;
+ }
+
+ return 0;
+}
{ "diff", MNT_DEBUG_DIFF, "mountinfo changes tracking" },
{ "fs", MNT_DEBUG_FS, "FS abstraction" },
{ "help", MNT_DEBUG_HELP, "this help" },
+ { "hook", MNT_DEBUG_HOOK, "hooks functionality" },
{ "locks", MNT_DEBUG_LOCKS, "mtab and utab locking" },
{ "loop", MNT_DEBUG_LOOP, "loop devices routines" },
{ "options", MNT_DEBUG_OPTIONS, "mount options parsing" },
#define MNT_DEBUG_BTRFS (1 << 12)
#define MNT_DEBUG_LOOP (1 << 13)
#define MNT_DEBUG_VERITY (1 << 14)
+#define MNT_DEBUG_HOOK (1 << 15)
#define MNT_DEBUG_ALL 0xFFFF
};
/*
- * Additional mounts
+ * Context hooks
+ *
+ * TODO: this will be public one day when libmount will support modules for
+ * stuff like veritydev.c.
*/
-struct libmnt_addmount {
- unsigned long mountflags;
+enum {
+ MNT_STAGE_PREP_SOURCE = 1,
+ MNT_STAGE_PREP_TARGET,
+ MNT_STAGE_PREP_OPTIONS,
+
+ MNT_STAGE_MOUNT_PRE = 100,
+ MNT_STAGE_MOUNT,
+ MNT_STAGE_MOUNT_POST,
+
+ MNT_STAGE_POST = 200
+};
- struct list_head mounts;
+struct libmnt_hookset {
+ const char *name; /* hook set name */
+ int (*init)(struct libmnt_context *, const struct libmnt_hookset *); /* initialization function */
+ int (*deinit)(struct libmnt_context *, const struct libmnt_hookset *); /* cleanup function */
};
+/* built-in hooks */
+extern const struct libmnt_hookset hookset_mount_legacy;
+
+
+extern int mnt_context_init_hooksets(struct libmnt_context *cxt);
+extern int mnt_context_deinit_hooksets(struct libmnt_context *cxt);
+extern const struct libmnt_hookset *mnt_context_get_hookset(struct libmnt_context *cxt, const char *name);
+
+extern int mnt_context_set_hookset_data(struct libmnt_context *cxt,
+ const struct libmnt_hookset *hs,
+ void *data);
+
+extern void *mnt_context_get_hookset_data(struct libmnt_context *cxt,
+ const struct libmnt_hookset *hs);
+
+extern int mnt_context_has_hook(struct libmnt_context *cxt,
+ const struct libmnt_hookset *hs,
+ int stage,
+ void *data);
+
+extern int mnt_context_append_hook(struct libmnt_context *cxt,
+ const struct libmnt_hookset *hs,
+ int stage,
+ void *data,
+ int (*func)(struct libmnt_context *,
+ const struct libmnt_hookset *,
+ void *));
+
+extern int mnt_context_remove_hook(struct libmnt_context *cxt,
+ const struct libmnt_hookset *hs,
+ int stage,
+ void **data);
+extern int mnt_context_call_hooks(struct libmnt_context *cxt, int stage);
+
+/*
+ * Namespace
+ */
struct libmnt_ns {
int fd; /* file descriptor of namespace, -1 when inactive */
struct libmnt_cache *cache; /* paths cache associated with NS */
const void *mountdata; /* final mount(2) data, string or binary data */
unsigned long user_mountflags; /* MNT_MS_* (loop=, user=, ...) */
-
- struct list_head addmounts; /* additional mounts */
+ unsigned long orig_mountflags; /* original flags (see mnt_context_merge_mflags()) */
struct libmnt_cache *cache; /* paths cache */
struct libmnt_lock *lock; /* utab lock */
int nchildren; /* number of children */
pid_t pid; /* 0=parent; PID=child */
-
int syscall_status; /* 1: not called yet, 0: success, <0: -errno */
struct libmnt_ns ns_orig; /* original namespace */
struct libmnt_ns *ns_cur; /* pointer to current namespace */
unsigned int enabled_textdomain : 1; /* bindtextdomain() called */
- unsigned int noautofs : 1; /* ignore autofs mounts */
+ unsigned int noautofs : 1; /* ignore autofs mounts */
+ unsigned int is_propagation_only : 1;
+
+ struct list_head hooksets_datas; /* global hooksets data */
+ struct list_head hooksets_hooks; /* global hooksets data */
};
/* flags */
__attribute__((nonnull(1)));
extern int __mnt_fs_set_fstype_ptr(struct libmnt_fs *fs, char *fstype)
__attribute__((nonnull(1)));
+extern int __mnt_fs_set_target_ptr(struct libmnt_fs *fs, char *tgt)
+ __attribute__((nonnull(1)));
/* context.c */
extern struct libmnt_context *mnt_copy_context(struct libmnt_context *o);
extern int mnt_context_propagation_only(struct libmnt_context *cxt)
__attribute__((nonnull));
-extern struct libmnt_addmount *mnt_new_addmount(void);
-extern void mnt_free_addmount(struct libmnt_addmount *ad);
-
extern int mnt_context_setup_loopdev(struct libmnt_context *cxt);
extern int mnt_context_delete_loopdev(struct libmnt_context *cxt);
extern int mnt_context_clear_loopdev(struct libmnt_context *cxt);