public inbox for linux-fsdevel@vger.kernel.org
 help / color / mirror / Atom feed
* [PATCH v1 0/6] exfat: unify FAT chain walking helpers
@ 2026-03-31  9:11 Chi Zhiling
  2026-03-31  9:11 ` [PATCH v1 1/6] exfat: fix incorrect directory checksum after rename to shorter name Chi Zhiling
                   ` (6 more replies)
  0 siblings, 7 replies; 9+ messages in thread
From: Chi Zhiling @ 2026-03-31  9:11 UTC (permalink / raw)
  To: Namjae Jeon, Sungjong Seo, Yuezhang Mo
  Cc: linux-fsdevel, linux-kernel, Chi Zhiling

From: Chi Zhiling <chizhiling@kylinos.cn>

This series introduces and uses two new helpers to simplify FAT chain
walking logic across the exfat driver:

1. exfat_fat_walk - walks FAT chain by a given step, handling both
   ALLOC_NO_FAT_CHAIN and ALLOC_FAT_CHAIN modes

2. exfat_chain_advance - walks an exfat_chain structure by a given step,
   updating both ->dir and ->size fields atomically with proper boundary
   checking

These helpers replace open-coded cluster walking logic in dir.c, namei.c
and inode.c, improving code readability and ensuring consistent error
handling for corrupted FAT chains.

Also includes a fix for incorrect directory checksum when renaming a
file to a shorter name.

The base commit is exfat/dev without iomap changes


Chi Zhiling (6):
  exfat: fix incorrect directory checksum after rename to shorter name
  exfat: introduce exfat_fat_walk helper
  exfat: use exfat_fat_walk helper to simplify fat entry walking
  exfat: remove NULL cache pointer case in exfat_ent_get
  exfat: introduce exfat_chain_advance helper
  exfat: use exfat_chain_advance helper

 fs/exfat/dir.c      | 111 +++++++++-----------------------------------
 fs/exfat/exfat_fs.h |  49 ++++++++++++++++++-
 fs/exfat/fatent.c   |  23 ++++-----
 fs/exfat/inode.c    |  11 +----
 fs/exfat/namei.c    |  29 +++---------
 5 files changed, 88 insertions(+), 135 deletions(-)

-- 
2.43.0


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

* [PATCH v1 1/6] exfat: fix incorrect directory checksum after rename to shorter name
  2026-03-31  9:11 [PATCH v1 0/6] exfat: unify FAT chain walking helpers Chi Zhiling
@ 2026-03-31  9:11 ` Chi Zhiling
  2026-03-31  9:11 ` [PATCH v1 2/6] exfat: introduce exfat_fat_walk helper Chi Zhiling
                   ` (5 subsequent siblings)
  6 siblings, 0 replies; 9+ messages in thread
From: Chi Zhiling @ 2026-03-31  9:11 UTC (permalink / raw)
  To: Namjae Jeon, Sungjong Seo, Yuezhang Mo
  Cc: linux-fsdevel, linux-kernel, Chi Zhiling

From: Chi Zhiling <chizhiling@kylinos.cn>

When renaming a file in-place to a shorter name, exfat_remove_entries
marks excess entries as DELETED, but es->num_entries is not updated
accordingly. As a result, exfat_update_dir_chksum iterates over the
deleted entries and computes an incorrect checksum.

This does not lead to persistent corruption because mark_inode_dirty()
is called afterward, and __exfat_write_inode later recomputes the
checksum using the correct num_entries value.

Fix by setting es->num_entries = num_entries in exfat_init_ext_entry.

Signed-off-by: Chi Zhiling <chizhiling@kylinos.cn>
---
 fs/exfat/dir.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/fs/exfat/dir.c b/fs/exfat/dir.c
index a2c2b998808c..7619410d668e 100644
--- a/fs/exfat/dir.c
+++ b/fs/exfat/dir.c
@@ -490,6 +490,7 @@ void exfat_init_ext_entry(struct exfat_entry_set_cache *es, int num_entries,
 	unsigned short *uniname = p_uniname->name;
 	struct exfat_dentry *ep;
 
+	es->num_entries = num_entries;
 	ep = exfat_get_dentry_cached(es, ES_IDX_FILE);
 	ep->dentry.file.num_ext = (unsigned char)(num_entries - 1);
 
-- 
2.43.0


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

* [PATCH v1 2/6] exfat: introduce exfat_fat_walk helper
  2026-03-31  9:11 [PATCH v1 0/6] exfat: unify FAT chain walking helpers Chi Zhiling
  2026-03-31  9:11 ` [PATCH v1 1/6] exfat: fix incorrect directory checksum after rename to shorter name Chi Zhiling
@ 2026-03-31  9:11 ` Chi Zhiling
  2026-03-31  9:11 ` [PATCH v1 3/6] exfat: use exfat_fat_walk helper to simplify fat entry walking Chi Zhiling
                   ` (4 subsequent siblings)
  6 siblings, 0 replies; 9+ messages in thread
From: Chi Zhiling @ 2026-03-31  9:11 UTC (permalink / raw)
  To: Namjae Jeon, Sungjong Seo, Yuezhang Mo
  Cc: linux-fsdevel, linux-kernel, Chi Zhiling

From: Chi Zhiling <chizhiling@kylinos.cn>

Introduce exfat_fat_walk() to walk the FAT chain by a given step,
handling both ALLOC_NO_FAT_CHAIN and ALLOC_FAT_CHAIN modes. Also
redefine exfat_get_next_cluster as a thin wrapper around it for
backward compatibility.

Signed-off-by: Chi Zhiling <chizhiling@kylinos.cn>
---
 fs/exfat/exfat_fs.h | 23 ++++++++++++++++++++++-
 1 file changed, 22 insertions(+), 1 deletion(-)

diff --git a/fs/exfat/exfat_fs.h b/fs/exfat/exfat_fs.h
index 9fed9fb33cae..530459ab9acc 100644
--- a/fs/exfat/exfat_fs.h
+++ b/fs/exfat/exfat_fs.h
@@ -437,7 +437,8 @@ int exfat_set_volume_dirty(struct super_block *sb);
 int exfat_clear_volume_dirty(struct super_block *sb);
 
 /* fatent.c */
-#define exfat_get_next_cluster(sb, pclu) exfat_ent_get(sb, *(pclu), pclu, NULL)
+#define exfat_get_next_cluster(sb, pclu) \
+	exfat_fat_walk(sb, (pclu), 1, ALLOC_FAT_CHAIN)
 
 int exfat_alloc_cluster(struct inode *inode, unsigned int num_alloc,
 		struct exfat_chain *p_chain, bool sync_bmap);
@@ -456,6 +457,26 @@ int exfat_count_num_clusters(struct super_block *sb,
 int exfat_blk_readahead(struct super_block *sb, sector_t sec,
 		sector_t *ra, blkcnt_t *ra_cnt, sector_t end);
 
+static inline int
+exfat_fat_walk(struct super_block *sb, unsigned int *clu,
+		unsigned int step, int flags)
+{
+	struct buffer_head *bh = NULL;
+
+	if (flags == ALLOC_NO_FAT_CHAIN) {
+		(*clu) += step;
+		return 0;
+	}
+
+	while (step--) {
+		if (exfat_ent_get(sb, *clu, clu, &bh))
+			return -EIO;
+	}
+	brelse(bh);
+
+	return 0;
+}
+
 /* balloc.c */
 int exfat_load_bitmap(struct super_block *sb);
 void exfat_free_bitmap(struct exfat_sb_info *sbi);
-- 
2.43.0


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

* [PATCH v1 3/6] exfat: use exfat_fat_walk helper to simplify fat entry walking
  2026-03-31  9:11 [PATCH v1 0/6] exfat: unify FAT chain walking helpers Chi Zhiling
  2026-03-31  9:11 ` [PATCH v1 1/6] exfat: fix incorrect directory checksum after rename to shorter name Chi Zhiling
  2026-03-31  9:11 ` [PATCH v1 2/6] exfat: introduce exfat_fat_walk helper Chi Zhiling
@ 2026-03-31  9:11 ` Chi Zhiling
  2026-03-31  9:11 ` [PATCH v1 4/6] exfat: remove NULL cache pointer case in exfat_ent_get Chi Zhiling
                   ` (3 subsequent siblings)
  6 siblings, 0 replies; 9+ messages in thread
From: Chi Zhiling @ 2026-03-31  9:11 UTC (permalink / raw)
  To: Namjae Jeon, Sungjong Seo, Yuezhang Mo
  Cc: linux-fsdevel, linux-kernel, Chi Zhiling

From: Chi Zhiling <chizhiling@kylinos.cn>

Replace the custom exfat_walk_fat_chain() function and open-coded
FAT chain walking logic with the exfat_fat_walk() helper across
exfat_find_location, __exfat_get_dentry_set, and exfat_map_cluster.

Signed-off-by: Chi Zhiling <chizhiling@kylinos.cn>
---
 fs/exfat/dir.c   | 39 +++------------------------------------
 fs/exfat/inode.c | 11 ++---------
 2 files changed, 5 insertions(+), 45 deletions(-)

diff --git a/fs/exfat/dir.c b/fs/exfat/dir.c
index 7619410d668e..cfc6f16a5fb2 100644
--- a/fs/exfat/dir.c
+++ b/fs/exfat/dir.c
@@ -562,38 +562,6 @@ int exfat_put_dentry_set(struct exfat_entry_set_cache *es, int sync)
 	return err;
 }
 
-static int exfat_walk_fat_chain(struct super_block *sb,
-		struct exfat_chain *p_dir, unsigned int byte_offset,
-		unsigned int *clu)
-{
-	struct exfat_sb_info *sbi = EXFAT_SB(sb);
-	unsigned int clu_offset;
-	unsigned int cur_clu;
-
-	clu_offset = EXFAT_B_TO_CLU(byte_offset, sbi);
-	cur_clu = p_dir->dir;
-
-	if (p_dir->flags == ALLOC_NO_FAT_CHAIN) {
-		cur_clu += clu_offset;
-	} else {
-		while (clu_offset > 0) {
-			if (exfat_get_next_cluster(sb, &cur_clu))
-				return -EIO;
-			if (cur_clu == EXFAT_EOF_CLUSTER) {
-				exfat_fs_error(sb,
-					"invalid dentry access beyond EOF (clu : %u, eidx : %d)",
-					p_dir->dir,
-					EXFAT_B_TO_DEN(byte_offset));
-				return -EIO;
-			}
-			clu_offset--;
-		}
-	}
-
-	*clu = cur_clu;
-	return 0;
-}
-
 static int exfat_find_location(struct super_block *sb, struct exfat_chain *p_dir,
 			       int entry, sector_t *sector, int *offset)
 {
@@ -603,7 +571,8 @@ static int exfat_find_location(struct super_block *sb, struct exfat_chain *p_dir
 
 	off = EXFAT_DEN_TO_B(entry);
 
-	ret = exfat_walk_fat_chain(sb, p_dir, off, &clu);
+	clu = p_dir->dir;
+	ret = exfat_fat_walk(sb, &clu, EXFAT_B_TO_CLU(off, sbi), p_dir->flags);
 	if (ret)
 		return ret;
 
@@ -792,9 +761,7 @@ static int __exfat_get_dentry_set(struct exfat_entry_set_cache *es,
 		if (exfat_is_last_sector_in_cluster(sbi, sec)) {
 			unsigned int clu = exfat_sector_to_cluster(sbi, sec);
 
-			if (p_dir->flags == ALLOC_NO_FAT_CHAIN)
-				clu++;
-			else if (exfat_get_next_cluster(sb, &clu))
+			if (exfat_fat_walk(sb, &clu, 1, p_dir->flags))
 				goto put_es;
 			sec = exfat_cluster_to_sector(sbi, clu);
 		} else {
diff --git a/fs/exfat/inode.c b/fs/exfat/inode.c
index beb9ea7cca9f..817d9a135bb6 100644
--- a/fs/exfat/inode.c
+++ b/fs/exfat/inode.c
@@ -225,15 +225,8 @@ static int exfat_map_cluster(struct inode *inode, unsigned int clu_offset,
 		 * *clu = (the first cluster of the allocated chain) =>
 		 * (the last cluster of ...)
 		 */
-		if (ei->flags == ALLOC_NO_FAT_CHAIN) {
-			*clu += num_to_be_allocated - 1;
-		} else {
-			while (num_to_be_allocated > 1) {
-				if (exfat_get_next_cluster(sb, clu))
-					return -EIO;
-				num_to_be_allocated--;
-			}
-		}
+		if (exfat_fat_walk(sb, clu, num_to_be_allocated - 1, ei->flags))
+			return -EIO;
 		*count = 1;
 	}
 
-- 
2.43.0


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

* [PATCH v1 4/6] exfat: remove NULL cache pointer case in exfat_ent_get
  2026-03-31  9:11 [PATCH v1 0/6] exfat: unify FAT chain walking helpers Chi Zhiling
                   ` (2 preceding siblings ...)
  2026-03-31  9:11 ` [PATCH v1 3/6] exfat: use exfat_fat_walk helper to simplify fat entry walking Chi Zhiling
@ 2026-03-31  9:11 ` Chi Zhiling
  2026-03-31  9:11 ` [PATCH v1 5/6] exfat: introduce exfat_chain_advance helper Chi Zhiling
                   ` (2 subsequent siblings)
  6 siblings, 0 replies; 9+ messages in thread
From: Chi Zhiling @ 2026-03-31  9:11 UTC (permalink / raw)
  To: Namjae Jeon, Sungjong Seo, Yuezhang Mo
  Cc: linux-fsdevel, linux-kernel, Chi Zhiling

From: Chi Zhiling <chizhiling@kylinos.cn>

Since exfat_get_next_cluster has been updated, no callers pass a NULL
pointer to exfat_ent_get, so remove the handling logic for this case.

Signed-off-by: Chi Zhiling <chizhiling@kylinos.cn>
---
 fs/exfat/fatent.c | 23 +++++++++--------------
 1 file changed, 9 insertions(+), 14 deletions(-)

diff --git a/fs/exfat/fatent.c b/fs/exfat/fatent.c
index f2e5d5dde393..dce0955e689a 100644
--- a/fs/exfat/fatent.c
+++ b/fs/exfat/fatent.c
@@ -44,11 +44,11 @@ static int exfat_end_bh(struct super_block *sb, struct buffer_head *bh)
 }
 
 static int __exfat_ent_get(struct super_block *sb, unsigned int loc,
-		unsigned int *content, struct buffer_head **last)
+		unsigned int *content, struct buffer_head **cache)
 {
 	unsigned int off;
 	sector_t sec;
-	struct buffer_head *bh = last ? *last : NULL;
+	struct buffer_head *bh = *cache;
 
 	sec = FAT_ENT_OFFSET_SECTOR(sb, loc);
 	off = FAT_ENT_OFFSET_BYTE_IN_SECTOR(sb, loc);
@@ -56,8 +56,7 @@ static int __exfat_ent_get(struct super_block *sb, unsigned int loc,
 	if (!bh || bh->b_blocknr != sec || !buffer_uptodate(bh)) {
 		brelse(bh);
 		bh = sb_bread(sb, sec);
-		if (last)
-			*last = bh;
+		*cache = bh;
 		if (unlikely(!bh))
 			return -EIO;
 	}
@@ -68,8 +67,6 @@ static int __exfat_ent_get(struct super_block *sb, unsigned int loc,
 	if (*content > EXFAT_BAD_CLUSTER)
 		*content = EXFAT_EOF_CLUSTER;
 
-	if (!last)
-		brelse(bh);
 	return 0;
 }
 
@@ -111,7 +108,7 @@ int exfat_ent_set(struct super_block *sb, unsigned int loc,
  * Caller must release the buffer_head if no error return.
  */
 int exfat_ent_get(struct super_block *sb, unsigned int loc,
-		unsigned int *content, struct buffer_head **last)
+		unsigned int *content, struct buffer_head **cache)
 {
 	struct exfat_sb_info *sbi = EXFAT_SB(sb);
 
@@ -122,7 +119,7 @@ int exfat_ent_get(struct super_block *sb, unsigned int loc,
 		goto err;
 	}
 
-	if (unlikely(__exfat_ent_get(sb, loc, content, last))) {
+	if (unlikely(__exfat_ent_get(sb, loc, content, cache))) {
 		exfat_fs_error_ratelimit(sb,
 			"failed to access to FAT (entry 0x%08x)",
 			loc);
@@ -151,13 +148,11 @@ int exfat_ent_get(struct super_block *sb, unsigned int loc,
 	}
 
 	return 0;
-err:
-	if (last) {
-		brelse(*last);
 
-		/* Avoid double release */
-		*last = NULL;
-	}
+err:
+	/* Avoid double release */
+	brelse(*cache);
+	*cache = NULL;
 	return -EIO;
 }
 
-- 
2.43.0


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

* [PATCH v1 5/6] exfat: introduce exfat_chain_advance helper
  2026-03-31  9:11 [PATCH v1 0/6] exfat: unify FAT chain walking helpers Chi Zhiling
                   ` (3 preceding siblings ...)
  2026-03-31  9:11 ` [PATCH v1 4/6] exfat: remove NULL cache pointer case in exfat_ent_get Chi Zhiling
@ 2026-03-31  9:11 ` Chi Zhiling
  2026-03-31  9:11 ` [PATCH v1 6/6] exfat: use " Chi Zhiling
  2026-03-31 15:27 ` [syzbot ci] Re: exfat: unify FAT chain walking helpers syzbot ci
  6 siblings, 0 replies; 9+ messages in thread
From: Chi Zhiling @ 2026-03-31  9:11 UTC (permalink / raw)
  To: Namjae Jeon, Sungjong Seo, Yuezhang Mo
  Cc: linux-fsdevel, linux-kernel, Chi Zhiling

From: Chi Zhiling <chizhiling@kylinos.cn>

Introduce exfat_chain_advance() to walk a exfat_chain structure by a
given step, updating both ->dir and ->size fields atomically. This
helper handles both ALLOC_NO_FAT_CHAIN and ALLOC_FAT_CHAIN modes with
proper boundary checking.

Signed-off-by: Chi Zhiling <chizhiling@kylinos.cn>
---
 fs/exfat/exfat_fs.h | 26 ++++++++++++++++++++++++++
 1 file changed, 26 insertions(+)

diff --git a/fs/exfat/exfat_fs.h b/fs/exfat/exfat_fs.h
index 530459ab9acc..d7f88a326cf0 100644
--- a/fs/exfat/exfat_fs.h
+++ b/fs/exfat/exfat_fs.h
@@ -552,6 +552,32 @@ int exfat_read_volume_label(struct super_block *sb,
 int exfat_write_volume_label(struct super_block *sb,
 			     struct exfat_uni_name *label);
 
+static inline int exfat_chain_advance(struct super_block *sb,
+		struct exfat_chain *chain, unsigned int step)
+{
+	if (chain->flags == ALLOC_NO_FAT_CHAIN) {
+		if (chain->size > step) {
+			chain->dir += step;
+			chain->size -= step;
+		} else if (chain->size == step) {
+			chain->dir = EXFAT_EOF_CLUSTER;
+			chain->size = 0;
+		} else {
+			return -EIO;
+		}
+	} else {
+		if (exfat_fat_walk(sb, &chain->dir, step, ALLOC_FAT_CHAIN))
+			return -EIO;
+		if (chain->size > step) {
+			chain->size -= step;
+		} else {
+			chain->size = 0;
+			WARN_ON_ONCE(chain->dir != EXFAT_EOF_CLUSTER);
+		}
+	}
+	return 0;
+}
+
 /* inode.c */
 extern const struct inode_operations exfat_file_inode_operations;
 void exfat_sync_inode(struct inode *inode);
-- 
2.43.0


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

* [PATCH v1 6/6] exfat: use exfat_chain_advance helper
  2026-03-31  9:11 [PATCH v1 0/6] exfat: unify FAT chain walking helpers Chi Zhiling
                   ` (4 preceding siblings ...)
  2026-03-31  9:11 ` [PATCH v1 5/6] exfat: introduce exfat_chain_advance helper Chi Zhiling
@ 2026-03-31  9:11 ` Chi Zhiling
  2026-03-31 15:27 ` [syzbot ci] Re: exfat: unify FAT chain walking helpers syzbot ci
  6 siblings, 0 replies; 9+ messages in thread
From: Chi Zhiling @ 2026-03-31  9:11 UTC (permalink / raw)
  To: Namjae Jeon, Sungjong Seo, Yuezhang Mo
  Cc: linux-fsdevel, linux-kernel, Chi Zhiling

From: Chi Zhiling <chizhiling@kylinos.cn>

Replace open-coded cluster chain walking logic with exfat_chain_advance()
across exfat_readdir, exfat_find_dir_entry, exfat_count_dir_entries,
exfat_search_empty_slot and exfat_check_dir_empty.

Signed-off-by: Chi Zhiling <chizhiling@kylinos.cn>
---
 fs/exfat/dir.c   | 71 ++++++++++++------------------------------------
 fs/exfat/namei.c | 29 +++++---------------
 2 files changed, 25 insertions(+), 75 deletions(-)

diff --git a/fs/exfat/dir.c b/fs/exfat/dir.c
index cfc6f16a5fb2..adc79694373a 100644
--- a/fs/exfat/dir.c
+++ b/fs/exfat/dir.c
@@ -93,25 +93,19 @@ static int exfat_readdir(struct inode *inode, loff_t *cpos, struct exfat_dir_ent
 	clu_offset = EXFAT_DEN_TO_CLU(dentry, sbi);
 	exfat_chain_dup(&clu, &dir);
 
-	if (clu.flags == ALLOC_NO_FAT_CHAIN) {
-		clu.dir += clu_offset;
-		clu.size -= clu_offset;
-	} else {
+	if (clu.flags == ALLOC_FAT_CHAIN) {
 		/* hint_information */
 		if (clu_offset > 0 && ei->hint_bmap.off != EXFAT_EOF_CLUSTER &&
 		    ei->hint_bmap.off > 0 && clu_offset >= ei->hint_bmap.off) {
 			clu_offset -= ei->hint_bmap.off;
 			clu.dir = ei->hint_bmap.clu;
-		}
-
-		while (clu_offset > 0 && clu.dir != EXFAT_EOF_CLUSTER) {
-			if (exfat_get_next_cluster(sb, &(clu.dir)))
-				return -EIO;
-
-			clu_offset--;
+			clu.size -= ei->hint_bmap.off;
 		}
 	}
 
+	if (exfat_chain_advance(sb, &clu, clu_offset))
+		return -EIO;
+
 	while (clu.dir != EXFAT_EOF_CLUSTER && dentry < max_dentries) {
 		i = dentry & (dentries_per_clu - 1);
 
@@ -160,15 +154,8 @@ static int exfat_readdir(struct inode *inode, loff_t *cpos, struct exfat_dir_ent
 			return 0;
 		}
 
-		if (clu.flags == ALLOC_NO_FAT_CHAIN) {
-			if (--clu.size > 0)
-				clu.dir++;
-			else
-				clu.dir = EXFAT_EOF_CLUSTER;
-		} else {
-			if (exfat_get_next_cluster(sb, &(clu.dir)))
-				return -EIO;
-		}
+		if (exfat_chain_advance(sb, &clu, 1))
+			return -EIO;
 	}
 
 out:
@@ -1077,19 +1064,12 @@ int exfat_find_dir_entry(struct super_block *sb, struct exfat_inode_info *ei,
 			step = DIRENT_STEP_FILE;
 		}
 
-		if (clu.flags == ALLOC_NO_FAT_CHAIN) {
-			if (--clu.size > 0)
-				clu.dir++;
-			else
-				clu.dir = EXFAT_EOF_CLUSTER;
-		} else {
-			if (exfat_get_next_cluster(sb, &clu.dir))
-				return -EIO;
+		if (exfat_chain_advance(sb, &clu, 1))
+			return -EIO;
+		/* break if the cluster chain includes a loop */
+		if (unlikely(++clu_count > EXFAT_DATA_CLUSTER_COUNT(sbi)))
+			goto not_found;
 
-			/* break if the cluster chain includes a loop */
-			if (unlikely(++clu_count > EXFAT_DATA_CLUSTER_COUNT(sbi)))
-				goto not_found;
-		}
 	}
 
 not_found:
@@ -1124,14 +1104,7 @@ int exfat_find_dir_entry(struct super_block *sb, struct exfat_inode_info *ei,
 	if (!((dentry + 1) & (dentries_per_clu - 1))) {
 		int ret = 0;
 
-		if (clu.flags == ALLOC_NO_FAT_CHAIN) {
-			if (--clu.size > 0)
-				clu.dir++;
-			else
-				clu.dir = EXFAT_EOF_CLUSTER;
-		} else {
-			ret = exfat_get_next_cluster(sb, &clu.dir);
-		}
+		ret = exfat_chain_advance(sb, &clu, 1);
 
 		if (ret || clu.dir == EXFAT_EOF_CLUSTER) {
 			/* just initialized hint_stat */
@@ -1176,20 +1149,12 @@ int exfat_count_dir_entries(struct super_block *sb, struct exfat_chain *p_dir)
 			count++;
 		}
 
-		if (clu.flags == ALLOC_NO_FAT_CHAIN) {
-			if (--clu.size > 0)
-				clu.dir++;
-			else
-				clu.dir = EXFAT_EOF_CLUSTER;
-		} else {
-			if (exfat_get_next_cluster(sb, &(clu.dir)))
-				return -EIO;
-
-			if (unlikely(++clu_count > sbi->used_clusters)) {
-				exfat_fs_error(sb, "FAT or bitmap is corrupted");
-				return -EIO;
-			}
+		if (exfat_chain_advance(sb, &clu, 1))
+			return -EIO;
 
+		if (unlikely(++clu_count > sbi->used_clusters)) {
+			exfat_fs_error(sb, "FAT or bitmap is corrupted");
+			return -EIO;
 		}
 	}
 
diff --git a/fs/exfat/namei.c b/fs/exfat/namei.c
index ef2a3488c1b3..e0d1c3bf5555 100644
--- a/fs/exfat/namei.c
+++ b/fs/exfat/namei.c
@@ -246,16 +246,8 @@ static int exfat_search_empty_slot(struct super_block *sb,
 		i += ret;
 
 		while (i >= dentries_per_clu) {
-			if (clu.flags == ALLOC_NO_FAT_CHAIN) {
-				if (--clu.size > 0)
-					clu.dir++;
-				else
-					clu.dir = EXFAT_EOF_CLUSTER;
-			} else {
-				if (exfat_get_next_cluster(sb, &clu.dir))
-					return -EIO;
-			}
-
+			if (exfat_chain_advance(sb, &clu, 1))
+				return -EIO;
 			i -= dentries_per_clu;
 		}
 	}
@@ -924,19 +916,12 @@ static int exfat_check_dir_empty(struct super_block *sb,
 			return -ENOTEMPTY;
 		}
 
-		if (clu.flags == ALLOC_NO_FAT_CHAIN) {
-			if (--clu.size > 0)
-				clu.dir++;
-			else
-				clu.dir = EXFAT_EOF_CLUSTER;
-		} else {
-			if (exfat_get_next_cluster(sb, &(clu.dir)))
-				return -EIO;
+		if (exfat_chain_advance(sb, &clu, 1))
+			return -EIO;
 
-			/* break if the cluster chain includes a loop */
-			if (unlikely(++clu_count > EXFAT_DATA_CLUSTER_COUNT(sbi)))
-				break;
-		}
+		/* break if the cluster chain includes a loop */
+		if (unlikely(++clu_count > EXFAT_DATA_CLUSTER_COUNT(sbi)))
+			break;
 	}
 
 	return 0;
-- 
2.43.0


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

* [syzbot ci] Re: exfat: unify FAT chain walking helpers
  2026-03-31  9:11 [PATCH v1 0/6] exfat: unify FAT chain walking helpers Chi Zhiling
                   ` (5 preceding siblings ...)
  2026-03-31  9:11 ` [PATCH v1 6/6] exfat: use " Chi Zhiling
@ 2026-03-31 15:27 ` syzbot ci
  2026-04-01  2:45   ` Chi Zhiling
  6 siblings, 1 reply; 9+ messages in thread
From: syzbot ci @ 2026-03-31 15:27 UTC (permalink / raw)
  To: chizhiling, chizhiling, linkinjeon, linux-fsdevel, linux-kernel,
	sj1557.seo, yuezhang.mo
  Cc: syzbot, syzkaller-bugs

syzbot ci has tested the following series

[v1] exfat: unify FAT chain walking helpers
https://lore.kernel.org/all/20260331091113.20882-1-chizhiling@163.com
* [PATCH v1 1/6] exfat: fix incorrect directory checksum after rename to shorter name
* [PATCH v1 2/6] exfat: introduce exfat_fat_walk helper
* [PATCH v1 3/6] exfat: use exfat_fat_walk helper to simplify fat entry walking
* [PATCH v1 4/6] exfat: remove NULL cache pointer case in exfat_ent_get
* [PATCH v1 5/6] exfat: introduce exfat_chain_advance helper
* [PATCH v1 6/6] exfat: use exfat_chain_advance helper

and found the following issue:
WARNING in exfat_find_empty_entry

Full report is available here:
https://ci.syzbot.org/series/d9d5a999-e11a-43e1-ac76-9c718e136f25

***

WARNING in exfat_find_empty_entry

tree:      linux-next
URL:       https://kernel.googlesource.com/pub/scm/linux/kernel/git/next/linux-next
base:      d1d75eaf01abceb3d5cb50253375b5f254b6be54
arch:      amd64
compiler:  Debian clang version 21.1.8 (++20251221033036+2078da43e25a-1~exp1~20251221153213.50), Debian LLD 21.1.8
config:    https://ci.syzbot.org/builds/177ab8e9-7eef-44cc-8b1d-6e8e2b44adaf/config
C repro:   https://ci.syzbot.org/findings/9b0e35b9-b5be-440e-9461-2f19c03cb16a/c_repro
syz repro: https://ci.syzbot.org/findings/9b0e35b9-b5be-440e-9461-2f19c03cb16a/syz_repro

exFAT-fs (loop0): start_clu is invalid cluster(0xffffffff)
exFAT-fs (loop0): valid_size(150994954) is greater than size(10)
------------[ cut here ]------------
chain->dir != EXFAT_EOF_CLUSTER
WARNING: fs/exfat/exfat_fs.h:575 at exfat_chain_advance fs/exfat/exfat_fs.h:575 [inline], CPU#0: syz.0.17/5981
WARNING: fs/exfat/exfat_fs.h:575 at exfat_search_empty_slot fs/exfat/namei.c:249 [inline], CPU#0: syz.0.17/5981
WARNING: fs/exfat/exfat_fs.h:575 at exfat_find_empty_entry+0x754/0x13b0 fs/exfat/namei.c:318, CPU#0: syz.0.17/5981
Modules linked in:
CPU: 0 UID: 0 PID: 5981 Comm: syz.0.17 Not tainted syzkaller #0 PREEMPT(full) 
Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 1.16.2-debian-1.16.2-1 04/01/2014
RIP: 0010:exfat_chain_advance fs/exfat/exfat_fs.h:575 [inline]
RIP: 0010:exfat_search_empty_slot fs/exfat/namei.c:249 [inline]
RIP: 0010:exfat_find_empty_entry+0x754/0x13b0 fs/exfat/namei.c:318
Code: 00 bf ff ff ff ff 44 89 e6 e8 18 56 1f ff 41 83 fc ff 75 10 e8 cd 51 1f ff 45 29 ee 0f 8d ad fe ff ff eb 12 e8 bd 51 1f ff 90 <0f> 0b 90 45 29 ee 0f 8d 99 fe ff ff e8 ab 51 1f ff 41 01 df 48 8b
RSP: 0018:ffffc9000499f340 EFLAGS: 00010293
RAX: ffffffff82a639c3 RBX: 0000000000000020 RCX: ffff88816d588000
RDX: 0000000000000000 RSI: 000000000000000b RDI: 00000000ffffffff
RBP: ffffc9000499f570 R08: ffff888119e14a5b R09: 1ffff110233c294b
R10: dffffc0000000000 R11: ffffed10233c294c R12: 000000000000000b
R13: 0000000000000080 R14: 0000000000000000 R15: 0000000000000060
FS:  000055557e214500(0000) GS:ffff88818de66000(0000) knlGS:0000000000000000
CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
CR2: 0000200000006200 CR3: 00000001bcf7c000 CR4: 00000000000006f0
Call Trace:
 <TASK>
 exfat_rename_file fs/exfat/namei.c:1025 [inline]
 __exfat_rename fs/exfat/namei.c:1177 [inline]
 exfat_rename+0xb49/0x1f90 fs/exfat/namei.c:1251
 vfs_rename+0xa96/0xeb0 fs/namei.c:6026
 filename_renameat2+0x539/0x9c0 fs/namei.c:6144
 __do_sys_rename fs/namei.c:6188 [inline]
 __se_sys_rename+0x55/0x2c0 fs/namei.c:6184
 do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline]
 do_syscall_64+0x14d/0xf80 arch/x86/entry/syscall_64.c:94
 entry_SYSCALL_64_after_hwframe+0x77/0x7f
RIP: 0033:0x7f66f1b9c819
Code: ff c3 66 2e 0f 1f 84 00 00 00 00 00 0f 1f 44 00 00 48 89 f8 48 89 f7 48 89 d6 48 89 ca 4d 89 c2 4d 89 c8 4c 8b 4c 24 08 0f 05 <48> 3d 01 f0 ff ff 73 01 c3 48 c7 c1 e8 ff ff ff f7 d8 64 89 01 48
RSP: 002b:00007ffcad04e2e8 EFLAGS: 00000246 ORIG_RAX: 0000000000000052
RAX: ffffffffffffffda RBX: 00007f66f1e15fa0 RCX: 00007f66f1b9c819
RDX: 0000000000000000 RSI: 0000200000000400 RDI: 0000200000006200
RBP: 00007f66f1c32c91 R08: 0000000000000000 R09: 0000000000000000
R10: 0000000000000000 R11: 0000000000000246 R12: 0000000000000000
R13: 00007f66f1e15fac R14: 00007f66f1e15fa0 R15: 00007f66f1e15fa0
 </TASK>


***

If these findings have caused you to resend the series or submit a
separate fix, please add the following tag to your commit message:
  Tested-by: syzbot@syzkaller.appspotmail.com

---
This report is generated by a bot. It may contain errors.
syzbot ci engineers can be reached at syzkaller@googlegroups.com.

To test a patch for this bug, please reply with `#syz test`
(should be on a separate line).

The patch should be attached to the email.
Note: arguments like custom git repos and branches are not supported.

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

* Re: [syzbot ci] Re: exfat: unify FAT chain walking helpers
  2026-03-31 15:27 ` [syzbot ci] Re: exfat: unify FAT chain walking helpers syzbot ci
@ 2026-04-01  2:45   ` Chi Zhiling
  0 siblings, 0 replies; 9+ messages in thread
From: Chi Zhiling @ 2026-04-01  2:45 UTC (permalink / raw)
  To: syzbot ci, chizhiling, linkinjeon, linux-fsdevel, linux-kernel,
	sj1557.seo, yuezhang.mo
  Cc: syzbot, syzkaller-bugs

On 3/31/26 11:27 PM, syzbot ci wrote:
> syzbot ci has tested the following series
> 
> [v1] exfat: unify FAT chain walking helpers
> https://lore.kernel.org/all/20260331091113.20882-1-chizhiling@163.com
> * [PATCH v1 1/6] exfat: fix incorrect directory checksum after rename to shorter name
> * [PATCH v1 2/6] exfat: introduce exfat_fat_walk helper
> * [PATCH v1 3/6] exfat: use exfat_fat_walk helper to simplify fat entry walking
> * [PATCH v1 4/6] exfat: remove NULL cache pointer case in exfat_ent_get
> * [PATCH v1 5/6] exfat: introduce exfat_chain_advance helper
> * [PATCH v1 6/6] exfat: use exfat_chain_advance helper
> 
> and found the following issue:
> WARNING in exfat_find_empty_entry
> 
> Full report is available here:
> https://ci.syzbot.org/series/d9d5a999-e11a-43e1-ac76-9c718e136f25
> 
> ***
> 
> WARNING in exfat_find_empty_entry
> 
> tree:      linux-next
> URL:       https://kernel.googlesource.com/pub/scm/linux/kernel/git/next/linux-next
> base:      d1d75eaf01abceb3d5cb50253375b5f254b6be54
> arch:      amd64
> compiler:  Debian clang version 21.1.8 (++20251221033036+2078da43e25a-1~exp1~20251221153213.50), Debian LLD 21.1.8
> config:    https://ci.syzbot.org/builds/177ab8e9-7eef-44cc-8b1d-6e8e2b44adaf/config
> C repro:   https://ci.syzbot.org/findings/9b0e35b9-b5be-440e-9461-2f19c03cb16a/c_repro
> syz repro: https://ci.syzbot.org/findings/9b0e35b9-b5be-440e-9461-2f19c03cb16a/syz_repro
> 
> exFAT-fs (loop0): start_clu is invalid cluster(0xffffffff)
> exFAT-fs (loop0): valid_size(150994954) is greater than size(10)
> ------------[ cut here ]------------
> chain->dir != EXFAT_EOF_CLUSTER
> WARNING: fs/exfat/exfat_fs.h:575 at exfat_chain_advance fs/exfat/exfat_fs.h:575 [inline], CPU#0: syz.0.17/5981
> WARNING: fs/exfat/exfat_fs.h:575 at exfat_search_empty_slot fs/exfat/namei.c:249 [inline], CPU#0: syz.0.17/5981
> WARNING: fs/exfat/exfat_fs.h:575 at exfat_find_empty_entry+0x754/0x13b0 fs/exfat/namei.c:318, CPU#0: syz.0.17/5981
> Modules linked in:
> CPU: 0 UID: 0 PID: 5981 Comm: syz.0.17 Not tainted syzkaller #0 PREEMPT(full)
> Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 1.16.2-debian-1.16.2-1 04/01/2014
> RIP: 0010:exfat_chain_advance fs/exfat/exfat_fs.h:575 [inline]
> RIP: 0010:exfat_search_empty_slot fs/exfat/namei.c:249 [inline]
> RIP: 0010:exfat_find_empty_entry+0x754/0x13b0 fs/exfat/namei.c:318
> Code: 00 bf ff ff ff ff 44 89 e6 e8 18 56 1f ff 41 83 fc ff 75 10 e8 cd 51 1f ff 45 29 ee 0f 8d ad fe ff ff eb 12 e8 bd 51 1f ff 90 <0f> 0b 90 45 29 ee 0f 8d 99 fe ff ff e8 ab 51 1f ff 41 01 df 48 8b
> RSP: 0018:ffffc9000499f340 EFLAGS: 00010293
> RAX: ffffffff82a639c3 RBX: 0000000000000020 RCX: ffff88816d588000
> RDX: 0000000000000000 RSI: 000000000000000b RDI: 00000000ffffffff
> RBP: ffffc9000499f570 R08: ffff888119e14a5b R09: 1ffff110233c294b
> R10: dffffc0000000000 R11: ffffed10233c294c R12: 000000000000000b
> R13: 0000000000000080 R14: 0000000000000000 R15: 0000000000000060
> FS:  000055557e214500(0000) GS:ffff88818de66000(0000) knlGS:0000000000000000
> CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
> CR2: 0000200000006200 CR3: 00000001bcf7c000 CR4: 00000000000006f0
> Call Trace:
>   <TASK>
>   exfat_rename_file fs/exfat/namei.c:1025 [inline]
>   __exfat_rename fs/exfat/namei.c:1177 [inline]
>   exfat_rename+0xb49/0x1f90 fs/exfat/namei.c:1251
>   vfs_rename+0xa96/0xeb0 fs/namei.c:6026
>   filename_renameat2+0x539/0x9c0 fs/namei.c:6144
>   __do_sys_rename fs/namei.c:6188 [inline]
>   __se_sys_rename+0x55/0x2c0 fs/namei.c:6184
>   do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline]
>   do_syscall_64+0x14d/0xf80 arch/x86/entry/syscall_64.c:94
>   entry_SYSCALL_64_after_hwframe+0x77/0x7f
> RIP: 0033:0x7f66f1b9c819
> Code: ff c3 66 2e 0f 1f 84 00 00 00 00 00 0f 1f 44 00 00 48 89 f8 48 89 f7 48 89 d6 48 89 ca 4d 89 c2 4d 89 c8 4c 8b 4c 24 08 0f 05 <48> 3d 01 f0 ff ff 73 01 c3 48 c7 c1 e8 ff ff ff f7 d8 64 89 01 48
> RSP: 002b:00007ffcad04e2e8 EFLAGS: 00000246 ORIG_RAX: 0000000000000052
> RAX: ffffffffffffffda RBX: 00007f66f1e15fa0 RCX: 00007f66f1b9c819
> RDX: 0000000000000000 RSI: 0000200000000400 RDI: 0000200000006200
> RBP: 00007f66f1c32c91 R08: 0000000000000000 R09: 0000000000000000
> R10: 0000000000000000 R11: 0000000000000246 R12: 0000000000000000
> R13: 00007f66f1e15fac R14: 00007f66f1e15fa0 R15: 00007f66f1e15fa0
>   </TASK>

It looks like I should remove this WARN_ON_ONCE statement. This check is 
not reasonable when accessing directories, and I will fix it in v2.

Thanks,

> 
> 
> ***
> 
> If these findings have caused you to resend the series or submit a
> separate fix, please add the following tag to your commit message:
>    Tested-by: syzbot@syzkaller.appspotmail.com
> 
> ---
> This report is generated by a bot. It may contain errors.
> syzbot ci engineers can be reached at syzkaller@googlegroups.com.
> 
> To test a patch for this bug, please reply with `#syz test`
> (should be on a separate line).
> 
> The patch should be attached to the email.
> Note: arguments like custom git repos and branches are not supported.


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

end of thread, other threads:[~2026-04-01  2:46 UTC | newest]

Thread overview: 9+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-03-31  9:11 [PATCH v1 0/6] exfat: unify FAT chain walking helpers Chi Zhiling
2026-03-31  9:11 ` [PATCH v1 1/6] exfat: fix incorrect directory checksum after rename to shorter name Chi Zhiling
2026-03-31  9:11 ` [PATCH v1 2/6] exfat: introduce exfat_fat_walk helper Chi Zhiling
2026-03-31  9:11 ` [PATCH v1 3/6] exfat: use exfat_fat_walk helper to simplify fat entry walking Chi Zhiling
2026-03-31  9:11 ` [PATCH v1 4/6] exfat: remove NULL cache pointer case in exfat_ent_get Chi Zhiling
2026-03-31  9:11 ` [PATCH v1 5/6] exfat: introduce exfat_chain_advance helper Chi Zhiling
2026-03-31  9:11 ` [PATCH v1 6/6] exfat: use " Chi Zhiling
2026-03-31 15:27 ` [syzbot ci] Re: exfat: unify FAT chain walking helpers syzbot ci
2026-04-01  2:45   ` Chi Zhiling

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox