[PATCH 6/6] fs/ntfs3: fold file size handling into ntfs_set_size()

Konstantin Komarov <[email protected]> Tue, 26 May 2026 15:49:07 +0200
Newsgroups dev.linux.lists.ntfs3,org.kernel.vger.linux-fsdevel,org.kernel.vger.linux-kernel
Message-ID <[email protected]>
Remove the separate ntfs_extend() and ntfs_truncate() helpers and route
file size changes through ntfs_set_size().

This consolidates ntfs3 size updates in one place and lets the write,
fallocate, and setattr paths share the same logic for updating i_size,
valid data length, and preallocated extents.

This patch fixes a few issues found during internal tests.

Signed-off-by: Konstantin Komarov <[email protected]>
---
 fs/ntfs3/file.c  | 178 +++++++++++------------------------------------
 fs/ntfs3/inode.c |  21 +++---
 2 files changed, 51 insertions(+), 148 deletions(-)

diff --git a/fs/ntfs3/file.c b/fs/ntfs3/file.c
index 26bec5be248e..06a5d9b44ac1 100644
--- a/fs/ntfs3/file.c
+++ b/fs/ntfs3/file.c
@@ -450,93 +450,6 @@ static int ntfs_file_mmap_prepare(struct vm_area_desc *desc)
 	return err;
 }
 
-static int ntfs_extend(struct inode *inode, loff_t pos, size_t count,
-		       struct file *file)
-{
-	struct ntfs_inode *ni = ntfs_i(inode);
-	struct address_space *mapping = inode->i_mapping;
-	loff_t end = pos + count;
-	bool extend_init = file && pos > ni->i_valid;
-	int err;
-
-	if (end <= inode->i_size && !extend_init)
-		return 0;
-
-	/* Mark rw ntfs as dirty. It will be cleared at umount. */
-	ntfs_set_state(ni->mi.sbi, NTFS_DIRTY_DIRTY);
-
-	if (end > inode->i_size) {
-		/*
-		 * Normal files: increase file size, allocate space.
-		 * Sparse/Compressed: increase file size. No space allocated.
-		 */
-		err = ntfs_set_size(inode, end);
-		if (err)
-			goto out;
-	}
-
-	if (extend_init && !is_compressed(ni)) {
-		err = ntfs_extend_initialized_size(file, ni, pos);
-		if (err)
-			goto out;
-	} else {
-		err = 0;
-	}
-
-	inode_set_mtime_to_ts(inode, inode_set_ctime_current(inode));
-	mark_inode_dirty(inode);
-
-	if (IS_SYNC(inode)) {
-		int err2;
-
-		err = filemap_fdatawrite_range(mapping, pos, end - 1);
-		err2 = write_inode_now(inode, 1);
-		if (!err)
-			err = err2;
-		if (!err)
-			err = filemap_fdatawait_range(mapping, pos, end - 1);
-	}
-
-out:
-	return err;
-}
-
-static int ntfs_truncate(struct inode *inode, loff_t new_size)
-{
-	int err;
-	struct ntfs_inode *ni = ntfs_i(inode);
-	u64 new_valid = min_t(u64, ni->i_valid, new_size);
-
-	truncate_setsize(inode, new_size);
-
-	ni_lock(ni);
-
-	down_write(&ni->file.run_lock);
-	err = attr_set_size_ex(ni, ATTR_DATA, NULL, 0, &ni->file.run, new_size,
-			       &new_valid, ni->mi.sbi->options->prealloc, NULL,
-			       false);
-	up_write(&ni->file.run_lock);
-
-	ni->i_valid = new_valid;
-
-	ni_unlock(ni);
-
-	if (err)
-		return err;
-
-	ni->std_fa |= FILE_ATTRIBUTE_ARCHIVE;
-	inode_set_mtime_to_ts(inode, inode_set_ctime_current(inode));
-	if (!IS_DIRSYNC(inode)) {
-		mark_inode_dirty(inode);
-	} else {
-		err = ntfs_sync_inode(inode);
-		if (err)
-			return err;
-	}
-
-	return 0;
-}
-
 /*
  * ntfs_fallocate - file_operations::ntfs_fallocate
  *
@@ -743,57 +656,25 @@ static long ntfs_fallocate(struct file *file, int mode, loff_t vbo, loff_t len)
 		if (is_supported_holes) {
 			CLST vcn = vbo >> cluster_bits;
 			CLST cend = bytes_to_cluster(sbi, end);
-			CLST cend_v = bytes_to_cluster(sbi, ni->i_valid);
 			CLST lcn, clen;
 			bool new;
 
-			if (cend_v > cend)
-				cend_v = cend;
-
 			/*
 			 * Allocate and zero new clusters.
-			 * Zeroing these clusters may be too long.
-			 */
-			for (; vcn < cend_v; vcn += clen) {
-				err = attr_data_get_block(ni, vcn, cend_v - vcn,
-							  &lcn, &clen, &new,
-							  true, NULL, false);
-				if (err)
-					goto out;
-			}
-
-			/*
-			 * Moving up 'valid size'.
-			 */
-			err = ntfs_extend_initialized_size(
-				file, ni, (u64)cend_v << cluster_bits);
-			if (err)
-				goto out;
-
-			/*
-			 * Allocate but not zero new clusters.
 			 */
 			for (; vcn < cend; vcn += clen) {
 				err = attr_data_get_block(ni, vcn, cend - vcn,
 							  &lcn, &clen, &new,
-							  false, NULL, false);
+							  true, NULL, false);
 				if (err)
 					goto out;
 			}
 		}
 
 		if (mode & FALLOC_FL_KEEP_SIZE) {
-			ni_lock(ni);
-			/* True - Keep preallocated. */
-			err = attr_set_size(ni, ATTR_DATA, NULL, 0,
-					    &ni->file.run, i_size, &ni->i_valid,
-					    true);
-			ni_unlock(ni);
+			err = ntfs_set_size(inode, i_size);
 			if (err)
 				goto out;
-			i_size_write(inode, i_size);
-		} else if (new_size > i_size) {
-			i_size_write(inode, new_size);
 		}
 	}
 
@@ -850,16 +731,20 @@ int ntfs_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
 		oldsize = i_size_read(inode);
 		newsize = attr->ia_size;
 
-		if (newsize <= oldsize)
-			err = ntfs_truncate(inode, newsize);
-		else
-			err = ntfs_extend(inode, newsize, 0, NULL);
+		if (newsize != oldsize) {
+			truncate_setsize(inode, newsize);
 
-		if (err)
-			goto out;
+			err = ntfs_set_size(inode, newsize);
+			if (err) {
+				i_size_write(inode, oldsize);
+				goto out;
+			}
 
-		ni->ni_flags |= NI_FLAG_UPDATE_PARENT;
-		i_size_write(inode, newsize);
+			ni->std_fa |= FILE_ATTRIBUTE_ARCHIVE;
+			ni->ni_flags |= NI_FLAG_UPDATE_PARENT;
+			inode_set_mtime_to_ts(inode,
+					      inode_set_ctime_current(inode));
+		}
 	}
 
 	setattr_copy(idmap, inode, attr);
@@ -1333,6 +1218,7 @@ static ssize_t ntfs_file_write_iter(struct kiocb *iocb, struct iov_iter *from)
 	struct file *file = iocb->ki_filp;
 	struct inode *inode = file_inode(file);
 	struct ntfs_inode *ni = ntfs_i(inode);
+	loff_t vbo, endbyte;
 	ssize_t ret, err;
 
 	if (!inode_trylock(inode)) {
@@ -1367,15 +1253,30 @@ static ssize_t ntfs_file_write_iter(struct kiocb *iocb, struct iov_iter *from)
 		goto out;
 	}
 
-	ret = ntfs_extend(inode, iocb->ki_pos, ret, file);
-	if (ret)
-		goto out;
+	vbo = iocb->ki_pos;
+	endbyte = vbo + ret;
+
+	if (endbyte > inode->i_size) {
+		/*
+		 * Normal files: increase file size, allocate space.
+		 * Sparse/Compressed: increase file size. No space allocated.
+		 */
+		ret = ntfs_set_size(inode, endbyte);
+		if (ret)
+			goto out;
+	}
 
 	if (is_compressed(ni)) {
 		ret = ntfs_compress_write(iocb, from);
 		goto out;
 	}
 
+	if (vbo > ni->i_valid) {
+		ret = ntfs_extend_initialized_size(file, ni, vbo);
+		if (ret)
+			goto out;
+	}
+
 	/* Fallback to buffered I/O if the inode does not support direct I/O. */
 	if (!(iocb->ki_flags & IOCB_DIRECT) ||
 	    !ntfs_should_use_dio(iocb, from)) {
@@ -1408,7 +1309,7 @@ static ssize_t ntfs_file_write_iter(struct kiocb *iocb, struct iov_iter *from)
 	}
 
 	if (ret >= 0 && iov_iter_count(from)) {
-		loff_t offset = iocb->ki_pos, endbyte;
+		vbo = iocb->ki_pos;
 
 		iocb->ki_flags &= ~IOCB_DIRECT;
 		err = iomap_file_buffered_write(iocb, from, &ntfs_iomap_ops,
@@ -1426,15 +1327,15 @@ static ssize_t ntfs_file_write_iter(struct kiocb *iocb, struct iov_iter *from)
 		* to complete off the I/O request.
 		*/
 		ret += err;
-		endbyte = offset + err - 1;
-		err = filemap_write_and_wait_range(inode->i_mapping, offset,
+		endbyte = vbo + err - 1;
+		err = filemap_write_and_wait_range(inode->i_mapping, vbo,
 						   endbyte);
 		if (err) {
 			ret = err;
 			goto out;
 		}
 
-		invalidate_mapping_pages(inode->i_mapping, offset >> PAGE_SHIFT,
+		invalidate_mapping_pages(inode->i_mapping, vbo >> PAGE_SHIFT,
 					 endbyte >> PAGE_SHIFT);
 	}
 
@@ -1515,8 +1416,9 @@ static int ntfs_file_release(struct inode *inode, struct file *file)
 		down_write(&ni->file.run_lock);
 
 		/* Deallocate preallocated. */
-		err = attr_set_size(ni, ATTR_DATA, NULL, 0, &ni->file.run,
-				    inode->i_size, &ni->i_valid, false);
+		err = attr_set_size_ex(ni, ATTR_DATA, NULL, 0, &ni->file.run,
+				       inode->i_size, &ni->i_valid, false, NULL,
+				       true);
 
 		up_write(&ni->file.run_lock);
 		ni_unlock(ni);
diff --git a/fs/ntfs3/inode.c b/fs/ntfs3/inode.c
index 9a75c2d832ca..2d3aad60caa4 100644
--- a/fs/ntfs3/inode.c
+++ b/fs/ntfs3/inode.c
@@ -693,17 +693,18 @@ int ntfs_set_size(struct inode *inode, u64 new_size)
 		return -EFBIG;
 	}
 
+	/* Mark rw ntfs as dirty. It will be cleared at umount. */
+	ntfs_set_state(sbi, NTFS_DIRTY_DIRTY);
+
 	ni_lock(ni);
 	down_write(&ni->file.run_lock);
+	if (new_size < ni->i_valid)
+		ni->i_valid = new_size;
 
+	/* last 'true' means keep preallocated. */
 	err = attr_set_size(ni, ATTR_DATA, NULL, 0, &ni->file.run, new_size,
 			    &ni->i_valid, true);
 
-	if (!err) {
-		i_size_write(inode, new_size);
-		mark_inode_dirty(inode);
-	}
-
 	up_write(&ni->file.run_lock);
 	ni_unlock(ni);
 
@@ -778,11 +779,6 @@ static int ntfs_iomap_begin(struct inode *inode, loff_t offset, loff_t length,
 		return err;
 	}
 
-	if (!clen) {
-		/* broken file? */
-		return -EINVAL;
-	}
-
 	if (lcn == EOF_LCN) {
 		/* request out of file. */
 		if (flags & IOMAP_REPORT) {
@@ -816,6 +812,11 @@ static int ntfs_iomap_begin(struct inode *inode, loff_t offset, loff_t length,
 		return 0;
 	}
 
+	if (!clen) {
+		/* broken file? */
+		return -EINVAL;
+	}
+
 	iomap->bdev = inode->i_sb->s_bdev;
 	iomap->offset = offset;
 	iomap->length = ((loff_t)clen << cluster_bits) - off;
-- 
2.43.0