summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--fs/exfat/file.c77
-rw-r--r--fs/exfat/inode.c14
2 files changed, 70 insertions, 21 deletions
diff --git a/fs/exfat/file.c b/fs/exfat/file.c
index 270e2f934124..d25a96a148af 100644
--- a/fs/exfat/file.c
+++ b/fs/exfat/file.c
@@ -18,32 +18,69 @@
static int exfat_cont_expand(struct inode *inode, loff_t size)
{
- struct address_space *mapping = inode->i_mapping;
- loff_t start = i_size_read(inode), count = size - i_size_read(inode);
- int err, err2;
+ int ret;
+ unsigned int num_clusters, new_num_clusters, last_clu;
+ struct exfat_inode_info *ei = EXFAT_I(inode);
+ struct super_block *sb = inode->i_sb;
+ struct exfat_sb_info *sbi = EXFAT_SB(sb);
+ struct exfat_chain clu;
- err = generic_cont_expand_simple(inode, size);
- if (err)
- return err;
+ ret = inode_newsize_ok(inode, size);
+ if (ret)
+ return ret;
+
+ num_clusters = EXFAT_B_TO_CLU_ROUND_UP(ei->i_size_ondisk, sbi);
+ new_num_clusters = EXFAT_B_TO_CLU_ROUND_UP(size, sbi);
+
+ if (new_num_clusters == num_clusters)
+ goto out;
+
+ exfat_chain_set(&clu, ei->start_clu, num_clusters, ei->flags);
+ ret = exfat_find_last_cluster(sb, &clu, &last_clu);
+ if (ret)
+ return ret;
+ clu.dir = (last_clu == EXFAT_EOF_CLUSTER) ?
+ EXFAT_EOF_CLUSTER : last_clu + 1;
+ clu.size = 0;
+ clu.flags = ei->flags;
+
+ ret = exfat_alloc_cluster(inode, new_num_clusters - num_clusters,
+ &clu, IS_DIRSYNC(inode));
+ if (ret)
+ return ret;
+
+ /* Append new clusters to chain */
+ if (clu.flags != ei->flags) {
+ exfat_chain_cont_cluster(sb, ei->start_clu, num_clusters);
+ ei->flags = ALLOC_FAT_CHAIN;
+ }
+ if (clu.flags == ALLOC_FAT_CHAIN)
+ if (exfat_ent_set(sb, last_clu, clu.dir))
+ goto free_clu;
+
+ if (num_clusters == 0)
+ ei->start_clu = clu.dir;
+
+out:
inode_set_mtime_to_ts(inode, inode_set_ctime_current(inode));
- EXFAT_I(inode)->valid_size = size;
- mark_inode_dirty(inode);
+ /* Expanded range not zeroed, do not update valid_size */
+ i_size_write(inode, size);
- if (!IS_SYNC(inode))
- return 0;
+ 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;
- err = filemap_fdatawrite_range(mapping, start, start + count - 1);
- err2 = sync_mapping_buffers(mapping);
- if (!err)
- err = err2;
- err2 = write_inode_now(inode, 1);
- if (!err)
- err = err2;
- if (err)
- return err;
+ if (IS_DIRSYNC(inode))
+ return write_inode_now(inode, 1);
+
+ mark_inode_dirty(inode);
+
+ return 0;
- return filemap_fdatawait_range(mapping, start, start + count - 1);
+free_clu:
+ exfat_free_cluster(inode, &clu);
+ return -EIO;
}
static bool exfat_allow_set_time(struct exfat_sb_info *sbi, struct inode *inode)
diff --git a/fs/exfat/inode.c b/fs/exfat/inode.c
index b02677c9fd45..522edcbb2ce4 100644
--- a/fs/exfat/inode.c
+++ b/fs/exfat/inode.c
@@ -75,8 +75,17 @@ int __exfat_write_inode(struct inode *inode, int sync)
if (ei->start_clu == EXFAT_EOF_CLUSTER)
on_disk_size = 0;
- ep2->dentry.stream.valid_size = cpu_to_le64(ei->valid_size);
ep2->dentry.stream.size = cpu_to_le64(on_disk_size);
+ /*
+ * mmap write does not use exfat_write_end(), valid_size may be
+ * extended to the sector-aligned length in exfat_get_block().
+ * So we need to fixup valid_size to the writren length.
+ */
+ if (on_disk_size < ei->valid_size)
+ ep2->dentry.stream.valid_size = ep2->dentry.stream.size;
+ else
+ ep2->dentry.stream.valid_size = cpu_to_le64(ei->valid_size);
+
if (on_disk_size) {
ep2->dentry.stream.flags = ei->flags;
ep2->dentry.stream.start_clu = cpu_to_le32(ei->start_clu);
@@ -340,6 +349,9 @@ static int exfat_get_block(struct inode *inode, sector_t iblock,
pos, ei->i_size_aligned);
goto unlock_ret;
}
+
+ ei->valid_size = EXFAT_BLK_TO_B(iblock + max_blocks, sb);
+ mark_inode_dirty(inode);
} else {
valid_blks = EXFAT_B_TO_BLK(ei->valid_size, sb);