coreutils
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[PATCH] copy: disallow copy_file_range() on Linux kernels before 5.3


From: Pádraig Brady
Subject: [PATCH] copy: disallow copy_file_range() on Linux kernels before 5.3
Date: Thu, 13 May 2021 00:09:02 +0100

copy_file_range() before Linux kernel release 5.3 had many issues,
as described at https://lwn.net/Articles/789527/, which was
referenced from https://lwn.net/Articles/846403/; a more general
article discussing the generality of copy_file_range().
Linux kernel 5.3 was released in September 2019, which is new enough
that we need to actively avoid older kernels.

* src/copy.c (functional_copy_file_range): A new function
that returns false for Linux kernels before version 5.3.
(sparse_copy): Call this new function to gate use of
copy_file_range().
---
 src/copy.c | 47 +++++++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 45 insertions(+), 2 deletions(-)

diff --git a/src/copy.c b/src/copy.c
index e92996390..df586294f 100644
--- a/src/copy.c
+++ b/src/copy.c
@@ -21,6 +21,7 @@
 #include <assert.h>
 #include <sys/ioctl.h>
 #include <sys/types.h>
+#include <sys/utsname.h>
 #include <selinux/selinux.h>
 
 #if HAVE_HURD_H
@@ -64,6 +65,7 @@
 #include "write-any-file.h"
 #include "areadlink.h"
 #include "yesno.h"
+#include "xstrtol.h"
 #include "selinux.h"
 
 #if USE_XATTR
@@ -244,6 +246,47 @@ create_hole (int fd, char const *name, bool punch_holes, 
off_t size)
   return true;
 }
 
+/* copy_file_range() before Linux kernel release 5.3 had many issues,
+   as described at https://lwn.net/Articles/789527/,
+   so return FALSE for Linux kernels earlier than that.
+   This function can be removed when such kernels (released before Sep 2019)
+   are no longer a consideration.  */
+
+static bool
+functional_copy_file_range (void)
+{
+#ifdef __linux__
+  static int version_allowed = -1;
+
+  if (version_allowed == -1)
+    version_allowed = 0;
+  else
+    return version_allowed;
+
+  struct utsname name;
+  if (uname (&name) == -1)
+    return false;
+
+  char *p = name.release;
+  uintmax_t ver[2] = {0, 0};
+  size_t iver = 0;
+
+  do
+    {
+      strtol_error err = xstrtoumax (p, &p, 10, &ver[iver], NULL);
+      if (err != LONGINT_OK || *p++ != '.')
+        break;
+    }
+  while (++iver < ARRAY_CARDINALITY (ver));
+
+  version_allowed = (ver[0] > 5 || (ver[0] == 5 && ver[1] >= 3));
+
+  return version_allowed;
+#else
+  return true;
+#endif
+
+}
 
 /* Copy the regular file open on SRC_FD/SRC_NAME to DST_FD/DST_NAME,
    honoring the MAKE_HOLES setting and using the BUF_SIZE-byte buffer
@@ -266,9 +309,9 @@ sparse_copy (int src_fd, int dest_fd, char *buf, size_t 
buf_size,
   *last_write_made_hole = false;
   *total_n_read = 0;
 
-  /* If not looking for holes, use copy_file_range if available,
+  /* If not looking for holes, use copy_file_range if functional,
      but don't use if reflink disallowed as that may be implicit.  */
-  if ((! hole_size) && allow_reflink)
+  if ((! hole_size) && allow_reflink && functional_copy_file_range ())
     while (max_n_read)
       {
         /* Copy at most COPY_MAX bytes at a time; this is min
-- 
2.26.2




reply via email to

[Prev in Thread] Current Thread [Next in Thread]