fs/ntfs3: fold file size handling into ntfs_set_size()

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 <almaz.alexandrovich@paragon-software.com>
This commit is contained in:
Konstantin Komarov 2026-05-22 15:11:25 +02:00
parent 9245b4adb1
commit ecbb433f9a
No known key found for this signature in database
GPG Key ID: A9B0331F832407B6
2 changed files with 51 additions and 148 deletions

View File

@ -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);

View File

@ -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;