ovl: relax requirement for uuid=off,index=on
authorAmir Goldstein <amir73il@gmail.com>
Tue, 20 Jan 2026 14:58:31 +0000 (15:58 +0100)
committerAmir Goldstein <amir73il@gmail.com>
Fri, 6 Feb 2026 12:48:23 +0000 (13:48 +0100)
uuid=off,index=on required that all upper/lower directories are on the
same filesystem.

Relax the requirement so that only all the lower directories need to be
on the same filesystem.

Reported-by: André Almeida <andrealmeid@igalia.com>
Link: https://lore.kernel.org/r/20260114-tonyk-get_disk_uuid-v1-3-e6a319e25d57@igalia.com/
Signed-off-by: Amir Goldstein <amir73il@gmail.com>
Documentation/filesystems/overlayfs.rst
fs/overlayfs/namei.c
fs/overlayfs/overlayfs.h
fs/overlayfs/super.c

index ab98980..af5a69f 100644 (file)
@@ -753,9 +753,9 @@ Note: the mount options index=off,nfs_export=on are conflicting for a
 read-write mount and will result in an error.
 
 Note: the mount option uuid=off can be used to replace UUID of the underlying
-filesystem in file handles with null, and effectively disable UUID checks. This
+filesystem in file handles with null, in order to relax the UUID checks. This
 can be useful in case the underlying disk is copied and the UUID of this copy
-is changed. This is only applicable if all lower/upper/work directories are on
+is changed. This is only applicable if all lower directories are on
 the same filesystem, otherwise it will fallback to normal behaviour.
 
 
@@ -769,7 +769,7 @@ controlled by the "uuid" mount option, which supports these values:
     UUID of overlayfs is null. fsid is taken from upper most filesystem.
 - "off":
     UUID of overlayfs is null. fsid is taken from upper most filesystem.
-    UUID of underlying layers is ignored.
+    UUID of underlying layers is ignored and null used instead.
 - "on":
     UUID of overlayfs is generated and used to report a unique fsid.
     UUID is stored in xattr "trusted.overlay.uuid", making overlayfs fsid
index e9a69c9..74c5146 100644 (file)
@@ -158,6 +158,18 @@ invalid:
        goto out;
 }
 
+bool ovl_uuid_match(struct ovl_fs *ofs, const struct super_block *sb,
+                   const uuid_t *uuid)
+{
+       /*
+        * Make sure that the stored uuid matches the uuid of the lower
+        * layer where file handle will be decoded.
+        * In case of uuid=off option just make sure that stored uuid is null.
+        */
+       return ovl_origin_uuid(ofs) ? uuid_equal(uuid, &sb->s_uuid) :
+                                     uuid_is_null(uuid);
+}
+
 struct dentry *ovl_decode_real_fh(struct ovl_fs *ofs, struct ovl_fh *fh,
                                  struct vfsmount *mnt, bool connected)
 {
@@ -167,14 +179,7 @@ struct dentry *ovl_decode_real_fh(struct ovl_fs *ofs, struct ovl_fh *fh,
        if (!capable(CAP_DAC_READ_SEARCH))
                return NULL;
 
-       /*
-        * Make sure that the stored uuid matches the uuid of the lower
-        * layer where file handle will be decoded.
-        * In case of uuid=off option just make sure that stored uuid is null.
-        */
-       if (ovl_origin_uuid(ofs) ?
-           !uuid_equal(&fh->fb.uuid, &mnt->mnt_sb->s_uuid) :
-           !uuid_is_null(&fh->fb.uuid))
+       if (!ovl_uuid_match(ofs, mnt->mnt_sb, &fh->fb.uuid))
                return NULL;
 
        bytes = (fh->fb.len - offsetof(struct ovl_fb, fid));
index f9ac9bd..cf10661 100644 (file)
@@ -710,6 +710,8 @@ static inline int ovl_check_fh_len(struct ovl_fh *fh, int fh_len)
        return ovl_check_fb_len(&fh->fb, fh_len - OVL_FH_WIRE_OFFSET);
 }
 
+bool ovl_uuid_match(struct ovl_fs *ofs, const struct super_block *sb,
+                   const uuid_t *uuid);
 struct dentry *ovl_decode_real_fh(struct ovl_fs *ofs, struct ovl_fh *fh,
                                  struct vfsmount *mnt, bool connected);
 int ovl_check_origin_fh(struct ovl_fs *ofs, struct ovl_fh *fh, bool connected,
index ba9146f..c9f166a 100644 (file)
@@ -940,7 +940,7 @@ static bool ovl_lower_uuid_ok(struct ovl_fs *ofs, const uuid_t *uuid)
                 * disable lower file handle decoding on all of them.
                 */
                if (ofs->fs[i].is_lower &&
-                   uuid_equal(&ofs->fs[i].sb->s_uuid, uuid)) {
+                   ovl_uuid_match(ofs, ofs->fs[i].sb, uuid)) {
                        ofs->fs[i].bad_uuid = true;
                        return false;
                }
@@ -952,6 +952,7 @@ static bool ovl_lower_uuid_ok(struct ovl_fs *ofs, const uuid_t *uuid)
 static int ovl_get_fsid(struct ovl_fs *ofs, const struct path *path)
 {
        struct super_block *sb = path->mnt->mnt_sb;
+       const uuid_t *uuid = ovl_origin_uuid(ofs) ? &sb->s_uuid : &uuid_null;
        unsigned int i;
        dev_t dev;
        int err;
@@ -963,7 +964,7 @@ static int ovl_get_fsid(struct ovl_fs *ofs, const struct path *path)
                        return i;
        }
 
-       if (!ovl_lower_uuid_ok(ofs, &sb->s_uuid)) {
+       if (!ovl_lower_uuid_ok(ofs, uuid)) {
                bad_uuid = true;
                if (ofs->config.xino == OVL_XINO_AUTO) {
                        ofs->config.xino = OVL_XINO_OFF;
@@ -975,9 +976,8 @@ static int ovl_get_fsid(struct ovl_fs *ofs, const struct path *path)
                        warn = true;
                }
                if (warn) {
-                       pr_warn("%s uuid detected in lower fs '%pd2', falling back to xino=%s,index=off,nfs_export=off.\n",
-                               uuid_is_null(&sb->s_uuid) ? "null" :
-                                                           "conflicting",
+                       pr_warn("%s uuid in non-single lower fs '%pd2', falling back to xino=%s,index=off,nfs_export=off.\n",
+                               uuid_is_null(uuid) ? "null" : "conflicting",
                                path->dentry, ovl_xino_mode(&ofs->config));
                }
        }
@@ -1469,10 +1469,7 @@ static int ovl_fill_super_creds(struct fs_context *fc, struct super_block *sb)
        if (!ovl_upper_mnt(ofs))
                sb->s_flags |= SB_RDONLY;
 
-       if (!ovl_origin_uuid(ofs) && ofs->numfs > 1) {
-               pr_warn("The uuid=off requires a single fs for lower and upper, falling back to uuid=null.\n");
-               ofs->config.uuid = OVL_UUID_NULL;
-       } else if (ovl_has_fsid(ofs) && ovl_upper_mnt(ofs)) {
+       if (ovl_has_fsid(ofs) && ovl_upper_mnt(ofs)) {
                /* Use per instance persistent uuid/fsid */
                ovl_init_uuid_xattr(sb, ofs, &ctx->upper);
        }