Skip to content

[Deepin-Kernel-SIG] [linux 6.18.y] [Upstream] ntfs 7.2 - #2099

Open
opsiff wants to merge 17 commits into
deepin-community:linux-6.18.yfrom
opsiff:linux-6.18.y-2026-08-25-update-ntfs
Open

[Deepin-Kernel-SIG] [linux 6.18.y] [Upstream] ntfs 7.2#2099
opsiff wants to merge 17 commits into
deepin-community:linux-6.18.yfrom
opsiff:linux-6.18.y-2026-08-25-update-ntfs

Conversation

@opsiff

@opsiff opsiff commented Aug 25, 2026

Copy link
Copy Markdown
Member

Summary by Sourcery

Harden NTFS filesystem operations and metadata handling for Linux 6.18.

Bug Fixes:

  • Fix NTFS writeback, truncation, allocation, shutdown, lookup, and rename races that could deadlock, lose writes, or access invalid state.
  • Prevent invalid metadata references and improve error handling during runlist and attribute operations.
  • Protect NTFS system files from userspace modification and preserve required file attributes during inode creation.

Enhancements:

  • Improve NTFS metadata integrity by restoring index records after writes and safely handling runlist memory sizing and allocation.
  • Correct directory creation mode handling and refine MFT writeback sizing and fallocate zeroing behavior.

Cen Zhang and others added 14 commits August 25, 2026 12:56
ntfs_write_mft_block() maps each $MFT record through the $MFT data
runlist. For sub-folio clusters it looks up a struct runlist_element under
ni->runlist.lock, drops the lock, and later uses rl->length and rl->vcn
when choosing folio_sz.

That pointer is only borrowed from ni->runlist.rl. Concurrent $MFT
allocation extension can merge a replacement runlist under the same lock,
and ntfs_rl_realloc() can free the old backing array. If that happens
between the lookup and the later folio_sz decision, writeback can
dereference freed runlist storage.

The buggy scenario involves two paths, with each column showing the order
within that path:

MFT writeback path:               $MFT allocation extension:
1. Look up rl under               1. Extend the $MFT data allocation.
   ni->runlist.lock.              2. Publish a replacement runlist.
2. Drop ni->runlist.lock.         3. Free the old runlist array.
3. Read rl->length and rl->vcn
   to choose folio_sz.

Compute the remaining run length while ni->runlist.lock is still held, and
use that scalar after unlock. This preserves the existing folio sizing
decision without carrying a borrowed runlist_element across the lock
boundary.

Validation reproduced this kernel report:
BUG: KASAN: slab-use-after-free in ntfs_mft_writepages+0x1c8d/0x1fb0

Call Trace:
 <TASK>
 dump_stack_lvl+0x66/0xa0
 print_report+0xce/0x630
 ? ntfs_mft_writepages+0x1c8d/0x1fb0
 ? srso_alias_return_thunk+0x5/0xfbef5
 ? __virt_addr_valid+0x20d/0x410
 ? ntfs_mft_writepages+0x1c8d/0x1fb0
 kasan_report+0xe0/0x110
 ? ntfs_mft_writepages+0x1c8d/0x1fb0
 ntfs_mft_writepages+0x1c8d/0x1fb0
 ? __pfx_ntfs_mft_writepages+0x10/0x10
 ? __pfx___mutex_unlock_slowpath+0x10/0x10
 ? srso_alias_return_thunk+0x5/0xfbef5
 ? iput+0x92/0xa80
 do_writepages+0x219/0x530
 ? __pfx_do_writepages+0x10/0x10
 __writeback_single_inode+0x117/0xf50
 ? do_raw_spin_lock+0x130/0x270
 ? __pfx_do_raw_spin_lock+0x10/0x10
 ? __pfx___writeback_single_inode+0x10/0x10
 ? srso_alias_return_thunk+0x5/0xfbef5
 writeback_sb_inodes+0x65b/0x1810
 ? srso_alias_return_thunk+0x5/0xfbef5
 ? lock_acquire+0x2b8/0x2f0
 ? __pfx_writeback_sb_inodes+0x10/0x10
 ? lock_release+0x1e0/0x280
 ? _raw_spin_unlock+0x23/0x40
 ? move_expired_inodes+0x2b8/0x850
 __writeback_inodes_wb+0xf4/0x270
 ? __pfx___writeback_inodes_wb+0x10/0x10
 ? srso_alias_return_thunk+0x5/0xfbef5
 ? queue_io+0x2e4/0x410
 wb_writeback+0x666/0x880
 ? srso_alias_return_thunk+0x5/0xfbef5
 ? __pfx_wb_writeback+0x10/0x10
 ? srso_alias_return_thunk+0x5/0xfbef5
 ? srso_alias_return_thunk+0x5/0xfbef5
 ? get_nr_dirty_inodes+0x1c/0x170
 wb_workfn+0x75e/0xbb0
 ? srso_alias_return_thunk+0x5/0xfbef5
 ? _raw_spin_unlock_irqrestore+0x27/0x60
 ? __pfx_wb_workfn+0x10/0x10
 ? __pfx_debug_object_deactivate+0x10/0x10
 ? lock_acquire+0x2b8/0x2f0
 ? srso_alias_return_thunk+0x5/0xfbef5
 ? lock_release+0x1e0/0x280
 process_one_work+0x8d0/0x1870
 ? __pfx_process_one_work+0x10/0x10
 ? srso_alias_return_thunk+0x5/0xfbef5
 worker_thread+0x575/0xf80
 ? __pfx_worker_thread+0x10/0x10
 kthread+0x2e7/0x3c0
 ? __pfx_kthread+0x10/0x10
 ret_from_fork+0x576/0x810
 ? __pfx_ret_from_fork+0x10/0x10
 ? srso_alias_return_thunk+0x5/0xfbef5
 ? __switch_to+0x57e/0xe10
 ? __switch_to_asm+0x33/0x70
 ? __pfx_kthread+0x10/0x10
 ret_from_fork_asm+0x1a/0x30
 </TASK>

Allocated by task 970:
 kasan_save_stack+0x33/0x60
 kasan_save_track+0x14/0x30
 __kasan_kmalloc+0xaa/0xb0
 __kvmalloc_node_noprof+0x353/0x920
 ntfs_rl_realloc+0x3c/0x80
 ntfs_runlists_merge+0x1212/0x3010
 ntfs_mft_data_extend_allocation_nolock+0x3e0/0x1f40
 ntfs_mft_record_alloc+0x1ab4/0x4f10
 __ntfs_create+0x680/0x2e50
 ntfs_create+0x1e6/0x3a0
 path_openat+0x2b55/0x3c10
 do_file_open+0x1f4/0x460
 do_sys_openat2+0xde/0x170
 __x64_sys_openat+0x122/0x1e0
 do_syscall_64+0x115/0x6a0
 entry_SYSCALL_64_after_hwframe+0x77/0x7f

Freed by task 1294:
 kasan_save_stack+0x33/0x60
 kasan_save_track+0x14/0x30
 kasan_save_free_info+0x3b/0x60
 __kasan_slab_free+0x5f/0x80
 kfree+0x307/0x580
 ntfs_rl_realloc+0x66/0x80
 ntfs_runlists_merge+0x1212/0x3010
 ntfs_mft_data_extend_allocation_nolock+0x3e0/0x1f40
 ntfs_mft_record_alloc+0x1ab4/0x4f10
 __ntfs_create+0x680/0x2e50
 ntfs_create+0x1e6/0x3a0
 path_openat+0x2b55/0x3c10
 do_file_open+0x1f4/0x460
 do_sys_openat2+0xde/0x170
 __x64_sys_openat+0x122/0x1e0
 do_syscall_64+0x115/0x6a0
 entry_SYSCALL_64_after_hwframe+0x77/0x7f

Fixes: 115380f ("ntfs: update mft operations")
Assisted-by: Codex:gpt-5.5
Signed-off-by: Cen Zhang <zzzccc427@gmail.com>
Reviewed-by: Hyunchul Lee <hyc.lee@gmail.com>
Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
(cherry picked from commit 81fe702)
Signed-off-by: Wentao Guan <guanwentao@uniontech.com>
ntfs_attr_fallocate() allocates holes and delayed allocations inside
initialized size by looking up the current runlist element under
ni->runlist.lock. The returned struct runlist_element is only a borrowed
pointer into ni->runlist.rl. A writer can replace and free that array
after the read lock is dropped, so later reads of rl->lcn, rl->length and
rl->vcn can touch freed memory.

The buggy scenario involves two paths, with each column showing the order
within that path:

ntfs_attr_fallocate():
  1. Take ni->runlist.lock for read.
  2. Get rl from ntfs_attr_find_vcn_nolock().
  3. Drop ni->runlist.lock.
  4. Read rl->lcn, rl->length and rl->vcn.

mmap page_mkwrite:
  1. Enter ntfs_filemap_page_mkwrite().
  2. Reach __ntfs_write_iomap_begin() and ntfs_attr_map_cluster().
  3. Merge allocation state with ntfs_runlists_merge().
  4. Reallocate ni->runlist.rl in ntfs_rl_realloc(), freeing the old array.

Validation reproduced this kernel report:
BUG: KASAN: slab-use-after-free in ntfs_attr_fallocate+0xbb8/0xd00

Call Trace:
 <TASK>
 dump_stack_lvl+0x66/0xa0
 print_report+0xce/0x630
 ? ntfs_attr_fallocate+0xbb8/0xd00
 ? srso_alias_return_thunk+0x5/0xfbef5
 ? __virt_addr_valid+0x20d/0x410
 ? ntfs_attr_fallocate+0xbb8/0xd00
 kasan_report+0xe0/0x110
 ? ntfs_attr_fallocate+0xbb8/0xd00
 ntfs_attr_fallocate+0xbb8/0xd00
 ? lock_acquire+0x2b8/0x2f0
 ? __pfx_ntfs_attr_fallocate+0x10/0x10
 ? 0xffffffffc0000095
 ? down_write+0x10d/0x1e0
 ntfs_fallocate+0x5c9/0x1d00
 ? __pfx_ntfs_fallocate+0x10/0x10
 ? srso_alias_return_thunk+0x5/0xfbef5
 ? lock_acquire+0x2b8/0x2f0
 ? srso_alias_return_thunk+0x5/0xfbef5
 ? selinux_file_permission+0x3a7/0x510
 vfs_fallocate+0x29d/0xd30
 __x64_sys_fallocate+0xc7/0x150
 ? do_syscall_64+0x81/0x6a0
 do_syscall_64+0x115/0x6a0
 entry_SYSCALL_64_after_hwframe+0x77/0x7f

Allocated by task 410:
 kasan_save_stack+0x33/0x60
 kasan_save_track+0x14/0x30
 __kasan_kmalloc+0xaa/0xb0
 __kvmalloc_node_noprof+0x353/0x920
 ntfs_rl_realloc+0x3f/0x110
 ntfs_runlists_merge+0xaa3/0x3010
 ntfs_attr_map_cluster+0x4e5/0xf80
 ntfs_attr_fallocate+0x53f/0xd00
 ntfs_fallocate+0x5c9/0x1d00
 vfs_fallocate+0x29d/0xd30
 __x64_sys_fallocate+0xc7/0x150
 do_syscall_64+0x115/0x6a0
 entry_SYSCALL_64_after_hwframe+0x77/0x7f

Freed by task 424:
 kasan_save_stack+0x33/0x60
 kasan_save_track+0x14/0x30
 kasan_save_free_info+0x3b/0x60
 __kasan_slab_free+0x5f/0x80
 kfree+0x307/0x580
 ntfs_rl_realloc+0x6f/0x110
 ntfs_runlists_merge+0x7b1/0x3010
 ntfs_attr_map_cluster+0x4e5/0xf80
 __ntfs_write_iomap_begin+0x8cd/0x2280
 iomap_iter+0x6de/0x11e0
 iomap_page_mkwrite+0x391/0x650
 ntfs_filemap_page_mkwrite+0x1ac/0x400
 do_page_mkwrite+0x15c/0x280
 __handle_mm_fault+0xd6d/0x1ca0
 handle_mm_fault+0x19c/0x470
 do_user_addr_fault+0x23b/0x9c0
 exc_page_fault+0x5c/0xc0
 asm_exc_page_fault+0x26/0x30

Fix this by copying the needed runlist fields while the read lock is still
held and using only those scalar snapshots after unlocking.

After the snapshot, ntfs_attr_map_cluster() can also find that the range
is already mapped and return balloc=false. Only call ntfs_dio_zero_range()
when new clusters were allocated, matching the write iomap path and
preserving the zero-newly-allocated-holes behavior.

Fixes: 495e90f ("ntfs: update attrib operations")
Assisted-by: Codex:gpt-5.5
Signed-off-by: Cen Zhang <zzzccc427@gmail.com>
Reviewed-by: Hyunchul Lee <hyc.lee@gmail.com>
Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
(cherry picked from commit 88496c4)
Signed-off-by: Wentao Guan <guanwentao@uniontech.com>
ntfs_file_fsync(), ntfs_dir_fsync() and __ntfs_write_inode() lock an
inode's mrec_lock before taking the mrec_lock of its parent directory.

ntfs_rename() takes old_ni->mrec_lock and old_dir_ni->mrec_lock
before taking new_ni->mrec_lock for an existing target, or
new_dir_ni->mrec_lock for a cross-directory rename.
This can deadlock when ntfs_file_fsync() or __ntfs_write_inode() holds
the target inode, or when ntfs_dir_fsync() holds a child target
directory, while rename() holds the parent directory and waits for the
target.

Fix this by locking the existing target inode before taking any parent
directory mrec_lock. For cross-directory renames where the target parent
is a descendant of the source parent, lock the target parent before the
source parent so the directory order matches the child-to-parent order used
by ntfs_file_fsync(), ntfs_dir_fsync(), and __ntfs_write_inode().

Reported-by: Peiyang He <peiyang_he@smail.nju.edu.cn>
Closes: https://lore.kernel.org/all/C4D296F0E9F3D66C+9397ffbc-eb55-44bb-9b3f-5da4809e7955@smail.nju.edu.cn/
Fixes: af0db57 ("ntfs: update inode operations")
Cc: stable@vger.kernel.org
Signed-off-by: Peiyang He <peiyang_he@smail.nju.edu.cn>
Assisted-by: Codex:gpt-5.5
Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
(cherry picked from commit eb94f5a)
Signed-off-by: Wentao Guan <guanwentao@uniontech.com>
vfs_mkdir() now sets the S_IFDIR type bit in the mode it passes to
->mkdir(), so OR-ing S_IFDIR into the mode again in ntfs_mkdir() is
redundant. Drop it.

Assisted-by: LLM
Signed-off-by: Jori Koolstra <jkoolstra@xs4all.nl>
Link: https://patch.msgid.link/20260630105400.68459-32-jkoolstra@xs4all.nl
Reviewed-by: NeilBrown <neil@brown.name>
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
(cherry picked from commit a380b9693c7a005806a7bf901f4a2be8b4395249)
Signed-off-by: Wentao Guan <guanwentao@uniontech.com>
An attribute-list update performed while allocating clusters can drop the
last reference to the temporary attribute inode. Evicting that inode
drops its reference to the base inode and can invoke ntfs_drop_big_inode()
for the base inode from within the base inode's own writeback path.

If the base inode is unlinked, ntfs_drop_big_inode() calls
truncate_setsize(), which waits for the inode's folio writeback to
complete. The same writeback worker is responsible for completing that
writeback, so it waits for itself indefinitely.

Prevent this self-deadlock by grabbing a reference to the base inode at the
beginning of ntfs_writepages() and releasing it at the end of the function.
This defers eviction until all bios have been submitted, allowing the wait
for folio writeback to complete safely.

Fixes: b041ca5 ("ntfs: update iomap and address space operations")
Cc: stable@vger.kernel.org
Signed-off-by: Hyunchul Lee <hyc.lee@gmail.com>
Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
(cherry picked from commit 77dc384)
Signed-off-by: Wentao Guan <guanwentao@uniontech.com>
When ntfs_map_runlist_nolock() needs to look up the attribute extent
containing a target VCN (ctx_needs_reset == true), it calls
ntfs_attr_lookup() and then expects the result to be a non-resident
attribute, since only non-resident attributes have a mapping pairs
array to decompress.

A crafted NTFS image can place a resident attribute where a non-resident
one is expected, causing ntfs_attr_lookup() to succeed but return a
resident attribute record.  Previously this was caught only by a
WARN_ON(), which does not stop execution.  The code then falls through to
read a->data.non_resident.highest_vcn from what is actually a resident
attribute, accessing the wrong union member and corrupting the VCN range
check.

The caller path triggering this warning during mount is:

  ntfs_map_runlist_nolock
  ntfs_empty_logfile
  load_system_files
  ntfs_fill_super

In this path ctx is NULL, so ntfs_map_runlist_nolock() allocates a
temporary search context internally and sets ctx_needs_reset = true.
The existing resident-attribute guard in the ctx != NULL branch already
returns -EIO silently for the same condition; make the ctx_needs_reset
path consistent by replacing the WARN_ON() with the same -EIO error
return.

This causes the crafted image to be rejected with a mount error instead
of triggering a kernel warning.

Fixes: 495e90f ("ntfs: update attrib operations")
Cc: stable@vger.kernel.org
Reported-by: Sangho Lee <kudo3228@gmail.com>
Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
(cherry picked from commit b8d6c52)
Signed-off-by: Wentao Guan <guanwentao@uniontech.com>
When a system file such as $Bitmap is exposed via show_sys_files and
written from userspace, the volume is corrupted and, because the cluster
allocator scans $Bitmap through the same inode's page cache, a write to
$Bitmap also deadlocks writeback against the folio it already holds locked.

These files are maintained by the driver itself and have no valid reason
to be written through the file interface. Mark base metadata files
(mft_no < FILE_first_user) as immutable during inode read so the VFS
rejects write, mmap, truncate and unlink with -EPERM. Directories are
skipped so the root and $Extend remain usable. Internal metadata updates
do not go through the VFS write path and are unaffected.

Fixes: af0db57 ("ntfs: update inode operations")
Cc: stable@vger.kernel.org
Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
(cherry picked from commit f72df3a)
Signed-off-by: Wentao Guan <guanwentao@uniontech.com>
ntfs_icx_ib_sync_write() calls post_write_mst_fixup() when ntfs_ib_write()
returns an error, intending to restore the buffer after a failed write.

However, ntfs_ib_write() returns an error immediately if
pre_write_mst_fixup() validation fails. The caller,
ntfs_icx_ib_sync_write(), interprets any error as a write failure
requiring rollback. It does not differentiate between I/O errors and
validation failures, and calls post_write_mst_fixup() anyway.

Since post_write_mst_fixup() assumes that the index_block contents is
correct, it doesn't perform the boundary checks, which results in
out-of-bounds memory access.

An attacker can craft a malicious NTFS image with:
  - large index_block.usa_ofs offset, pointing outside the ntfs_record
  - index_block.usa_count = 0, causing integer underflow
  - or index_block.usa_count larger than actual number of sectors in the
    ntfs_record, causing out-of-bounds access

KASAN reports describing the memory corruption:
  ==================================================================
  BUG: KASAN: slab-out-of-bounds in post_write_mst_fixup+0x19c/0x1d0
  Read of size 2 at addr ffff8881586c9018 by task p/9428
  Call Trace:
   <TASK>
   dump_stack_lvl+0x100/0x190
   print_report+0x139/0x4ad
   ? post_write_mst_fixup+0x19c/0x1d0
   ? __virt_addr_valid+0x262/0x500
   ? post_write_mst_fixup+0x19c/0x1d0
   kasan_report+0xe4/0x1d0
   ? post_write_mst_fixup+0x19c/0x1d0
   post_write_mst_fixup+0x19c/0x1d0
   ntfs_icx_ib_sync_write+0x179/0x220
   ntfs_inode_sync_filename+0x83d/0x1080
   __ntfs_write_inode+0x1049/0x1480
   ntfs_file_fsync+0x131/0x9b0
  ==================================================================
  BUG: KASAN: slab-out-of-bounds in post_write_mst_fixup+0x1aa/0x1d0
  Write of size 2 at addr ffff8881586c91fe by task p/9428
  Call Trace:
   <TASK>
   dump_stack_lvl+0x100/0x190
   print_report+0x139/0x4ad
   ? post_write_mst_fixup+0x1aa/0x1d0
   ? __virt_addr_valid+0x262/0x500
   ? post_write_mst_fixup+0x1aa/0x1d0
   kasan_report+0xe4/0x1d0
   ? post_write_mst_fixup+0x1aa/0x1d0
   post_write_mst_fixup+0x1aa/0x1d0
   ntfs_icx_ib_sync_write+0x179/0x220
   ntfs_inode_sync_filename+0x83d/0x1080
   __ntfs_write_inode+0x1049/0x1480
   ntfs_file_fsync+0x131/0x9b0
  ==================================================================

Let's move the post_write_mst_fixup() call to ntfs_ib_write().
The ntfs_ib_write() function calls pre_write_mst_fixup() at the beginning.
If the index_block contents is invalid, pre_write_mst_fixup() fails and
ntfs_ib_write() returns early without calling post_write_mst_fixup() on
bad index_block.

Fixes: 0a8ac0c ("ntfs: update directory operations")
Cc: stable@vger.kernel.org
Signed-off-by: Valeriy Yashnikov <yashnikov.valeriy@gmail.com>
Reviewed-by: Hyunchul Lee <hyc.lee@gmail.com>
Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
(cherry picked from commit 5b6eedd)
Signed-off-by: Wentao Guan <guanwentao@uniontech.com>
ntfs_non_resident_attr_insert_range() allocates hole_rl before mapping the
whole runlist. If ntfs_attr_map_whole_runlist() fails, the error path drops
ni->runlist.lock and returns without freeing hole_rl. This leaks memory
of sizeof(*hole_rl) * 2 bytes.

Fix this memory leak by freeing hole_rl before returning from
that error path, matching the later error paths in the same function.

Fixes: 495e90f ("ntfs: update attrib operations")
Cc: stable@vger.kernel.org
Signed-off-by: Peiyang He <peiyang_he@smail.nju.edu.cn>
Reviewed-by: Hyunchul Lee <hyc.lee@gmail.com>
Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
(cherry picked from commit 06769b8)
Signed-off-by: Wentao Guan <guanwentao@uniontech.com>
ntfs_lookup_inode_by_name() returns MFT references read from directory
index entries on disk. These values are untrusted, but the function can
currently return an error-marked MFT reference to its callers without
validating it.

Callers later decode lookup failures with MREF_ERR(). A crafted NTFS image
can set the MREF error bit while leaving the low bits as an arbitrary
value, causing callers to consume a bogus pseudo-errno instead of treating
the lookup result as corrupted on-disk metadata.

Fix this at the source by normalizing every error-marked MFT reference
returned from ntfs_lookup_inode_by_name() to ERR_MREF(-EIO). Apply this to
all four directory lookup return paths so every caller gets a validated
result without needing additional checks or an API change.

This keeps the sanitization in the common lookup helper, which is cleaner
than duplicating validation in each caller.

Fixes: 1e9ea7e ("Revert "fs: Remove NTFS classic"")
Cc: stable@vger.kernel.org
Reported-by: Hongling Zeng <zenghongling@kylinos.cn>
Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
(cherry picked from commit d97a36b)
Signed-off-by: Wentao Guan <guanwentao@uniontech.com>
generic_shutdown_super() clears SB_ACTIVE before evicting cached inodes.
If eviction selects the fake inode for a base inode's unnamed
$ATTRIBUTE_LIST attribute, ntfs_evict_big_inode() drops the fake inode's
reference on the base inode while the fake inode is still hashed and marked
I_FREEING.

That iput can synchronously write back the base inode. The writeback path
may update mapping pairs and call ntfs_attrlist_update(), which
unconditionally calls ntfs_attr_iget() for the same $ATTRIBUTE_LIST fake
inode. VFS then finds the I_FREEING inode and waits for eviction to finish,
but the current task is still inside that eviction path, causing a
self-deadlock in find_inode().

Fix this by mirroring the teardown guard used by __ntfs_write_inode():
once SB_ACTIVE has been cleared, do not try to iget the attribute-list
fake inode. Return -EIO so teardown aborts the update instead of waiting on
the inode it is evicting.

Reported-by: Peiyang He <peiyang_he@smail.nju.edu.cn>
Closes: https://lore.kernel.org/all/AB8D5E603E6EA856+ae5f622a-dd3a-4e38-bdd2-42276ae0e1a8@smail.nju.edu.cn/
Fixes: 495e90f ("ntfs: update attrib operations")
Cc: stable@vger.kernel.org
Signed-off-by: Peiyang He <peiyang_he@smail.nju.edu.cn>
Assisted-by: Codex:gpt-5.5
Reviewed-by: Hyunchul Lee <hyc.lee@gmail.com>
Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
(cherry picked from commit 0ebe8f6)
Signed-off-by: Wentao Guan <guanwentao@uniontech.com>
When creating a WSL special file (socket, fifo, character or block
device), __ntfs_create() sets FILE_ATTRIBUTE_RECALL_ON_OPEN in ni->flags
as valid_reparse_data() requires for these tags. This flag is
intentionally absent from $FILE_NAME, so the subsequent reload

        ni->flags = fn->file_attributes;

drops it from ni->flags, the authoritative copy written back to
$STANDARD_INFORMATION. The on-disk file_attributes becomes 0x00000404
instead of 0x00040404, and after a remount valid_reparse_data() rejects
the reparse point while fsck reports "$REPARSE_POINT data is corrupted".

Preserve the RECALL_ON_OPEN bit across the reload. Symlinks do not set
that bit, so they are unaffected.

Fixes: af0db57 ("ntfs: update inode operations")
Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
(cherry picked from commit 523307b)
Signed-off-by: Wentao Guan <guanwentao@uniontech.com>
Add a shared helper to safely convert runlist element counts to byte sizes
using overflow checks, and use it in both ntfs_rl_realloc() and
ntfs_rl_realloc_nofail().

Fixes: 11ccc91 ("ntfs: update runlist handling and cluster allocator")
Co-developed-by: Alper Mudar <kommandant_alper@proton.me>
Signed-off-by: Alper Mudar <kommandant_alper@proton.me>
Tested-by: Alper Mudar <kommandant_alper@proton.me>
Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
(cherry picked from commit 8bed376)
Signed-off-by: Wentao Guan <guanwentao@uniontech.com>
ntfs_non_resident_attr_shrink() shrinks attribute sizes but fails to
trim the page cache. This leaves orphaned dirty folios beyond the new
end of the attribute, leading to writeback failures (-ENOENT), data
loss, and $EA chain corruption.

Fix this by truncating the page cache to the new size immediately after
updating the sizes, preventing writeback from flushing out-of-range folios.

Fixes: 495e90f ("ntfs: update attrib operations")
Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
(cherry picked from commit 4e646ec)
Signed-off-by: Wentao Guan <guanwentao@uniontech.com>
@sourcery-ai

sourcery-ai Bot commented Aug 25, 2026

Copy link
Copy Markdown

Reviewer's Guide

The PR backports NTFS 7.2 upstream fixes for writeback and shutdown deadlocks, stale page-cache and runlist correctness, metadata/error validation, safer system-file handling, deterministic rename locking, and robust MFT/index write behavior.

Sequence diagram for NTFS writeback eviction safety

sequenceDiagram
    participant WB as Writeback
    participant NTFS as NTFS
    participant Inode as Inode
    participant Iomap as iomap_writepages

    WB->>NTFS: ntfs_writepages
    NTFS->>Inode: igrab
    NTFS->>Iomap: iomap_writepages
    Iomap-->>NTFS: ret
    NTFS->>Inode: iput
    NTFS-->>WB: ret
Loading

Sequence diagram for NTFS attribute shrink cache invalidation

sequenceDiagram
    participant Caller
    participant Attr as NTFS attribute
    participant Cache as Page cache
    participant Runlist

    Caller->>Attr: ntfs_non_resident_attr_shrink
    Attr->>Runlist: truncate allocation and runlist
    Attr->>Cache: truncate_inode_pages
    Cache-->>Attr: stale folios removed
    Attr-->>Caller: result
Loading

Flow diagram for NTFS rename lock ordering

flowchart TD
    Start["ntfs_rename"] --> Same{Same parent directory?}
    Same -->|Yes| OldDir["Lock old directory"]
    Same -->|No| Order{new_dir_first?}
    Order -->|Yes| NewFirst["Lock new directory, then old directory"]
    Order -->|No| OldFirst["Lock old directory, then new directory"]
    OldDir --> Target["Lock replacement inode if present"]
    NewFirst --> Target
    OldFirst --> Target
    Target --> Check["Check NInoBeingDeleted"]
    Check --> Operation["ntfs_delete / __ntfs_link"]
Loading

File-Level Changes

Change Details Files
Harden writeback, allocation, truncation, and runlist handling against races, stale cache data, invalid metadata, and allocation failures.
  • Pin relevant inodes during iomap writeback to avoid eviction deadlocks.
  • Convert invalid resident-attribute states to errors and truncate page cache after shrinking attributes.
  • Free temporary runlists on failure, snapshot runlist fields under lock, and avoid unnecessary zeroing for non-allocating fallocate paths.
  • Add overflow/size validation and configurable GFP flags to runlist reallocation.
fs/ntfs/aops.c
fs/ntfs/attrib.c
fs/ntfs/runlist.c
Improve shutdown safety and on-disk metadata integrity checks.
  • Reject attribute-list inode lookups after the superblock becomes inactive.
  • Normalize invalid directory MFT references to -EIO.
  • Restore MST-protected index records immediately after index writes, including failed writes.
fs/ntfs/attrlist.c
fs/ntfs/dir.c
fs/ntfs/index.c
Restrict unsafe system-file access and preserve NTFS inode metadata during creation.
  • Force regular system files before FILE_first_user immutable while leaving directories usable.
  • Preserve FILE_ATTRIBUTE_RECALL_ON_OPEN when initializing new inode flags.
  • Pass directory mode directly when creating directories.
fs/ntfs/inode.c
fs/ntfs/namei.c
Make rename locking deterministic across directories and replacement targets.
  • Precompute involved inodes and parent ordering using is_subdir.
  • Acquire parent and target locks in explicit nested orders.
  • Validate deletion state for all affected inodes and symmetrically release locks on every exit path.
fs/ntfs/namei.c
Make MFT writeback runlist access lifetime-safe.
  • Keep only the required runlist length after releasing the runlist lock.
  • Use the captured length when selecting the folio write size.
fs/ntfs/mft.c

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@deepin-ci-robot

Copy link
Copy Markdown

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by:
Once this PR has been reviewed and has the lgtm label, please ask for approval from opsiff. For more information see the Code Review Process.

The full list of commands accepted by this bot can be found here.

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've reviewed your changes and they look great!


Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Hardens NTFS metadata, allocation, writeback, and locking behavior for Linux 6.18.

Changes:

  • Improves runlist safety and writeback lifecycle handling.
  • Hardens metadata validation, preservation, and system-file protection.
  • Fixes allocation, truncation, shutdown, and rename handling.

Reviewed changes

Copilot reviewed 9 out of 9 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
fs/ntfs/runlist.c Adds overflow-safe, configurable runlist allocation.
fs/ntfs/namei.c Updates creation flags and rename locking.
fs/ntfs/mft.c Avoids using runlist entries after unlocking.
fs/ntfs/inode.c Protects NTFS system files from userspace writes.
fs/ntfs/index.c Restores MST-protected index data after writes.
fs/ntfs/dir.c Rejects invalid directory MFT references.
fs/ntfs/attrlist.c Avoids attribute lookup during shutdown.
fs/ntfs/attrib.c Hardens truncation, fallocate, and runlist handling.
fs/ntfs/aops.c Pins inodes during writeback.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread fs/ntfs/namei.c Outdated
ntfs_set_volume_flags(vol, VOLUME_IS_DIRTY);

ni = __ntfs_create(idmap, dir, uname, uname_len, S_IFDIR | mode, 0, NULL, 0);
ni = __ntfs_create(idmap, dir, uname, uname_len, mode, 0, NULL, 0);

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

Comment thread fs/ntfs/attrib.c Outdated
Comment on lines +5669 to +5670
if (err > 0)
goto out;

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

Comment thread fs/ntfs/attrib.c Outdated
* 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);

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

opsiff added 3 commits August 25, 2026 13:33
deepin inclusion
category: bugfix

vfs_mkdir() passes a mode with the S_IFMT bits cleared on kernels where
vfs_prepare_mode() does not yet add the file type (the type argument is
0).  Dropping the explicit S_IFDIR makes __ntfs_create() take its
non-directory path: the inode gets a $DATA attribute instead of $I30,
and neither its VFS mode nor its MFT record is marked as a directory.

Restore the directory type bit at the mkdir call site so that kernels
whose vfs_mkdir() does not set S_IFDIR still create real directories.

Fixes: a380b9693c7a0 ("ntfs: drop redundant S_IFDIR from mkdir") # this branch only
Signed-off-by: Wentao Guan <guanwentao@uniontech.com>
ntfs_dio_zero_range() returns either 0 or a negative errno from
blkdev_issue_zeroout(); it never returns a positive value.  The
zeroing failure check in ntfs_attr_fallocate() therefore never fired,
so a failed zeroing operation was silently ignored: the loop kept
going, the newly allocated clusters were folded into initialized_size
and the write could succeed leaving stale on-disk data.

Treat any nonzero return as an error and abort the allocation.

Fixes: 495e90f ("ntfs: update attrib operations")
Cc: stable@vger.kernel.org
Signed-off-by: Wentao Guan <guanwentao@uniontech.com>
ntfs_non_resident_attr_shrink() called truncate_inode_pages() only
after ntfs_cluster_free() had freed the backing clusters and
ntfs_rl_truncate_nolock() had truncated the runlist.  This leaves a
window where writeback can start on a stale folio; if it already holds
the folio while waiting for mrec_lock, the truncation can also wait on
that folio and deadlock.  The MM truncation contract requires dropping
page-cache contents before releasing their backing resources, so move
the invalidation ahead of the cluster free under the write/truncate
serialization.

Fixes: 4e646ec ("ntfs: drop stale page-cache when shrinking a non-resident attr")
Cc: stable@vger.kernel.org
Signed-off-by: Wentao Guan <guanwentao@uniontech.com>

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 9 out of 9 changed files in this pull request and generated 2 comments.

Comment thread fs/ntfs/attrib.c
* the write. The MM truncation contract requires dropping page-cache
* contents before releasing their backing resources.
*/
truncate_inode_pages(VFS_I(ni)->i_mapping, newsize);
Comment thread fs/ntfs/attrib.c
Comment on lines +5670 to +5671
if (err)
goto out;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants