copy_file_range (which calls generic_copy_file_checks) uses the inode file size to adjust the copy count parameter. This breaks with special filesystems like procfs/sysfs, where the file size appears to be zero, but content is actually returned when a read operation is performed.
This commit ignores the source file size, and makes copy_file_range match the end of file behaviour documented in POSIX's "read", where 0 is returned to mark EOF. This would allow "cp" and other standard tools to make use of copy_file_range with the exact same behaviour as they had in the past. Fixes: 96e6e8f4a68d ("vfs: add missing checks to copy_file_range") Signed-off-by: Nicolas Boichat <drink...@chromium.org> --- This can be reproduced with this simple test case: #define _GNU_SOURCE #include <fcntl.h> #include <stdio.h> #include <stdlib.h> #include <sys/stat.h> #include <unistd.h> int main(int argc, char **argv) { int fd_in, fd_out; loff_t ret; fd_in = open("/proc/version", O_RDONLY); fd_out = open("version", O_CREAT | O_WRONLY | O_TRUNC, 0644); do { ret = copy_file_range(fd_in, NULL, fd_out, NULL, 1024, 0); printf("%d bytes copied\n", (int)ret); } while (ret > 0); return 0; } Without this patch, `version` output file is empty, and no bytes are copied: 0 bytes copied With this patch, the loop runs twice and the content of the file is copied: 315 bytes copied 0 bytes copied We hit this issue when upgrading Go compiler from 1.13 to 1.15 [1], as we use Go's `io.Copy` to copy the content of `/sys/kernel/debug/tracing/trace` to a temporary file. Under the hood, Go 1.15 uses `copy_file_range` syscall to optimize the copy operation. However, that fails to copy any content when the input file is from sysfs/tracefs, with an apparent size of 0 (but there is still content when you `cat` it, of course). [1] http://issuetracker.google.com/issues/178332739 fs/read_write.c | 8 -------- 1 file changed, 8 deletions(-) diff --git a/fs/read_write.c b/fs/read_write.c index 75f764b43418..7236146f6ad7 100644 --- a/fs/read_write.c +++ b/fs/read_write.c @@ -1424,7 +1424,6 @@ static int generic_copy_file_checks(struct file *file_in, loff_t pos_in, struct inode *inode_in = file_inode(file_in); struct inode *inode_out = file_inode(file_out); uint64_t count = *req_count; - loff_t size_in; int ret; ret = generic_file_rw_checks(file_in, file_out); @@ -1442,13 +1441,6 @@ static int generic_copy_file_checks(struct file *file_in, loff_t pos_in, if (pos_in + count < pos_in || pos_out + count < pos_out) return -EOVERFLOW; - /* Shorten the copy to EOF */ - size_in = i_size_read(inode_in); - if (pos_in >= size_in) - count = 0; - else - count = min(count, size_in - (uint64_t)pos_in); - ret = generic_write_check_limits(file_out, pos_out, &count); if (ret) return ret; -- 2.30.0.280.ga3ce27912f-goog