summaryrefslogtreecommitdiff
path: root/fs/exfat
AgeCommit message (Collapse)Author
2026-10-21exfat: fix reading a directory whose FAT chain is longer than its sizeHengyu Liang
Commit 08cf4a8181b4 ("exfat: use exfat_chain_advance helper") made the directory code walk the clusters of a directory with exfat_chain_advance(). However, exfat_chain_advance() returns -EIO once it has walked as many clusters as the size of the directory says. The FAT chain of a directory can be longer than that, because some cameras stop updating the size, see commit 1e5654de0f51 ("exfat: handle wrong stream entry size in exfat_readdir()"). As of now, opening such a directory fails with EIO as soon as the chain is two clusters longer than the size. Reading a directory that has a FAT chain after a seek past its end fails with EIO too. The issue can be reproduced with an image in which the size of a directory with 200 files and 7 clusters is lowered to 2 clusters: mount -o loop exfat.img /mnt ls /mnt/d | wc -l Before commit 08cf4a8181b4 ("exfat: use exfat_chain_advance helper"), the result is: 200 After that commit, ls fails with "Input/output error" and the result is: 0 This patch will make exfat_chain_advance() follow the FAT to its end when the directory has a FAT chain, as the code did before, and keep the size check for a directory without one. Fixes: 08cf4a8181b4 ("exfat: use exfat_chain_advance helper") Cc: stable@vger.kernel.org Signed-off-by: Hengyu Liang <hengyul@cs.unc.edu> Reviewed-by: Chi Zhiling <chizhiling@kylinos.cn> Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
2026-10-21exfat: free clusters beyond EOF when a write fails or is shortHengyu Liang
Commit 82a81a7352bc ("exfat: add iomap buffered I/O support") and commit 867b9c96dc83 ("exfat: add iomap direct I/O support") made a write allocate the clusters for its whole range in one step. They also dropped exfat_write_failed(), which freed the clusters beyond the end of the file after a failed or short write. However, a write can stop early, for example when the process is killed or when a part of the buffer cannot be read. As of now, the clusters for the part that was not written stay allocated beyond the end of the file. exfat_cont_expand() does not expect them, so a later write or truncate that extends the file fails with ENOSPC. The directory entry records only the size of the file, so the space can be lost for good once the volume is unmounted. The issue can be reproduced on a 64M volume made by mkfs.exfat and mounted on /mnt, with a write of 1M from a buffer that has only its first 4K mapped: #include <fcntl.h> #include <stdio.h> #include <sys/mman.h> #include <sys/stat.h> #include <unistd.h> int main(void) { char *p = mmap(NULL, 8192, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); int fd = open("/mnt/f", O_CREAT | O_WRONLY, 0644); struct stat st; munmap(p + 4096, 4096); printf("write: %zd\n", write(fd, p, 1 << 20)); fstat(fd, &st); printf("blocks: %ld\n", (long)st.st_blocks); printf("ftruncate: %d\n", ftruncate(fd, 1 << 16)); return 0; } Before commit 82a81a7352bc ("exfat: add iomap buffered I/O support"), the result is: write: 4096 blocks: 8 ftruncate: 0 After that commit, the result is: write: 4096 blocks: 2048 ftruncate: -1 This patch will bring back exfat_write_failed() and call it when a write ends before the end of its range, so that the clusters beyond the end of the file are freed again. It waits for direct I/O in flight first, as exfat_setattr() does before it frees clusters. Fixes: 82a81a7352bc ("exfat: add iomap buffered I/O support") Fixes: 867b9c96dc83 ("exfat: add iomap direct I/O support") Cc: stable@vger.kernel.org Assisted-by: LLM Signed-off-by: Hengyu Liang <hengyul@cs.unc.edu> Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
2026-10-21exfat: clean stale bdev buffers of newly allocated clustersNicolas Falesy
Directory entries are read and written through buffer_heads of the block device. Removing the entries of a directory dirties such a buffer. If the directory is then removed and its cluster is reused for file data before writeback, the stale buffer stays dirty in the block device mapping, and the next flush of the block device (fsync, syncfs or umount) writes it over the new file data. The file silently reads back with one sector replaced by the deleted directory entries. This was harmless while file data went through exfat_get_block(), as __block_write_begin_int() and do_direct_IO() call clean_bdev_aliases() for newly allocated blocks. Since data I/O moved to iomap, nothing drops these buffers any more. Clean the aliases in exfat_alloc_cluster(), once per contiguous run of allocated clusters. Doing it at allocation time covers every path that allocates clusters: buffered and direct writes, and also fallocate() and an extending truncate(), which allocate clusters before the data is written, so the later iomap mapping does not see them as new. Tested on the exfat dev branch in a KVM guest with a reproducer that removes the entries of a directory, removes the directory and writes a file over its freed cluster: buffered with and without fsync, O_DIRECT, after fallocate(), after an extending ftruncate(), and with the freed cluster in the middle of a fragmented allocation. Without this patch every run corrupted the file (55/55); with it, none did (0/55). A churn test that creates, removes, preallocates and truncates directories and files, then runs fsck.exfat, is clean with and without the patch. Fixes: 82a81a7352bc ("exfat: add iomap buffered I/O support") Fixes: 867b9c96dc83 ("exfat: add iomap direct I/O support") Link: https://lore.kernel.org/all/010001a1222f66ba-0c745f29-d4fe-4862-93ff-1db2055b05fc-000000@email.amazonses.com/ Suggested-by: Namjae Jeon <linkinjeon@kernel.org> Assisted-by: Claude-Opus-5.5 Signed-off-by: Nicolas Falesy <nicolas@nicolasfalesy.com> Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
2026-10-21exfat: mark straddling folio RO and zero the post-EOF rangeYuezhang Mo
When extending a file across a non-page-aligned EOF, the folio containing the old EOF must be marked RO so that subsequent mmap writes trigger ->page_mkwrite() and update valid_size. The straddling folio remains mapped to the same filesystem block after the file is extended. As a result, the existing writable PTE may remain in place, allowing mmap writes to bypass ->page_mkwrite() and leave valid_size unchanged. Furthermore, writes to the post-EOF range may occur between truncate_pagecache() and locking the folio. So the post-EOF range should be zeroed after marking the straddling folio RO, and using truncate_pagecache() to zero out the post-EOF range is redundant. Introduce exfat_pagecache_isize_extended() to mark the straddling folio RO and zero the post-EOF range after clearing writable mapping. This ensures that data written beyond the old EOF before the extension is not exposed as valid file data, and that subsequent mmap writes trigger ->page_mkwrite() to update valid_size. Fixes: 82a81a7352bc ("exfat: add iomap buffered I/O support") Cc: stable@vger.kernel.org Signed-off-by: Yuezhang Mo <Yuezhang.Mo@sony.com> Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
2026-10-21exfat: fix a large buffered write failing when the volume is nearly fullHengyu Liang
Commit 82a81a7352bc ("exfat: add iomap buffered I/O support") made a buffered write map the whole range of the write in one step, so the clusters for all of it are allocated at once. However, exfat_alloc_cluster() returns -ENOSPC when fewer clusters are free than it is asked for. As of now, a write() that is larger than the free space writes nothing and fails with ENOSPC, while it used to write as much as fits and return a short count. The issue can be reproduced with a 16M image made by mkfs.exfat that is filled except for 256K: mount -o loop /img /mnt dd if=/dev/zero of=/mnt/hole bs=256k count=1 dd if=/dev/zero of=/mnt/fill bs=4k rm /mnt/hole dd if=/dev/zero of=/mnt/f bs=1M count=1 wc -c < /mnt/f Before commit 82a81a7352bc ("exfat: add iomap buffered I/O support"), the result is: 262144 After that commit, the result is: 0 This patch will map a single cluster when the clusters for the whole range cannot be allocated, so that the write goes on until the volume is full. Direct I/O is not changed, it still fails without writing as it did before that commit. Fixes: 82a81a7352bc ("exfat: add iomap buffered I/O support") Cc: stable@vger.kernel.org Signed-off-by: Hengyu Liang <hengyul@cs.unc.edu> Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
2026-10-21exfat: zero invalid tail on partial overwritesNamjae Jeon
Buffered overwrite that ends before valid_size in the same filesystem block reads the entire block without IOMAP_F_ZERO_TAIL. If an existing cluster contains stale bytes beyond valid_size, the synchronous read loads them into the page cache and marks the block uptodate. Subsequent buffered reads can return these bytes while i_size exceeds valid_size. For example, with 512-byte blocks, valid_size of 1000 and i_size of 2048, a cold-cache overwrite of [900, 950) exposes stale data in [1000, 1024). Setting ZERO_TAIL for this write would also erase valid data in [950, 1000). Use iomap_truncate_page() to zero only the invalid tail at valid_size before preparing such buffered overwrites. Call it after dropping s_lock and before iomap locks the write folio. The inode lock held by buffered writes serializes valid_size against truncation and page_mkwrite. Skip direct I/O, fault mappings and new allocations. Writes that reach valid_size continue to use IOMAP_F_ZERO_TAIL. Fixes: 82a81a7352bc ("exfat: add iomap buffered I/O support") Cc: stable@vger.kernel.org Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
2026-10-21exfat: drain in-flight DIO before buffered writesJiale Yao
An asynchronous direct write can remain in flight after the inode lock is released. If a buffered operation dirties page cache while the direct write is still pending, the direct write's post-I/O invalidation can find the dirty pages, report a page cache invalidation failure, and record -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. ExFAT does not drain in-flight DIO before a regular buffered write, the buffered fallback after iomap_dio_rw() returns -ENOTBLK or a short write, or the page-cache operations used to extend valid_size. Wait for in-flight DIO before these paths can dirty page cache. 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: 867b9c96dc83 ("exfat: add iomap direct I/O support") Cc: stable@vger.kernel.org Link: https://lore.kernel.org/r/20260629113827.4074335-3-libaokun@linux.alibaba.com Signed-off-by: Jiale Yao <yaojiale02@163.com> Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
2026-10-21exfat: make valid_size and zeroed_size atomicChi Zhiling
ei->valid_size and ei->zeroed_size are 64-bit fields read from bio completion context (exfat_iomap_read_end_io()) and from ->iomap_begin without i_rwsem, while writers update them under i_rwsem. On 32-bit these loads can tear, and the "if (x < new) x = new" read-modify-write can lose an update or regress the value when two writers race. Convert both to atomic64_t and funnel updates through a cmpxchg-based monotonic advance helper so the value only moves forward; plain reads use exfat_get_*(), and the few shrink/init paths (truncate, inode fill, create) use exfat_set_*(). This is a prerequisite for taking exfat_page_mkwrite() out from under i_rwsem. Fixes: 82a81a7352bc ("exfat: add iomap buffered I/O support") Cc: stable@vger.kernel.org Signed-off-by: Chi Zhiling <chizhiling@kylinos.cn> Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
2026-10-21exfat: dirty all new pages when extending valid_sizeChi Zhiling
In the current code, exfat_zero_new_range() does not mark non-uptodate pages as dirty, which may cause some blocks within a page to not be written back. This commit no longer checks each block in a non-uptodate page individually. Instead, it reads the entire page back into the cache and then writes it back as a whole. This allows concurrent writes and zeroing operations to be synchronized using the folio lock. Fixes: 1135704ed22f ("exfat: fix valid_size extension over a shared writable mapping") Cc: stable@vger.kernel.org Signed-off-by: Chi Zhiling <chizhiling@kylinos.cn> Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
2026-10-21exfat: hold the invalidate lock while truncatingChi Zhiling
truncate_setsize() ends in truncate_inode_pages(), which is documented as being "Called under (and serialised by) inode->i_rwsem and mapping->invalidate_lock". Hold invalidate_lock while executing truncate_setsize() to ensure that truncate and mmap writes are mutually exclusive. This can simplify the handling in exfat_page_mkwrite(). Fixes: 82a81a7352bc ("exfat: add iomap buffered I/O support") Cc: stable@vger.kernel.org Signed-off-by: Chi Zhiling <chizhiling@kylinos.cn> Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
2026-10-21exfat: advance valid_size to EOF for append writesChi Zhiling
For an append write, the write position is recalculated by generic_write_checks(). Therefore, the write position must be obtained after generic_write_checks() returns. Using the original write position to advance valid_size may cause the valid_size update to be skipped, potentially exposing stale data. Also move the truncate_pagecache() check below the recalculated position, so that pos is never read before it is assigned. Fixes: 11a347fb6cef ("exfat: change to get file size from DataLength") Cc: stable@vger.kernel.org Signed-off-by: Chi Zhiling <chizhiling@kylinos.cn> Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
2026-10-21exfat: remove runtime integer divisionDavid Timber
Replace the arithmetic ops that compiles to the use of actual integer division with bitwise ops. As the divisors involved are all power of two, integer division is not necessary. No functional change. Signed-off-by: David Timber <dxdt@dev.snart.me> Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
2026-10-21exfat: update percent_in_use field when updating volume flagsDavid Timber
Currently, the exfat fs implementation does not modify the percent_in_use in the boot sector header at all. Although the field is only an advisory field for situations where bit counting is not feasible, not updating the field at all can cause confusion if the the value is actually exposed to the user. Calculate the value using integer arithmetic only when the dirty flag is cleared. If the dirty flag is set, set to 0xFF. It is set to 0xFF when mounting under the assumption that the number of used clusters will change - this is basically what is recommended in the SD Association's specs. When clearing the dirty flag, if overridden by sbi->vol_flags_persistent, set to 0xFF. This is intentional because if there are orphan clusters, the calculated value would be incorrect and correcting the fs error is fsck.exfat's job. Signed-off-by: David Timber <dxdt@dev.snart.me> Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
2026-10-21exfat: clear the volume dirty flag only when remounting read-onlyChi Zhiling
exfat_reconfigure() does not hold s_lock while calling sync_filesystem(). Therefore, the filesystem can become dirty again between the sync and clearing the volume dirty flag, leaving a dirty filesystem with a clean volume flag. Holding s_lock across sync_filesystem() is not an option because it can lead to a deadlock: writeback takes s_lock in exfat_write_inode(). Instead, clear the volume dirty flag only when remounting the filesystem read-only, where no writer can modify the filesystem after the sync. For a normal read-only remount, reconfigure_super() calls sb_prepare_remount_readonly() before ->reconfigure(), which returns -EBUSY if any writer is still active. Afterwards sb_start_ro_state_change() sets sb->s_readonly_remount, so mnt_get_write_access() fails with -EROFS and no new writer can start until the reconfiguration finishes. Therefore no writer can race with the sync and the clearing. Forced remounts are different: with SB_FORCE, reconfigure_super() skips sb_prepare_remount_readonly(), so the writers active at that moment are not blocked and can still dirty the filesystem after the sync. Do not clear the volume dirty flag in this case. Signed-off-by: Chi Zhiling <chizhiling@kylinos.cn> Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
2026-10-21exfat: validate vendor allocation directory entriesYang Wen
The exfat entry validator checks the type and ordering of benign secondary entries, but does not validate Vendor Allocation entry fields. In addition, exfat_find() reads only the first two entries, allowing malformed trailing Vendor Allocation entries to bypass validation. Validate the allocation flags, VendorGuid, FirstCluster, DataLength, and NoFatChain extent. Read the complete entry set during lookup so malformed Vendor Allocation entries are rejected. Signed-off-by: Yang Wen <anmuxixixi@gmail.com> Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
2026-10-21exfat: take bitmap_lock at the start of exfat_alloc_cluster()Chi Zhiling
exfat_alloc_cluster() checks sbi->used_clusters against the total number of data clusters before acquiring sbi->bitmap_lock. A concurrent allocation can update sbi->used_clusters after the check but before the lock is acquired, making the check stale. This can allow the allocation to proceed even though there are not enough free clusters, causing it to fail partway through. Acquire sbi->bitmap_lock before checking sbi->used_clusters so that the free-space check and subsequent cluster allocation are serialized with concurrent allocations. Fixes: f55c096f62f1 ("exfat: do not zero the extended part") Signed-off-by: Chi Zhiling <chizhiling@kylinos.cn> Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
2026-10-21exfat: fix used_clusters accounting during cluster allocationChi Zhiling
In the current exfat_alloc_cluster(), p_chain->size is incremented as each cluster is allocated so that the partially allocated chain can be freed by __exfat_free_cluster() on the error path. However, sbi->used_clusters is still updated only after all clusters have been allocated, while __exfat_free_cluster() unconditionally subtracts the number of freed clusters from sbi->used_clusters. Therefore, when allocation fails partway through, the partially allocated clusters are subtracted from sbi->used_clusters even though they were never added to it. This corrupts the free-space accounting and can cause sbi->used_clusters to underflow. Increment sbi->used_clusters together with p_chain->size as each cluster is allocated. This keeps the accounting consistent with __exfat_free_cluster() on both the success and error paths. This issue is caught by generic/476 with 256k cluster size: Ran: generic/476 Failures: generic/476 Failed 1 of 1 tests *** fsck.exfat output *** ERROR: <path>: more clusters are allocated. truncate to N bytes ... /dev/vdc: corrupted. directories 331, files 526 /dev/vdc: files corrupted 9, files fixed 0 *** end fsck.exfat output *** Fixes: d5c514b6a0c0 ("exfat: fix the newly allocated clusters are not freed in error handling") Signed-off-by: Chi Zhiling <chizhiling@kylinos.cn> Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
2026-10-21exfat: map allocated extents for swap activationNamjae Jeon
exFAT reports allocated ranges beyond valid_size as IOMAP_HOLE when IOMAP_REPORT is set. iomap_swapfile_activate() also uses IOMAP_REPORT while collecting physical extents for a swap file, so it treats the preallocated tail as unallocated and rejects the file with -EINVAL. Use swap-specific iomap operations that suppress IOMAP_REPORT before mapping the file. This reports physically allocated ranges as IOMAP_UNWRITTEN during swap activation without changing the byte-accurate SEEK_HOLE and SEEK_DATA behavior of the regular iomap operations. Tested-by: Petr Vorel <pvorel@suse.cz> Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
2026-10-19Merge tag 'vfs-7.4-rc1.idmap' of ↵Linus Torvalds
git://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs Pull vfs idmap constification from Christian Brauner: "This makes struct mnt_idmap const throughout the tree. A mount's idmapping is immutable. The only thing modified after alloc_mnt_idmap() is the reference count and that is hidden behind mnt_idmap_get() and mnt_idmap_put(). Everything else only reads from it. That is the same model struct cred uses and the idmapping is sensitive enough to deserve it. The conversion goes bottom up through the vfs{g,u}id and fs{g,u}id helpers, the privilege checks, acl, xattr and quota and then every inode operation that takes an idmap, so ->permission(), ->getattr(), ->setattr(), ->create(), ->mkdir(), ->mknod(), ->symlink(), ->rename(), ->tmpfile(), ->get_acl(), ->set_acl() and ->fileattr_set() with their implementations in every filesystem, plus the LSM hooks. struct vfsmount, struct mount_kattr and struct kstatmount store a const pointer and nop_mnt_idmap and invalid_mnt_idmap live in rodata, so anything that tries to modify them faults instead of silently corrupting the initial idmapping" * tag 'vfs-7.4-rc1.idmap' of git://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs: (27 commits) fs: make nop_mnt_idmap and invalid_mnt_idmap const fs: port mnt_idmap() and file_mnt_idmap() to const mnt_idmap fs: port vfs_*() helpers to const mnt_idmap fs: port ->setattr() to pass const mnt_idmap fs: port ->getattr() to pass const mnt_idmap fs: port ->create() to pass const mnt_idmap fs: port ->symlink() to pass const mnt_idmap fs: port ->mkdir() to pass const mnt_idmap fs: port ->rename() to pass const mnt_idmap fs: port ->mknod() to pass const mnt_idmap fs: port ->tmpfile() to pass const mnt_idmap fs: port ->get_acl() to pass const mnt_idmap fs: port ->set_acl() to pass const mnt_idmap fs: port ->fileattr_set() to pass const mnt_idmap fs: port xattr to const mnt_idmap fs: port ->permission() to pass const mnt_idmap fs: port acl to const mnt_idmap fs: port inode_init_owner() to const mnt_idmap fs: port inode_owner_or_capable() to const mnt_idmap fs: port privilege checking helpers to const mnt_idmap ...
2026-09-23fs: port ->setattr() to pass const mnt_idmapChristian Brauner
Convert to const struct mnt_idmap. A mount's idmapping is immutable. The only thing that is allowed to be modified afterwards is the reference count and that is hidden behind mnt_idmap_get() and mnt_idmap_put(). Everything else only ever reads from the idmapping. This is the same model that struct cred uses and the idmapping is also rather sensitive. So make the idmap argument const wherever we can. The conversion is done from the bottom up so callers can continue to pass a non-const pointer to a const parameter until the conversion is finished. No functional changes. Link: https://patch.msgid.link/20260901-work-idmap-const-v1-24-54ccd48e100b@kernel.org Acked-by: Paul Moore <paul@paul-moore.com> Reviewed-by: Jan Kara <jack@suse.cz> Reviewed-by: Seth Forshee <sforshee@kernel.org> Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
2026-09-23fs: port ->getattr() to pass const mnt_idmapChristian Brauner
Convert to const struct mnt_idmap. A mount's idmapping is immutable. The only thing that is allowed to be modified afterwards is the reference count and that is hidden behind mnt_idmap_get() and mnt_idmap_put(). Everything else only ever reads from the idmapping. This is the same model that struct cred uses and the idmapping is also rather sensitive. So make the idmap argument const wherever we can. The conversion is done from the bottom up so callers can continue to pass a non-const pointer to a const parameter until the conversion is finished. No functional changes. Link: https://patch.msgid.link/20260901-work-idmap-const-v1-23-54ccd48e100b@kernel.org Reviewed-by: Jan Kara <jack@suse.cz> Reviewed-by: Seth Forshee <sforshee@kernel.org> Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
2026-09-23fs: port ->create() to pass const mnt_idmapChristian Brauner
Convert to const struct mnt_idmap. A mount's idmapping is immutable. The only thing that is allowed to be modified afterwards is the reference count and that is hidden behind mnt_idmap_get() and mnt_idmap_put(). Everything else only ever reads from the idmapping. This is the same model that struct cred uses and the idmapping is also rather sensitive. So make the idmap argument const wherever we can. The conversion is done from the bottom up so callers can continue to pass a non-const pointer to a const parameter until the conversion is finished. No functional changes. Link: https://patch.msgid.link/20260901-work-idmap-const-v1-22-54ccd48e100b@kernel.org Reviewed-by: Jan Kara <jack@suse.cz> Reviewed-by: Seth Forshee <sforshee@kernel.org> Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
2026-09-23fs: port ->mkdir() to pass const mnt_idmapChristian Brauner
Convert to const struct mnt_idmap. A mount's idmapping is immutable. The only thing that is allowed to be modified afterwards is the reference count and that is hidden behind mnt_idmap_get() and mnt_idmap_put(). Everything else only ever reads from the idmapping. This is the same model that struct cred uses and the idmapping is also rather sensitive. So make the idmap argument const wherever we can. The conversion is done from the bottom up so callers can continue to pass a non-const pointer to a const parameter until the conversion is finished. No functional changes. Link: https://patch.msgid.link/20260901-work-idmap-const-v1-20-54ccd48e100b@kernel.org Reviewed-by: Jan Kara <jack@suse.cz> Reviewed-by: Seth Forshee <sforshee@kernel.org> Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
2026-09-23fs: port ->rename() to pass const mnt_idmapChristian Brauner
Convert to const struct mnt_idmap. A mount's idmapping is immutable. The only thing that is allowed to be modified afterwards is the reference count and that is hidden behind mnt_idmap_get() and mnt_idmap_put(). Everything else only ever reads from the idmapping. This is the same model that struct cred uses and the idmapping is also rather sensitive. So make the idmap argument const wherever we can. The conversion is done from the bottom up so callers can continue to pass a non-const pointer to a const parameter until the conversion is finished. No functional changes. Link: https://patch.msgid.link/20260901-work-idmap-const-v1-19-54ccd48e100b@kernel.org Reviewed-by: Jan Kara <jack@suse.cz> Reviewed-by: Seth Forshee <sforshee@kernel.org> Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
2026-09-23fs: port i_{g,u}id_into_vfs{g,u}id() to const mnt_idmapChristian Brauner
Convert to const struct mnt_idmap. A mount's idmapping is immutable. The only thing that is allowed to be modified afterwards is the reference count and that is hidden behind mnt_idmap_get() and mnt_idmap_put(). Everything else only ever reads from the idmapping. This is the same model that struct cred uses and the idmapping is also rather sensitive. So make the idmap argument const wherever we can. The conversion is done from the bottom up so callers can continue to pass a non-const pointer to a const parameter until the conversion is finished. No functional changes. Link: https://patch.msgid.link/20260901-work-idmap-const-v1-5-54ccd48e100b@kernel.org Reviewed-by: Jan Kara <jack@suse.cz> Reviewed-by: Seth Forshee <sforshee@kernel.org> Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
2026-09-10exfat: check for a directory write error with buffer_write_io_error()Chao Shi
exfat_update_bhs() waits for the writes it issued and then tests !buffer_uptodate() to find the ones that failed. That relies on the write completion handler clearing BH_Uptodate on error, which this series removes: a buffer whose write failed still holds the data the filesystem asked to be written, so declaring it not up to date is wrong and makes callers re-read it. Test BH_Write_EIO, which is what the completion handler sets and what this code actually wants to know. No behaviour change today - a failed write sets BH_Write_EIO and clears BH_Uptodate together. It stops being a no-op at the end of the series, where the new test is the one that still works. Acked-by: Weidong Zhu <weizhu@fiu.edu> Signed-off-by: Chao Shi <coshi036@gmail.com> Link: https://patch.msgid.link/0784ef63a525434e7c0aff730eca7b43e043095d.1785951556.git.coshi036@gmail.com Reviewed-by: Jan Kara <jack@suse.cz> Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
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>