summaryrefslogtreecommitdiff
path: root/fs/ntfs
AgeCommit message (Collapse)Author
18 hoursMerge branch 'vfs.all' of ↵Mark Brown
https://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs.git
32 hoursMerge branch 'vfs-7.4.idmap' into vfs.allChristian Brauner
41 hoursntfs: report birth time for symbolic linksXuewen Wang
NTFS already loads the creation time into i_crtime and reports it through ntfs_getattr(). However, the symbolic-link inode operations omit getattr, so statx() with AT_SYMLINK_NOFOLLOW falls back to generic_fillattr() and does not return STATX_BTIME for the link itself. Use ntfs_getattr() for symbolic links to expose their stored creation time. Signed-off-by: Xuewen Wang <wangxuewen@kylinos.cn> Reviewed-by: Baolin Liu <liubaolin@kylinos.cn> Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
41 hoursntfs: support FS_IOC_GETFSUUIDBaolin Liu
NTFS reads the 64-bit volume serial number from the boot sector but leaves the superblock UUID unset. As a result, the generic FS_IOC_GETFSUUID handler returns ENOTTY for files and directories on an NTFS volume. Register the on-disk little-endian serial number as an eight-byte filesystem identifier with super_set_uuid(). The generic VFS handler can then expose the existing volume identifier. Signed-off-by: Baolin Liu <liubaolin@kylinos.cn> Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
41 hoursntfs: flush the block device when syncing a directoryDaeMyung Kang
ntfs_dir_fsync() writes directory metadata and calls sync_blockdev(), but does not flush the block device write cache. A successful fsync() can therefore leave the directory's metadata in a volatile device cache. Issue a cache flush after successful writeback and return any flush error. This matches the regular-file fsync and syncfs paths. With QEMU blkdebug injecting -EIO for flush_to_disk, fsync() on an NTFS directory returned 0 before this change while syncfs() returned -EIO. After this change, both return -EIO. A kretprobe confirmed the flush error in the directory fsync path. Fixes: 1e9ea7e04472 ("Revert "fs: Remove NTFS classic"") Cc: stable@vger.kernel.org Signed-off-by: DaeMyung Kang <charsyam@gmail.com> Reviewed-by: Baolin Liu <liubaolin@kylinos.cn> Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
41 hoursntfs: report block device flush failures from fsyncDaeMyung Kang
ntfs_file_fsync() calls blkdev_issue_flush() after writeback but discards its return value. If the device cannot flush its write cache, fsync() reports success even though the file is not guaranteed to be durable. Reproduce this with QEMU blkdebug failing flush_to_disk with -EIO. On ntfs-next at 04fb910da2c8, a kretprobe recorded -EIO from blkdev_issue_flush() while fsync() returned 0. With this change, fsync() returns -EIO and logs the failure. The syncfs() path already reports this error. Fixes: 9c87959601e8 ("ntfs: update file operations") Cc: stable@vger.kernel.org Signed-off-by: DaeMyung Kang <charsyam@gmail.com> Reviewed-by: Baolin Liu <liubaolin@kylinos.cn> Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
3 daysntfs: fix NULL dereference on index allocation open failureNamjae Jeon
ntfs_index_lookup() returns -ENOENT when ntfs_ia_open() fails to open the index allocation. This error path leaves icx->ib unset, unlike a missing key in an otherwise valid index, which returns an insertion context. ntfs_ie_add() treats -ENOENT as a valid insertion position and dereferences icx->ib when the lookup is not in the index root. A corrupt $I30 bitmap attribute can therefore cause a NULL pointer dereference when adding a directory entry. Return -EIO for index allocation open failures so insertion aborts through the existing error path. Keep -ENOENT for genuinely missing keys and document that other errors leave no valid index entry. Fixes: 0a8ac0c1fa0b ("ntfs: update directory operations") Reported-by: 1sh1ro <yangguikun19@qq.com> Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
3 daysntfs: compare streams_interface with mounted volume on remountDaeMyung Kang
During reconfiguration, fc->s_fs_info points to a newly allocated ntfs_volume. It does not contain the mounted volume's stream setting. Consequently, a windows mount rejects a remount with the same streams_interface=windows setting, but accepts streams_interface=none without changing the active pathname interface. This also breaks an ordinary read-only remount when the existing windows setting is passed again, for example from fstab. Compare the requested setting with the mounted volume instead. Report an unsupported change with ntfs_warning() so it does not invoke the mounted filesystem's error-handling policy. Fixes: 2de2cdd9cc08 ("ntfs: add pathname access for named streams") Signed-off-by: DaeMyung Kang <charsyam@gmail.com> Reviewed-by: Baolin Liu <liubaolin@kylinos.cn> Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
4 daysntfs: widen the sub-block offsets in ntfs_decompress()Zhan Xusheng
ntfs_decompress() tracks where the current sub-block starts and ends inside the destination page with two u16s: u16 do_sb_start; u16 do_sb_end; ... do_sb_start = *dest_ofs; do_sb_end = do_sb_start + NTFS_SB_SIZE; *dest_ofs is an int page offset masked with ~PAGE_MASK, and NTFS_SB_SIZE is 0x1000, so do_sb_end needs PAGE_SIZE + 0xfff of range. That exceeds u16 once PAGE_SIZE reaches 0x10000: the last sub-block of a page starts at 0xf000 and do_sb_end wraps from 0x10000 to 0. The zero-fill that pads a short sub-block then computes a negative length: if (dp_addr < dp_sb_end) { int nr_bytes = do_sb_end - *dest_ofs; memset(dp_addr, 0, nr_bytes); A sub-block made only of symbol tokens reaches this without consulting do_sb_end on the way, because that path just does *dp_addr++ = *cb++; ++*dest_ofs; and is bounded by dp_addr against dp_sb_end. With do_sb_start at 0xf000 and eight literal bytes emitted, nr_bytes is -61448 and the memset() runs for 18446744073709490168 bytes. Getting there is cheap: fifteen sub-blocks of eleven bytes each advance *dest_ofs to 0xf000, since the same zero-fill pads each of them out to 0x1000 while do_sb_end is still exact, and the sixteenth one triggers the wrap. A compression block is 0x10000 bytes with 4 KiB clusters and the fixed compression unit of 4, so all sixteen fit with room to spare. Phrase tokens are bounded against do_sb_end and so return -EOVERFLOW instead, and the if (*dest_index == dest_max_index && do_sb_end > dest_max_ofs) check at the top of the sub-block stops firing for the same reason. Use int for both, matching *dest_ofs and dest_max_ofs. The narrow type came in with the original import and survived the removal and revert of the driver. Fixes: 1e9ea7e04472 ("Revert "fs: Remove NTFS classic"") Signed-off-by: Zhan Xusheng <zhanxusheng@xiaomi.com> Reviewed-by: Baolin Liu <liubaolin@kylinos.cn> Reviewed-by: Hyunchul Lee <hyc.lee@gmail.com> Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
4 daysntfs: reject direct I/O reads of encrypted filesZhan Xusheng
ntfs_file_read_iter() refuses direct I/O for compressed and WOF compressed attributes but lets encrypted ones reach iomap_dio_rw(). ntfs_read_iomap_begin_non_resident() has no NInoEncrypted() test, so it reports IOMAP_MAPPED for the attribute's real clusters and the raw on-disk ciphertext is copied to the caller. Buffered reads are refused by ntfs_read_folio(), but direct I/O never calls it. Fixes: 9c87959601e8 ("ntfs: update file operations") Signed-off-by: Zhan Xusheng <zhanxusheng@xiaomi.com> Reviewed-by: Baolin Liu <liubaolin@kylinos.cn> Reviewed-by: Hyunchul Lee <hyc.lee@gmail.com> Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
4 daysntfs: do not read ahead encrypted filesZhan Xusheng
ntfs_read_folio() refuses EFS-encrypted files with -EOPNOTSUPP because decryption is not implemented. ntfs_readahead() has no NInoEncrypted() test and passes them to iomap_readahead(), which maps the runlist and fills the folios with the raw on-disk ciphertext. The folios are marked uptodate, so the read that follows never reaches ->read_folio and returns ciphertext instead of -EOPNOTSUPP. ntfs_read_locked_inode() sets NInoEncrypted() and NInoNonResident() for a non-resident encrypted $DATA attribute and rejects the encrypted-and-compressed combination, so none of the three existing conditions fires. read(), mmap(), splice() and fadvise() all populate the page cache through readahead. Skipping readahead leaves the folio unpopulated, so the read falls back to ntfs_read_folio() and gets the intended error. Fixes: b041ca562526 ("ntfs: update iomap and address space operations") Signed-off-by: Zhan Xusheng <zhanxusheng@xiaomi.com> Reviewed-by: Baolin Liu <liubaolin@kylinos.cn> Reviewed-by: Hyunchul Lee <hyc.lee@gmail.com> Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
4 daysntfs: persist nodump in the $LINUXFLAGS EABaolin Liu
Store nodump in bit 2 of LINUXFLAGS and allow it through fileattr_set. Report the cached persistent bit through fileattr_get and STATX_ATTR_NODUMP, including the statx supported-attribute mask. Reuse the existing EA update and inode-load paths; nodump needs no additional inode flag enforcement. Document the complete immutable, append-only and nodump bitmask. Signed-off-by: Baolin Liu <liubaolin@kylinos.cn> Reviewed-by: Hyunchul Lee <hyc.lee@gmail.com> Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
4 daysntfs: persist append-only in the $LINUXFLAGS EABaolin Liu
Extend the persistent fileattr interface to append-only using bit 1 of LINUXFLAGS. Restore S_APPEND on inode load and update it after successful EA writes. Include append in the known-bit mask while retaining all other unknown bits. Reuse the existing VFS capability checks and EA safeguards. Document append-only alongside immutable. Both flags now survive inode reclaim and remount without changing the NTFS attribute format. Signed-off-by: Baolin Liu <liubaolin@kylinos.cn> Reviewed-by: Hyunchul Lee <hyc.lee@gmail.com> Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
4 daysntfs: persist immutable in the $LINUXFLAGS EABaolin Liu
Add a fileattr setter for immutable and store its state in bit 0 of a little-endian u32 in the private $LINUXFLAGS EA. Reuse the standard NTFS EA attributes and restore the flag when loading an inode, so eviction and unmount do not discard the setting. Use $LINUXFLAGS to avoid the WSL-owned $LX namespace. Preserve unknown bits and delete the entry only when the entire value is zero. Reject malformed values with EUCLEAN. Reject direct xattr writes and removal to avoid bypassing fileattr permissions or stale cached state. Update the cached flags only after the EA update succeeds. Reject clearing immutable on system metadata files and files protected by sys_immutable, following inode-load policy. Do not persist a bit derived solely from this policy. Preserve append state without allowing it to be changed at this stage. Compression, encryption and mount-wide case folding remain read-only. Document the format and its Linux-only enforcement. Signed-off-by: Baolin Liu <liubaolin@kylinos.cn> Reviewed-by: Hyunchul Lee <hyc.lee@gmail.com> Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
4 daysntfs: add fileattr query supportBaolin Liu
Add fileattr_get operations for files and directories, as well as the existing symlink and special inode operation tables. Report compression, encryption, immutable and append-only from cached inode state, and FS_CASEFOLD_FL from the mount-wide case sensitivity policy. This introduces querying only. It neither reads a private flag EA nor allows userspace to change the reported flags. Signed-off-by: Baolin Liu <liubaolin@kylinos.cn> Reviewed-by: Hyunchul Lee <hyc.lee@gmail.com> Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
4 daysntfs: restore EA attributes when an update failsBaolin Liu
Creating or replacing an EA can leave EA data and information out of sync if a later update fails. Existing deletion rollback ignores its own errors, and creation changes metadata before preparing the new list. Prepare the complete new list while retaining both original attributes. Update EA data and information only after preparation succeeds, restore both on failure, and publish the cached presence state only on success. Keep structure errors as EUCLEAN and preserve operation errors. Mark the volume erroneous and shut it down if either restoration fails. This handles synchronous failures; it does not make the two updates atomic across power loss or guarantee restoration on a failing device. Signed-off-by: Baolin Liu <liubaolin@kylinos.cn> Reviewed-by: Hyunchul Lee <hyc.lee@gmail.com> Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
4 daysntfs: distinguish empty EA values from removalBaolin Liu
The EA setter identifies removal using a zero value length together with XATTR_REPLACE. A replacement with a valid empty value is therefore mistaken for deletion. Use a NULL value to identify removal and keep zero-length values valid. Reject removal or XATTR_REPLACE for an absent entry before creating metadata, including when the file has no EA attributes. Signed-off-by: Baolin Liu <liubaolin@kylinos.cn> Reviewed-by: Hyunchul Lee <hyc.lee@gmail.com> Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
4 daysntfs: preserve EA attribute lookup errorsBaolin Liu
ntfs_attr_exist() treats lookup failures as absence. Using it while updating EAs can select creation or removal paths after an allocation or I/O failure. Use an EA-local lookup helper that preserves errors from mapping the MFT record, allocating the search context and looking up the attribute. Check both EA attributes before modifying either and return EUCLEAN if only one exists. Keep the known presence state for the final update. Signed-off-by: Baolin Liu <liubaolin@kylinos.cn> Reviewed-by: Hyunchul Lee <hyc.lee@gmail.com> Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
4 daysntfs: propagate EA write and truncate errorsBaolin Liu
ntfs_write_ea() replaces negative write errors with EIO and ignores errors when shrinking the attribute after writing a shorter list. This can report success even though the final resize failed. Preserve negative write errors, use EIO for short writes, and return truncate failures to the caller. Signed-off-by: Baolin Liu <liubaolin@kylinos.cn> Reviewed-by: Hyunchul Lee <hyc.lee@gmail.com> Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
4 daysntfs: validate EA chains and link zero-terminated tailsBaolin Liu
A zero next-entry offset terminates an EA list. Appending an entry without linking the old tail makes the new entry unreachable. Invalid entries are also treated as missing names, allowing setters to proceed on corrupt metadata. Validate the entire chain before modifying it and connect a zero tail to the append position. Check name terminators and retain the full tail span when removing an entry. Return EUCLEAN for malformed EA structures and inconsistent information lengths, preserving that error through the get, set and list paths. Keep ENOENT for a missing name and translate it to ENODATA only at the xattr read boundary. Preserve lower-level read errors. Reuse the same full-chain validation in ntfs_listxattr(), including size-only queries, before copying names. Reject malformed lists with EUCLEAN instead of returning an empty or partial list. Signed-off-by: Baolin Liu <liubaolin@kylinos.cn> Reviewed-by: Hyunchul Lee <hyc.lee@gmail.com> Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
4 daysntfs: keep the EA presence flag after creating the first entryBaolin Liu
The successful EA update path tests ea_info_qsize when updating NInoHasEA, but this size excludes the appended entry. Creating the first EA, or replacing the only entry, can therefore clear the cached presence flag despite a successful update. Reads then return ENODATA until reload. Include the appended entry in the size used for the presence test. Signed-off-by: Baolin Liu <liubaolin@kylinos.cn> Reviewed-by: Hyunchul Lee <hyc.lee@gmail.com> Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
4 daysntfs: add pathname access for named streamsNamjae Jeon
Add an optional Windows-style pathname interface for named $DATA streams, enabled with streams_interface=windows. The option defaults to none, and the named-stream ioctls remain available regardless of this setting. Support lookup, creation, and removal of named streams for existing regular files and directories. A stream is opened as a regular file through the base-name and stream-name form in the final path component. A stream can be created only for an existing file or directory. The pathname interface accepts a base file name and stream name only. It rejects paths that also specify an NTFS attribute type such as $DATA. In windows mode, ordinary filenames containing a colon are hidden from directory listings and cannot be accessed through the pathname interface. Match stream names case-insensitively. Streams and their base objects report the same inode number. Removing the base object while a stream is open detaches the stream from the namespace. Since NTFS has no orphan-stream list, a crash can leave the stream data unreachable on disk. Expose named streams as regular file descriptors for the file operations requested by Wine, including read, write, fsync, fdatasync, sync_file_range, file locking, mmap, futimes, fstat, ftruncate, SEEK_DATA, SEEK_HOLE, fallocate, and pathname unlink. Stream timestamps are shared with the base file. fstat reports the base metadata and inode number with the stream's own size and allocation. Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
4 daysntfs: add named stream ioctls supportNamjae Jeon
NTFS supports multiple named $DATA attributes, commonly known as alternate data streams. Add NTFS-specific ioctls to list, read, write, and remove named streams through an opened base file or directory. The UAPI provides separate commands for each operation. - NTFS_IOC_STREAM_READ - NTFS_IOC_STREAM_WRITE - NTFS_IOC_STREAM_REMOVE - NTFS_IOC_LIST_STREAMS Read and write requests use struct ntfs_stream to specify the stream name, byte offset, and transfer length. Writes create the stream if it does not already exist. A write failure after creation may leave the new stream visible. It is not rolled back because another caller may already have opened it. Userspace can remove it with NTFS_IOC_STREAM_REMOVE. The list command returns variable-length entries containing each named stream's data size, allocated size, and name. If the buffer is too small, the command returns -ENOSPC without copying entries. bytes_returned reports the required buffer size, and stream_count reports the number of streams. Stream names use the mounted filesystem NLS by default. Set NTFS_STREAM_FL_UTF16 to pass or receive raw UTF-16LE names. In NLS mode, names that cannot be represented by the mounted character set are omitted from the list. Use UTF-16 mode for lossless enumeration. Read and write transfers are limited to 16 MiB per request. Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
9 daysntfs: propagate EA truncate errorsNamjae Jeon
Propagate errors from truncating an EA stream so callers do not report a successful xattr update after a failed resize. Fixes: fc053f05ca28 ("ntfs: add reparse and ea operations") Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
9 daysntfs: clear stale pagecache on initialized-size extensionNamjae Jeon
iomap_zero_range() skips holes, so EOF-polluted cache data can survive gap zeroing. Discard the uninitialized cache range before advancing initialized_size so later reads cannot expose stale bytes. Perform the cleanup in ntfs_extend_initialized_size() so both file writes and shared writable mmap preparation use it. Hold the invalidate lock while reading initialized_size, clearing the cache, zeroing the gap, and updating the attribute. Release the lock on every exit path. Fixes: af0db57d4293 ("ntfs: update inode operations") Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
9 daysntfs: sync attribute inode size after resizeNamjae Jeon
Internal attribute truncation bypasses truncate_setsize(), so successful resizes must update the attribute inode's i_size. Restrict the update to attribute inodes to keep KEEP_SIZE fallocate from changing a file's i_size. Fixes: 495e90fa3348 ("ntfs: update attrib operations") Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
9 daysntfs: replace put_folio zeroing with iomap tail handlingNamjae Jeon
The NTFS put_folio callbacks clear uninitialized data around buffered writes and zero ranges. Move this work to iomap block preparation and use the default folio release path. Set IOMAP_F_ZERO_TAIL only for mapped writes and zero ranges starting at or beyond initialized_size. Split mapped ranges at initialized_size and use iomap_truncate_page() to clear the boundary block's uninitialized tail when a mapping includes initialized bytes in that block. This preserves valid data when a short copy retries with a smaller length inside a cached iomap. Apply this to buffered write mappings and the read mappings reused by iomap_zero_range(). Record initialized_size in the validity cookie and revalidate it after locking each folio. Skip bytes initialized since the tail range was selected to preserve data written through a concurrent shared mapping. Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
9 daysntfs: validate and reserve bad clusters at mountNamjae Jeon
The cluster allocator only consults $Bitmap and never loads $BadClus:$Bad. A free bitmap bit for a known bad cluster can therefore place that cluster in the free-space cache and make it available for new file allocations. Load the named nonresident $Bad stream and map every attribute extent before validating full-volume coverage and identity mappings for bad runs. Reserve only free bits in $Bitmap on writable mounts and flush the corrections before the initial free-space scan. Keep bad-cluster counts diagnostic so reservations are accounted for exactly once. Force read-only mounts with repair advice when $Bad cannot be loaded or validated, and prevent read-write remounts after such errors. Leave the bitmap unchanged on read-only mounts and fail mounts on memory allocation failure. Suppress errors=panic while loading $BadClus so damaged metadata can fall back to read-only. Reviewed-by: Hyunchul Lee <hyc.lee@gmail.com> Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
10 daysntfs: only require compressed size on first extentHyunchul Lee
The attribute validator required the compressed_size field for every sparse or compressed extent. Continuation extents omit that field, so their valid mapping_pairs_offset of 64 was rejected and compressed reads failed. Fixes: ffc755828de1 ("ntfs: Fix min_len for compressed/sparse attributes in ntfs_non_resident_attr_value_is_valid()") Signed-off-by: Hyunchul Lee <hyc.lee@gmail.com> Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
10 daysntfs: fix kmap_local leak in ntfs_check_logfile()Karl Mehltretter
ntfs_check_logfile() calls kmap_local_folio() on every iteration of its search loop but kunmap_local() only once per folio. The loop visits positions 0, 512, 1024, 2048, 4096 and so on. With 4 KiB pages the first four are in one page, so a mount whose first LogFile folio is in highmem leaves three entries on the kmap_local stack of the mounting task. Kernels without HIGHMEM are not affected. The leaked entries keep migration disabled. nr_pinned of the runqueue the mount ran on stays raised after the task exits, so taking that CPU offline blocks forever in balance_hotplug_wait(). The fifth mount in one task reaches KM_MAX_IDX (12 leaked entries plus 4 in the loop) and hits the BUG_ON() in kmap_local_idx_push(): kernel BUG at mm/highmem.c:480! On x86-32 the generic entry code also warns on return to user space: DEBUG_LOCKS_WARN_ON(current->kmap_ctrl.idx) WARNING: include/linux/highmem-internal.h:17 at __do_fast_syscall_32+0x21e/0x550 Map each page once, when it is read, and unmap that address. Fixes: 5218cd102aec ("ntfs: update misc operations") Cc: stable@vger.kernel.org Assisted-by: LLM Signed-off-by: Karl Mehltretter <kmehltretter@gmail.com> Reviewed-by: Hyunchul Lee <hyc.lee@gmail.com> Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
10 daysntfs: fix kunmap_local() of advanced pointers in check_mft_mirror()Karl Mehltretter
check_mft_mirror() compares the mft records of a page with the mirror records by advancing kmft and kmirr by the record size for every record. It then passes the advanced pointers to kunmap_local(). After the last record of a page they point one page past the mapping. With HIGHMEM kunmap_local() warns that the address does not match the top of the kmap_local stack. What it does next depends on how the architecture finds the page table entry: - 32-bit ARM looks the entry up by that address. It clears the entry of the neighbouring fixmap slot and leaves the real mapping in place. The next kmap_local() on that CPU that reaches the slot hits the BUG_ON(!pte_none()) in __kmap_local_pfn_prot(). - x86-32 takes the entry from the stack index, so the wrong address does not select a wrong entry. In QEMU the mount carried on after the warning. With 4 KiB pages and 1 KiB mft records the four mirror records fill exactly one page, so every mount whose $MFT and $MFTMirr folios are in highmem hits it. A fresh mkntfs volume on 32-bit ARM (multi_v7_defconfig, 256 MiB of highmem) fails on the first mount: WARNING: mm/highmem.c:623 at kunmap_local_indexed+0x254/0x26c, CPU#1: mount/85 kunmap_local_indexed from ntfs_fill_super+0x224c/0x35a0 ntfs_fill_super from get_tree_bdev_flags+0x1d8/0x2a4 ntfs: volume version 3.1, dev loop0, cluster size 4096 kernel BUG at mm/highmem.c:565! PC is at __kmap_local_pfn_prot+0x210/0x214 __kmap_local_page_prot from ntfs_check_logfile+0x1fc/0x1210 Found with syzkaller. Kernels without HIGHMEM do not show it. kmap_local_folio() returns the direct map address there and kunmap_local() has no mapping to remove. Keep the addresses returned by kmap_local_folio() and unmap with those. Fixes: 6251f0b0de7d ("ntfs: update super block operations") Cc: stable@vger.kernel.org Assisted-by: LLM Signed-off-by: Karl Mehltretter <kmehltretter@gmail.com> Reviewed-by: Hyunchul Lee <hyc.lee@gmail.com> Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
10 daysntfs: drain in-flight DIO before buffered write fallbackJiale Yao
An asynchronous direct write can remain in flight after the inode lock is released. If another direct write falls back to buffered I/O while the first write is still pending, iomap_file_buffered_write() can dirty pages before the first write completes its post-I/O page cache invalidation. The invalidation then finds dirty pages, reports a page cache invalidation failure, and records -EIO in the mapping error sequence. A later fsync() therefore returns -EIO. Commit 15cdefd0c0522f9d5e12d947fa04f4c11649b699 ("ext4: drain in-flight DIO before buffered write fallback") fixed the same race in ext4. NTFS has an equivalent fallback after iomap_dio_rw() returns -ENOTBLK or a short write, but does not drain other in-flight DIO before dirtying the page cache. Wait for in-flight DIO before calling iomap_file_buffered_write() in the fallback path. Since NTFS supports IOCB_NOWAIT, do not enter the blocking fallback for such requests. Return -EAGAIN if no bytes were written, or preserve the positive short-write result if the direct write made partial progress. A reproducer using concurrent AIO direct writes and buffered fallback triggered the following warning and made a subsequent fsync() return -EIO: Page cache invalidation failure on direct I/O. Possible data corruption due to collision with buffered I/O! Fixes: 9c87959601e8 ("ntfs: update file operations") Link: https://lore.kernel.org/r/20260629113827.4074335-3-libaokun@linux.alibaba.com Reviewed-by: Baolin Liu <liubaolin@kylinos.cn> Reviewed-by: Hyunchul Lee <hyc.lee@gmail.com> Signed-off-by: Jiale Yao <yaojiale02@163.com> Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
11 daysntfs: do not give a contiguous cluster allocation a second runMatthias Goergens
ntfs_attr_map_cluster() asks ntfs_cluster_alloc() for contiguous clusters and merges the whole result into the runlist, but reports only its first run, so its callers zero only that run. Within a bitmap page the allocator keeps such a request to one run, because a cluster in use ends it. The zone search can also move on without testing the clusters after the run, though: it skips a bitmap page that has no free clusters, it goes from the end of the zone to the start of pass 2, and once every zone has been searched it shrinks the MFT zone by half and goes on at its new end. After each of these jumps the first cluster tested is still taken as the next one of the run, and if it is free it starts a second run elsewhere on the volume. fallocate() over a hole then leaves the clusters of that second run holding whatever they held before: on a nearly full volume, 16 of 20 fallocated clusters read back a deleted file's data, in each of the three cases. Delayed allocation gets a second run without punching any holes: filling the same volume with write() reaches the last case when the MFT zone is shrunk. The allocator then takes only the first run off the dirty-cluster count, and after the fill statfs() reported no free space while 939 clusters were still free, until the next mount. Before setting the bit of a cluster for a contiguous request, check that the cluster comes right after the run, and return the run as it is if not; no bit has to be cleared. ntfs_attr_map_cluster() already handles a short run, which a cluster in use produces in the same way: its callers ask again for the rest. Fixes: 11ccc9107dc4 ("ntfs: update runlist handling and cluster allocator") Suggested-by: Namjae Jeon <linkinjeon@kernel.org> Link: https://lore.kernel.org/all/CAKYAXd-agPFvDj2-HvjP3SLtG7AxkqUeNPqYyrLt-BN7fT8w6g@mail.gmail.com/ Signed-off-by: Matthias Goergens <matthias.goergens@gmail.com> Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
11 daysntfs: do not refuse fallocate() when the MFT zone is emptyMatthias Goergens
ntfs_fallocate() fails with -ENOSPC whenever the MFT zone has shrunk to nothing. The cluster allocator gives the MFT zone up when it cannot find free clusters elsewhere, and nothing restores it before the next mount, so after one such failure every fallocate() on the volume fails with ENOSPC, punching holes included, however much space is freed. The allocator failure fixed by the previous patch was enough to get there: afterwards a one-cluster fallocate() failed with ENOSPC while 123 MiB of the 128 MiB volume were free. write() and truncate() do not look at the MFT zone, and ntfs_allocate_range() already fails with -ENOSPC when too few clusters are free. Remove the check. Fixes: 9c87959601e8 ("ntfs: update file operations") Signed-off-by: Matthias Goergens <matthias.goergens@gmail.com> Reviewed-by: Baolin Liu <liubaolin@kylinos.cn> Reviewed-by: Hyunchul Lee <hyc.lee@gmail.com> Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
11 daysntfs: restart the zone search when the allocation hint failsMatthias Goergens
When ntfs_cluster_alloc() is given a start_lcn, it first tries the clusters from there on. If that does not satisfy the request, it moves on to the zone's current position, but keeps the pass, the zone_end and the has_guess state it had. That loses free clusters in two ways. If the search had already moved on to pass 2, whose range ends at the hint, it scans only from the zone position to the hint and misses every free cluster between the start of the zone and the zone position. If no cluster had been tested yet, has_guess is still set, so the cluster at the zone position is tried as if it had been the hint, and when that cluster is in use the rest of the bitmap buffer is skipped. Both happen when the hint lies at or past the end of the volume, which ntfs_attr_map_cluster() produces when it extrapolates from the last allocated run across a hole. The allocator then finds nothing, shrinks the MFT zone to nothing trying to satisfy the request, and fails with -ENOSPC. To reproduce on a 128 MiB volume with 4 KiB clusters, extend two files in turn by one cluster at a time, alternating between fallocate(), write() past EOF and truncate() up, so that the runs of each file are separated by holes. When the volume is full, truncate both files back to 1 MiB and start again. During the second round a one-cluster fallocate() fails with ENOSPC while 40 MiB is free. Start the zone over as if no hint had been given. A contiguous request can get there with the run from the hint already allocated, when that run reached the end of a bitmap buffer or of the zone. Return that run instead, as when a cluster in use ends it; going on at the zone position gives the request a second run that its caller does not expect. That could happen before this patch too, whenever the cluster at the zone position was free. ntfs_attr_map_cluster() reports and zeroes only the first run, so after a fallocate() of 20 clusters over a hole, with the hint 4 clusters before the end of the volume, 16 of them read back the data of a deleted file. Fixes: 11ccc9107dc4 ("ntfs: update runlist handling and cluster allocator") Suggested-by: Baolin Liu <liubaolin@kylinos.cn> Signed-off-by: Matthias Goergens <matthias.goergens@gmail.com> Reviewed-by: Hyunchul Lee <hyc.lee@gmail.com> Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
11 daysntfs: set the attribute list size before reserving space for itMatthias Goergens
ntfs_attrlist_update_locked() resizes the attribute list and then, for $MFT, tries to reserve the maximum list size. When that allocation fails with -ENOSPC it falls back to ntfs_attrlist_repack(), which reads the list through ntfs_inode_attr_pread(). The read stops at i_size, but i_size is only updated after the reserve, so when the list has just grown the repack gets a short read and returns -EIO. Only -ENOSPC from the reserve is ignored, so the -EIO fails the $MFT extension and with it the file creation that needed a new mft record. On a 32 MiB volume with 512-byte clusters whose $MFT has a non-resident attribute list, 591 of 1500 creations of empty files succeed and the rest fail with EIO while 742 KiB is still free: ntfs: (device vda): ntfs_attrlist_update_locked(): Failed to reserve attribute list space ntfs: Failed add attr entry to attrlist ntfs: MP update failed ntfs: (device vda): ntfs_mft_record_alloc(): Failed to extend mft data allocation. Update i_size as soon as the list has been resized. File creation then goes on until the volume is full and fails with ENOSPC. Fixes: b1d732e62a5b ("ntfs: repack $MFT/$ATTRIBUTE LIST") Signed-off-by: Matthias Goergens <matthias.goergens@gmail.com> Reviewed-by: Baolin Liu <liubaolin@kylinos.cn> Reviewed-by: Hyunchul Lee <hyc.lee@gmail.com> Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
13 daysntfs: balance the $MFT runlist lock in data extension error pathsMatthias Goergens
ntfs_mft_data_extend_allocation_nolock() drops the $MFT runlist lock before allocating clusters, and every path into undo_alloc arrives without it, except a map_mft_record() failure, which takes it first. undo_alloc never releases it, so the lock leaks and the next $MFT extension and $MFT writeback hang. The restore_undo_alloc failure path, on the other hand, releases the lock without holding it. And undo_alloc frees the clusters with ntfs_cluster_free() and truncates the runlist without the lock, although both require it. Enter undo_alloc without the lock on every path. Merge the new clusters with ntfs_runlists_merge_keep_src() and keep the runlist that ntfs_cluster_alloc() returned until the extension is complete. On failure, undo_alloc truncates $MFT's runlist under the lock and, after dropping it, frees the clusters from the kept runlist with ntfs_cluster_free_from_rl(). That keeps lcnbmp_lock outside the runlist lock, the order given by the comment before the cluster allocation. Unlike ntfs_cluster_free() on a volume mounted with -o discard, this does not issue discards for the clusters, which were never written; the runlist merge failure path already frees them the same way. If the truncation fails, the runlist may still reference the clusters, so they stay allocated and the volume is marked for chkdsk; at this call site the truncation only shrinks the runlist and cannot fail today. A shorter fix would keep calling ntfs_cluster_free() on the live runlist without the lock, relying on $MFT's runlist being fully mapped and changed only by this function under mrec_lock. That breaks ntfs_cluster_free()'s documented locking rule on an argument about the rest of the driver, so this patch does not do that. Fixes: 115380f9a2f9 ("ntfs: update mft operations") Cc: stable@vger.kernel.org Signed-off-by: Matthias Goergens <matthias.goergens@gmail.com> Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
13 daysntfs: add a runlist merge that leaves the source runlist to the callerMatthias Goergens
ntfs_runlists_merge() frees the source runlist once its runs are in the destination. A caller that may have to undo the merge then has to work out its runs again from the merged runlist, where the first and the last of them may have been coalesced with neighbouring runs. Add ntfs_runlists_merge_keep_src(), which neither modifies nor frees the source runlist. For that, ntfs_rl_append() and ntfs_rl_replace() now merge the right-hand end into the copied run in the destination instead of into the last source run before copying it, and the merge helpers take the source as const, so the compiler checks that none of them writes to it. The merged runlist is the same as before. For every caller, a source runlist without any mapped run, which the merge already warns about, now fails with -EINVAL instead of carrying on with nothing to insert: with no run copied, the right-hand merge into the destination would index before the insertion point. The next patch uses ntfs_runlists_merge_keep_src() to free exactly the clusters it allocated when it has to undo an $MFT data extension. Suggested-by: Hyunchul Lee <hyc.lee@gmail.com> Cc: stable@vger.kernel.org # needed by the next patch Signed-off-by: Matthias Goergens <matthias.goergens@gmail.com> Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
13 daysntfs: do not use a stale runlist pointer when undoing $MFT extensionMatthias Goergens
When ntfs_mft_data_extend_allocation_nolock() fails after it has rebuilt the mapping pairs of the last $MFT data extent, undo_alloc truncates the runlist and then rebuilds the old mapping pairs from rl2, a pointer into the runlist taken before the truncation. ntfs_rl_truncate_nolock() can reallocate the runlist, and KASAN then reports a use-after-free in ntfs_mapping_pairs_build(). Pass the start of the runlist instead, and hold the runlist lock for reading while the mapping pairs are built from it. ntfs_mapping_pairs_build() skips to the element containing the first vcn by itself. Fixes: 1e9ea7e04472 ("Revert "fs: Remove NTFS classic"") Cc: stable@vger.kernel.org Reviewed-by: Baolin Liu <liubaolin@kylinos.cn> Signed-off-by: Matthias Goergens <matthias.goergens@gmail.com> Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
13 daysntfs: hold the runlist lock while expanding a non-resident attributeMatthias Goergens
ntfs_non_resident_attr_expand() changes ni->runlist.rl while it allocates clusters: ntfs_attr_map_whole_runlist(), the compressed branch's ntfs_rl_realloc() and ntfs_runlists_merge() can each kvfree() the old array. Unless the caller already holds ni->runlist.lock, none of this happens under it; only the rollback takes it. Callers without it include the write path, fallocate, ntfs_inode_attr_pwrite() and ntfs_resident_attr_resize(). Those callers hold mrec_lock, and the write path also inode_lock, but the iomap read side takes neither: FIEMAP, page faults, readahead and splice read reach ntfs_read_iomap_begin_non_resident(), which takes only ni->runlist.lock. A FIEMAP running while write(2) extends the same file can walk the array that ntfs_runlists_merge() has just freed: BUG: KASAN: slab-use-after-free in ntfs_attr_vcn_to_rl+0x19b/0x200 A reader that finds a vcn unmapped in the stale array also maps it from disk into the runlist the writer is changing, which shows up as "Run lists overlap. Cannot merge!". Take ni->runlist.lock for writing around the part of the expansion that changes the runlist and allocated_size, up to and including the mapping pairs update, as ntfs_non_resident_attr_shrink() does since commit 91709ba5d6d7 ("ntfs: protect runlist updates with the runlist lock"), and hold it throughout the rollback, where ntfs_cluster_free() requires it. The lock nests inside mrec_lock, as on the truncate-up path, which already calls this function with the runlist lock held. Where the caller says it holds the lock (locked_ni == ni), assert that it holds it for writing. For an $ATTRIBUTE_LIST whose lock the caller does not hold, drop the lock again before the mapping pairs update: that update can resize the same list through ntfs_attrlist_update_locked(), which returns -ENOSPC when told that the list's lock is held. Holding it there made punching holes into a large fragmented file fail with -ENOSPC. Fixes: 495e90fa3348 ("ntfs: update attrib operations") Cc: stable@vger.kernel.org Signed-off-by: Matthias Goergens <matthias.goergens@gmail.com> Reviewed-by: Baolin Liu <liubaolin@kylinos.cn> Reviewed-by: Hyunchul Lee <hyc.lee@gmail.com> Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
2026-09-30ntfs: reject non-resident attributes whose sizes exceed their allocationMatthias Goergens
ntfs_read_locked_inode(), ntfs_read_locked_attr_inode() and ntfs_read_locked_index_inode() take a non-resident attribute's data_size and initialized_size from disk without checking them against its allocated_size. The runlist ends at allocated_size and the read path maps what lies beyond it as a hole, so a data_size larger than the allocation makes reads return zeros that are not on disk, with no error. On a crafted volume with 4 KiB clusters where a 6144000-byte file claims a data_size and initialized_size 64 KiB beyond its allocation, reading the file returns 16 clusters of such zeros. A sparse file behaves the same. A compressed file instead fails with -EIO after a burst of "Still have pages left!" errors from ntfs_read_compressed_block(). Require 0 <= initialized_size <= data_size <= allocated_size, with allocated_size a multiple of the cluster size, when the inode is read, as fs/ntfs3 does in mi_enum_attr(), and fail with -EIO otherwise, which marks the inode bad and the volume as having errors. initialized_size above data_size is rejected too because an extending write zeroes the gap it opens only from initialized_size onwards. An unaligned allocated_size ends inside a cluster that the read path treats as past the end: a crafted 66440-byte file with 4 KiB clusters reads its last 904 bytes as zeros. Sparse and compressed attributes need no exception. Every non-resident attribute has a cluster-aligned allocated_size >= data_size, holes included, on eight public test volumes (seven written by Windows, with LZNT1-compressed files, a sparse $UsnJrnl:$J and a OneDrive placeholder whose unnamed $DATA is one hole, and one by mkntfs), on a volume written by ntfs-3g and on one written by this driver, and the patched driver reads every file on them as before. fs/ntfs3 has enforced the same checks since v6.6, also without exceptions. The layout.h comment saying that data_size can exceed allocated_size for compressed and sparse attributes is corrected. This also covers a non-resident attribute list, which load_attribute_list() reads through ntfs_attr_iget(), and $MFT, whose inode goes through ntfs_read_locked_inode() after the previous patch's check. $MFT's own non-resident attribute list goes through neither: ntfs_read_inode_mount() loads it with load_attribute_list_mount() and ntfs_read_locked_inode() skips it for $MFT, so the check is added there too. The check was already missing in the classic driver; the Fixes tag names the commit that brought that code back. Fixes: 1e9ea7e04472 ("Revert "fs: Remove NTFS classic"") Signed-off-by: Matthias Goergens <matthias.goergens@gmail.com> Reviewed-by: Hyunchul Lee <hyc.lee@gmail.com> Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
2026-09-30ntfs: fail the mount when $MFT's data size exceeds its allocationMatthias Goergens
ntfs_read_inode_mount() takes $MFT's i_size from data_size without checking it against allocated_size, unlike the resident case in ntfs_read_locked_inode(). mft records between the two are not on disk. On a crafted volume with 512-byte clusters whose $MFT is cut to 4 clusters while data_size still says 27 records, reading the folio holding records 0-3 finds the end of the runlist at vcn 4. An unpatched kernel hangs there on the folio lock, as in "ntfs: fail the mount when $MFT needs its own extent records". With the previous patch vcn 4-7 are past the allocation and so read as a hole: records 2 and 3 come back as zeros and the mount carries on until check_mft_mirror() finds the zeroed record 2. Refuse such a $MFT before anything is read through it. The mount now fails with: ntfs: (device vda): ntfs_read_inode_mount(): $MFT data size 27648 exceeds its allocated size 2048. $MFT is corrupt. Run chkdsk. fs/ntfs3 rejects any non-resident attribute whose data_size exceeds its allocated size. This patch checks only $MFT; the next one covers the other non-resident attributes. Fixes: b041ca562526 ("ntfs: update iomap and address space operations") Cc: stable@vger.kernel.org Signed-off-by: Matthias Goergens <matthias.goergens@gmail.com> Reviewed-by: Hyunchul Lee <hyc.lee@gmail.com> Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
2026-09-30ntfs: do not map a vcn as a hole when its runlist lookup failedMatthias Goergens
ntfs_attr_vcn_to_rl() retries ntfs_map_runlist_nolock() for any lcn up to LCN_RL_NOT_MAPPED, which includes LCN_ENOENT, but turns a failed retry into an error only for LCN_RL_NOT_MAPPED. For LCN_ENOENT the error is dropped and the read path maps the range as a hole. An LCN_ENOENT below allocated_size comes from a base extent with a highest_vcn of 0, which ntfs_mapping_pairs_decompress() takes to map the whole attribute, so the runlist ends after its last mapping pair. If the pairs end early, the retry finds the same extent and fails with -ENOENT. On a crafted volume with 4 KiB clusters, a 64-cluster file whose mapping pairs stop after 16 clusters reads 48 clusters of zeros, with no error. The same layout gets a crafted $MFT past the check from "ntfs: fail the mount when $MFT needs its own extent records". With 512-byte clusters and $MFT's mapping pairs ending at vcn 4, an unpatched kernel hangs on the folio lock reading records 0-3. With the check alone, the -EIO is dropped, records 2 and 3 read as zeros and the mount carries on until check_mft_mirror() finds the zeroed record 2. Fail the lookup whenever the retry leaves @vcn unmapped below allocated_size, -ENOENT included. At or beyond allocated_size the end of the runlist is still returned as it is. A failed expansion in ntfs_non_resident_attr_expand() or ntfs_attrlist_repack() truncates the runlist under the runlist lock but restores allocated_size only after dropping it. A lookup in between would now fail, so restore allocated_size before dropping the lock in both, under size_lock, which ntfs_attr_vcn_to_rl() takes to read it. The crafted file now fails from vcn 16 on with -EIO, and the crafted volume fails to mount with the check's message. Fixes: 495e90fa3348 ("ntfs: update attrib operations") Cc: stable@vger.kernel.org Signed-off-by: Matthias Goergens <matthias.goergens@gmail.com> Reviewed-by: Hyunchul Lee <hyc.lee@gmail.com> Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
2026-09-30ntfs: fail the mount when $MFT needs its own extent recordsMatthias Goergens
Mounting a crafted image hangs the mount process forever, unkillable at 0% CPU. Only the hung-task detector reports it: INFO: task mount:74 blocked in I/O wait for more than 30 seconds. folio_wait_bit_common <- waits forever filemap_read_folio map_mft_record_folio map_mft_record ntfs_map_runlist_nolock ntfs_attr_vcn_to_rl __ntfs_read_iomap_begin iomap_read_folio ntfs_read_folio <- already holds that folio's lock ntfs_read_inode_mount ntfs_fill_super ntfs_read_inode_mount() assembles $MFT's runlist one $DATA extent at a time, hoping, as its comment says, that it never needs a part of $MFT it has not decoded yet. If $MFT's attribute list puts one of its attributes or later $DATA extents in an extent record outside the runlist decoded so far, reading that record re-enters ntfs_map_runlist_nolock() for $MFT. Depending on the layout, that waits on the folio lock it already holds, as above, blocks on $MFT's runlist lock, or dereferences NULL in map_extent_mft_record(). Mark the volume for the whole $DATA enumeration and have ntfs_map_runlist_nolock() refuse $MFT with -EIO while the mark is set. The enumeration decodes each extent itself with ntfs_mapping_pairs_decompress(), so it does not need that path. A validly placed record can trip the check too. The driver puts an extent record for $MFT's own mapping pairs before the first vcn it describes, but with clusters smaller than a page, the folio holding it can still run past the decoded runlist. An unpatched kernel hangs on that layout as well; with the check the mount fails. The check then refuses only the folio's tail, so this patch needs "ntfs: do not map an unmappable runlist fragment as a hole": without it the tail is zero-filled and the mount serves an all-zero mft record. The mount now fails instead of hanging: ntfs: (device vda): ntfs_map_runlist_nolock(): $MFT needs its own extent records to describe itself; cannot mount. Tested under qemu with KASAN, PROVE_LOCKING and the hung-task detector on ntfs-next, with and without the whole series: six crafted images that hang or crash an unpatched kernel fail to mount with the series, and nine images that mount without it still mount, three of them with the same file listing and contents, among them a volume written by ntfs-3g whose $MFT has its $DATA in three extent records. Fixes: b041ca562526 ("ntfs: update iomap and address space operations") Suggested-by: Hyunchul Lee <hyc.lee@gmail.com> Cc: stable@vger.kernel.org Signed-off-by: Matthias Goergens <matthias.goergens@gmail.com> Reviewed-by: Hyunchul Lee <hyc.lee@gmail.com> Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
2026-09-30ntfs: do not turn an unmappable runlist fragment into delalloc on writeMatthias Goergens
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 <matthias.goergens@gmail.com> Reviewed-by: Hyunchul Lee <hyc.lee@gmail.com> Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
2026-09-30ntfs: do not map an unmappable runlist fragment as a holeMatthias Goergens
When the extent mft record holding part of a file's runlist cannot be read, ntfs_attr_vcn_to_rl() ignores the failed ntfs_map_runlist_nolock() retry and returns with *lcn == LCN_RL_NOT_MAPPED. The iomap read path only rejects lcn < LCN_ENOENT, so it maps the range as a hole and read() returns zeros with no error. The first read already does this: ntfs_attr_map_whole_runlist() keeps the fragments it could read, readahead drops its error, and the next lookup finds the unmapped tail. On a file whose runlist is split between its base record (vcn 0-214) and one extent record (vcn 215-1499), breaking only the extent record's FILE magic makes the kernel log "Failed to map extent mft record", yet read() returns 1285 clusters of zeros for vcn 215-1499. A damaged attribute list entry, which makes the retry fail with -ENOENT, gives the same zeros. Return -ENOMEM if the retry ran out of memory and -EIO otherwise. Both callers already handle an ERR_PTR; other negative lcns, including LCN_ENOENT, are returned as before, and so is LCN_RL_NOT_MAPPED at or beyond allocated_size, where nothing is mapped: the runlist ends there with LCN_RL_NOT_MAPPED when only the last extent is mapped, as after a write into it, and with clusters smaller than a page every read of a file's last folio looks up such vcns. Reads of vcn 215-1499 now fail with -EIO. An intact volume exercised with buffered, mmap and O_DIRECT I/O, fallocate, truncate, sparse and compressed files behaves as before. Fixes: 495e90fa3348 ("ntfs: update attrib operations") Cc: stable@vger.kernel.org Signed-off-by: Matthias Goergens <matthias.goergens@gmail.com> Reviewed-by: Hyunchul Lee <hyc.lee@gmail.com> Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
2026-09-29ntfs: accept standard sparse compression unitHyunchul Lee
Windows native sparse-file inspection shows that a non-resident $DATA attribute can be marked sparse and record the standard compression unit (4) even when the file is not compressed. Fixes: af0db57d4293 ("ntfs: update inode operations") Reviewed-by: Baolin Liu <liubaolin@kylinos.cn> Signed-off-by: Hyunchul Lee <hyc.lee@gmail.com> Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
2026-09-29ntfs: avoid misclassifying directory reparse pointsHyunchul Lee
Windows directory symlinks and junctions can carry FILE_ATTR_DUP_FILE_NAME_INDEX_PRESENT in their entry while the reparse flag is stored only in. The old directory enumeration path treated such entries as DT_DIR, allowing no-follow userspace scanners to recurse through symlink targets. Fixes: 0a8ac0c1fa0b ("ntfs: update directory operations") Signed-off-by: Hyunchul Lee <hyc.lee@gmail.com> Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
2026-09-25ntfs: account for direct MFT bitmap cluster allocationXuewen Wang
The MFT bitmap extension fast path sets $Bitmap directly, bypassing ntfs_cluster_alloc() and its free_clusters and lcn_empty_bits_per_page updates. Successful extensions overreport free space, and rollback adds back counts that were never deducted. Update both counts under lcnbmp_lock and the bitmap folio lock. Require a nonzero page count and space remaining after delayed-allocation reservations; otherwise use the regular allocator. The page check also prevents underflow when the initial scan recorded zero after a read error. Fixes: 6251f0b0de7d ("ntfs: update super block operations") Suggested-by: Baolin Liu <liubaolin@kylinos.cn> Reviewed-by: Baolin Liu <liubaolin@kylinos.cn> Signed-off-by: Xuewen Wang <wangxuewen@kylinos.cn> Reviewed-by: Baolin Liu <liubaolin@kylinos.cn> Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
2026-09-25ntfs: wait for the bitmap scan before MFT bitmap extensionXuewen Wang
If MFT bitmap extension fails before the initial $Bitmap scan completes, rollback calls ntfs_bitmap_clear_bit(), which holds a folio lock while ntfs_set_lcn_empty_bits() waits for the scan. The scanner deadlocks if it still needs that folio. Wait for the initial scan before taking the runlist or bitmap folio locks and attempting allocation. Fixes: 11ccc9107dc4 ("ntfs: update runlist handling and cluster allocator") Reviewed-by: Baolin Liu <liubaolin@kylinos.cn> Signed-off-by: Xuewen Wang <wangxuewen@kylinos.cn> Reviewed-by: Baolin Liu <liubaolin@kylinos.cn> Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>