summaryrefslogtreecommitdiff
path: root/fs/exfat
AgeCommit message (Collapse)Author
2026-08-22Merge tag 'exfat-for-7.3-rc1' of ↵Linus Torvalds
git://git.kernel.org/pub/scm/linux/kernel/git/linkinjeon/exfat Pull exfat updates from Namjae Jeon: "A set of exfat fixes covering shared writable mappings, entry error handling, allocation cleanup, FITRIM bounds, and locking: - Fix valid_size extension over shared writable mappings by lazily zeroing page-cache gaps and ensuring racing stores cannot be overwritten or extend beyond valid_size - Clean up partially written directory entries on add-entry failure and safely free new directory clusters - Free a newly allocated directory cluster when zeroing it fails - Write the destination entry before removing the source entry during rename, preserving the source if the destination write fails - Keep FITRIM within the requested range, even when the free-space search wraps around the allocation bitmap - Fix truncated FS_IOC_GETFSLABEL results by passing the destination buffer size in bytes to the UTF-16-to-NLS conversion - Fix overflow in the cluster-to-dentry calculation, which could cause readdir to stop early on large volumes - Replace the per-inode truncate_lock with inode_lock, using shared locking in bmap to serialize against concurrent truncation - Update the exfat mailing list address in MAINTAINERS" * tag 'exfat-for-7.3-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/linkinjeon/exfat: exfat: replace truncate_lock with inode_lock exfat: keep FITRIM within the requested range exfat: fix truncated volume labels returned by FS_IOC_GETFSLABEL exfat: fix overflow in cluster-to-dentry conversion exfat: clean up new entry on add entry failure exfat: free new directory cluster if zeroing fails exfat: write moved entry before removing source MAINTAINERS: update mailing list address for exfat exfat: fix valid_size extension over a shared writable mapping
2026-08-17Merge tag 'vfs-7.3-rc1.lookup' of ↵Linus Torvalds
git://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs Pull vfs lookup updates from Christian Brauner: "This refactors lookup_open() and adds vfs_lookup_open() for nfsd. mnt_want_write() and parent locking are moved into lookup_open() itself. audit_inode_child() is also now called in lookup_open() on failure. That is the calling convention in vfs_create() and vfs_mkdir(), but lookup_open() made no such call when atomic_open() should have created a file and did not. And neither did the regular ->create() path fwiw. This also contains work to remove the unneeded excl argument from the ->create() inode op" * tag 'vfs-7.3-rc1.lookup' of git://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs: fs/namei.c: fix coding style in atomic_open() and lookup_open() fs/namei.c: fix kerneldoc of atomic_open() and vfs_lookup_open() fs/namei.c: update stale comments in lookup_open() Remove excl arg to ->create inode_operation fs/namei.c: update kerneldoc of atomic_open() vfs: call audit_inode_child() in lookup_open() on failure vfs: move create error && negative dentry case in lookup_open() up VFS: add vfs_lookup_open() for nfsd VFS: move delegated_inode retry loop into lookup_open() VFS: move mnt_want_write() and locking into lookup_open()
2026-08-17Merge tag 'vfs-7.3-rc1.iomap' of ↵Linus Torvalds
git://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs Pull iomap updates from Christian Brauner: "The bulk of this is the conversion of iomap to a single ->iomap_next() callback and thus finishing the move to an iterator model. Every iomap operation drove its iteration through a struct iomap_ops holding ->iomap_begin() and ->iomap_end(). iomap_iter() only ever sees those as pointers. That means every step of every iteration is an indirect call. This collapses both into one ->iomap_next() callback that finishes the previous mapping and produces the next one. This lets callers inline the iteration loop and pass its ->iomap_next() as a compile time constant. That means the compiler can turn it into a direct and hence inlineable call. This also allows future callers to express custom logic to drive the iteration forward better. xfs, btrfs, ext4, ext2, erofs, f2fs, gfs2, hpfs, fuse, exfat, zonefs, ntfs, ntfs3 and the block device mapping are all converted. No functional changes are intended. This also adds a simple direct I/O path for small reads. On Gen5 NVMe the __iomap_dio_rw() dominates 4K random reads. The same single-core io_uring poll mode workload reaches ~3.2M IOPS against the raw block device but only ~1.92M through ext4 or XFS. __iomap_dio_rw(), iomap_iter(), iomap_dio_bio_iter() and kfree() were at the top of the profile. The new path is very lightweight if no special behavior is requested. The bio comes from a dedicated bioset and laid out so the whole request is a single cacheline aligned allocation. Completion runs inline. That takes ext4 from 1.92M to 2.19M IOPS in the original workload. fio shows around: - 4% at libaio queue depths of 64 and up - around 5% for io_uring - up to 10% for io_uring poll mode at depth 256 on both ext4 and xfs. A few other patches: - iomap_folio_mark_uptodate() lets a filesystem that writes into the page cache outside the iomap read and write paths keep iomap's internal uptodate bitmap in sync, which fuse needs for server-pushed notify stores before it can enable large folios; - two fixes for iomap_bio_read_folio_range_sync(): a potential crash when device integrity behavior is changed and a missing bio_uninit(). - a folio batch release fix on iomap callback failures - FGP_NOFS is dropped from iomap_get_folio() - documentation fix" * tag 'vfs-7.3-rc1.iomap' of git://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs: (29 commits) iomap: iomap_bio_read_folio_range_sync is missing a call to bio_uninit iomap: don't free integrity payload that doesn't exist docs: fix grammatical error in iomap docs exfat: convert iomap ops to ->iomap_next() fuse: convert iomap ops to ->iomap_next() hpfs: convert iomap ops to ->iomap_next() gfs2: convert iomap ops to ->iomap_next() f2fs: convert iomap ops to ->iomap_next() block: convert iomap ops to ->iomap_next() ext2: convert iomap ops to ->iomap_next() zonefs: convert iomap ops to ->iomap_next() erofs: convert iomap ops to ->iomap_next() ext4: convert iomap ops to ->iomap_next() ntfs: convert iomap ops to ->iomap_next() ntfs3: convert iomap ops to ->iomap_next() btrfs: convert iomap ops to ->iomap_next() xfs: convert iomap ops to ->iomap_next() iomap: add ->iomap_next() iomap: use GFP_NOWAIT when application for iomap_dio_simple allocations iomap: decouple simple direct I/O reads from iomap_dio_rw ...
2026-08-17exfat: replace truncate_lock with inode_lockChi Zhiling
Remove the per-inode truncate_lock and rely on inode_lock instead. exfat_setattr() truncates under inode_lock (held exclusively by the VFS callers), and exfat_aop_bmap() now takes inode_lock shared to exclude a concurrent truncate, providing the same mutual exclusion with a single lock. Signed-off-by: Chi Zhiling <chizhiling@kylinos.cn> Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
2026-08-14exfat: keep FITRIM within the requested rangeYang Wen
exfat_find_free_bitmap() searches the entire allocation bitmap and may wrap around to its beginning. exfat_trim_fs() does not verify that the returned cluster is still within the requested FITRIM range. As a result, a partial FITRIM operation may discard free clusters outside the user-specified range and report a trimmed length larger than the requested length. Validate each returned cluster against the requested range and stop the search when it wraps around or passes the range end. Signed-off-by: Yang Wen <anmuxixixi@gmail.com> Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
2026-08-11exfat: fix truncated volume labels returned by FS_IOC_GETFSLABELYang Wen
exfat_ioctl_get_volume_label() passes uniname.name_len to exfat_utf16_to_nls() as the output buffer length. However, name_len is the number of UTF-16 code units, while exfat_utf16_to_nls() expects the buffer size in bytes. As a result, volume labels that expand during charset conversion are truncated.The destination buffer is FSLABEL_MAX bytes long, so pass its actual size to the conversion helper. Signed-off-by: Yang Wen <anmuxixixi@gmail.com> Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
2026-08-06exfat: fix overflow in cluster-to-dentry conversionYang Wen
The cluster-to-dentry calculation is performed as u32 and may overflow and wrap around on large volumes. This can result in an incorrect max_dentries value, causing readdir to stop early and omit directory entries. Cast nr_clusters to u64 before shifting to avoid the overflow. Signed-off-by: Yang Wen <anmuxixixi@gmail.com> Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
2026-07-31exfat: convert iomap ops to ->iomap_next()Joanne Koong
Convert exfat iomap_ops to the new ->iomap_next() callback. Each callback is generated with the DEFINE_IOMAP_ITER_NEXT() and DEFINE_IOMAP_ITER_NEXT_END() macros, which wrap the iomap_iter_next() helper to finish the previous mapping if needed and produce the next one. No functional changes are intended. Acked-by: Namjae Jeon <linkinjeon@kernel.org> Signed-off-by: Joanne Koong <joannelkoong@gmail.com> Link: https://patch.msgid.link/20260729192737.3190206-20-joannelkoong@gmail.com Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
2026-07-31Remove excl arg to ->create inode_operationNeilBrown
The only time that 'false' is passed as the 'excl' arg to the ->create inode_operation is in lookup_open() when ->atomic_open is not provided by the parent directory. *all* directory inode_operations which do not have ->atomic_open completely ignore the 'excl' arg. Therefore we don't need the 'excl' arg. Those few ->create operations which pay attention to the arg are only ever called with a value of 'true'. We remove that arg and change all ->create operations to behave as those thhe arg were 'true'. Signed-off-by: NeilBrown <neil@brown.name> Link: https://patch.msgid.link/178290671516.27465.15984496764174914338@noble.neil.brown.name Reviewed-by: Jori Koolstra <jkoolstra@xs4all.nl> Reviewed-by: Jan Kara <jack@suse.cz> Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
2026-07-29exfat: clean up new entry on add entry failureYichong Chen
exfat_add_entry() initializes a new directory entry set before writing it with exfat_put_dentry_set(). If the write fails, mkdir/create returns an error but a partially written entry may be left behind. For non-zero-size directories, the failure also happens after a cluster has been allocated for the new directory. Clean up the new entry best-effort, and free the newly allocated directory cluster only when the cleanup writeback succeeds. This avoids freeing a cluster that may still be referenced by an on-disk entry if the cleanup fails. Signed-off-by: Yichong Chen <chenyichong@uniontech.com> Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
2026-07-28exfat: free new directory cluster if zeroing failsYichong Chen
exfat_alloc_new_dir() allocates a cluster for a new directory before zeroing it. If exfat_zeroed_cluster() fails, the function returns the error but leaves the allocated cluster in use. Free the newly allocated cluster before returning the zeroing error. Signed-off-by: Yichong Chen <chenyichong@uniontech.com> Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
2026-07-27exfat: write moved entry before removing sourceYichong Chen
exfat_move_file() removes the old directory entry before the new entry has been written. If writing the new entry fails, rename returns an error after the source entry has already been marked deleted. Write the new entry first, then remove the old entry and update the cached inode location. This keeps the source entry intact if creating the destination entry fails. Signed-off-by: Yichong Chen <chenyichong@uniontech.com> Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
2026-07-25exfat: fix valid_size extension over a shared writable mappingNamjae Jeon
When a shared writable mapping has its valid_size extended by a buffered write or a page fault, exfat zeroes the page-cache gap below the new valid_size. A store through the mapping can race with this zeroing and be overwritten. Fix this by zeroing the gap lazily. Drop ->map_pages so that every first write fault goes through exfat_page_mkwrite(), which advances valid_size to cover the faulting page. With fault-around enabled, a store could install a writable PTE, skip ->page_mkwrite(), and land past valid_size without advancing it. Extending valid_size one faulting page at a time also leaves never-written pages in a large mapping alone. The gap is filled with block granularity, zeroing only the not-uptodate blocks and preserving blocks that may hold data stored through the mapping. On the buffered-write path the invalidate lock is held and the gap is unmapped before zeroing, so a racing store re-faults and, under the inode lock, completes only after the gap has been zeroed and valid_size covers it. Fixes: 82a81a7352bc ("exfat: add iomap buffered I/O support") Co-developed-by: Yuezhang Mo <Yuezhang.Mo@sony.com> Signed-off-by: Yuezhang Mo <Yuezhang.Mo@sony.com> Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
2026-07-01iomap: consolidate bio submissionChristoph Hellwig
Add a iomap_bio_submit_read_endio helper factored out of iomap_bio_submit_read to that all ->submit_read implementations for iomap_read_ops that use iomap_bio_read_folio_range can shared the logic. Right now that logic is mostly trivial, but already has a bug for XFS because the XFS version is too trivial: file system integrity validation needs a workqueue context and thus can't happen from the default iomap bi_end_io I/O handler. Unfortunately the iomap refactoring just before fs integrity landed moved code around here and the call go misplaced, meaning it never got called. The PI information still is verified by the block layer, but the offloading is less efficient (and the future userspace interface can't get at it). Fixes: 0b10a370529c ("iomap: support T10 protection information") Cc: stable@vger.kernel.org # v7.1 Signed-off-by: Christoph Hellwig <hch@lst.de> Link: https://patch.msgid.link/20260629121750.3392300-2-hch@lst.de Acked-by: Namjae Jeon <linkinjeon@kernel.org> Reviewed-by: "Darrick J. Wong" <djwong@kernel.org> Reviewed-by: Joanne Koong <joannelkoong@gmail.com> Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
2026-06-15exfat: bound uniname advance in exfat_find_dir_entry()Bryam Vargas
In exfat_find_dir_entry(), each TYPE_EXTEND (file name) entry advances the output pointer by a fixed amount while the loop guard only tracks the accumulated name length: if (++order == 2) uniname = p_uniname->name; else uniname += EXFAT_FILE_NAME_LEN; len = exfat_extract_uni_name(ep, entry_uniname); name_len += len; unichar = *(uniname+len); *(uniname+len) = 0x0; uniname grows by EXFAT_FILE_NAME_LEN (15) per name entry, but name_len grows only by the actual extracted length, which is shorter when a name fragment contains an early NUL. The only guard is `name_len >= MAX_NAME_LENGTH`, so a crafted directory with many short name fragments lets uniname run far past the p_uniname->name[MAX_NAME_LENGTH + 3] buffer while name_len stays small, causing an out-of-bounds read and write at *(uniname+len). The sibling extractor exfat_get_uniname_from_ext_entry() already stops on a short fragment (the lockstep `len != EXFAT_FILE_NAME_LEN` guard added in commit d42334578eba ("exfat: check if filename entries exceeds max filename length")); exfat_find_dir_entry() never got the equivalent. Track the per-entry write offset as a count and reject a fragment once the offset, or the offset plus the extracted length, would exceed MAX_NAME_LENGTH, before forming the output pointer. Fixes: ca06197382bd ("exfat: add directory operations") Cc: stable@vger.kernel.org Suggested-by: Namjae Jeon <linkinjeon@kernel.org> Signed-off-by: Bryam Vargas <hexlabsecurity@proton.me> Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
2026-06-15exfat: add swap_activate supportJan Polensky
Commit 07d67f3e9083 ("exfat: add iomap buffered I/O support") converted exfat buffered I/O to iomap, but did not add a .swap_activate handler to the address_space_operations. swapon(2) on an exfat swapfile then fails with EINVAL, which causes LTP swap tests to fail. Add exfat_iomap_swap_activate() and hook it into exfat_aops so exfat uses iomap_swapfile_activate() for swapfile activation. Fixes: 614f71ca1bdf ("exfat: add iomap buffered I/O support") Closes: https://lore.kernel.org/all/20260603110212.3020276-1-japo@linux.ibm.com/ Signed-off-by: Jan Polensky <japo@linux.ibm.com> Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
2026-06-15exfat: preserve benign secondary entries during rename and moveRochan Avlur
Commit 8258ef28001a ("exfat: handle unreconized benign secondary entries") added cluster freeing for benign secondary entries inside exfat_remove_entries(). However, exfat_remove_entries() is also called from the rename and move paths (exfat_rename_file and exfat_move_file), where the old entry set is being relocated rather than deleted. This causes benign secondary entries such as vendor extension entries to be silently destroyed on rename or cross-directory move, violating the exFAT spec requirement (section 8.2) that implementations preserve unrecognized benign secondary entries. Fix this by adding a free_benign parameter to exfat_remove_entries() so callers can suppress cluster freeing during relocation, and extending exfat_init_ext_entry() to copy trailing benign secondary entries from the old entry set into the new one internally. Also clean up the error paths to delete newly allocated entries on failure. Fixes: 8258ef28001a ("exfat: handle unreconized benign secondary entries") Cc: stable@vger.kernel.org Link: https://lore.kernel.org/linux-fsdevel/CAG7tbBV--waov7XVu2FHQEc6paR92dufS=em9DW5Kzsrpu3iQg@mail.gmail.com/ Signed-off-by: Rochan Avlur <rochan.avlur@gmail.com> Reviewed-by: Yuezhang Mo <Yuezhang.Mo@sony.com> Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
2026-06-15exfat: serialize truncate against in-flight DIONamjae Jeon
exfat_setattr() did not call inode_dio_wait() before performing a size change, leaving a window where a concurrent in-flight DIO write could be operating on clusters that the truncate is about to free. Add inode_dio_wait() before the truncate_setsize()/exfat_truncate() sequence so that any in-flight DIO completes before cluster freeing begins. Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
2026-06-15exfat: add support for SEEK_HOLE and SEEK_DATA in llseekNamjae Jeon
Adds exfat_file_llseek() that implements these whence values via the iomap layer (iomap_seek_hole() and iomap_seek_data()) using the existing exfat_read_iomap_ops. Unlike many other modern filesystems, exFAT does not support sparse files with unallocated clusters (holes). In exFAT, clusters are always fully allocated once they are written or preallocated. In addition, exFAT maintains a separate "Valid Data Length" (valid_size) that is distinct from the logical file size. This affects how holes are reported during seeking. In exfat_iomap_begin(), ranges where the offset is greater than or equal to ei->valid_size are mapped as IOMAP_UNWRITTEN, while ranges below valid_size are mapped as IOMAP_MAPPED. This mapping behavior is used by the iomap seek functions to correctly report SEEK_HOLE and SEEK_DATA positions. - Ranges with offset >= ei->valid_size are mapped as IOMAP_HOLE. - Ranges with offset < ei->valid_size are mapped as IOMAP_MAPPED. Reviewed-by: Christoph Hellwig <hch@lst.de> Acked-by: Christoph Hellwig <hch@lst.de> Acked-by: "Darrick J. Wong" <djwong@kernel.org> Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
2026-06-15exfat: add iomap direct I/O supportNamjae Jeon
Add iomap-based direct I/O support to the exfat filesystem. This replaces the previous exfat_direct_IO() implementation that used blockdev_direct_IO() with iomap_dio_rw() interface. Reviewed-by: Christoph Hellwig <hch@lst.de> Acked-by: Christoph Hellwig <hch@lst.de> Acked-by: "Darrick J. Wong" <djwong@kernel.org> Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
2026-06-15exfat: add iomap buffered I/O supportNamjae Jeon
Add full buffered I/O support using the iomap framework to the exfat filesystem. This will replaces the old exfat_get_block(), exfat_write_begin(), exfat_write_end(), and exfat_block_truncate_page() with their iomap equivalents. Buffered writes now use iomap_file_buffered_write(), read uses iomap_bio_read_folio() and iomap_bio_readahead(), and writeback is handled through iomap_writepages(). Acked-by: Christoph Hellwig <hch@lst.de> Acked-by: "Darrick J. Wong" <djwong@kernel.org> Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
2026-06-15exfat: fix implicit declaration of brelse()Namjae Jeon
exfat_cluster_walk() calls brelse(bh) without including the header that declares the function, causing the following build error: fs/exfat/exfat_fs.h:542:9: error: implicit declaration of function ‘brelse’ [-Werror=implicit-function-declaration] Fix this by adding the missing buffer_head.h in exfat_fs.h. Acked-by: Christoph Hellwig <hch@lst.de> Acked-by: "Darrick J. Wong" <djwong@kernel.org> Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
2026-06-15exfat: add data_start_bytes and exfat_cluster_to_phys_bytes() helperNamjae Jeon
This caches the data area start offset in bytes (data_start_bytes) and introduces a helper function exfat_cluster_to_phys_bytes() to compute the physical byte position of a given cluster. Acked-by: Christoph Hellwig <hch@lst.de> Acked-by: "Darrick J. Wong" <djwong@kernel.org> Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
2026-06-15exfat: add support for multi-cluster allocationNamjae Jeon
Currently exfat_map_cluster() allocates and returns only one cluster at a time even when more clusters are needed. This causes multiple FAT walks and repeated allocation calls during large sequential writes or when using iomap for writes. This change exfat_map_cluster() and exfat_alloc_cluster() to be able to allocate multiple contiguous clusters. Acked-by: Christoph Hellwig <hch@lst.de> Acked-by: "Darrick J. Wong" <djwong@kernel.org> Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
2026-06-15exfat: add exfat_file_open()Namjae Jeon
Add exfat_file_open() to handle file open operation for exFAT. This change is a preparation step before introducing iomap-based direct IO support. Acked-by: Christoph Hellwig <hch@lst.de> Acked-by: "Darrick J. Wong" <djwong@kernel.org> Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
2026-06-15exfat: add balloc parameter to exfat_map_cluster() for iomap supportNamjae Jeon
In preparation for supporting the iomap infrastructure, we need to know whether a new cluster was allocated or not in exfat_map_cluster(). Add an optional 'bool *balloc' output parameter. When a new cluster is allocated, *balloc is set to true. Pass NULL from exfat_get_block() to preserve the existing behavior. Acked-by: Christoph Hellwig <hch@lst.de> Acked-by: "Darrick J. Wong" <djwong@kernel.org> Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
2026-06-15exfat: replace unsafe macros with static inline functionsNamjae Jeon
The current exFAT driver relies on various macros for unit conversions between clusters, blocks, sectors, and directory entries. These macros are structurally unsafe as they lack type enforcement and are prone to potential integer overflows during bit-shift operations, especially on 64-bit architectures. Replace all arithmetic macros with static inline functions to provide strict type checking and explicit casting. Acked-by: Christoph Hellwig <hch@lst.de> Acked-by: "Darrick J. Wong" <djwong@kernel.org> Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
2026-06-15exfat: simplify exfat_lookup()Al Viro
1) d_splice_alias() handles ERR_PTR() for inode just fine 2) no need to even look for existing aliases in case of directory inodes; just punt to d_splice_alias(), it'll do the right thing 3) no need to bother with 'd_unhashed(alias)' case - d_find_alias() would've returned that only in case of a directory, and d_splice_alias() will handle that just fine on its own. 4) exfat_d_anon_disconn() is entirely pointless now - we only get to evaluating it in case dentry->d_parent == alias->d_parent and alias being a non-directory. But in that case IS_ROOT(alias) can't possibly be true - that would've reqiured alias == alias->d_parent, i.e alias == dentry->d_parent and dentry->d_parent is guaranteed to be a directory. So exfat_d_anon_disconn() would always return false when it's called, which makes && !exfat_d_anon_disconn(alias) a no-op. Signed-off-by: Al Viro <viro@zeniv.linux.org.uk> Reviewed-by: Sungjong Seo <sj1557.seo@samsung.com> Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
2026-06-15exfat: fix potential use-after-free in exfat_find_dir_entry()Michael Bommarito
In exfat_find_dir_entry(), the buffer_head obtained from exfat_get_dentry() is released with brelse(bh) before the fall-through TYPE_EXTEND branch reads the directory entry through ep (which points into bh->b_data): brelse(bh); if (entry_type == TYPE_EXTEND) { ... len = exfat_extract_uni_name(ep, entry_uniname); ... } After brelse() drops our reference, nothing guarantees that the underlying page backing bh->b_data remains valid for the subsequent exfat_extract_uni_name() read. This is the same pattern fixed in commit fc961522ddbd ("exfat: Fix potential use after free in exfat_load_upcase_table()"). Move brelse(bh) so it runs after ep is no longer dereferenced on each branch. Confirmed on QEMU x86_64 with CONFIG_KASAN=y + CONFIG_DEBUG_PAGEALLOC=y + CONFIG_PAGE_POISONING=y on linux-next, using a crafted exFAT image (long filename with same-hash collisions forcing the TYPE_EXTEND path). With a debug-only invalidate_bdev() inserted between brelse(bh) and the ep read to make the stale-deref window deterministic, the unpatched kernel faults: BUG: KASAN: use-after-free in exfat_find_dir_entry+0x133b/0x15a0 BUG: unable to handle page fault for address: ffff88801a5fa0c2 Oops: 0000 [#1] SMP DEBUG_PAGEALLOC KASAN NOPTI RIP: 0010:exfat_find_dir_entry+0x1188/0x15a0 With this patch applied, the same instrumented harness completes cleanly under the same sanitizer stack. I have not reproduced a crash on an uninstrumented kernel under ordinary reclaim; the instrumented A/B establishes the lifetime violation and that the patch closes it, not an unaided triggerability claim. Fixes: ca06197382bd ("exfat: add directory operations") Cc: stable@vger.kernel.org Assisted-by: Claude:claude-opus-4-7 Signed-off-by: Michael Bommarito <michael.bommarito@gmail.com> Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
2026-06-15exfat: fix handling of damaged volume in exfat_create_upcase_table()David Timber
When the size of the upcase table is set to zero in the dentry for any reason(e.g. corrupted media or misbehaving device), an integer overflow causes the module to loop indefinitely. If the size of the upcase table is read zero, do not attempt to load the table. Instead, fallback to loading the default upcase table. If the size of the upcase table is zero or no upcase table is found, raise exfat_fs_error() to mark the volume read-only. Signed-off-by: David Timber <dxdt@dev.snart.me> Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
2026-05-11exfat: Implement fileattr_get for case sensitivityChuck Lever
Report exFAT's case sensitivity behavior via the FS_XFLAG_CASEFOLD flag. exFAT compares names through the volume's upcase table; in practice that table folds case, and case is preserved at rest. Acked-by: Namjae Jeon <linkinjeon@kernel.org> Reviewed-by: Roland Mainz <roland.mainz@nrubsig.org> Signed-off-by: Chuck Lever <chuck.lever@oracle.com> Link: https://github.com/exfatprogs/exfatprogs/issues/313 Link: https://patch.msgid.link/20260507-case-sensitivity-v14-4-e62cc8200435@oracle.com Signed-off-by: Christian Brauner <brauner@kernel.org>
2026-04-13Merge tag 'exfat-for-7.1-rc1' of ↵Linus Torvalds
git://git.kernel.org/pub/scm/linux/kernel/git/linkinjeon/exfat Pull exfat updates from Namjae Jeon: - Implement FALLOC_FL_ALLOCATE_RANGE to add support for preallocating clusters without zeroing, helping to reduce file fragmentation - Add a unified block readahead helper for FAT chain conversion, bitmap allocation, and directory entry lookups - Optimize exfat_chain_cont_cluster() by caching buffer heads to minimize mark_buffer_dirty() and mirroring overhead during NO_FAT_CHAIN to FAT_CHAIN conversion - Switch to truncate_inode_pages_final() in evict_inode() to prevent BUG_ON caused by shadow entries during reclaim - Fix a 32-bit truncation bug in directory entry calculations by ensuring proper bitwise coercion - Fix sb->s_maxbytes calculation to correctly reflect the maximum possible volume size for a given cluster size, resolving xfstests generic/213 - Introduced exfat_cluster_walk() helper to traverse FAT chains by a specified step, handling both ALLOC_NO_FAT_CHAIN and ALLOC_FAT_CHAIN modes - Introduced exfat_chain_advance() helper to advance an exfat_chain structure, updating both the current cluster and remaining size - Remove dead assignments and fix Smatch warnings * tag 'exfat-for-7.1-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/linkinjeon/exfat: exfat: use exfat_chain_advance helper exfat: introduce exfat_chain_advance helper exfat: remove NULL cache pointer case in exfat_ent_get exfat: use exfat_cluster_walk helper exfat: introduce exfat_cluster_walk helper exfat: fix incorrect directory checksum after rename to shorter name exfat: fix s_maxbytes exfat: fix passing zero to ERR_PTR() in exfat_mkdir() exfat: fix error handling for FAT table operations exfat: optimize exfat_chain_cont_cluster with cached buffer heads exfat: drop redundant sec parameter from exfat_mirror_bh exfat: use readahead helper in exfat_get_dentry exfat: use readahead helper in exfat_allocate_bitmap exfat: add block readahead in exfat_chain_cont_cluster exfat: add fallocate FALLOC_FL_ALLOCATE_RANGE support exfat: Fix bitwise operation having different size exfat: Drop dead assignment of num_clusters exfat: use truncate_inode_pages_final() at evict_inode()
2026-04-03exfat: use exfat_chain_advance helperChi Zhiling
Replace open-coded cluster chain walking logic with exfat_chain_advance() across exfat_readdir, exfat_find_dir_entry, exfat_count_dir_entries, exfat_search_empty_slot and exfat_check_dir_empty. Signed-off-by: Chi Zhiling <chizhiling@kylinos.cn> Reviewed-by: Sungjong Seo <sj1557.seo@samsung.com> Reviewed-by: Yuezhang Mo <Yuezhang.Mo@sony.com> Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
2026-04-03exfat: introduce exfat_chain_advance helperChi Zhiling
Introduce exfat_chain_advance() to walk a exfat_chain structure by a given step, updating both ->dir and ->size fields atomically. This helper handles both ALLOC_NO_FAT_CHAIN and ALLOC_FAT_CHAIN modes with proper boundary checking. Suggested-by: Yuezhang Mo <Yuezhang.Mo@sony.com> Signed-off-by: Chi Zhiling <chizhiling@kylinos.cn> Reviewed-by: Sungjong Seo <sj1557.seo@samsung.com> Reviewed-by: Yuezhang Mo <Yuezhang.Mo@sony.com> Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
2026-04-03exfat: remove NULL cache pointer case in exfat_ent_getChi Zhiling
Since exfat_get_next_cluster has been updated, no callers pass a NULL pointer to exfat_ent_get, so remove the handling logic for this case. Signed-off-by: Chi Zhiling <chizhiling@kylinos.cn> Reviewed-by: Sungjong Seo <sj1557.seo@samsung.com> Reviewed-by: Yuezhang Mo <Yuezhang.Mo@sony.com> Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
2026-04-03exfat: use exfat_cluster_walk helperChi Zhiling
Replace the custom exfat_walk_fat_chain() function and open-coded FAT chain walking logic with the exfat_cluster_walk() helper across exfat_find_location, __exfat_get_dentry_set, and exfat_map_cluster. Suggested-by: Sungjong Seo <sj1557.seo@samsung.com> Signed-off-by: Chi Zhiling <chizhiling@kylinos.cn> Reviewed-by: Sungjong Seo <sj1557.seo@samsung.com> Reviewed-by: Yuezhang Mo <Yuezhang.Mo@sony.com> Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
2026-04-03exfat: introduce exfat_cluster_walk helperChi Zhiling
Introduce exfat_cluster_walk() to walk the FAT chain by a given step, handling both ALLOC_NO_FAT_CHAIN and ALLOC_FAT_CHAIN modes. Also redefine exfat_get_next_cluster as a thin wrapper around it for backward compatibility. Signed-off-by: Chi Zhiling <chizhiling@kylinos.cn> Reviewed-by: Sungjong Seo <sj1557.seo@samsung.com> Reviewed-by: Yuezhang Mo <Yuezhang.Mo@sony.com> Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
2026-04-03exfat: fix incorrect directory checksum after rename to shorter nameChi Zhiling
When renaming a file in-place to a shorter name, exfat_remove_entries marks excess entries as DELETED, but es->num_entries is not updated accordingly. As a result, exfat_update_dir_chksum iterates over the deleted entries and computes an incorrect checksum. This does not lead to persistent corruption because mark_inode_dirty() is called afterward, and __exfat_write_inode later recomputes the checksum using the correct num_entries value. Fix by setting es->num_entries = num_entries in exfat_init_ext_entry. Signed-off-by: Chi Zhiling <chizhiling@kylinos.cn> Reviewed-by: Sungjong Seo <sj1557.seo@samsung.com> Reviewed-by: Yuezhang Mo <Yuezhang.Mo@sony.com> Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
2026-03-31exfat: fix s_maxbytesDavid Timber
With fallocate support, xfstest unit generic/213 fails with QA output created by 213 We should get: fallocate: No space left on device Strangely, xfs_io sometimes says "Success" when something went wrong -fallocate: No space left on device +fallocate: File too large because sb->s_maxbytes is set to the volume size. To be in line with other non-extent-based filesystems, set to max volume size possible with the cluster size of the volume. Signed-off-by: David Timber <dxdt@dev.snart.me> Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
2026-03-26fs: Rename generic_file_fsync() to simple_fsync()Jan Kara
The implementation is now really basic so rename generic_file_fsync() simple_fsync() and __generic_file_fsync() to simple_fsync_noflush(). Signed-off-by: Jan Kara <jack@suse.cz> Link: https://patch.msgid.link/20260326095354.16340-56-jack@suse.cz Signed-off-by: Christian Brauner <brauner@kernel.org>
2026-03-26exfat: Drop pointless invalidate_inode_buffers() callJan Kara
EXFAT never calls mark_buffer_dirty_inode() and thus invalidate_inode_buffers() never has anything to evict. Drop the pointless call. Signed-off-by: Jan Kara <jack@suse.cz> Link: https://patch.msgid.link/20260326095354.16340-49-jack@suse.cz Signed-off-by: Christian Brauner <brauner@kernel.org>
2026-03-26exfat: fix passing zero to ERR_PTR() in exfat_mkdir()Yang Wen
Detected by Smatch. namei.c:890 exfat_mkdir() warn: passing zero to 'ERR_PTR' Signed-off-by: Yang Wen <anmuxixixi@gmail.com> Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
2026-03-05exfat: fix error handling for FAT table operationsChi Zhiling
Fix three error handling issues in FAT table operations: 1. Fix exfat_update_bh() to properly return errors from sync_dirty_buffer 2. Fix exfat_end_bh() to properly return errors from exfat_update_bh() and exfat_mirror_bh() 3. Fix ignored return values from exfat_chain_cont_cluster() in inode.c and namei.c These fixes ensure that FAT table write errors are properly propagated to the caller instead of being silently ignored. Signed-off-by: Chi Zhiling <chizhiling@kylinos.cn> Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
2026-03-05exfat: optimize exfat_chain_cont_cluster with cached buffer headsChi Zhiling
When converting files from NO_FAT_CHAIN to FAT_CHAIN format, profiling reveals significant time spent in mark_buffer_dirty() and exfat_mirror_bh() operations. This overhead occurs because each FAT entry modification triggers a full block dirty marking and mirroring operation. For consecutive clusters that reside in the same block, optimize by caching the buffer head and performing dirty marking only once at the end of the block's modifications. Performance improvements for converting a 30GB file: | Cluster Size | Before Patch | After Patch | Speedup | |--------------|--------------|-------------|---------| | 512 bytes | 4.243s | 1.866s | 2.27x | | 4KB | 0.863s | 0.236s | 3.66x | | 32KB | 0.069s | 0.034s | 2.03x | | 256KB | 0.012s | 0.006s | 2.00x | Signed-off-by: Chi Zhiling <chizhiling@kylinos.cn> Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
2026-03-05exfat: drop redundant sec parameter from exfat_mirror_bhChi Zhiling
The sector offset can be obtained from bh->b_blocknr, so drop the redundant sec parameter from exfat_mirror_bh(). Also clean up the function to use exfat_update_bh() helper. No functional changes. Signed-off-by: Chi Zhiling <chizhiling@kylinos.cn> Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
2026-03-05exfat: use readahead helper in exfat_get_dentryChi Zhiling
Replace the custom exfat_dir_readahead() function with the unified exfat_blk_readahead() helper in exfat_get_dentry(). This removes the duplicate readahead implementation and uses the common interface, also reducing code complexity. Signed-off-by: Chi Zhiling <chizhiling@kylinos.cn> Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
2026-03-05exfat: use readahead helper in exfat_allocate_bitmapChi Zhiling
Use the newly added exfat_blk_readahead() helper in exfat_allocate_bitmap() to simplify the code. This eliminates the duplicate inline readahead logic and uses the unified readahead interface. Signed-off-by: Chi Zhiling <chizhiling@kylinos.cn> Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
2026-03-05exfat: add block readahead in exfat_chain_cont_clusterChi Zhiling
When a file cannot allocate contiguous clusters, exfat converts the file from NO_FAT_CHAIN to FAT_CHAIN format. For large files, this conversion process can take a significant amount of time. Add simple readahead to read all the FAT blocks in advance, as these blocks are consecutive, significantly improving the conversion performance. Test in an empty exfat filesystem: dd if=/dev/zero of=/mnt/file bs=1M count=30k dd if=/dev/zero of=/mnt/file2 bs=1M count=1 time cat /mnt/file2 >> /mnt/file | cluster size | before patch | after patch | | ------------ | ------------ | ----------- | | 512 | 47.667s | 4.316s | | 4k | 6.436s | 0.541s | | 32k | 0.758s | 0.071s | | 256k | 0.117s | 0.011s | Signed-off-by: Chi Zhiling <chizhiling@kylinos.cn> Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
2026-03-04exfat: add fallocate FALLOC_FL_ALLOCATE_RANGE supportDavid Timber
Currently, the Linux (ex)FAT drivers do not employ any cluster allocation strategy to keep fragmentation at bay. As a result, when multiple processes are competing for new clusters to expand files in exfat filesystem on Linux simultaneously, the files end up heavily fragmented. HDDs are most impacted, but this could also have some negative impact on various forms of flash memory depending on the type of underlying technology. For instance, modern digital cameras produce multiple media files for a single video stream. If the application does not take the fragmentation issue into account or the system is under memory pressure, the kernel end up allocating clusters in said files in a interleaved manner. Demo script: for (( i = 0; i < 4; i += 1 )); do dd if=/dev/urandom iflag=fullblock bs=1M count=64 of=frag-$i & done for (( i = 0; i < 4; i += 1 )); do wait done filefrag frag-* Result - Linux kernel native exfat, async mount: 780 extents found 740 extents found 809 extents found 712 extents found Result - Linux kernel native exfat, sync mount: 1852 extents found 1836 extents found 1846 extents found 1881 extents found Result - Windows XP: 3 extents found 3 extents found 3 extents found 2 extents found Windows kernel, on the other hand, regardless of the underlying storage interface or the medium, seems to space out clusters for each file. Similar strategy has to be employed by Linux fat filesystems for efficient utilisation of storage backend. In the meantime, userspace applications like rsync may use fallocate to combat this issue. This patch may introduce a regression-like behaviour to some niche filesystem-agnostic applications that use fallocate and proceed to non-sequentially write to the file. Examples: - libtorrent's use of posix_fallocate() and the first fragment from a peer is near the end of the file - "Download accelerators" that do partial content requests(HTTP 206) in multiple threads writing to the same file The delay incurred in such use cases is documented in WinAPI. Patches that add the ioctl equivalents to the WinAPI function SetFileValidData() and `fsutil file queryvaliddata ...` will follow. Signed-off-by: David Timber <dxdt@dev.snart.me> Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
2026-03-04exfat: Fix bitwise operation having different sizePhilipp Hahn
cpos has type loff_t (long long), while s_blocksize has type u32. The inversion wil happen on u32, the coercion to s64 happens afterwards and will do 0-left-paddding, resulting in the upper bits getting masked out. Cast s_blocksize to loff_t before negating it. Found by static code analysis using Klocwork. Signed-off-by: Philipp Hahn <phahn-oss@avm.de> Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>