From 26e51efbcb60b2eeddc3e7488fd214a02586851a Mon Sep 17 00:00:00 2001 From: Matthias Goergens Date: Wed, 30 Sep 2026 11:48:31 +0800 Subject: [PATCH] ntfs: do not turn an unmappable runlist fragment into delalloc on write ntfs_write_simple_iomap_begin_non_resident() has the same unchecked retry. LCN_RL_NOT_MAPPED passes its "lcn <= LCN_HOLE" test, so a buffered write into a range whose extent record cannot be read is merged into the runlist as LCN_DELALLOC over clusters that are already allocated on disk, and write() succeeds. If the record stays unreadable, writeback fails too and the data never reaches the disk; the error only shows up at a later fsync() or a synchronous write. On the volume from the previous patch, after a read of the file, an 8 KiB pwrite() at vcn 1000 returns 8192, fsync() returns -EIO, and after remount the range is unchanged. Fail the lookup here too, with -ENOMEM or -EIO. That pwrite() now fails with -EIO. Writes whose range can be mapped are unaffected. Fixes: b041ca562526 ("ntfs: update iomap and address space operations") Cc: stable@vger.kernel.org Signed-off-by: Matthias Goergens Reviewed-by: Hyunchul Lee Signed-off-by: Namjae Jeon --- fs/ntfs/iomap.c | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/fs/ntfs/iomap.c b/fs/ntfs/iomap.c index c812d7f19b36..b4475963e57a 100644 --- a/fs/ntfs/iomap.c +++ b/fs/ntfs/iomap.c @@ -394,7 +394,7 @@ static int ntfs_write_simple_iomap_begin_non_resident(struct inode *inode, loff_ loff_t vcn_ofs, rl_length; struct runlist_element *rl, *rlc; bool is_retry = false; - int err = 0; + int err = 0, map_err = 0; s64 vcn, lcn; s64 max_clu_count = ntfs_bytes_to_cluster(vol, round_up(length, vol->cluster_size)); @@ -429,12 +429,24 @@ static int ntfs_write_simple_iomap_begin_non_resident(struct inode *inode, loff_ if (lcn <= LCN_RL_NOT_MAPPED && is_retry == false) { is_retry = true; - if (!ntfs_map_runlist_nolock(ni, vcn, NULL)) { + map_err = ntfs_map_runlist_nolock(ni, vcn, NULL); + if (!map_err) { rl = ni->runlist.rl; goto remap_rl; } } + /* + * As in ntfs_attr_vcn_to_rl(): a runlist fragment that could not be + * mapped is not a hole. Treating it as one would put a delalloc + * extent over clusters that are allocated on disk but unknown to us. + */ + if (lcn == LCN_RL_NOT_MAPPED) { + up_write(&ni->runlist.lock); + mutex_unlock(&ni->mrec_lock); + return map_err == -ENOMEM ? -ENOMEM : -EIO; + } + max_clu_count = min(max_clu_count, rl->length - (vcn - rl->vcn)); if (max_clu_count == 0) { ntfs_error(inode->i_sb,