diff options
Diffstat (limited to 'fs/exfat')
| -rw-r--r-- | fs/exfat/Kconfig | 2 | ||||
| -rw-r--r-- | fs/exfat/balloc.c | 230 | ||||
| -rw-r--r-- | fs/exfat/cache.c | 4 | ||||
| -rw-r--r-- | fs/exfat/dir.c | 630 | ||||
| -rw-r--r-- | fs/exfat/exfat_fs.h | 99 | ||||
| -rw-r--r-- | fs/exfat/exfat_raw.h | 44 | ||||
| -rw-r--r-- | fs/exfat/fatent.c | 109 | ||||
| -rw-r--r-- | fs/exfat/file.c | 486 | ||||
| -rw-r--r-- | fs/exfat/inode.c | 303 | ||||
| -rw-r--r-- | fs/exfat/misc.c | 8 | ||||
| -rw-r--r-- | fs/exfat/namei.c | 663 | ||||
| -rw-r--r-- | fs/exfat/nls.c | 27 | ||||
| -rw-r--r-- | fs/exfat/super.c | 280 |
13 files changed, 1872 insertions, 1013 deletions
diff --git a/fs/exfat/Kconfig b/fs/exfat/Kconfig index 5a65071b5ecf..cbeca8e44d9b 100644 --- a/fs/exfat/Kconfig +++ b/fs/exfat/Kconfig @@ -2,7 +2,9 @@ config EXFAT_FS tristate "exFAT filesystem support" + select BUFFER_HEAD select NLS + select LEGACY_DIRECT_IO help This allows you to mount devices formatted with the exFAT file system. exFAT is typically used on SD-Cards or USB sticks. diff --git a/fs/exfat/balloc.c b/fs/exfat/balloc.c index 9f42f25fab92..5429041c7eaf 100644 --- a/fs/exfat/balloc.c +++ b/fs/exfat/balloc.c @@ -5,53 +5,80 @@ #include <linux/blkdev.h> #include <linux/slab.h> +#include <linux/bitmap.h> #include <linux/buffer_head.h> +#include <linux/backing-dev.h> #include "exfat_raw.h" #include "exfat_fs.h" -static const unsigned char free_bit[] = { - 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2,/* 0 ~ 19*/ - 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 5, 0, 1, 0, 2, 0, 1, 0, 3,/* 20 ~ 39*/ - 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2,/* 40 ~ 59*/ - 0, 1, 0, 6, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4,/* 60 ~ 79*/ - 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 5, 0, 1, 0, 2,/* 80 ~ 99*/ - 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0, 3,/*100 ~ 119*/ - 0, 1, 0, 2, 0, 1, 0, 7, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2,/*120 ~ 139*/ - 0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 5,/*140 ~ 159*/ - 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2,/*160 ~ 179*/ - 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 6, 0, 1, 0, 2, 0, 1, 0, 3,/*180 ~ 199*/ - 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2,/*200 ~ 219*/ - 0, 1, 0, 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4,/*220 ~ 239*/ - 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0 /*240 ~ 254*/ -}; - -static const unsigned char used_bit[] = { - 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4, 1, 2, 2, 3,/* 0 ~ 19*/ - 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 1, 2, 2, 3, 2, 3, 3, 4,/* 20 ~ 39*/ - 2, 3, 3, 4, 3, 4, 4, 5, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5,/* 40 ~ 59*/ - 4, 5, 5, 6, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,/* 60 ~ 79*/ - 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 2, 3, 3, 4,/* 80 ~ 99*/ - 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6,/*100 ~ 119*/ - 4, 5, 5, 6, 5, 6, 6, 7, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4,/*120 ~ 139*/ - 3, 4, 4, 5, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,/*140 ~ 159*/ - 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5,/*160 ~ 179*/ - 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 2, 3, 3, 4, 3, 4, 4, 5,/*180 ~ 199*/ - 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6,/*200 ~ 219*/ - 5, 6, 6, 7, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,/*220 ~ 239*/ - 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8 /*240 ~ 255*/ -}; +#if BITS_PER_LONG == 32 +#define __le_long __le32 +#define lel_to_cpu(A) le32_to_cpu(A) +#define cpu_to_lel(A) cpu_to_le32(A) +#elif BITS_PER_LONG == 64 +#define __le_long __le64 +#define lel_to_cpu(A) le64_to_cpu(A) +#define cpu_to_lel(A) cpu_to_le64(A) +#else +#error "BITS_PER_LONG not 32 or 64" +#endif /* * Allocation Bitmap Management Functions */ +static bool exfat_test_bitmap_range(struct super_block *sb, unsigned int clu, + unsigned int count) +{ + struct exfat_sb_info *sbi = EXFAT_SB(sb); + unsigned int start = clu; + unsigned int end = clu + count; + unsigned int ent_idx, i, b; + unsigned int bit_offset, bits_to_check; + __le_long *bitmap_le; + unsigned long mask, word; + + if (!is_valid_cluster(sbi, start) || !is_valid_cluster(sbi, end - 1)) + return false; + + while (start < end) { + ent_idx = CLUSTER_TO_BITMAP_ENT(start); + i = BITMAP_OFFSET_SECTOR_INDEX(sb, ent_idx); + b = BITMAP_OFFSET_BIT_IN_SECTOR(sb, ent_idx); + + bitmap_le = (__le_long *)sbi->vol_amap[i]->b_data; + + /* Calculate how many bits we can check in the current word */ + bit_offset = b % BITS_PER_LONG; + bits_to_check = min(end - start, + (unsigned int)(BITS_PER_LONG - bit_offset)); + + /* Create a bitmask for the range of bits to check */ + if (bits_to_check >= BITS_PER_LONG) + mask = ~0UL; + else + mask = ((1UL << bits_to_check) - 1) << bit_offset; + word = lel_to_cpu(bitmap_le[b / BITS_PER_LONG]); + + /* Check if all bits in the mask are set */ + if ((word & mask) != mask) + return false; + + start += bits_to_check; + } + + return true; +} + static int exfat_allocate_bitmap(struct super_block *sb, struct exfat_dentry *ep) { struct exfat_sb_info *sbi = EXFAT_SB(sb); + struct blk_plug plug; long long map_size; - unsigned int i, need_map_size; + unsigned int i, j, need_map_size; sector_t sector; + unsigned int max_ra_count; sbi->map_clu = le32_to_cpu(ep->dentry.bitmap.start_clu); map_size = le64_to_cpu(ep->dentry.bitmap.size); @@ -69,28 +96,43 @@ static int exfat_allocate_bitmap(struct super_block *sb, } sbi->map_sectors = ((need_map_size - 1) >> (sb->s_blocksize_bits)) + 1; - sbi->vol_amap = kmalloc_array(sbi->map_sectors, + sbi->vol_amap = kvmalloc_array(sbi->map_sectors, sizeof(struct buffer_head *), GFP_KERNEL); if (!sbi->vol_amap) return -ENOMEM; sector = exfat_cluster_to_sector(sbi, sbi->map_clu); + max_ra_count = min(sb->s_bdi->ra_pages, sb->s_bdi->io_pages) << + (PAGE_SHIFT - sb->s_blocksize_bits); for (i = 0; i < sbi->map_sectors; i++) { - sbi->vol_amap[i] = sb_bread(sb, sector + i); - if (!sbi->vol_amap[i]) { - /* release all buffers and free vol_amap */ - int j = 0; - - while (j < i) - brelse(sbi->vol_amap[j++]); - - kfree(sbi->vol_amap); - sbi->vol_amap = NULL; - return -EIO; + /* Trigger the next readahead in advance. */ + if (max_ra_count && 0 == (i % max_ra_count)) { + blk_start_plug(&plug); + for (j = i; j < min(max_ra_count, sbi->map_sectors - i) + i; j++) + sb_breadahead(sb, sector + j); + blk_finish_plug(&plug); } + + sbi->vol_amap[i] = sb_bread(sb, sector + i); + if (!sbi->vol_amap[i]) + goto err_out; } + if (exfat_test_bitmap_range(sb, sbi->map_clu, + EXFAT_B_TO_CLU_ROUND_UP(map_size, sbi)) == false) + goto err_out; + return 0; + +err_out: + j = 0; + /* release all buffers and free vol_amap */ + while (j < i) + brelse(sbi->vol_amap[j++]); + + kvfree(sbi->vol_amap); + sbi->vol_amap = NULL; + return -EIO; } int exfat_load_bitmap(struct super_block *sb) @@ -110,11 +152,8 @@ int exfat_load_bitmap(struct super_block *sb) return -EIO; type = exfat_get_entry_type(ep); - if (type == TYPE_UNUSED) - break; - if (type != TYPE_BITMAP) - continue; - if (ep->dentry.bitmap.flags == 0x0) { + if (type == TYPE_BITMAP && + ep->dentry.bitmap.flags == 0x0) { int err; err = exfat_allocate_bitmap(sb, ep); @@ -122,6 +161,9 @@ int exfat_load_bitmap(struct super_block *sb) return err; } brelse(bh); + + if (type == TYPE_UNUSED) + return -EINVAL; } if (exfat_get_next_cluster(sb, &clu.dir)) @@ -138,14 +180,13 @@ void exfat_free_bitmap(struct exfat_sb_info *sbi) for (i = 0; i < sbi->map_sectors; i++) __brelse(sbi->vol_amap[i]); - kfree(sbi->vol_amap); + kvfree(sbi->vol_amap); } -int exfat_set_bitmap(struct inode *inode, unsigned int clu, bool sync) +int exfat_set_bitmap(struct super_block *sb, unsigned int clu, bool sync) { int i, b; unsigned int ent_idx; - struct super_block *sb = inode->i_sb; struct exfat_sb_info *sbi = EXFAT_SB(sb); if (!is_valid_cluster(sbi, clu)) @@ -160,36 +201,49 @@ int exfat_set_bitmap(struct inode *inode, unsigned int clu, bool sync) return 0; } -void exfat_clear_bitmap(struct inode *inode, unsigned int clu, bool sync) +int exfat_clear_bitmap(struct super_block *sb, unsigned int clu, bool sync) { int i, b; unsigned int ent_idx; - struct super_block *sb = inode->i_sb; struct exfat_sb_info *sbi = EXFAT_SB(sb); - struct exfat_mount_options *opts = &sbi->options; if (!is_valid_cluster(sbi, clu)) - return; + return -EIO; ent_idx = CLUSTER_TO_BITMAP_ENT(clu); i = BITMAP_OFFSET_SECTOR_INDEX(sb, ent_idx); b = BITMAP_OFFSET_BIT_IN_SECTOR(sb, ent_idx); + if (!test_bit_le(b, sbi->vol_amap[i]->b_data)) + return -EIO; + clear_bit_le(b, sbi->vol_amap[i]->b_data); + exfat_update_bh(sbi->vol_amap[i], sync); - if (opts->discard) { - int ret_discard; + return 0; +} - ret_discard = sb_issue_discard(sb, - exfat_cluster_to_sector(sbi, clu), - (1 << sbi->sect_per_clus_bits), GFP_NOFS, 0); +bool exfat_test_bitmap(struct super_block *sb, unsigned int clu) +{ + int i, b; + unsigned int ent_idx; + struct exfat_sb_info *sbi = EXFAT_SB(sb); - if (ret_discard == -EOPNOTSUPP) { - exfat_err(sb, "discard not supported by device, disabling"); - opts->discard = 0; - } - } + if (!sbi->vol_amap) + return true; + + if (!is_valid_cluster(sbi, clu)) + return false; + + ent_idx = CLUSTER_TO_BITMAP_ENT(clu); + i = BITMAP_OFFSET_SECTOR_INDEX(sb, ent_idx); + b = BITMAP_OFFSET_BIT_IN_SECTOR(sb, ent_idx); + + if (!test_bit_le(b, sbi->vol_amap[i]->b_data)) + return false; + + return true; } /* @@ -200,32 +254,35 @@ unsigned int exfat_find_free_bitmap(struct super_block *sb, unsigned int clu) { unsigned int i, map_i, map_b, ent_idx; unsigned int clu_base, clu_free; - unsigned char k, clu_mask; + unsigned long clu_bits, clu_mask; struct exfat_sb_info *sbi = EXFAT_SB(sb); + __le_long bitval; WARN_ON(clu < EXFAT_FIRST_CLUSTER); - ent_idx = CLUSTER_TO_BITMAP_ENT(clu); - clu_base = BITMAP_ENT_TO_CLUSTER(ent_idx & ~(BITS_PER_BYTE_MASK)); + ent_idx = ALIGN_DOWN(CLUSTER_TO_BITMAP_ENT(clu), BITS_PER_LONG); + clu_base = BITMAP_ENT_TO_CLUSTER(ent_idx); clu_mask = IGNORED_BITS_REMAINED(clu, clu_base); map_i = BITMAP_OFFSET_SECTOR_INDEX(sb, ent_idx); map_b = BITMAP_OFFSET_BYTE_IN_SECTOR(sb, ent_idx); for (i = EXFAT_FIRST_CLUSTER; i < sbi->num_clusters; - i += BITS_PER_BYTE) { - k = *(sbi->vol_amap[map_i]->b_data + map_b); + i += BITS_PER_LONG) { + bitval = *(__le_long *)(sbi->vol_amap[map_i]->b_data + map_b); if (clu_mask > 0) { - k |= clu_mask; + bitval |= cpu_to_lel(clu_mask); clu_mask = 0; } - if (k < 0xFF) { - clu_free = clu_base + free_bit[k]; + if (lel_to_cpu(bitval) != ULONG_MAX) { + clu_bits = lel_to_cpu(bitval); + clu_free = clu_base + ffz(clu_bits); if (clu_free < sbi->num_clusters) return clu_free; } - clu_base += BITS_PER_BYTE; + clu_base += BITS_PER_LONG; + map_b += sizeof(long); - if (++map_b >= sb->s_blocksize || + if (map_b >= sb->s_blocksize || clu_base >= sbi->num_clusters) { if (++map_i >= sbi->map_sectors) { clu_base = EXFAT_FIRST_CLUSTER; @@ -244,25 +301,24 @@ int exfat_count_used_clusters(struct super_block *sb, unsigned int *ret_count) unsigned int count = 0; unsigned int i, map_i = 0, map_b = 0; unsigned int total_clus = EXFAT_DATA_CLUSTER_COUNT(sbi); - unsigned int last_mask = total_clus & BITS_PER_BYTE_MASK; - unsigned char clu_bits; - const unsigned char last_bit_mask[] = {0, 0b00000001, 0b00000011, - 0b00000111, 0b00001111, 0b00011111, 0b00111111, 0b01111111}; + unsigned int last_mask = total_clus & (BITS_PER_LONG - 1); + unsigned long *bitmap, clu_bits; total_clus &= ~last_mask; - for (i = 0; i < total_clus; i += BITS_PER_BYTE) { - clu_bits = *(sbi->vol_amap[map_i]->b_data + map_b); - count += used_bit[clu_bits]; - if (++map_b >= (unsigned int)sb->s_blocksize) { + for (i = 0; i < total_clus; i += BITS_PER_LONG) { + bitmap = (void *)(sbi->vol_amap[map_i]->b_data + map_b); + count += hweight_long(*bitmap); + map_b += sizeof(long); + if (map_b >= (unsigned int)sb->s_blocksize) { map_i++; map_b = 0; } } if (last_mask) { - clu_bits = *(sbi->vol_amap[map_i]->b_data + map_b); - clu_bits &= last_bit_mask[last_mask]; - count += used_bit[clu_bits]; + bitmap = (void *)(sbi->vol_amap[map_i]->b_data + map_b); + clu_bits = lel_to_cpu(*(__le_long *)bitmap); + count += hweight_long(clu_bits & BITMAP_LAST_WORD_MASK(last_mask)); } *ret_count = count; diff --git a/fs/exfat/cache.c b/fs/exfat/cache.c index 5a2f119b7e8c..d5ce0ae660ba 100644 --- a/fs/exfat/cache.c +++ b/fs/exfat/cache.c @@ -11,7 +11,7 @@ */ #include <linux/slab.h> -#include <asm/unaligned.h> +#include <linux/unaligned.h> #include <linux/buffer_head.h> #include "exfat_raw.h" @@ -46,7 +46,7 @@ int exfat_cache_init(void) { exfat_cachep = kmem_cache_create("exfat_cache", sizeof(struct exfat_cache), - 0, SLAB_RECLAIM_ACCOUNT|SLAB_MEM_SPREAD, + 0, SLAB_RECLAIM_ACCOUNT, exfat_cache_init_once); if (!exfat_cachep) return -ENOMEM; diff --git a/fs/exfat/dir.c b/fs/exfat/dir.c index 1dfa67f307f1..3045a58e124a 100644 --- a/fs/exfat/dir.c +++ b/fs/exfat/dir.c @@ -29,14 +29,16 @@ static int exfat_extract_uni_name(struct exfat_dentry *ep, } -static void exfat_get_uniname_from_ext_entry(struct super_block *sb, +static int exfat_get_uniname_from_ext_entry(struct super_block *sb, struct exfat_chain *p_dir, int entry, unsigned short *uniname) { - int i; + int i, err; struct exfat_entry_set_cache es; + unsigned int uni_len = 0, len; - if (exfat_get_dentry_set(&es, sb, p_dir, entry, ES_ALL_ENTRIES)) - return; + err = exfat_get_dentry_set(&es, sb, p_dir, entry, ES_ALL_ENTRIES); + if (err) + return err; /* * First entry : file entry @@ -51,17 +53,21 @@ static void exfat_get_uniname_from_ext_entry(struct super_block *sb, if (exfat_get_entry_type(ep) != TYPE_EXTEND) break; - exfat_extract_uni_name(ep, uniname); + len = exfat_extract_uni_name(ep, uniname); + uni_len += len; + if (len != EXFAT_FILE_NAME_LEN || uni_len >= MAX_NAME_LENGTH) + break; uniname += EXFAT_FILE_NAME_LEN; } exfat_put_dentry_set(&es, false); + return 0; } /* read a directory entry from the opened directory */ static int exfat_readdir(struct inode *inode, loff_t *cpos, struct exfat_dir_entry *dir_entry) { - int i, dentries_per_clu, num_ext; + int i, dentries_per_clu, num_ext, err; unsigned int type, clu_offset, max_dentries; struct exfat_chain dir, clu; struct exfat_uni_name uni_name; @@ -76,11 +82,8 @@ static int exfat_readdir(struct inode *inode, loff_t *cpos, struct exfat_dir_ent if (ei->type != TYPE_DIR) return -EPERM; - if (ei->entry == -1) - exfat_chain_set(&dir, sbi->root_dir, 0, ALLOC_FAT_CHAIN); - else - exfat_chain_set(&dir, ei->start_clu, - EXFAT_B_TO_CLU(i_size_read(inode), sbi), ei->flags); + exfat_chain_set(&dir, ei->start_clu, + EXFAT_B_TO_CLU(i_size_read(inode), sbi), ei->flags); dentries_per_clu = sbi->dentries_per_clu; max_dentries = (unsigned int)min_t(u64, MAX_EXFAT_DENTRIES, @@ -100,7 +103,7 @@ static int exfat_readdir(struct inode *inode, loff_t *cpos, struct exfat_dir_ent clu.dir = ei->hint_bmap.clu; } - while (clu_offset > 0) { + while (clu_offset > 0 && clu.dir != EXFAT_EOF_CLUSTER) { if (exfat_get_next_cluster(sb, &(clu.dir))) return -EIO; @@ -119,7 +122,7 @@ static int exfat_readdir(struct inode *inode, loff_t *cpos, struct exfat_dir_ent type = exfat_get_entry_type(ep); if (type == TYPE_UNUSED) { brelse(bh); - break; + goto out; } if (type != TYPE_FILE && type != TYPE_DIR) { @@ -129,25 +132,14 @@ static int exfat_readdir(struct inode *inode, loff_t *cpos, struct exfat_dir_ent num_ext = ep->dentry.file.num_ext; dir_entry->attr = le16_to_cpu(ep->dentry.file.attr); - exfat_get_entry_time(sbi, &dir_entry->crtime, - ep->dentry.file.create_tz, - ep->dentry.file.create_time, - ep->dentry.file.create_date, - ep->dentry.file.create_time_cs); - exfat_get_entry_time(sbi, &dir_entry->mtime, - ep->dentry.file.modify_tz, - ep->dentry.file.modify_time, - ep->dentry.file.modify_date, - ep->dentry.file.modify_time_cs); - exfat_get_entry_time(sbi, &dir_entry->atime, - ep->dentry.file.access_tz, - ep->dentry.file.access_time, - ep->dentry.file.access_date, - 0); *uni_name.name = 0x0; - exfat_get_uniname_from_ext_entry(sb, &clu, i, + err = exfat_get_uniname_from_ext_entry(sb, &clu, i, uni_name.name); + if (err) { + brelse(bh); + continue; + } exfat_utf16_to_nls(sb, &uni_name, dir_entry->namebuf.lfn, dir_entry->namebuf.lfnbuf_len); @@ -156,9 +148,8 @@ static int exfat_readdir(struct inode *inode, loff_t *cpos, struct exfat_dir_ent ep = exfat_get_dentry(sb, &clu, i + 1, &bh); if (!ep) return -EIO; - dir_entry->size = - le64_to_cpu(ep->dentry.stream.valid_size); - dir_entry->entry = dentry; + dir_entry->entry = i; + dir_entry->dir = clu; brelse(bh); ei->hint_bmap.off = EXFAT_DEN_TO_CLU(dentry, sbi); @@ -179,6 +170,7 @@ static int exfat_readdir(struct inode *inode, loff_t *cpos, struct exfat_dir_ent } } +out: dir_entry->namebuf.lfn[0] = '\0'; *cpos = EXFAT_DEN_TO_B(dentry); return 0; @@ -208,7 +200,10 @@ static void exfat_free_namebuf(struct exfat_dentry_namebuf *nb) exfat_init_namebuf(nb); } -/* skip iterating emit_dots when dir is empty */ +/* + * Before calling dir_emit*(), sbi->s_lock should be released + * because page fault can occur in dir_emit*(). + */ #define ITER_POS_FILLED_DOTS (2) static int exfat_iterate(struct file *file, struct dir_context *ctx) { @@ -223,35 +218,33 @@ static int exfat_iterate(struct file *file, struct dir_context *ctx) int err = 0, fake_offset = 0; exfat_init_namebuf(nb); - mutex_lock(&EXFAT_SB(sb)->s_lock); cpos = ctx->pos; if (!dir_emit_dots(file, ctx)) - goto unlock; + goto out; if (ctx->pos == ITER_POS_FILLED_DOTS) { cpos = 0; fake_offset = 1; } - if (cpos & (DENTRY_SIZE - 1)) { - err = -ENOENT; - goto unlock; - } + cpos = round_up(cpos, DENTRY_SIZE); /* name buffer should be allocated before use */ err = exfat_alloc_namebuf(nb); if (err) - goto unlock; + goto out; get_new: + mutex_lock(&EXFAT_SB(sb)->s_lock); + if (ei->flags == ALLOC_NO_FAT_CHAIN && cpos >= i_size_read(inode)) goto end_of_dir; err = exfat_readdir(inode, &cpos, &de); if (err) { /* - * At least we tried to read a sector. Move cpos to next sector - * position (should be aligned). + * At least we tried to read a sector. + * Move cpos to next sector position (should be aligned). */ if (err == -EIO) { cpos += 1 << (sb->s_blocksize_bits); @@ -265,7 +258,7 @@ get_new: if (!nb->lfn[0]) goto end_of_dir; - i_pos = ((loff_t)ei->start_clu << 32) | (de.entry & 0xffffffff); + i_pos = ((loff_t)de.dir.dir << 32) | (de.entry & 0xffffffff); tmp = exfat_iget(sb, i_pos); if (tmp) { inum = tmp->i_ino; @@ -274,16 +267,10 @@ get_new: inum = iunique(sb, EXFAT_ROOT_INO); } - /* - * Before calling dir_emit(), sb_lock should be released. - * Because page fault can occur in dir_emit() when the size - * of buffer given from user is larger than one page size. - */ mutex_unlock(&EXFAT_SB(sb)->s_lock); if (!dir_emit(ctx, nb->lfn, strlen(nb->lfn), inum, - (de.attr & ATTR_SUBDIR) ? DT_DIR : DT_REG)) - goto out_unlocked; - mutex_lock(&EXFAT_SB(sb)->s_lock); + (de.attr & EXFAT_ATTR_SUBDIR) ? DT_DIR : DT_REG)) + goto out; ctx->pos = cpos; goto get_new; @@ -291,9 +278,8 @@ end_of_dir: if (!cpos && fake_offset) cpos = ITER_POS_FILLED_DOTS; ctx->pos = cpos; -unlock: mutex_unlock(&EXFAT_SB(sb)->s_lock); -out_unlocked: +out: /* * To improve performance, free namebuf after unlock sb_lock. * If namebuf is not allocated, this function do nothing @@ -302,10 +288,11 @@ out_unlocked: return err; } +WRAP_DIR_ITER(exfat_iterate) // FIXME! const struct file_operations exfat_dir_operations = { .llseek = generic_file_llseek, .read = generic_read_dir, - .iterate = exfat_iterate, + .iterate_shared = shared_exfat_iterate, .unlocked_ioctl = exfat_ioctl, #ifdef CONFIG_COMPAT .compat_ioctl = exfat_compat_ioctl, @@ -354,7 +341,7 @@ unsigned int exfat_get_entry_type(struct exfat_dentry *ep) if (ep->type == EXFAT_VOLUME) return TYPE_VOLUME; if (ep->type == EXFAT_FILE) { - if (le16_to_cpu(ep->dentry.file.attr) & ATTR_SUBDIR) + if (le16_to_cpu(ep->dentry.file.attr) & EXFAT_ATTR_SUBDIR) return TYPE_DIR; return TYPE_FILE; } @@ -378,6 +365,12 @@ unsigned int exfat_get_entry_type(struct exfat_dentry *ep) return TYPE_ACL; return TYPE_CRITICAL_SEC; } + + if (ep->type == EXFAT_VENDOR_EXT) + return TYPE_VENDOR_EXT; + if (ep->type == EXFAT_VENDOR_ALLOC) + return TYPE_VENDOR_ALLOC; + return TYPE_BENIGN_SEC; } @@ -399,19 +392,22 @@ static void exfat_set_entry_type(struct exfat_dentry *ep, unsigned int type) ep->type = EXFAT_VOLUME; } else if (type == TYPE_DIR) { ep->type = EXFAT_FILE; - ep->dentry.file.attr = cpu_to_le16(ATTR_SUBDIR); + ep->dentry.file.attr = cpu_to_le16(EXFAT_ATTR_SUBDIR); } else if (type == TYPE_FILE) { ep->type = EXFAT_FILE; - ep->dentry.file.attr = cpu_to_le16(ATTR_ARCHIVE); + ep->dentry.file.attr = cpu_to_le16(EXFAT_ATTR_ARCHIVE); } } static void exfat_init_stream_entry(struct exfat_dentry *ep, - unsigned char flags, unsigned int start_clu, - unsigned long long size) + unsigned int start_clu, unsigned long long size) { + memset(ep, 0, sizeof(*ep)); exfat_set_entry_type(ep, TYPE_STREAM); - ep->dentry.stream.flags = flags; + if (size == 0) + ep->dentry.stream.flags = ALLOC_FAT_CHAIN; + else + ep->dentry.stream.flags = ALLOC_NO_FAT_CHAIN; ep->dentry.stream.start_clu = cpu_to_le32(start_clu); ep->dentry.stream.valid_size = cpu_to_le64(size); ep->dentry.stream.size = cpu_to_le64(size); @@ -435,156 +431,99 @@ static void exfat_init_name_entry(struct exfat_dentry *ep, } } -int exfat_init_dir_entry(struct inode *inode, struct exfat_chain *p_dir, - int entry, unsigned int type, unsigned int start_clu, - unsigned long long size) +void exfat_init_dir_entry(struct exfat_entry_set_cache *es, + unsigned int type, unsigned int start_clu, + unsigned long long size, struct timespec64 *ts) { - struct super_block *sb = inode->i_sb; + struct super_block *sb = es->sb; struct exfat_sb_info *sbi = EXFAT_SB(sb); - struct timespec64 ts = current_time(inode); struct exfat_dentry *ep; - struct buffer_head *bh; - - /* - * We cannot use exfat_get_dentry_set here because file ep is not - * initialized yet. - */ - ep = exfat_get_dentry(sb, p_dir, entry, &bh); - if (!ep) - return -EIO; + ep = exfat_get_dentry_cached(es, ES_IDX_FILE); + memset(ep, 0, sizeof(*ep)); exfat_set_entry_type(ep, type); - exfat_set_entry_time(sbi, &ts, + exfat_set_entry_time(sbi, ts, &ep->dentry.file.create_tz, &ep->dentry.file.create_time, &ep->dentry.file.create_date, &ep->dentry.file.create_time_cs); - exfat_set_entry_time(sbi, &ts, + exfat_set_entry_time(sbi, ts, &ep->dentry.file.modify_tz, &ep->dentry.file.modify_time, &ep->dentry.file.modify_date, &ep->dentry.file.modify_time_cs); - exfat_set_entry_time(sbi, &ts, + exfat_set_entry_time(sbi, ts, &ep->dentry.file.access_tz, &ep->dentry.file.access_time, &ep->dentry.file.access_date, NULL); - exfat_update_bh(bh, IS_DIRSYNC(inode)); - brelse(bh); - - ep = exfat_get_dentry(sb, p_dir, entry + 1, &bh); - if (!ep) - return -EIO; - - exfat_init_stream_entry(ep, - (type == TYPE_FILE) ? ALLOC_FAT_CHAIN : ALLOC_NO_FAT_CHAIN, - start_clu, size); - exfat_update_bh(bh, IS_DIRSYNC(inode)); - brelse(bh); - - return 0; + ep = exfat_get_dentry_cached(es, ES_IDX_STREAM); + exfat_init_stream_entry(ep, start_clu, size); } -int exfat_update_dir_chksum(struct inode *inode, struct exfat_chain *p_dir, - int entry) +static void exfat_free_benign_secondary_clusters(struct inode *inode, + struct exfat_dentry *ep) { struct super_block *sb = inode->i_sb; - int ret = 0; - int i, num_entries; - u16 chksum; - struct exfat_dentry *ep, *fep; - struct buffer_head *fbh, *bh; - - fep = exfat_get_dentry(sb, p_dir, entry, &fbh); - if (!fep) - return -EIO; + struct exfat_chain dir; + unsigned int start_clu = + le32_to_cpu(ep->dentry.generic_secondary.start_clu); + u64 size = le64_to_cpu(ep->dentry.generic_secondary.size); + unsigned char flags = ep->dentry.generic_secondary.flags; - num_entries = fep->dentry.file.num_ext + 1; - chksum = exfat_calc_chksum16(fep, DENTRY_SIZE, 0, CS_DIR_ENTRY); - - for (i = 1; i < num_entries; i++) { - ep = exfat_get_dentry(sb, p_dir, entry + i, &bh); - if (!ep) { - ret = -EIO; - goto release_fbh; - } - chksum = exfat_calc_chksum16(ep, DENTRY_SIZE, chksum, - CS_DEFAULT); - brelse(bh); - } + if (!(flags & ALLOC_POSSIBLE) || !start_clu || !size) + return; - fep->dentry.file.checksum = cpu_to_le16(chksum); - exfat_update_bh(fbh, IS_DIRSYNC(inode)); -release_fbh: - brelse(fbh); - return ret; + exfat_chain_set(&dir, start_clu, + EXFAT_B_TO_CLU_ROUND_UP(size, EXFAT_SB(sb)), + flags); + exfat_free_cluster(inode, &dir); } -int exfat_init_ext_entry(struct inode *inode, struct exfat_chain *p_dir, - int entry, int num_entries, struct exfat_uni_name *p_uniname) +void exfat_init_ext_entry(struct exfat_entry_set_cache *es, int num_entries, + struct exfat_uni_name *p_uniname) { - struct super_block *sb = inode->i_sb; int i; unsigned short *uniname = p_uniname->name; struct exfat_dentry *ep; - struct buffer_head *bh; - int sync = IS_DIRSYNC(inode); - - ep = exfat_get_dentry(sb, p_dir, entry, &bh); - if (!ep) - return -EIO; + ep = exfat_get_dentry_cached(es, ES_IDX_FILE); ep->dentry.file.num_ext = (unsigned char)(num_entries - 1); - exfat_update_bh(bh, sync); - brelse(bh); - - ep = exfat_get_dentry(sb, p_dir, entry + 1, &bh); - if (!ep) - return -EIO; + ep = exfat_get_dentry_cached(es, ES_IDX_STREAM); ep->dentry.stream.name_len = p_uniname->name_len; ep->dentry.stream.name_hash = cpu_to_le16(p_uniname->name_hash); - exfat_update_bh(bh, sync); - brelse(bh); - - for (i = EXFAT_FIRST_CLUSTER; i < num_entries; i++) { - ep = exfat_get_dentry(sb, p_dir, entry + i, &bh); - if (!ep) - return -EIO; + for (i = ES_IDX_FIRST_FILENAME; i < num_entries; i++) { + ep = exfat_get_dentry_cached(es, i); exfat_init_name_entry(ep, uniname); - exfat_update_bh(bh, sync); - brelse(bh); uniname += EXFAT_FILE_NAME_LEN; } - exfat_update_dir_chksum(inode, p_dir, entry); - return 0; + exfat_update_dir_chksum(es); } -int exfat_remove_entries(struct inode *inode, struct exfat_chain *p_dir, - int entry, int order, int num_entries) +void exfat_remove_entries(struct inode *inode, struct exfat_entry_set_cache *es, + int order) { - struct super_block *sb = inode->i_sb; int i; struct exfat_dentry *ep; - struct buffer_head *bh; - for (i = order; i < num_entries; i++) { - ep = exfat_get_dentry(sb, p_dir, entry + i, &bh); - if (!ep) - return -EIO; + for (i = order; i < es->num_entries; i++) { + ep = exfat_get_dentry_cached(es, i); + + if (exfat_get_entry_type(ep) & TYPE_BENIGN_SEC) + exfat_free_benign_secondary_clusters(inode, ep); exfat_set_entry_type(ep, TYPE_DELETED); - exfat_update_bh(bh, IS_DIRSYNC(inode)); - brelse(bh); } - return 0; + if (order < es->num_entries) + es->modified = true; } -void exfat_update_dir_chksum_with_entry_set(struct exfat_entry_set_cache *es) +void exfat_update_dir_chksum(struct exfat_entry_set_cache *es) { int chksum_type = CS_DIR_ENTRY, i; unsigned short chksum = 0; @@ -665,6 +604,11 @@ static int exfat_find_location(struct super_block *sb, struct exfat_chain *p_dir if (ret) return ret; + if (!exfat_test_bitmap(sb, clu)) { + exfat_err(sb, "failed to test cluster bit(%u)", clu); + return -EIO; + } + /* byte offset in cluster */ off = EXFAT_CLU_OFFSET(off, sbi); @@ -739,11 +683,11 @@ struct exfat_dentry *exfat_get_dentry(struct super_block *sb, } enum exfat_validate_dentry_mode { - ES_MODE_STARTED, ES_MODE_GET_FILE_ENTRY, ES_MODE_GET_STRM_ENTRY, ES_MODE_GET_NAME_ENTRY, ES_MODE_GET_CRITICAL_SEC_ENTRY, + ES_MODE_GET_BENIGN_SEC_ENTRY, }; static bool exfat_validate_entry(unsigned int type, @@ -753,40 +697,32 @@ static bool exfat_validate_entry(unsigned int type, return false; switch (*mode) { - case ES_MODE_STARTED: - if (type != TYPE_FILE && type != TYPE_DIR) - return false; - *mode = ES_MODE_GET_FILE_ENTRY; - return true; case ES_MODE_GET_FILE_ENTRY: if (type != TYPE_STREAM) return false; *mode = ES_MODE_GET_STRM_ENTRY; - return true; + break; case ES_MODE_GET_STRM_ENTRY: if (type != TYPE_EXTEND) return false; *mode = ES_MODE_GET_NAME_ENTRY; - return true; + break; case ES_MODE_GET_NAME_ENTRY: - if (type == TYPE_STREAM) - return false; - if (type != TYPE_EXTEND) { - if (!(type & TYPE_CRITICAL_SEC)) - return false; - *mode = ES_MODE_GET_CRITICAL_SEC_ENTRY; - } - return true; - case ES_MODE_GET_CRITICAL_SEC_ENTRY: - if (type == TYPE_EXTEND || type == TYPE_STREAM) + if (type & TYPE_BENIGN_SEC) + *mode = ES_MODE_GET_BENIGN_SEC_ENTRY; + else if (type != TYPE_EXTEND) return false; - if ((type & TYPE_CRITICAL_SEC) != TYPE_CRITICAL_SEC) + break; + case ES_MODE_GET_BENIGN_SEC_ENTRY: + /* Assume unreconized benign secondary entry */ + if (!(type & TYPE_BENIGN_SEC)) return false; - return true; + break; default: - WARN_ON_ONCE(1); return false; } + + return true; } struct exfat_dentry *exfat_get_dentry_cached( @@ -800,7 +736,7 @@ struct exfat_dentry *exfat_get_dentry_cached( } /* - * Returns a set of dentries for a file or dir. + * Returns a set of dentries. * * Note It provides a direct pointer to bh->data via exfat_get_dentry_cached(). * User should call exfat_get_dentry_set() after setting 'modified' to apply @@ -808,22 +744,24 @@ struct exfat_dentry *exfat_get_dentry_cached( * * in: * sb+p_dir+entry: indicates a file/dir - * type: specifies how many dentries should be included. + * num_entries: specifies how many dentries should be included. + * It will be set to es->num_entries if it is not 0. + * If num_entries is 0, es->num_entries will be obtained + * from the first dentry. + * out: + * es: pointer of entry set on success. * return: - * pointer of entry set on success, - * NULL on failure. + * 0 on success + * -error code on failure */ -int exfat_get_dentry_set(struct exfat_entry_set_cache *es, +static int __exfat_get_dentry_set(struct exfat_entry_set_cache *es, struct super_block *sb, struct exfat_chain *p_dir, int entry, - unsigned int type) + unsigned int num_entries) { int ret, i, num_bh; unsigned int off; sector_t sec; struct exfat_sb_info *sbi = EXFAT_SB(sb); - struct exfat_dentry *ep; - int num_entries; - enum exfat_validate_dentry_mode mode = ES_MODE_STARTED; struct buffer_head *bh; if (p_dir->dir == DIR_DELETED) { @@ -846,17 +784,23 @@ int exfat_get_dentry_set(struct exfat_entry_set_cache *es, return -EIO; es->bh[es->num_bh++] = bh; - ep = exfat_get_dentry_cached(es, ES_IDX_FILE); - if (!exfat_validate_entry(exfat_get_entry_type(ep), &mode)) - goto put_es; + if (num_entries == ES_ALL_ENTRIES) { + struct exfat_dentry *ep; + + ep = exfat_get_dentry_cached(es, ES_IDX_FILE); + if (ep->type != EXFAT_FILE) { + brelse(bh); + return -EIO; + } + + num_entries = ep->dentry.file.num_ext + 1; + } - num_entries = type == ES_ALL_ENTRIES ? - ep->dentry.file.num_ext + 1 : type; 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); + es->bh = kmalloc_array(num_bh, sizeof(*es->bh), GFP_NOFS); if (!es->bh) { brelse(bh); return -ENOMEM; @@ -884,8 +828,27 @@ int exfat_get_dentry_set(struct exfat_entry_set_cache *es, es->bh[es->num_bh++] = bh; } + return 0; + +put_es: + exfat_put_dentry_set(es, false); + return -EIO; +} + +int exfat_get_dentry_set(struct exfat_entry_set_cache *es, + struct super_block *sb, struct exfat_chain *p_dir, + int entry, unsigned int num_entries) +{ + int ret, i; + struct exfat_dentry *ep; + enum exfat_validate_dentry_mode mode = ES_MODE_GET_FILE_ENTRY; + + ret = __exfat_get_dentry_set(es, sb, p_dir, entry, num_entries); + if (ret < 0) + return ret; + /* validate cached dentries */ - for (i = ES_IDX_STREAM; i < num_entries; i++) { + for (i = ES_IDX_STREAM; i < es->num_entries; i++) { ep = exfat_get_dentry_cached(es, i); if (!exfat_validate_entry(exfat_get_entry_type(ep), &mode)) goto put_es; @@ -897,6 +860,85 @@ put_es: return -EIO; } +static int exfat_validate_empty_dentry_set(struct exfat_entry_set_cache *es) +{ + struct exfat_dentry *ep; + struct buffer_head *bh; + int i, off; + bool unused_hit = false; + + /* + * ONLY UNUSED OR DELETED DENTRIES ARE ALLOWED: + * Although it violates the specification for a deleted entry to + * follow an unused entry, some exFAT implementations could work + * like this. Therefore, to improve compatibility, let's allow it. + */ + for (i = 0; i < es->num_entries; i++) { + ep = exfat_get_dentry_cached(es, i); + if (ep->type == EXFAT_UNUSED) { + unused_hit = true; + } else if (!IS_EXFAT_DELETED(ep->type)) { + if (unused_hit) + goto err_used_follow_unused; + i++; + goto count_skip_entries; + } + } + + return 0; + +err_used_follow_unused: + off = es->start_off + (i << DENTRY_SIZE_BITS); + bh = es->bh[EXFAT_B_TO_BLK(off, es->sb)]; + + exfat_fs_error(es->sb, + "in sector %lld, dentry %d should be unused, but 0x%x", + bh->b_blocknr, off >> DENTRY_SIZE_BITS, ep->type); + + return -EIO; + +count_skip_entries: + es->num_entries = EXFAT_B_TO_DEN(EXFAT_BLK_TO_B(es->num_bh, es->sb) - es->start_off); + for (; i < es->num_entries; i++) { + ep = exfat_get_dentry_cached(es, i); + if (IS_EXFAT_DELETED(ep->type)) + break; + } + + return i; +} + +/* + * Get an empty dentry set. + * + * in: + * sb+p_dir+entry: indicates the empty dentry location + * num_entries: specifies how many empty dentries should be included. + * out: + * es: pointer of empty dentry set on success. + * return: + * 0 : on success + * >0 : the dentries are not empty, the return value is the number of + * dentries to be skipped for the next lookup. + * <0 : on failure + */ +int exfat_get_empty_dentry_set(struct exfat_entry_set_cache *es, + struct super_block *sb, struct exfat_chain *p_dir, + int entry, unsigned int num_entries) +{ + int ret; + + ret = __exfat_get_dentry_set(es, sb, p_dir, entry, num_entries); + if (ret < 0) + return ret; + + ret = exfat_validate_empty_dentry_set(es); + if (ret) + exfat_put_dentry_set(es, false); + + return ret; +} + static inline void exfat_reset_empty_hint(struct exfat_hint_femp *hint_femp) { hint_femp->eidx = EXFAT_HINT_NONE; @@ -959,6 +1001,7 @@ int exfat_find_dir_entry(struct super_block *sb, struct exfat_inode_info *ei, struct exfat_hint_femp candi_empty; struct exfat_sb_info *sbi = EXFAT_SB(sb); int num_entries = exfat_calc_num_entries(p_uniname); + unsigned int clu_count = 0; if (num_entries < 0) return num_entries; @@ -1047,7 +1090,8 @@ rewind: if (entry_type == TYPE_EXTEND) { unsigned short entry_uniname[16], unichar; - if (step != DIRENT_STEP_NAME) { + if (step != DIRENT_STEP_NAME || + name_len >= MAX_NAME_LENGTH) { step = DIRENT_STEP_FILE; continue; } @@ -1095,6 +1139,10 @@ rewind: } else { if (exfat_get_next_cluster(sb, &clu.dir)) return -EIO; + + /* break if the cluster chain includes a loop */ + if (unlikely(++clu_count > EXFAT_DATA_CLUSTER_COUNT(sbi))) + goto not_found; } } @@ -1152,34 +1200,12 @@ found: return dentry - num_ext; } -int exfat_count_ext_entries(struct super_block *sb, struct exfat_chain *p_dir, - int entry, struct exfat_dentry *ep) -{ - int i, count = 0; - unsigned int type; - struct exfat_dentry *ext_ep; - struct buffer_head *bh; - - for (i = 0, entry++; i < ep->dentry.file.num_ext; i++, entry++) { - ext_ep = exfat_get_dentry(sb, p_dir, entry, &bh); - if (!ext_ep) - return -EIO; - - type = exfat_get_entry_type(ext_ep); - brelse(bh); - if (type == TYPE_EXTEND || type == TYPE_STREAM) - count++; - else - break; - } - return count; -} - int exfat_count_dir_entries(struct super_block *sb, struct exfat_chain *p_dir) { int i, count = 0; int dentries_per_clu; unsigned int entry_type; + unsigned int clu_count = 0; struct exfat_chain clu; struct exfat_dentry *ep; struct exfat_sb_info *sbi = EXFAT_SB(sb); @@ -1212,8 +1238,174 @@ int exfat_count_dir_entries(struct super_block *sb, struct exfat_chain *p_dir) } 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; + } + } } return count; } + +static int exfat_get_volume_label_dentry(struct super_block *sb, + struct exfat_entry_set_cache *es) +{ + int i; + int dentry = 0; + unsigned int type; + struct exfat_sb_info *sbi = EXFAT_SB(sb); + struct exfat_hint_femp hint_femp; + struct exfat_inode_info *ei = EXFAT_I(sb->s_root->d_inode); + struct exfat_chain clu; + struct exfat_dentry *ep; + struct buffer_head *bh; + + hint_femp.eidx = EXFAT_HINT_NONE; + exfat_chain_set(&clu, sbi->root_dir, 0, ALLOC_FAT_CHAIN); + + while (clu.dir != EXFAT_EOF_CLUSTER) { + for (i = 0; i < sbi->dentries_per_clu; i++, dentry++) { + ep = exfat_get_dentry(sb, &clu, i, &bh); + if (!ep) + return -EIO; + + type = exfat_get_entry_type(ep); + if (hint_femp.eidx == EXFAT_HINT_NONE) { + if (type == TYPE_DELETED || type == TYPE_UNUSED) { + hint_femp.cur = clu; + hint_femp.eidx = dentry; + hint_femp.count = 1; + } + } + + if (type == TYPE_UNUSED) { + brelse(bh); + goto not_found; + } + + if (type != TYPE_VOLUME) { + brelse(bh); + continue; + } + + memset(es, 0, sizeof(*es)); + es->sb = sb; + es->bh = es->__bh; + es->bh[0] = bh; + es->num_bh = 1; + es->start_off = EXFAT_DEN_TO_B(i) % sb->s_blocksize; + + return 0; + } + + if (exfat_get_next_cluster(sb, &(clu.dir))) + return -EIO; + } + +not_found: + if (hint_femp.eidx == EXFAT_HINT_NONE) { + hint_femp.cur.dir = EXFAT_EOF_CLUSTER; + hint_femp.eidx = dentry; + hint_femp.count = 0; + } + + ei->hint_femp = hint_femp; + + return -ENOENT; +} + +int exfat_read_volume_label(struct super_block *sb, struct exfat_uni_name *label_out) +{ + int ret, i; + struct exfat_sb_info *sbi = EXFAT_SB(sb); + struct exfat_entry_set_cache es; + struct exfat_dentry *ep; + + mutex_lock(&sbi->s_lock); + + memset(label_out, 0, sizeof(*label_out)); + ret = exfat_get_volume_label_dentry(sb, &es); + if (ret < 0) { + /* + * ENOENT signifies that a volume label dentry doesn't exist + * We will treat this as an empty volume label and not fail. + */ + if (ret == -ENOENT) + ret = 0; + + goto unlock; + } + + ep = exfat_get_dentry_cached(&es, 0); + label_out->name_len = ep->dentry.volume_label.char_count; + if (label_out->name_len > EXFAT_VOLUME_LABEL_LEN) { + ret = -EIO; + exfat_put_dentry_set(&es, false); + goto unlock; + } + + for (i = 0; i < label_out->name_len; i++) + label_out->name[i] = le16_to_cpu(ep->dentry.volume_label.volume_label[i]); + + exfat_put_dentry_set(&es, false); +unlock: + mutex_unlock(&sbi->s_lock); + return ret; +} + +int exfat_write_volume_label(struct super_block *sb, + struct exfat_uni_name *label) +{ + int ret, i; + struct exfat_sb_info *sbi = EXFAT_SB(sb); + struct inode *root_inode = sb->s_root->d_inode; + struct exfat_entry_set_cache es; + struct exfat_chain clu; + struct exfat_dentry *ep; + + if (label->name_len > EXFAT_VOLUME_LABEL_LEN) + return -EINVAL; + + mutex_lock(&sbi->s_lock); + + ret = exfat_get_volume_label_dentry(sb, &es); + if (ret == -ENOENT) { + if (label->name_len == 0) { + /* No volume label dentry, no need to clear */ + ret = 0; + goto unlock; + } + + ret = exfat_find_empty_entry(root_inode, &clu, 1, &es); + } + + if (ret < 0) + goto unlock; + + ep = exfat_get_dentry_cached(&es, 0); + + if (label->name_len == 0 && ep->dentry.volume_label.char_count == 0) { + /* volume label had been cleared */ + exfat_put_dentry_set(&es, 0); + goto unlock; + } + + memset(ep, 0, sizeof(*ep)); + ep->type = EXFAT_VOLUME; + + for (i = 0; i < label->name_len; i++) + ep->dentry.volume_label.volume_label[i] = + cpu_to_le16(label->name[i]); + + ep->dentry.volume_label.char_count = label->name_len; + es.modified = true; + + ret = exfat_put_dentry_set(&es, IS_DIRSYNC(root_inode)); + +unlock: + mutex_unlock(&sbi->s_lock); + return ret; +} diff --git a/fs/exfat/exfat_fs.h b/fs/exfat/exfat_fs.h index bc6d21d7c5ad..176fef62574c 100644 --- a/fs/exfat/exfat_fs.h +++ b/fs/exfat/exfat_fs.h @@ -10,11 +10,10 @@ #include <linux/ratelimit.h> #include <linux/nls.h> #include <linux/blkdev.h> +#include <uapi/linux/exfat.h> #define EXFAT_ROOT_INO 1 -#define EXFAT_CLUSTERS_UNTRACKED (~0u) - /* * exfat error flags */ @@ -30,7 +29,6 @@ enum exfat_error_mode { enum { NLS_NAME_NO_LOSSY = 0, /* no lossy */ NLS_NAME_LOSSY = 1 << 0, /* just detected incorrect filename(s) */ - NLS_NAME_OVERLEN = 1 << 1, /* the length is over than its limit */ }; #define EXFAT_HASH_BITS 8 @@ -50,7 +48,7 @@ enum { #define ES_IDX_LAST_FILENAME(name_len) \ (ES_IDX_FIRST_FILENAME + EXFAT_FILENAME_ENTRY_NUM(name_len) - 1) -#define DIR_DELETED 0xFFFF0321 +#define DIR_DELETED 0xFFFFFFF7 /* type values */ #define TYPE_UNUSED 0x0000 @@ -71,6 +69,8 @@ enum { #define TYPE_PADDING 0x0402 #define TYPE_ACLTAB 0x0403 #define TYPE_BENIGN_SEC 0x0800 +#define TYPE_VENDOR_EXT 0x0801 +#define TYPE_VENDOR_ALLOC 0x0802 #define MAX_CHARSET_SIZE 6 /* max size of multi-byte character */ #define MAX_NAME_LENGTH 255 /* max len of file name excluding NULL */ @@ -133,8 +133,7 @@ enum { #define BITMAP_OFFSET_BIT_IN_SECTOR(sb, ent) (ent & BITS_PER_SECTOR_MASK(sb)) #define BITMAP_OFFSET_BYTE_IN_SECTOR(sb, ent) \ ((ent / BITS_PER_BYTE) & ((sb)->s_blocksize - 1)) -#define BITS_PER_BYTE_MASK 0x7 -#define IGNORED_BITS_REMAINED(clu, clu_base) ((1 << ((clu) - (clu_base))) - 1) +#define IGNORED_BITS_REMAINED(clu, clu_base) ((1UL << ((clu) - (clu_base))) - 1) #define ES_ENTRY_NUM(name_len) (ES_IDX_LAST_FILENAME(name_len) + 1) /* 19 entries = 1 file entry + 1 stream entry + 17 filename entries */ @@ -147,6 +146,9 @@ enum { #define DIR_CACHE_SIZE \ (DIV_ROUND_UP(EXFAT_DEN_TO_B(ES_MAX_ENTRY_NUM), SECTOR_SIZE) + 1) +/* Superblock flags */ +#define EXFAT_FLAGS_SHUTDOWN 1 + struct exfat_dentry_namebuf { char *lfn; int lfnbuf_len; /* usually MAX_UNINAME_BUF_SIZE */ @@ -199,13 +201,16 @@ struct exfat_entry_set_cache { #define IS_DYNAMIC_ES(es) ((es)->__bh != (es)->bh) struct exfat_dir_entry { + /* the cluster where file dentry is located */ struct exfat_chain dir; + /* the index of file dentry in ->dir */ int entry; unsigned int type; unsigned int start_clu; unsigned char flags; unsigned short attr; loff_t size; + loff_t valid_size; unsigned int num_subdirs; struct timespec64 atime; struct timespec64 mtime; @@ -232,6 +237,8 @@ struct exfat_mount_options { discard:1, /* Issue discard requests on deletions */ keep_last_dots:1; /* Keep trailing periods in paths */ int time_offset; /* Offset of timestamps from UTC (in minutes) */ + /* Support creating zero-size directory, default: false */ + bool zero_size_dir; }; /* @@ -263,6 +270,8 @@ struct exfat_sb_info { unsigned int clu_srch_ptr; /* cluster search pointer */ unsigned int used_clusters; /* number of used clusters */ + unsigned long s_exfat_flags; /* Exfat superblock flags */ + struct mutex s_lock; /* superblock lock */ struct mutex bitmap_lock; /* bitmap lock */ struct exfat_mount_options options; @@ -271,7 +280,6 @@ struct exfat_sb_info { spinlock_t inode_hash_lock; struct hlist_head inode_hashtable[EXFAT_HASH_SIZE]; - struct rcu_head rcu; }; @@ -281,7 +289,9 @@ struct exfat_sb_info { * EXFAT file system inode in-memory data */ struct exfat_inode_info { + /* the cluster where file dentry is located */ struct exfat_chain dir; + /* the index of file dentry in ->dir */ int entry; unsigned int type; unsigned short attr; @@ -306,15 +316,9 @@ struct exfat_inode_info { /* for avoiding the race between alloc and free */ unsigned int cache_valid_id; - /* - * NOTE: i_size_ondisk is 64bits, so must hold ->inode_lock to access. - * physically allocated size. - */ - loff_t i_size_ondisk; - /* block-aligned i_size (used in cont_write_begin) */ - loff_t i_size_aligned; /* on-disk position of directory entry or 0 */ loff_t i_pos; + loff_t valid_size; /* hash by i_location */ struct hlist_node i_hash_fat; /* protect bmap against truncate */ @@ -334,6 +338,11 @@ static inline struct exfat_inode_info *EXFAT_I(struct inode *inode) return container_of(inode, struct exfat_inode_info, vfs_inode); } +static inline int exfat_forced_shutdown(struct super_block *sb) +{ + return test_bit(EXFAT_FLAGS_SHUTDOWN, &EXFAT_SB(sb)->s_exfat_flags); +} + /* * If ->i_mode can't hold 0222 (i.e. ATTR_RO), we use ->i_attrs to * save ATTR_RO instead of ->i_mode. @@ -357,10 +366,10 @@ static inline int exfat_mode_can_hold_ro(struct inode *inode) static inline mode_t exfat_make_mode(struct exfat_sb_info *sbi, unsigned short attr, mode_t mode) { - if ((attr & ATTR_READONLY) && !(attr & ATTR_SUBDIR)) + if ((attr & EXFAT_ATTR_READONLY) && !(attr & EXFAT_ATTR_SUBDIR)) mode &= ~0222; - if (attr & ATTR_SUBDIR) + if (attr & EXFAT_ATTR_SUBDIR) return (mode & ~sbi->options.fs_dmask) | S_IFDIR; return (mode & ~sbi->options.fs_fmask) | S_IFREG; @@ -372,18 +381,18 @@ static inline unsigned short exfat_make_attr(struct inode *inode) unsigned short attr = EXFAT_I(inode)->attr; if (S_ISDIR(inode->i_mode)) - attr |= ATTR_SUBDIR; + attr |= EXFAT_ATTR_SUBDIR; if (exfat_mode_can_hold_ro(inode) && !(inode->i_mode & 0222)) - attr |= ATTR_READONLY; + attr |= EXFAT_ATTR_READONLY; return attr; } static inline void exfat_save_attr(struct inode *inode, unsigned short attr) { if (exfat_mode_can_hold_ro(inode)) - EXFAT_I(inode)->attr = attr & (ATTR_RWMASK | ATTR_READONLY); + EXFAT_I(inode)->attr = attr & (EXFAT_ATTR_RWMASK | EXFAT_ATTR_READONLY); else - EXFAT_I(inode)->attr = attr & ATTR_RWMASK; + EXFAT_I(inode)->attr = attr & EXFAT_ATTR_RWMASK; } static inline bool exfat_is_last_sector_in_cluster(struct exfat_sb_info *sbi, @@ -413,6 +422,11 @@ static inline bool is_valid_cluster(struct exfat_sb_info *sbi, return clus >= EXFAT_FIRST_CLUSTER && clus < sbi->num_clusters; } +static inline loff_t exfat_ondisk_size(const struct inode *inode) +{ + return ((loff_t)inode->i_blocks) << 9; +} + /* super.c */ int exfat_set_volume_dirty(struct super_block *sb); int exfat_clear_volume_dirty(struct super_block *sb); @@ -427,8 +441,6 @@ int exfat_ent_get(struct super_block *sb, unsigned int loc, unsigned int *content); int exfat_ent_set(struct super_block *sb, unsigned int loc, unsigned int content); -int exfat_count_ext_entries(struct super_block *sb, struct exfat_chain *p_dir, - int entry, struct exfat_dentry *p_entry); int exfat_chain_cont_cluster(struct super_block *sb, unsigned int chain, unsigned int len); int exfat_zeroed_cluster(struct inode *dir, unsigned int clu); @@ -440,8 +452,9 @@ int exfat_count_num_clusters(struct super_block *sb, /* balloc.c */ int exfat_load_bitmap(struct super_block *sb); void exfat_free_bitmap(struct exfat_sb_info *sbi); -int exfat_set_bitmap(struct inode *inode, unsigned int clu, bool sync); -void exfat_clear_bitmap(struct inode *inode, unsigned int clu, bool sync); +int exfat_set_bitmap(struct super_block *sb, unsigned int clu, bool sync); +int exfat_clear_bitmap(struct super_block *sb, unsigned int clu, bool sync); +bool exfat_test_bitmap(struct super_block *sb, unsigned int clu); unsigned int exfat_find_free_bitmap(struct super_block *sb, unsigned int clu); int exfat_count_used_clusters(struct super_block *sb, unsigned int *ret_count); int exfat_trim_fs(struct inode *inode, struct fstrim_range *range); @@ -450,19 +463,23 @@ int exfat_trim_fs(struct inode *inode, struct fstrim_range *range); extern const struct file_operations exfat_file_operations; int __exfat_truncate(struct inode *inode); void exfat_truncate(struct inode *inode); -int exfat_setattr(struct user_namespace *mnt_userns, struct dentry *dentry, +int exfat_setattr(struct mnt_idmap *idmap, struct dentry *dentry, struct iattr *attr); -int exfat_getattr(struct user_namespace *mnt_userns, const struct path *path, +int exfat_getattr(struct mnt_idmap *idmap, const struct path *path, struct kstat *stat, unsigned int request_mask, unsigned int query_flags); int exfat_file_fsync(struct file *file, loff_t start, loff_t end, int datasync); long exfat_ioctl(struct file *filp, unsigned int cmd, unsigned long arg); long exfat_compat_ioctl(struct file *filp, unsigned int cmd, unsigned long arg); +int exfat_force_shutdown(struct super_block *sb, u32 flags); /* namei.c */ extern const struct dentry_operations exfat_dentry_ops; extern const struct dentry_operations exfat_utf8_dentry_ops; +int exfat_find_empty_entry(struct inode *inode, + struct exfat_chain *p_dir, int num_entries, + struct exfat_entry_set_cache *es); /* cache.c */ int exfat_cache_init(void); @@ -476,16 +493,14 @@ int exfat_get_cluster(struct inode *inode, unsigned int cluster, extern const struct inode_operations exfat_dir_inode_operations; extern const struct file_operations exfat_dir_operations; unsigned int exfat_get_entry_type(struct exfat_dentry *p_entry); -int exfat_init_dir_entry(struct inode *inode, struct exfat_chain *p_dir, - int entry, unsigned int type, unsigned int start_clu, - unsigned long long size); -int exfat_init_ext_entry(struct inode *inode, struct exfat_chain *p_dir, - int entry, int num_entries, struct exfat_uni_name *p_uniname); -int exfat_remove_entries(struct inode *inode, struct exfat_chain *p_dir, - int entry, int order, int num_entries); -int exfat_update_dir_chksum(struct inode *inode, struct exfat_chain *p_dir, - int entry); -void exfat_update_dir_chksum_with_entry_set(struct exfat_entry_set_cache *es); +void exfat_init_dir_entry(struct exfat_entry_set_cache *es, + unsigned int type, unsigned int start_clu, + unsigned long long size, struct timespec64 *ts); +void exfat_init_ext_entry(struct exfat_entry_set_cache *es, int num_entries, + struct exfat_uni_name *p_uniname); +void exfat_remove_entries(struct inode *inode, struct exfat_entry_set_cache *es, + int order); +void exfat_update_dir_chksum(struct exfat_entry_set_cache *es); int exfat_calc_num_entries(struct exfat_uni_name *p_uniname); int exfat_find_dir_entry(struct super_block *sb, struct exfat_inode_info *ei, struct exfat_chain *p_dir, struct exfat_uni_name *p_uniname, @@ -497,9 +512,18 @@ struct exfat_dentry *exfat_get_dentry_cached(struct exfat_entry_set_cache *es, int num); int exfat_get_dentry_set(struct exfat_entry_set_cache *es, struct super_block *sb, struct exfat_chain *p_dir, int entry, - unsigned int type); + unsigned int num_entries); +#define exfat_get_dentry_set_by_ei(es, sb, ei) \ + exfat_get_dentry_set(es, sb, &(ei)->dir, (ei)->entry, ES_ALL_ENTRIES) +int exfat_get_empty_dentry_set(struct exfat_entry_set_cache *es, + struct super_block *sb, struct exfat_chain *p_dir, int entry, + unsigned int num_entries); int exfat_put_dentry_set(struct exfat_entry_set_cache *es, int sync); int exfat_count_dir_entries(struct super_block *sb, struct exfat_chain *p_dir); +int exfat_read_volume_label(struct super_block *sb, + struct exfat_uni_name *label_out); +int exfat_write_volume_label(struct super_block *sb, + struct exfat_uni_name *label); /* inode.c */ extern const struct inode_operations exfat_file_inode_operations; @@ -549,6 +573,7 @@ void __exfat_fs_error(struct super_block *sb, int report, const char *fmt, ...) void exfat_get_entry_time(struct exfat_sb_info *sbi, struct timespec64 *ts, u8 tz, __le16 time, __le16 date, u8 time_cs); void exfat_truncate_atime(struct timespec64 *ts); +void exfat_truncate_inode_atime(struct inode *inode); void exfat_set_entry_time(struct exfat_sb_info *sbi, struct timespec64 *ts, u8 *tz, __le16 *time, __le16 *date, u8 *time_cs); u16 exfat_calc_chksum16(void *data, int len, u16 chksum, int type); diff --git a/fs/exfat/exfat_raw.h b/fs/exfat/exfat_raw.h index 7f39b1c6469c..4082fa7b8c14 100644 --- a/fs/exfat/exfat_raw.h +++ b/fs/exfat/exfat_raw.h @@ -27,6 +27,7 @@ ((sbi)->num_clusters - EXFAT_RESERVED_CLUSTERS) /* AllocationPossible and NoFatChain field in GeneralSecondaryFlags Field */ +#define ALLOC_POSSIBLE 0x01 #define ALLOC_FAT_CHAIN 0x01 #define ALLOC_NO_FAT_CHAIN 0x03 @@ -50,6 +51,8 @@ #define EXFAT_STREAM 0xC0 /* stream entry */ #define EXFAT_NAME 0xC1 /* file name entry */ #define EXFAT_ACL 0xC2 /* stream entry */ +#define EXFAT_VENDOR_EXT 0xE0 /* vendor extension entry */ +#define EXFAT_VENDOR_ALLOC 0xE1 /* vendor allocation entry */ #define IS_EXFAT_CRITICAL_PRI(x) (x < 0xA0) #define IS_EXFAT_BENIGN_PRI(x) (x < 0xC0) @@ -61,21 +64,23 @@ #define CS_DEFAULT 2 /* file attributes */ -#define ATTR_READONLY 0x0001 -#define ATTR_HIDDEN 0x0002 -#define ATTR_SYSTEM 0x0004 -#define ATTR_VOLUME 0x0008 -#define ATTR_SUBDIR 0x0010 -#define ATTR_ARCHIVE 0x0020 +#define EXFAT_ATTR_READONLY 0x0001 +#define EXFAT_ATTR_HIDDEN 0x0002 +#define EXFAT_ATTR_SYSTEM 0x0004 +#define EXFAT_ATTR_VOLUME 0x0008 +#define EXFAT_ATTR_SUBDIR 0x0010 +#define EXFAT_ATTR_ARCHIVE 0x0020 -#define ATTR_RWMASK (ATTR_HIDDEN | ATTR_SYSTEM | ATTR_VOLUME | \ - ATTR_SUBDIR | ATTR_ARCHIVE) +#define EXFAT_ATTR_RWMASK (EXFAT_ATTR_HIDDEN | EXFAT_ATTR_SYSTEM | \ + EXFAT_ATTR_VOLUME | EXFAT_ATTR_SUBDIR | \ + EXFAT_ATTR_ARCHIVE) #define BOOTSEC_JUMP_BOOT_LEN 3 #define BOOTSEC_FS_NAME_LEN 8 #define BOOTSEC_OLDBPB_LEN 53 #define EXFAT_FILE_NAME_LEN 15 +#define EXFAT_VOLUME_LABEL_LEN 11 #define EXFAT_MIN_SECT_SIZE_BITS 9 #define EXFAT_MAX_SECT_SIZE_BITS 12 @@ -155,6 +160,29 @@ struct exfat_dentry { __le32 start_clu; __le64 size; } __packed upcase; /* up-case table directory entry */ + struct { + __u8 char_count; + __le16 volume_label[EXFAT_VOLUME_LABEL_LEN]; + __u8 reserved[8]; + } __packed volume_label; /* volume label directory entry */ + struct { + __u8 flags; + __u8 vendor_guid[16]; + __u8 vendor_defined[14]; + } __packed vendor_ext; /* vendor extension directory entry */ + struct { + __u8 flags; + __u8 vendor_guid[16]; + __u8 vendor_defined[2]; + __le32 start_clu; + __le64 size; + } __packed vendor_alloc; /* vendor allocation directory entry */ + struct { + __u8 flags; + __u8 custom_defined[18]; + __le32 start_clu; + __le64 size; + } __packed generic_secondary; /* generic secondary directory entry */ } __packed dentry; } __packed; diff --git a/fs/exfat/fatent.c b/fs/exfat/fatent.c index 41ae4cce1f42..c9c5f2e3a05e 100644 --- a/fs/exfat/fatent.c +++ b/fs/exfat/fatent.c @@ -4,7 +4,7 @@ */ #include <linux/slab.h> -#include <asm/unaligned.h> +#include <linux/unaligned.h> #include <linux/buffer_head.h> #include <linux/blkdev.h> @@ -89,35 +89,36 @@ int exfat_ent_get(struct super_block *sb, unsigned int loc, int err; if (!is_valid_cluster(sbi, loc)) { - exfat_fs_error(sb, "invalid access to FAT (entry 0x%08x)", + exfat_fs_error_ratelimit(sb, + "invalid access to FAT (entry 0x%08x)", loc); return -EIO; } err = __exfat_ent_get(sb, loc, content); if (err) { - exfat_fs_error(sb, + exfat_fs_error_ratelimit(sb, "failed to access to FAT (entry 0x%08x, err:%d)", loc, err); return err; } if (*content == EXFAT_FREE_CLUSTER) { - exfat_fs_error(sb, + exfat_fs_error_ratelimit(sb, "invalid access to FAT free cluster (entry 0x%08x)", loc); return -EIO; } if (*content == EXFAT_BAD_CLUSTER) { - exfat_fs_error(sb, + exfat_fs_error_ratelimit(sb, "invalid access to FAT bad cluster (entry 0x%08x)", loc); return -EIO; } if (*content != EXFAT_EOF_CLUSTER && !is_valid_cluster(sbi, *content)) { - exfat_fs_error(sb, + exfat_fs_error_ratelimit(sb, "invalid access to FAT (entry 0x%08x) bogus content (0x%08x)", loc, *content); return -EIO; @@ -144,6 +145,20 @@ int exfat_chain_cont_cluster(struct super_block *sb, unsigned int chain, return 0; } +static inline void exfat_discard_cluster(struct super_block *sb, + unsigned int clu, unsigned int num_clusters) +{ + int ret; + struct exfat_sb_info *sbi = EXFAT_SB(sb); + + ret = sb_issue_discard(sb, exfat_cluster_to_sector(sbi, clu), + sbi->sect_per_clus * num_clusters, GFP_NOFS, 0); + if (ret == -EOPNOTSUPP) { + exfat_err(sb, "discard not supported by device, disabling"); + sbi->options.discard = 0; + } +} + /* This function must be called with bitmap_lock held */ static int __exfat_free_cluster(struct inode *inode, struct exfat_chain *p_chain) { @@ -175,6 +190,7 @@ static int __exfat_free_cluster(struct inode *inode, struct exfat_chain *p_chain BITMAP_OFFSET_SECTOR_INDEX(sb, CLUSTER_TO_BITMAP_ENT(clu)); if (p_chain->flags == ALLOC_NO_FAT_CHAIN) { + int err; unsigned int last_cluster = p_chain->dir + p_chain->size - 1; do { bool sync = false; @@ -189,11 +205,18 @@ static int __exfat_free_cluster(struct inode *inode, struct exfat_chain *p_chain cur_cmap_i = next_cmap_i; } - exfat_clear_bitmap(inode, clu, (sync && IS_DIRSYNC(inode))); + err = exfat_clear_bitmap(sb, clu, (sync && IS_DIRSYNC(inode))); + if (err) + break; clu++; num_clusters++; } while (num_clusters < p_chain->size); + + if (sbi->options.discard) + exfat_discard_cluster(sb, p_chain->dir, p_chain->size); } else { + unsigned int nr_clu = 1; + do { bool sync = false; unsigned int n_clu = clu; @@ -210,16 +233,36 @@ static int __exfat_free_cluster(struct inode *inode, struct exfat_chain *p_chain cur_cmap_i = next_cmap_i; } - exfat_clear_bitmap(inode, clu, (sync && IS_DIRSYNC(inode))); + if (exfat_clear_bitmap(sb, clu, (sync && IS_DIRSYNC(inode)))) + break; + + if (sbi->options.discard) { + if (n_clu == clu + 1) + nr_clu++; + else { + exfat_discard_cluster(sb, clu - nr_clu + 1, nr_clu); + nr_clu = 1; + } + } + clu = n_clu; num_clusters++; if (err) - goto dec_used_clus; + break; + + if (num_clusters >= sbi->num_clusters - EXFAT_FIRST_CLUSTER) { + /* + * The cluster chain includes a loop, scan the + * bitmap to get the number of used clusters. + */ + exfat_count_used_clusters(sb, &sbi->used_clusters); + + return 0; + } } while (clu != EXFAT_EOF_CLUSTER); } -dec_used_clus: sbi->used_clusters -= num_clusters; return 0; } @@ -252,7 +295,7 @@ int exfat_find_last_cluster(struct super_block *sb, struct exfat_chain *p_chain, clu = next; if (exfat_ent_get(sb, clu, &next)) return -EIO; - } while (next != EXFAT_EOF_CLUSTER); + } while (next != EXFAT_EOF_CLUSTER && count <= p_chain->size); if (p_chain->size != count) { exfat_fs_error(sb, @@ -307,7 +350,7 @@ int exfat_alloc_cluster(struct inode *inode, unsigned int num_alloc, struct exfat_chain *p_chain, bool sync_bmap) { int ret = -ENOSPC; - unsigned int num_clusters = 0, total_cnt; + unsigned int total_cnt; unsigned int hint_clu, new_clu, last_clu = EXFAT_EOF_CLUSTER; struct super_block *sb = inode->i_sb; struct exfat_sb_info *sbi = EXFAT_SB(sb); @@ -344,17 +387,11 @@ int exfat_alloc_cluster(struct inode *inode, unsigned int num_alloc, /* check cluster validation */ if (!is_valid_cluster(sbi, hint_clu)) { - exfat_err(sb, "hint_cluster is invalid (%u)", - hint_clu); + if (hint_clu != sbi->num_clusters) + exfat_err(sb, "hint_cluster is invalid (%u), rewind to the first cluster", + hint_clu); hint_clu = EXFAT_FIRST_CLUSTER; - if (p_chain->flags == ALLOC_NO_FAT_CHAIN) { - if (exfat_chain_cont_cluster(sb, p_chain->dir, - num_clusters)) { - ret = -EIO; - goto unlock; - } - p_chain->flags = ALLOC_FAT_CHAIN; - } + p_chain->flags = ALLOC_FAT_CHAIN; } p_chain->dir = EXFAT_EOF_CLUSTER; @@ -364,7 +401,7 @@ int exfat_alloc_cluster(struct inode *inode, unsigned int num_alloc, if (new_clu != hint_clu && p_chain->flags == ALLOC_NO_FAT_CHAIN) { if (exfat_chain_cont_cluster(sb, p_chain->dir, - num_clusters)) { + p_chain->size)) { ret = -EIO; goto free_cluster; } @@ -372,13 +409,11 @@ int exfat_alloc_cluster(struct inode *inode, unsigned int num_alloc, } /* update allocation bitmap */ - if (exfat_set_bitmap(inode, new_clu, sync_bmap)) { + if (exfat_set_bitmap(sb, new_clu, sync_bmap)) { ret = -EIO; goto free_cluster; } - num_clusters++; - /* update FAT table */ if (p_chain->flags == ALLOC_FAT_CHAIN) { if (exfat_ent_set(sb, new_clu, EXFAT_EOF_CLUSTER)) { @@ -395,13 +430,14 @@ int exfat_alloc_cluster(struct inode *inode, unsigned int num_alloc, goto free_cluster; } } + p_chain->size++; + last_clu = new_clu; - if (--num_alloc == 0) { + if (p_chain->size == num_alloc) { sbi->clu_srch_ptr = hint_clu; - sbi->used_clusters += num_clusters; + sbi->used_clusters += num_alloc; - p_chain->size += num_clusters; mutex_unlock(&sbi->bitmap_lock); return 0; } @@ -412,7 +448,7 @@ int exfat_alloc_cluster(struct inode *inode, unsigned int num_alloc, if (p_chain->flags == ALLOC_NO_FAT_CHAIN) { if (exfat_chain_cont_cluster(sb, p_chain->dir, - num_clusters)) { + p_chain->size)) { ret = -EIO; goto free_cluster; } @@ -421,8 +457,7 @@ int exfat_alloc_cluster(struct inode *inode, unsigned int num_alloc, } } free_cluster: - if (num_clusters) - __exfat_free_cluster(inode, p_chain); + __exfat_free_cluster(inode, p_chain); unlock: mutex_unlock(&sbi->bitmap_lock); return ret; @@ -456,5 +491,15 @@ int exfat_count_num_clusters(struct super_block *sb, } *ret_count = count; + + /* + * since exfat_count_used_clusters() is not called, sbi->used_clusters + * cannot be used here. + */ + if (unlikely(i == sbi->num_clusters && clu != EXFAT_EOF_CLUSTER)) { + exfat_fs_error(sb, "The cluster chain has a loop"); + return -EIO; + } + return 0; } diff --git a/fs/exfat/file.c b/fs/exfat/file.c index f5b29072775d..536c8078f0c1 100644 --- a/fs/exfat/file.c +++ b/fs/exfat/file.c @@ -8,45 +8,95 @@ #include <linux/cred.h> #include <linux/buffer_head.h> #include <linux/blkdev.h> +#include <linux/fsnotify.h> +#include <linux/security.h> +#include <linux/msdos_fs.h> +#include <linux/writeback.h> #include "exfat_raw.h" #include "exfat_fs.h" static int exfat_cont_expand(struct inode *inode, loff_t size) { - struct address_space *mapping = inode->i_mapping; - loff_t start = i_size_read(inode), count = size - i_size_read(inode); - int err, err2; + int ret; + unsigned int num_clusters, new_num_clusters, last_clu; + struct exfat_inode_info *ei = EXFAT_I(inode); + struct super_block *sb = inode->i_sb; + struct exfat_sb_info *sbi = EXFAT_SB(sb); + struct exfat_chain clu; - err = generic_cont_expand_simple(inode, size); - if (err) - return err; + truncate_pagecache(inode, i_size_read(inode)); + + ret = inode_newsize_ok(inode, size); + if (ret) + return ret; + + num_clusters = EXFAT_B_TO_CLU(exfat_ondisk_size(inode), sbi); + new_num_clusters = EXFAT_B_TO_CLU_ROUND_UP(size, sbi); + + if (new_num_clusters == num_clusters) + goto out; + + if (num_clusters) { + exfat_chain_set(&clu, ei->start_clu, num_clusters, ei->flags); + ret = exfat_find_last_cluster(sb, &clu, &last_clu); + if (ret) + return ret; + + clu.dir = last_clu + 1; + } else { + last_clu = EXFAT_EOF_CLUSTER; + clu.dir = EXFAT_EOF_CLUSTER; + } + + clu.size = 0; + clu.flags = ei->flags; + + ret = exfat_alloc_cluster(inode, new_num_clusters - num_clusters, + &clu, inode_needs_sync(inode)); + if (ret) + return ret; + + /* Append new clusters to chain */ + if (num_clusters) { + if (clu.flags != ei->flags) + if (exfat_chain_cont_cluster(sb, ei->start_clu, num_clusters)) + goto free_clu; + + if (clu.flags == ALLOC_FAT_CHAIN) + if (exfat_ent_set(sb, last_clu, clu.dir)) + goto free_clu; + } else + ei->start_clu = clu.dir; + + ei->flags = clu.flags; - inode->i_ctime = inode->i_mtime = current_time(inode); +out: + inode_set_mtime_to_ts(inode, inode_set_ctime_current(inode)); + /* Expanded range not zeroed, do not update valid_size */ + i_size_write(inode, size); + + inode->i_blocks = round_up(size, sbi->cluster_size) >> 9; mark_inode_dirty(inode); - if (!IS_SYNC(inode)) - return 0; + if (IS_SYNC(inode)) + return write_inode_now(inode, 1); - err = filemap_fdatawrite_range(mapping, start, start + count - 1); - err2 = sync_mapping_buffers(mapping); - if (!err) - err = err2; - err2 = write_inode_now(inode, 1); - if (!err) - err = err2; - if (err) - return err; + return 0; - return filemap_fdatawait_range(mapping, start, start + count - 1); +free_clu: + exfat_free_cluster(inode, &clu); + return -EIO; } -static bool exfat_allow_set_time(struct exfat_sb_info *sbi, struct inode *inode) +static bool exfat_allow_set_time(struct mnt_idmap *idmap, + struct exfat_sb_info *sbi, struct inode *inode) { mode_t allow_utime = sbi->options.allow_utime; - if (!uid_eq(current_fsuid(), inode->i_uid)) { - if (in_group_p(inode->i_gid)) + if (!vfsuid_eq_kuid(i_uid_into_vfsuid(idmap, inode), + current_fsuid())) { + if (vfsgid_in_group_p(i_gid_into_vfsgid(idmap, inode))) allow_utime >>= 3; if (allow_utime & MAY_WRITE) return true; @@ -109,7 +159,7 @@ int __exfat_truncate(struct inode *inode) exfat_set_volume_dirty(sb); num_clusters_new = EXFAT_B_TO_CLU_ROUND_UP(i_size_read(inode), sbi); - num_clusters_phys = EXFAT_B_TO_CLU_ROUND_UP(ei->i_size_ondisk, sbi); + num_clusters_phys = EXFAT_B_TO_CLU(exfat_ondisk_size(inode), sbi); exfat_chain_set(&clu, ei->start_clu, num_clusters_phys, ei->flags); @@ -143,8 +193,11 @@ int __exfat_truncate(struct inode *inode) ei->start_clu = EXFAT_EOF_CLUSTER; } + if (i_size_read(inode) < ei->valid_size) + ei->valid_size = i_size_read(inode); + if (ei->type == TYPE_FILE) - ei->attr |= ATTR_ARCHIVE; + ei->attr |= EXFAT_ATTR_ARCHIVE; /* * update the directory entry @@ -192,8 +245,6 @@ void exfat_truncate(struct inode *inode) struct super_block *sb = inode->i_sb; struct exfat_sb_info *sbi = EXFAT_SB(sb); struct exfat_inode_info *ei = EXFAT_I(inode); - unsigned int blocksize = i_blocksize(inode); - loff_t aligned_size; int err; mutex_lock(&sbi->s_lock); @@ -209,31 +260,19 @@ void exfat_truncate(struct inode *inode) if (err) goto write_size; - inode->i_blocks = round_up(i_size_read(inode), sbi->cluster_size) >> - inode->i_blkbits; + inode->i_blocks = round_up(i_size_read(inode), sbi->cluster_size) >> 9; write_size: - aligned_size = i_size_read(inode); - if (aligned_size & (blocksize - 1)) { - aligned_size |= (blocksize - 1); - aligned_size++; - } - - if (ei->i_size_ondisk > i_size_read(inode)) - ei->i_size_ondisk = aligned_size; - - if (ei->i_size_aligned > i_size_read(inode)) - ei->i_size_aligned = aligned_size; mutex_unlock(&sbi->s_lock); } -int exfat_getattr(struct user_namespace *mnt_uerns, const struct path *path, +int exfat_getattr(struct mnt_idmap *idmap, const struct path *path, struct kstat *stat, unsigned int request_mask, unsigned int query_flags) { struct inode *inode = d_backing_inode(path->dentry); struct exfat_inode_info *ei = EXFAT_I(inode); - generic_fillattr(&init_user_ns, inode, stat); + generic_fillattr(idmap, request_mask, inode, stat); exfat_truncate_atime(&stat->atime); stat->result_mask |= STATX_BTIME; stat->btime.tv_sec = ei->i_crtime.tv_sec; @@ -242,7 +281,7 @@ int exfat_getattr(struct user_namespace *mnt_uerns, const struct path *path, return 0; } -int exfat_setattr(struct user_namespace *mnt_userns, struct dentry *dentry, +int exfat_setattr(struct mnt_idmap *idmap, struct dentry *dentry, struct iattr *attr) { struct exfat_sb_info *sbi = EXFAT_SB(dentry->d_sb); @@ -250,6 +289,9 @@ int exfat_setattr(struct user_namespace *mnt_userns, struct dentry *dentry, unsigned int ia_valid; int error; + if (unlikely(exfat_forced_shutdown(inode->i_sb))) + return -EIO; + if ((attr->ia_valid & ATTR_SIZE) && attr->ia_size > i_size_read(inode)) { error = exfat_cont_expand(inode, attr->ia_size); @@ -261,20 +303,22 @@ int exfat_setattr(struct user_namespace *mnt_userns, struct dentry *dentry, /* Check for setting the inode time. */ ia_valid = attr->ia_valid; if ((ia_valid & (ATTR_MTIME_SET | ATTR_ATIME_SET | ATTR_TIMES_SET)) && - exfat_allow_set_time(sbi, inode)) { + exfat_allow_set_time(idmap, sbi, inode)) { attr->ia_valid &= ~(ATTR_MTIME_SET | ATTR_ATIME_SET | ATTR_TIMES_SET); } - error = setattr_prepare(&init_user_ns, dentry, attr); + error = setattr_prepare(idmap, dentry, attr); attr->ia_valid = ia_valid; if (error) goto out; if (((attr->ia_valid & ATTR_UID) && - !uid_eq(attr->ia_uid, sbi->options.fs_uid)) || + (!uid_eq(from_vfsuid(idmap, i_user_ns(inode), attr->ia_vfsuid), + sbi->options.fs_uid))) || ((attr->ia_valid & ATTR_GID) && - !gid_eq(attr->ia_gid, sbi->options.fs_gid)) || + (!gid_eq(from_vfsgid(idmap, i_user_ns(inode), attr->ia_vfsgid), + sbi->options.fs_gid))) || ((attr->ia_valid & ATTR_MODE) && (attr->ia_mode & ~(S_IFREG | S_IFLNK | S_IFDIR | 0777)))) { error = -EPERM; @@ -291,10 +335,10 @@ int exfat_setattr(struct user_namespace *mnt_userns, struct dentry *dentry, } if (attr->ia_valid & ATTR_SIZE) - inode->i_mtime = inode->i_ctime = current_time(inode); + inode_set_mtime_to_ts(inode, inode_set_ctime_current(inode)); - setattr_copy(&init_user_ns, inode, attr); - exfat_truncate_atime(&inode->i_atime); + setattr_copy(idmap, inode, attr); + exfat_truncate_inode_atime(inode); if (attr->ia_valid & ATTR_SIZE) { error = exfat_block_truncate_page(inode, attr->ia_size); @@ -317,6 +361,93 @@ out: return error; } +/* + * modified ioctls from fat/file.c by Welmer Almesberger + */ +static int exfat_ioctl_get_attributes(struct inode *inode, u32 __user *user_attr) +{ + u32 attr; + + inode_lock_shared(inode); + attr = exfat_make_attr(inode); + inode_unlock_shared(inode); + + return put_user(attr, user_attr); +} + +static int exfat_ioctl_set_attributes(struct file *file, u32 __user *user_attr) +{ + struct inode *inode = file_inode(file); + struct exfat_sb_info *sbi = EXFAT_SB(inode->i_sb); + int is_dir = S_ISDIR(inode->i_mode); + u32 attr, oldattr; + struct iattr ia; + int err; + + err = get_user(attr, user_attr); + if (err) + goto out; + + err = mnt_want_write_file(file); + if (err) + goto out; + inode_lock(inode); + + oldattr = exfat_make_attr(inode); + + /* + * Mask attributes so we don't set reserved fields. + */ + attr &= (EXFAT_ATTR_READONLY | EXFAT_ATTR_HIDDEN | EXFAT_ATTR_SYSTEM | + EXFAT_ATTR_ARCHIVE); + attr |= (is_dir ? EXFAT_ATTR_SUBDIR : 0); + + /* Equivalent to a chmod() */ + ia.ia_valid = ATTR_MODE | ATTR_CTIME; + ia.ia_ctime = current_time(inode); + if (is_dir) + ia.ia_mode = exfat_make_mode(sbi, attr, 0777); + else + ia.ia_mode = exfat_make_mode(sbi, attr, 0666 | (inode->i_mode & 0111)); + + /* The root directory has no attributes */ + if (inode->i_ino == EXFAT_ROOT_INO && attr != EXFAT_ATTR_SUBDIR) { + err = -EINVAL; + goto out_unlock_inode; + } + + if (((attr | oldattr) & EXFAT_ATTR_SYSTEM) && + !capable(CAP_LINUX_IMMUTABLE)) { + err = -EPERM; + goto out_unlock_inode; + } + + /* + * The security check is questionable... We single + * out the RO attribute for checking by the security + * module, just because it maps to a file mode. + */ + err = security_inode_setattr(file_mnt_idmap(file), + file->f_path.dentry, &ia); + if (err) + goto out_unlock_inode; + + /* This MUST be done before doing anything irreversible... */ + err = exfat_setattr(file_mnt_idmap(file), file->f_path.dentry, &ia); + if (err) + goto out_unlock_inode; + + fsnotify_change(file->f_path.dentry, ia.ia_valid); + + exfat_save_attr(inode, attr); + mark_inode_dirty(inode); +out_unlock_inode: + inode_unlock(inode); + mnt_drop_write_file(file); +out: + return err; +} + static int exfat_ioctl_fitrim(struct inode *inode, unsigned long arg) { struct fstrim_range range; @@ -344,13 +475,86 @@ static int exfat_ioctl_fitrim(struct inode *inode, unsigned long arg) return 0; } +static int exfat_ioctl_shutdown(struct super_block *sb, unsigned long arg) +{ + u32 flags; + + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + + if (get_user(flags, (__u32 __user *)arg)) + return -EFAULT; + + return exfat_force_shutdown(sb, flags); +} + +static int exfat_ioctl_get_volume_label(struct super_block *sb, unsigned long arg) +{ + int ret; + char label[FSLABEL_MAX] = {0}; + struct exfat_uni_name uniname; + + ret = exfat_read_volume_label(sb, &uniname); + if (ret < 0) + return ret; + + ret = exfat_utf16_to_nls(sb, &uniname, label, uniname.name_len); + if (ret < 0) + return ret; + + if (copy_to_user((char __user *)arg, label, ret + 1)) + return -EFAULT; + + return 0; +} + +static int exfat_ioctl_set_volume_label(struct super_block *sb, + unsigned long arg) +{ + int ret = 0, lossy, label_len; + char label[FSLABEL_MAX] = {0}; + struct exfat_uni_name uniname; + + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + + if (copy_from_user(label, (char __user *)arg, FSLABEL_MAX)) + return -EFAULT; + + memset(&uniname, 0, sizeof(uniname)); + label_len = strnlen(label, FSLABEL_MAX - 1); + if (label[0]) { + ret = exfat_nls_to_utf16(sb, label, label_len, + &uniname, &lossy); + if (ret < 0) + return ret; + else if (lossy & NLS_NAME_LOSSY) + return -EINVAL; + } + + uniname.name_len = ret; + + return exfat_write_volume_label(sb, &uniname); +} + long exfat_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { struct inode *inode = file_inode(filp); + u32 __user *user_attr = (u32 __user *)arg; switch (cmd) { + case FAT_IOCTL_GET_ATTRIBUTES: + return exfat_ioctl_get_attributes(inode, user_attr); + case FAT_IOCTL_SET_ATTRIBUTES: + return exfat_ioctl_set_attributes(filp, user_attr); + case EXFAT_IOC_SHUTDOWN: + return exfat_ioctl_shutdown(inode->i_sb, arg); case FITRIM: return exfat_ioctl_fitrim(inode, arg); + case FS_IOC_GETFSLABEL: + return exfat_ioctl_get_volume_label(inode->i_sb, arg); + case FS_IOC_SETFSLABEL: + return exfat_ioctl_set_volume_label(inode->i_sb, arg); default: return -ENOTTY; } @@ -369,6 +573,9 @@ int exfat_file_fsync(struct file *filp, loff_t start, loff_t end, int datasync) struct inode *inode = filp->f_mapping->host; int err; + if (unlikely(exfat_forced_shutdown(inode->i_sb))) + return -EIO; + err = __generic_file_fsync(filp, start, end, datasync); if (err) return err; @@ -380,17 +587,190 @@ int exfat_file_fsync(struct file *filp, loff_t start, loff_t end, int datasync) return blkdev_issue_flush(inode->i_sb->s_bdev); } +static int exfat_extend_valid_size(struct inode *inode, loff_t new_valid_size) +{ + int err; + loff_t pos; + struct exfat_inode_info *ei = EXFAT_I(inode); + struct address_space *mapping = inode->i_mapping; + const struct address_space_operations *ops = mapping->a_ops; + + pos = ei->valid_size; + while (pos < new_valid_size) { + u32 len; + struct folio *folio; + unsigned long off; + + len = PAGE_SIZE - (pos & (PAGE_SIZE - 1)); + if (pos + len > new_valid_size) + len = new_valid_size - pos; + + err = ops->write_begin(NULL, mapping, pos, len, &folio, NULL); + if (err) + goto out; + + off = offset_in_folio(folio, pos); + folio_zero_new_buffers(folio, off, off + len); + + err = ops->write_end(NULL, mapping, pos, len, len, folio, NULL); + if (err < 0) + goto out; + pos += len; + + balance_dirty_pages_ratelimited(mapping); + cond_resched(); + } + + return 0; + +out: + return err; +} + +static ssize_t exfat_file_write_iter(struct kiocb *iocb, struct iov_iter *iter) +{ + ssize_t ret; + struct file *file = iocb->ki_filp; + struct inode *inode = file_inode(file); + struct exfat_inode_info *ei = EXFAT_I(inode); + loff_t pos = iocb->ki_pos; + loff_t valid_size; + + if (unlikely(exfat_forced_shutdown(inode->i_sb))) + return -EIO; + + inode_lock(inode); + + if (pos > i_size_read(inode)) + truncate_pagecache(inode, i_size_read(inode)); + + valid_size = ei->valid_size; + + ret = generic_write_checks(iocb, iter); + if (ret <= 0) + goto unlock; + + if (iocb->ki_flags & IOCB_DIRECT) { + unsigned long align = pos | iov_iter_alignment(iter); + + if (!IS_ALIGNED(align, i_blocksize(inode)) && + !IS_ALIGNED(align, bdev_logical_block_size(inode->i_sb->s_bdev))) { + ret = -EINVAL; + goto unlock; + } + } + + if (pos > valid_size) { + ret = exfat_extend_valid_size(inode, pos); + if (ret < 0 && ret != -ENOSPC) { + exfat_err(inode->i_sb, + "write: fail to zero from %llu to %llu(%zd)", + valid_size, pos, ret); + } + if (ret < 0) + goto unlock; + } + + ret = __generic_file_write_iter(iocb, iter); + if (ret < 0) + goto unlock; + + inode_unlock(inode); + + if (pos > valid_size) + pos = valid_size; + + if (iocb->ki_pos > pos) { + ssize_t err = generic_write_sync(iocb, iocb->ki_pos - pos); + if (err < 0) + return err; + } + + return ret; + +unlock: + inode_unlock(inode); + + return ret; +} + +static ssize_t exfat_file_read_iter(struct kiocb *iocb, struct iov_iter *iter) +{ + struct inode *inode = file_inode(iocb->ki_filp); + + if (unlikely(exfat_forced_shutdown(inode->i_sb))) + return -EIO; + + return generic_file_read_iter(iocb, iter); +} + +static vm_fault_t exfat_page_mkwrite(struct vm_fault *vmf) +{ + int err; + struct vm_area_struct *vma = vmf->vma; + struct file *file = vma->vm_file; + struct inode *inode = file_inode(file); + struct exfat_inode_info *ei = EXFAT_I(inode); + loff_t start, end; + + if (!inode_trylock(inode)) + return VM_FAULT_RETRY; + + start = ((loff_t)vma->vm_pgoff << PAGE_SHIFT); + end = min_t(loff_t, i_size_read(inode), + start + vma->vm_end - vma->vm_start); + + if (ei->valid_size < end) { + err = exfat_extend_valid_size(inode, end); + if (err < 0) { + inode_unlock(inode); + return vmf_fs_error(err); + } + } + + inode_unlock(inode); + + return filemap_page_mkwrite(vmf); +} + +static const struct vm_operations_struct exfat_file_vm_ops = { + .fault = filemap_fault, + .map_pages = filemap_map_pages, + .page_mkwrite = exfat_page_mkwrite, +}; + +static int exfat_file_mmap_prepare(struct vm_area_desc *desc) +{ + struct file *file = desc->file; + + if (unlikely(exfat_forced_shutdown(file_inode(desc->file)->i_sb))) + return -EIO; + + file_accessed(file); + desc->vm_ops = &exfat_file_vm_ops; + return 0; +} + +static ssize_t exfat_splice_read(struct file *in, loff_t *ppos, + struct pipe_inode_info *pipe, size_t len, unsigned int flags) +{ + if (unlikely(exfat_forced_shutdown(file_inode(in)->i_sb))) + return -EIO; + + return filemap_splice_read(in, ppos, pipe, len, flags); +} + const struct file_operations exfat_file_operations = { .llseek = generic_file_llseek, - .read_iter = generic_file_read_iter, - .write_iter = generic_file_write_iter, + .read_iter = exfat_file_read_iter, + .write_iter = exfat_file_write_iter, .unlocked_ioctl = exfat_ioctl, #ifdef CONFIG_COMPAT .compat_ioctl = exfat_compat_ioctl, #endif - .mmap = generic_file_mmap, + .mmap_prepare = exfat_file_mmap_prepare, .fsync = exfat_file_fsync, - .splice_read = generic_file_splice_read, + .splice_read = exfat_splice_read, .splice_write = iter_file_splice_write, }; diff --git a/fs/exfat/inode.c b/fs/exfat/inode.c index 5b644cb057fa..f9501c3a3666 100644 --- a/fs/exfat/inode.c +++ b/fs/exfat/inode.c @@ -25,7 +25,8 @@ int __exfat_write_inode(struct inode *inode, int sync) struct super_block *sb = inode->i_sb; struct exfat_sb_info *sbi = EXFAT_SB(sb); struct exfat_inode_info *ei = EXFAT_I(inode); - bool is_dir = (ei->type == TYPE_DIR) ? true : false; + bool is_dir = (ei->type == TYPE_DIR); + struct timespec64 ts; if (inode->i_ino == EXFAT_ROOT_INO) return 0; @@ -42,7 +43,7 @@ int __exfat_write_inode(struct inode *inode, int sync) exfat_set_volume_dirty(sb); /* get the directory entry of given file or directory */ - if (exfat_get_dentry_set(&es, sb, &(ei->dir), ei->entry, ES_ALL_ENTRIES)) + if (exfat_get_dentry_set_by_ei(&es, sb, ei)) return -EIO; ep = exfat_get_dentry_cached(&es, ES_IDX_FILE); ep2 = exfat_get_dentry_cached(&es, ES_IDX_STREAM); @@ -55,16 +56,18 @@ int __exfat_write_inode(struct inode *inode, int sync) &ep->dentry.file.create_time, &ep->dentry.file.create_date, &ep->dentry.file.create_time_cs); - exfat_set_entry_time(sbi, &inode->i_mtime, - &ep->dentry.file.modify_tz, - &ep->dentry.file.modify_time, - &ep->dentry.file.modify_date, - &ep->dentry.file.modify_time_cs); - exfat_set_entry_time(sbi, &inode->i_atime, - &ep->dentry.file.access_tz, - &ep->dentry.file.access_time, - &ep->dentry.file.access_date, - NULL); + ts = inode_get_mtime(inode); + exfat_set_entry_time(sbi, &ts, + &ep->dentry.file.modify_tz, + &ep->dentry.file.modify_time, + &ep->dentry.file.modify_date, + &ep->dentry.file.modify_time_cs); + ts = inode_get_atime(inode); + exfat_set_entry_time(sbi, &ts, + &ep->dentry.file.access_tz, + &ep->dentry.file.access_time, + &ep->dentry.file.access_date, + NULL); /* File size should be zero if there is no cluster allocated */ on_disk_size = i_size_read(inode); @@ -72,8 +75,17 @@ int __exfat_write_inode(struct inode *inode, int sync) if (ei->start_clu == EXFAT_EOF_CLUSTER) on_disk_size = 0; - ep2->dentry.stream.valid_size = cpu_to_le64(on_disk_size); - ep2->dentry.stream.size = ep2->dentry.stream.valid_size; + ep2->dentry.stream.size = cpu_to_le64(on_disk_size); + /* + * mmap write does not use exfat_write_end(), valid_size may be + * extended to the sector-aligned length in exfat_get_block(). + * So we need to fixup valid_size to the writren length. + */ + if (on_disk_size < ei->valid_size) + ep2->dentry.stream.valid_size = ep2->dentry.stream.size; + else + ep2->dentry.stream.valid_size = cpu_to_le64(ei->valid_size); + if (on_disk_size) { ep2->dentry.stream.flags = ei->flags; ep2->dentry.stream.start_clu = cpu_to_le32(ei->start_clu); @@ -82,7 +94,7 @@ int __exfat_write_inode(struct inode *inode, int sync) ep2->dentry.stream.start_clu = EXFAT_FREE_CLUSTER; } - exfat_update_dir_chksum_with_entry_set(&es); + exfat_update_dir_chksum(&es); return exfat_put_dentry_set(&es, sync); } @@ -90,6 +102,9 @@ int exfat_write_inode(struct inode *inode, struct writeback_control *wbc) { int ret; + if (unlikely(exfat_forced_shutdown(inode->i_sb))) + return -EIO; + mutex_lock(&EXFAT_SB(inode->i_sb)->s_lock); ret = __exfat_write_inode(inode, wbc->sync_mode == WB_SYNC_ALL); mutex_unlock(&EXFAT_SB(inode->i_sb)->s_lock); @@ -118,11 +133,9 @@ static int exfat_map_cluster(struct inode *inode, unsigned int clu_offset, struct exfat_sb_info *sbi = EXFAT_SB(sb); struct exfat_inode_info *ei = EXFAT_I(inode); unsigned int local_clu_offset = clu_offset; - unsigned int num_to_be_allocated = 0, num_clusters = 0; + unsigned int num_to_be_allocated = 0, num_clusters; - if (ei->i_size_ondisk > 0) - num_clusters = - EXFAT_B_TO_CLU_ROUND_UP(ei->i_size_ondisk, sbi); + num_clusters = EXFAT_B_TO_CLU(exfat_ondisk_size(inode), sbi); if (clu_offset >= num_clusters) num_to_be_allocated = clu_offset - num_clusters + 1; @@ -220,8 +233,7 @@ static int exfat_map_cluster(struct inode *inode, unsigned int clu_offset, num_clusters += num_to_be_allocated; *clu = new_clu.dir; - inode->i_blocks += - num_to_be_allocated << sbi->sect_per_clus_bits; + inode->i_blocks += EXFAT_CLU_TO_B(num_to_be_allocated, sbi) >> 9; /* * Move *clu pointer along FAT chains (hole care) because the @@ -249,21 +261,6 @@ static int exfat_map_cluster(struct inode *inode, unsigned int clu_offset, return 0; } -static int exfat_map_new_buffer(struct exfat_inode_info *ei, - struct buffer_head *bh, loff_t pos) -{ - if (buffer_delay(bh) && pos > ei->i_size_aligned) - return -EIO; - set_buffer_new(bh); - - /* - * Adjust i_size_aligned if i_size_ondisk is bigger than it. - */ - if (ei->i_size_ondisk > ei->i_size_aligned) - ei->i_size_aligned = ei->i_size_ondisk; - return 0; -} - static int exfat_get_block(struct inode *inode, sector_t iblock, struct buffer_head *bh_result, int create) { @@ -276,10 +273,12 @@ static int exfat_get_block(struct inode *inode, sector_t iblock, unsigned int cluster, sec_offset; sector_t last_block; sector_t phys = 0; - loff_t pos; + sector_t valid_blks; + loff_t i_size; mutex_lock(&sbi->s_lock); - last_block = EXFAT_B_TO_BLK_ROUND_UP(i_size_read(inode), sb); + i_size = i_size_read(inode); + last_block = EXFAT_B_TO_BLK_ROUND_UP(i_size, sb); if (iblock >= last_block && !create) goto done; @@ -304,31 +303,103 @@ static int exfat_get_block(struct inode *inode, sector_t iblock, mapped_blocks = sbi->sect_per_clus - sec_offset; max_blocks = min(mapped_blocks, max_blocks); - /* Treat newly added block / cluster */ - if (iblock < last_block) - create = 0; + map_bh(bh_result, sb, phys); + if (buffer_delay(bh_result)) + clear_buffer_delay(bh_result); + + /* + * In most cases, we just need to set bh_result to mapped, unmapped + * or new status as follows: + * 1. i_size == valid_size + * 2. write case (create == 1) + * 3. direct_read (!bh_result->b_folio) + * -> the unwritten part will be zeroed in exfat_direct_IO() + * + * Otherwise, in the case of buffered read, it is necessary to take + * care the last nested block if valid_size is not equal to i_size. + */ + if (i_size == ei->valid_size || create || !bh_result->b_folio) + valid_blks = EXFAT_B_TO_BLK_ROUND_UP(ei->valid_size, sb); + else + valid_blks = EXFAT_B_TO_BLK(ei->valid_size, sb); - if (create || buffer_delay(bh_result)) { - pos = EXFAT_BLK_TO_B((iblock + 1), sb); - if (ei->i_size_ondisk < pos) - ei->i_size_ondisk = pos; + /* The range has been fully written, map it */ + if (iblock + max_blocks < valid_blks) + goto done; + + /* The range has been partially written, map the written part */ + if (iblock < valid_blks) { + max_blocks = valid_blks - iblock; + goto done; } + /* The area has not been written, map and mark as new for create case */ if (create) { - err = exfat_map_new_buffer(ei, bh_result, pos); - if (err) { - exfat_fs_error(sb, - "requested for bmap out of range(pos : (%llu) > i_size_aligned(%llu)\n", - pos, ei->i_size_aligned); - goto unlock_ret; + set_buffer_new(bh_result); + ei->valid_size = EXFAT_BLK_TO_B(iblock + max_blocks, sb); + mark_inode_dirty(inode); + goto done; + } + + /* + * The area has just one block partially written. + * In that case, we should read and fill the unwritten part of + * a block with zero. + */ + if (bh_result->b_folio && iblock == valid_blks && + (ei->valid_size & (sb->s_blocksize - 1))) { + loff_t size, pos; + void *addr; + + max_blocks = 1; + + /* + * No buffer_head is allocated. + * (1) bmap: It's enough to set blocknr without I/O. + * (2) read: The unwritten part should be filled with zero. + * If a folio does not have any buffers, + * let's returns -EAGAIN to fallback to + * block_read_full_folio() for per-bh IO. + */ + if (!folio_buffers(bh_result->b_folio)) { + err = -EAGAIN; + goto done; } + + pos = EXFAT_BLK_TO_B(iblock, sb); + size = ei->valid_size - pos; + addr = folio_address(bh_result->b_folio) + + offset_in_folio(bh_result->b_folio, pos); + + /* Check if bh->b_data points to proper addr in folio */ + if (bh_result->b_data != addr) { + exfat_fs_error_ratelimit(sb, + "b_data(%p) != folio_addr(%p)", + bh_result->b_data, addr); + err = -EINVAL; + goto done; + } + + /* Read a block */ + err = bh_read(bh_result, 0); + if (err < 0) + goto done; + + /* Zero unwritten part of a block */ + memset(bh_result->b_data + size, 0, bh_result->b_size - size); + err = 0; + goto done; } - if (buffer_delay(bh_result)) - clear_buffer_delay(bh_result); - map_bh(bh_result, sb, phys); + /* + * The area has not been written, clear mapped for read/bmap cases. + * If so, it will be filled with zero without reading from disk. + */ + clear_buffer_mapped(bh_result); done: bh_result->b_size = EXFAT_BLK_TO_B(max_blocks, sb); + if (err < 0) + clear_buffer_mapped(bh_result); unlock_ret: mutex_unlock(&sbi->s_lock); return err; @@ -341,12 +412,26 @@ static int exfat_read_folio(struct file *file, struct folio *folio) static void exfat_readahead(struct readahead_control *rac) { + struct address_space *mapping = rac->mapping; + struct inode *inode = mapping->host; + struct exfat_inode_info *ei = EXFAT_I(inode); + loff_t pos = readahead_pos(rac); + + /* Range cross valid_size, read it page by page. */ + if (ei->valid_size < i_size_read(inode) && + pos <= ei->valid_size && + ei->valid_size < pos + readahead_length(rac)) + return; + mpage_readahead(rac, exfat_get_block); } static int exfat_writepages(struct address_space *mapping, struct writeback_control *wbc) { + if (unlikely(exfat_forced_shutdown(mapping->host->i_sb))) + return -EIO; + return mpage_writepages(mapping, wbc, exfat_get_block); } @@ -356,21 +441,22 @@ static void exfat_write_failed(struct address_space *mapping, loff_t to) if (to > i_size_read(inode)) { truncate_pagecache(inode, i_size_read(inode)); - inode->i_mtime = inode->i_ctime = current_time(inode); + inode_set_mtime_to_ts(inode, inode_set_ctime_current(inode)); exfat_truncate(inode); } } -static int exfat_write_begin(struct file *file, struct address_space *mapping, - loff_t pos, unsigned int len, - struct page **pagep, void **fsdata) +static int exfat_write_begin(const struct kiocb *iocb, + struct address_space *mapping, + loff_t pos, unsigned int len, + struct folio **foliop, void **fsdata) { int ret; - *pagep = NULL; - ret = cont_write_begin(file, mapping, pos, len, pagep, fsdata, - exfat_get_block, - &EXFAT_I(mapping->host)->i_size_ondisk); + if (unlikely(exfat_forced_shutdown(mapping->host->i_sb))) + return -EIO; + + ret = block_write_begin(mapping, pos, len, foliop, exfat_get_block); if (ret < 0) exfat_write_failed(mapping, pos+len); @@ -378,29 +464,27 @@ static int exfat_write_begin(struct file *file, struct address_space *mapping, return ret; } -static int exfat_write_end(struct file *file, struct address_space *mapping, - loff_t pos, unsigned int len, unsigned int copied, - struct page *pagep, void *fsdata) +static int exfat_write_end(const struct kiocb *iocb, + struct address_space *mapping, + loff_t pos, unsigned int len, unsigned int copied, + struct folio *folio, void *fsdata) { struct inode *inode = mapping->host; struct exfat_inode_info *ei = EXFAT_I(inode); int err; - err = generic_write_end(file, mapping, pos, len, copied, pagep, fsdata); - - if (ei->i_size_aligned < i_size_read(inode)) { - exfat_fs_error(inode->i_sb, - "invalid size(size(%llu) > aligned(%llu)\n", - i_size_read(inode), ei->i_size_aligned); - return -EIO; - } - + err = generic_write_end(iocb, mapping, pos, len, copied, folio, fsdata); if (err < len) exfat_write_failed(mapping, pos+len); - if (!(err < 0) && !(ei->attr & ATTR_ARCHIVE)) { - inode->i_mtime = inode->i_ctime = current_time(inode); - ei->attr |= ATTR_ARCHIVE; + if (!(err < 0) && pos + err > ei->valid_size) { + ei->valid_size = pos + err; + mark_inode_dirty(inode); + } + + if (!(err < 0) && !(ei->attr & EXFAT_ATTR_ARCHIVE)) { + inode_set_mtime_to_ts(inode, inode_set_ctime_current(inode)); + ei->attr |= EXFAT_ATTR_ARCHIVE; mark_inode_dirty(inode); } @@ -411,31 +495,41 @@ static ssize_t exfat_direct_IO(struct kiocb *iocb, struct iov_iter *iter) { struct address_space *mapping = iocb->ki_filp->f_mapping; struct inode *inode = mapping->host; - loff_t size = iocb->ki_pos + iov_iter_count(iter); + struct exfat_inode_info *ei = EXFAT_I(inode); + loff_t pos = iocb->ki_pos; + loff_t size = pos + iov_iter_count(iter); int rw = iov_iter_rw(iter); ssize_t ret; - if (rw == WRITE) { - /* - * FIXME: blockdev_direct_IO() doesn't use ->write_begin(), - * so we need to update the ->i_size_aligned to block boundary. - * - * But we must fill the remaining area or hole by nul for - * updating ->i_size_aligned - * - * Return 0, and fallback to normal buffered write. - */ - if (EXFAT_I(inode)->i_size_aligned < size) - return 0; - } - /* * Need to use the DIO_LOCKING for avoiding the race * condition of exfat_get_block() and ->truncate(). */ ret = blockdev_direct_IO(iocb, inode, iter, exfat_get_block); - if (ret < 0 && (rw & WRITE)) - exfat_write_failed(mapping, size); + if (ret < 0) { + if (rw == WRITE && ret != -EIOCBQUEUED) + exfat_write_failed(mapping, size); + + return ret; + } else + size = pos + ret; + + if (rw == WRITE) { + /* + * If the block had been partially written before this write, + * ->valid_size will not be updated in exfat_get_block(), + * update it here. + */ + if (ei->valid_size < size) { + ei->valid_size = size; + mark_inode_dirty(inode); + } + } else if (pos < ei->valid_size && ei->valid_size < size) { + /* zero the unwritten part in the partially written block */ + iov_iter_revert(iter, size - ei->valid_size); + iov_iter_zero(size - ei->valid_size, iter); + } + return ret; } @@ -535,6 +629,7 @@ static int exfat_fill_inode(struct inode *inode, struct exfat_dir_entry *info) ei->start_clu = info->start_clu; ei->flags = info->flags; ei->type = info->type; + ei->valid_size = info->valid_size; ei->version = 0; ei->hint_stat.eidx = 0; @@ -548,7 +643,7 @@ static int exfat_fill_inode(struct inode *inode, struct exfat_dir_entry *info) inode_inc_iversion(inode); inode->i_generation = get_random_u32(); - if (info->attr & ATTR_SUBDIR) { /* directory */ + if (info->attr & EXFAT_ATTR_SUBDIR) { /* directory */ inode->i_generation &= ~1; inode->i_mode = exfat_make_mode(sbi, info->attr, 0777); inode->i_op = &exfat_dir_inode_operations; @@ -565,23 +660,13 @@ static int exfat_fill_inode(struct inode *inode, struct exfat_dir_entry *info) i_size_write(inode, size); - /* ondisk and aligned size should be aligned with block size */ - if (size & (inode->i_sb->s_blocksize - 1)) { - size |= (inode->i_sb->s_blocksize - 1); - size++; - } - - ei->i_size_aligned = size; - ei->i_size_ondisk = size; - exfat_save_attr(inode, info->attr); - inode->i_blocks = round_up(i_size_read(inode), sbi->cluster_size) >> - inode->i_blkbits; - inode->i_mtime = info->mtime; - inode->i_ctime = info->mtime; + inode->i_blocks = round_up(i_size_read(inode), sbi->cluster_size) >> 9; + inode_set_mtime_to_ts(inode, info->mtime); + inode_set_ctime_to_ts(inode, info->mtime); ei->i_crtime = info->crtime; - inode->i_atime = info->atime; + inode_set_atime_to_ts(inode, info->atime); return 0; } diff --git a/fs/exfat/misc.c b/fs/exfat/misc.c index 2e1a1a6b1021..fa8459828046 100644 --- a/fs/exfat/misc.c +++ b/fs/exfat/misc.c @@ -126,6 +126,14 @@ void exfat_truncate_atime(struct timespec64 *ts) ts->tv_nsec = 0; } +void exfat_truncate_inode_atime(struct inode *inode) +{ + struct timespec64 atime = inode_get_atime(inode); + + exfat_truncate_atime(&atime); + inode_set_atime_to_ts(inode, atime); +} + u16 exfat_calc_chksum16(void *data, int len, u16 chksum, int type) { int i; diff --git a/fs/exfat/namei.c b/fs/exfat/namei.c index 5f995eba5dbb..dfe957493d49 100644 --- a/fs/exfat/namei.c +++ b/fs/exfat/namei.c @@ -31,10 +31,9 @@ static inline void exfat_d_version_set(struct dentry *dentry, * If it happened, the negative dentry isn't actually negative anymore. So, * drop it. */ -static int exfat_d_revalidate(struct dentry *dentry, unsigned int flags) +static int exfat_d_revalidate(struct inode *dir, const struct qstr *name, + struct dentry *dentry, unsigned int flags) { - int ret; - if (flags & LOOKUP_RCU) return -ECHILD; @@ -58,11 +57,7 @@ static int exfat_d_revalidate(struct dentry *dentry, unsigned int flags) if (flags & (LOOKUP_CREATE | LOOKUP_RENAME_TARGET)) return 0; - spin_lock(&dentry->d_lock); - ret = inode_eq_iversion(d_inode(dentry->d_parent), - exfat_d_version(dentry)); - spin_unlock(&dentry->d_lock); - return ret; + return inode_eq_iversion(dir, exfat_d_version(dentry)); } /* returns the length of a struct qstr, ignoring trailing dots if necessary */ @@ -204,21 +199,16 @@ const struct dentry_operations exfat_utf8_dentry_ops = { .d_compare = exfat_utf8_d_cmp, }; -/* used only in search empty_slot() */ -#define CNT_UNUSED_NOHIT (-1) -#define CNT_UNUSED_HIT (-2) /* search EMPTY CONTINUOUS "num_entries" entries */ static int exfat_search_empty_slot(struct super_block *sb, struct exfat_hint_femp *hint_femp, struct exfat_chain *p_dir, - int num_entries) + int num_entries, struct exfat_entry_set_cache *es) { - int i, dentry, num_empty = 0; + int i, dentry, ret; int dentries_per_clu; - unsigned int type; struct exfat_chain clu; - struct exfat_dentry *ep; struct exfat_sb_info *sbi = EXFAT_SB(sb); - struct buffer_head *bh; + int total_entries = EXFAT_CLU_TO_DEN(p_dir->size, sbi); dentries_per_clu = sbi->dentries_per_clu; @@ -231,7 +221,7 @@ static int exfat_search_empty_slot(struct super_block *sb, * Otherwise, and if "dentry + hint_famp->count" is also equal * to "p_dir->size * dentries_per_clu", it means ENOSPC. */ - if (dentry + hint_femp->count == p_dir->size * dentries_per_clu && + if (dentry + hint_femp->count == total_entries && num_entries > hint_femp->count) return -ENOSPC; @@ -242,69 +232,41 @@ static int exfat_search_empty_slot(struct super_block *sb, dentry = 0; } - while (clu.dir != EXFAT_EOF_CLUSTER) { + while (dentry + num_entries <= total_entries && + clu.dir != EXFAT_EOF_CLUSTER) { i = dentry & (dentries_per_clu - 1); - for (; i < dentries_per_clu; i++, dentry++) { - ep = exfat_get_dentry(sb, &clu, i, &bh); - if (!ep) - return -EIO; - type = exfat_get_entry_type(ep); - brelse(bh); - - if (type == TYPE_UNUSED || type == TYPE_DELETED) { - num_empty++; - if (hint_femp->eidx == EXFAT_HINT_NONE) { - hint_femp->eidx = dentry; - hint_femp->count = CNT_UNUSED_NOHIT; - exfat_chain_set(&hint_femp->cur, - clu.dir, clu.size, clu.flags); - } - - if (type == TYPE_UNUSED && - hint_femp->count != CNT_UNUSED_HIT) - hint_femp->count = CNT_UNUSED_HIT; + ret = exfat_get_empty_dentry_set(es, sb, &clu, i, num_entries); + if (ret < 0) + return ret; + else if (ret == 0) + return dentry; + + dentry += ret; + 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 (hint_femp->eidx != EXFAT_HINT_NONE && - hint_femp->count == CNT_UNUSED_HIT) { - /* unused empty group means - * an empty group which includes - * unused dentry - */ - exfat_fs_error(sb, - "found bogus dentry(%d) beyond unused empty group(%d) (start_clu : %u, cur_clu : %u)", - dentry, hint_femp->eidx, - p_dir->dir, clu.dir); + if (exfat_get_next_cluster(sb, &clu.dir)) return -EIO; - } - - num_empty = 0; - hint_femp->eidx = EXFAT_HINT_NONE; } - if (num_empty >= num_entries) { - /* found and invalidate hint_femp */ - hint_femp->eidx = EXFAT_HINT_NONE; - return (dentry - (num_entries - 1)); - } - } - - 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; + i -= dentries_per_clu; } } - hint_femp->eidx = p_dir->size * dentries_per_clu - num_empty; - hint_femp->count = num_empty; - if (num_empty == 0) + hint_femp->eidx = dentry; + hint_femp->count = 0; + if (dentry == total_entries || clu.dir == EXFAT_EOF_CLUSTER) exfat_chain_set(&hint_femp->cur, EXFAT_EOF_CLUSTER, 0, clu.flags); + else + hint_femp->cur = clu; return -ENOSPC; } @@ -321,11 +283,26 @@ static int exfat_check_max_dentries(struct inode *inode) return 0; } -/* find empty directory entry. - * if there isn't any empty slot, expand cluster chain. +/* + * Find an empty directory entry set. + * + * If there isn't any empty slot, expand cluster chain. + * + * in: + * inode: inode of the parent directory + * num_entries: specifies how many dentries in the empty directory entry set + * + * out: + * p_dir: the cluster where the empty directory entry set is located + * es: The found empty directory entry set + * + * return: + * the directory entry index in p_dir is returned on succeeds + * -error code is returned on failure */ -static int exfat_find_empty_entry(struct inode *inode, - struct exfat_chain *p_dir, int num_entries) +int exfat_find_empty_entry(struct inode *inode, + struct exfat_chain *p_dir, int num_entries, + struct exfat_entry_set_cache *es) { int dentry; unsigned int ret, last_clu; @@ -343,22 +320,31 @@ static int exfat_find_empty_entry(struct inode *inode, ei->hint_femp.eidx = EXFAT_HINT_NONE; } + exfat_chain_set(p_dir, ei->start_clu, + EXFAT_B_TO_CLU(i_size_read(inode), sbi), ei->flags); + while ((dentry = exfat_search_empty_slot(sb, &hint_femp, p_dir, - num_entries)) < 0) { - if (dentry == -EIO) - break; + num_entries, es)) < 0) { + if (dentry != -ENOSPC) + return dentry; if (exfat_check_max_dentries(inode)) return -ENOSPC; - /* we trust p_dir->size regardless of FAT type */ - if (exfat_find_last_cluster(sb, p_dir, &last_clu)) - return -EIO; - /* * Allocate new cluster to this directory */ - exfat_chain_set(&clu, last_clu + 1, 0, p_dir->flags); + if (ei->start_clu != EXFAT_EOF_CLUSTER) { + /* we trust p_dir->size regardless of FAT type */ + if (exfat_find_last_cluster(sb, p_dir, &last_clu)) + return -EIO; + + exfat_chain_set(&clu, last_clu + 1, 0, p_dir->flags); + } else { + /* This directory is empty */ + exfat_chain_set(&clu, EXFAT_EOF_CLUSTER, 0, + ALLOC_NO_FAT_CHAIN); + } /* allocate a cluster */ ret = exfat_alloc_cluster(inode, 1, &clu, IS_DIRSYNC(inode)); @@ -368,6 +354,12 @@ static int exfat_find_empty_entry(struct inode *inode, if (exfat_zeroed_cluster(inode, clu.dir)) return -EIO; + if (ei->start_clu == EXFAT_EOF_CLUSTER) { + ei->start_clu = clu.dir; + p_dir->dir = clu.dir; + hint_femp.eidx = 0; + } + /* append to the FAT chain */ if (clu.flags != p_dir->flags) { /* no-fat-chain bit is disabled, @@ -393,13 +385,15 @@ static int exfat_find_empty_entry(struct inode *inode, /* directory inode should be updated in here */ i_size_write(inode, size); - ei->i_size_ondisk += sbi->cluster_size; - ei->i_size_aligned += sbi->cluster_size; + ei->valid_size += sbi->cluster_size; ei->flags = p_dir->flags; - inode->i_blocks += 1 << sbi->sect_per_clus_bits; + inode->i_blocks += sbi->cluster_size >> 9; } - return dentry; + p_dir->dir = exfat_sector_to_cluster(sbi, es->bh[0]->b_blocknr); + p_dir->size -= dentry / sbi->dentries_per_clu; + + return dentry & (sbi->dentries_per_clu - 1); } /* @@ -407,14 +401,11 @@ static int exfat_find_empty_entry(struct inode *inode, * Zero if it was successful; otherwise nonzero. */ static int __exfat_resolve_path(struct inode *inode, const unsigned char *path, - struct exfat_chain *p_dir, struct exfat_uni_name *p_uniname, - int lookup) + struct exfat_uni_name *p_uniname, int lookup) { int namelen; int lossy = NLS_NAME_NO_LOSSY; struct super_block *sb = inode->i_sb; - struct exfat_sb_info *sbi = EXFAT_SB(sb); - struct exfat_inode_info *ei = EXFAT_I(inode); int pathlen = strlen(path); /* @@ -451,26 +442,21 @@ static int __exfat_resolve_path(struct inode *inode, const unsigned char *path, return namelen; /* return error value */ if ((lossy && !lookup) || !namelen) - return (lossy & NLS_NAME_OVERLEN) ? -ENAMETOOLONG : -EINVAL; - - exfat_chain_set(p_dir, ei->start_clu, - EXFAT_B_TO_CLU(i_size_read(inode), sbi), ei->flags); + return -EINVAL; return 0; } static inline int exfat_resolve_path(struct inode *inode, - const unsigned char *path, struct exfat_chain *dir, - struct exfat_uni_name *uni) + const unsigned char *path, struct exfat_uni_name *uni) { - return __exfat_resolve_path(inode, path, dir, uni, 0); + return __exfat_resolve_path(inode, path, uni, 0); } static inline int exfat_resolve_path_for_lookup(struct inode *inode, - const unsigned char *path, struct exfat_chain *dir, - struct exfat_uni_name *uni) + const unsigned char *path, struct exfat_uni_name *uni) { - return __exfat_resolve_path(inode, path, dir, uni, 1); + return __exfat_resolve_path(inode, path, uni, 1); } static inline loff_t exfat_make_i_pos(struct exfat_dir_entry *info) @@ -479,18 +465,19 @@ static inline loff_t exfat_make_i_pos(struct exfat_dir_entry *info) } static int exfat_add_entry(struct inode *inode, const char *path, - struct exfat_chain *p_dir, unsigned int type, - struct exfat_dir_entry *info) + unsigned int type, struct exfat_dir_entry *info) { int ret, dentry, num_entries; struct super_block *sb = inode->i_sb; struct exfat_sb_info *sbi = EXFAT_SB(sb); struct exfat_uni_name uniname; struct exfat_chain clu; + struct timespec64 ts = current_time(inode); + struct exfat_entry_set_cache es; int clu_size = 0; unsigned int start_clu = EXFAT_FREE_CLUSTER; - ret = exfat_resolve_path(inode, path, p_dir, &uniname); + ret = exfat_resolve_path(inode, path, &uniname); if (ret) goto out; @@ -501,16 +488,18 @@ static int exfat_add_entry(struct inode *inode, const char *path, } /* exfat_find_empty_entry must be called before alloc_cluster() */ - dentry = exfat_find_empty_entry(inode, p_dir, num_entries); + dentry = exfat_find_empty_entry(inode, &info->dir, num_entries, &es); if (dentry < 0) { ret = dentry; /* -EIO or -ENOSPC */ goto out; } - if (type == TYPE_DIR) { + if (type == TYPE_DIR && !sbi->options.zero_size_dir) { ret = exfat_alloc_new_dir(inode, &clu); - if (ret) + if (ret) { + exfat_put_dentry_set(&es, false); goto out; + } start_clu = clu.dir; clu_size = sbi->cluster_size; } @@ -519,31 +508,33 @@ static int exfat_add_entry(struct inode *inode, const char *path, /* fill the dos name directory entry information of the created file. * the first cluster is not determined yet. (0) */ - ret = exfat_init_dir_entry(inode, p_dir, dentry, type, - start_clu, clu_size); - if (ret) - goto out; + exfat_init_dir_entry(&es, type, start_clu, clu_size, &ts); + exfat_init_ext_entry(&es, num_entries, &uniname); - ret = exfat_init_ext_entry(inode, p_dir, dentry, num_entries, &uniname); + ret = exfat_put_dentry_set(&es, IS_DIRSYNC(inode)); if (ret) goto out; - info->dir = *p_dir; info->entry = dentry; info->flags = ALLOC_NO_FAT_CHAIN; info->type = type; if (type == TYPE_FILE) { - info->attr = ATTR_ARCHIVE; + info->attr = EXFAT_ATTR_ARCHIVE; info->start_clu = EXFAT_EOF_CLUSTER; info->size = 0; info->num_subdirs = 0; } else { - info->attr = ATTR_SUBDIR; - info->start_clu = start_clu; + info->attr = EXFAT_ATTR_SUBDIR; + if (sbi->options.zero_size_dir) + info->start_clu = EXFAT_EOF_CLUSTER; + else + info->start_clu = start_clu; info->size = clu_size; info->num_subdirs = EXFAT_MIN_SUBDIR; } + info->valid_size = info->size; + memset(&info->crtime, 0, sizeof(info->crtime)); memset(&info->mtime, 0, sizeof(info->mtime)); memset(&info->atime, 0, sizeof(info->atime)); @@ -551,26 +542,28 @@ out: return ret; } -static int exfat_create(struct user_namespace *mnt_userns, struct inode *dir, +static int exfat_create(struct mnt_idmap *idmap, struct inode *dir, struct dentry *dentry, umode_t mode, bool excl) { struct super_block *sb = dir->i_sb; struct inode *inode; - struct exfat_chain cdir; struct exfat_dir_entry info; loff_t i_pos; int err; + loff_t size = i_size_read(dir); + + if (unlikely(exfat_forced_shutdown(sb))) + return -EIO; mutex_lock(&EXFAT_SB(sb)->s_lock); exfat_set_volume_dirty(sb); - err = exfat_add_entry(dir, dentry->d_name.name, &cdir, TYPE_FILE, - &info); + err = exfat_add_entry(dir, dentry->d_name.name, TYPE_FILE, &info); if (err) goto unlock; inode_inc_iversion(dir); - dir->i_ctime = dir->i_mtime = current_time(dir); - if (IS_DIRSYNC(dir)) + inode_set_mtime_to_ts(dir, inode_set_ctime_current(dir)); + if (IS_DIRSYNC(dir) && size != i_size_read(dir)) exfat_sync_inode(dir); else mark_inode_dirty(dir); @@ -582,9 +575,9 @@ static int exfat_create(struct user_namespace *mnt_userns, struct inode *dir, goto unlock; inode_inc_iversion(inode); - inode->i_mtime = inode->i_atime = inode->i_ctime = - EXFAT_I(inode)->i_crtime = current_time(inode); - exfat_truncate_atime(&inode->i_atime); + EXFAT_I(inode)->i_crtime = simple_inode_init_ts(inode); + exfat_truncate_inode_atime(inode); + /* timestamp is already written, so mark_inode_dirty() is unneeded. */ d_instantiate(dentry, inode); @@ -594,7 +587,7 @@ unlock: } /* lookup a file */ -static int exfat_find(struct inode *dir, struct qstr *qname, +static int exfat_find(struct inode *dir, const struct qstr *qname, struct exfat_dir_entry *info) { int ret, dentry, count; @@ -612,10 +605,13 @@ static int exfat_find(struct inode *dir, struct qstr *qname, return -ENOENT; /* check the validity of directory name in the given pathname */ - ret = exfat_resolve_path_for_lookup(dir, qname->name, &cdir, &uni_name); + ret = exfat_resolve_path_for_lookup(dir, qname->name, &uni_name); if (ret) return ret; + exfat_chain_set(&cdir, ei->start_clu, + EXFAT_B_TO_CLU(i_size_read(dir), sbi), ei->flags); + /* check the validation of hint_stat and initialize it if required */ if (ei->version != (inode_peek_iversion_raw(dir) & 0xffffffff)) { ei->hint_stat.clu = cdir.dir; @@ -629,15 +625,16 @@ static int exfat_find(struct inode *dir, struct qstr *qname, if (dentry < 0) return dentry; /* -error value */ - info->dir = cdir; - info->entry = dentry; - info->num_subdirs = 0; - /* adjust cdir to the optimized value */ cdir.dir = hint_opt.clu; if (cdir.flags & ALLOC_NO_FAT_CHAIN) cdir.size -= dentry / sbi->dentries_per_clu; dentry = hint_opt.eidx; + + info->dir = cdir; + info->entry = dentry; + info->num_subdirs = 0; + if (exfat_get_dentry_set(&es, sb, &cdir, dentry, ES_2_ENTRIES)) return -EIO; ep = exfat_get_dentry_cached(&es, ES_IDX_FILE); @@ -645,15 +642,28 @@ static int exfat_find(struct inode *dir, struct qstr *qname, info->type = exfat_get_entry_type(ep); info->attr = le16_to_cpu(ep->dentry.file.attr); - info->size = le64_to_cpu(ep2->dentry.stream.valid_size); - if ((info->type == TYPE_FILE) && (info->size == 0)) { + info->valid_size = le64_to_cpu(ep2->dentry.stream.valid_size); + info->size = le64_to_cpu(ep2->dentry.stream.size); + + info->start_clu = le32_to_cpu(ep2->dentry.stream.start_clu); + if (!is_valid_cluster(sbi, info->start_clu) && info->size) { + exfat_warn(sb, "start_clu is invalid cluster(0x%x)", + info->start_clu); + info->size = 0; + info->valid_size = 0; + } + + if (info->valid_size > info->size) { + exfat_warn(sb, "valid_size(%lld) is greater than size(%lld)", + info->valid_size, info->size); + info->valid_size = info->size; + } + + if (info->size == 0) { info->flags = ALLOC_NO_FAT_CHAIN; info->start_clu = EXFAT_EOF_CLUSTER; - } else { + } else info->flags = ep2->dentry.stream.flags; - info->start_clu = - le32_to_cpu(ep2->dentry.stream.start_clu); - } exfat_get_entry_time(sbi, &info->crtime, ep->dentry.file.create_tz, @@ -672,6 +682,16 @@ static int exfat_find(struct inode *dir, struct qstr *qname, 0); exfat_put_dentry_set(&es, false); + if (info->valid_size < 0) { + exfat_fs_error(sb, "data valid size is invalid(%lld)", info->valid_size); + return -EIO; + } + + if (unlikely(EXFAT_B_TO_CLU_ROUND_UP(info->size, sbi) > sbi->used_clusters)) { + exfat_fs_error(sb, "data size is invalid(%lld)", info->size); + return -EIO; + } + if (ei->start_clu == EXFAT_FREE_CLUSTER) { exfat_fs_error(sb, "non-zero size file starts with zero cluster (size : %llu, p_dir : %u, entry : 0x%08x)", @@ -775,58 +795,48 @@ unlock: /* remove an entry, BUT don't truncate */ static int exfat_unlink(struct inode *dir, struct dentry *dentry) { - struct exfat_chain cdir; - struct exfat_dentry *ep; struct super_block *sb = dir->i_sb; struct inode *inode = dentry->d_inode; struct exfat_inode_info *ei = EXFAT_I(inode); - struct buffer_head *bh; - int num_entries, entry, err = 0; + struct exfat_entry_set_cache es; + int err = 0; + + if (unlikely(exfat_forced_shutdown(sb))) + return -EIO; mutex_lock(&EXFAT_SB(sb)->s_lock); - exfat_chain_dup(&cdir, &ei->dir); - entry = ei->entry; if (ei->dir.dir == DIR_DELETED) { exfat_err(sb, "abnormal access to deleted dentry"); err = -ENOENT; goto unlock; } - ep = exfat_get_dentry(sb, &cdir, entry, &bh); - if (!ep) { - err = -EIO; - goto unlock; - } - num_entries = exfat_count_ext_entries(sb, &cdir, entry, ep); - if (num_entries < 0) { + err = exfat_get_dentry_set_by_ei(&es, sb, ei); + if (err) { err = -EIO; - brelse(bh); goto unlock; } - num_entries++; - brelse(bh); exfat_set_volume_dirty(sb); + /* update the directory entry */ - if (exfat_remove_entries(dir, &cdir, entry, 0, num_entries)) { - err = -EIO; + exfat_remove_entries(inode, &es, ES_IDX_FILE); + + err = exfat_put_dentry_set(&es, IS_DIRSYNC(inode)); + if (err) goto unlock; - } /* This doesn't modify ei */ ei->dir.dir = DIR_DELETED; inode_inc_iversion(dir); - dir->i_mtime = dir->i_atime = current_time(dir); - exfat_truncate_atime(&dir->i_atime); - if (IS_DIRSYNC(dir)) - exfat_sync_inode(dir); - else - mark_inode_dirty(dir); + simple_inode_init_ts(dir); + exfat_truncate_inode_atime(dir); + mark_inode_dirty(dir); clear_nlink(inode); - inode->i_mtime = inode->i_atime = current_time(inode); - exfat_truncate_atime(&inode->i_atime); + simple_inode_init_ts(inode); + exfat_truncate_inode_atime(inode); exfat_unhash_inode(inode); exfat_d_version_set(dentry, inode_query_iversion(dir)); unlock: @@ -834,26 +844,28 @@ unlock: return err; } -static int exfat_mkdir(struct user_namespace *mnt_userns, struct inode *dir, - struct dentry *dentry, umode_t mode) +static struct dentry *exfat_mkdir(struct mnt_idmap *idmap, struct inode *dir, + struct dentry *dentry, umode_t mode) { struct super_block *sb = dir->i_sb; struct inode *inode; struct exfat_dir_entry info; - struct exfat_chain cdir; loff_t i_pos; int err; + loff_t size = i_size_read(dir); + + if (unlikely(exfat_forced_shutdown(sb))) + return ERR_PTR(-EIO); mutex_lock(&EXFAT_SB(sb)->s_lock); exfat_set_volume_dirty(sb); - err = exfat_add_entry(dir, dentry->d_name.name, &cdir, TYPE_DIR, - &info); + err = exfat_add_entry(dir, dentry->d_name.name, TYPE_DIR, &info); if (err) goto unlock; inode_inc_iversion(dir); - dir->i_ctime = dir->i_mtime = current_time(dir); - if (IS_DIRSYNC(dir)) + inode_set_mtime_to_ts(dir, inode_set_ctime_current(dir)); + if (IS_DIRSYNC(dir) && size != i_size_read(dir)) exfat_sync_inode(dir); else mark_inode_dirty(dir); @@ -866,16 +878,15 @@ static int exfat_mkdir(struct user_namespace *mnt_userns, struct inode *dir, goto unlock; inode_inc_iversion(inode); - inode->i_mtime = inode->i_atime = inode->i_ctime = - EXFAT_I(inode)->i_crtime = current_time(inode); - exfat_truncate_atime(&inode->i_atime); + EXFAT_I(inode)->i_crtime = simple_inode_init_ts(inode); + exfat_truncate_inode_atime(inode); /* timestamp is already written, so mark_inode_dirty() is unneeded. */ d_instantiate(dentry, inode); unlock: mutex_unlock(&EXFAT_SB(sb)->s_lock); - return err; + return ERR_PTR(err); } static int exfat_check_dir_empty(struct super_block *sb, @@ -883,6 +894,7 @@ static int exfat_check_dir_empty(struct super_block *sb, { int i, dentries_per_clu; unsigned int type; + unsigned int clu_count = 0; struct exfat_chain clu; struct exfat_dentry *ep; struct exfat_sb_info *sbi = EXFAT_SB(sb); @@ -890,6 +902,9 @@ static int exfat_check_dir_empty(struct super_block *sb, dentries_per_clu = sbi->dentries_per_clu; + if (p_dir->dir == EXFAT_EOF_CLUSTER) + return 0; + exfat_chain_dup(&clu, p_dir); while (clu.dir != EXFAT_EOF_CLUSTER) { @@ -916,6 +931,10 @@ static int exfat_check_dir_empty(struct super_block *sb, } else { if (exfat_get_next_cluster(sb, &(clu.dir))) return -EIO; + + /* break if the cluster chain includes a loop */ + if (unlikely(++clu_count > EXFAT_DATA_CLUSTER_COUNT(sbi))) + break; } } @@ -925,18 +944,17 @@ static int exfat_check_dir_empty(struct super_block *sb, static int exfat_rmdir(struct inode *dir, struct dentry *dentry) { struct inode *inode = dentry->d_inode; - struct exfat_dentry *ep; - struct exfat_chain cdir, clu_to_free; + struct exfat_chain clu_to_free; struct super_block *sb = inode->i_sb; struct exfat_sb_info *sbi = EXFAT_SB(sb); struct exfat_inode_info *ei = EXFAT_I(inode); - struct buffer_head *bh; - int num_entries, entry, err; + struct exfat_entry_set_cache es; + int err; - mutex_lock(&EXFAT_SB(inode->i_sb)->s_lock); + if (unlikely(exfat_forced_shutdown(sb))) + return -EIO; - exfat_chain_dup(&cdir, &ei->dir); - entry = ei->entry; + mutex_lock(&EXFAT_SB(inode->i_sb)->s_lock); if (ei->dir.dir == DIR_DELETED) { exfat_err(sb, "abnormal access to deleted dentry"); @@ -955,32 +973,25 @@ static int exfat_rmdir(struct inode *dir, struct dentry *dentry) goto unlock; } - ep = exfat_get_dentry(sb, &cdir, entry, &bh); - if (!ep) { - err = -EIO; - goto unlock; - } - - num_entries = exfat_count_ext_entries(sb, &cdir, entry, ep); - if (num_entries < 0) { + err = exfat_get_dentry_set_by_ei(&es, sb, ei); + if (err) { err = -EIO; - brelse(bh); goto unlock; } - num_entries++; - brelse(bh); exfat_set_volume_dirty(sb); - err = exfat_remove_entries(dir, &cdir, entry, 0, num_entries); - if (err) { - exfat_err(sb, "failed to exfat_remove_entries : err(%d)", err); + + exfat_remove_entries(inode, &es, ES_IDX_FILE); + + err = exfat_put_dentry_set(&es, IS_DIRSYNC(dir)); + if (err) goto unlock; - } + ei->dir.dir = DIR_DELETED; inode_inc_iversion(dir); - dir->i_mtime = dir->i_atime = current_time(dir); - exfat_truncate_atime(&dir->i_atime); + simple_inode_init_ts(dir); + exfat_truncate_inode_atime(dir); if (IS_DIRSYNC(dir)) exfat_sync_inode(dir); else @@ -988,8 +999,8 @@ static int exfat_rmdir(struct inode *dir, struct dentry *dentry) drop_nlink(dir); clear_nlink(inode); - inode->i_mtime = inode->i_atime = current_time(inode); - exfat_truncate_atime(&inode->i_atime); + simple_inode_init_ts(inode); + exfat_truncate_inode_atime(inode); exfat_unhash_inode(inode); exfat_d_version_set(dentry, inode_query_iversion(dir)); unlock: @@ -997,157 +1008,128 @@ unlock: return err; } -static int exfat_rename_file(struct inode *inode, struct exfat_chain *p_dir, - int oldentry, struct exfat_uni_name *p_uniname, - struct exfat_inode_info *ei) +static int exfat_rename_file(struct inode *parent_inode, + struct exfat_uni_name *p_uniname, struct exfat_inode_info *ei) { - int ret, num_old_entries, num_new_entries; + int ret, num_new_entries; struct exfat_dentry *epold, *epnew; - struct super_block *sb = inode->i_sb; - struct buffer_head *new_bh, *old_bh; - int sync = IS_DIRSYNC(inode); - - epold = exfat_get_dentry(sb, p_dir, oldentry, &old_bh); - if (!epold) - return -EIO; + struct super_block *sb = parent_inode->i_sb; + struct exfat_entry_set_cache old_es, new_es; + int sync = IS_DIRSYNC(parent_inode); - num_old_entries = exfat_count_ext_entries(sb, p_dir, oldentry, epold); - if (num_old_entries < 0) + if (unlikely(exfat_forced_shutdown(sb))) return -EIO; - num_old_entries++; num_new_entries = exfat_calc_num_entries(p_uniname); if (num_new_entries < 0) return num_new_entries; - if (num_old_entries < num_new_entries) { - int newentry; + ret = exfat_get_dentry_set_by_ei(&old_es, sb, ei); + if (ret) { + ret = -EIO; + return ret; + } - newentry = - exfat_find_empty_entry(inode, p_dir, num_new_entries); - if (newentry < 0) - return newentry; /* -EIO or -ENOSPC */ + epold = exfat_get_dentry_cached(&old_es, ES_IDX_FILE); - epnew = exfat_get_dentry(sb, p_dir, newentry, &new_bh); - if (!epnew) - return -EIO; + if (old_es.num_entries < num_new_entries) { + int newentry; + struct exfat_chain dir; - *epnew = *epold; - if (exfat_get_entry_type(epnew) == TYPE_FILE) { - epnew->dentry.file.attr |= cpu_to_le16(ATTR_ARCHIVE); - ei->attr |= ATTR_ARCHIVE; + newentry = exfat_find_empty_entry(parent_inode, &dir, + num_new_entries, &new_es); + if (newentry < 0) { + ret = newentry; /* -EIO or -ENOSPC */ + goto put_old_es; } - exfat_update_bh(new_bh, sync); - brelse(old_bh); - brelse(new_bh); - epold = exfat_get_dentry(sb, p_dir, oldentry + 1, &old_bh); - if (!epold) - return -EIO; - epnew = exfat_get_dentry(sb, p_dir, newentry + 1, &new_bh); - if (!epnew) { - brelse(old_bh); - return -EIO; + epnew = exfat_get_dentry_cached(&new_es, ES_IDX_FILE); + *epnew = *epold; + if (exfat_get_entry_type(epnew) == TYPE_FILE) { + epnew->dentry.file.attr |= cpu_to_le16(EXFAT_ATTR_ARCHIVE); + ei->attr |= EXFAT_ATTR_ARCHIVE; } + epold = exfat_get_dentry_cached(&old_es, ES_IDX_STREAM); + epnew = exfat_get_dentry_cached(&new_es, ES_IDX_STREAM); *epnew = *epold; - exfat_update_bh(new_bh, sync); - brelse(old_bh); - brelse(new_bh); - ret = exfat_init_ext_entry(inode, p_dir, newentry, - num_new_entries, p_uniname); + exfat_init_ext_entry(&new_es, num_new_entries, p_uniname); + + ret = exfat_put_dentry_set(&new_es, sync); if (ret) - return ret; + goto put_old_es; - exfat_remove_entries(inode, p_dir, oldentry, 0, - num_old_entries); - ei->dir = *p_dir; + exfat_remove_entries(parent_inode, &old_es, ES_IDX_FILE); + ei->dir = dir; ei->entry = newentry; } else { if (exfat_get_entry_type(epold) == TYPE_FILE) { - epold->dentry.file.attr |= cpu_to_le16(ATTR_ARCHIVE); - ei->attr |= ATTR_ARCHIVE; + epold->dentry.file.attr |= cpu_to_le16(EXFAT_ATTR_ARCHIVE); + ei->attr |= EXFAT_ATTR_ARCHIVE; } - exfat_update_bh(old_bh, sync); - brelse(old_bh); - ret = exfat_init_ext_entry(inode, p_dir, oldentry, - num_new_entries, p_uniname); - if (ret) - return ret; - exfat_remove_entries(inode, p_dir, oldentry, num_new_entries, - num_old_entries); + exfat_remove_entries(parent_inode, &old_es, ES_IDX_FIRST_FILENAME + 1); + exfat_init_ext_entry(&old_es, num_new_entries, p_uniname); } - return 0; + return exfat_put_dentry_set(&old_es, sync); + +put_old_es: + exfat_put_dentry_set(&old_es, false); + return ret; } -static int exfat_move_file(struct inode *inode, struct exfat_chain *p_olddir, - int oldentry, struct exfat_chain *p_newdir, +static int exfat_move_file(struct inode *parent_inode, struct exfat_uni_name *p_uniname, struct exfat_inode_info *ei) { - int ret, newentry, num_new_entries, num_old_entries; + int ret, newentry, num_new_entries; struct exfat_dentry *epmov, *epnew; - struct super_block *sb = inode->i_sb; - struct buffer_head *mov_bh, *new_bh; - - epmov = exfat_get_dentry(sb, p_olddir, oldentry, &mov_bh); - if (!epmov) - return -EIO; - - num_old_entries = exfat_count_ext_entries(sb, p_olddir, oldentry, - epmov); - if (num_old_entries < 0) - return -EIO; - num_old_entries++; + struct exfat_entry_set_cache mov_es, new_es; + struct exfat_chain newdir; num_new_entries = exfat_calc_num_entries(p_uniname); if (num_new_entries < 0) return num_new_entries; - newentry = exfat_find_empty_entry(inode, p_newdir, num_new_entries); - if (newentry < 0) - return newentry; /* -EIO or -ENOSPC */ - - epnew = exfat_get_dentry(sb, p_newdir, newentry, &new_bh); - if (!epnew) + ret = exfat_get_dentry_set_by_ei(&mov_es, parent_inode->i_sb, ei); + if (ret) return -EIO; - *epnew = *epmov; - if (exfat_get_entry_type(epnew) == TYPE_FILE) { - epnew->dentry.file.attr |= cpu_to_le16(ATTR_ARCHIVE); - ei->attr |= ATTR_ARCHIVE; + newentry = exfat_find_empty_entry(parent_inode, &newdir, + num_new_entries, &new_es); + if (newentry < 0) { + ret = newentry; /* -EIO or -ENOSPC */ + goto put_mov_es; } - exfat_update_bh(new_bh, IS_DIRSYNC(inode)); - brelse(mov_bh); - brelse(new_bh); - epmov = exfat_get_dentry(sb, p_olddir, oldentry + 1, &mov_bh); - if (!epmov) - return -EIO; - epnew = exfat_get_dentry(sb, p_newdir, newentry + 1, &new_bh); - if (!epnew) { - brelse(mov_bh); - return -EIO; + epmov = exfat_get_dentry_cached(&mov_es, ES_IDX_FILE); + epnew = exfat_get_dentry_cached(&new_es, ES_IDX_FILE); + *epnew = *epmov; + if (exfat_get_entry_type(epnew) == TYPE_FILE) { + epnew->dentry.file.attr |= cpu_to_le16(EXFAT_ATTR_ARCHIVE); + ei->attr |= EXFAT_ATTR_ARCHIVE; } + epmov = exfat_get_dentry_cached(&mov_es, ES_IDX_STREAM); + epnew = exfat_get_dentry_cached(&new_es, ES_IDX_STREAM); *epnew = *epmov; - exfat_update_bh(new_bh, IS_DIRSYNC(inode)); - brelse(mov_bh); - brelse(new_bh); - ret = exfat_init_ext_entry(inode, p_newdir, newentry, num_new_entries, - p_uniname); + exfat_init_ext_entry(&new_es, num_new_entries, p_uniname); + exfat_remove_entries(parent_inode, &mov_es, ES_IDX_FILE); + + ei->dir = newdir; + ei->entry = newentry; + + ret = exfat_put_dentry_set(&new_es, IS_DIRSYNC(parent_inode)); if (ret) - return ret; + goto put_mov_es; - exfat_remove_entries(inode, p_olddir, oldentry, 0, num_old_entries); + return exfat_put_dentry_set(&mov_es, IS_DIRSYNC(parent_inode)); - exfat_chain_set(&ei->dir, p_newdir->dir, p_newdir->size, - p_newdir->flags); +put_mov_es: + exfat_put_dentry_set(&mov_es, false); - ei->entry = newentry; - return 0; + return ret; } /* rename or move a old file into a new file */ @@ -1156,20 +1138,12 @@ static int __exfat_rename(struct inode *old_parent_inode, struct dentry *new_dentry) { int ret; - int dentry; - struct exfat_chain olddir, newdir; - struct exfat_chain *p_dir = NULL; struct exfat_uni_name uni_name; - struct exfat_dentry *ep; struct super_block *sb = old_parent_inode->i_sb; struct exfat_sb_info *sbi = EXFAT_SB(sb); const unsigned char *new_path = new_dentry->d_name.name; struct inode *new_inode = new_dentry->d_inode; - int num_entries; struct exfat_inode_info *new_ei = NULL; - unsigned int new_entry_type = TYPE_UNUSED; - int new_entry = 0; - struct buffer_head *new_bh = NULL; /* check the validity of pointer parameters */ if (new_path == NULL || strlen(new_path) == 0) @@ -1180,11 +1154,6 @@ static int __exfat_rename(struct inode *old_parent_inode, return -ENOENT; } - exfat_chain_set(&olddir, EXFAT_I(old_parent_inode)->start_clu, - EXFAT_B_TO_CLU_ROUND_UP(i_size_read(old_parent_inode), sbi), - EXFAT_I(old_parent_inode)->flags); - dentry = ei->entry; - /* check whether new dir is existing directory and empty */ if (new_inode) { ret = -EIO; @@ -1195,17 +1164,8 @@ static int __exfat_rename(struct inode *old_parent_inode, goto out; } - p_dir = &(new_ei->dir); - new_entry = new_ei->entry; - ep = exfat_get_dentry(sb, p_dir, new_entry, &new_bh); - if (!ep) - goto out; - - new_entry_type = exfat_get_entry_type(ep); - brelse(new_bh); - /* if new_inode exists, update ei */ - if (new_entry_type == TYPE_DIR) { + if (S_ISDIR(new_inode->i_mode)) { struct exfat_chain new_clu; new_clu.dir = new_ei->start_clu; @@ -1221,43 +1181,36 @@ static int __exfat_rename(struct inode *old_parent_inode, } /* check the validity of directory name in the given new pathname */ - ret = exfat_resolve_path(new_parent_inode, new_path, &newdir, - &uni_name); + ret = exfat_resolve_path(new_parent_inode, new_path, &uni_name); if (ret) goto out; exfat_set_volume_dirty(sb); - if (olddir.dir == newdir.dir) - ret = exfat_rename_file(new_parent_inode, &olddir, dentry, - &uni_name, ei); + if (new_parent_inode == old_parent_inode) + ret = exfat_rename_file(new_parent_inode, &uni_name, ei); else - ret = exfat_move_file(new_parent_inode, &olddir, dentry, - &newdir, &uni_name, ei); + ret = exfat_move_file(new_parent_inode, &uni_name, ei); if (!ret && new_inode) { + struct exfat_entry_set_cache es; + /* delete entries of new_dir */ - ep = exfat_get_dentry(sb, p_dir, new_entry, &new_bh); - if (!ep) { + ret = exfat_get_dentry_set_by_ei(&es, sb, new_ei); + if (ret) { ret = -EIO; goto del_out; } - num_entries = exfat_count_ext_entries(sb, p_dir, new_entry, ep); - if (num_entries < 0) { - ret = -EIO; - goto del_out; - } - brelse(new_bh); + exfat_remove_entries(new_inode, &es, ES_IDX_FILE); - if (exfat_remove_entries(new_inode, p_dir, new_entry, 0, - num_entries + 1)) { - ret = -EIO; + ret = exfat_put_dentry_set(&es, IS_DIRSYNC(new_inode)); + if (ret) goto del_out; - } /* Free the clusters if new_inode is a dir(as if exfat_rmdir) */ - if (new_entry_type == TYPE_DIR) { + if (S_ISDIR(new_inode->i_mode) && + new_ei->start_clu != EXFAT_EOF_CLUSTER) { /* new_ei, new_clu_to_free */ struct exfat_chain new_clu_to_free; @@ -1271,6 +1224,7 @@ static int __exfat_rename(struct inode *old_parent_inode, } i_size_write(new_inode, 0); + new_ei->valid_size = 0; new_ei->start_clu = EXFAT_EOF_CLUSTER; new_ei->flags = ALLOC_NO_FAT_CHAIN; } @@ -1285,7 +1239,7 @@ out: return ret; } -static int exfat_rename(struct user_namespace *mnt_userns, +static int exfat_rename(struct mnt_idmap *idmap, struct inode *old_dir, struct dentry *old_dentry, struct inode *new_dir, struct dentry *new_dentry, unsigned int flags) @@ -1294,6 +1248,7 @@ static int exfat_rename(struct user_namespace *mnt_userns, struct super_block *sb = old_dir->i_sb; loff_t i_pos; int err; + loff_t size = i_size_read(new_dir); /* * The VFS already checks for existence, so for local filesystems @@ -1312,10 +1267,10 @@ static int exfat_rename(struct user_namespace *mnt_userns, goto unlock; inode_inc_iversion(new_dir); - new_dir->i_ctime = new_dir->i_mtime = new_dir->i_atime = - EXFAT_I(new_dir)->i_crtime = current_time(new_dir); - exfat_truncate_atime(&new_dir->i_atime); - if (IS_DIRSYNC(new_dir)) + simple_rename_timestamp(old_dir, old_dentry, new_dir, new_dentry); + EXFAT_I(new_dir)->i_crtime = current_time(new_dir); + exfat_truncate_inode_atime(new_dir); + if (IS_DIRSYNC(new_dir) && size != i_size_read(new_dir)) exfat_sync_inode(new_dir); else mark_inode_dirty(new_dir); @@ -1336,10 +1291,7 @@ static int exfat_rename(struct user_namespace *mnt_userns, } inode_inc_iversion(old_dir); - old_dir->i_ctime = old_dir->i_mtime = current_time(old_dir); - if (IS_DIRSYNC(old_dir)) - exfat_sync_inode(old_dir); - else + if (new_dir != old_dir) mark_inode_dirty(old_dir); if (new_inode) { @@ -1354,8 +1306,7 @@ static int exfat_rename(struct user_namespace *mnt_userns, exfat_warn(sb, "abnormal access to an inode dropped"); WARN_ON(new_inode->i_nlink == 0); } - new_inode->i_ctime = EXFAT_I(new_inode)->i_crtime = - current_time(new_inode); + EXFAT_I(new_inode)->i_crtime = current_time(new_inode); } unlock: diff --git a/fs/exfat/nls.c b/fs/exfat/nls.c index 705710f93e2d..57db08a5271c 100644 --- a/fs/exfat/nls.c +++ b/fs/exfat/nls.c @@ -6,7 +6,7 @@ #include <linux/string.h> #include <linux/slab.h> #include <linux/buffer_head.h> -#include <asm/unaligned.h> +#include <linux/unaligned.h> #include "exfat_raw.h" #include "exfat_fs.h" @@ -616,9 +616,6 @@ static int exfat_nls_to_ucs2(struct super_block *sb, unilen++; } - if (p_cstring[i] != '\0') - lossy |= NLS_NAME_OVERLEN; - *uniname = '\0'; p_uniname->name_len = unilen; p_uniname->name_hash = exfat_calc_chksum16(upname, unilen << 1, 0, @@ -655,7 +652,6 @@ static int exfat_load_upcase_table(struct super_block *sb, unsigned int sect_size = sb->s_blocksize; unsigned int i, index = 0; u32 chksum = 0; - int ret; unsigned char skip = false; unsigned short *upcase_table; @@ -673,8 +669,7 @@ static int exfat_load_upcase_table(struct super_block *sb, if (!bh) { exfat_err(sb, "failed to read sector(0x%llx)", (unsigned long long)sector); - ret = -EIO; - goto free_table; + return -EIO; } sector++; for (i = 0; i < sect_size && index <= 0xFFFF; i += 2) { @@ -701,15 +696,12 @@ static int exfat_load_upcase_table(struct super_block *sb, exfat_err(sb, "failed to load upcase table (idx : 0x%08x, chksum : 0x%08x, utbl_chksum : 0x%08x)", index, chksum, utbl_checksum); - ret = -EINVAL; -free_table: - exfat_free_upcase_table(sbi); - return ret; + return -EINVAL; } static int exfat_load_default_upcase_table(struct super_block *sb) { - int i, ret = -EIO; + int i; struct exfat_sb_info *sbi = EXFAT_SB(sb); unsigned char skip = false; unsigned short uni = 0, *upcase_table; @@ -740,8 +732,7 @@ static int exfat_load_default_upcase_table(struct super_block *sb) return 0; /* FATAL error: default upcase table has error */ - exfat_free_upcase_table(sbi); - return ret; + return -EIO; } int exfat_create_upcase_table(struct super_block *sb) @@ -785,14 +776,17 @@ int exfat_create_upcase_table(struct super_block *sb) le32_to_cpu(ep->dentry.upcase.checksum)); brelse(bh); - if (ret && ret != -EIO) + if (ret && ret != -EIO) { + /* free memory from exfat_load_upcase_table call */ + exfat_free_upcase_table(sbi); goto load_default; + } /* load successfully */ return ret; } - if (exfat_get_next_cluster(sb, &(clu.dir))) + if (exfat_get_next_cluster(sb, &clu.dir)) return -EIO; } @@ -804,4 +798,5 @@ load_default: void exfat_free_upcase_table(struct exfat_sb_info *sbi) { kvfree(sbi->vol_utbl); + sbi->vol_utbl = NULL; } diff --git a/fs/exfat/super.c b/fs/exfat/super.c index 35f0305cd493..10e872a99663 100644 --- a/fs/exfat/super.c +++ b/fs/exfat/super.c @@ -31,14 +31,14 @@ static void exfat_free_iocharset(struct exfat_sb_info *sbi) kfree(sbi->options.iocharset); } -static void exfat_delayed_free(struct rcu_head *p) +static void exfat_set_iocharset(struct exfat_mount_options *opts, + char *iocharset) { - struct exfat_sb_info *sbi = container_of(p, struct exfat_sb_info, rcu); - - unload_nls(sbi->nls_io); - exfat_free_iocharset(sbi); - exfat_free_upcase_table(sbi); - kfree(sbi); + opts->iocharset = iocharset; + if (!strcmp(opts->iocharset, "utf8")) + opts->utf8 = 1; + else + opts->utf8 = 0; } static void exfat_put_super(struct super_block *sb) @@ -46,28 +46,10 @@ static void exfat_put_super(struct super_block *sb) struct exfat_sb_info *sbi = EXFAT_SB(sb); mutex_lock(&sbi->s_lock); + exfat_clear_volume_dirty(sb); exfat_free_bitmap(sbi); brelse(sbi->boot_bh); mutex_unlock(&sbi->s_lock); - - call_rcu(&sbi->rcu, exfat_delayed_free); -} - -static int exfat_sync_fs(struct super_block *sb, int wait) -{ - struct exfat_sb_info *sbi = EXFAT_SB(sb); - int err = 0; - - if (!wait) - return 0; - - /* If there are some dirty buffers in the bdev inode */ - mutex_lock(&sbi->s_lock); - sync_blockdev(sb->s_bdev); - if (exfat_clear_volume_dirty(sb)) - err = -EIO; - mutex_unlock(&sbi->s_lock); - return err; } static int exfat_statfs(struct dentry *dentry, struct kstatfs *buf) @@ -76,15 +58,6 @@ static int exfat_statfs(struct dentry *dentry, struct kstatfs *buf) struct exfat_sb_info *sbi = EXFAT_SB(sb); unsigned long long id = huge_encode_dev(sb->s_bdev->bd_dev); - if (sbi->used_clusters == EXFAT_CLUSTERS_UNTRACKED) { - mutex_lock(&sbi->s_lock); - if (exfat_count_used_clusters(sb, &sbi->used_clusters)) { - mutex_unlock(&sbi->s_lock); - return -EIO; - } - mutex_unlock(&sbi->s_lock); - } - buf->f_type = sb->s_magic; buf->f_bsize = sbi->cluster_size; buf->f_blocks = sbi->num_clusters - 2; /* clu 0 & 1 */ @@ -174,9 +147,46 @@ static int exfat_show_options(struct seq_file *m, struct dentry *root) seq_puts(m, ",sys_tz"); else if (opts->time_offset) seq_printf(m, ",time_offset=%d", opts->time_offset); + if (opts->zero_size_dir) + seq_puts(m, ",zero_size_dir"); + return 0; +} + +int exfat_force_shutdown(struct super_block *sb, u32 flags) +{ + int ret; + struct exfat_sb_info *sbi = sb->s_fs_info; + struct exfat_mount_options *opts = &sbi->options; + + if (exfat_forced_shutdown(sb)) + return 0; + + switch (flags) { + case EXFAT_GOING_DOWN_DEFAULT: + case EXFAT_GOING_DOWN_FULLSYNC: + ret = bdev_freeze(sb->s_bdev); + if (ret) + return ret; + bdev_thaw(sb->s_bdev); + set_bit(EXFAT_FLAGS_SHUTDOWN, &sbi->s_exfat_flags); + break; + case EXFAT_GOING_DOWN_NOSYNC: + set_bit(EXFAT_FLAGS_SHUTDOWN, &sbi->s_exfat_flags); + break; + default: + return -EINVAL; + } + + if (opts->discard) + opts->discard = 0; return 0; } +static void exfat_shutdown(struct super_block *sb) +{ + exfat_force_shutdown(sb, EXFAT_GOING_DOWN_NOSYNC); +} + static struct inode *exfat_alloc_inode(struct super_block *sb) { struct exfat_inode_info *ei; @@ -200,9 +210,9 @@ static const struct super_operations exfat_sops = { .write_inode = exfat_write_inode, .evict_inode = exfat_evict_inode, .put_super = exfat_put_super, - .sync_fs = exfat_sync_fs, .statfs = exfat_statfs, .show_options = exfat_show_options, + .shutdown = exfat_shutdown, }; enum { @@ -218,6 +228,7 @@ enum { Opt_keep_last_dots, Opt_sys_tz, Opt_time_offset, + Opt_zero_size_dir, /* Deprecated options */ Opt_utf8, @@ -234,18 +245,19 @@ static const struct constant_table exfat_param_enums[] = { }; static const struct fs_parameter_spec exfat_parameters[] = { - fsparam_u32("uid", Opt_uid), - fsparam_u32("gid", Opt_gid), + fsparam_uid("uid", Opt_uid), + fsparam_gid("gid", Opt_gid), fsparam_u32oct("umask", Opt_umask), fsparam_u32oct("dmask", Opt_dmask), fsparam_u32oct("fmask", Opt_fmask), fsparam_u32oct("allow_utime", Opt_allow_utime), fsparam_string("iocharset", Opt_charset), fsparam_enum("errors", Opt_errors, exfat_param_enums), - fsparam_flag("discard", Opt_discard), + fsparam_flag_no("discard", Opt_discard), fsparam_flag("keep_last_dots", Opt_keep_last_dots), fsparam_flag("sys_tz", Opt_sys_tz), fsparam_s32("time_offset", Opt_time_offset), + fsparam_flag_no("zero_size_dir", Opt_zero_size_dir), __fsparam(NULL, "utf8", Opt_utf8, fs_param_deprecated, NULL), __fsparam(NULL, "debug", Opt_debug, fs_param_deprecated, @@ -270,10 +282,10 @@ static int exfat_parse_param(struct fs_context *fc, struct fs_parameter *param) switch (opt) { case Opt_uid: - opts->fs_uid = make_kuid(current_user_ns(), result.uint_32); + opts->fs_uid = result.uid; break; case Opt_gid: - opts->fs_gid = make_kgid(current_user_ns(), result.uint_32); + opts->fs_gid = result.gid; break; case Opt_umask: opts->fs_fmask = result.uint_32; @@ -290,14 +302,14 @@ static int exfat_parse_param(struct fs_context *fc, struct fs_parameter *param) break; case Opt_charset: exfat_free_iocharset(sbi); - opts->iocharset = param->string; + exfat_set_iocharset(opts, param->string); param->string = NULL; break; case Opt_errors: opts->errors = result.uint_32; break; case Opt_discard: - opts->discard = 1; + opts->discard = !result.negated; break; case Opt_keep_last_dots: opts->keep_last_dots = 1; @@ -314,6 +326,9 @@ static int exfat_parse_param(struct fs_context *fc, struct fs_parameter *param) return -EINVAL; opts->time_offset = result.int_32; break; + case Opt_zero_size_dir: + opts->zero_size_dir = !result.negated; + break; case Opt_utf8: case Opt_debug: case Opt_namecase: @@ -336,13 +351,12 @@ static void exfat_hash_init(struct super_block *sb) INIT_HLIST_HEAD(&sbi->inode_hashtable[i]); } -static int exfat_read_root(struct inode *inode) +static int exfat_read_root(struct inode *inode, struct exfat_chain *root_clu) { struct super_block *sb = inode->i_sb; struct exfat_sb_info *sbi = EXFAT_SB(sb); struct exfat_inode_info *ei = EXFAT_I(inode); - struct exfat_chain cdir; - int num_subdirs, num_clu = 0; + int num_subdirs; exfat_chain_set(&ei->dir, sbi->root_dir, 0, ALLOC_FAT_CHAIN); ei->entry = -1; @@ -355,12 +369,9 @@ static int exfat_read_root(struct inode *inode) ei->hint_stat.clu = sbi->root_dir; ei->hint_femp.eidx = EXFAT_HINT_NONE; - exfat_chain_set(&cdir, sbi->root_dir, 0, ALLOC_FAT_CHAIN); - if (exfat_count_num_clusters(sb, &cdir, &num_clu)) - return -EIO; - i_size_write(inode, num_clu << sbi->cluster_size_bits); + i_size_write(inode, EXFAT_CLU_TO_B(root_clu->size, sbi)); - num_subdirs = exfat_count_dir_entries(sb, &cdir); + num_subdirs = exfat_count_dir_entries(sb, root_clu); if (num_subdirs < 0) return -EIO; set_nlink(inode, num_subdirs + EXFAT_MIN_SUBDIR); @@ -369,20 +380,16 @@ static int exfat_read_root(struct inode *inode) inode->i_gid = sbi->options.fs_gid; inode_inc_iversion(inode); inode->i_generation = 0; - inode->i_mode = exfat_make_mode(sbi, ATTR_SUBDIR, 0777); + inode->i_mode = exfat_make_mode(sbi, EXFAT_ATTR_SUBDIR, 0777); inode->i_op = &exfat_dir_inode_operations; inode->i_fop = &exfat_dir_operations; - inode->i_blocks = round_up(i_size_read(inode), sbi->cluster_size) >> - inode->i_blkbits; + inode->i_blocks = round_up(i_size_read(inode), sbi->cluster_size) >> 9; ei->i_pos = ((loff_t)sbi->root_dir << 32) | 0xffffffff; - ei->i_size_aligned = i_size_read(inode); - ei->i_size_ondisk = i_size_read(inode); - exfat_save_attr(inode, ATTR_SUBDIR); - inode->i_mtime = inode->i_atime = inode->i_ctime = ei->i_crtime = - current_time(inode); - exfat_truncate_atime(&inode->i_atime); + exfat_save_attr(inode, EXFAT_ATTR_SUBDIR); + ei->i_crtime = simple_inode_init_ts(inode); + exfat_truncate_inode_atime(inode); return 0; } @@ -426,7 +433,10 @@ static int exfat_read_boot_sector(struct super_block *sb) struct exfat_sb_info *sbi = EXFAT_SB(sb); /* set block size to read super block */ - sb_min_blocksize(sb, 512); + if (!sb_min_blocksize(sb, 512)) { + exfat_err(sb, "unable to set blocksize"); + return -EINVAL; + } /* read boot sector */ sbi->boot_bh = sb_bread(sb, 0); @@ -501,7 +511,6 @@ static int exfat_read_boot_sector(struct super_block *sb) sbi->vol_flags = le16_to_cpu(p_boot->vol_flags); sbi->vol_flags_persistent = sbi->vol_flags & (VOLUME_DIRTY | MEDIA_FAILURE); sbi->clu_srch_ptr = EXFAT_FIRST_CLUSTER; - sbi->used_clusters = EXFAT_CLUSTERS_UNTRACKED; /* check consistencies */ if ((u64)sbi->num_FAT_sectors << p_boot->sect_size_bits < @@ -578,7 +587,8 @@ static int exfat_verify_boot_region(struct super_block *sb) } /* mount the file system volume */ -static int __exfat_fill_super(struct super_block *sb) +static int __exfat_fill_super(struct super_block *sb, + struct exfat_chain *root_clu) { int ret; struct exfat_sb_info *sbi = EXFAT_SB(sb); @@ -595,6 +605,18 @@ static int __exfat_fill_super(struct super_block *sb) goto free_bh; } + /* + * Call exfat_count_num_cluster() before searching for up-case and + * bitmap directory entries to avoid infinite loop if they are missing + * and the cluster chain includes a loop. + */ + exfat_chain_set(root_clu, sbi->root_dir, 0, ALLOC_FAT_CHAIN); + ret = exfat_count_num_clusters(sb, root_clu, &root_clu->size); + if (ret) { + exfat_err(sb, "failed to count the number of clusters in root"); + goto free_bh; + } + ret = exfat_create_upcase_table(sb); if (ret) { exfat_err(sb, "failed to load upcase table"); @@ -604,7 +626,18 @@ static int __exfat_fill_super(struct super_block *sb) ret = exfat_load_bitmap(sb); if (ret) { exfat_err(sb, "failed to load alloc-bitmap"); - goto free_upcase_table; + goto free_bh; + } + + if (!exfat_test_bitmap(sb, sbi->root_dir)) { + exfat_warn(sb, "failed to test first cluster bit of root dir(%u)", + sbi->root_dir); + /* + * The first cluster bit of the root directory should never + * be unset except when storage is corrupted. This bit is + * set to allow operations after mount. + */ + exfat_set_bitmap(sb, sbi->root_dir, false); } ret = exfat_count_used_clusters(sb, &sbi->used_clusters); @@ -617,8 +650,6 @@ static int __exfat_fill_super(struct super_block *sb) free_alloc_bitmap: exfat_free_bitmap(sbi); -free_upcase_table: - exfat_free_upcase_table(sbi); free_bh: brelse(sbi->boot_bh); return ret; @@ -629,6 +660,7 @@ static int exfat_fill_super(struct super_block *sb, struct fs_context *fc) struct exfat_sb_info *sbi = sb->s_fs_info; struct exfat_mount_options *opts = &sbi->options; struct inode *root_inode; + struct exfat_chain root_clu; int err; if (opts->allow_utime == (unsigned short)-1) @@ -647,7 +679,7 @@ static int exfat_fill_super(struct super_block *sb, struct fs_context *fc) sb->s_time_min = EXFAT_MIN_TIMESTAMP_SECS; sb->s_time_max = EXFAT_MAX_TIMESTAMP_SECS; - err = __exfat_fill_super(sb); + err = __exfat_fill_super(sb, &root_clu); if (err) { exfat_err(sb, "failed to recognize exfat type"); goto check_nls_io; @@ -656,8 +688,8 @@ static int exfat_fill_super(struct super_block *sb, struct fs_context *fc) /* set up enough so that it can read an inode */ exfat_hash_init(sb); - if (!strcmp(sbi->options.iocharset, "utf8")) - opts->utf8 = 1; + if (sbi->options.utf8) + set_default_d_op(sb, &exfat_utf8_dentry_ops); else { sbi->nls_io = load_nls(sbi->options.iocharset); if (!sbi->nls_io) { @@ -666,13 +698,9 @@ static int exfat_fill_super(struct super_block *sb, struct fs_context *fc) err = -EINVAL; goto free_table; } + set_default_d_op(sb, &exfat_dentry_ops); } - if (sbi->options.utf8) - sb->s_d_op = &exfat_utf8_dentry_ops; - else - sb->s_d_op = &exfat_dentry_ops; - root_inode = new_inode(sb); if (!root_inode) { exfat_err(sb, "failed to allocate root inode"); @@ -682,7 +710,7 @@ static int exfat_fill_super(struct super_block *sb, struct fs_context *fc) root_inode->i_ino = EXFAT_ROOT_INO; inode_set_iversion(root_inode, 1); - err = exfat_read_root(root_inode); + err = exfat_read_root(root_inode, &root_clu); if (err) { exfat_err(sb, "failed to initialize root inode"); goto put_inode; @@ -705,15 +733,10 @@ put_inode: sb->s_root = NULL; free_table: - exfat_free_upcase_table(sbi); exfat_free_bitmap(sbi); brelse(sbi->boot_bh); check_nls_io: - unload_nls(sbi->nls_io); - exfat_free_iocharset(sbi); - sb->s_fs_info = NULL; - kfree(sbi); return err; } @@ -722,22 +745,62 @@ static int exfat_get_tree(struct fs_context *fc) return get_tree_bdev(fc, exfat_fill_super); } +static void exfat_free_sbi(struct exfat_sb_info *sbi) +{ + exfat_free_iocharset(sbi); + kfree(sbi); +} + static void exfat_free(struct fs_context *fc) { struct exfat_sb_info *sbi = fc->s_fs_info; - if (sbi) { - exfat_free_iocharset(sbi); - kfree(sbi); - } + if (sbi) + exfat_free_sbi(sbi); } static int exfat_reconfigure(struct fs_context *fc) { + struct super_block *sb = fc->root->d_sb; + struct exfat_sb_info *remount_sbi = fc->s_fs_info; + struct exfat_sb_info *sbi = EXFAT_SB(sb); + struct exfat_mount_options *new_opts = &remount_sbi->options; + struct exfat_mount_options *cur_opts = &sbi->options; + fc->sb_flags |= SB_NODIRATIME; - /* volume flag will be updated in exfat_sync_fs */ - sync_filesystem(fc->root->d_sb); + sync_filesystem(sb); + mutex_lock(&sbi->s_lock); + exfat_clear_volume_dirty(sb); + mutex_unlock(&sbi->s_lock); + + if (new_opts->allow_utime == (unsigned short)-1) + new_opts->allow_utime = ~new_opts->fs_dmask & 0022; + + /* + * Since the old settings of these mount options are cached in + * inodes or dentries, they cannot be modified dynamically. + */ + if (strcmp(new_opts->iocharset, cur_opts->iocharset) || + new_opts->keep_last_dots != cur_opts->keep_last_dots || + new_opts->sys_tz != cur_opts->sys_tz || + new_opts->time_offset != cur_opts->time_offset || + !uid_eq(new_opts->fs_uid, cur_opts->fs_uid) || + !gid_eq(new_opts->fs_gid, cur_opts->fs_gid) || + new_opts->fs_fmask != cur_opts->fs_fmask || + new_opts->fs_dmask != cur_opts->fs_dmask || + new_opts->allow_utime != cur_opts->allow_utime) + return -EINVAL; + + if (new_opts->discard != cur_opts->discard && + new_opts->discard && + !bdev_max_discard_sectors(sb->s_bdev)) { + exfat_warn(sb, "remounting with \"discard\" option, but the device does not support discard"); + return -EINVAL; + } + + swap(*cur_opts, *new_opts); + return 0; } @@ -761,26 +824,55 @@ static int exfat_init_fs_context(struct fs_context *fc) ratelimit_state_init(&sbi->ratelimit, DEFAULT_RATELIMIT_INTERVAL, DEFAULT_RATELIMIT_BURST); - sbi->options.fs_uid = current_uid(); - sbi->options.fs_gid = current_gid(); - sbi->options.fs_fmask = current->fs->umask; - sbi->options.fs_dmask = current->fs->umask; + if (fc->purpose == FS_CONTEXT_FOR_RECONFIGURE && fc->root) { + struct super_block *sb = fc->root->d_sb; + struct exfat_mount_options *cur_opts = &EXFAT_SB(sb)->options; + + sbi->options.fs_uid = cur_opts->fs_uid; + sbi->options.fs_gid = cur_opts->fs_gid; + sbi->options.fs_fmask = cur_opts->fs_fmask; + sbi->options.fs_dmask = cur_opts->fs_dmask; + } else { + sbi->options.fs_uid = current_uid(); + sbi->options.fs_gid = current_gid(); + sbi->options.fs_fmask = current->fs->umask; + sbi->options.fs_dmask = current->fs->umask; + } + sbi->options.allow_utime = -1; - sbi->options.iocharset = exfat_default_iocharset; sbi->options.errors = EXFAT_ERRORS_RO; + exfat_set_iocharset(&sbi->options, exfat_default_iocharset); fc->s_fs_info = sbi; fc->ops = &exfat_context_ops; return 0; } +static void delayed_free(struct rcu_head *p) +{ + struct exfat_sb_info *sbi = container_of(p, struct exfat_sb_info, rcu); + + unload_nls(sbi->nls_io); + exfat_free_upcase_table(sbi); + exfat_free_sbi(sbi); +} + +static void exfat_kill_sb(struct super_block *sb) +{ + struct exfat_sb_info *sbi = sb->s_fs_info; + + kill_block_super(sb); + if (sbi) + call_rcu(&sbi->rcu, delayed_free); +} + static struct file_system_type exfat_fs_type = { .owner = THIS_MODULE, .name = "exfat", .init_fs_context = exfat_init_fs_context, .parameters = exfat_parameters, - .kill_sb = kill_block_super, - .fs_flags = FS_REQUIRES_DEV, + .kill_sb = exfat_kill_sb, + .fs_flags = FS_REQUIRES_DEV | FS_ALLOW_IDMAP, }; static void exfat_inode_init_once(void *foo) @@ -805,7 +897,7 @@ static int __init init_exfat_fs(void) exfat_inode_cachep = kmem_cache_create("exfat_inode_cache", sizeof(struct exfat_inode_info), - 0, SLAB_RECLAIM_ACCOUNT | SLAB_MEM_SPREAD, + 0, SLAB_RECLAIM_ACCOUNT, exfat_inode_init_once); if (!exfat_inode_cachep) { err = -ENOMEM; |
