Skip to content

Commit 11a347f

Browse files
YuezhangMonamjaejeon
authored andcommitted
exfat: change to get file size from DataLength
In stream extension directory entry, the ValidDataLength field describes how far into the data stream user data has been written, and the DataLength field describes the file size. Signed-off-by: Yuezhang Mo <[email protected]> Reviewed-by: Andy Wu <[email protected]> Reviewed-by: Aoyama Wataru <[email protected]> Reviewed-by: Sungjong Seo <[email protected]> Signed-off-by: Namjae Jeon <[email protected]>
1 parent 34939ae commit 11a347f

File tree

4 files changed

+231
-19
lines changed

4 files changed

+231
-19
lines changed

fs/exfat/exfat_fs.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,7 @@ struct exfat_dir_entry {
207207
unsigned char flags;
208208
unsigned short attr;
209209
loff_t size;
210+
loff_t valid_size;
210211
unsigned int num_subdirs;
211212
struct timespec64 atime;
212213
struct timespec64 mtime;
@@ -316,6 +317,7 @@ struct exfat_inode_info {
316317
loff_t i_size_aligned;
317318
/* on-disk position of directory entry or 0 */
318319
loff_t i_pos;
320+
loff_t valid_size;
319321
/* hash by i_location */
320322
struct hlist_node i_hash_fat;
321323
/* protect bmap against truncate */

fs/exfat/file.c

Lines changed: 116 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
#include <linux/fsnotify.h>
1212
#include <linux/security.h>
1313
#include <linux/msdos_fs.h>
14+
#include <linux/writeback.h>
1415

1516
#include "exfat_raw.h"
1617
#include "exfat_fs.h"
@@ -26,6 +27,7 @@ static int exfat_cont_expand(struct inode *inode, loff_t size)
2627
return err;
2728

2829
inode_set_mtime_to_ts(inode, inode_set_ctime_current(inode));
30+
EXFAT_I(inode)->valid_size = size;
2931
mark_inode_dirty(inode);
3032

3133
if (!IS_SYNC(inode))
@@ -146,6 +148,9 @@ int __exfat_truncate(struct inode *inode)
146148
ei->start_clu = EXFAT_EOF_CLUSTER;
147149
}
148150

151+
if (i_size_read(inode) < ei->valid_size)
152+
ei->valid_size = i_size_read(inode);
153+
149154
if (ei->type == TYPE_FILE)
150155
ei->attr |= EXFAT_ATTR_ARCHIVE;
151156

@@ -474,15 +479,124 @@ int exfat_file_fsync(struct file *filp, loff_t start, loff_t end, int datasync)
474479
return blkdev_issue_flush(inode->i_sb->s_bdev);
475480
}
476481

482+
static int exfat_file_zeroed_range(struct file *file, loff_t start, loff_t end)
483+
{
484+
int err;
485+
struct inode *inode = file_inode(file);
486+
struct address_space *mapping = inode->i_mapping;
487+
const struct address_space_operations *ops = mapping->a_ops;
488+
489+
while (start < end) {
490+
u32 zerofrom, len;
491+
struct page *page = NULL;
492+
493+
zerofrom = start & (PAGE_SIZE - 1);
494+
len = PAGE_SIZE - zerofrom;
495+
if (start + len > end)
496+
len = end - start;
497+
498+
err = ops->write_begin(file, mapping, start, len, &page, NULL);
499+
if (err)
500+
goto out;
501+
502+
zero_user_segment(page, zerofrom, zerofrom + len);
503+
504+
err = ops->write_end(file, mapping, start, len, len, page, NULL);
505+
if (err < 0)
506+
goto out;
507+
start += len;
508+
509+
balance_dirty_pages_ratelimited(mapping);
510+
cond_resched();
511+
}
512+
513+
out:
514+
return err;
515+
}
516+
517+
static ssize_t exfat_file_write_iter(struct kiocb *iocb, struct iov_iter *iter)
518+
{
519+
ssize_t ret;
520+
struct file *file = iocb->ki_filp;
521+
struct inode *inode = file_inode(file);
522+
struct exfat_inode_info *ei = EXFAT_I(inode);
523+
loff_t pos = iocb->ki_pos;
524+
loff_t valid_size;
525+
526+
inode_lock(inode);
527+
528+
valid_size = ei->valid_size;
529+
530+
ret = generic_write_checks(iocb, iter);
531+
if (ret < 0)
532+
goto unlock;
533+
534+
if (pos > valid_size) {
535+
ret = exfat_file_zeroed_range(file, valid_size, pos);
536+
if (ret < 0 && ret != -ENOSPC) {
537+
exfat_err(inode->i_sb,
538+
"write: fail to zero from %llu to %llu(%zd)",
539+
valid_size, pos, ret);
540+
}
541+
if (ret < 0)
542+
goto unlock;
543+
}
544+
545+
ret = __generic_file_write_iter(iocb, iter);
546+
if (ret < 0)
547+
goto unlock;
548+
549+
inode_unlock(inode);
550+
551+
if (pos > valid_size)
552+
pos = valid_size;
553+
554+
if (iocb_is_dsync(iocb) && iocb->ki_pos > pos) {
555+
ssize_t err = vfs_fsync_range(file, pos, iocb->ki_pos - 1,
556+
iocb->ki_flags & IOCB_SYNC);
557+
if (err < 0)
558+
return err;
559+
}
560+
561+
return ret;
562+
563+
unlock:
564+
inode_unlock(inode);
565+
566+
return ret;
567+
}
568+
569+
static int exfat_file_mmap(struct file *file, struct vm_area_struct *vma)
570+
{
571+
int ret;
572+
struct inode *inode = file_inode(file);
573+
struct exfat_inode_info *ei = EXFAT_I(inode);
574+
loff_t start = ((loff_t)vma->vm_pgoff << PAGE_SHIFT);
575+
loff_t end = min_t(loff_t, i_size_read(inode),
576+
start + vma->vm_end - vma->vm_start);
577+
578+
if ((vma->vm_flags & VM_WRITE) && ei->valid_size < end) {
579+
ret = exfat_file_zeroed_range(file, ei->valid_size, end);
580+
if (ret < 0) {
581+
exfat_err(inode->i_sb,
582+
"mmap: fail to zero from %llu to %llu(%d)",
583+
start, end, ret);
584+
return ret;
585+
}
586+
}
587+
588+
return generic_file_mmap(file, vma);
589+
}
590+
477591
const struct file_operations exfat_file_operations = {
478592
.llseek = generic_file_llseek,
479593
.read_iter = generic_file_read_iter,
480-
.write_iter = generic_file_write_iter,
594+
.write_iter = exfat_file_write_iter,
481595
.unlocked_ioctl = exfat_ioctl,
482596
#ifdef CONFIG_COMPAT
483597
.compat_ioctl = exfat_compat_ioctl,
484598
#endif
485-
.mmap = generic_file_mmap,
599+
.mmap = exfat_file_mmap,
486600
.fsync = exfat_file_fsync,
487601
.splice_read = filemap_splice_read,
488602
.splice_write = iter_file_splice_write,

fs/exfat/inode.c

Lines changed: 107 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -75,8 +75,8 @@ int __exfat_write_inode(struct inode *inode, int sync)
7575
if (ei->start_clu == EXFAT_EOF_CLUSTER)
7676
on_disk_size = 0;
7777

78-
ep2->dentry.stream.valid_size = cpu_to_le64(on_disk_size);
79-
ep2->dentry.stream.size = ep2->dentry.stream.valid_size;
78+
ep2->dentry.stream.valid_size = cpu_to_le64(ei->valid_size);
79+
ep2->dentry.stream.size = cpu_to_le64(on_disk_size);
8080
if (on_disk_size) {
8181
ep2->dentry.stream.flags = ei->flags;
8282
ep2->dentry.stream.start_clu = cpu_to_le32(ei->start_clu);
@@ -278,6 +278,7 @@ static int exfat_get_block(struct inode *inode, sector_t iblock,
278278
unsigned int cluster, sec_offset;
279279
sector_t last_block;
280280
sector_t phys = 0;
281+
sector_t valid_blks;
281282
loff_t pos;
282283

283284
mutex_lock(&sbi->s_lock);
@@ -306,29 +307,88 @@ static int exfat_get_block(struct inode *inode, sector_t iblock,
306307
mapped_blocks = sbi->sect_per_clus - sec_offset;
307308
max_blocks = min(mapped_blocks, max_blocks);
308309

309-
/* Treat newly added block / cluster */
310-
if (iblock < last_block)
311-
create = 0;
312-
313-
if (create || buffer_delay(bh_result)) {
314-
pos = EXFAT_BLK_TO_B((iblock + 1), sb);
310+
pos = EXFAT_BLK_TO_B((iblock + 1), sb);
311+
if ((create && iblock >= last_block) || buffer_delay(bh_result)) {
315312
if (ei->i_size_ondisk < pos)
316313
ei->i_size_ondisk = pos;
317314
}
318315

316+
map_bh(bh_result, sb, phys);
317+
if (buffer_delay(bh_result))
318+
clear_buffer_delay(bh_result);
319+
319320
if (create) {
321+
valid_blks = EXFAT_B_TO_BLK_ROUND_UP(ei->valid_size, sb);
322+
323+
if (iblock + max_blocks < valid_blks) {
324+
/* The range has been written, map it */
325+
goto done;
326+
} else if (iblock < valid_blks) {
327+
/*
328+
* The range has been partially written,
329+
* map the written part.
330+
*/
331+
max_blocks = valid_blks - iblock;
332+
goto done;
333+
}
334+
335+
/* The area has not been written, map and mark as new. */
320336
err = exfat_map_new_buffer(ei, bh_result, pos);
321337
if (err) {
322338
exfat_fs_error(sb,
323339
"requested for bmap out of range(pos : (%llu) > i_size_aligned(%llu)\n",
324340
pos, ei->i_size_aligned);
325341
goto unlock_ret;
326342
}
343+
} else {
344+
valid_blks = EXFAT_B_TO_BLK(ei->valid_size, sb);
345+
346+
if (iblock + max_blocks < valid_blks) {
347+
/* The range has been written, map it */
348+
goto done;
349+
} else if (iblock < valid_blks) {
350+
/*
351+
* The area has been partially written,
352+
* map the written part.
353+
*/
354+
max_blocks = valid_blks - iblock;
355+
goto done;
356+
} else if (iblock == valid_blks &&
357+
(ei->valid_size & (sb->s_blocksize - 1))) {
358+
/*
359+
* The block has been partially written,
360+
* zero the unwritten part and map the block.
361+
*/
362+
loff_t size, off;
363+
364+
max_blocks = 1;
365+
366+
/*
367+
* For direct read, the unwritten part will be zeroed in
368+
* exfat_direct_IO()
369+
*/
370+
if (!bh_result->b_folio)
371+
goto done;
372+
373+
pos -= sb->s_blocksize;
374+
size = ei->valid_size - pos;
375+
off = pos & (PAGE_SIZE - 1);
376+
377+
folio_set_bh(bh_result, bh_result->b_folio, off);
378+
err = bh_read(bh_result, 0);
379+
if (err < 0)
380+
goto unlock_ret;
381+
382+
folio_zero_segment(bh_result->b_folio, off + size,
383+
off + sb->s_blocksize);
384+
} else {
385+
/*
386+
* The range has not been written, clear the mapped flag
387+
* to only zero the cache and do not read from disk.
388+
*/
389+
clear_buffer_mapped(bh_result);
390+
}
327391
}
328-
329-
if (buffer_delay(bh_result))
330-
clear_buffer_delay(bh_result);
331-
map_bh(bh_result, sb, phys);
332392
done:
333393
bh_result->b_size = EXFAT_BLK_TO_B(max_blocks, sb);
334394
unlock_ret:
@@ -343,6 +403,17 @@ static int exfat_read_folio(struct file *file, struct folio *folio)
343403

344404
static void exfat_readahead(struct readahead_control *rac)
345405
{
406+
struct address_space *mapping = rac->mapping;
407+
struct inode *inode = mapping->host;
408+
struct exfat_inode_info *ei = EXFAT_I(inode);
409+
loff_t pos = readahead_pos(rac);
410+
411+
/* Range cross valid_size, read it page by page. */
412+
if (ei->valid_size < i_size_read(inode) &&
413+
pos <= ei->valid_size &&
414+
ei->valid_size < pos + readahead_length(rac))
415+
return;
416+
346417
mpage_readahead(rac, exfat_get_block);
347418
}
348419

@@ -370,9 +441,7 @@ static int exfat_write_begin(struct file *file, struct address_space *mapping,
370441
int ret;
371442

372443
*pagep = NULL;
373-
ret = cont_write_begin(file, mapping, pos, len, pagep, fsdata,
374-
exfat_get_block,
375-
&EXFAT_I(mapping->host)->i_size_ondisk);
444+
ret = block_write_begin(mapping, pos, len, pagep, exfat_get_block);
376445

377446
if (ret < 0)
378447
exfat_write_failed(mapping, pos+len);
@@ -400,6 +469,11 @@ static int exfat_write_end(struct file *file, struct address_space *mapping,
400469
if (err < len)
401470
exfat_write_failed(mapping, pos+len);
402471

472+
if (!(err < 0) && pos + err > ei->valid_size) {
473+
ei->valid_size = pos + err;
474+
mark_inode_dirty(inode);
475+
}
476+
403477
if (!(err < 0) && !(ei->attr & EXFAT_ATTR_ARCHIVE)) {
404478
inode_set_mtime_to_ts(inode, inode_set_ctime_current(inode));
405479
ei->attr |= EXFAT_ATTR_ARCHIVE;
@@ -413,6 +487,8 @@ static ssize_t exfat_direct_IO(struct kiocb *iocb, struct iov_iter *iter)
413487
{
414488
struct address_space *mapping = iocb->ki_filp->f_mapping;
415489
struct inode *inode = mapping->host;
490+
struct exfat_inode_info *ei = EXFAT_I(inode);
491+
loff_t pos = iocb->ki_pos;
416492
loff_t size = iocb->ki_pos + iov_iter_count(iter);
417493
int rw = iov_iter_rw(iter);
418494
ssize_t ret;
@@ -436,8 +512,21 @@ static ssize_t exfat_direct_IO(struct kiocb *iocb, struct iov_iter *iter)
436512
* condition of exfat_get_block() and ->truncate().
437513
*/
438514
ret = blockdev_direct_IO(iocb, inode, iter, exfat_get_block);
439-
if (ret < 0 && (rw & WRITE))
440-
exfat_write_failed(mapping, size);
515+
if (ret < 0) {
516+
if (rw == WRITE)
517+
exfat_write_failed(mapping, size);
518+
519+
if (ret != -EIOCBQUEUED)
520+
return ret;
521+
} else
522+
size = pos + ret;
523+
524+
/* zero the unwritten part in the partially written block */
525+
if (rw == READ && pos < ei->valid_size && ei->valid_size < size) {
526+
iov_iter_revert(iter, size - ei->valid_size);
527+
iov_iter_zero(size - ei->valid_size, iter);
528+
}
529+
441530
return ret;
442531
}
443532

@@ -537,6 +626,7 @@ static int exfat_fill_inode(struct inode *inode, struct exfat_dir_entry *info)
537626
ei->start_clu = info->start_clu;
538627
ei->flags = info->flags;
539628
ei->type = info->type;
629+
ei->valid_size = info->valid_size;
540630

541631
ei->version = 0;
542632
ei->hint_stat.eidx = 0;

0 commit comments

Comments
 (0)