From: Karel Zak Date: Tue, 23 Aug 2022 09:55:51 +0000 (+0200) Subject: libmount: reimplement loop= by hooks X-Git-Tag: v2.39-rc1~296 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=3611b10c6b616e848dac191efab8e5b593d87c0b;p=thirdparty%2Futil-linux.git libmount: reimplement loop= by hooks Signed-off-by: Karel Zak --- diff --git a/libmount/src/context.c b/libmount/src/context.c index 77f453b3e4..f95437d88c 100644 --- a/libmount/src/context.c +++ b/libmount/src/context.c @@ -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'", diff --git a/libmount/src/context_loopdev.c b/libmount/src/context_loopdev.c index f160f0c969..652ae76377 100644 --- a/libmount/src/context_loopdev.c +++ b/libmount/src/context_loopdev.c @@ -2,7 +2,7 @@ /* * This file is part of libmount from util-linux project. * - * Copyright (C) 2011-2018 Karel Zak + * Copyright (C) 2011-2022 Karel Zak * * 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 @@ -22,72 +22,37 @@ #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 +}; diff --git a/libmount/src/hooks.c b/libmount/src/hooks.c index d6d8d89415..2dc44d9feb 100644 --- a/libmount/src/hooks.c +++ b/libmount/src/hooks.c @@ -25,6 +25,7 @@ static const struct libmnt_hookset *hooksets[] = { #ifdef __linux__ + &hookset_loopdev, &hookset_mkdir, &hookset_subdir, &hookset_mount_legacy, diff --git a/libmount/src/mountP.h b/libmount/src/mountP.h index 82a206201f..5365ef84f5 100644 --- a/libmount/src/mountP.h +++ b/libmount/src/mountP.h @@ -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. */ -#define MNT_FL_LOOPDEV_READY (1 << 26) /* /dev/loop 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);