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