Skip to content

Commit 4165cee

Browse files
committed
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
2 parents 79952bd + cb7d850 commit 4165cee

File tree

9 files changed

+200
-127
lines changed

9 files changed

+200
-127
lines changed

MAINTAINERS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8466,6 +8466,7 @@ N: binfmt
84668466
EXFAT FILE SYSTEM
84678467
M: Namjae Jeon <linkinjeon@kernel.org>
84688468
M: Sungjong Seo <sj1557.seo@samsung.com>
8469+
R: Yuezhang Mo <yuezhang.mo@sony.com>
84698470
L: linux-fsdevel@vger.kernel.org
84708471
S: Maintained
84718472
T: git git://git.kernel.org/pub/scm/linux/kernel/git/linkinjeon/exfat.git

fs/exfat/balloc.c

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -91,18 +91,18 @@ int exfat_load_bitmap(struct super_block *sb)
9191
return -EIO;
9292

9393
type = exfat_get_entry_type(ep);
94-
if (type == TYPE_UNUSED)
95-
break;
96-
if (type != TYPE_BITMAP)
97-
continue;
98-
if (ep->dentry.bitmap.flags == 0x0) {
94+
if (type == TYPE_BITMAP &&
95+
ep->dentry.bitmap.flags == 0x0) {
9996
int err;
10097

10198
err = exfat_allocate_bitmap(sb, ep);
10299
brelse(bh);
103100
return err;
104101
}
105102
brelse(bh);
103+
104+
if (type == TYPE_UNUSED)
105+
return -EINVAL;
106106
}
107107

108108
if (exfat_get_next_cluster(sb, &clu.dir))

fs/exfat/exfat_fs.h

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#include <linux/ratelimit.h>
1111
#include <linux/nls.h>
1212
#include <linux/blkdev.h>
13+
#include <uapi/linux/exfat.h>
1314

1415
#define EXFAT_ROOT_INO 1
1516

@@ -148,6 +149,9 @@ enum {
148149
#define DIR_CACHE_SIZE \
149150
(DIV_ROUND_UP(EXFAT_DEN_TO_B(ES_MAX_ENTRY_NUM), SECTOR_SIZE) + 1)
150151

152+
/* Superblock flags */
153+
#define EXFAT_FLAGS_SHUTDOWN 1
154+
151155
struct exfat_dentry_namebuf {
152156
char *lfn;
153157
int lfnbuf_len; /* usually MAX_UNINAME_BUF_SIZE */
@@ -267,6 +271,8 @@ struct exfat_sb_info {
267271
unsigned int clu_srch_ptr; /* cluster search pointer */
268272
unsigned int used_clusters; /* number of used clusters */
269273

274+
unsigned long s_exfat_flags; /* Exfat superblock flags */
275+
270276
struct mutex s_lock; /* superblock lock */
271277
struct mutex bitmap_lock; /* bitmap lock */
272278
struct exfat_mount_options options;
@@ -309,13 +315,6 @@ struct exfat_inode_info {
309315
/* for avoiding the race between alloc and free */
310316
unsigned int cache_valid_id;
311317

312-
/*
313-
* NOTE: i_size_ondisk is 64bits, so must hold ->inode_lock to access.
314-
* physically allocated size.
315-
*/
316-
loff_t i_size_ondisk;
317-
/* block-aligned i_size (used in cont_write_begin) */
318-
loff_t i_size_aligned;
319318
/* on-disk position of directory entry or 0 */
320319
loff_t i_pos;
321320
loff_t valid_size;
@@ -338,6 +337,11 @@ static inline struct exfat_inode_info *EXFAT_I(struct inode *inode)
338337
return container_of(inode, struct exfat_inode_info, vfs_inode);
339338
}
340339

340+
static inline int exfat_forced_shutdown(struct super_block *sb)
341+
{
342+
return test_bit(EXFAT_FLAGS_SHUTDOWN, &EXFAT_SB(sb)->s_exfat_flags);
343+
}
344+
341345
/*
342346
* If ->i_mode can't hold 0222 (i.e. ATTR_RO), we use ->i_attrs to
343347
* save ATTR_RO instead of ->i_mode.
@@ -417,6 +421,11 @@ static inline bool is_valid_cluster(struct exfat_sb_info *sbi,
417421
return clus >= EXFAT_FIRST_CLUSTER && clus < sbi->num_clusters;
418422
}
419423

424+
static inline loff_t exfat_ondisk_size(const struct inode *inode)
425+
{
426+
return ((loff_t)inode->i_blocks) << 9;
427+
}
428+
420429
/* super.c */
421430
int exfat_set_volume_dirty(struct super_block *sb);
422431
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);
461470
long exfat_ioctl(struct file *filp, unsigned int cmd, unsigned long arg);
462471
long exfat_compat_ioctl(struct file *filp, unsigned int cmd,
463472
unsigned long arg);
473+
int exfat_force_shutdown(struct super_block *sb, u32 flags);
464474

465475
/* namei.c */
466476
extern const struct dentry_operations exfat_dentry_ops;

fs/exfat/file.c

Lines changed: 68 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ static int exfat_cont_expand(struct inode *inode, loff_t size)
2929
if (ret)
3030
return ret;
3131

32-
num_clusters = EXFAT_B_TO_CLU_ROUND_UP(ei->i_size_ondisk, sbi);
32+
num_clusters = EXFAT_B_TO_CLU(exfat_ondisk_size(inode), sbi);
3333
new_num_clusters = EXFAT_B_TO_CLU_ROUND_UP(size, sbi);
3434

3535
if (new_num_clusters == num_clusters)
@@ -74,8 +74,6 @@ static int exfat_cont_expand(struct inode *inode, loff_t size)
7474
/* Expanded range not zeroed, do not update valid_size */
7575
i_size_write(inode, size);
7676

77-
ei->i_size_aligned = round_up(size, sb->s_blocksize);
78-
ei->i_size_ondisk = ei->i_size_aligned;
7977
inode->i_blocks = round_up(size, sbi->cluster_size) >> 9;
8078
mark_inode_dirty(inode);
8179

@@ -159,7 +157,7 @@ int __exfat_truncate(struct inode *inode)
159157
exfat_set_volume_dirty(sb);
160158

161159
num_clusters_new = EXFAT_B_TO_CLU_ROUND_UP(i_size_read(inode), sbi);
162-
num_clusters_phys = EXFAT_B_TO_CLU_ROUND_UP(ei->i_size_ondisk, sbi);
160+
num_clusters_phys = EXFAT_B_TO_CLU(exfat_ondisk_size(inode), sbi);
163161

164162
exfat_chain_set(&clu, ei->start_clu, num_clusters_phys, ei->flags);
165163

@@ -245,8 +243,6 @@ void exfat_truncate(struct inode *inode)
245243
struct super_block *sb = inode->i_sb;
246244
struct exfat_sb_info *sbi = EXFAT_SB(sb);
247245
struct exfat_inode_info *ei = EXFAT_I(inode);
248-
unsigned int blocksize = i_blocksize(inode);
249-
loff_t aligned_size;
250246
int err;
251247

252248
mutex_lock(&sbi->s_lock);
@@ -264,17 +260,6 @@ void exfat_truncate(struct inode *inode)
264260

265261
inode->i_blocks = round_up(i_size_read(inode), sbi->cluster_size) >> 9;
266262
write_size:
267-
aligned_size = i_size_read(inode);
268-
if (aligned_size & (blocksize - 1)) {
269-
aligned_size |= (blocksize - 1);
270-
aligned_size++;
271-
}
272-
273-
if (ei->i_size_ondisk > i_size_read(inode))
274-
ei->i_size_ondisk = aligned_size;
275-
276-
if (ei->i_size_aligned > i_size_read(inode))
277-
ei->i_size_aligned = aligned_size;
278263
mutex_unlock(&sbi->s_lock);
279264
}
280265

@@ -302,6 +287,9 @@ int exfat_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
302287
unsigned int ia_valid;
303288
int error;
304289

290+
if (unlikely(exfat_forced_shutdown(inode->i_sb)))
291+
return -EIO;
292+
305293
if ((attr->ia_valid & ATTR_SIZE) &&
306294
attr->ia_size > i_size_read(inode)) {
307295
error = exfat_cont_expand(inode, attr->ia_size);
@@ -485,6 +473,19 @@ static int exfat_ioctl_fitrim(struct inode *inode, unsigned long arg)
485473
return 0;
486474
}
487475

476+
static int exfat_ioctl_shutdown(struct super_block *sb, unsigned long arg)
477+
{
478+
u32 flags;
479+
480+
if (!capable(CAP_SYS_ADMIN))
481+
return -EPERM;
482+
483+
if (get_user(flags, (__u32 __user *)arg))
484+
return -EFAULT;
485+
486+
return exfat_force_shutdown(sb, flags);
487+
}
488+
488489
long exfat_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
489490
{
490491
struct inode *inode = file_inode(filp);
@@ -495,6 +496,8 @@ long exfat_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
495496
return exfat_ioctl_get_attributes(inode, user_attr);
496497
case FAT_IOCTL_SET_ATTRIBUTES:
497498
return exfat_ioctl_set_attributes(filp, user_attr);
499+
case EXFAT_IOC_SHUTDOWN:
500+
return exfat_ioctl_shutdown(inode->i_sb, arg);
498501
case FITRIM:
499502
return exfat_ioctl_fitrim(inode, arg);
500503
default:
@@ -515,6 +518,9 @@ int exfat_file_fsync(struct file *filp, loff_t start, loff_t end, int datasync)
515518
struct inode *inode = filp->f_mapping->host;
516519
int err;
517520

521+
if (unlikely(exfat_forced_shutdown(inode->i_sb)))
522+
return -EIO;
523+
518524
err = __generic_file_fsync(filp, start, end, datasync);
519525
if (err)
520526
return err;
@@ -526,32 +532,32 @@ int exfat_file_fsync(struct file *filp, loff_t start, loff_t end, int datasync)
526532
return blkdev_issue_flush(inode->i_sb->s_bdev);
527533
}
528534

529-
static int exfat_file_zeroed_range(struct file *file, loff_t start, loff_t end)
535+
static int exfat_extend_valid_size(struct file *file, loff_t new_valid_size)
530536
{
531537
int err;
538+
loff_t pos;
532539
struct inode *inode = file_inode(file);
540+
struct exfat_inode_info *ei = EXFAT_I(inode);
533541
struct address_space *mapping = inode->i_mapping;
534542
const struct address_space_operations *ops = mapping->a_ops;
535543

536-
while (start < end) {
537-
u32 zerofrom, len;
544+
pos = ei->valid_size;
545+
while (pos < new_valid_size) {
546+
u32 len;
538547
struct folio *folio;
539548

540-
zerofrom = start & (PAGE_SIZE - 1);
541-
len = PAGE_SIZE - zerofrom;
542-
if (start + len > end)
543-
len = end - start;
549+
len = PAGE_SIZE - (pos & (PAGE_SIZE - 1));
550+
if (pos + len > new_valid_size)
551+
len = new_valid_size - pos;
544552

545-
err = ops->write_begin(file, mapping, start, len, &folio, NULL);
553+
err = ops->write_begin(file, mapping, pos, len, &folio, NULL);
546554
if (err)
547555
goto out;
548556

549-
folio_zero_range(folio, offset_in_folio(folio, start), len);
550-
551-
err = ops->write_end(file, mapping, start, len, len, folio, NULL);
557+
err = ops->write_end(file, mapping, pos, len, len, folio, NULL);
552558
if (err < 0)
553559
goto out;
554-
start += len;
560+
pos += len;
555561

556562
balance_dirty_pages_ratelimited(mapping);
557563
cond_resched();
@@ -579,7 +585,7 @@ static ssize_t exfat_file_write_iter(struct kiocb *iocb, struct iov_iter *iter)
579585
goto unlock;
580586

581587
if (pos > valid_size) {
582-
ret = exfat_file_zeroed_range(file, valid_size, pos);
588+
ret = exfat_extend_valid_size(file, pos);
583589
if (ret < 0 && ret != -ENOSPC) {
584590
exfat_err(inode->i_sb,
585591
"write: fail to zero from %llu to %llu(%zd)",
@@ -613,26 +619,46 @@ static ssize_t exfat_file_write_iter(struct kiocb *iocb, struct iov_iter *iter)
613619
return ret;
614620
}
615621

616-
static int exfat_file_mmap(struct file *file, struct vm_area_struct *vma)
622+
static vm_fault_t exfat_page_mkwrite(struct vm_fault *vmf)
617623
{
618-
int ret;
624+
int err;
625+
struct vm_area_struct *vma = vmf->vma;
626+
struct file *file = vma->vm_file;
619627
struct inode *inode = file_inode(file);
620628
struct exfat_inode_info *ei = EXFAT_I(inode);
621-
loff_t start = ((loff_t)vma->vm_pgoff << PAGE_SHIFT);
622-
loff_t end = min_t(loff_t, i_size_read(inode),
629+
loff_t start, end;
630+
631+
if (!inode_trylock(inode))
632+
return VM_FAULT_RETRY;
633+
634+
start = ((loff_t)vma->vm_pgoff << PAGE_SHIFT);
635+
end = min_t(loff_t, i_size_read(inode),
623636
start + vma->vm_end - vma->vm_start);
624637

625-
if ((vma->vm_flags & VM_WRITE) && ei->valid_size < end) {
626-
ret = exfat_file_zeroed_range(file, ei->valid_size, end);
627-
if (ret < 0) {
628-
exfat_err(inode->i_sb,
629-
"mmap: fail to zero from %llu to %llu(%d)",
630-
start, end, ret);
631-
return ret;
638+
if (ei->valid_size < end) {
639+
err = exfat_extend_valid_size(file, end);
640+
if (err < 0) {
641+
inode_unlock(inode);
642+
return vmf_fs_error(err);
632643
}
633644
}
634645

635-
return generic_file_mmap(file, vma);
646+
inode_unlock(inode);
647+
648+
return filemap_page_mkwrite(vmf);
649+
}
650+
651+
static const struct vm_operations_struct exfat_file_vm_ops = {
652+
.fault = filemap_fault,
653+
.map_pages = filemap_map_pages,
654+
.page_mkwrite = exfat_page_mkwrite,
655+
};
656+
657+
static int exfat_file_mmap(struct file *file, struct vm_area_struct *vma)
658+
{
659+
file_accessed(file);
660+
vma->vm_ops = &exfat_file_vm_ops;
661+
return 0;
636662
}
637663

638664
const struct file_operations exfat_file_operations = {

0 commit comments

Comments
 (0)