]> git.ipfire.org Git - thirdparty/util-linux.git/commitdiff
libmount: reimplement loop= by hooks
authorKarel Zak <kzak@redhat.com>
Tue, 23 Aug 2022 09:55:51 +0000 (11:55 +0200)
committerKarel Zak <kzak@redhat.com>
Tue, 3 Jan 2023 11:58:42 +0000 (12:58 +0100)
Signed-off-by: Karel Zak <kzak@redhat.com>
libmount/src/context.c
libmount/src/context_loopdev.c
libmount/src/hooks.c
libmount/src/mountP.h

index 77f453b3e43a64e2adb6867a0422cad06cb8e8b3..f95437d88c9445600577440c6e8a360856085fb1 100644 (file)
@@ -61,8 +61,6 @@ struct libmnt_context *mnt_new_context(void)
 
        mnt_context_reset_status(cxt);
 
-       cxt->loopdev_fd = -1;
-
        cxt->ns_orig.fd = -1;
        cxt->ns_tgt.fd = -1;
        cxt->ns_cur = &cxt->ns_orig;
@@ -107,7 +105,6 @@ void mnt_free_context(struct libmnt_context *cxt)
        mnt_unref_fs(cxt->fs_template);
        mnt_unref_optlist(cxt->optlist);
 
-       mnt_context_clear_loopdev(cxt);
        mnt_free_lock(cxt->lock);
        mnt_free_update(cxt->update);
 
@@ -1854,6 +1851,9 @@ int mnt_context_prepare_srcpath(struct libmnt_context *cxt)
                goto end;
        }
 
+       rc = mnt_context_call_hooks(cxt, MNT_STAGE_PREP_SOURCE);
+       if (rc)
+               goto end;
 
        /*
         * Initialize verity or loop device
@@ -1868,10 +1868,6 @@ int mnt_context_prepare_srcpath(struct libmnt_context *cxt)
                rc = mnt_context_setup_veritydev(cxt);
                if (rc)
                        goto end;
-       } else if (mnt_context_is_loopdev(cxt)) {
-               rc = mnt_context_setup_loopdev(cxt);
-               if (rc)
-                       goto end;
        }
 
        DBG(CXT, ul_debugobj(cxt, "final srcpath '%s'",
index f160f0c9698d73150c0f37c4b564b77cb1170f74..652ae76377b11fd1956644d9c6dcc67881b3fa1a 100644 (file)
@@ -2,7 +2,7 @@
 /*
  * This file is part of libmount from util-linux project.
  *
- * Copyright (C) 2011-2018 Karel Zak <kzak@redhat.com>
+ * Copyright (C) 2011-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
 #include "strutils.h"
 #include "linux_version.h"
 
-int mnt_context_is_loopdev(struct libmnt_context *cxt)
-{
-       int rc;
-       const char *type, *src;
-       struct libmnt_optlist *ol;
-       unsigned long flags = 0;
-
-       assert(cxt);
-
-       assert((cxt->flags & MNT_FL_MOUNTFLAGS_MERGED));
+struct hook_data {
+       int loopdev_fd;
+};
 
-       if (!cxt->fs)
-               return 0;
-
-       ol = mnt_context_get_optlist(cxt);
-       if (!ol)
-               return 0;
-
-       if (mnt_optlist_is_bind(ol)
-           || mnt_optlist_is_move(ol)
-           || mnt_context_propagation_only(cxt))
-               return 0;
+/* de-initiallize this module */
+static int hookset_deinit(struct libmnt_context *cxt, const struct libmnt_hookset *hs)
+{
+       void *data;
 
-       src = mnt_fs_get_srcpath(cxt->fs);
-       if (!src)
-               return 0;               /* backing file not set */
+       DBG(HOOK, ul_debugobj(hs, "deinit '%s'", hs->name));
 
-       /* userspace specific flags */
-       rc = mnt_context_get_user_mflags(cxt, &flags);
-       if (rc)
-               return 0;
-
-       if (flags & (MNT_MS_LOOP | MNT_MS_OFFSET | MNT_MS_SIZELIMIT)) {
-               DBG(LOOP, ul_debugobj(cxt, "loopdev specific options detected"));
-               return 1;
+       /* remove all our hooks */
+       while (mnt_context_remove_hook(cxt, hs, 0, &data) == 0) {
+               free(data);
+               data = NULL;
        }
 
-       /* Automatically create a loop device from a regular file if a
-        * filesystem is not specified or the filesystem is known for libblkid
-        * (these filesystems work with block devices only). The file size
-        * should be at least 1KiB, otherwise we will create an empty loopdev with
-        * no mountable filesystem...
-        *
-        * Note that there is no restriction (on kernel side) that would prevent a regular
-        * file as a mount(2) source argument. A filesystem that is able to mount
-        * regular files could be implemented.
-        */
-       type = mnt_fs_get_fstype(cxt->fs);
-
-       if (mnt_fs_is_regularfs(cxt->fs) &&
-           (!type || strcmp(type, "auto") == 0 || blkid_known_fstype(type))) {
-               struct stat st;
+       return 0;
+}
 
-               if (stat(src, &st) == 0 && S_ISREG(st.st_mode) &&
-                   st.st_size > 1024) {
+static inline struct hook_data *new_hook_data(void)
+{
+       struct hook_data *hd = calloc(1, sizeof(*hd));
 
-                       DBG(LOOP, ul_debugobj(cxt, "automatically enabling loop= option"));
-                       mnt_optlist_append_flags(ol, MNT_MS_LOOP, cxt->map_userspace);
-                       return 1;
-               }
-       }
+       if (!hd)
+               return NULL;
 
-       return 0;
+       hd->loopdev_fd =  -1;
+       return hd;
 }
 
-
 /* Check if there already exists a mounted loop device on the mountpoint node
  * with the same parameters.
  */
@@ -162,20 +127,16 @@ is_mounted_same_loopfile(struct libmnt_context *cxt,
        return rc;
 }
 
-int mnt_context_setup_loopdev(struct libmnt_context *cxt)
+static int setup_loopdev(struct libmnt_context *cxt,
+                        struct libmnt_optlist *ol, struct hook_data *hd)
 {
        const char *backing_file, *loopdev = NULL;
        struct loopdev_cxt lc;
        int rc = 0, lo_flags = 0;
        uint64_t offset = 0, sizelimit = 0;
        bool reuse = FALSE;
-       struct libmnt_optlist *ol;
        struct libmnt_opt *opt, *loopopt = NULL;
 
-       assert(cxt);
-       assert(cxt->fs);
-       assert((cxt->flags & MNT_FL_MOUNTFLAGS_MERGED));
-
        backing_file = mnt_fs_get_srcpath(cxt->fs);
        if (!backing_file)
                return -EINVAL;
@@ -381,9 +342,6 @@ success:
                rc = mnt_fs_set_source(cxt->fs, loopcxt_get_device(&lc));
 
        if (!rc) {
-               /* success */
-               cxt->flags |= MNT_FL_LOOPDEV_READY;     /* TODO??? */
-
                if (loopopt && (reuse || loopcxt_is_autoclear(&lc))) {
                        /*
                         * autoclear flag accepted by the kernel, don't store
@@ -404,8 +362,8 @@ success:
                /* we have to keep the device open until mount(1),
                 * otherwise it will be auto-cleared by kernel
                 */
-               cxt->loopdev_fd = loopcxt_get_fd(&lc);
-               if (cxt->loopdev_fd < 0) {
+               hd->loopdev_fd = loopcxt_get_fd(&lc);
+               if (hd->loopdev_fd < 0) {
                        DBG(LOOP, ul_debugobj(cxt, "failed to get loopdev FD"));
                        rc = -errno;
                } else
@@ -417,10 +375,7 @@ done_no_deinit:
        return rc;
 }
 
-/*
- * Deletes loop device
- */
-int mnt_context_delete_loopdev(struct libmnt_context *cxt)
+static int delete_loopdev(struct libmnt_context *cxt, struct hook_data *hd)
 {
        const char *src;
        int rc;
@@ -432,40 +387,147 @@ int mnt_context_delete_loopdev(struct libmnt_context *cxt)
        if (!src)
                return -EINVAL;
 
-       if (cxt->loopdev_fd > -1)
-               close(cxt->loopdev_fd);
+       if (hd && hd->loopdev_fd > -1) {
+               close(hd->loopdev_fd);
+               hd->loopdev_fd = -1;
+       }
 
-       rc = loopdev_delete(src);
-       cxt->flags &= ~MNT_FL_LOOPDEV_READY;
-       cxt->loopdev_fd = -1;
+       rc = loopdev_delete(src);       /* see lib/loopdev.c */
 
        DBG(LOOP, ul_debugobj(cxt, "deleted [rc=%d]", rc));
        return rc;
 }
 
-/*
- * Clears loopdev stuff in context, should be called after
- * failed or successful mount(2).
- */
-int mnt_context_clear_loopdev(struct libmnt_context *cxt)
+/* Now used by umount until context_umount.c will use hooks toosee  */
+int mnt_context_delete_loopdev(struct libmnt_context *cxt)
 {
-       assert(cxt);
+       return delete_loopdev(cxt, NULL);
+}
+
+static int is_loopdev_required(struct libmnt_context *cxt, struct libmnt_optlist *ol)
+{
+       const char *src, *type;
+       unsigned long flags = 0;
+
+       if (cxt->action != MNT_ACT_MOUNT)
+               return 0;
+       if (!cxt->fs)
+               return 0;
+       if (mnt_optlist_is_bind(ol)
+           || mnt_optlist_is_move(ol)
+           || mnt_context_propagation_only(cxt))
+               return 0;
+
+       src = mnt_fs_get_srcpath(cxt->fs);
+       if (!src)
+               return 0;               /* backing file not set */
+
+       /* userspace flags */
+       if (mnt_context_get_user_mflags(cxt, &flags))
+               return 0;
+
+       if (flags & (MNT_MS_LOOP | MNT_MS_OFFSET | MNT_MS_SIZELIMIT)) {
+               DBG(LOOP, ul_debugobj(cxt, "loopdev specific options detected"));
+               return 1;
+       }
+
+       /* Automatically create a loop device from a regular file if a
+        * filesystem is not specified or the filesystem is known for libblkid
+        * (these filesystems work with block devices only). The file size
+        * should be at least 1KiB, otherwise we will create an empty loopdev with
+        * no mountable filesystem...
+        *
+        * Note that there is no restriction (on kernel side) that would prevent a regular
+        * file as a mount(2) source argument. A filesystem that is able to mount
+        * regular files could be implemented.
+        */
+       type = mnt_fs_get_fstype(cxt->fs);
+
+       if (mnt_fs_is_regularfs(cxt->fs) &&
+           (!type || strcmp(type, "auto") == 0 || blkid_known_fstype(type))) {
+               struct stat st;
+
+               if (stat(src, &st) == 0 && S_ISREG(st.st_mode) &&
+                   st.st_size > 1024) {
+
+                       DBG(LOOP, ul_debugobj(cxt, "automatically enabling loop= option"));
+                       mnt_optlist_append_flags(ol, MNT_MS_LOOP, cxt->map_userspace);
+                       return 1;
+               }
+       }
 
-       if (mnt_context_get_status(cxt) == 0 &&
-           (cxt->flags & MNT_FL_LOOPDEV_READY)) {
+       return 0;
+}
+
+/* call after mount(2) */
+static int hook_cleanup_loopdev(
+                       struct libmnt_context *cxt,
+                       const struct libmnt_hookset *hs __attribute__((__unused__)),
+                       void *data)
+{
+       struct hook_data *hd = (struct hook_data *) data;
+
+       if (!hd || hd->loopdev_fd < 0)
+               return 0;
+
+       if (mnt_context_get_status(cxt) == 0) {
                /*
                 * mount(2) failed, delete loopdev
                 */
-               mnt_context_delete_loopdev(cxt);
+               delete_loopdev(cxt, hd);
 
-       } else if (cxt->loopdev_fd > -1) {
+       } else {
                /*
                 * mount(2) success, close the device
                 */
                DBG(LOOP, ul_debugobj(cxt, "closing FD"));
-               close(cxt->loopdev_fd);
+               close(hd->loopdev_fd);
+               hd->loopdev_fd = -1;
        }
-       cxt->loopdev_fd = -1;
+
        return 0;
 }
 
+/* call to prepare mount source */
+static int hook_prepare_loopdev(
+                        struct libmnt_context *cxt,
+                        const struct libmnt_hookset *hs,
+                        void *data __attribute__((__unused__)))
+{
+       struct libmnt_optlist *ol;
+       struct hook_data *hd;
+       int rc;
+
+       assert(cxt);
+
+       ol = mnt_context_get_optlist(cxt);
+       if (!ol)
+               return -ENOMEM;
+       if (!is_loopdev_required(cxt, ol))
+               return 0;
+       hd = new_hook_data();
+       if (!hd)
+               return -ENOMEM;
+
+       rc = setup_loopdev(cxt, ol, hd);
+       if (!rc)
+               rc = mnt_context_append_hook(cxt, hs,
+                               MNT_STAGE_MOUNT_POST,
+                               hd, hook_cleanup_loopdev);
+       if (rc) {
+               delete_loopdev(cxt, hd);
+               free(hd);
+       }
+       return rc;
+}
+
+
+const struct libmnt_hookset hookset_loopdev =
+{
+        .name = "__loopdev",
+
+        .firststage = MNT_STAGE_PREP_SOURCE,
+        .firstcall = hook_prepare_loopdev,
+
+        .deinit = hookset_deinit
+};
index d6d8d89415170648e829263a4353a0181ace6610..2dc44d9feb965dd778735b9c6918924c5d16e780 100644 (file)
@@ -25,6 +25,7 @@
 static const struct libmnt_hookset *hooksets[] =
 {
 #ifdef __linux__
+       &hookset_loopdev,
        &hookset_mkdir,
        &hookset_subdir,
        &hookset_mount_legacy,
index 82a206201fe10ef4bbbac289723ed9b682ec9e83..5365ef84f52c7edfa94adcd7efd33166107c8d65 100644 (file)
@@ -311,6 +311,7 @@ extern const struct libmnt_hookset hookset_mkdir;
 extern const struct libmnt_hookset hookset_subdir;
 extern const struct libmnt_hookset hookset_owner;
 extern const struct libmnt_hookset hookset_idmap;
+extern const struct libmnt_hookset hookset_loopdev;
 
 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);
@@ -377,7 +378,6 @@ struct libmnt_context
        void    (*pwd_release_cb)(struct libmnt_context *, char *);     /* release password */
 
        int     optsmode;       /* fstab optstr mode MNT_OPTSMODE_{AUTO,FORCE,IGNORE} */
-       int     loopdev_fd;     /* open loopdev */
 
        unsigned long   mountflags;     /* final mount(2) flags */
        const void      *mountdata;     /* final mount(2) data, string or binary data */
@@ -446,7 +446,6 @@ struct libmnt_context
 #define MNT_FL_SAVED_USER      (1 << 23)
 #define MNT_FL_PREPARED                (1 << 24)
 #define MNT_FL_HELPER          (1 << 25)       /* [u]mount.<type> */
-#define MNT_FL_LOOPDEV_READY   (1 << 26)       /* /dev/loop<N> initialized by the library */
 #define MNT_FL_MOUNTOPTS_FIXED  (1 << 27)
 #define MNT_FL_TABPATHS_CHECKED        (1 << 28)
 #define MNT_FL_FORCED_RDONLY   (1 << 29)       /* mounted read-only on write-protected device */
@@ -579,15 +578,10 @@ extern int mnt_context_update_tabs(struct libmnt_context *cxt);
 extern int mnt_context_umount_setopt(struct libmnt_context *cxt, int c, char *arg);
 extern int mnt_context_mount_setopt(struct libmnt_context *cxt, int c, char *arg);
 
-extern int mnt_context_is_loopdev(struct libmnt_context *cxt)
-                       __attribute__((nonnull));
-
 extern int mnt_context_propagation_only(struct libmnt_context *cxt)
                        __attribute__((nonnull));
 
-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);
 
 extern int mnt_fork_context(struct libmnt_context *cxt);