bug#79139: cp --reflink truncates sparse files on ZFS
Paul Eggert <[email protected]>
| Newsgroups | gmane.comp.gnu.core-utils.bugs |
|---|---|
| Organization | UCLA Computer Science Department |
| Message-ID | <[email protected]> |
On 2025-08-01 15:05, Collin Funk wrote: > I was hoping that file could be made a tiny stub, due to the > workarounds for Linux 4.19 being mostly unnecessary now that it is EOL. > But now we have a new problem to deal with. :) That we do. But we can more thorougly stubify the old Linux kernel bug workaround while we're in the neighborhood. Probably best not to remove it entirely as RHEL 8 still uses the no-longer-supported kernel. To do that, I installed the attached patches into Gnulib and propagated them into coreutils. Boldly closing the bug report. Thanks, Leah, for reporting it. That one was quite a whopper.
0001-copy-file-range-tune-for-more-modern-kernels.patch
(text/x-patch, 5.1 KB)
From 626f229915b114731cc4c9d9bda9eaa82d58180b Mon Sep 17 00:00:00 2001 From: Paul Eggert <[email protected]> Date: Fri, 1 Aug 2025 14:46:51 -0700 Subject: [PATCH 1/2] copy-file-range: tune for more-modern kernels MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * lib/copy-file-range.c [__linux__ && HAVE_COPY_FILE_RANGE]: Include <linux/version.h>. Include <sys/utsname.h> only for kernel 5.2 and earlier. (CHECK_LINUX_KERNEL_VERSION): New macro. (copy_file_range) [__linux__ && HAVE_COPY_FILE_RANGE]: Call utsname only when built for kernel 5.2 and earlier. (copy_file_range) [!__linux__ && HAVE_COPY_FILE_RANGE]: Use underlying copy_file_range. This doesn’t change behavior since the code is not compiled in this case, but it makes the code a bit cleaner. * modules/copy-file-range (Depends-on): Add bool. --- ChangeLog | 15 +++++++++++++ lib/copy-file-range.c | 48 +++++++++++++++++++++++++---------------- m4/copy-file-range.m4 | 3 +-- modules/copy-file-range | 1 + 4 files changed, 47 insertions(+), 20 deletions(-) diff --git a/ChangeLog b/ChangeLog index 60b8d08d37..b33d7fd00b 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,18 @@ +2025-08-01 Paul Eggert <[email protected]> + + copy-file-range: tune for more-modern kernels + * lib/copy-file-range.c [__linux__ && HAVE_COPY_FILE_RANGE]: + Include <linux/version.h>. Include <sys/utsname.h> + only for kernel 5.2 and earlier. + (CHECK_LINUX_KERNEL_VERSION): New macro. + (copy_file_range) [__linux__ && HAVE_COPY_FILE_RANGE]: + Call utsname only when built for kernel 5.2 and earlier. + (copy_file_range) [!__linux__ && HAVE_COPY_FILE_RANGE]: + Use underlying copy_file_range. This doesn’t change + behavior since the code is not compiled in this case, + but it makes the code a bit cleaner. + * modules/copy-file-range (Depends-on): Add bool. + 2025-08-01 Bruno Haible <[email protected]> sigsegv: Use new ioctl available in Linux >= 6.11. diff --git a/lib/copy-file-range.c b/lib/copy-file-range.c index 8e0c644c08..73e02db1c1 100644 --- a/lib/copy-file-range.c +++ b/lib/copy-file-range.c @@ -21,7 +21,13 @@ #include <errno.h> #if defined __linux__ && HAVE_COPY_FILE_RANGE +# include <linux/version.h> # include <sys/utsname.h> +# if LINUX_VERSION_CODE < KERNEL_VERSION (5, 3, 0) +# define CHECK_LINUX_KERNEL_VERSION true +# else +# define CHECK_LINUX_KERNEL_VERSION false +# endif #endif ssize_t @@ -31,32 +37,38 @@ copy_file_range (int infd, off_t *pinoff, { #undef copy_file_range -#if defined __linux__ && HAVE_COPY_FILE_RANGE +#if HAVE_COPY_FILE_RANGE + bool ok = true; + +# if CHECK_LINUX_KERNEL_VERSION /* The implementation of copy_file_range (which first appeared in Linux kernel release 4.5) had many issues before release 5.3 <https://lwn.net/Articles/789527/>, so fail with ENOSYS for Linux kernels 5.2 and earlier. - This workaround, and the configure-time check for Linux, can be - removed when such kernels (released March 2016 through September - 2019) are no longer a consideration. As of January 2021, the - furthest-future planned kernel EOL is December 2024 for kernel - release 4.19. */ + This workaround can be removed when such kernels (released March + 2016 through September 2019) are no longer a consideration. + Although all such kernels have reached EOL, some distros use + older kernels. For example, RHEL 8 uses kernel 4.18 and has an + EOL of 2029. */ - static signed char ok; + static signed char kernel_ok; + if (! kernel_ok) + { + struct utsname name; + uname (&name); + char *p = name.release; + kernel_ok = ((p[1] != '.' || '5' < p[0] + || (p[0] == '5' && (p[3] != '.' || '2' < p[2]))) + ? 1 : -1); + } - if (! ok) - { - struct utsname name; - uname (&name); - char *p = name.release; - ok = ((p[1] != '.' || '5' < p[0] - || (p[0] == '5' && (p[3] != '.' || '2' < p[2]))) - ? 1 : -1); - } + if (kernel_ok < 0) + ok = false; +# endif - if (0 < ok) - return copy_file_range (infd, pinoff, outfd, poutoff, length, flags); + if (ok) + return copy_file_range (infd, pinoff, outfd, poutoff, length, flags); #endif /* There is little need to emulate copy_file_range with read+write, diff --git a/m4/copy-file-range.m4 b/m4/copy-file-range.m4 index 13081d9b11..ec17fc68c7 100644 --- a/m4/copy-file-range.m4 +++ b/m4/copy-file-range.m4 @@ -52,8 +52,7 @@ AC_DEFUN([gl_FUNC_COPY_FILE_RANGE], case $host_os in linux*) - # See copy-file-range.c comment re pre-5.3 Linux kernel bugs. - # We should be able to remove this hack in 2025. + # See copy-file-range.c comment re Linux kernel bugs. REPLACE_COPY_FILE_RANGE=1;; esac fi diff --git a/modules/copy-file-range b/modules/copy-file-range index 12b6fcb86e..573558a945 100644 --- a/modules/copy-file-range +++ b/modules/copy-file-range @@ -6,6 +6,7 @@ lib/copy-file-range.c m4/copy-file-range.m4 Depends-on: +bool largefile unistd-h -- 2.48.1
0002-copy-file-range-work-around-glibc-bug-33245.patch
(text/x-patch, 3.4 KB)
From 948ba80168293e3d8a905eeffb0ab61424c4c374 Mon Sep 17 00:00:00 2001 From: Paul Eggert <[email protected]> Date: Fri, 1 Aug 2025 16:01:13 -0700 Subject: [PATCH 2/2] copy-file-range: work around glibc bug 33245 Problem reported by Leah Neukirchen <https://bugs.gnu.org/79139>. * lib/copy-file-range.c: Include sys-limits.h. (copy_file_range) [glibc 2.42 and earlier]: Copy at most SYS_BUFSIZE_MAX bytes. * m4/copy-file-range.m4 (gl_FUNC_COPY_FILE_RANGE): Replace on glibc platforms, as well as on Linux platforms. * modules/copy-file-range (Files): Add lib/sys-limits.h. --- ChangeLog | 9 +++++++++ lib/copy-file-range.c | 17 ++++++++++++++++- m4/copy-file-range.m4 | 4 ++-- modules/copy-file-range | 1 + 4 files changed, 28 insertions(+), 3 deletions(-) diff --git a/ChangeLog b/ChangeLog index b33d7fd00b..e60a387611 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,14 @@ 2025-08-01 Paul Eggert <[email protected]> + copy-file-range: work around glibc bug 33245 + Problem reported by Leah Neukirchen <https://bugs.gnu.org/79139>. + * lib/copy-file-range.c: Include sys-limits.h. + (copy_file_range) [glibc 2.42 and earlier]: + Copy at most SYS_BUFSIZE_MAX bytes. + * m4/copy-file-range.m4 (gl_FUNC_COPY_FILE_RANGE): + Replace on glibc platforms, as well as on Linux platforms. + * modules/copy-file-range (Files): Add lib/sys-limits.h. + copy-file-range: tune for more-modern kernels * lib/copy-file-range.c [__linux__ && HAVE_COPY_FILE_RANGE]: Include <linux/version.h>. Include <sys/utsname.h> diff --git a/lib/copy-file-range.c b/lib/copy-file-range.c index 73e02db1c1..5c12f6f715 100644 --- a/lib/copy-file-range.c +++ b/lib/copy-file-range.c @@ -30,6 +30,8 @@ # endif #endif +#include "sys-limits.h" + ssize_t copy_file_range (int infd, off_t *pinoff, int outfd, off_t *poutoff, @@ -68,7 +70,20 @@ copy_file_range (int infd, off_t *pinoff, # endif if (ok) - return copy_file_range (infd, pinoff, outfd, poutoff, length, flags); + { +# if defined __GLIBC__ && ! (2 < __GLIBC__ + (43 <= __GLIBC_MINOR__)) + /* Work around glibc bug 33245 + <https://sourceware.org/bugzilla/show_bug.cgi?id=33245>. + This bug is present in glibc 2.42 (2025) and fixed in 2.43, + so this workaround, and the configure-time check for glibc, + can be removed once glibc 2.42 and earlier is no longer a + consideration. Perhaps in 2040. */ + if (SYS_BUFSIZE_MAX < length) + length = SYS_BUFSIZE_MAX; +# endif + + return copy_file_range (infd, pinoff, outfd, poutoff, length, flags); + } #endif /* There is little need to emulate copy_file_range with read+write, diff --git a/m4/copy-file-range.m4 b/m4/copy-file-range.m4 index ec17fc68c7..85cfef71f4 100644 --- a/m4/copy-file-range.m4 +++ b/m4/copy-file-range.m4 @@ -51,8 +51,8 @@ AC_DEFUN([gl_FUNC_COPY_FILE_RANGE], [Define to 1 if the function copy_file_range exists.]) case $host_os in - linux*) - # See copy-file-range.c comment re Linux kernel bugs. + *-gnu* | gnu* | linux*) + # See copy-file-range.c comment re glibc and Linux kernel bugs. REPLACE_COPY_FILE_RANGE=1;; esac fi diff --git a/modules/copy-file-range b/modules/copy-file-range index 573558a945..e3512eaffa 100644 --- a/modules/copy-file-range +++ b/modules/copy-file-range @@ -3,6 +3,7 @@ Copy parts of files Files: lib/copy-file-range.c +lib/sys-limits.h m4/copy-file-range.m4 Depends-on: -- 2.48.1