[RFC PATCH] fuse: fall back to copy_splice_read() when i_size is zero
Yafang Shao <[email protected]> Mon, 6 Jul 2026 10:57:05 +0800
| Newsgroups | dev.linux.lists.fuse-devel |
|---|---|
| Message-ID | <[email protected]> |
After upgrading the kernel from 6.1.y to 6.18.y on production servers,
some containers failed to read lxcfs data. The root cause is that our
lxcfs is still at the old v3.0.4, which does not include commit
25982f5 ("Set the file size to 4k") [0].
Between 6.1.y and 6.18.y, the FUSE splice_read path was converted
from generic_file_splice_read() to filemap_splice_read() by commit
2cb1e08985e3 ("splice: Use filemap_splice_read() instead of
generic_file_splice_read()"). Unlike generic_file_splice_read(),
filemap_splice_read() strictly checks i_size and refuses to read
from zero-size files, which breaks virtual FUSE filesystems like
the old lxcfs.
While the proper long-term fix is to upgrade lxcfs, the kernel
should be resilient to this common FUSE daemon behavior. Therefore,
when i_size is zero, fall back to copy_splice_read() instead of
filemap_splice_read(). copy_splice_read() calls the file's read_iter
operation, which sends a FUSE_READ request to the daemon and lets it
generate the content dynamically. This is safe because for genuinely
empty files the daemon simply returns 0 bytes.
Link: https://github.com/lxc/lxcfs/commit/25982f5d7e008e6ad13df28af3006ab8286f4c97 [0]
Signed-off-by: Yafang Shao <[email protected]>
Cc: David Howells <[email protected]>
---
fs/fuse/file.c | 8 +++++++-
1 file changed, 7 insertions(+), 1 deletion(-)
diff --git a/fs/fuse/file.c b/fs/fuse/file.c
index e052a0d44dee..78a499b24ac8 100644
--- a/fs/fuse/file.c
+++ b/fs/fuse/file.c
@@ -1893,8 +1893,14 @@ static ssize_t fuse_splice_read(struct file *in, loff_t *ppos,
/* FOPEN_DIRECT_IO overrides FOPEN_PASSTHROUGH */
if (fuse_file_passthrough(ff) && !(ff->open_flags & FOPEN_DIRECT_IO))
return fuse_passthrough_splice_read(in, ppos, pipe, len, flags);
- else
+ else if (i_size_read(in->f_mapping->host))
+ /* filemap_splice_read() requires non-zero i_size; for virtual
+ * FUSE daemons that report st_size = 0, fall back to
+ * copy_splice_read() which reaches the daemon via read_iter.
+ */
return filemap_splice_read(in, ppos, pipe, len, flags);
+ else
+ return copy_splice_read(in, ppos, pipe, len, flags);
}
static ssize_t fuse_splice_write(struct pipe_inode_info *pipe, struct file *out,
--
2.52.0