diff options
Diffstat (limited to 'lib/copy-file-range.c')
-rw-r--r-- | lib/copy-file-range.c | 34 |
1 files changed, 34 insertions, 0 deletions
diff --git a/lib/copy-file-range.c b/lib/copy-file-range.c index 96f1ec7c5e8..1ec7f4de67c 100644 --- a/lib/copy-file-range.c +++ b/lib/copy-file-range.c @@ -20,11 +20,45 @@ #include <errno.h> +#if defined __linux__ && HAVE_COPY_FILE_RANGE +# include <sys/utsname.h> +#endif + ssize_t copy_file_range (int infd, off_t *pinoff, int outfd, off_t *poutoff, size_t length, unsigned int flags) { +#undef copy_file_range + +#if defined __linux__ && HAVE_COPY_FILE_RANGE + /* 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. */ + + static signed char ok; + + 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 (0 < ok) + return copy_file_range (infd, pinoff, outfd, poutoff, length, flags); +#endif + /* There is little need to emulate copy_file_range with read+write, since programs that use copy_file_range must fall back on read+write anyway. */ |