[PATCH RFC v3 2/7] ovl: Create ovl_casefold() to support casefolded strncmp()

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



To add overlayfs support casefold filesystems, create a new function
ovl_casefold(), to be able to do case-insensitive strncmp().

ovl_casefold() allocates a new buffer and stores the casefolded version
of the string on it. If the allocation or the casefold operation fails,
fallback to use the original string. The caller of the function is
responsible of freeing the buffer.

The other string to be compared is casefolded in a previous step and
stored at `struct ovl_cache_entry` member `char *cf_name`.

Finally, set the strncmp() parameters to the casefold versions of the
names to achieve case-insensitive support.

For the non-casefold names, nothing changes and the rb_tree
search/insert functions just ignores this change.

Signed-off-by: André Almeida <andrealmeid@xxxxxxxxxx>
---
Changes from v2:
- Refactor the patch to do a single kmalloc() per rb_tree operation
- Instead of casefolding the cache entry name everytime per strncmp(),
  casefold it once and reuse it for every strncmp().
---
 fs/overlayfs/readdir.c | 92 +++++++++++++++++++++++++++++++++++++++++++-------
 1 file changed, 80 insertions(+), 12 deletions(-)

diff --git a/fs/overlayfs/readdir.c b/fs/overlayfs/readdir.c
index 2f42fec97f76c2000f76e15c60975db567b2c6d6..422f991393dfae12bcacf326414b7ee19e486ac8 100644
--- a/fs/overlayfs/readdir.c
+++ b/fs/overlayfs/readdir.c
@@ -71,20 +71,58 @@ static struct ovl_cache_entry *ovl_cache_entry_from_node(struct rb_node *n)
 	return rb_entry(n, struct ovl_cache_entry, node);
 }
 
+static int ovl_casefold(struct unicode_map *map, const char *str, int len, char **dst)
+{
+#if IS_ENABLED(CONFIG_UNICODE)
+	const struct qstr qstr = { .name = str, .len = len };
+	int cf_len;
+
+	if (!map || is_dot_dotdot(str, len))
+		return -1;
+
+	*dst = kmalloc(OVL_NAME_LEN, GFP_KERNEL);
+
+	if (dst) {
+		cf_len = utf8_casefold(map, &qstr, *dst, OVL_NAME_LEN);
+
+		if (cf_len > 0)
+			return cf_len;
+	}
+#endif
+
+	return -1;
+}
+
 static bool ovl_cache_entry_find_link(const char *name, int len,
 				      struct rb_node ***link,
-				      struct rb_node **parent)
+				      struct rb_node **parent,
+				      struct unicode_map *map)
 {
+	int ret;
+	char *dst = NULL;
 	bool found = false;
+	const char *str = name;
 	struct rb_node **newp = *link;
 
+	ret = ovl_casefold(map, name, len, &dst);
+
+	if (ret > 0) {
+		str = dst;
+		len = ret;
+	}
+
 	while (!found && *newp) {
 		int cmp;
+		char *aux;
 		struct ovl_cache_entry *tmp;
 
 		*parent = *newp;
+
 		tmp = ovl_cache_entry_from_node(*newp);
-		cmp = strncmp(name, tmp->name, len);
+
+		aux = tmp->cf_name ? tmp->cf_name : tmp->name;
+
+		cmp = strncmp(str, aux, len);
 		if (cmp > 0)
 			newp = &tmp->node.rb_right;
 		else if (cmp < 0 || len < tmp->len)
@@ -94,27 +132,50 @@ static bool ovl_cache_entry_find_link(const char *name, int len,
 	}
 	*link = newp;
 
+	kfree(dst);
+
 	return found;
 }
 
 static struct ovl_cache_entry *ovl_cache_entry_find(struct rb_root *root,
-						    const char *name, int len)
+						    const char *name, int len,
+						    struct unicode_map *map)
 {
 	struct rb_node *node = root->rb_node;
-	int cmp;
+	struct ovl_cache_entry *p;
+	const char *str = name;
+	bool found = false;
+	char *dst = NULL;
+	int cmp, ret;
+
+	ret = ovl_casefold(map, name, len, &dst);
+
+	if (ret > 0) {
+		str = dst;
+		len = ret;
+	}
+
+	while (!found && node) {
+		char *aux;
 
-	while (node) {
-		struct ovl_cache_entry *p = ovl_cache_entry_from_node(node);
+		p = ovl_cache_entry_from_node(node);
 
-		cmp = strncmp(name, p->name, len);
+		aux = p->cf_name ? p->cf_name : p->name;
+
+		cmp = strncmp(str, aux, len);
 		if (cmp > 0)
 			node = p->node.rb_right;
 		else if (cmp < 0 || len < p->len)
 			node = p->node.rb_left;
 		else
-			return p;
+			found = true;
 	}
 
+	kfree(dst);
+
+	if (found)
+		return p;
+
 	return NULL;
 }
 
@@ -212,7 +273,7 @@ static bool ovl_cache_entry_add_rb(struct ovl_readdir_data *rdd,
 	struct rb_node *parent = NULL;
 	struct ovl_cache_entry *p;
 
-	if (ovl_cache_entry_find_link(name, len, &newp, &parent))
+	if (ovl_cache_entry_find_link(name, len, &newp, &parent, rdd->map))
 		return true;
 
 	p = ovl_cache_entry_new(rdd, name, len, ino, d_type);
@@ -234,7 +295,7 @@ static bool ovl_fill_lowest(struct ovl_readdir_data *rdd,
 {
 	struct ovl_cache_entry *p;
 
-	p = ovl_cache_entry_find(rdd->root, name, namelen);
+	p = ovl_cache_entry_find(rdd->root, name, namelen, rdd->map);
 	if (p) {
 		list_move_tail(&p->l_node, &rdd->middle);
 	} else {
@@ -640,7 +701,8 @@ static int ovl_dir_read_impure(const struct path *path,  struct list_head *list,
 			struct rb_node *parent = NULL;
 
 			if (WARN_ON(ovl_cache_entry_find_link(p->name, p->len,
-							      &newp, &parent)))
+							      &newp, &parent,
+							      rdd.map)))
 				return -EIO;
 
 			rb_link_node(&p->node, parent, newp);
@@ -701,6 +763,7 @@ struct ovl_readdir_translate {
 	struct dir_context *orig_ctx;
 	struct ovl_dir_cache *cache;
 	struct dir_context ctx;
+	struct unicode_map *map;
 	u64 parent_ino;
 	int fsid;
 	int xinobits;
@@ -721,7 +784,7 @@ static bool ovl_fill_real(struct dir_context *ctx, const char *name,
 	} else if (rdt->cache) {
 		struct ovl_cache_entry *p;
 
-		p = ovl_cache_entry_find(&rdt->cache->root, name, namelen);
+		p = ovl_cache_entry_find(&rdt->cache->root, name, namelen, rdt->map);
 		if (p)
 			ino = p->ino;
 	} else if (rdt->xinobits) {
@@ -763,11 +826,16 @@ static int ovl_iterate_real(struct file *file, struct dir_context *ctx)
 		.orig_ctx = ctx,
 		.xinobits = ovl_xino_bits(ofs),
 		.xinowarn = ovl_xino_warn(ofs),
+		.map      = NULL,
 	};
 
 	if (rdt.xinobits && lower_layer)
 		rdt.fsid = lower_layer->fsid;
 
+#if IS_ENABLED(CONFIG_UNICODE)
+	rdt.map = dir->d_sb->s_encoding;
+#endif
+
 	if (OVL_TYPE_MERGE(ovl_path_type(dir->d_parent))) {
 		struct kstat stat;
 		struct path statpath = file->f_path;

-- 
2.50.1





[Index of Archives]     [Linux Filesystems Devel]     [Linux NFS]     [Linux NILFS]     [Linux USB Devel]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux