[RFC PATCH 5/8] fat: drop ->write_begin() and ->write_end() usage from fat_cont_expand()
From: Matheus Alves de Almeida
Date: Sat Oct 31 2026 - 12:47:00 EST
fat_cont_expand() currently relies on ->write_begin() and ->write_end()
to zero the new file range. These callbacks will no longer be available
after the iomap conversion, so a new method for zeroing is needed.
Zero new ranges folio-by-folio instead.
Signed-off-by: Matheus Alves de Almeida <matheus.aalmeida@xxxxxxxxxxxx>
---
fs/fat/file.c | 122 +++++++++++++++++++++++++++++++-------------------
1 file changed, 76 insertions(+), 46 deletions(-)
diff --git a/fs/fat/file.c b/fs/fat/file.c
index 177a26186ddc..8689cbbdad13 100644
--- a/fs/fat/file.c
+++ b/fs/fat/file.c
@@ -18,6 +18,8 @@
#include <linux/security.h>
#include <linux/falloc.h>
#include <linux/fileattr.h>
+#include <linux/rmap.h>
+#include <linux/writeback.h>
#include "fat.h"
static long fat_fallocate(struct file *file, int mode,
@@ -353,68 +355,96 @@ static int fat_preallocate(struct inode *inode, loff_t new_size)
return fat_add_clusters(inode, nr_cluster);
}
-static int fat_cont_expand_simple(struct inode *inode, loff_t size)
+static int fat_zero_and_extend(struct inode *inode, loff_t new_size)
{
+ struct msdos_inode_info *mi = MSDOS_I(inode);
struct address_space *mapping = inode->i_mapping;
- const struct address_space_operations *aops = mapping->a_ops;
+ loff_t pos = i_size_read(inode), fpos, next;
+ loff_t old_size = pos;
struct folio *folio;
- void *fsdata = NULL;
- int err;
+ size_t from, to;
+ pgoff_t index;
- err = aops->write_begin(NULL, mapping, size, 0, &folio, &fsdata);
- if (err)
- goto out;
+ while (pos < new_size) {
+ index = pos >> PAGE_SHIFT;
- err = aops->write_end(NULL, mapping, size, 0, 0, folio, fsdata);
+ folio = read_mapping_folio(mapping, index, NULL);
+ if (IS_ERR(folio))
+ return PTR_ERR(folio);
-out:
- return err;
+ folio_lock(folio);
+ if (folio->mapping != mapping) {
+ folio_unlock(folio);
+ folio_put(folio);
+ continue;
+ }
+ folio_wait_stable(folio);
+
+ fpos = folio_pos(folio);
+ next = min_t(loff_t, new_size, fpos + folio_size(folio));
+ from = pos - fpos;
+ to = next - fpos;
+
+ /*
+ * An existing shared writeable mmap of the file could allow
+ * writing into the newly zeroed range, so make it read-only.
+ */
+ if (pos == old_size && from)
+ folio_mkclean(folio);
+
+ folio_zero_segment(folio, from, to);
+ folio_mark_dirty(folio);
+ mi->mmu_private = max(mi->mmu_private, next);
+ i_size_write(inode, next);
+
+ folio_unlock(folio);
+ folio_put(folio);
+
+ balance_dirty_pages_ratelimited(mapping);
+ if (fatal_signal_pending(current))
+ return -EINTR;
+
+ pos = next;
+ }
+
+ return 0;
}
-static int fat_cont_expand(struct inode *inode, loff_t size)
+static int fat_cont_expand(struct inode *inode, loff_t new_size)
{
- struct address_space *mapping = inode->i_mapping;
- loff_t start = inode->i_size, count = size - inode->i_size;
+ struct msdos_inode_info *mi = MSDOS_I(inode);
+ loff_t old_alloc = inode_get_bytes(inode);
+ loff_t old_size = i_size_read(inode);
+ loff_t old_mmu_private = mi->mmu_private;
int err;
- /*
- * Preallocating space here is more efficient than during
- * fat_cont_expand_simple() because of batching.
- */
- err = inode_newsize_ok(inode, size);
- if (err)
- goto out;
- err = fat_preallocate(inode, size);
- if (err)
- goto out;
- err = fat_cont_expand_simple(inode, size);
+ err = inode_newsize_ok(inode, new_size);
if (err)
- goto out;
+ return err;
+ inode_dio_wait(inode);
+ err = fat_preallocate(inode, new_size);
+ if (err) {
+ /* fat_chain_add() does not rollback on failure */
+ fat_truncate_blocks(inode, old_alloc);
+ return err;
+ }
+ err = fat_zero_and_extend(inode, new_size);
+ if (err) {
+ filemap_invalidate_lock(inode->i_mapping);
+ truncate_setsize(inode, old_size);
+ fat_truncate_blocks(inode, old_alloc);
+ mi->mmu_private = old_mmu_private;
+ filemap_invalidate_unlock(inode->i_mapping);
+ return err;
+ }
fat_truncate_time(inode, NULL, FAT_UPDATE_CMTIME);
+ mi->i_attrs |= ATTR_ARCH;
mark_inode_dirty(inode);
- if (IS_SYNC(inode)) {
- int err2;
+ if (IS_SYNC(inode))
+ return write_inode_now(inode, 1);
- /*
- * Opencode syncing since we don't have a file open to use
- * standard fsync path.
- */
- err = filemap_fdatawrite_range(mapping, start,
- start + count - 1);
- err2 = mmb_sync(&MSDOS_I(inode)->i_metadata_bhs);
- if (!err)
- err = err2;
- err2 = write_inode_now(inode, 1);
- if (!err)
- err = err2;
- if (!err) {
- err = filemap_fdatawait_range(mapping, start,
- start + count - 1);
- }
- }
-out:
- return err;
+ return 0;
}
/*
--
2.43.0