summaryrefslogtreecommitdiff
path: root/fs/ntfs/attrib.c
diff options
context:
space:
mode:
Diffstat (limited to 'fs/ntfs/attrib.c')
-rw-r--r--fs/ntfs/attrib.c46
1 files changed, 34 insertions, 12 deletions
diff --git a/fs/ntfs/attrib.c b/fs/ntfs/attrib.c
index dd8828098511..58f32aac5f61 100644
--- a/fs/ntfs/attrib.c
+++ b/fs/ntfs/attrib.c
@@ -175,7 +175,10 @@ int ntfs_map_runlist_nolock(struct ntfs_inode *ni, s64 vcn, struct ntfs_attr_sea
err = -EIO;
goto err_out;
}
- WARN_ON(!ctx->attr->non_resident);
+ if (unlikely(!ctx->attr->non_resident)) {
+ err = -EIO;
+ goto err_out;
+ }
}
a = ctx->attr;
/*
@@ -4290,6 +4293,16 @@ static int ntfs_non_resident_attr_shrink(struct ntfs_inode *ni, const s64 newsiz
ni->initialized_size = newsize;
ctx->attr->data.non_resident.initialized_size = cpu_to_le64(newsize);
}
+
+ /*
+ * Drop any page-cache folios that now lie beyond the shrunk
+ * attribute. The clusters backing them have just been freed and the
+ * runlist truncated, so leaving stale dirty folios around makes a
+ * later writeback map a vcn past the new allocation, which fails with
+ * -ENOENT and loses the write.
+ */
+ truncate_inode_pages(VFS_I(ni)->i_mapping, newsize);
+
/* Update data size in the index. */
if (ni->type == AT_DATA && ni->name == AT_UNNAMED)
NInoSetFileNameDirty(ni);
@@ -5325,6 +5338,7 @@ int ntfs_non_resident_attr_insert_range(struct ntfs_inode *ni, s64 start_vcn, s6
ret = ntfs_attr_map_whole_runlist(ni);
if (ret) {
up_write(&ni->runlist.lock);
+ kfree(hole_rl);
return ret;
}
@@ -5536,6 +5550,7 @@ int ntfs_attr_fallocate(struct ntfs_inode *ni, loff_t start, loff_t byte_len, bo
s64 old_data_size;
s64 vcn_start, vcn_end, vcn_uninit, vcn, try_alloc_cnt;
s64 lcn, alloc_cnt;
+ s64 rl_lcn, rl_length, rl_vcn;
int err = 0;
struct runlist_element *rl;
bool balloc;
@@ -5615,19 +5630,23 @@ int ntfs_attr_fallocate(struct ntfs_inode *ni, loff_t start, loff_t byte_len, bo
while (vcn < vcn_uninit) {
down_read(&ni->runlist.lock);
rl = ntfs_attr_find_vcn_nolock(ni, vcn, NULL);
- up_read(&ni->runlist.lock);
if (IS_ERR(rl)) {
+ up_read(&ni->runlist.lock);
err = PTR_ERR(rl);
goto out;
}
+ rl_lcn = rl->lcn;
+ rl_length = rl->length;
+ rl_vcn = rl->vcn;
+ up_read(&ni->runlist.lock);
- if (rl->lcn > 0) {
- vcn += rl->length - (vcn - rl->vcn);
- } else if (rl->lcn == LCN_DELALLOC || rl->lcn == LCN_HOLE) {
- try_alloc_cnt = min(rl->length - (vcn - rl->vcn),
+ if (rl_lcn > 0) {
+ vcn += rl_length - (vcn - rl_vcn);
+ } else if (rl_lcn == LCN_DELALLOC || rl_lcn == LCN_HOLE) {
+ try_alloc_cnt = min(rl_length - (vcn - rl_vcn),
vcn_uninit - vcn);
- if (rl->lcn == LCN_DELALLOC) {
+ if (rl_lcn == LCN_DELALLOC) {
vcn += try_alloc_cnt;
continue;
}
@@ -5642,11 +5661,14 @@ int ntfs_attr_fallocate(struct ntfs_inode *ni, loff_t start, loff_t byte_len, bo
if (err)
goto out;
- err = ntfs_dio_zero_range(VFS_I(ni),
- lcn << vol->cluster_size_bits,
- alloc_cnt << vol->cluster_size_bits);
- if (err > 0)
- goto out;
+ if (balloc) {
+ err = ntfs_dio_zero_range(VFS_I(ni),
+ lcn << vol->cluster_size_bits,
+ alloc_cnt <<
+ vol->cluster_size_bits);
+ if (err > 0)
+ goto out;
+ }
if (signal_pending(current))
goto out;