From: Karel Zak Date: Fri, 6 Dec 2024 11:41:09 +0000 (+0100) Subject: libmount: create EROFS loopdev only after ENOTBLK X-Git-Tag: v2.42-start~119^2 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=d20fa0c812107b4c1f9412d7e878b5c23a258c54;p=thirdparty%2Futil-linux.git libmount: create EROFS loopdev only after ENOTBLK The EROFS can mount regular files with a filesystem image without the need for a loop device. For backward compatibility with previous versions of EROFS, a loop device will only be created if the first attempt to mount results ENOTBLK error. Addresses: https://github.com/util-linux/util-linux/pull/3288 Signed-off-by: Karel Zak --- diff --git a/libmount/src/context_mount.c b/libmount/src/context_mount.c index af2fd7ed4..c64e735da 100644 --- a/libmount/src/context_mount.c +++ b/libmount/src/context_mount.c @@ -962,6 +962,24 @@ int mnt_context_finalize_mount(struct libmnt_context *cxt) return rc; } +static int is_erofs_regfile(struct libmnt_context *cxt) +{ + const char *type = mnt_fs_get_fstype(cxt->fs); + const char *src = mnt_fs_get_srcpath(cxt->fs); + unsigned long flags = 0; + struct stat st; + + if (!type || strcmp(type, "erofs") != 0) + return 0; + if (mnt_context_get_user_mflags(cxt, &flags)) + return 0; + if (flags & (MNT_MS_LOOP | MNT_MS_OFFSET | MNT_MS_SIZELIMIT)) + return 0; /* it's already loopdev */ + if (!src || stat(src, &st) != 0 || !S_ISREG(st.st_mode)) + return 0; + return 1; +} + /** * mnt_context_mount: * @cxt: mount context @@ -1066,6 +1084,23 @@ again: } } + /* + * Try mount EROFS image again with loop device. + * See hook_loopdev.c:is_loopdev_required() for more details. + */ + if (rc && mnt_context_get_syscall_errno(cxt) == ENOTBLK + && is_erofs_regfile(cxt)) { + struct libmnt_optlist *ol = mnt_context_get_optlist(cxt); + + mnt_context_reset_status(cxt); + DBG(CXT, ul_debugobj(cxt, "enabling loop= for EROFS")); + mnt_optlist_append_flags(ol, MNT_MS_LOOP, cxt->map_userspace); + + rc = mnt_context_call_hooks(cxt, MNT_STAGE_PREP_SOURCE); + if (!rc) + goto again; + } + if (rc == 0) rc = mnt_context_call_hooks(cxt, MNT_STAGE_POST); diff --git a/libmount/src/hook_loopdev.c b/libmount/src/hook_loopdev.c index a19ddab3b..65d0f739e 100644 --- a/libmount/src/hook_loopdev.c +++ b/libmount/src/hook_loopdev.c @@ -413,6 +413,7 @@ static int is_loopdev_required(struct libmnt_context *cxt, struct libmnt_optlist { const char *src, *type; unsigned long flags = 0; + struct stat st; if (cxt->action != MNT_ACT_MOUNT) return 0; @@ -434,37 +435,55 @@ static int is_loopdev_required(struct libmnt_context *cxt, struct libmnt_optlist if (mnt_context_get_user_mflags(cxt, &flags)) return 0; + /* loop= (sizelimit= or offset=) explicitly specified */ 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. + /* Automatically create a loop device from a regular file. The file + * size should be at least 1KiB, otherwise we will create an empty + * loop device with no mountable filesystem. */ - 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)) + return 0; + if (st.st_size <= 1024) + return 0; - if (stat(src, &st) == 0 && S_ISREG(st.st_mode) && - st.st_size > 1024) { + type = mnt_fs_get_fstype(cxt->fs); + if (!type || strcmp(type, "auto") == 0) { + char *autotype = NULL; + int rc; - DBG(LOOP, ul_debugobj(cxt, "automatically enabling loop= option")); - mnt_optlist_append_flags(ol, MNT_MS_LOOP, cxt->map_userspace); - return 1; + rc = mnt_context_guess_srcpath_fstype(cxt, &autotype); + if (rc) { + DBG(CXT, ul_debugobj(cxt, "failed to guess regfile FS type [rc=%d]", rc)); + return 0; + } + if (autotype) { + __mnt_fs_set_fstype_ptr(cxt->fs, autotype); + type = mnt_fs_get_fstype(cxt->fs); } } - return 0; + /* The EROFS kernel driver can be compiled with EROFS_FS_BACKED_BY_FILE, + * allowing for the mounting of a regular file as a filesystem without + * a loop device. + */ + if (type && strcmp(type, "erofs") == 0) + return 0; + + /* 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. For this + * reason we do not enable loop device for unknown filesystems. + */ + if (type && !blkid_known_fstype(type)) + return 0; + + DBG(LOOP, ul_debugobj(cxt, "automatically enabling loop= option")); + mnt_optlist_append_flags(ol, MNT_MS_LOOP, cxt->map_userspace); + return 1; } /* call after mount(2) */