exfat: support dynamic allocate bh for exfat_entry_set_cache
authorYuezhang Mo <Yuezhang.Mo@sony.com>
Wed, 9 Nov 2022 05:50:22 +0000 (13:50 +0800)
committerNamjae Jeon <linkinjeon@kernel.org>
Mon, 12 Dec 2022 02:02:49 +0000 (11:02 +0900)
In special cases, a file or a directory may occupied more than 19
directory entries, pre-allocating 3 bh is not enough. Such as
  - Support vendor secondary directory entry in the future.
  - Since file directory entry is damaged, the SecondaryCount
    field is bigger than 18.

So this commit supports dynamic allocation of bh.

Signed-off-by: Yuezhang Mo <Yuezhang.Mo@sony.com>
Reviewed-by: Andy Wu <Andy.Wu@sony.com>
Reviewed-by: Aoyama Wataru <wataru.aoyama@sony.com>
Reviewed-by: Sungjong Seo <sj1557.seo@samsung.com>
Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
fs/exfat/dir.c
fs/exfat/exfat_fs.h

index 30d0ac4..03e9c9e 100644 (file)
@@ -615,6 +615,10 @@ int exfat_free_dentry_set(struct exfat_entry_set_cache *es, int sync)
                        bforget(es->bh[i]);
                else
                        brelse(es->bh[i]);
+
+       if (IS_DYNAMIC_ES(es))
+               kfree(es->bh);
+
        kfree(es);
        return err;
 }
@@ -847,6 +851,7 @@ struct exfat_entry_set_cache *exfat_get_dentry_set(struct super_block *sb,
        /* byte offset in sector */
        off = EXFAT_BLK_OFFSET(byte_offset, sb);
        es->start_off = off;
+       es->bh = es->__bh;
 
        /* sector offset in cluster */
        sec = EXFAT_B_TO_BLK(byte_offset, sb);
@@ -866,6 +871,16 @@ struct exfat_entry_set_cache *exfat_get_dentry_set(struct super_block *sb,
        es->num_entries = num_entries;
 
        num_bh = EXFAT_B_TO_BLK_ROUND_UP(off + num_entries * DENTRY_SIZE, sb);
+       if (num_bh > ARRAY_SIZE(es->__bh)) {
+               es->bh = kmalloc_array(num_bh, sizeof(*es->bh), GFP_KERNEL);
+               if (!es->bh) {
+                       brelse(bh);
+                       kfree(es);
+                       return NULL;
+               }
+               es->bh[0] = bh;
+       }
+
        for (i = 1; i < num_bh; i++) {
                /* get the next sector */
                if (exfat_is_last_sector_in_cluster(sbi, sec)) {
index af55018..82395ae 100644 (file)
@@ -185,11 +185,14 @@ struct exfat_entry_set_cache {
        struct super_block *sb;
        unsigned int start_off;
        int num_bh;
-       struct buffer_head *bh[DIR_CACHE_SIZE];
+       struct buffer_head *__bh[DIR_CACHE_SIZE];
+       struct buffer_head **bh;
        unsigned int num_entries;
        bool modified;
 };
 
+#define IS_DYNAMIC_ES(es)      ((es)->__bh != (es)->bh)
+
 struct exfat_dir_entry {
        struct exfat_chain dir;
        int entry;