mirror of
https://github.com/torvalds/linux.git
synced 2026-04-18 06:44:00 -04:00
Merge tag 'exfat-for-7.1-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/linkinjeon/exfat
Pull exfat updates from Namjae Jeon: - Implement FALLOC_FL_ALLOCATE_RANGE to add support for preallocating clusters without zeroing, helping to reduce file fragmentation - Add a unified block readahead helper for FAT chain conversion, bitmap allocation, and directory entry lookups - Optimize exfat_chain_cont_cluster() by caching buffer heads to minimize mark_buffer_dirty() and mirroring overhead during NO_FAT_CHAIN to FAT_CHAIN conversion - Switch to truncate_inode_pages_final() in evict_inode() to prevent BUG_ON caused by shadow entries during reclaim - Fix a 32-bit truncation bug in directory entry calculations by ensuring proper bitwise coercion - Fix sb->s_maxbytes calculation to correctly reflect the maximum possible volume size for a given cluster size, resolving xfstests generic/213 - Introduced exfat_cluster_walk() helper to traverse FAT chains by a specified step, handling both ALLOC_NO_FAT_CHAIN and ALLOC_FAT_CHAIN modes - Introduced exfat_chain_advance() helper to advance an exfat_chain structure, updating both the current cluster and remaining size - Remove dead assignments and fix Smatch warnings * tag 'exfat-for-7.1-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/linkinjeon/exfat: exfat: use exfat_chain_advance helper exfat: introduce exfat_chain_advance helper exfat: remove NULL cache pointer case in exfat_ent_get exfat: use exfat_cluster_walk helper exfat: introduce exfat_cluster_walk helper exfat: fix incorrect directory checksum after rename to shorter name exfat: fix s_maxbytes exfat: fix passing zero to ERR_PTR() in exfat_mkdir() exfat: fix error handling for FAT table operations exfat: optimize exfat_chain_cont_cluster with cached buffer heads exfat: drop redundant sec parameter from exfat_mirror_bh exfat: use readahead helper in exfat_get_dentry exfat: use readahead helper in exfat_allocate_bitmap exfat: add block readahead in exfat_chain_cont_cluster exfat: add fallocate FALLOC_FL_ALLOCATE_RANGE support exfat: Fix bitwise operation having different size exfat: Drop dead assignment of num_clusters exfat: use truncate_inode_pages_final() at evict_inode()
This commit is contained in:
@@ -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])
|
||||
|
||||
173
fs/exfat/dir.c
173
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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include <linux/ratelimit.h>
|
||||
#include <linux/nls.h>
|
||||
#include <linux/blkdev.h>
|
||||
#include <linux/backing-dev.h>
|
||||
#include <uapi/linux/exfat.h>
|
||||
|
||||
#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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#include <linux/msdos_fs.h>
|
||||
#include <linux/writeback.h>
|
||||
#include <linux/filelock.h>
|
||||
#include <linux/falloc.h>
|
||||
|
||||
#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,
|
||||
};
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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))
|
||||
|
||||
Reference in New Issue
Block a user