diff mbox series

ovl: update inode size after extending passthrough write

Message ID 20241011134601.667572-1-amir73il@gmail.com (mailing list archive)
State New
Headers show
Series ovl: update inode size after extending passthrough write | expand

Commit Message

Amir Goldstein Oct. 11, 2024, 1:46 p.m. UTC
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@huawei.com>
Closes: https://github.com/libfuse/libfuse/issues/1048
Fixes: 57e1176e6086 ("fuse: implement read/write passthrough")
Signed-off-by: Amir Goldstein <amir73il@gmail.com>
---
 fs/fuse/passthrough.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

Comments

Amir Goldstein Oct. 11, 2024, 1:54 p.m. UTC | #1
Wrong subject line - resending...

On Fri, Oct 11, 2024 at 3:46 PM Amir Goldstein <amir73il@gmail.com> wrote:
>
> 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@huawei.com>
> Closes: https://github.com/libfuse/libfuse/issues/1048
> Fixes: 57e1176e6086 ("fuse: implement read/write passthrough")
> Signed-off-by: Amir Goldstein <amir73il@gmail.com>
> ---
>  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
>
diff mbox series

Patch

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)