mirror of
https://github.com/torvalds/linux.git
synced 2024-11-21 19:41:42 +00:00
Description for this pull request:
- Clean-up unnecessary codes as ->valid_size is supported. - buffered-IO fallback is no longer needed when using direct-IO. - Move ->valid_size extension from mmap to ->page_mkwrite. This improves the overhead caused by unnecessary zero-out during mmap. - Fix memleaks from exfat_load_bitmap() and exfat_create_upcase_table(). - Add sops->shutdown and ioctl. - Add Yuezhang Mo as a reviwer. -----BEGIN PGP SIGNATURE----- iQJKBAABCgA0FiEE6NzKS6Uv/XAAGHgyZwv7A1FEIQgFAmbytEQWHGxpbmtpbmpl b25Aa2VybmVsLm9yZwAKCRBnC/sDUUQhCEqUD/sEerRjBeNi+ivTvYtxqQGaDCnj Re6gBUt138rF2qyVcX3dP0wMHVNEHzjtdJjZGuQXAKttkZ1qW1wGbz0kyIyFjRfZ MHPaaqAavDiDFqxZnJvB9xKsuU6mb0Kr0JC6mKet3KD+Q2VekePSX+3SvwRDcPNb 4CroYvJtOOWy21FKvKc2LxZBrowTElCPIhiXbHgWRhJBVhi4edrDo0391enzkKwt Is0/RzMbAsQ08Ap+TH6YIlPtA9aVSiTDyal1YaIgpXjaVxqF3MpMfPFG6+XJ8GOw k9BXM5XH5YXPZXallG8Fkx5Hh6Nrf9Vuvt68KbLQuzL6MdDEb8vTPEycQFHpapLx hk5TrL23Ok2RU/AJJXUDxii+J+3YzuTgIL6sdgJbaYb1ZYebiMzjRkwUJpH3dqg+ lx1QtYWsVRR8fTtBEle1yVbOPcuyUWUkMpKVIUseVL0EiQNpiwBSGKKuus3Cul4O KA6Kx8hYEguHAIBn5U52mzIl9Ye+j+QyRmcmA/qnObk/1h+5FKn+HgnMINex0qmz PXzI+cLta6TZKtb8+KnTNImRXCDtcvtG9wkF25M3vmzBMiLfTnEZsXKwF+fPiydw +N19vX6HVT8JpIOGhbsRQp7abLR2IhYCeZQCWdT09Ol0VUsXx87+CfsLQpM3xw4U 79nicqiwHjVP98Wjyg== =vVfO -----END PGP SIGNATURE----- Merge tag 'exfat-for-6.12-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/linkinjeon/exfat Pull exfat updates from Namjae Jeon: - Clean-up unnecessary codes as ->valid_size is supported - buffered-IO fallback is no longer needed when using direct-IO - Move ->valid_size extension from mmap to ->page_mkwrite. This improves the overhead caused by unnecessary zero-out during mmap. - Fix memleaks from exfat_load_bitmap() and exfat_create_upcase_table() - Add sops->shutdown and ioctl - Add Yuezhang Mo as a reviwer * tag 'exfat-for-6.12-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/linkinjeon/exfat: MAINTAINERS: exfat: add myself as reviewer exfat: resolve memory leak from exfat_create_upcase_table() exfat: move extend valid_size into ->page_mkwrite() exfat: fix memory leak in exfat_load_bitmap() exfat: Implement sops->shutdown and ioctl exfat: do not fallback to buffered write exfat: drop ->i_size_ondisk
This commit is contained in:
commit
4165cee7ec
@ -8466,6 +8466,7 @@ N: binfmt
|
||||
EXFAT FILE SYSTEM
|
||||
M: Namjae Jeon <linkinjeon@kernel.org>
|
||||
M: Sungjong Seo <sj1557.seo@samsung.com>
|
||||
R: Yuezhang Mo <yuezhang.mo@sony.com>
|
||||
L: linux-fsdevel@vger.kernel.org
|
||||
S: Maintained
|
||||
T: git git://git.kernel.org/pub/scm/linux/kernel/git/linkinjeon/exfat.git
|
||||
|
@ -91,11 +91,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);
|
||||
@ -103,6 +100,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))
|
||||
|
@ -10,6 +10,7 @@
|
||||
#include <linux/ratelimit.h>
|
||||
#include <linux/nls.h>
|
||||
#include <linux/blkdev.h>
|
||||
#include <uapi/linux/exfat.h>
|
||||
|
||||
#define EXFAT_ROOT_INO 1
|
||||
|
||||
@ -148,6 +149,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 */
|
||||
@ -267,6 +271,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;
|
||||
@ -309,13 +315,6 @@ 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;
|
||||
@ -338,6 +337,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.
|
||||
@ -417,6 +421,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);
|
||||
@ -461,6 +470,7 @@ 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;
|
||||
|
110
fs/exfat/file.c
110
fs/exfat/file.c
@ -29,7 +29,7 @@ static int exfat_cont_expand(struct inode *inode, loff_t size)
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
num_clusters = EXFAT_B_TO_CLU_ROUND_UP(ei->i_size_ondisk, sbi);
|
||||
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)
|
||||
@ -74,8 +74,6 @@ out:
|
||||
/* Expanded range not zeroed, do not update valid_size */
|
||||
i_size_write(inode, size);
|
||||
|
||||
ei->i_size_aligned = round_up(size, sb->s_blocksize);
|
||||
ei->i_size_ondisk = ei->i_size_aligned;
|
||||
inode->i_blocks = round_up(size, sbi->cluster_size) >> 9;
|
||||
mark_inode_dirty(inode);
|
||||
|
||||
@ -159,7 +157,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);
|
||||
|
||||
@ -245,8 +243,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);
|
||||
@ -264,17 +260,6 @@ void exfat_truncate(struct inode *inode)
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
@ -302,6 +287,9 @@ int exfat_setattr(struct mnt_idmap *idmap, 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);
|
||||
@ -485,6 +473,19 @@ 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);
|
||||
}
|
||||
|
||||
long exfat_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
struct inode *inode = file_inode(filp);
|
||||
@ -495,6 +496,8 @@ long exfat_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
|
||||
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);
|
||||
default:
|
||||
@ -515,6 +518,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;
|
||||
@ -526,32 +532,32 @@ 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_file_zeroed_range(struct file *file, loff_t start, loff_t end)
|
||||
static int exfat_extend_valid_size(struct file *file, loff_t new_valid_size)
|
||||
{
|
||||
int err;
|
||||
loff_t pos;
|
||||
struct inode *inode = file_inode(file);
|
||||
struct exfat_inode_info *ei = EXFAT_I(inode);
|
||||
struct address_space *mapping = inode->i_mapping;
|
||||
const struct address_space_operations *ops = mapping->a_ops;
|
||||
|
||||
while (start < end) {
|
||||
u32 zerofrom, len;
|
||||
pos = ei->valid_size;
|
||||
while (pos < new_valid_size) {
|
||||
u32 len;
|
||||
struct folio *folio;
|
||||
|
||||
zerofrom = start & (PAGE_SIZE - 1);
|
||||
len = PAGE_SIZE - zerofrom;
|
||||
if (start + len > end)
|
||||
len = end - start;
|
||||
len = PAGE_SIZE - (pos & (PAGE_SIZE - 1));
|
||||
if (pos + len > new_valid_size)
|
||||
len = new_valid_size - pos;
|
||||
|
||||
err = ops->write_begin(file, mapping, start, len, &folio, NULL);
|
||||
err = ops->write_begin(file, mapping, pos, len, &folio, NULL);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
folio_zero_range(folio, offset_in_folio(folio, start), len);
|
||||
|
||||
err = ops->write_end(file, mapping, start, len, len, folio, NULL);
|
||||
err = ops->write_end(file, mapping, pos, len, len, folio, NULL);
|
||||
if (err < 0)
|
||||
goto out;
|
||||
start += len;
|
||||
pos += len;
|
||||
|
||||
balance_dirty_pages_ratelimited(mapping);
|
||||
cond_resched();
|
||||
@ -579,7 +585,7 @@ static ssize_t exfat_file_write_iter(struct kiocb *iocb, struct iov_iter *iter)
|
||||
goto unlock;
|
||||
|
||||
if (pos > valid_size) {
|
||||
ret = exfat_file_zeroed_range(file, valid_size, pos);
|
||||
ret = exfat_extend_valid_size(file, pos);
|
||||
if (ret < 0 && ret != -ENOSPC) {
|
||||
exfat_err(inode->i_sb,
|
||||
"write: fail to zero from %llu to %llu(%zd)",
|
||||
@ -613,26 +619,46 @@ unlock:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int exfat_file_mmap(struct file *file, struct vm_area_struct *vma)
|
||||
static vm_fault_t exfat_page_mkwrite(struct vm_fault *vmf)
|
||||
{
|
||||
int ret;
|
||||
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 = ((loff_t)vma->vm_pgoff << PAGE_SHIFT);
|
||||
loff_t end = min_t(loff_t, i_size_read(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 ((vma->vm_flags & VM_WRITE) && ei->valid_size < end) {
|
||||
ret = exfat_file_zeroed_range(file, ei->valid_size, end);
|
||||
if (ret < 0) {
|
||||
exfat_err(inode->i_sb,
|
||||
"mmap: fail to zero from %llu to %llu(%d)",
|
||||
start, end, ret);
|
||||
return ret;
|
||||
if (ei->valid_size < end) {
|
||||
err = exfat_extend_valid_size(file, end);
|
||||
if (err < 0) {
|
||||
inode_unlock(inode);
|
||||
return vmf_fs_error(err);
|
||||
}
|
||||
}
|
||||
|
||||
return generic_file_mmap(file, vma);
|
||||
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(struct file *file, struct vm_area_struct *vma)
|
||||
{
|
||||
file_accessed(file);
|
||||
vma->vm_ops = &exfat_file_vm_ops;
|
||||
return 0;
|
||||
}
|
||||
|
||||
const struct file_operations exfat_file_operations = {
|
||||
|
@ -102,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);
|
||||
@ -130,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;
|
||||
@ -260,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)
|
||||
{
|
||||
@ -288,7 +274,6 @@ 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 pos;
|
||||
|
||||
mutex_lock(&sbi->s_lock);
|
||||
last_block = EXFAT_B_TO_BLK_ROUND_UP(i_size_read(inode), sb);
|
||||
@ -316,12 +301,6 @@ 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);
|
||||
|
||||
pos = EXFAT_BLK_TO_B((iblock + 1), sb);
|
||||
if ((create && iblock >= last_block) || buffer_delay(bh_result)) {
|
||||
if (ei->i_size_ondisk < pos)
|
||||
ei->i_size_ondisk = pos;
|
||||
}
|
||||
|
||||
map_bh(bh_result, sb, phys);
|
||||
if (buffer_delay(bh_result))
|
||||
clear_buffer_delay(bh_result);
|
||||
@ -342,13 +321,7 @@ static int exfat_get_block(struct inode *inode, sector_t iblock,
|
||||
}
|
||||
|
||||
/* The area has not been written, map and mark as new. */
|
||||
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);
|
||||
@ -371,7 +344,7 @@ static int exfat_get_block(struct inode *inode, sector_t iblock,
|
||||
* The block has been partially written,
|
||||
* zero the unwritten part and map the block.
|
||||
*/
|
||||
loff_t size, off;
|
||||
loff_t size, off, pos;
|
||||
|
||||
max_blocks = 1;
|
||||
|
||||
@ -382,7 +355,7 @@ static int exfat_get_block(struct inode *inode, sector_t iblock,
|
||||
if (!bh_result->b_folio)
|
||||
goto done;
|
||||
|
||||
pos -= sb->s_blocksize;
|
||||
pos = EXFAT_BLK_TO_B(iblock, sb);
|
||||
size = ei->valid_size - pos;
|
||||
off = pos & (PAGE_SIZE - 1);
|
||||
|
||||
@ -432,6 +405,9 @@ static void exfat_readahead(struct readahead_control *rac)
|
||||
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);
|
||||
}
|
||||
|
||||
@ -452,6 +428,9 @@ static int exfat_write_begin(struct file *file, struct address_space *mapping,
|
||||
{
|
||||
int ret;
|
||||
|
||||
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)
|
||||
@ -469,14 +448,6 @@ static int exfat_write_end(struct file *file, struct address_space *mapping,
|
||||
int err;
|
||||
|
||||
err = generic_write_end(file, mapping, pos, len, copied, folio, 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;
|
||||
}
|
||||
|
||||
if (err < len)
|
||||
exfat_write_failed(mapping, pos+len);
|
||||
|
||||
@ -504,20 +475,6 @@ static ssize_t exfat_direct_IO(struct kiocb *iocb, struct iov_iter *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().
|
||||
@ -531,8 +488,18 @@ static ssize_t exfat_direct_IO(struct kiocb *iocb, struct iov_iter *iter)
|
||||
} else
|
||||
size = pos + ret;
|
||||
|
||||
/* zero the unwritten part in the partially written block */
|
||||
if (rw == READ && pos < ei->valid_size && ei->valid_size < size) {
|
||||
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);
|
||||
}
|
||||
@ -667,15 +634,6 @@ 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) >> 9;
|
||||
|
@ -372,8 +372,6 @@ 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 += sbi->cluster_size >> 9;
|
||||
@ -549,6 +547,9 @@ static int exfat_create(struct mnt_idmap *idmap, struct inode *dir,
|
||||
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,
|
||||
@ -772,6 +773,9 @@ static int exfat_unlink(struct inode *dir, struct dentry *dentry)
|
||||
struct exfat_entry_set_cache es;
|
||||
int entry, 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;
|
||||
@ -825,6 +829,9 @@ static int exfat_mkdir(struct mnt_idmap *idmap, struct inode *dir,
|
||||
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_DIR,
|
||||
@ -915,6 +922,9 @@ static int exfat_rmdir(struct inode *dir, struct dentry *dentry)
|
||||
struct exfat_entry_set_cache es;
|
||||
int entry, err;
|
||||
|
||||
if (unlikely(exfat_forced_shutdown(sb)))
|
||||
return -EIO;
|
||||
|
||||
mutex_lock(&EXFAT_SB(inode->i_sb)->s_lock);
|
||||
|
||||
exfat_chain_dup(&cdir, &ei->dir);
|
||||
@ -982,6 +992,9 @@ static int exfat_rename_file(struct inode *inode, struct exfat_chain *p_dir,
|
||||
struct exfat_entry_set_cache old_es, new_es;
|
||||
int sync = IS_DIRSYNC(inode);
|
||||
|
||||
if (unlikely(exfat_forced_shutdown(sb)))
|
||||
return -EIO;
|
||||
|
||||
num_new_entries = exfat_calc_num_entries(p_uniname);
|
||||
if (num_new_entries < 0)
|
||||
return num_new_entries;
|
||||
|
@ -779,8 +779,11 @@ 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;
|
||||
|
@ -46,6 +46,9 @@ static int exfat_sync_fs(struct super_block *sb, int wait)
|
||||
struct exfat_sb_info *sbi = EXFAT_SB(sb);
|
||||
int err = 0;
|
||||
|
||||
if (unlikely(exfat_forced_shutdown(sb)))
|
||||
return 0;
|
||||
|
||||
if (!wait)
|
||||
return 0;
|
||||
|
||||
@ -167,6 +170,41 @@ static int exfat_show_options(struct seq_file *m, struct dentry *root)
|
||||
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;
|
||||
@ -193,6 +231,7 @@ static const struct super_operations exfat_sops = {
|
||||
.sync_fs = exfat_sync_fs,
|
||||
.statfs = exfat_statfs,
|
||||
.show_options = exfat_show_options,
|
||||
.shutdown = exfat_shutdown,
|
||||
};
|
||||
|
||||
enum {
|
||||
@ -370,8 +409,6 @@ static int exfat_read_root(struct inode *inode)
|
||||
|
||||
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, EXFAT_ATTR_SUBDIR);
|
||||
ei->i_crtime = simple_inode_init_ts(inode);
|
||||
|
25
include/uapi/linux/exfat.h
Normal file
25
include/uapi/linux/exfat.h
Normal file
@ -0,0 +1,25 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
|
||||
/*
|
||||
* Copyright (C) 2024 Unisoc Technologies Co., Ltd.
|
||||
*/
|
||||
|
||||
#ifndef _UAPI_LINUX_EXFAT_H
|
||||
#define _UAPI_LINUX_EXFAT_H
|
||||
#include <linux/types.h>
|
||||
#include <linux/ioctl.h>
|
||||
|
||||
/*
|
||||
* exfat-specific ioctl commands
|
||||
*/
|
||||
|
||||
#define EXFAT_IOC_SHUTDOWN _IOR('X', 125, __u32)
|
||||
|
||||
/*
|
||||
* Flags used by EXFAT_IOC_SHUTDOWN
|
||||
*/
|
||||
|
||||
#define EXFAT_GOING_DOWN_DEFAULT 0x0 /* default with full sync */
|
||||
#define EXFAT_GOING_DOWN_FULLSYNC 0x1 /* going down with full sync*/
|
||||
#define EXFAT_GOING_DOWN_NOSYNC 0x2 /* going down */
|
||||
|
||||
#endif /* _UAPI_LINUX_EXFAT_H */
|
Loading…
Reference in New Issue
Block a user