linux-fsdevel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH RFC v2 0/8] ovl: Enable support for casefold filesystems
@ 2025-08-05  3:09 André Almeida
  2025-08-05  3:09 ` [PATCH RFC v2 1/8] olv: Store casefold name for case-insentive dentries André Almeida
                   ` (7 more replies)
  0 siblings, 8 replies; 13+ messages in thread
From: André Almeida @ 2025-08-05  3:09 UTC (permalink / raw)
  To: Miklos Szeredi, Amir Goldstein, Theodore Tso,
	Gabriel Krisman Bertazi
  Cc: linux-unionfs, linux-kernel, linux-fsdevel, Alexander Viro,
	Christian Brauner, Jan Kara, kernel-dev, André Almeida

Hi all,

We would like to support the usage of casefold filesystems with
overlayfs to be used with container tools. This use case requires a
simple setup, where every layer will have the same encoding setting
(i.e. Unicode version and flags), using one upper and one lower layer.

* Implementation

When merge layers, ovl uses a red-black tree to check if a given dentry
name from a lower layers already exists in the upper layer. For merging
case-insensitive names, we need to store then in tree casefolded.
However, when displaying to the user the dentry name, we need to respect
the name chosen when the file was created (e.g. Picture.PNG, instead of
picture.png). To achieve this, I create a new field for cache entries
that stores the casefolded names and a function ovl_strcmp() that uses
this name for searching the rb_tree. For composing the layer, ovl uses
the original name, keeping it consistency with whatever name the user
created.

The rest of the patches are mostly for checking if casefold is being
consistently used across the layers and dropping the mount restrictions
that prevented case-insensitive filesystems to be mounted.

Thanks for the feedback!

---
Changes in v2:
- Almost a full rewritten from the v1.
v1: https://lore.kernel.org/lkml/20250409-tonyk-overlayfs-v1-0-3991616fe9a3@igalia.com/

---
André Almeida (8):
      olv: Store casefold name for case-insentive dentries
      ovl: Create ovl_strcmp() with casefold support
      fs: Create sb_same_encoding() helper
      ovl: Ensure that all mount points have the same encoding
      ovl: Set case-insensitive dentry operations for ovl sb
      ovl: Set inode S_CASEFOLD for casefolded dentries
      ovl: Check casefold consistency in ovl stack
      ovl: Drop restrictions for casefolded dentries

 fs/overlayfs/inode.c   |  7 +++++
 fs/overlayfs/namei.c   | 25 ++++++---------
 fs/overlayfs/params.c  |  7 -----
 fs/overlayfs/readdir.c | 82 ++++++++++++++++++++++++++++++++++++++++++++++++--
 fs/overlayfs/super.c   | 21 +++++++++++++
 fs/overlayfs/util.c    |  8 ++---
 include/linux/fs.h     | 22 ++++++++++++++
 7 files changed, 143 insertions(+), 29 deletions(-)
---
base-commit: ba04dc6f8768e61d6de2d0c5c5079a8b54e62fbb
change-id: 20250409-tonyk-overlayfs-591f5e4d407a

Best regards,
-- 
André Almeida <andrealmeid@igalia.com>


^ permalink raw reply	[flat|nested] 13+ messages in thread

* [PATCH RFC v2 1/8] olv: Store casefold name for case-insentive dentries
  2025-08-05  3:09 [PATCH RFC v2 0/8] ovl: Enable support for casefold filesystems André Almeida
@ 2025-08-05  3:09 ` André Almeida
  2025-08-05  3:09 ` [PATCH RFC v2 2/8] ovl: Create ovl_strcmp() with casefold support André Almeida
                   ` (6 subsequent siblings)
  7 siblings, 0 replies; 13+ messages in thread
From: André Almeida @ 2025-08-05  3:09 UTC (permalink / raw)
  To: Miklos Szeredi, Amir Goldstein, Theodore Tso,
	Gabriel Krisman Bertazi
  Cc: linux-unionfs, linux-kernel, linux-fsdevel, Alexander Viro,
	Christian Brauner, Jan Kara, kernel-dev, André Almeida

In order to make case-insentive mounting points work, overlayfs needs
the casefolded version of its dentries so the search and insertion in
the struct ovl_readdir_data's red-black compares the dentry names in a
case-insentive fashion.

If a dentry is casefolded, compute and store it's casefolded name and
it's Unicode map. If utf8_casefold() fails, set it's name pointer as
NULL so it can be ignored and fallback to the original name.

Signed-off-by: André Almeida <andrealmeid@igalia.com>
---
 fs/overlayfs/readdir.c | 40 +++++++++++++++++++++++++++++++++++++++-
 1 file changed, 39 insertions(+), 1 deletion(-)

diff --git a/fs/overlayfs/readdir.c b/fs/overlayfs/readdir.c
index b65cdfce31ce27172d28d879559f1008b9c87320..83bca1bcb0488461b08effa70b32ff2fefba134e 100644
--- a/fs/overlayfs/readdir.c
+++ b/fs/overlayfs/readdir.c
@@ -16,6 +16,8 @@
 #include <linux/overflow.h>
 #include "overlayfs.h"
 
+#define OVL_NAME_LEN 255
+
 struct ovl_cache_entry {
 	unsigned int len;
 	unsigned int type;
@@ -27,6 +29,9 @@ struct ovl_cache_entry {
 	bool is_upper;
 	bool is_whiteout;
 	bool check_xwhiteout;
+	struct unicode_map *map;
+	char *cf_name;
+	int cf_len;
 	char name[];
 };
 
@@ -50,6 +55,7 @@ struct ovl_readdir_data {
 	bool is_upper;
 	bool d_type_supported;
 	bool in_xwhiteouts_dir;
+	struct unicode_map *map;
 };
 
 struct ovl_dir_file {
@@ -166,6 +172,29 @@ static struct ovl_cache_entry *ovl_cache_entry_new(struct ovl_readdir_data *rdd,
 	p->is_whiteout = false;
 	/* Defer check for overlay.whiteout to ovl_iterate() */
 	p->check_xwhiteout = rdd->in_xwhiteouts_dir && d_type == DT_REG;
+	p->map = rdd->map;
+	p->cf_name = NULL;
+
+	if (p->map && !is_dot_dotdot(name, len)) {
+		const struct qstr str = { .name = name, .len = len };
+		int ret;
+
+		p->cf_name = kmalloc(OVL_NAME_LEN, GFP_KERNEL);
+
+		if (!p->cf_name) {
+			kfree(p);
+			return NULL;
+		}
+
+		ret = utf8_casefold(p->map, &str, p->cf_name, OVL_NAME_LEN);
+
+		if (ret < 0) {
+			kfree(p->cf_name);
+			p->cf_name = NULL;
+		} else {
+			p->cf_len = ret;
+		}
+	}
 
 	if (d_type == DT_CHR) {
 		p->next_maybe_whiteout = rdd->first_maybe_whiteout;
@@ -223,8 +252,10 @@ void ovl_cache_free(struct list_head *list)
 	struct ovl_cache_entry *p;
 	struct ovl_cache_entry *n;
 
-	list_for_each_entry_safe(p, n, list, l_node)
+	list_for_each_entry_safe(p, n, list, l_node) {
+		kfree(p->cf_name);
 		kfree(p);
+	}
 
 	INIT_LIST_HEAD(list);
 }
@@ -357,12 +388,19 @@ static int ovl_dir_read_merged(struct dentry *dentry, struct list_head *list,
 		.list = list,
 		.root = root,
 		.is_lowest = false,
+		.map = NULL,
 	};
 	int idx, next;
 	const struct ovl_layer *layer;
 
 	for (idx = 0; idx != -1; idx = next) {
 		next = ovl_path_next(idx, dentry, &realpath, &layer);
+
+#if IS_ENABLED(CONFIG_UNICODE)
+		if (ovl_dentry_casefolded(realpath.dentry))
+			rdd.map = realpath.dentry->d_sb->s_encoding;
+#endif
+
 		rdd.is_upper = ovl_dentry_upper(dentry) == realpath.dentry;
 		rdd.in_xwhiteouts_dir = layer->has_xwhiteouts &&
 					ovl_dentry_has_xwhiteouts(dentry);

-- 
2.50.1


^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [PATCH RFC v2 2/8] ovl: Create ovl_strcmp() with casefold support
  2025-08-05  3:09 [PATCH RFC v2 0/8] ovl: Enable support for casefold filesystems André Almeida
  2025-08-05  3:09 ` [PATCH RFC v2 1/8] olv: Store casefold name for case-insentive dentries André Almeida
@ 2025-08-05  3:09 ` André Almeida
  2025-08-05  5:08   ` Al Viro
  2025-08-05 14:56   ` Gabriel Krisman Bertazi
  2025-08-05  3:09 ` [PATCH RFC v2 3/8] fs: Create sb_same_encoding() helper André Almeida
                   ` (5 subsequent siblings)
  7 siblings, 2 replies; 13+ messages in thread
From: André Almeida @ 2025-08-05  3:09 UTC (permalink / raw)
  To: Miklos Szeredi, Amir Goldstein, Theodore Tso,
	Gabriel Krisman Bertazi
  Cc: linux-unionfs, linux-kernel, linux-fsdevel, Alexander Viro,
	Christian Brauner, Jan Kara, kernel-dev, André Almeida

To add overlayfs support casefold filesystems, create a new function
ovl_strcmp() with support for casefold names.

If the ovl_cache_entry have stored a casefold name, use it and create
a casfold version of the name that is going to be compared to.

For the casefold support, just comparing the strings does not work
because we need the dentry enconding, so make this function find the
equivalent dentry for a giving directory, if any.

As this function is used for search and insertion in the red-black tree,
that means that the tree node keys are going to be the casefolded
version of the dentry's names. Otherwise, the search would not work for
case-insensitive mount points.

For the non-casefold names, nothing changes.

Signed-off-by: André Almeida <andrealmeid@igalia.com>
---
I wonder what should be done here if kmalloc fails, if the strcmp()
should fail as well or just fallback to the normal name?
---
 fs/overlayfs/readdir.c | 42 ++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 40 insertions(+), 2 deletions(-)

diff --git a/fs/overlayfs/readdir.c b/fs/overlayfs/readdir.c
index 83bca1bcb0488461b08effa70b32ff2fefba134e..1b8eb10e72a229ade40d18795746d3c779797a06 100644
--- a/fs/overlayfs/readdir.c
+++ b/fs/overlayfs/readdir.c
@@ -72,6 +72,44 @@ static struct ovl_cache_entry *ovl_cache_entry_from_node(struct rb_node *n)
 	return rb_entry(n, struct ovl_cache_entry, node);
 }
 
+/*
+ * Compare a string with a cache entry, with support for casefold names.
+ */
+static int ovl_strcmp(const char *str, struct ovl_cache_entry *p, int len)
+{
+
+	const struct qstr qstr = { .name = str, .len = len };
+	const char *p_name = p->name, *name = str;
+	char *dst = NULL;
+	int cmp, cf_len;
+
+	if (p->cf_name)
+		p_name = p->cf_name;
+
+	if (p->map && !is_dot_dotdot(str, len)) {
+		dst = kmalloc(OVL_NAME_LEN, GFP_KERNEL);
+
+		/*
+		 * strcmp can't fail, so we fallback to the use the original
+		 * name
+		 */
+		if (dst) {
+			cf_len = utf8_casefold(p->map, &qstr, dst, OVL_NAME_LEN);
+
+			if (cf_len > 0) {
+				name = dst;
+				dst[cf_len] = '\0';
+			}
+		}
+	}
+
+	cmp = strncmp(name, p_name, cf_len);
+
+	kfree(dst);
+
+	return cmp;
+}
+
 static bool ovl_cache_entry_find_link(const char *name, int len,
 				      struct rb_node ***link,
 				      struct rb_node **parent)
@@ -85,7 +123,7 @@ static bool ovl_cache_entry_find_link(const char *name, int len,
 
 		*parent = *newp;
 		tmp = ovl_cache_entry_from_node(*newp);
-		cmp = strncmp(name, tmp->name, len);
+		cmp = ovl_strcmp(name, tmp, len);
 		if (cmp > 0)
 			newp = &tmp->node.rb_right;
 		else if (cmp < 0 || len < tmp->len)
@@ -107,7 +145,7 @@ static struct ovl_cache_entry *ovl_cache_entry_find(struct rb_root *root,
 	while (node) {
 		struct ovl_cache_entry *p = ovl_cache_entry_from_node(node);
 
-		cmp = strncmp(name, p->name, len);
+		cmp = ovl_strcmp(name, p, len);
 		if (cmp > 0)
 			node = p->node.rb_right;
 		else if (cmp < 0 || len < p->len)

-- 
2.50.1


^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [PATCH RFC v2 3/8] fs: Create sb_same_encoding() helper
  2025-08-05  3:09 [PATCH RFC v2 0/8] ovl: Enable support for casefold filesystems André Almeida
  2025-08-05  3:09 ` [PATCH RFC v2 1/8] olv: Store casefold name for case-insentive dentries André Almeida
  2025-08-05  3:09 ` [PATCH RFC v2 2/8] ovl: Create ovl_strcmp() with casefold support André Almeida
@ 2025-08-05  3:09 ` André Almeida
  2025-08-05  3:09 ` [PATCH RFC v2 4/8] ovl: Ensure that all mount points have the same encoding André Almeida
                   ` (4 subsequent siblings)
  7 siblings, 0 replies; 13+ messages in thread
From: André Almeida @ 2025-08-05  3:09 UTC (permalink / raw)
  To: Miklos Szeredi, Amir Goldstein, Theodore Tso,
	Gabriel Krisman Bertazi
  Cc: linux-unionfs, linux-kernel, linux-fsdevel, Alexander Viro,
	Christian Brauner, Jan Kara, kernel-dev, André Almeida

For cases where a file lookup can go to different mount points (like in
overlayfs), both super blocks must have the same encoding and the same
flags. To help with that, create a sb_same_encoding() function.

Signed-off-by: André Almeida <andrealmeid@igalia.com>
---
 include/linux/fs.h | 22 ++++++++++++++++++++++
 1 file changed, 22 insertions(+)

diff --git a/include/linux/fs.h b/include/linux/fs.h
index 1948b2c828d3e83691f0b0892b4a7a87501c11db..413d4b2b7a41c0706324d01e71963bc481f383e4 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -3744,6 +3744,28 @@ static inline bool sb_has_encoding(const struct super_block *sb)
 #endif
 }
 
+/*
+ * Compare if two super blocks have the same encoding and flags
+ */
+static inline bool sb_same_encoding(const struct super_block *sb1,
+				    const struct super_block *sb2)
+{
+#if IS_ENABLED(CONFIG_UNICODE)
+	if (sb1->s_encoding == sb2->s_encoding)
+		return true;
+
+	if (sb1->s_encoding && sb2->s_encoding &&
+	    (sb1->s_encoding->version == sb2->s_encoding->version) &&
+	    (sb1->s_encoding_flags == sb2->s_encoding_flags))
+		return true;
+	else
+		return false;
+#else
+	return true;
+#endif
+}
+
+
 int may_setattr(struct mnt_idmap *idmap, struct inode *inode,
 		unsigned int ia_valid);
 int setattr_prepare(struct mnt_idmap *, struct dentry *, struct iattr *);

-- 
2.50.1


^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [PATCH RFC v2 4/8] ovl: Ensure that all mount points have the same encoding
  2025-08-05  3:09 [PATCH RFC v2 0/8] ovl: Enable support for casefold filesystems André Almeida
                   ` (2 preceding siblings ...)
  2025-08-05  3:09 ` [PATCH RFC v2 3/8] fs: Create sb_same_encoding() helper André Almeida
@ 2025-08-05  3:09 ` André Almeida
  2025-08-05  3:09 ` [PATCH RFC v2 5/8] ovl: Set case-insensitive dentry operations for ovl sb André Almeida
                   ` (3 subsequent siblings)
  7 siblings, 0 replies; 13+ messages in thread
From: André Almeida @ 2025-08-05  3:09 UTC (permalink / raw)
  To: Miklos Szeredi, Amir Goldstein, Theodore Tso,
	Gabriel Krisman Bertazi
  Cc: linux-unionfs, linux-kernel, linux-fsdevel, Alexander Viro,
	Christian Brauner, Jan Kara, kernel-dev, André Almeida

When mounting different mount points with casefold support, they should
use the same encoding version and have the same flags to avoid any kind
of incompatibility issues.

Signed-off-by: André Almeida <andrealmeid@igalia.com>
---
 fs/overlayfs/super.c | 11 +++++++++++
 1 file changed, 11 insertions(+)

diff --git a/fs/overlayfs/super.c b/fs/overlayfs/super.c
index 4afa91882075110fdeb5e265ce207236c9eff28e..cfe8010616414a5ec0421b9ac5947596bfd0a5bd 100644
--- a/fs/overlayfs/super.c
+++ b/fs/overlayfs/super.c
@@ -998,6 +998,7 @@ static int ovl_get_layers(struct super_block *sb, struct ovl_fs *ofs,
 	int err;
 	unsigned int i;
 	size_t nr_merged_lower;
+	struct super_block *sb1 = NULL;
 
 	ofs->fs = kcalloc(ctx->nr + 2, sizeof(struct ovl_sb), GFP_KERNEL);
 	if (ofs->fs == NULL)
@@ -1024,6 +1025,8 @@ static int ovl_get_layers(struct super_block *sb, struct ovl_fs *ofs,
 	if (ovl_upper_mnt(ofs)) {
 		ofs->fs[0].sb = ovl_upper_mnt(ofs)->mnt_sb;
 		ofs->fs[0].is_lower = false;
+
+		sb1 = ofs->fs[0].sb;
 	}
 
 	nr_merged_lower = ctx->nr - ctx->nr_data;
@@ -1067,6 +1070,9 @@ static int ovl_get_layers(struct super_block *sb, struct ovl_fs *ofs,
 			return err;
 		}
 
+		if (!sb1)
+			sb1 = mnt->mnt_sb;
+
 		/*
 		 * Make lower layers R/O.  That way fchmod/fchown on lower file
 		 * will fail instead of modifying lower fs.
@@ -1083,6 +1089,11 @@ static int ovl_get_layers(struct super_block *sb, struct ovl_fs *ofs,
 		l->name = NULL;
 		ofs->numlayer++;
 		ofs->fs[fsid].is_lower = true;
+
+		if (!sb_same_encoding(sb1, mnt->mnt_sb)) {
+			pr_err("all layers must have the same encoding\n");
+			return -EINVAL;
+		}
 	}
 
 	/*

-- 
2.50.1


^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [PATCH RFC v2 5/8] ovl: Set case-insensitive dentry operations for ovl sb
  2025-08-05  3:09 [PATCH RFC v2 0/8] ovl: Enable support for casefold filesystems André Almeida
                   ` (3 preceding siblings ...)
  2025-08-05  3:09 ` [PATCH RFC v2 4/8] ovl: Ensure that all mount points have the same encoding André Almeida
@ 2025-08-05  3:09 ` André Almeida
  2025-08-05  3:09 ` [PATCH RFC v2 6/8] ovl: Set inode S_CASEFOLD for casefolded dentries André Almeida
                   ` (2 subsequent siblings)
  7 siblings, 0 replies; 13+ messages in thread
From: André Almeida @ 2025-08-05  3:09 UTC (permalink / raw)
  To: Miklos Szeredi, Amir Goldstein, Theodore Tso,
	Gabriel Krisman Bertazi
  Cc: linux-unionfs, linux-kernel, linux-fsdevel, Alexander Viro,
	Christian Brauner, Jan Kara, kernel-dev, André Almeida

For filesystems with encoding (i.e. with case-insensitive support), set
the dentry operations for the super block with generic_set_sb_d_ops().
Also, use the upper layer encoding as the ovl super block encoding.

Signed-off-by: André Almeida <andrealmeid@igalia.com>
---
 fs/overlayfs/super.c | 10 ++++++++++
 1 file changed, 10 insertions(+)

diff --git a/fs/overlayfs/super.c b/fs/overlayfs/super.c
index cfe8010616414a5ec0421b9ac5947596bfd0a5bd..5d55a287a8c0f69aeaf2ada862c59bd7eb7b10f2 100644
--- a/fs/overlayfs/super.c
+++ b/fs/overlayfs/super.c
@@ -1423,6 +1423,16 @@ int ovl_fill_super(struct super_block *sb, struct fs_context *fc)
 
 		sb->s_stack_depth = upper_sb->s_stack_depth;
 		sb->s_time_gran = upper_sb->s_time_gran;
+
+#if IS_ENABLED(CONFIG_UNICODE)
+		if (sb_has_encoding(upper_sb)) {
+			sb->s_encoding = upper_sb->s_encoding;
+			sb->s_encoding_flags = upper_sb->s_encoding_flags;
+		}
+
+		generic_set_sb_d_ops(sb);
+#endif
+
 	}
 	oe = ovl_get_lowerstack(sb, ctx, ofs, layers);
 	err = PTR_ERR(oe);

-- 
2.50.1


^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [PATCH RFC v2 6/8] ovl: Set inode S_CASEFOLD for casefolded dentries
  2025-08-05  3:09 [PATCH RFC v2 0/8] ovl: Enable support for casefold filesystems André Almeida
                   ` (4 preceding siblings ...)
  2025-08-05  3:09 ` [PATCH RFC v2 5/8] ovl: Set case-insensitive dentry operations for ovl sb André Almeida
@ 2025-08-05  3:09 ` André Almeida
  2025-08-05  3:09 ` [PATCH RFC v2 7/8] ovl: Check casefold consistency in ovl stack André Almeida
  2025-08-05  3:09 ` [PATCH RFC v2 8/8] ovl: Drop restrictions for casefolded dentries André Almeida
  7 siblings, 0 replies; 13+ messages in thread
From: André Almeida @ 2025-08-05  3:09 UTC (permalink / raw)
  To: Miklos Szeredi, Amir Goldstein, Theodore Tso,
	Gabriel Krisman Bertazi
  Cc: linux-unionfs, linux-kernel, linux-fsdevel, Alexander Viro,
	Christian Brauner, Jan Kara, kernel-dev, André Almeida

For casefolded dentries, set their inode flag with S_CASEFOLD.

Signed-off-by: André Almeida <andrealmeid@igalia.com>
---
 fs/overlayfs/inode.c | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/fs/overlayfs/inode.c b/fs/overlayfs/inode.c
index ecb9f2019395ecd01a124ad029375b1a1d13ebb5..5dec29811b6a5088f838793eb2f561abd12db1d2 100644
--- a/fs/overlayfs/inode.c
+++ b/fs/overlayfs/inode.c
@@ -1301,6 +1301,13 @@ struct inode *ovl_get_inode(struct super_block *sb,
 
 	if (inode->i_state & I_NEW)
 		unlock_new_inode(inode);
+
+	if (realpath.dentry && ovl_dentry_casefolded(realpath.dentry)) {
+		int flags = inode->i_flags | S_CASEFOLD;
+
+		inode_set_flags(inode, flags, flags);
+	}
+
 out:
 	return inode;
 

-- 
2.50.1


^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [PATCH RFC v2 7/8] ovl: Check casefold consistency in ovl stack
  2025-08-05  3:09 [PATCH RFC v2 0/8] ovl: Enable support for casefold filesystems André Almeida
                   ` (5 preceding siblings ...)
  2025-08-05  3:09 ` [PATCH RFC v2 6/8] ovl: Set inode S_CASEFOLD for casefolded dentries André Almeida
@ 2025-08-05  3:09 ` André Almeida
  2025-08-05  3:09 ` [PATCH RFC v2 8/8] ovl: Drop restrictions for casefolded dentries André Almeida
  7 siblings, 0 replies; 13+ messages in thread
From: André Almeida @ 2025-08-05  3:09 UTC (permalink / raw)
  To: Miklos Szeredi, Amir Goldstein, Theodore Tso,
	Gabriel Krisman Bertazi
  Cc: linux-unionfs, linux-kernel, linux-fsdevel, Alexander Viro,
	Christian Brauner, Jan Kara, kernel-dev, André Almeida

overlayfs supports case-insensitive filesystems as long as they are
consistent regarding its enabled status. If one layer has it enabled,
every other layer must have it enabled as well. Check if this
consistency is being respected, and return an error otherwise.

Signed-off-by: André Almeida <andrealmeid@igalia.com>
---
 fs/overlayfs/namei.c | 25 ++++++++++---------------
 1 file changed, 10 insertions(+), 15 deletions(-)

diff --git a/fs/overlayfs/namei.c b/fs/overlayfs/namei.c
index 76d6248b625e7c58e09685e421aef616aadea40a..5dee504667911f04ce543f7977d0d4c4a1190cc7 100644
--- a/fs/overlayfs/namei.c
+++ b/fs/overlayfs/namei.c
@@ -238,18 +238,6 @@ static int ovl_lookup_single(struct dentry *base, struct ovl_lookup_data *d,
 	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);
@@ -259,9 +247,16 @@ 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;
+	/*
+	 * We allow filesystems that are case-folding capable as long as the
+	 * layers are consistently enabled in the stack, enabled for every layer
+	 * or disabled in all layers. 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) != ovl_dentry_casefolded(this)) {
+		warn = "casefold mismatch between parent and child";
+		err = -ESTALE;
 		goto out_warn;
 	}
 

-- 
2.50.1


^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [PATCH RFC v2 8/8] ovl: Drop restrictions for casefolded dentries
  2025-08-05  3:09 [PATCH RFC v2 0/8] ovl: Enable support for casefold filesystems André Almeida
                   ` (6 preceding siblings ...)
  2025-08-05  3:09 ` [PATCH RFC v2 7/8] ovl: Check casefold consistency in ovl stack André Almeida
@ 2025-08-05  3:09 ` André Almeida
  7 siblings, 0 replies; 13+ messages in thread
From: André Almeida @ 2025-08-05  3:09 UTC (permalink / raw)
  To: Miklos Szeredi, Amir Goldstein, Theodore Tso,
	Gabriel Krisman Bertazi
  Cc: linux-unionfs, linux-kernel, linux-fsdevel, Alexander Viro,
	Christian Brauner, Jan Kara, kernel-dev, André Almeida

Drop the restriction for casefold dentries to enable support for
casefold filesystems in overlayfs.

Signed-off-by: André Almeida <andrealmeid@igalia.com>
---
 fs/overlayfs/params.c | 7 -------
 fs/overlayfs/util.c   | 8 ++++----
 2 files changed, 4 insertions(+), 11 deletions(-)

diff --git a/fs/overlayfs/params.c b/fs/overlayfs/params.c
index f4e7fff909ac49e2f8c58a76273426c1158a7472..dd3a893d37603842f7d19d90accb981f0d12e971 100644
--- a/fs/overlayfs/params.c
+++ b/fs/overlayfs/params.c
@@ -281,13 +281,6 @@ static int ovl_mount_dir_check(struct fs_context *fc, const struct path *path,
 	if (!d_is_dir(path->dentry))
 		return invalfc(fc, "%s is not a directory", name);
 
-	/*
-	 * Allow filesystems that are case-folding capable but deny composing
-	 * ovl stack from case-folded directories.
-	 */
-	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);
 
diff --git a/fs/overlayfs/util.c b/fs/overlayfs/util.c
index a33115e7384c129c543746326642813add63f060..7a6ee058568283453350153c1720c35e11ad4d1b 100644
--- a/fs/overlayfs/util.c
+++ b/fs/overlayfs/util.c
@@ -210,11 +210,11 @@ bool ovl_dentry_weird(struct dentry *dentry)
 		return true;
 
 	/*
-	 * Allow filesystems that are case-folding capable but deny composing
-	 * ovl stack from case-folded directories.
+	 * Exceptionally for casefold dentries, we accept that they have their
+	 * own hash and compare operations
 	 */
-	if (sb_has_encoding(dentry->d_sb))
-		return IS_CASEFOLDED(d_inode(dentry));
+	if (ovl_dentry_casefolded(dentry))
+		return false;
 
 	return dentry->d_flags & (DCACHE_OP_HASH | DCACHE_OP_COMPARE);
 }

-- 
2.50.1


^ permalink raw reply related	[flat|nested] 13+ messages in thread

* Re: [PATCH RFC v2 2/8] ovl: Create ovl_strcmp() with casefold support
  2025-08-05  3:09 ` [PATCH RFC v2 2/8] ovl: Create ovl_strcmp() with casefold support André Almeida
@ 2025-08-05  5:08   ` Al Viro
  2025-08-05 13:01     ` André Almeida
  2025-08-05 14:56   ` Gabriel Krisman Bertazi
  1 sibling, 1 reply; 13+ messages in thread
From: Al Viro @ 2025-08-05  5:08 UTC (permalink / raw)
  To: André Almeida
  Cc: Miklos Szeredi, Amir Goldstein, Theodore Tso,
	Gabriel Krisman Bertazi, linux-unionfs, linux-kernel,
	linux-fsdevel, Christian Brauner, Jan Kara, kernel-dev

On Tue, Aug 05, 2025 at 12:09:06AM -0300, André Almeida wrote:

> +static int ovl_strcmp(const char *str, struct ovl_cache_entry *p, int len)

> +	if (p->map && !is_dot_dotdot(str, len)) {
> +		dst = kmalloc(OVL_NAME_LEN, GFP_KERNEL);

...`

> +	kfree(dst);
> +
> +	return cmp;
> +}
> +

> @@ -107,7 +145,7 @@ static struct ovl_cache_entry *ovl_cache_entry_find(struct rb_root *root,
>  	while (node) {
>  		struct ovl_cache_entry *p = ovl_cache_entry_from_node(node);
>  
> -		cmp = strncmp(name, p->name, len);
> +		cmp = ovl_strcmp(name, p, len);
>  		if (cmp > 0)
>  			node = p->node.rb_right;
>  		else if (cmp < 0 || len < p->len)

Am I misreading that, or do really we get a kmalloc()/kfree() for each
sodding tree node we traverse on rbtree lookup here?

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH RFC v2 2/8] ovl: Create ovl_strcmp() with casefold support
  2025-08-05  5:08   ` Al Viro
@ 2025-08-05 13:01     ` André Almeida
  0 siblings, 0 replies; 13+ messages in thread
From: André Almeida @ 2025-08-05 13:01 UTC (permalink / raw)
  To: Al Viro
  Cc: Miklos Szeredi, Amir Goldstein, Theodore Tso,
	Gabriel Krisman Bertazi, linux-unionfs, linux-kernel,
	linux-fsdevel, Christian Brauner, Jan Kara, kernel-dev

Em 05/08/2025 02:08, Al Viro escreveu:
> On Tue, Aug 05, 2025 at 12:09:06AM -0300, André Almeida wrote:
> 
>> +static int ovl_strcmp(const char *str, struct ovl_cache_entry *p, int len)
> 
>> +	if (p->map && !is_dot_dotdot(str, len)) {
>> +		dst = kmalloc(OVL_NAME_LEN, GFP_KERNEL);
> 
> ...`
> 
>> +	kfree(dst);
>> +
>> +	return cmp;
>> +}
>> +
> 
>> @@ -107,7 +145,7 @@ static struct ovl_cache_entry *ovl_cache_entry_find(struct rb_root *root,
>>   	while (node) {
>>   		struct ovl_cache_entry *p = ovl_cache_entry_from_node(node);
>>   
>> -		cmp = strncmp(name, p->name, len);
>> +		cmp = ovl_strcmp(name, p, len);
>>   		if (cmp > 0)
>>   			node = p->node.rb_right;
>>   		else if (cmp < 0 || len < p->len)
> 
> Am I misreading that, or do really we get a kmalloc()/kfree() for each
> sodding tree node we traverse on rbtree lookup here?

Yes, this is what's implemented here, as it is. Alternatively, I could 
allocate one buffer prior to the rbtree search/insert to be reused, and 
free it later... I going to add that for the v3.

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH RFC v2 2/8] ovl: Create ovl_strcmp() with casefold support
  2025-08-05  3:09 ` [PATCH RFC v2 2/8] ovl: Create ovl_strcmp() with casefold support André Almeida
  2025-08-05  5:08   ` Al Viro
@ 2025-08-05 14:56   ` Gabriel Krisman Bertazi
  2025-08-05 18:40     ` André Almeida
  1 sibling, 1 reply; 13+ messages in thread
From: Gabriel Krisman Bertazi @ 2025-08-05 14:56 UTC (permalink / raw)
  To: André Almeida
  Cc: Miklos Szeredi, Amir Goldstein, Theodore Tso, linux-unionfs,
	linux-kernel, linux-fsdevel, Alexander Viro, Christian Brauner,
	Jan Kara, kernel-dev

André Almeida <andrealmeid@igalia.com> writes:

> To add overlayfs support casefold filesystems, create a new function
> ovl_strcmp() with support for casefold names.
>
> If the ovl_cache_entry have stored a casefold name, use it and create
> a casfold version of the name that is going to be compared to.
>
> For the casefold support, just comparing the strings does not work
> because we need the dentry enconding, so make this function find the
> equivalent dentry for a giving directory, if any.
>
> As this function is used for search and insertion in the red-black tree,
> that means that the tree node keys are going to be the casefolded
> version of the dentry's names. Otherwise, the search would not work for
> case-insensitive mount points.
>
> For the non-casefold names, nothing changes.
>
> Signed-off-by: André Almeida <andrealmeid@igalia.com>
> ---
> I wonder what should be done here if kmalloc fails, if the strcmp()
> should fail as well or just fallback to the normal name?
> ---
>  fs/overlayfs/readdir.c | 42 ++++++++++++++++++++++++++++++++++++++++--
>  1 file changed, 40 insertions(+), 2 deletions(-)
>
> diff --git a/fs/overlayfs/readdir.c b/fs/overlayfs/readdir.c
> index 83bca1bcb0488461b08effa70b32ff2fefba134e..1b8eb10e72a229ade40d18795746d3c779797a06 100644
> --- a/fs/overlayfs/readdir.c
> +++ b/fs/overlayfs/readdir.c
> @@ -72,6 +72,44 @@ static struct ovl_cache_entry *ovl_cache_entry_from_node(struct rb_node *n)
>  	return rb_entry(n, struct ovl_cache_entry, node);
>  }
>  
> +/*
> + * Compare a string with a cache entry, with support for casefold names.
> + */
> +static int ovl_strcmp(const char *str, struct ovl_cache_entry *p, int len)
> +{

Why do you need to re-casefold str on every call to ovl_strcmp?  Isn't
it done in a loop while walking the rbtree with a constant "str" (i.e.,
the name being added, see ovl_cache_entry_find)? Can't you do it once,
outside of ovl_strcmp? This way you don't repeatedly allocate/free
memory for each node of the tree (as Viro mentioned), and you don't have
to deal with kmalloc failures here.

> +
> +	const struct qstr qstr = { .name = str, .len = len };
> +	const char *p_name = p->name, *name = str;
> +	char *dst = NULL;
> +	int cmp, cf_len;
> +
> +	if (p->cf_name)
> +		p_name = p->cf_name;

This should check IS_ENABLED(CONFIG_UNICODE) so it can be
compiled out by anyone doing CONFIG_UNICODE=n

> +
> +	if (p->map && !is_dot_dotdot(str, len)) {
> +		dst = kmalloc(OVL_NAME_LEN, GFP_KERNEL);
> +
> +		/*
> +		 * strcmp can't fail, so we fallback to the use the original
> +		 * name
> +		 */
> +		if (dst) {
> +			cf_len = utf8_casefold(p->map, &qstr, dst, OVL_NAME_LEN);

utf8_casefold can fail, as you know and checked.  But if it does, a
negative cf_len is passed to strncmp and cast to a very high
value.

> +
> +			if (cf_len > 0) {
> +				name = dst;
> +				dst[cf_len] = '\0';
> +			}

utf8_casefold ensures the string is NULL-terminated on success already.

> +		}
> +	}
> +
> +	cmp = strncmp(name, p_name, cf_len);
> +
> +	kfree(dst);
> +
> +	return cmp;
> +}
> +
>  static bool ovl_cache_entry_find_link(const char *name, int len,
>  				      struct rb_node ***link,
>  				      struct rb_node **parent)
> @@ -85,7 +123,7 @@ static bool ovl_cache_entry_find_link(const char *name, int len,
>  
>  		*parent = *newp;
>  		tmp = ovl_cache_entry_from_node(*newp);
> -		cmp = strncmp(name, tmp->name, len);
> +		cmp = ovl_strcmp(name, tmp, len);
>  		if (cmp > 0)
>  			newp = &tmp->node.rb_right;
>  		else if (cmp < 0 || len < tmp->len)
> @@ -107,7 +145,7 @@ static struct ovl_cache_entry *ovl_cache_entry_find(struct rb_root *root,
>  	while (node) {
>  		struct ovl_cache_entry *p = ovl_cache_entry_from_node(node);
>  
> -		cmp = strncmp(name, p->name, len);
> +		cmp = ovl_strcmp(name, p, len);
>  		if (cmp > 0)
>  			node = p->node.rb_right;
>  		else if (cmp < 0 || len < p->len)

-- 
Gabriel Krisman Bertazi

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH RFC v2 2/8] ovl: Create ovl_strcmp() with casefold support
  2025-08-05 14:56   ` Gabriel Krisman Bertazi
@ 2025-08-05 18:40     ` André Almeida
  0 siblings, 0 replies; 13+ messages in thread
From: André Almeida @ 2025-08-05 18:40 UTC (permalink / raw)
  To: Gabriel Krisman Bertazi
  Cc: Miklos Szeredi, Amir Goldstein, Theodore Tso, linux-unionfs,
	linux-kernel, linux-fsdevel, Alexander Viro, Christian Brauner,
	Jan Kara, kernel-dev

Hi Gabriel!

Em 05/08/2025 11:56, Gabriel Krisman Bertazi escreveu:
> André Almeida <andrealmeid@igalia.com> writes:
> 
>> To add overlayfs support casefold filesystems, create a new function
>> ovl_strcmp() with support for casefold names.
>>
>> If the ovl_cache_entry have stored a casefold name, use it and create
>> a casfold version of the name that is going to be compared to.
>>
>> For the casefold support, just comparing the strings does not work
>> because we need the dentry enconding, so make this function find the
>> equivalent dentry for a giving directory, if any.
>>
>> As this function is used for search and insertion in the red-black tree,
>> that means that the tree node keys are going to be the casefolded
>> version of the dentry's names. Otherwise, the search would not work for
>> case-insensitive mount points.
>>
>> For the non-casefold names, nothing changes.
>>
>> Signed-off-by: André Almeida <andrealmeid@igalia.com>
>> ---
>> I wonder what should be done here if kmalloc fails, if the strcmp()
>> should fail as well or just fallback to the normal name?
>> ---
>>   fs/overlayfs/readdir.c | 42 ++++++++++++++++++++++++++++++++++++++++--
>>   1 file changed, 40 insertions(+), 2 deletions(-)
>>
>> diff --git a/fs/overlayfs/readdir.c b/fs/overlayfs/readdir.c
>> index 83bca1bcb0488461b08effa70b32ff2fefba134e..1b8eb10e72a229ade40d18795746d3c779797a06 100644
>> --- a/fs/overlayfs/readdir.c
>> +++ b/fs/overlayfs/readdir.c
>> @@ -72,6 +72,44 @@ static struct ovl_cache_entry *ovl_cache_entry_from_node(struct rb_node *n)
>>   	return rb_entry(n, struct ovl_cache_entry, node);
>>   }
>>   
>> +/*
>> + * Compare a string with a cache entry, with support for casefold names.
>> + */
>> +static int ovl_strcmp(const char *str, struct ovl_cache_entry *p, int len)
>> +{
> 
> Why do you need to re-casefold str on every call to ovl_strcmp?  Isn't
> it done in a loop while walking the rbtree with a constant "str" (i.e.,
> the name being added, see ovl_cache_entry_find)? Can't you do it once,
> outside of ovl_strcmp? This way you don't repeatedly allocate/free
> memory for each node of the tree (as Viro mentioned), and you don't have
> to deal with kmalloc failures here.
> 

Yes, that's a more reasonable approach, I will do it for v3

>> +
>> +	const struct qstr qstr = { .name = str, .len = len };
>> +	const char *p_name = p->name, *name = str;
>> +	char *dst = NULL;
>> +	int cmp, cf_len;
>> +
>> +	if (p->cf_name)
>> +		p_name = p->cf_name;
> 
> This should check IS_ENABLED(CONFIG_UNICODE) so it can be
> compiled out by anyone doing CONFIG_UNICODE=n
> 
>> +
>> +	if (p->map && !is_dot_dotdot(str, len)) {
>> +		dst = kmalloc(OVL_NAME_LEN, GFP_KERNEL);
>> +
>> +		/*
>> +		 * strcmp can't fail, so we fallback to the use the original
>> +		 * name
>> +		 */
>> +		if (dst) {
>> +			cf_len = utf8_casefold(p->map, &qstr, dst, OVL_NAME_LEN);
> 
> utf8_casefold can fail, as you know and checked.  But if it does, a
> negative cf_len is passed to strncmp and cast to a very high
> value.
> 

ops, that's right, thanks for the feedback!

^ permalink raw reply	[flat|nested] 13+ messages in thread

end of thread, other threads:[~2025-08-05 18:40 UTC | newest]

Thread overview: 13+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-08-05  3:09 [PATCH RFC v2 0/8] ovl: Enable support for casefold filesystems André Almeida
2025-08-05  3:09 ` [PATCH RFC v2 1/8] olv: Store casefold name for case-insentive dentries André Almeida
2025-08-05  3:09 ` [PATCH RFC v2 2/8] ovl: Create ovl_strcmp() with casefold support André Almeida
2025-08-05  5:08   ` Al Viro
2025-08-05 13:01     ` André Almeida
2025-08-05 14:56   ` Gabriel Krisman Bertazi
2025-08-05 18:40     ` André Almeida
2025-08-05  3:09 ` [PATCH RFC v2 3/8] fs: Create sb_same_encoding() helper André Almeida
2025-08-05  3:09 ` [PATCH RFC v2 4/8] ovl: Ensure that all mount points have the same encoding André Almeida
2025-08-05  3:09 ` [PATCH RFC v2 5/8] ovl: Set case-insensitive dentry operations for ovl sb André Almeida
2025-08-05  3:09 ` [PATCH RFC v2 6/8] ovl: Set inode S_CASEFOLD for casefolded dentries André Almeida
2025-08-05  3:09 ` [PATCH RFC v2 7/8] ovl: Check casefold consistency in ovl stack André Almeida
2025-08-05  3:09 ` [PATCH RFC v2 8/8] ovl: Drop restrictions for casefolded dentries André Almeida

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).