diff --git a/fs/exfat/balloc.c b/fs/exfat/balloc.c index 3a32f49f9dbd..625f2f14d4fe 100644 --- a/fs/exfat/balloc.c +++ b/fs/exfat/balloc.c @@ -74,11 +74,10 @@ 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, j, need_map_size; - sector_t sector; - unsigned int max_ra_count; + sector_t sector, end, ra; + blkcnt_t ra_cnt = 0; sbi->map_clu = le32_to_cpu(ep->dentry.bitmap.start_clu); map_size = le64_to_cpu(ep->dentry.bitmap.size); @@ -100,17 +99,12 @@ static int exfat_allocate_bitmap(struct super_block *sb, 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); + sector = ra = exfat_cluster_to_sector(sbi, sbi->map_clu); + end = sector + sbi->map_sectors - 1; + for (i = 0; i < sbi->map_sectors; i++) { /* 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); - } + exfat_blk_readahead(sb, sector + i, &ra, &ra_cnt, end); sbi->vol_amap[i] = sb_bread(sb, sector + i); if (!sbi->vol_amap[i]) diff --git a/fs/exfat/dir.c b/fs/exfat/dir.c index 3a4853693d8b..ac008ccaa97d 100644 --- a/fs/exfat/dir.c +++ b/fs/exfat/dir.c @@ -93,25 +93,19 @@ static int exfat_readdir(struct inode *inode, loff_t *cpos, struct exfat_dir_ent clu_offset = EXFAT_DEN_TO_CLU(dentry, sbi); exfat_chain_dup(&clu, &dir); - if (clu.flags == ALLOC_NO_FAT_CHAIN) { - clu.dir += clu_offset; - clu.size -= clu_offset; - } else { + if (clu.flags == ALLOC_FAT_CHAIN) { /* hint_information */ if (clu_offset > 0 && ei->hint_bmap.off != EXFAT_EOF_CLUSTER && ei->hint_bmap.off > 0 && clu_offset >= ei->hint_bmap.off) { clu_offset -= ei->hint_bmap.off; clu.dir = ei->hint_bmap.clu; - } - - while (clu_offset > 0 && clu.dir != EXFAT_EOF_CLUSTER) { - if (exfat_get_next_cluster(sb, &(clu.dir))) - return -EIO; - - clu_offset--; + clu.size -= ei->hint_bmap.off; } } + if (exfat_chain_advance(sb, &clu, clu_offset)) + return -EIO; + while (clu.dir != EXFAT_EOF_CLUSTER && dentry < max_dentries) { i = dentry & (dentries_per_clu - 1); @@ -160,15 +154,8 @@ static int exfat_readdir(struct inode *inode, loff_t *cpos, struct exfat_dir_ent return 0; } - if (clu.flags == ALLOC_NO_FAT_CHAIN) { - if (--clu.size > 0) - clu.dir++; - else - clu.dir = EXFAT_EOF_CLUSTER; - } else { - if (exfat_get_next_cluster(sb, &(clu.dir))) - return -EIO; - } + if (exfat_chain_advance(sb, &clu, 1)) + return -EIO; } out: @@ -249,7 +236,7 @@ get_new: */ if (err == -EIO) { cpos += 1 << (sb->s_blocksize_bits); - cpos &= ~(sb->s_blocksize - 1); + cpos &= ~(loff_t)(sb->s_blocksize - 1); } err = -EIO; @@ -490,6 +477,7 @@ void exfat_init_ext_entry(struct exfat_entry_set_cache *es, int num_entries, unsigned short *uniname = p_uniname->name; struct exfat_dentry *ep; + es->num_entries = num_entries; ep = exfat_get_dentry_cached(es, ES_IDX_FILE); ep->dentry.file.num_ext = (unsigned char)(num_entries - 1); @@ -561,38 +549,6 @@ int exfat_put_dentry_set(struct exfat_entry_set_cache *es, int sync) return err; } -static int exfat_walk_fat_chain(struct super_block *sb, - struct exfat_chain *p_dir, unsigned int byte_offset, - unsigned int *clu) -{ - struct exfat_sb_info *sbi = EXFAT_SB(sb); - unsigned int clu_offset; - unsigned int cur_clu; - - clu_offset = EXFAT_B_TO_CLU(byte_offset, sbi); - cur_clu = p_dir->dir; - - if (p_dir->flags == ALLOC_NO_FAT_CHAIN) { - cur_clu += clu_offset; - } else { - while (clu_offset > 0) { - if (exfat_get_next_cluster(sb, &cur_clu)) - return -EIO; - if (cur_clu == EXFAT_EOF_CLUSTER) { - exfat_fs_error(sb, - "invalid dentry access beyond EOF (clu : %u, eidx : %d)", - p_dir->dir, - EXFAT_B_TO_DEN(byte_offset)); - return -EIO; - } - clu_offset--; - } - } - - *clu = cur_clu; - return 0; -} - static int exfat_find_location(struct super_block *sb, struct exfat_chain *p_dir, int entry, sector_t *sector, int *offset) { @@ -602,10 +558,19 @@ static int exfat_find_location(struct super_block *sb, struct exfat_chain *p_dir off = EXFAT_DEN_TO_B(entry); - ret = exfat_walk_fat_chain(sb, p_dir, off, &clu); + clu = p_dir->dir; + ret = exfat_cluster_walk(sb, &clu, EXFAT_B_TO_CLU(off, sbi), p_dir->flags); if (ret) return ret; + if (clu == EXFAT_EOF_CLUSTER) { + exfat_fs_error(sb, + "unexpected early break in cluster chain (clu : %u, len : %d)", + p_dir->dir, + EXFAT_B_TO_CLU(off, sbi)); + return -EIO; + } + if (!exfat_test_bitmap(sb, clu)) { exfat_err(sb, "failed to test cluster bit(%u)", clu); return -EIO; @@ -623,44 +588,11 @@ static int exfat_find_location(struct super_block *sb, struct exfat_chain *p_dir return 0; } -#define EXFAT_MAX_RA_SIZE (128*1024) -static int exfat_dir_readahead(struct super_block *sb, sector_t sec) -{ - struct exfat_sb_info *sbi = EXFAT_SB(sb); - struct buffer_head *bh; - unsigned int max_ra_count = EXFAT_MAX_RA_SIZE >> sb->s_blocksize_bits; - unsigned int page_ra_count = PAGE_SIZE >> sb->s_blocksize_bits; - unsigned int adj_ra_count = max(sbi->sect_per_clus, page_ra_count); - unsigned int ra_count = min(adj_ra_count, max_ra_count); - - /* Read-ahead is not required */ - if (sbi->sect_per_clus == 1) - return 0; - - if (sec < sbi->data_start_sector) { - exfat_err(sb, "requested sector is invalid(sect:%llu, root:%llu)", - (unsigned long long)sec, sbi->data_start_sector); - return -EIO; - } - - /* Not sector aligned with ra_count, resize ra_count to page size */ - if ((sec - sbi->data_start_sector) & (ra_count - 1)) - ra_count = page_ra_count; - - bh = sb_find_get_block(sb, sec); - if (!bh || !buffer_uptodate(bh)) { - unsigned int i; - - for (i = 0; i < ra_count; i++) - sb_breadahead(sb, (sector_t)(sec + i)); - } - brelse(bh); - return 0; -} - struct exfat_dentry *exfat_get_dentry(struct super_block *sb, struct exfat_chain *p_dir, int entry, struct buffer_head **bh) { + struct exfat_sb_info *sbi = EXFAT_SB(sb); + unsigned int sect_per_clus = sbi->sect_per_clus; unsigned int dentries_per_page = EXFAT_B_TO_DEN(PAGE_SIZE); int off; sector_t sec; @@ -673,9 +605,18 @@ struct exfat_dentry *exfat_get_dentry(struct super_block *sb, if (exfat_find_location(sb, p_dir, entry, &sec, &off)) return NULL; - if (p_dir->dir != EXFAT_FREE_CLUSTER && - !(entry & (dentries_per_page - 1))) - exfat_dir_readahead(sb, sec); + if (sect_per_clus > 1 && + (entry & (dentries_per_page - 1)) == 0) { + sector_t ra = sec; + blkcnt_t cnt = 0; + unsigned int ra_count = sect_per_clus; + + /* Not sector aligned with ra_count, resize ra_count to page size */ + if ((sec - sbi->data_start_sector) & (ra_count - 1)) + ra_count = PAGE_SIZE >> sb->s_blocksize_bits; + + exfat_blk_readahead(sb, sec, &ra, &cnt, sec + ra_count - 1); + } *bh = sb_bread(sb, sec); if (!*bh) @@ -815,9 +756,7 @@ static int __exfat_get_dentry_set(struct exfat_entry_set_cache *es, if (exfat_is_last_sector_in_cluster(sbi, sec)) { unsigned int clu = exfat_sector_to_cluster(sbi, sec); - if (p_dir->flags == ALLOC_NO_FAT_CHAIN) - clu++; - else if (exfat_get_next_cluster(sb, &clu)) + if (exfat_cluster_walk(sb, &clu, 1, p_dir->flags)) goto put_es; sec = exfat_cluster_to_sector(sbi, clu); } else { @@ -1133,19 +1072,12 @@ rewind: step = DIRENT_STEP_FILE; } - if (clu.flags == ALLOC_NO_FAT_CHAIN) { - if (--clu.size > 0) - clu.dir++; - else - clu.dir = EXFAT_EOF_CLUSTER; - } else { - if (exfat_get_next_cluster(sb, &clu.dir)) - return -EIO; + if (exfat_chain_advance(sb, &clu, 1)) + return -EIO; - /* break if the cluster chain includes a loop */ - if (unlikely(++clu_count > EXFAT_DATA_CLUSTER_COUNT(sbi))) - goto not_found; - } + /* break if the cluster chain includes a loop */ + if (unlikely(++clu_count > EXFAT_DATA_CLUSTER_COUNT(sbi))) + goto not_found; } not_found: @@ -1180,14 +1112,7 @@ found: if (!((dentry + 1) & (dentries_per_clu - 1))) { int ret = 0; - if (clu.flags == ALLOC_NO_FAT_CHAIN) { - if (--clu.size > 0) - clu.dir++; - else - clu.dir = EXFAT_EOF_CLUSTER; - } else { - ret = exfat_get_next_cluster(sb, &clu.dir); - } + ret = exfat_chain_advance(sb, &clu, 1); if (ret || clu.dir == EXFAT_EOF_CLUSTER) { /* just initialized hint_stat */ @@ -1232,20 +1157,12 @@ int exfat_count_dir_entries(struct super_block *sb, struct exfat_chain *p_dir) count++; } - if (clu.flags == ALLOC_NO_FAT_CHAIN) { - if (--clu.size > 0) - clu.dir++; - else - clu.dir = EXFAT_EOF_CLUSTER; - } else { - if (exfat_get_next_cluster(sb, &(clu.dir))) - return -EIO; - - if (unlikely(++clu_count > sbi->used_clusters)) { - exfat_fs_error(sb, "FAT or bitmap is corrupted"); - return -EIO; - } + if (exfat_chain_advance(sb, &clu, 1)) + return -EIO; + if (unlikely(++clu_count > sbi->used_clusters)) { + exfat_fs_error(sb, "FAT or bitmap is corrupted"); + return -EIO; } } diff --git a/fs/exfat/exfat_fs.h b/fs/exfat/exfat_fs.h index 2dbed5f8ec26..89ef5368277f 100644 --- a/fs/exfat/exfat_fs.h +++ b/fs/exfat/exfat_fs.h @@ -10,6 +10,7 @@ #include #include #include +#include #include #define EXFAT_ROOT_INO 1 @@ -79,6 +80,10 @@ enum { #define EXFAT_HINT_NONE -1 #define EXFAT_MIN_SUBDIR 2 +#define EXFAT_BLK_RA_SIZE(sb) \ + (min_t(blkcnt_t, (sb)->s_bdi->ra_pages, (sb)->s_bdi->io_pages) \ + << (PAGE_SHIFT - (sb)->s_blocksize_bits)) + /* * helpers for cluster size to byte conversion. */ @@ -117,9 +122,9 @@ enum { #define FAT_ENT_SIZE (4) #define FAT_ENT_SIZE_BITS (2) #define FAT_ENT_OFFSET_SECTOR(sb, loc) (EXFAT_SB(sb)->FAT1_start_sector + \ - (((u64)loc << FAT_ENT_SIZE_BITS) >> sb->s_blocksize_bits)) + (((u64)(loc) << FAT_ENT_SIZE_BITS) >> sb->s_blocksize_bits)) #define FAT_ENT_OFFSET_BYTE_IN_SECTOR(sb, loc) \ - ((loc << FAT_ENT_SIZE_BITS) & (sb->s_blocksize - 1)) + (((loc) << FAT_ENT_SIZE_BITS) & (sb->s_blocksize - 1)) /* * helpers for bitmap. @@ -432,7 +437,8 @@ int exfat_set_volume_dirty(struct super_block *sb); int exfat_clear_volume_dirty(struct super_block *sb); /* fatent.c */ -#define exfat_get_next_cluster(sb, pclu) exfat_ent_get(sb, *(pclu), pclu, NULL) +#define exfat_get_next_cluster(sb, pclu) \ + exfat_cluster_walk(sb, (pclu), 1, ALLOC_FAT_CHAIN) int exfat_alloc_cluster(struct inode *inode, unsigned int num_alloc, struct exfat_chain *p_chain, bool sync_bmap); @@ -448,6 +454,28 @@ int exfat_find_last_cluster(struct super_block *sb, struct exfat_chain *p_chain, unsigned int *ret_clu); int exfat_count_num_clusters(struct super_block *sb, struct exfat_chain *p_chain, unsigned int *ret_count); +int exfat_blk_readahead(struct super_block *sb, sector_t sec, + sector_t *ra, blkcnt_t *ra_cnt, sector_t end); + +static inline int +exfat_cluster_walk(struct super_block *sb, unsigned int *clu, + unsigned int step, int flags) +{ + struct buffer_head *bh = NULL; + + if (flags == ALLOC_NO_FAT_CHAIN) { + (*clu) += step; + return 0; + } + + while (step--) { + if (exfat_ent_get(sb, *clu, clu, &bh)) + return -EIO; + } + brelse(bh); + + return 0; +} /* balloc.c */ int exfat_load_bitmap(struct super_block *sb); @@ -524,6 +552,27 @@ int exfat_read_volume_label(struct super_block *sb, int exfat_write_volume_label(struct super_block *sb, struct exfat_uni_name *label); +static inline int exfat_chain_advance(struct super_block *sb, + struct exfat_chain *chain, unsigned int step) +{ + unsigned int clu = chain->dir; + + if (unlikely(chain->size < step)) + return -EIO; + + if (exfat_cluster_walk(sb, &clu, step, chain->flags)) + return -EIO; + + chain->size -= step; + + if (chain->size == 0 && chain->flags == ALLOC_NO_FAT_CHAIN) + chain->dir = EXFAT_EOF_CLUSTER; + else + chain->dir = clu; + + return 0; +} + /* inode.c */ extern const struct inode_operations exfat_file_inode_operations; void exfat_sync_inode(struct inode *inode); @@ -577,7 +626,7 @@ 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); u32 exfat_calc_chksum32(void *data, int len, u32 chksum, int type); -void exfat_update_bh(struct buffer_head *bh, int sync); +int exfat_update_bh(struct buffer_head *bh, int sync); int exfat_update_bhs(struct buffer_head **bhs, int nr_bhs, int sync); void exfat_chain_set(struct exfat_chain *ec, unsigned int dir, unsigned int size, unsigned char flags); diff --git a/fs/exfat/exfat_raw.h b/fs/exfat/exfat_raw.h index 4082fa7b8c14..ec70cd35bba0 100644 --- a/fs/exfat/exfat_raw.h +++ b/fs/exfat/exfat_raw.h @@ -25,6 +25,7 @@ #define EXFAT_FIRST_CLUSTER 2 #define EXFAT_DATA_CLUSTER_COUNT(sbi) \ ((sbi)->num_clusters - EXFAT_RESERVED_CLUSTERS) +#define EXFAT_MAX_NUM_CLUSTER (0xFFFFFFF5) /* AllocationPossible and NoFatChain field in GeneralSecondaryFlags Field */ #define ALLOC_POSSIBLE 0x01 diff --git a/fs/exfat/fatent.c b/fs/exfat/fatent.c index f87576ca7032..dce0955e689a 100644 --- a/fs/exfat/fatent.c +++ b/fs/exfat/fatent.c @@ -11,11 +11,11 @@ #include "exfat_raw.h" #include "exfat_fs.h" -static int exfat_mirror_bh(struct super_block *sb, sector_t sec, - struct buffer_head *bh) +static int exfat_mirror_bh(struct super_block *sb, struct buffer_head *bh) { struct buffer_head *c_bh; struct exfat_sb_info *sbi = EXFAT_SB(sb); + sector_t sec = bh->b_blocknr; sector_t sec2; int err = 0; @@ -25,22 +25,30 @@ static int exfat_mirror_bh(struct super_block *sb, sector_t sec, if (!c_bh) return -ENOMEM; memcpy(c_bh->b_data, bh->b_data, sb->s_blocksize); - set_buffer_uptodate(c_bh); - mark_buffer_dirty(c_bh); - if (sb->s_flags & SB_SYNCHRONOUS) - err = sync_dirty_buffer(c_bh); + err = exfat_update_bh(c_bh, sb->s_flags & SB_SYNCHRONOUS); brelse(c_bh); } return err; } +static int exfat_end_bh(struct super_block *sb, struct buffer_head *bh) +{ + int err; + + err = exfat_update_bh(bh, sb->s_flags & SB_SYNCHRONOUS); + if (!err) + err = exfat_mirror_bh(sb, bh); + brelse(bh); + return err; +} + static int __exfat_ent_get(struct super_block *sb, unsigned int loc, - unsigned int *content, struct buffer_head **last) + unsigned int *content, struct buffer_head **cache) { unsigned int off; sector_t sec; - struct buffer_head *bh = last ? *last : NULL; + struct buffer_head *bh = *cache; sec = FAT_ENT_OFFSET_SECTOR(sb, loc); off = FAT_ENT_OFFSET_BYTE_IN_SECTOR(sb, loc); @@ -48,8 +56,7 @@ static int __exfat_ent_get(struct super_block *sb, unsigned int loc, if (!bh || bh->b_blocknr != sec || !buffer_uptodate(bh)) { brelse(bh); bh = sb_bread(sb, sec); - if (last) - *last = bh; + *cache = bh; if (unlikely(!bh)) return -EIO; } @@ -60,39 +67,48 @@ static int __exfat_ent_get(struct super_block *sb, unsigned int loc, if (*content > EXFAT_BAD_CLUSTER) *content = EXFAT_EOF_CLUSTER; - if (!last) - brelse(bh); + return 0; +} + +static int __exfat_ent_set(struct super_block *sb, unsigned int loc, + unsigned int content, struct buffer_head **cache) +{ + sector_t sec; + __le32 *fat_entry; + struct buffer_head *bh = cache ? *cache : NULL; + unsigned int off; + + sec = FAT_ENT_OFFSET_SECTOR(sb, loc); + off = FAT_ENT_OFFSET_BYTE_IN_SECTOR(sb, loc); + + if (!bh || bh->b_blocknr != sec || !buffer_uptodate(bh)) { + if (bh) + exfat_end_bh(sb, bh); + bh = sb_bread(sb, sec); + if (cache) + *cache = bh; + if (unlikely(!bh)) + return -EIO; + } + + fat_entry = (__le32 *)&(bh->b_data[off]); + *fat_entry = cpu_to_le32(content); + if (!cache) + exfat_end_bh(sb, bh); return 0; } int exfat_ent_set(struct super_block *sb, unsigned int loc, unsigned int content) { - unsigned int off; - sector_t sec; - __le32 *fat_entry; - struct buffer_head *bh; - - sec = FAT_ENT_OFFSET_SECTOR(sb, loc); - off = FAT_ENT_OFFSET_BYTE_IN_SECTOR(sb, loc); - - bh = sb_bread(sb, sec); - if (!bh) - return -EIO; - - fat_entry = (__le32 *)&(bh->b_data[off]); - *fat_entry = cpu_to_le32(content); - exfat_update_bh(bh, sb->s_flags & SB_SYNCHRONOUS); - exfat_mirror_bh(sb, sec, bh); - brelse(bh); - return 0; + return __exfat_ent_set(sb, loc, content, NULL); } /* * Caller must release the buffer_head if no error return. */ int exfat_ent_get(struct super_block *sb, unsigned int loc, - unsigned int *content, struct buffer_head **last) + unsigned int *content, struct buffer_head **cache) { struct exfat_sb_info *sbi = EXFAT_SB(sb); @@ -103,7 +119,7 @@ int exfat_ent_get(struct super_block *sb, unsigned int loc, goto err; } - if (unlikely(__exfat_ent_get(sb, loc, content, last))) { + if (unlikely(__exfat_ent_get(sb, loc, content, cache))) { exfat_fs_error_ratelimit(sb, "failed to access to FAT (entry 0x%08x)", loc); @@ -132,31 +148,69 @@ int exfat_ent_get(struct super_block *sb, unsigned int loc, } return 0; -err: - if (last) { - brelse(*last); - /* Avoid double release */ - *last = NULL; - } +err: + /* Avoid double release */ + brelse(*cache); + *cache = NULL; return -EIO; } +int exfat_blk_readahead(struct super_block *sb, sector_t sec, + sector_t *ra, blkcnt_t *ra_cnt, sector_t end) +{ + struct blk_plug plug; + + if (sec < *ra) + return 0; + + *ra += *ra_cnt; + + /* No blocks left (or only the last block), skip readahead. */ + if (*ra >= end) + return 0; + + *ra_cnt = min(end - *ra + 1, EXFAT_BLK_RA_SIZE(sb)); + if (*ra_cnt == 0) { + /* Move 'ra' to the end to disable readahead. */ + *ra = end; + return 0; + } + + blk_start_plug(&plug); + for (unsigned int i = 0; i < *ra_cnt; i++) + sb_breadahead(sb, *ra + i); + blk_finish_plug(&plug); + return 0; +} + int exfat_chain_cont_cluster(struct super_block *sb, unsigned int chain, unsigned int len) { + struct buffer_head *bh = NULL; + sector_t sec, end, ra; + blkcnt_t ra_cnt = 0; + if (!len) return 0; + ra = FAT_ENT_OFFSET_SECTOR(sb, chain); + end = FAT_ENT_OFFSET_SECTOR(sb, chain + len - 1); + while (len > 1) { - if (exfat_ent_set(sb, chain, chain + 1)) + sec = FAT_ENT_OFFSET_SECTOR(sb, chain); + exfat_blk_readahead(sb, sec, &ra, &ra_cnt, end); + + if (__exfat_ent_set(sb, chain, chain + 1, &bh)) return -EIO; chain++; len--; } - if (exfat_ent_set(sb, chain, EXFAT_EOF_CLUSTER)) + if (__exfat_ent_set(sb, chain, EXFAT_EOF_CLUSTER, &bh)) return -EIO; + + exfat_end_bh(sb, bh); return 0; } diff --git a/fs/exfat/file.c b/fs/exfat/file.c index 4e8d34a75b66..354bdcfe4abc 100644 --- a/fs/exfat/file.c +++ b/fs/exfat/file.c @@ -13,6 +13,7 @@ #include #include #include +#include #include "exfat_raw.h" #include "exfat_fs.h" @@ -33,6 +34,7 @@ static int exfat_cont_expand(struct inode *inode, loff_t size) return ret; num_clusters = EXFAT_B_TO_CLU(exfat_ondisk_size(inode), sbi); + /* integer overflow is already checked in inode_newsize_ok(). */ new_num_clusters = EXFAT_B_TO_CLU_ROUND_UP(size, sbi); if (new_num_clusters == num_clusters) @@ -90,6 +92,45 @@ free_clu: return -EIO; } +/* + * Preallocate space for a file. This implements exfat's fallocate file + * operation, which gets called from sys_fallocate system call. User space + * requests len bytes at offset. In contrary to fat, we only support + * FALLOC_FL_ALLOCATE_RANGE because by leaving the valid data length(VDL) + * field, it is unnecessary to zero out the newly allocated clusters. + */ +static long exfat_fallocate(struct file *file, int mode, + loff_t offset, loff_t len) +{ + struct inode *inode = file->f_mapping->host; + loff_t newsize = offset + len; + int err = 0; + + /* No support for other modes */ + if (mode != FALLOC_FL_ALLOCATE_RANGE) + return -EOPNOTSUPP; + + /* No support for dir */ + if (!S_ISREG(inode->i_mode)) + return -EOPNOTSUPP; + + if (unlikely(exfat_forced_shutdown(inode->i_sb))) + return -EIO; + + inode_lock(inode); + + if (newsize <= i_size_read(inode)) + goto error; + + /* This is just an expanding truncate */ + err = exfat_cont_expand(inode, newsize); + +error: + inode_unlock(inode); + + return err; +} + static bool exfat_allow_set_time(struct mnt_idmap *idmap, struct exfat_sb_info *sbi, struct inode *inode) { @@ -771,6 +812,7 @@ const struct file_operations exfat_file_operations = { .fsync = exfat_file_fsync, .splice_read = exfat_splice_read, .splice_write = iter_file_splice_write, + .fallocate = exfat_fallocate, .setlease = generic_setlease, }; diff --git a/fs/exfat/inode.c b/fs/exfat/inode.c index 04559b88482d..1ea4c740fef9 100644 --- a/fs/exfat/inode.c +++ b/fs/exfat/inode.c @@ -204,8 +204,9 @@ static int exfat_map_cluster(struct inode *inode, unsigned int clu_offset, * so fat-chain should be synced with * alloc-bitmap */ - exfat_chain_cont_cluster(sb, ei->start_clu, - num_clusters); + if (exfat_chain_cont_cluster(sb, ei->start_clu, + num_clusters)) + return -EIO; ei->flags = ALLOC_FAT_CHAIN; } if (new_clu.flags == ALLOC_FAT_CHAIN) @@ -213,7 +214,6 @@ static int exfat_map_cluster(struct inode *inode, unsigned int clu_offset, return -EIO; } - num_clusters += num_to_be_allocated; *clu = new_clu.dir; inode->i_blocks += EXFAT_CLU_TO_B(num_to_be_allocated, sbi) >> 9; @@ -225,15 +225,8 @@ static int exfat_map_cluster(struct inode *inode, unsigned int clu_offset, * *clu = (the first cluster of the allocated chain) => * (the last cluster of ...) */ - if (ei->flags == ALLOC_NO_FAT_CHAIN) { - *clu += num_to_be_allocated - 1; - } else { - while (num_to_be_allocated > 1) { - if (exfat_get_next_cluster(sb, clu)) - return -EIO; - num_to_be_allocated--; - } - } + if (exfat_cluster_walk(sb, clu, num_to_be_allocated - 1, ei->flags)) + return -EIO; *count = 1; } @@ -686,7 +679,7 @@ out: void exfat_evict_inode(struct inode *inode) { - truncate_inode_pages(&inode->i_data, 0); + truncate_inode_pages_final(&inode->i_data); if (!inode->i_nlink) { i_size_write(inode, 0); diff --git a/fs/exfat/misc.c b/fs/exfat/misc.c index fa8459828046..6f11a96a4ffa 100644 --- a/fs/exfat/misc.c +++ b/fs/exfat/misc.c @@ -161,13 +161,17 @@ u32 exfat_calc_chksum32(void *data, int len, u32 chksum, int type) return chksum; } -void exfat_update_bh(struct buffer_head *bh, int sync) +int exfat_update_bh(struct buffer_head *bh, int sync) { + int err = 0; + set_buffer_uptodate(bh); mark_buffer_dirty(bh); if (sync) - sync_dirty_buffer(bh); + err = sync_dirty_buffer(bh); + + return err; } int exfat_update_bhs(struct buffer_head **bhs, int nr_bhs, int sync) diff --git a/fs/exfat/namei.c b/fs/exfat/namei.c index 670116ae9ec8..2c5636634b4a 100644 --- a/fs/exfat/namei.c +++ b/fs/exfat/namei.c @@ -246,15 +246,8 @@ static int exfat_search_empty_slot(struct super_block *sb, i += ret; while (i >= dentries_per_clu) { - if (clu.flags == ALLOC_NO_FAT_CHAIN) { - if (--clu.size > 0) - clu.dir++; - else - clu.dir = EXFAT_EOF_CLUSTER; - } else { - if (exfat_get_next_cluster(sb, &clu.dir)) - return -EIO; - } + if (exfat_chain_advance(sb, &clu, 1)) + return -EIO; i -= dentries_per_clu; } @@ -365,7 +358,8 @@ int exfat_find_empty_entry(struct inode *inode, /* no-fat-chain bit is disabled, * so fat-chain should be synced with alloc-bitmap */ - exfat_chain_cont_cluster(sb, p_dir->dir, p_dir->size); + if (exfat_chain_cont_cluster(sb, p_dir->dir, p_dir->size)) + return -EIO; p_dir->flags = ALLOC_FAT_CHAIN; hint_femp.cur.flags = ALLOC_FAT_CHAIN; } @@ -873,9 +867,10 @@ static struct dentry *exfat_mkdir(struct mnt_idmap *idmap, struct inode *dir, i_pos = exfat_make_i_pos(&info); inode = exfat_build_inode(sb, &info, i_pos); - err = PTR_ERR_OR_ZERO(inode); - if (err) + if (IS_ERR(inode)) { + err = PTR_ERR(inode); goto unlock; + } inode_inc_iversion(inode); EXFAT_I(inode)->i_crtime = simple_inode_init_ts(inode); @@ -886,7 +881,7 @@ static struct dentry *exfat_mkdir(struct mnt_idmap *idmap, struct inode *dir, unlock: mutex_unlock(&EXFAT_SB(sb)->s_lock); - return ERR_PTR(err); + return err ? ERR_PTR(err) : NULL; } static int exfat_check_dir_empty(struct super_block *sb, @@ -923,19 +918,12 @@ static int exfat_check_dir_empty(struct super_block *sb, return -ENOTEMPTY; } - if (clu.flags == ALLOC_NO_FAT_CHAIN) { - if (--clu.size > 0) - clu.dir++; - else - clu.dir = EXFAT_EOF_CLUSTER; - } else { - if (exfat_get_next_cluster(sb, &(clu.dir))) - return -EIO; + if (exfat_chain_advance(sb, &clu, 1)) + return -EIO; - /* break if the cluster chain includes a loop */ - if (unlikely(++clu_count > EXFAT_DATA_CLUSTER_COUNT(sbi))) - break; - } + /* break if the cluster chain includes a loop */ + if (unlikely(++clu_count > EXFAT_DATA_CLUSTER_COUNT(sbi))) + break; } return 0; diff --git a/fs/exfat/super.c b/fs/exfat/super.c index 83396fd265cd..95d87e2d7717 100644 --- a/fs/exfat/super.c +++ b/fs/exfat/super.c @@ -531,9 +531,14 @@ static int exfat_read_boot_sector(struct super_block *sb) if (sbi->vol_flags & MEDIA_FAILURE) exfat_warn(sb, "Medium has reported failures. Some data may be lost."); - /* exFAT file size is limited by a disk volume size */ - sb->s_maxbytes = (u64)(sbi->num_clusters - EXFAT_RESERVED_CLUSTERS) << - sbi->cluster_size_bits; + /* + * Set to the max possible volume size for this volume's cluster size so + * that any integer overflow from bytes to cluster size conversion is + * checked in inode_newsize_ok(). Clamped to MAX_LFS_FILESIZE for 32-bit + * machines. + */ + sb->s_maxbytes = min(MAX_LFS_FILESIZE, + EXFAT_CLU_TO_B((loff_t)EXFAT_MAX_NUM_CLUSTER, sbi)); /* check logical sector size */ if (exfat_calibrate_blocksize(sb, 1 << p_boot->sect_size_bits))