]> git.ipfire.org Git - thirdparty/util-linux.git/commitdiff
libmount: create EROFS loopdev only after ENOTBLK
authorKarel Zak <kzak@redhat.com>
Fri, 6 Dec 2024 11:41:09 +0000 (12:41 +0100)
committerKarel Zak <kzak@redhat.com>
Mon, 9 Dec 2024 08:47:05 +0000 (09:47 +0100)
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 <kzak@redhat.com>
libmount/src/context_mount.c
libmount/src/hook_loopdev.c

index af2fd7ed4b73e731bded84118891c51b8b68de98..c64e735da57fd765827ba81172fb6892cb4536b7 100644 (file)
@@ -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);
 
index a19ddab3be4b6ec1d368ed98a2b8b905f7a08a4e..65d0f739e830b8beb07de71e166395438581ba30 100644 (file)
@@ -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) */