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