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
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.