]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
ovl: support layers on case-folding capable filesystems
authorAmir Goldstein <amir73il@gmail.com>
Mon, 2 Jun 2025 17:17:02 +0000 (19:17 +0200)
committerChristian Brauner <brauner@kernel.org>
Fri, 18 Jul 2025 09:09:33 +0000 (11:09 +0200)
Case folding is often applied to subtrees and not on an entire
filesystem.

Disallowing layers from filesystems that support case folding is over
limiting.

Replace the rule that case-folding capable are not allowed as layers
with a rule that case folded directories are not allowed in a merged
directory stack.

Should case folding be enabled on an underlying directory while
overlayfs is mounted the outcome is generally undefined.

Specifically in ovl_lookup(), we check the base underlying directory
and fail with -ESTALE and write a warning to kmsg if an underlying
directory case folding is enabled.

Suggested-by: Kent Overstreet <kent.overstreet@linux.dev>
Link: https://lore.kernel.org/linux-fsdevel/20250520051600.1903319-1-kent.overstreet@linux.dev/
Signed-off-by: Amir Goldstein <amir73il@gmail.com>
Link: https://lore.kernel.org/20250602171702.1941891-1-amir73il@gmail.com
Reviewed-by: Kent Overstreet <kent.overstreet@linux.dev>
Signed-off-by: Christian Brauner <brauner@kernel.org>
fs/overlayfs/namei.c
fs/overlayfs/overlayfs.h
fs/overlayfs/params.c
fs/overlayfs/util.c

index 0b8b28392eb7c594499e7ebdae5ffafd4d5a440a..1fc68b59fa5d6039b6ca2270d7c780c54702a537 100644 (file)
@@ -230,13 +230,26 @@ static int ovl_lookup_single(struct dentry *base, struct ovl_lookup_data *d,
                             struct dentry **ret, bool drop_negative)
 {
        struct ovl_fs *ofs = OVL_FS(d->sb);
-       struct dentry *this;
+       struct dentry *this = NULL;
+       const char *warn;
        struct path path;
        int err;
        bool last_element = !post[0];
        bool is_upper = d->layer->idx == 0;
        char val;
 
+       /*
+        * We allow filesystems that are case-folding capable but deny composing
+        * ovl stack from case-folded directories. If someone has enabled case
+        * folding on a directory on underlying layer, the warranty of the ovl
+        * stack is voided.
+        */
+       if (ovl_dentry_casefolded(base)) {
+               warn = "case folded parent";
+               err = -ESTALE;
+               goto out_warn;
+       }
+
        this = ovl_lookup_positive_unlocked(d, name, base, namelen, drop_negative);
        if (IS_ERR(this)) {
                err = PTR_ERR(this);
@@ -246,10 +259,17 @@ static int ovl_lookup_single(struct dentry *base, struct ovl_lookup_data *d,
                goto out_err;
        }
 
+       if (ovl_dentry_casefolded(this)) {
+               warn = "case folded child";
+               err = -EREMOTE;
+               goto out_warn;
+       }
+
        if (ovl_dentry_weird(this)) {
                /* Don't support traversing automounts and other weirdness */
+               warn = "unsupported object type";
                err = -EREMOTE;
-               goto out_err;
+               goto out_warn;
        }
 
        path.dentry = this;
@@ -283,8 +303,9 @@ static int ovl_lookup_single(struct dentry *base, struct ovl_lookup_data *d,
        } else {
                if (ovl_lookup_trap_inode(d->sb, this)) {
                        /* Caught in a trap of overlapping layers */
+                       warn = "overlapping layers";
                        err = -ELOOP;
-                       goto out_err;
+                       goto out_warn;
                }
 
                if (last_element)
@@ -316,6 +337,10 @@ put_and_out:
        this = NULL;
        goto out;
 
+out_warn:
+       pr_warn_ratelimited("failed lookup in %s (%pd2, name='%.*s', err=%i): %s\n",
+                           is_upper ? "upper" : "lower", base,
+                           namelen, name, err, warn);
 out_err:
        dput(this);
        return err;
index 78deb89e16b56aaebd9fdeb6ad06099206403844..3c52ecddfc9cc06ef817aa2c9f6d5552f09b74ce 100644 (file)
@@ -446,6 +446,12 @@ void ovl_dentry_init_reval(struct dentry *dentry, struct dentry *upperdentry,
 void ovl_dentry_init_flags(struct dentry *dentry, struct dentry *upperdentry,
                           struct ovl_entry *oe, unsigned int mask);
 bool ovl_dentry_weird(struct dentry *dentry);
+
+static inline bool ovl_dentry_casefolded(struct dentry *dentry)
+{
+       return sb_has_encoding(dentry->d_sb) && IS_CASEFOLDED(d_inode(dentry));
+}
+
 enum ovl_path_type ovl_path_type(struct dentry *dentry);
 void ovl_path_upper(struct dentry *dentry, struct path *path);
 void ovl_path_lower(struct dentry *dentry, struct path *path);
index f42488c019572479d8fdcfc1efd62b21d2995875..2b9b31524c38d4c0b0765a54429ff484169175ff 100644 (file)
@@ -282,13 +282,11 @@ static int ovl_mount_dir_check(struct fs_context *fc, const struct path *path,
                return invalfc(fc, "%s is not a directory", name);
 
        /*
-        * Root dentries of case-insensitive capable filesystems might
-        * not have the dentry operations set, but still be incompatible
-        * with overlayfs.  Check explicitly to prevent post-mount
-        * failures.
+        * Allow filesystems that are case-folding capable but deny composing
+        * ovl stack from case-folded directories.
         */
-       if (sb_has_encoding(path->mnt->mnt_sb))
-               return invalfc(fc, "case-insensitive capable filesystem on %s not supported", name);
+       if (ovl_dentry_casefolded(path->dentry))
+               return invalfc(fc, "case-insensitive directory on %s not supported", name);
 
        if (ovl_dentry_weird(path->dentry))
                return invalfc(fc, "filesystem on %s not supported", name);
index 2b4754c645eeac7908c976d168802c17a6d009de..cc793c8f001f13cde04e1ddfae3333fc879e2738 100644 (file)
@@ -206,10 +206,17 @@ bool ovl_dentry_weird(struct dentry *dentry)
        if (!d_can_lookup(dentry) && !d_is_file(dentry) && !d_is_symlink(dentry))
                return true;
 
-       return dentry->d_flags & (DCACHE_NEED_AUTOMOUNT |
-                                 DCACHE_MANAGE_TRANSIT |
-                                 DCACHE_OP_HASH |
-                                 DCACHE_OP_COMPARE);
+       if (dentry->d_flags & (DCACHE_NEED_AUTOMOUNT | DCACHE_MANAGE_TRANSIT))
+               return true;
+
+       /*
+        * Allow filesystems that are case-folding capable but deny composing
+        * ovl stack from case-folded directories.
+        */
+       if (sb_has_encoding(dentry->d_sb))
+               return IS_CASEFOLDED(d_inode(dentry));
+
+       return dentry->d_flags & (DCACHE_OP_HASH | DCACHE_OP_COMPARE);
 }
 
 enum ovl_path_type ovl_path_type(struct dentry *dentry)