mirror of
https://github.com/torvalds/linux.git
synced 2025-12-07 20:06:24 +00:00
Merge tag 'exfat-for-6.15-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/linkinjeon/exfat
Pull exfat updates from Namjae Jeon: - Fix random stack corruption and incorrect error returns in exfat_get_block() - Optimize exfat_get_block() by improving checking corner cases - Fix an endless loop by self-linked chain in exfat_find_last_cluster - Remove dead EXFAT_CLUSTERS_UNTRACKED codes - Add missing shutdown check - Improve the delete performance with discard mount option * tag 'exfat-for-6.15-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/linkinjeon/exfat: exfat: call bh_read in get_block only when necessary exfat: fix potential wrong error return from get_block exfat: fix missing shutdown check exfat: fix the infinite loop in exfat_find_last_cluster() exfat: fix random stack corruption after get_block exfat: remove count used cluster from exfat_statfs() exfat: support batch discard of clusters when freeing clusters
This commit is contained in:
@@ -147,7 +147,6 @@ int exfat_clear_bitmap(struct inode *inode, unsigned int clu, bool sync)
|
||||
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 -EIO;
|
||||
@@ -163,19 +162,6 @@ int exfat_clear_bitmap(struct inode *inode, unsigned int clu, bool sync)
|
||||
|
||||
exfat_update_bh(sbi->vol_amap[i], sync);
|
||||
|
||||
if (opts->discard) {
|
||||
int ret_discard;
|
||||
|
||||
ret_discard = sb_issue_discard(sb,
|
||||
exfat_cluster_to_sector(sbi, clu),
|
||||
(1 << sbi->sect_per_clus_bits), GFP_NOFS, 0);
|
||||
|
||||
if (ret_discard == -EOPNOTSUPP) {
|
||||
exfat_err(sb, "discard not supported by device, disabling");
|
||||
opts->discard = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -14,8 +14,6 @@
|
||||
|
||||
#define EXFAT_ROOT_INO 1
|
||||
|
||||
#define EXFAT_CLUSTERS_UNTRACKED (~0u)
|
||||
|
||||
/*
|
||||
* exfat error flags
|
||||
*/
|
||||
|
||||
@@ -144,6 +144,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)
|
||||
{
|
||||
@@ -196,7 +210,12 @@ static int __exfat_free_cluster(struct inode *inode, struct exfat_chain *p_chain
|
||||
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;
|
||||
@@ -215,6 +234,16 @@ static int __exfat_free_cluster(struct inode *inode, struct exfat_chain *p_chain
|
||||
|
||||
if (exfat_clear_bitmap(inode, 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++;
|
||||
|
||||
@@ -265,7 +294,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,
|
||||
|
||||
@@ -582,6 +582,9 @@ static ssize_t exfat_file_write_iter(struct kiocb *iocb, struct iov_iter *iter)
|
||||
loff_t pos = iocb->ki_pos;
|
||||
loff_t valid_size;
|
||||
|
||||
if (unlikely(exfat_forced_shutdown(inode->i_sb)))
|
||||
return -EIO;
|
||||
|
||||
inode_lock(inode);
|
||||
|
||||
valid_size = ei->valid_size;
|
||||
@@ -635,6 +638,16 @@ unlock:
|
||||
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;
|
||||
@@ -672,14 +685,26 @@ static const struct vm_operations_struct exfat_file_vm_ops = {
|
||||
|
||||
static int exfat_file_mmap(struct file *file, struct vm_area_struct *vma)
|
||||
{
|
||||
if (unlikely(exfat_forced_shutdown(file_inode(file)->i_sb)))
|
||||
return -EIO;
|
||||
|
||||
file_accessed(file);
|
||||
vma->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,
|
||||
.read_iter = exfat_file_read_iter,
|
||||
.write_iter = exfat_file_write_iter,
|
||||
.unlocked_ioctl = exfat_ioctl,
|
||||
#ifdef CONFIG_COMPAT
|
||||
@@ -687,7 +712,7 @@ const struct file_operations exfat_file_operations = {
|
||||
#endif
|
||||
.mmap = exfat_file_mmap,
|
||||
.fsync = exfat_file_fsync,
|
||||
.splice_read = filemap_splice_read,
|
||||
.splice_read = exfat_splice_read,
|
||||
.splice_write = iter_file_splice_write,
|
||||
};
|
||||
|
||||
|
||||
154
fs/exfat/inode.c
154
fs/exfat/inode.c
@@ -274,9 +274,11 @@ static int exfat_get_block(struct inode *inode, sector_t iblock,
|
||||
sector_t last_block;
|
||||
sector_t phys = 0;
|
||||
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;
|
||||
|
||||
@@ -305,77 +307,99 @@ static int exfat_get_block(struct inode *inode, sector_t iblock,
|
||||
if (buffer_delay(bh_result))
|
||||
clear_buffer_delay(bh_result);
|
||||
|
||||
if (create) {
|
||||
/*
|
||||
* 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);
|
||||
|
||||
if (iblock + max_blocks < valid_blks) {
|
||||
/* The range has been written, map it */
|
||||
goto done;
|
||||
} else if (iblock < valid_blks) {
|
||||
/*
|
||||
* The range has been partially written,
|
||||
* map the written part.
|
||||
*/
|
||||
max_blocks = valid_blks - iblock;
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* The area has not been written, map and mark as new. */
|
||||
set_buffer_new(bh_result);
|
||||
|
||||
ei->valid_size = EXFAT_BLK_TO_B(iblock + max_blocks, sb);
|
||||
mark_inode_dirty(inode);
|
||||
} else {
|
||||
else
|
||||
valid_blks = EXFAT_B_TO_BLK(ei->valid_size, sb);
|
||||
|
||||
if (iblock + max_blocks < valid_blks) {
|
||||
/* The range has been written, map it */
|
||||
goto done;
|
||||
} else if (iblock < valid_blks) {
|
||||
/*
|
||||
* The area has been partially written,
|
||||
* map the written part.
|
||||
*/
|
||||
max_blocks = valid_blks - iblock;
|
||||
goto done;
|
||||
} else if (iblock == valid_blks &&
|
||||
(ei->valid_size & (sb->s_blocksize - 1))) {
|
||||
/*
|
||||
* The block has been partially written,
|
||||
* zero the unwritten part and map the block.
|
||||
*/
|
||||
loff_t size, off, pos;
|
||||
/* The range has been fully written, map it */
|
||||
if (iblock + max_blocks < valid_blks)
|
||||
goto done;
|
||||
|
||||
max_blocks = 1;
|
||||
|
||||
/*
|
||||
* For direct read, the unwritten part will be zeroed in
|
||||
* exfat_direct_IO()
|
||||
*/
|
||||
if (!bh_result->b_folio)
|
||||
goto done;
|
||||
|
||||
pos = EXFAT_BLK_TO_B(iblock, sb);
|
||||
size = ei->valid_size - pos;
|
||||
off = pos & (PAGE_SIZE - 1);
|
||||
|
||||
folio_set_bh(bh_result, bh_result->b_folio, off);
|
||||
err = bh_read(bh_result, 0);
|
||||
if (err < 0)
|
||||
goto unlock_ret;
|
||||
|
||||
folio_zero_segment(bh_result->b_folio, off + size,
|
||||
off + sb->s_blocksize);
|
||||
} else {
|
||||
/*
|
||||
* The range has not been written, clear the mapped flag
|
||||
* to only zero the cache and do not read from disk.
|
||||
*/
|
||||
clear_buffer_mapped(bh_result);
|
||||
}
|
||||
/* 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) {
|
||||
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;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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;
|
||||
|
||||
@@ -67,15 +67,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 */
|
||||
@@ -531,7 +522,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 <
|
||||
|
||||
Reference in New Issue
Block a user