yangyun reported that libfuse test test_copy_file_range() copies zero bytes from a newly written file when fuse passthrough is enabled. The reason is that extending passthrough write is not updating the fuse inode size and when vfs_copy_file_range() observes a zero size inode, it returns without calling the filesystem copy_file_range() method. Extend the fuse inode size to the size of the backing inode after every passthrough write if the backing inode size is larger. This does not yet provide cache coherency of fuse inode attributes and backing inode attributes, but it should prevent situations where fuse inode size is too small, causing read/copy to be wrongly shortened. Reported-by: yangyun <yangyun50@xxxxxxxxxx> Closes: https://github.com/libfuse/libfuse/issues/1048 Fixes: 57e1176e6086 ("fuse: implement read/write passthrough") Signed-off-by: Amir Goldstein <amir73il@xxxxxxxxx> --- fs/fuse/passthrough.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/fs/fuse/passthrough.c b/fs/fuse/passthrough.c index ba3207f6c4ce..d3047a4bc40e 100644 --- a/fs/fuse/passthrough.c +++ b/fs/fuse/passthrough.c @@ -20,9 +20,18 @@ static void fuse_file_accessed(struct file *file) static void fuse_file_modified(struct file *file) { + struct fuse_file *ff = file->private_data; + struct file *backing_file = fuse_file_passthrough(ff); struct inode *inode = file_inode(file); - - fuse_invalidate_attr_mask(inode, FUSE_STATX_MODSIZE); + loff_t size = i_size_read(file_inode(backing_file)); + + /* + * Most of the time we will be holding inode_lock(), but even if we are + * called from async io completion without inode_lock(), the last write + * will update fuse inode size to the size of the backing inode, even if + * the last write was not the extending write. + */ + fuse_write_update_attr(inode, size, size); } ssize_t fuse_passthrough_read_iter(struct kiocb *iocb, struct iov_iter *iter) -- 2.34.1