Blame SOURCES/0077-exfat-handle-wrong-stream-entry-size-in-exfat_readdi.patch

Kmods SIG 9e3ffb
From 1e5654de0f51890f88abd409ebf4867782431e81 Mon Sep 17 00:00:00 2001
Kmods SIG 9e3ffb
From: Namjae Jeon <namjae.jeon@samsung.com>
Kmods SIG 9e3ffb
Date: Fri, 11 Jun 2021 09:40:24 +0900
Kmods SIG 9e3ffb
Subject: [Backport 1e5654de0f51] exfat: handle wrong stream entry size in
Kmods SIG 9e3ffb
 exfat_readdir()
Kmods SIG 9e3ffb
Kmods SIG 9e3ffb
The compatibility issue between linux exfat and exfat of some camera
Kmods SIG 9e3ffb
company was reported from Florian. In their exfat, if the number of files
Kmods SIG 9e3ffb
exceeds any limit, the DataLength in stream entry of the directory is
Kmods SIG 9e3ffb
no longer updated. So some files created from camera does not show in
Kmods SIG 9e3ffb
linux exfat. because linux exfat doesn't allow that cpos becomes larger
Kmods SIG 9e3ffb
than DataLength of stream entry. This patch check DataLength in stream
Kmods SIG 9e3ffb
entry only if the type is ALLOC_NO_FAT_CHAIN and add the check ensure
Kmods SIG 9e3ffb
that dentry offset does not exceed max dentries size(256 MB) to avoid
Kmods SIG 9e3ffb
the circular FAT chain issue.
Kmods SIG 9e3ffb
Kmods SIG 9e3ffb
Fixes: ca06197382bd ("exfat: add directory operations")
Kmods SIG 9e3ffb
Cc: stable@vger.kernel.org # v5.9
Kmods SIG 9e3ffb
Reported-by: Florian Cramer <flrncrmr@gmail.com>
Kmods SIG 9e3ffb
Reviewed-by: Sungjong Seo <sj1557.seo@samsung.com>
Kmods SIG 9e3ffb
Tested-by: Chris Down <chris@chrisdown.name>
Kmods SIG 9e3ffb
Signed-off-by: Namjae Jeon <namjae.jeon@samsung.com>
Kmods SIG 9e3ffb
---
Kmods SIG 9e3ffb
 src/dir.c | 8 +++++---
Kmods SIG 9e3ffb
 1 file changed, 5 insertions(+), 3 deletions(-)
Kmods SIG 9e3ffb
Kmods SIG 9e3ffb
diff --git a/src/dir.c b/src/dir.c
Kmods SIG 9e3ffb
index c4523648472a088a2477ea9c3ffe7416c498b523..cb1c0d8c17141b1a0b1fdb70167cce3ec3c5446b 100644
Kmods SIG 9e3ffb
--- a/src/dir.c
Kmods SIG 9e3ffb
+++ b/src/dir.c
Kmods SIG 9e3ffb
@@ -63,7 +63,7 @@ static void exfat_get_uniname_from_ext_entry(struct super_block *sb,
Kmods SIG 9e3ffb
 static int exfat_readdir(struct inode *inode, loff_t *cpos, struct exfat_dir_entry *dir_entry)
Kmods SIG 9e3ffb
 {
Kmods SIG 9e3ffb
 	int i, dentries_per_clu, dentries_per_clu_bits = 0, num_ext;
Kmods SIG 9e3ffb
-	unsigned int type, clu_offset;
Kmods SIG 9e3ffb
+	unsigned int type, clu_offset, max_dentries;
Kmods SIG 9e3ffb
 	sector_t sector;
Kmods SIG 9e3ffb
 	struct exfat_chain dir, clu;
Kmods SIG 9e3ffb
 	struct exfat_uni_name uni_name;
Kmods SIG 9e3ffb
@@ -86,6 +86,8 @@ static int exfat_readdir(struct inode *inode, loff_t *cpos, struct exfat_dir_ent
Kmods SIG 9e3ffb
 
Kmods SIG 9e3ffb
 	dentries_per_clu = sbi->dentries_per_clu;
Kmods SIG 9e3ffb
 	dentries_per_clu_bits = ilog2(dentries_per_clu);
Kmods SIG 9e3ffb
+	max_dentries = (unsigned int)min_t(u64, MAX_EXFAT_DENTRIES,
Kmods SIG 9e3ffb
+					   (u64)sbi->num_clusters << dentries_per_clu_bits);
Kmods SIG 9e3ffb
 
Kmods SIG 9e3ffb
 	clu_offset = dentry >> dentries_per_clu_bits;
Kmods SIG 9e3ffb
 	exfat_chain_dup(&clu, &dir;;
Kmods SIG 9e3ffb
@@ -109,7 +111,7 @@ static int exfat_readdir(struct inode *inode, loff_t *cpos, struct exfat_dir_ent
Kmods SIG 9e3ffb
 		}
Kmods SIG 9e3ffb
 	}
Kmods SIG 9e3ffb
 
Kmods SIG 9e3ffb
-	while (clu.dir != EXFAT_EOF_CLUSTER) {
Kmods SIG 9e3ffb
+	while (clu.dir != EXFAT_EOF_CLUSTER && dentry < max_dentries) {
Kmods SIG 9e3ffb
 		i = dentry & (dentries_per_clu - 1);
Kmods SIG 9e3ffb
 
Kmods SIG 9e3ffb
 		for ( ; i < dentries_per_clu; i++, dentry++) {
Kmods SIG 9e3ffb
@@ -245,7 +247,7 @@ static int exfat_iterate(struct file *filp, struct dir_context *ctx)
Kmods SIG 9e3ffb
 	if (err)
Kmods SIG 9e3ffb
 		goto unlock;
Kmods SIG 9e3ffb
 get_new:
Kmods SIG 9e3ffb
-	if (cpos >= i_size_read(inode))
Kmods SIG 9e3ffb
+	if (ei->flags == ALLOC_NO_FAT_CHAIN && cpos >= i_size_read(inode))
Kmods SIG 9e3ffb
 		goto end_of_dir;
Kmods SIG 9e3ffb
 
Kmods SIG 9e3ffb
 	err = exfat_readdir(inode, &cpos, &de);
Kmods SIG 9e3ffb
-- 
Kmods SIG 9e3ffb
2.31.1
Kmods SIG 9e3ffb