]> git.ipfire.org Git - thirdparty/util-linux.git/commitdiff
libmount: implement hooks for a legacy mount(2)
authorKarel Zak <kzak@redhat.com>
Thu, 19 May 2022 11:55:43 +0000 (13:55 +0200)
committerKarel Zak <kzak@redhat.com>
Tue, 3 Jan 2023 11:52:37 +0000 (12:52 +0100)
* replaces context "addmounts" functionality with more generic hooks

* "hooksets" handles complex functionality by a set of hooks.

  During initialization (or later), the hookset can define
  arbitrary hook function(s). The library will call the functions from
  a specified place ("stage"). Now supported stages are
  prepare-options, pre-mount, mount, and post-mount.

  This solution looks complex at first glance, but it will help
  keep all extensions separated from core library code (IDs mapping,
  X-mount.chown/chmod, X-mount.subdir, etc.). It will also be possible to
  support multiple implementations for the same functionality (classic
  mount(2) vs. new fsmount(2)) without #ifdefs storms etc.

  Maybe later we can also use hooksets for external library modules
  (like verity support).

* __legacy-mount hookset implements support for the classic mount(2)
  syscall. Supported hooks:

  prepare-options - analyzes the current setting (libmnt_context)
    and setup other hooks when necessary

  mount - calls mount(2) for standard "/dev to /mnt" use-cases

  post-mount (two possible hooks):
      - calls mount(2) to modify propagation flags
      - calls mount(2) to implement bind-remount (mount --bind -oro)

Signed-off-by: Karel Zak <kzak@redhat.com>
libmount/meson.build
libmount/src/Makemodule.am
libmount/src/context.c
libmount/src/context_mount.c
libmount/src/fs.c
libmount/src/hook_mount_legacy.c [new file with mode: 0644]
libmount/src/hooks.c [new file with mode: 0644]
libmount/src/init.c
libmount/src/mountP.h

index d454e9f9a269ed9ed579385ff9f3e32fd42befc8..21c84ac95d201dd21f14c24949c15a761da8a06c 100644 (file)
@@ -17,6 +17,7 @@ configure_file(
 lib_mount_sources = '''
   src/mountP.h
   src/cache.c
+  src/hooks.c
   src/fs.c
   src/init.c
   src/iter.c
@@ -42,6 +43,7 @@ if LINUX
     src/context_veritydev.c
     src/context_mount.c
     src/context_umount.c
+    src/hook_mount_legacy.c
     src/monitor.c
 '''.split()
 endif
index c2579b0acc998e47f6d05e7d4ec7c5b71f77399f..2174eeb4c9e953165a3ddf66c160bda32b8b408c 100644 (file)
@@ -11,6 +11,7 @@ libmount_la_SOURCES = \
        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 \
@@ -31,6 +32,7 @@ libmount_la_SOURCES += \
        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
index a6d78953cdbea266bb6dd9f15766166e806e7a7c..2178627a7f8a9080c5951f603a75cc4f8316bd6c 100644 (file)
@@ -61,8 +61,6 @@ struct libmnt_context *mnt_new_context(void)
        cxt->tgt_group = (gid_t) -1;
        cxt->tgt_mode = (mode_t) -1;
 
-       INIT_LIST_HEAD(&cxt->addmounts);
-
        ruid = getuid();
        euid = geteuid();
 
@@ -74,6 +72,9 @@ struct libmnt_context *mnt_new_context(void)
        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;
 
@@ -112,6 +113,7 @@ void mnt_free_context(struct libmnt_context *cxt)
        mnt_free_update(cxt->update);
 
        mnt_context_set_target_ns(cxt, NULL);
+       mnt_context_deinit_hooksets(cxt);
 
        free(cxt->children);
 
@@ -170,20 +172,15 @@ int mnt_reset_context(struct libmnt_context *cxt)
        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);
@@ -308,6 +305,7 @@ struct libmnt_context *mnt_copy_context(struct libmnt_context *o)
 
        n->mountflags = o->mountflags;
        n->mountdata = o->mountdata;
+       n->is_propagation_only = o->is_propagation_only;
 
        mnt_context_reset_status(n);
 
@@ -1700,28 +1698,32 @@ int mnt_context_set_mflags(struct libmnt_context *cxt, unsigned long flags)
 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;
 }
 
@@ -2254,6 +2256,10 @@ int mnt_context_prepare_helper(struct libmnt_context *cxt, const char *name,
        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;
@@ -2266,7 +2272,7 @@ int mnt_context_merge_mflags(struct libmnt_context *cxt)
        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);
@@ -2278,6 +2284,14 @@ int mnt_context_merge_mflags(struct libmnt_context *cxt)
                        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;
 }
 
@@ -2667,13 +2681,12 @@ int mnt_context_propagation_only(struct libmnt_context *cxt)
        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);
 }
index 3eaa12f0f37156ee5bb9a29b491ace0b1b572dbe..6e8464a337e2183d6e755fc71f157954713b61d7 100644 (file)
 #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;
@@ -272,18 +138,7 @@ static int fix_optstr(struct libmnt_context *cxt)
                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;
 
@@ -353,7 +208,6 @@ static int fix_optstr(struct libmnt_context *cxt)
                        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)
@@ -365,6 +219,8 @@ static int fix_optstr(struct libmnt_context *cxt)
                        return -MNT_ERR_NAMESPACE;
        }
 
+       mnt_context_call_hooks(cxt, MNT_STAGE_PREP_OPTIONS);
+
        /* refresh merged optstr */
        free(fs->optstr);
        fs->optstr = NULL;
@@ -716,43 +572,6 @@ static int exec_helper(struct libmnt_context *cxt)
        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,
@@ -787,8 +606,7 @@ static int do_mount_subdir(struct libmnt_context *cxt,
 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);
@@ -800,108 +618,72 @@ static int do_mount(struct libmnt_context *cxt, const char *try_type)
                        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);
@@ -910,7 +692,10 @@ static int do_mount(struct libmnt_context *cxt, const char *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;
 }
 
@@ -1104,6 +889,8 @@ int mnt_context_prepare_mount(struct libmnt_context *cxt)
                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)
@@ -1397,8 +1184,12 @@ again:
                        goto again;
                }
        }
+
+       mnt_context_deinit_hooksets(cxt);
+
        if (!mnt_context_switch_ns(cxt, ns_old))
                return -MNT_ERR_NAMESPACE;
+
        return rc;
 }
 
index 180cfc862d2ee43492ba9b6fc91d0fa58e1801f7..46a22c75a7b6f86bb8248eef6a1697a1ef41403d 100644 (file)
@@ -552,6 +552,13 @@ int mnt_fs_set_target(struct libmnt_fs *fs, const char *tgt)
        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;
diff --git a/libmount/src/hook_mount_legacy.c b/libmount/src/hook_mount_legacy.c
new file mode 100644 (file)
index 0000000..a82b682
--- /dev/null
@@ -0,0 +1,308 @@
+/* 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
+};
diff --git a/libmount/src/hooks.c b/libmount/src/hooks.c
new file mode 100644 (file)
index 0000000..e5307bd
--- /dev/null
@@ -0,0 +1,291 @@
+/* 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;
+}
index 2410fcc3a29ebd7adcf69ea19be0ba4dbca87084..d7f46b7a2dd416bba5abf78102ea9a0edfc6b944 100644 (file)
@@ -29,6 +29,7 @@ UL_DEBUG_DEFINE_MASKNAMES(libmount) =
        { "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" },
index 6f8f03a331eecee4a30af8a31079d44c5c19b4c0..98e40d23a1b18c8aa0ee515a896921c86a2b56ee 100644 (file)
@@ -47,6 +47,7 @@
 #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
 
@@ -275,14 +276,66 @@ enum {
 };
 
 /*
- * 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 */
@@ -328,8 +381,7 @@ struct libmnt_context
        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 */
@@ -354,7 +406,6 @@ struct libmnt_context
        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 */
@@ -362,7 +413,11 @@ struct libmnt_context
        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 */
@@ -421,6 +476,8 @@ extern int __mnt_fs_set_source_ptr(struct libmnt_fs *fs, char *source)
                        __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);
@@ -450,9 +507,6 @@ extern int mnt_context_is_loopdev(struct libmnt_context *cxt)
 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);