* [PATCH] copy_file_range: New function to copy file data
@ 2017-11-17 14:56 Florian Weimer
2017-11-17 14:58 ` Florian Weimer
` (3 more replies)
0 siblings, 4 replies; 24+ messages in thread
From: Florian Weimer @ 2017-11-17 14:56 UTC (permalink / raw)
To: libc-alpha
The semantics are based on the Linux system call, but a very close
emulation in user space is provided.
2017-11-17 Florian Weimer <fweimer@redhat.com>
* io/Makefile (routines): Add copy_file_range.
(tests): Add tst-copy_file_range.
(tests-static, tests-internal): Add tst-copy_file_range-compat.
* io/Versions (GLIBC_2.27): Export copy_file_range.
* io/copy_file_range-common.c: New file.
* io/copy_file_range.c: Likewise.
* io/tst-copy_file_range-compat.c: Likewise.
* io/tst-copy_file_range.c: Likewise.
* manual/llio.texi (Copying File Data): New section.
* posix/unistd.h [__USE_GNU] (copy_file_range): Declare.
* support/Makefile (libsupport-routines): Add support-xfstat,
support_enter_mount_namespace, xftruncate, xlseek.
* support/namespace.h (support_enter_mount_namespace): Declare.
* support/support-xfstat.c: New file.
* support/support_enter_mount_namespace.c: Likewise.
* support/xftruncate.c: Likewise.
* support/xlseek.c: Likewise.
* sysdeps/unix/sysv/linux/**.abilist: Update.
* sysdeps/unix/sysv/linux/copy_file_range.c: New file.
diff --git a/io/Makefile b/io/Makefile
index 2f26bf56db..f2f0f0d040 100644
--- a/io/Makefile
+++ b/io/Makefile
@@ -52,7 +52,7 @@ routines := \
ftw ftw64 fts fts64 poll ppoll \
posix_fadvise posix_fadvise64 \
posix_fallocate posix_fallocate64 \
- sendfile sendfile64 \
+ sendfile sendfile64 copy_file_range \
utimensat futimens
# These routines will be omitted from the libc shared object.
@@ -70,7 +70,13 @@ tests := test-utime test-stat test-stat2 test-lfs tst-getcwd \
tst-symlinkat tst-linkat tst-readlinkat tst-mkdirat \
tst-mknodat tst-mkfifoat tst-ttyname_r bug-ftw5 \
tst-posix_fallocate tst-posix_fallocate64 \
- tst-fts tst-fts-lfs tst-open-tmpfile
+ tst-fts tst-fts-lfs tst-open-tmpfile \
+ tst-copy_file_range \
+
+# This test includes the compat implementation of copy_file_range,
+# which uses internal, unexported libc functions.
+tests-static += tst-copy_file_range-compat
+tests-internal += tst-copy_file_range-compat
ifeq ($(run-built-tests),yes)
tests-special += $(objpfx)ftwtest.out
diff --git a/io/Versions b/io/Versions
index 64316cd025..98898cb9d5 100644
--- a/io/Versions
+++ b/io/Versions
@@ -125,4 +125,7 @@ libc {
GLIBC_2.23 {
fts64_children; fts64_close; fts64_open; fts64_read; fts64_set;
}
+ GLIBC_2.27 {
+ copy_file_range;
+ }
}
diff --git a/io/copy_file_range-common.c b/io/copy_file_range-common.c
new file mode 100644
index 0000000000..84fc01ef57
--- /dev/null
+++ b/io/copy_file_range-common.c
@@ -0,0 +1,163 @@
+/* Emulation of copy_file_range.
+ Copyright (C) 2017 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, see
+ <http://www.gnu.org/licenses/>. */
+
+/* The following macros should be defined before including this
+ file:
+
+ COPY_FILE_RANGE_DECL Declaration specifiers for the function below.
+ COPY_FILE_RANGE Name of the function to define. */
+
+#include <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <limits.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+COPY_FILE_RANGE_DECL
+ssize_t
+COPY_FILE_RANGE (int infd, __off64_t *pinoff,
+ int outfd, __off64_t *poutoff,
+ size_t length, unsigned int flags)
+{
+ if (flags != 0)
+ {
+ __set_errno (EINVAL);
+ return -1;
+ }
+
+ struct stat64 instat;
+ struct stat64 outstat;
+ if (fstat64 (infd, &instat) != 0 || fstat64 (outfd, &outstat))
+ return -1;
+ if (S_ISDIR (instat.st_mode) || S_ISDIR (outstat.st_mode))
+ {
+ __set_errno (EISDIR);
+ return -1;
+ }
+ if (!S_ISREG (instat.st_mode) || !S_ISREG (outstat.st_mode))
+ {
+ /* We need a regular input file so that the we can seek
+ backwards in case of a write failure. */
+ __set_errno (EINVAL);
+ return -1;
+ }
+ if (instat.st_dev != outstat.st_dev)
+ {
+ /* Cross-device copies are not supported. */
+ __set_errno (EXDEV);
+ return -1;
+ }
+
+ /* The output descriptor must not have O_APPEND set. */
+ {
+ int flags = __fcntl (outfd, F_GETFL);
+ if (flags & O_APPEND)
+ {
+ __set_errno (EBADF);
+ return -1;
+ }
+ }
+
+ /* Avoid an overflow in the result. */
+ if (length > SSIZE_MAX)
+ length = SSIZE_MAX;
+
+ /* Main copying loop. The buffer size is arbitrary and is a
+ trade-off between stack size consumption, cache usage, and
+ amortization of system call overhead. */
+ size_t copied = 0;
+ char buf[8192];
+ while (length > 0)
+ {
+ size_t to_read = length;
+ if (to_read > sizeof (buf))
+ to_read = sizeof (buf);
+
+ /* Fill the buffer. */
+ ssize_t read_count;
+ if (pinoff == NULL)
+ read_count = read (infd, buf, to_read);
+ else
+ read_count = __libc_pread64 (infd, buf, to_read, *pinoff);
+ if (read_count == 0)
+ /* End of file reached prematurely. */
+ return copied;
+ if (read_count < 0)
+ {
+ if (copied > 0)
+ /* Report the number of bytes copied so far. */
+ return copied;
+ return -1;
+ }
+ if (pinoff != 0)
+ *pinoff += read_count;
+
+ /* Write the buffer part which was read to the destination. */
+ char *end = buf + read_count;
+ for (char *p = buf; p < end; )
+ {
+ ssize_t write_count;
+ if (poutoff == NULL)
+ write_count = write (outfd, p, end - p);
+ else
+ write_count = __libc_pwrite64 (outfd, p, end - p, *poutoff);
+ if (write_count == 0)
+ {
+ /* Assume that this means no space on the target file
+ system, and use the error handling path below. */
+ __set_errno (ENOSPC);
+ write_count = -1;
+ }
+ if (write_count < 0)
+ {
+ /* Adjust the input read position to match what we have
+ written, so that the caller can pick up after the
+ error. */
+ size_t written = p - buf;
+ size_t overread = read_count - written;
+ if (pinoff == NULL)
+ {
+ if (overread > 0)
+ {
+ /* We are on an error recovery path, so we
+ cannot deal with failure here. */
+ int save_errno = errno;
+ (void) __libc_lseek64 (infd, -overread, SEEK_CUR);
+ __set_errno (save_errno);
+ }
+ }
+ else /* pinoff != NULL */
+ *pinoff -= overread;
+
+ if (copied > 0)
+ /* Report the number of bytes copied so far. */
+ return copied + written;
+ return -1;
+ }
+ p += write_count;
+ if (poutoff != NULL)
+ *poutoff += write_count;
+ } /* Write loop. */
+
+ copied += read_count;
+ length -= read_count;
+ }
+ return copied;
+}
diff --git a/io/copy_file_range.c b/io/copy_file_range.c
new file mode 100644
index 0000000000..fdb6317619
--- /dev/null
+++ b/io/copy_file_range.c
@@ -0,0 +1,22 @@
+/* Generic implementation of copy_file_range.
+ Copyright (C) 2017 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, see
+ <http://www.gnu.org/licenses/>. */
+
+#define COPY_FILE_RANGE_DECL
+#define COPY_FILE_RANGE copy_file_range
+
+#include <io/copy_file_range-common.c>
diff --git a/io/tst-copy_file_range-compat.c b/io/tst-copy_file_range-compat.c
new file mode 100644
index 0000000000..873b6980b1
--- /dev/null
+++ b/io/tst-copy_file_range-compat.c
@@ -0,0 +1,30 @@
+/* Test the fallback implementation of copy_file_range.
+ Copyright (C) 2017 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, see
+ <http://www.gnu.org/licenses/>. */
+
+/* Get the declaration of the official copy_of_range function. */
+#include <unistd.h>
+
+/* Compile a local version of copy_file_range. */
+#define COPY_FILE_RANGE_DECL static
+#define COPY_FILE_RANGE copy_file_range_compat
+#include <io/copy_file_range-common.c>
+
+/* Re-use the test, but run it against copy_file_range_compat defined
+ above. */
+#define copy_file_range copy_file_range_compat
+#include "tst-copy_file_range.c"
diff --git a/io/tst-copy_file_range.c b/io/tst-copy_file_range.c
new file mode 100644
index 0000000000..af81d42817
--- /dev/null
+++ b/io/tst-copy_file_range.c
@@ -0,0 +1,729 @@
+/* Tests for copy_file_range.
+ Copyright (C) 2017 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, see
+ <http://www.gnu.org/licenses/>. */
+
+#include <array_length.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <libgen.h>
+#include <poll.h>
+#include <sched.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <support/check.h>
+#include <support/namespace.h>
+#include <support/support.h>
+#include <support/temp_file.h>
+#include <support/test-driver.h>
+#include <support/xunistd.h>
+#include <sys/mount.h>
+
+/* Boolean flags which indicate whether to use pointers with explicit
+ output flags. */
+static int do_inoff;
+static int do_outoff;
+
+/* Name and descriptors of the input files. Files are truncated and
+ reopened (with O_RDWR) between tests. */
+static char *infile;
+static int infd;
+static char *outfile;
+static int outfd;
+
+/* Like the above, but on a different file system. xdevfile can be
+ NULL if no suitable file system has been found. */
+static char *xdevfile;
+
+/* Input and output offsets. Set according to do_inoff and do_outoff
+ before the test. The offsets themselves are always set to
+ zero. */
+static off64_t inoff;
+static off64_t *pinoff;
+static off64_t outoff;
+static off64_t *poutoff;
+
+/* These are a collection of copy sizes used in tests. The selection
+ takes into account that the fallback implementation uses an
+ internal buffer of 8192 bytes. */
+enum { maximum_size = 99999 };
+static const int typical_sizes[] =
+ { 0, 1, 2, 3, 1024, 2048, 4096, 8191, 8192, 8193, 16383, 16384, 16385,
+ maximum_size };
+
+/* The random contents of this array can be used as a pattern to check
+ for correct write operations. */
+static unsigned char random_data[maximum_size];
+
+/* The size chosen by the test harness. */
+static int current_size;
+
+/* Discover the maximum file size (writable offset) for outfd.
+ Truncate outfd to length zero as a side effect. */
+static off64_t
+find_maximum_offset (void)
+{
+ xftruncate (outfd, 0);
+ static off64_t cache = 0;
+ if (cache != 0)
+ return cache;
+
+ uint64_t upper = -1;
+ upper >>= 1; /* Maximum of off64_t. */
+ TEST_VERIFY ((off64_t) upper > 0);
+ TEST_VERIFY ((off64_t) (upper + 1) < 0);
+ if (lseek (outfd, upper, SEEK_SET) >= 0)
+ {
+ if (write (outfd, "", 1) == 1)
+ FAIL_EXIT1 ("created a file larger than the off64_t range");
+ }
+
+ uint64_t lower = 1024 * 1024; /* A reasonable minimum file size. */
+ /* Loop invariant: writing at lower succeeds, writing at upper fails. */
+ while (lower + 1 < upper)
+ {
+ uint64_t middle = (lower + upper) / 2;
+ if (test_verbose > 0)
+ printf ("info: %s: remaining test range %" PRIu64 " .. %" PRIu64
+ ", probe at %" PRIu64 "\n", __func__, lower, upper, middle);
+ xftruncate (outfd, 0);
+ if (lseek (outfd, middle, SEEK_SET) >= 0
+ && write (outfd, "", 1) == 1)
+ lower = middle;
+ else
+ upper = middle;
+ }
+ TEST_VERIFY (lower + 1 == upper);
+ xftruncate (outfd, 0);
+ cache = lower;
+ printf ("info: maximum writable file offset: %" PRIu64 " (%" PRIx64 ")\n",
+ lower, lower);
+ return lower;
+}
+
+/* Perform a copy of a file. */
+static void
+simple_file_copy (void)
+{
+ xwrite (infd, random_data, current_size);
+
+ int length;
+ int in_skipped; /* Expected skipped bytes in input. */
+ if (do_inoff)
+ {
+ xlseek (infd, 1, SEEK_SET);
+ inoff = 2;
+ length = current_size - 3;
+ in_skipped = 2;
+ }
+ else
+ {
+ xlseek (infd, 3, SEEK_SET);
+ length = current_size - 5;
+ in_skipped = 3;
+ }
+ int out_skipped; /* Expected skipped bytes before the written data. */
+ if (do_outoff)
+ {
+ xlseek (outfd, 4, SEEK_SET);
+ outoff = 5;
+ out_skipped = 5;
+ }
+ else
+ {
+ xlseek (outfd, 6, SEEK_SET);
+ length = current_size - 6;
+ out_skipped = 6;
+ }
+ if (length < 0)
+ length = 0;
+
+ TEST_COMPARE (copy_file_range (infd, pinoff, outfd, poutoff,
+ length, 0), length);
+ if (do_inoff)
+ {
+ TEST_COMPARE (inoff, 2 + length);
+ TEST_COMPARE (xlseek (infd, 0, SEEK_CUR), 1);
+ }
+ else
+ TEST_COMPARE (xlseek (infd, 0, SEEK_CUR), 3 + length);
+ if (do_outoff)
+ {
+ TEST_COMPARE (outoff, 5 + length);
+ TEST_COMPARE (xlseek (outfd, 0, SEEK_CUR), 4);
+ }
+ else
+ TEST_COMPARE (xlseek (outfd, 0, SEEK_CUR), 6 + length);
+
+ struct stat64 st;
+ xfstat (outfd, &st);
+ if (length > 0)
+ TEST_COMPARE (st.st_size, out_skipped + length);
+ else
+ {
+ /* If we did not write anything, we also did not add any
+ padding. */
+ TEST_COMPARE (st.st_size, 0);
+ return;
+ }
+
+ xlseek (outfd, 0, SEEK_SET);
+ char *bytes = xmalloc (st.st_size);
+ TEST_COMPARE (read (outfd, bytes, st.st_size), st.st_size);
+ for (int i = 0; i < out_skipped; ++i)
+ TEST_COMPARE (bytes[i], 0);
+ TEST_VERIFY (memcmp (bytes + out_skipped, random_data + in_skipped,
+ length) == 0);
+ free (bytes);
+}
+
+/* Test that reading from a pipe willfails. */
+static void
+pipe_as_source (void)
+{
+ int pipefds[2];
+ xpipe (pipefds);
+
+ for (int length = 0; length < 2; ++length)
+ {
+ if (test_verbose > 0)
+ printf ("info: %s: length=%d\n", __func__, length);
+
+ /* Make sure that there is something to copy in the pipe. */
+ xwrite (pipefds[1], "@", 1);
+
+ TEST_COMPARE (copy_file_range (pipefds[0], pinoff, outfd, poutoff,
+ length, 0), -1);
+ /* Linux 4.10 and later return EINVAL. Older kernels return
+ EXDEV. */
+ TEST_VERIFY (errno == EINVAL || errno == EXDEV);
+ TEST_COMPARE (inoff, 0);
+ TEST_COMPARE (outoff, 0);
+ TEST_COMPARE (xlseek (outfd, 0, SEEK_CUR), 0);
+
+ /* Make sure that nothing was read. */
+ char buf = 'A';
+ TEST_COMPARE (read (pipefds[0], &buf, 1), 1);
+ TEST_COMPARE (buf, '@');
+ }
+
+ xclose (pipefds[0]);
+ xclose (pipefds[1]);
+}
+
+/* Test that writing to a pipe fails. */
+static void
+pipe_as_destination (void)
+{
+ /* Make sure that there is something to read in the input file. */
+ xwrite (infd, "abc", 3);
+ xlseek (infd, 0, SEEK_SET);
+
+ int pipefds[2];
+ xpipe (pipefds);
+
+ for (int length = 0; length < 2; ++length)
+ {
+ if (test_verbose > 0)
+ printf ("info: %s: length=%d\n", __func__, length);
+
+ TEST_COMPARE (copy_file_range (infd, pinoff, pipefds[1], poutoff,
+ length, 0), -1);
+ /* Linux 4.10 and later return EINVAL. Older kernels return
+ EXDEV. */
+ TEST_VERIFY (errno == EINVAL || errno == EXDEV);
+ TEST_COMPARE (inoff, 0);
+ TEST_COMPARE (outoff, 0);
+ TEST_COMPARE (xlseek (infd, 0, SEEK_CUR), 0);
+
+ /* Make sure that nothing was written. */
+ struct pollfd pollfd = { .fd = pipefds[0], .events = POLLIN, };
+ TEST_COMPARE (poll (&pollfd, 1, 0), 0);
+ }
+
+ xclose (pipefds[0]);
+ xclose (pipefds[1]);
+}
+
+/* Test a write failure after (potentially) writing some bytes. */
+static void
+delayed_write_failure (void)
+{
+ /* We need to write something to provoke the error. */
+ if (current_size == 0)
+ return;
+ xwrite (infd, random_data, sizeof (random_data));
+ xlseek (infd, 0, SEEK_SET);
+
+ /* Write failure near the start. */
+ off64_t where = find_maximum_offset ();
+ if (current_size == 1)
+ ++where;
+ outoff = where;
+ if (do_outoff)
+ xlseek (outfd, 1, SEEK_SET);
+ else
+ xlseek (outfd, where, SEEK_SET);
+ TEST_COMPARE (copy_file_range (infd, pinoff, outfd, poutoff,
+ sizeof (random_data), 0), -1);
+ TEST_COMPARE (errno, EINVAL);
+ TEST_COMPARE (xlseek (infd, 0, SEEK_CUR), 0);
+ if (do_outoff)
+ TEST_COMPARE (xlseek (outfd, 0, SEEK_CUR), 1);
+ else
+ TEST_COMPARE (xlseek (outfd, 0, SEEK_CUR), where);
+ TEST_COMPARE (outoff, where);
+ struct stat64 st;
+ xfstat (outfd, &st);
+ TEST_COMPARE (st.st_size, 0);
+
+ /* Write failure near the end. */
+ if (current_size == 1)
+ /* This would be same as the first test because there is only one
+ byte. */
+ return;
+ where = find_maximum_offset () - current_size + 1;
+ if (current_size == sizeof (random_data))
+ /* Otherwise we do not reach the non-writable byte. */
+ ++where;
+ outoff = where;
+ if (do_outoff)
+ xlseek (outfd, 1, SEEK_SET);
+ else
+ xlseek (outfd, where, SEEK_SET);
+ ssize_t ret = copy_file_range (infd, pinoff, outfd, poutoff,
+ sizeof (random_data), 0);
+ if (ret < 0)
+ {
+ TEST_COMPARE (ret, -1);
+ TEST_COMPARE (errno, EINVAL);
+ xfstat (outfd, &st);
+ TEST_COMPARE (st.st_size, 0);
+ }
+ else
+ {
+ /* The first copy succeeded. This happens in the emulation
+ because the internal buffer of limited size does not
+ necessarily cross the off64_t boundary on the first write
+ operation. */
+ if (test_verbose > 0)
+ printf ("info: copy_file_range (%zu) returned %zd\n",
+ sizeof (random_data), ret);
+ TEST_VERIFY (ret > 0);
+ TEST_VERIFY (ret < maximum_size);
+ xfstat (outfd, &st);
+ TEST_COMPARE (st.st_size, where + ret);
+ if (do_inoff)
+ {
+ TEST_COMPARE (inoff, ret);
+ TEST_COMPARE (xlseek (infd, 0, SEEK_CUR), 0);
+ }
+ else
+ TEST_COMPARE (xlseek (infd, 0, SEEK_CUR), ret);
+
+ char *buffer = xmalloc (ret);
+ TEST_COMPARE (pread (outfd, buffer, ret, where), ret);
+ TEST_VERIFY (memcmp (buffer, random_data, ret) == 0);
+ free (buffer);
+
+ /* The second copy fails. */
+ TEST_COMPARE (copy_file_range (infd, pinoff, outfd, poutoff,
+ sizeof (random_data), 0), -1);
+ TEST_COMPARE (errno, EINVAL);
+ }
+}
+
+/* Test a write failure across devices. */
+static void
+cross_device_failure (void)
+{
+ if (xdevfile == NULL)
+ /* Subtest not supported due to missing cross-device file. */
+ return;
+
+ /* We need something to write. */
+ xwrite (infd, random_data, sizeof (random_data));
+ xlseek (infd, 0, SEEK_SET);
+
+ int xdevfd = xopen (xdevfile, O_RDWR | O_LARGEFILE, 0);
+ TEST_COMPARE (copy_file_range (infd, pinoff, xdevfd, poutoff,
+ current_size, 0), -1);
+ TEST_COMPARE (errno, EXDEV);
+ TEST_COMPARE (xlseek (infd, 0, SEEK_CUR), 0);
+ struct stat64 st;
+ xfstat (xdevfd, &st);
+ TEST_COMPARE (st.st_size, 0);
+
+ xclose (xdevfd);
+}
+
+/* Try to exercise ENOSPC behavior with a tempfs file system (so that
+ we do not have to fill up a regular file system to get the error).
+ This function runs in a subprocess, so that we do not change the
+ mount namespace of the actual test process. */
+static void
+enospc_failure_1 (void *closure)
+{
+#ifdef CLONE_NEWNS
+ support_become_root ();
+
+ /* Make sure that we do not alter the file system mounts of the
+ parents. */
+ if (! support_enter_mount_namespace ())
+ {
+ printf ("warning: ENOSPC test skipped\n");
+ return;
+ }
+
+ char *mountpoint = closure;
+ if (mount ("none", mountpoint, "tmpfs", MS_NODEV | MS_NOEXEC,
+ "size=500k") != 0)
+ {
+ printf ("warning: could not mount tmpfs at %s: %m\n", mountpoint);
+ return;
+ }
+
+ /* The source file must reside on the same file system. */
+ char *intmpfsfile = xasprintf ("%s/%s", mountpoint, "in");
+ int intmpfsfd = xopen (intmpfsfile, O_RDWR | O_CREAT | O_LARGEFILE, 0600);
+ xwrite (intmpfsfd, random_data, sizeof (random_data));
+ xlseek (intmpfsfd, 1, SEEK_SET);
+ inoff = 1;
+
+ char *outtmpfsfile = xasprintf ("%s/%s", mountpoint, "out");
+ int outtmpfsfd = xopen (outtmpfsfile, O_RDWR | O_CREAT | O_LARGEFILE, 0600);
+
+ /* Fill the file with data until ENOSPC is reached. */
+ while (true)
+ {
+ ssize_t ret = write (outtmpfsfd, random_data, sizeof (random_data));
+ if (ret < 0 && errno != ENOSPC)
+ FAIL_EXIT1 ("write to %s: %m", outtmpfsfile);
+ if (ret < sizeof (random_data))
+ break;
+ }
+ TEST_COMPARE (write (outtmpfsfd, "", 1), -1);
+ TEST_COMPARE (errno, ENOSPC);
+ off64_t maxsize = xlseek (outtmpfsfd, 0, SEEK_CUR);
+ TEST_VERIFY_EXIT (maxsize > sizeof (random_data));
+
+ /* Constructed the expected file contents. */
+ char *expected = xmalloc (maxsize);
+ TEST_COMPARE (pread (outtmpfsfd, expected, maxsize, 0), maxsize);
+ /* Go back a little, so some bytes can be written. */
+ enum { offset = 20000 };
+ TEST_VERIFY_EXIT (offset < maxsize);
+ TEST_VERIFY_EXIT (offset < sizeof (random_data));
+ memcpy (expected + maxsize - offset, random_data + 1, offset);
+
+ if (do_outoff)
+ {
+ outoff = maxsize - offset;
+ xlseek (outtmpfsfd, 2, SEEK_SET);
+ }
+ else
+ xlseek (outtmpfsfd, -offset, SEEK_CUR);
+
+ /* First call is expected to succeed because we made room for some
+ bytes. */
+ TEST_COMPARE (copy_file_range (intmpfsfd, pinoff, outtmpfsfd, poutoff,
+ maximum_size, 0), offset);
+ if (do_inoff)
+ {
+ TEST_COMPARE (inoff, 1 + offset);
+ TEST_COMPARE (xlseek (intmpfsfd, 0, SEEK_CUR), 1);
+ }
+ else
+ TEST_COMPARE (xlseek (intmpfsfd, 0, SEEK_CUR), 1 + offset);
+ if (do_outoff)
+ {
+ TEST_COMPARE (outoff, maxsize);
+ TEST_COMPARE (xlseek (outtmpfsfd, 0, SEEK_CUR), 2);
+ }
+ else
+ TEST_COMPARE (xlseek (outtmpfsfd, 0, SEEK_CUR), maxsize);
+ struct stat64 st;
+ xfstat (outtmpfsfd, &st);
+ TEST_COMPARE (st.st_size, maxsize);
+ char *actual = xmalloc (st.st_size);
+ TEST_COMPARE (pread (outtmpfsfd, actual, st.st_size, 0), st.st_size);
+ TEST_VERIFY (memcmp (expected, actual, maxsize) == 0);
+
+ /* Second call should fail with ENOSPC. */
+ TEST_COMPARE (copy_file_range (intmpfsfd, pinoff, outtmpfsfd, poutoff,
+ maximum_size, 0), -1);
+ TEST_COMPARE (errno, ENOSPC);
+
+ /* Offsets should be unchanged. */
+ if (do_inoff)
+ {
+ TEST_COMPARE (inoff, 1 + offset);
+ TEST_COMPARE (xlseek (intmpfsfd, 0, SEEK_CUR), 1);
+ }
+ else
+ TEST_COMPARE (xlseek (intmpfsfd, 0, SEEK_CUR), 1 + offset);
+ if (do_outoff)
+ {
+ TEST_COMPARE (outoff, maxsize);
+ TEST_COMPARE (xlseek (outtmpfsfd, 0, SEEK_CUR), 2);
+ }
+ else
+ TEST_COMPARE (xlseek (outtmpfsfd, 0, SEEK_CUR), maxsize);
+ TEST_COMPARE (xlseek (outtmpfsfd, 0, SEEK_END), maxsize);
+ TEST_COMPARE (pread (outtmpfsfd, actual, maxsize, 0), maxsize);
+ TEST_VERIFY (memcmp (expected, actual, maxsize) == 0);
+
+ free (actual);
+ free (expected);
+
+ xclose (intmpfsfd);
+ xclose (outtmpfsfd);
+ free (intmpfsfile);
+ free (outtmpfsfile);
+
+#else /* !CLONE_NEWNS */
+ puts ("warning: ENOSPC test skipped (no mount namespaces)");
+#endif
+}
+
+/* Call enospc_failure_1 in a subprocess. */
+static void
+enospc_failure (void)
+{
+ char *mountpoint
+ = support_create_temp_directory ("tst-copy_file_range-enospc-");
+ support_isolate_in_subprocess (enospc_failure_1, mountpoint);
+ free (mountpoint);
+}
+
+/* The target file descriptor must have O_APPEND enabled. */
+static void
+oappend_failure (void)
+{
+ /* Add data, to make sure we do not fail because there is
+ insufficient input data. */
+ xwrite (infd, random_data, current_size);
+ xlseek (infd, 0, SEEK_SET);
+
+ xclose (outfd);
+ outfd = xopen (outfile, O_RDWR | O_APPEND, 0);
+ TEST_COMPARE (copy_file_range (infd, pinoff, outfd, poutoff,
+ current_size, 0), -1);
+ TEST_COMPARE (errno, EBADF);
+}
+
+/* Test that a short input file results in a shortened copy. */
+static void
+short_copy (void)
+{
+ if (current_size == 0)
+ /* Nothing to shorten. */
+ return;
+
+ /* Two subtests, one with offset 0 and current_size - 1 bytes, and
+ another one with current_size bytes, but offset 1. */
+ for (int shift = 0; shift < 2; ++shift)
+ {
+ if (test_verbose > 0)
+ printf ("info: shift=%d\n", shift);
+ xftruncate (infd, 0);
+ xlseek (infd, 0, SEEK_SET);
+ xwrite (infd, random_data, current_size - !shift);
+
+ if (do_inoff)
+ {
+ inoff = shift;
+ xlseek (infd, 2, SEEK_SET);
+ }
+ else
+ {
+ inoff = 3;
+ xlseek (infd, shift, SEEK_SET);
+ }
+ ftruncate (outfd, 0);
+ xlseek (outfd, 0, SEEK_SET);
+ outoff = 0;
+
+ /* First call copies current_size - 1 bytes. */
+ TEST_COMPARE (copy_file_range (infd, pinoff, outfd, poutoff,
+ current_size, 0), current_size - 1);
+ char *buffer = xmalloc (current_size);
+ TEST_COMPARE (pread (outfd, buffer, current_size, 0), current_size - 1);
+ TEST_VERIFY (memcmp (buffer, random_data + shift, current_size - 1)
+ == 0);
+ free (buffer);
+
+ if (do_inoff)
+ {
+ TEST_COMPARE (inoff, current_size - 1 + shift);
+ TEST_COMPARE (xlseek (infd, 0, SEEK_CUR), 2);
+ }
+ else
+ TEST_COMPARE (xlseek (infd, 0, SEEK_CUR), current_size - 1 + shift);
+ if (do_outoff)
+ {
+ TEST_COMPARE (outoff, current_size - 1);
+ TEST_COMPARE (xlseek (outfd, 0, SEEK_CUR), 0);
+ }
+ else
+ TEST_COMPARE (xlseek (outfd, 0, SEEK_CUR), current_size - 1);
+
+ /* First call copies zero bytes. */
+ TEST_COMPARE (copy_file_range (infd, pinoff, outfd, poutoff,
+ current_size, 0), 0);
+ /* And the offsets are unchanged. */
+ if (do_inoff)
+ {
+ TEST_COMPARE (inoff, current_size - 1 + shift);
+ TEST_COMPARE (xlseek (infd, 0, SEEK_CUR), 2);
+ }
+ else
+ TEST_COMPARE (xlseek (infd, 0, SEEK_CUR), current_size - 1 + shift);
+ if (do_outoff)
+ {
+ TEST_COMPARE (outoff, current_size - 1);
+ TEST_COMPARE (xlseek (outfd, 0, SEEK_CUR), 0);
+ }
+ else
+ TEST_COMPARE (xlseek (outfd, 0, SEEK_CUR), current_size - 1);
+ }
+}
+
+/* A named test function. */
+struct test_case
+{
+ const char *name;
+ void (*func) (void);
+ bool sizes; /* If true, call the test with different current_size values. */
+};
+
+/* The available test cases. */
+static struct test_case tests[] =
+ {
+ { "simple_file_copy", simple_file_copy, .sizes = true },
+ { "pipe_as_source", pipe_as_source, },
+ { "pipe_as_destination", pipe_as_destination, },
+ { "delayed_write_failure", delayed_write_failure, .sizes = true },
+ { "cross_device_failure", cross_device_failure, .sizes = true },
+ { "enospc_failure", enospc_failure, },
+ { "oappend_failure", oappend_failure, .sizes = true },
+ { "short_copy", short_copy, .sizes = true },
+ };
+
+static int
+do_test (void)
+{
+ for (unsigned char *p = random_data; p < array_end (random_data); ++p)
+ *p = rand () >> 24;
+
+ infd = create_temp_file ("tst-copy_file_range-in-", &infile);
+ xclose (create_temp_file ("tst-copy_file_range-out-", &outfile));
+
+ /* Try to find a different directory from the default input/output
+ file. */
+ {
+ struct stat64 instat;
+ xfstat (infd, &instat);
+ static const char *const candidates[] =
+ { NULL, "/var/tmp", "/dev/shm" };
+ for (const char *const *c = candidates; c < array_end (candidates); ++c)
+ {
+ const char *path = *c;
+ char *to_free = NULL;
+ if (path == NULL)
+ {
+ to_free = xreadlink ("/proc/self/exe");
+ path = dirname (to_free);
+ }
+
+ struct stat64 cstat;
+ xstat (path, &cstat);
+ if (cstat.st_dev == instat.st_dev)
+ {
+ free (to_free);
+ continue;
+ }
+
+ printf ("info: using alternate temporary files directory: %s\n", path);
+ xdevfile = xasprintf ("%s/tst-copy_file_range-xdev-XXXXXX", path);
+ free (to_free);
+ break;
+ }
+ if (xdevfile != NULL)
+ {
+ int xdevfd = mkstemp (xdevfile);
+ if (xdevfd < 0)
+ FAIL_EXIT1 ("mkstemp (\"%s\"): %m", xdevfile);
+ struct stat64 xdevst;
+ xfstat (xdevfd, &xdevst);
+ TEST_VERIFY (xdevst.st_dev != instat.st_dev);
+ add_temp_file (xdevfile);
+ xclose (xdevfd);
+ }
+ else
+ puts ("warning: no alternate directory on different file system found");
+ }
+ xclose (infd);
+
+ for (do_inoff = 0; do_inoff < 2; ++do_inoff)
+ for (do_outoff = 0; do_outoff < 2; ++do_outoff)
+ for (struct test_case *test = tests; test < array_end (tests); ++test)
+ for (const int *size = typical_sizes;
+ size < array_end (typical_sizes); ++size)
+ {
+ current_size = *size;
+ if (test_verbose > 0)
+ printf ("info: %s do_inoff=%d do_outoff=%d current_size=%d\n",
+ test->name, do_inoff, do_outoff, current_size);
+
+ inoff = 0;
+ if (do_inoff)
+ pinoff = &inoff;
+ else
+ pinoff = NULL;
+ outoff = 0;
+ if (do_outoff)
+ poutoff = &outoff;
+ else
+ poutoff = NULL;
+
+ infd = xopen (infile, O_RDWR | O_LARGEFILE, 0);
+ xftruncate (infd, 0);
+ outfd = xopen (outfile, O_RDWR | O_LARGEFILE, 0);
+ xftruncate (outfd, 0);
+
+ test->func ();
+
+ xclose (infd);
+ xclose (outfd);
+
+ if (!test->sizes)
+ /* Skip the other sizes unless they have been
+ requested. */
+ break;
+ }
+
+ free (infile);
+ free (outfile);
+ free (xdevfile);
+
+ return 0;
+}
+
+#include <support/test-driver.c>
diff --git a/manual/llio.texi b/manual/llio.texi
index 825fd94e32..d56dadae12 100644
--- a/manual/llio.texi
+++ b/manual/llio.texi
@@ -41,6 +41,7 @@ directly.)
* Stream/Descriptor Precautions:: Precautions needed if you use both
descriptors and streams.
* Scatter-Gather:: Fast I/O to discontinuous buffers.
+* Copying File Data:: Copying data between files.
* Memory-mapped I/O:: Using files like memory.
* Waiting for I/O:: How to check for input or output
on multiple file descriptors.
@@ -1363,6 +1364,80 @@ may be easier to use than these functions. However, @code{readv} and
(as opposed to the total output), are large. In that case, a high-level
stream would not be able to cache the data efficiently.
+@node Copying File Data
+@section Copying data between two files
+@cindex copying files
+@cindex file copy
+
+A special function is provided to copy data between two files on the
+same file system. The system can optimize such copy operations. This
+is particularly important on network file systems, where the data would
+otherwise have to be transferred twice over the network.
+
+Note that this function only copies file data, but not metadata such as
+file permissions or extended attributes.
+
+@deftypefun ssize_t copy_file_range (int @var{inputfd}, off64_t *@var{inputpos}, int @var{outputfd}, off64_t *@var{outputpos}, ssize_t @var{length}, unsigned int @var{flags})
+@standards{GNU, unistd.h}
+@safety{@prelim{}@mtsafe{}@assafe{}@acsafe{}}
+
+This function copies up to @var{length} bytes from the file descriptor
+@var{inputfd} to the file descriptor @var{outputfd}.
+
+The function can operate on both the current file position (like
+@code{read} and @code{write}) and an explicit offset (like @code{pread}
+and @code{pwrite}). If the @var{inputpos} pointer is null, the file
+position of @var{inputfd} is used as the starting point of the copy
+operation, and the file position is advanced during it. If
+@var{inputpos} is not null, then @code{*@var{inputpos}} is used as the
+starting point of the copy operation, and @code{*@var{inputpos}} is
+incremented by the number of copied bytes, but the file position remains
+unchanged. Similar rules apply to @var{outputfd} and @var{outputpos}
+for the output file position.
+
+The @var{flags} argument is currently reserved and must be zero.
+
+The @code{copy_file_range} function returns the number of bytes copied.
+This can be less than the specified @var{length} in case the input file
+contains fewer remaining bytes than @var{length}, or if a read or write
+failure occurs. The return value is zero if the end of the input file
+is encountered immediately.
+
+If no bytes can be copied, to report an error, @code{copy_file_range}
+returns the value @math{-1} and sets @code{errno}. The following
+@code{errno} error conditions are specific to this function.
+
+@table @code
+@item EISDIR
+At least one of the descriptors @var{inputfd} or @var{outputfd} refers
+to a directory.
+
+@item EINVAL
+At least one of the descriptors @var{inputfd} or @var{outputfd} refers
+to a non-regular, non-directory file (such as a socket or a FIFO).
+
+The @var{flags} argument is not zero.
+
+@item EBADF
+The argument @var{inputfd} is not a valid file descriptor open for
+reading.
+
+The argument @var{outputfd} is not a valid file descriptor open for
+writing, or @var{outputfd} has been opened with @code{O_APPEND}.
+
+@item EXDEV
+The input and output files reside on different file systems.
+@end table
+
+In addition, @code{copy_file_range} can result with the error codes
+which are used by @code{read}, @code{pread}, @code{write}, and
+@code{pwrite}.
+
+The @code{copy_file_range} is a cancellation point. In case of
+cancellation, the input location (the file position or the value at
+@code{@var{inputpos}}) is indeterminate.
+@end deftypefun
+
@node Memory-mapped I/O
@section Memory-mapped I/O
diff --git a/posix/unistd.h b/posix/unistd.h
index 32b0f4898f..65317c79fd 100644
--- a/posix/unistd.h
+++ b/posix/unistd.h
@@ -1105,7 +1105,12 @@ extern int lockf64 (int __fd, int __cmd, __off64_t __len) __wur;
do __result = (long int) (expression); \
while (__result == -1L && errno == EINTR); \
__result; }))
-#endif
+
+/* Copy LENGTH bytes from INFD to OUTFD. */
+ssize_t copy_file_range (int __infd, __off64_t *__pinoff,
+ int __outfd, __off64_t *__poutoff,
+ size_t __length, unsigned int __flags);
+#endif /* __USE_GNU */
#if defined __USE_POSIX199309 || defined __USE_UNIX98
/* Synchronize at least the data part of a file with the underlying
diff --git a/support/Makefile b/support/Makefile
index b2ea371135..f5a2344981 100644
--- a/support/Makefile
+++ b/support/Makefile
@@ -36,12 +36,14 @@ libsupport-routines = \
oom_error \
resolv_test \
set_fortify_handler \
+ support-xfstat \
support-xstat \
support_become_root \
support_can_chroot \
support_capture_subprocess \
support_capture_subprocess_check \
support_chroot \
+ support_enter_mount_namespace \
support_enter_network_namespace \
support_format_address_family \
support_format_addrinfo \
@@ -72,8 +74,10 @@ libsupport-routines = \
xfclose \
xfopen \
xfork \
+ xftruncate \
xgetsockname \
xlisten \
+ xlseek \
xmalloc \
xmemstream \
xmkdir \
diff --git a/support/namespace.h b/support/namespace.h
index 9eddb1a0e9..b5e2d1474a 100644
--- a/support/namespace.h
+++ b/support/namespace.h
@@ -51,6 +51,11 @@ bool support_can_chroot (void);
has sufficient privileges. */
bool support_enter_network_namespace (void);
+/* Enter a mount namespace and mark / as private (not shared). If
+ this function returns true, mount operations in this process will
+ not affect the host system afterwards. */
+bool support_enter_mount_namespace (void);
+
/* Return true if support_enter_network_namespace managed to enter a
UTS namespace. */
bool support_in_uts_namespace (void);
diff --git a/support/support-xfstat.c b/support/support-xfstat.c
new file mode 100644
index 0000000000..4c8ee9142b
--- /dev/null
+++ b/support/support-xfstat.c
@@ -0,0 +1,28 @@
+/* fstat64 with error checking.
+ Copyright (C) 2017 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, see
+ <http://www.gnu.org/licenses/>. */
+
+#include <support/check.h>
+#include <support/xunistd.h>
+#include <sys/stat.h>
+
+void
+xfstat (int fd, struct stat64 *result)
+{
+ if (fstat64 (fd, result) != 0)
+ FAIL_EXIT1 ("fstat64 (%d): %m", fd);
+}
diff --git a/support/support_enter_mount_namespace.c b/support/support_enter_mount_namespace.c
new file mode 100644
index 0000000000..6140692075
--- /dev/null
+++ b/support/support_enter_mount_namespace.c
@@ -0,0 +1,45 @@
+/* Enter a mount namespace.
+ Copyright (C) 2017 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, see
+ <http://www.gnu.org/licenses/>. */
+
+#include <support/namespace.h>
+
+#include <sched.h>
+#include <stdio.h>
+#include <sys/mount.h>
+
+bool
+support_enter_mount_namespace (void)
+{
+#ifdef CLONE_NEWNS
+ if (unshare (CLONE_NEWNS) == 0)
+ {
+ /* On some systems, / is marked as MS_SHARED, which means that
+ mounts within the namespace leak to the rest of the system,
+ which is not what we want. */
+ if (mount ("none", "/", NULL, MS_REC | MS_PRIVATE, NULL) != 0)
+ {
+ printf ("warning: making the mount namespace private failed: %m\n");
+ return false;
+ }
+ return true;
+ }
+ else
+ printf ("warning: unshare (CLONE_NEWNS) failed: %m\n");
+#endif /* CLONE_NEWNS */
+ return false;
+}
diff --git a/support/xftruncate.c b/support/xftruncate.c
new file mode 100644
index 0000000000..9c4e9e3050
--- /dev/null
+++ b/support/xftruncate.c
@@ -0,0 +1,27 @@
+/* ftruncate with error checking.
+ Copyright (C) 2017 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, see
+ <http://www.gnu.org/licenses/>. */
+
+#include <support/check.h>
+#include <support/xunistd.h>
+
+void
+xftruncate (int fd, long long length)
+{
+ if (ftruncate64 (fd, length) != 0)
+ FAIL_EXIT1 ("ftruncate64 (%d, %lld): %m", fd, length);
+}
diff --git a/support/xlseek.c b/support/xlseek.c
new file mode 100644
index 0000000000..0a75a9f2e6
--- /dev/null
+++ b/support/xlseek.c
@@ -0,0 +1,29 @@
+/* lseek with error checking.
+ Copyright (C) 2017 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, see
+ <http://www.gnu.org/licenses/>. */
+
+#include <support/check.h>
+#include <support/xunistd.h>
+
+long long
+xlseek (int fd, long long offset, int whence)
+{
+ long long result = lseek64 (fd, offset, whence);
+ if (result < 0)
+ FAIL_EXIT1 ("lseek64 (%d, %lld, %d): %m", fd, offset, whence);
+ return result;
+}
diff --git a/support/xunistd.h b/support/xunistd.h
index 00376f7aae..29da063c15 100644
--- a/support/xunistd.h
+++ b/support/xunistd.h
@@ -36,10 +36,13 @@ void xpipe (int[2]);
void xdup2 (int, int);
int xopen (const char *path, int flags, mode_t);
void xstat (const char *path, struct stat64 *);
+void xfstat (int fd, struct stat64 *);
void xmkdir (const char *path, mode_t);
void xchroot (const char *path);
void xunlink (const char *path);
long xsysconf (int name);
+long long xlseek (int fd, long long offset, int whence);
+void xftruncate (int fd, long long length);
/* Read the link at PATH. The caller should free the returned string
with free. */
diff --git a/sysdeps/unix/sysv/linux/aarch64/libc.abilist b/sysdeps/unix/sysv/linux/aarch64/libc.abilist
index 85788be12b..6907889239 100644
--- a/sysdeps/unix/sysv/linux/aarch64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/aarch64/libc.abilist
@@ -2104,6 +2104,7 @@ GLIBC_2.26 pwritev2 F
GLIBC_2.26 pwritev64v2 F
GLIBC_2.26 reallocarray F
GLIBC_2.27 GLIBC_2.27 A
+GLIBC_2.27 copy_file_range F
GLIBC_2.27 glob F
GLIBC_2.27 glob64 F
GLIBC_2.27 memfd_create F
diff --git a/sysdeps/unix/sysv/linux/alpha/libc.abilist b/sysdeps/unix/sysv/linux/alpha/libc.abilist
index 3b463dacbe..de78d30d90 100644
--- a/sysdeps/unix/sysv/linux/alpha/libc.abilist
+++ b/sysdeps/unix/sysv/linux/alpha/libc.abilist
@@ -2015,6 +2015,7 @@ GLIBC_2.26 pwritev2 F
GLIBC_2.26 pwritev64v2 F
GLIBC_2.26 reallocarray F
GLIBC_2.27 GLIBC_2.27 A
+GLIBC_2.27 copy_file_range F
GLIBC_2.27 glob F
GLIBC_2.27 glob64 F
GLIBC_2.27 memfd_create F
diff --git a/sysdeps/unix/sysv/linux/arm/libc.abilist b/sysdeps/unix/sysv/linux/arm/libc.abilist
index a1315aef35..7b0c7afc23 100644
--- a/sysdeps/unix/sysv/linux/arm/libc.abilist
+++ b/sysdeps/unix/sysv/linux/arm/libc.abilist
@@ -105,6 +105,7 @@ GLIBC_2.26 pwritev2 F
GLIBC_2.26 pwritev64v2 F
GLIBC_2.26 reallocarray F
GLIBC_2.27 GLIBC_2.27 A
+GLIBC_2.27 copy_file_range F
GLIBC_2.27 glob F
GLIBC_2.27 glob64 F
GLIBC_2.27 memfd_create F
diff --git a/sysdeps/unix/sysv/linux/copy_file_range.c b/sysdeps/unix/sysv/linux/copy_file_range.c
new file mode 100644
index 0000000000..6bb301dfef
--- /dev/null
+++ b/sysdeps/unix/sysv/linux/copy_file_range.c
@@ -0,0 +1,46 @@
+/* Linux implementation of copy_file_range.
+ Copyright (C) 2017 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, see
+ <http://www.gnu.org/licenses/>. */
+
+#include <errno.h>
+#include <sysdep-cancel.h>
+#include <unistd.h>
+
+/* Include the fallback implementation. */
+#ifndef __ASSUME_COPY_FILE_RANGE
+#define COPY_FILE_RANGE_DECL static
+#define COPY_FILE_RANGE copy_file_range_compat
+#include <io/copy_file_range-common.c>
+#endif
+
+ssize_t
+copy_file_range (int infd, __off64_t *pinoff,
+ int outfd, __off64_t *poutoff,
+ size_t length, unsigned int flags)
+{
+#ifdef __NR_copy_file_range
+ ssize_t ret = SYSCALL_CANCEL (copy_file_range, infd, pinoff, outfd, poutoff,
+ length, flags);
+# ifndef __ASSUME_COPY_FILE_RANGE
+ if (ret == -1 && errno == ENOSYS)
+ ret = copy_file_range_compat (infd, pinoff, outfd, poutoff, length, flags);
+# endif
+ return ret;
+#else /* !__NR_copy_file_range */
+ return copy_file_range_compat (infd, pinoff, outfd, poutoff, length, flags);
+#endif
+}
diff --git a/sysdeps/unix/sysv/linux/hppa/libc.abilist b/sysdeps/unix/sysv/linux/hppa/libc.abilist
index 7397d728f2..110ff052a6 100644
--- a/sysdeps/unix/sysv/linux/hppa/libc.abilist
+++ b/sysdeps/unix/sysv/linux/hppa/libc.abilist
@@ -1869,6 +1869,7 @@ GLIBC_2.26 pwritev2 F
GLIBC_2.26 pwritev64v2 F
GLIBC_2.26 reallocarray F
GLIBC_2.27 GLIBC_2.27 A
+GLIBC_2.27 copy_file_range F
GLIBC_2.27 glob F
GLIBC_2.27 glob64 F
GLIBC_2.27 memfd_create F
diff --git a/sysdeps/unix/sysv/linux/i386/libc.abilist b/sysdeps/unix/sysv/linux/i386/libc.abilist
index cffdf251d6..0d7aca5d39 100644
--- a/sysdeps/unix/sysv/linux/i386/libc.abilist
+++ b/sysdeps/unix/sysv/linux/i386/libc.abilist
@@ -2034,6 +2034,7 @@ GLIBC_2.26 strtof128_l F
GLIBC_2.26 wcstof128 F
GLIBC_2.26 wcstof128_l F
GLIBC_2.27 GLIBC_2.27 A
+GLIBC_2.27 copy_file_range F
GLIBC_2.27 glob F
GLIBC_2.27 glob64 F
GLIBC_2.27 memfd_create F
diff --git a/sysdeps/unix/sysv/linux/ia64/libc.abilist b/sysdeps/unix/sysv/linux/ia64/libc.abilist
index 3292510a55..a7a740aa1a 100644
--- a/sysdeps/unix/sysv/linux/ia64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/ia64/libc.abilist
@@ -1898,6 +1898,7 @@ GLIBC_2.26 strtof128_l F
GLIBC_2.26 wcstof128 F
GLIBC_2.26 wcstof128_l F
GLIBC_2.27 GLIBC_2.27 A
+GLIBC_2.27 copy_file_range F
GLIBC_2.27 glob F
GLIBC_2.27 glob64 F
GLIBC_2.27 memfd_create F
diff --git a/sysdeps/unix/sysv/linux/kernel-features.h b/sysdeps/unix/sysv/linux/kernel-features.h
index 2e1fe6597a..ac2d5412e7 100644
--- a/sysdeps/unix/sysv/linux/kernel-features.h
+++ b/sysdeps/unix/sysv/linux/kernel-features.h
@@ -107,3 +107,8 @@
#if __LINUX_KERNEL_VERSION >= 0x031300
# define __ASSUME_EXECVEAT 1
#endif
+
+/* Support for the copy_file_range syscall was added in 4.5. */
+#if __LINUX_KERNEL_VERSION >= 0x040500
+# define __ASSUME_COPY_FILE_RANGE 1
+#endif
diff --git a/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist b/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist
index 636bbdd1a7..06b195f548 100644
--- a/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist
+++ b/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist
@@ -106,6 +106,7 @@ GLIBC_2.26 pwritev2 F
GLIBC_2.26 pwritev64v2 F
GLIBC_2.26 reallocarray F
GLIBC_2.27 GLIBC_2.27 A
+GLIBC_2.27 copy_file_range F
GLIBC_2.27 glob F
GLIBC_2.27 glob64 F
GLIBC_2.27 memfd_create F
diff --git a/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist b/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist
index 6952863f86..4bc674f5fb 100644
--- a/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist
+++ b/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist
@@ -1983,6 +1983,7 @@ GLIBC_2.26 pwritev2 F
GLIBC_2.26 pwritev64v2 F
GLIBC_2.26 reallocarray F
GLIBC_2.27 GLIBC_2.27 A
+GLIBC_2.27 copy_file_range F
GLIBC_2.27 glob F
GLIBC_2.27 glob64 F
GLIBC_2.27 memfd_create F
diff --git a/sysdeps/unix/sysv/linux/microblaze/libc.abilist b/sysdeps/unix/sysv/linux/microblaze/libc.abilist
index ac5b56abab..ea4c597c56 100644
--- a/sysdeps/unix/sysv/linux/microblaze/libc.abilist
+++ b/sysdeps/unix/sysv/linux/microblaze/libc.abilist
@@ -2104,6 +2104,7 @@ GLIBC_2.26 pwritev2 F
GLIBC_2.26 pwritev64v2 F
GLIBC_2.26 reallocarray F
GLIBC_2.27 GLIBC_2.27 A
+GLIBC_2.27 copy_file_range F
GLIBC_2.27 glob F
GLIBC_2.27 glob64 F
GLIBC_2.27 memfd_create F
diff --git a/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist
index bb0958e842..c0cb2ea16d 100644
--- a/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist
+++ b/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist
@@ -1958,6 +1958,7 @@ GLIBC_2.26 pwritev2 F
GLIBC_2.26 pwritev64v2 F
GLIBC_2.26 reallocarray F
GLIBC_2.27 GLIBC_2.27 A
+GLIBC_2.27 copy_file_range F
GLIBC_2.27 glob F
GLIBC_2.27 glob64 F
GLIBC_2.27 memfd_create F
diff --git a/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist
index 9104eb4d6d..1afb7b7544 100644
--- a/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist
+++ b/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist
@@ -1956,6 +1956,7 @@ GLIBC_2.26 pwritev2 F
GLIBC_2.26 pwritev64v2 F
GLIBC_2.26 reallocarray F
GLIBC_2.27 GLIBC_2.27 A
+GLIBC_2.27 copy_file_range F
GLIBC_2.27 glob F
GLIBC_2.27 glob64 F
GLIBC_2.27 memfd_create F
diff --git a/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist
index 58a5d5e141..b093d879ab 100644
--- a/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist
+++ b/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist
@@ -1954,6 +1954,7 @@ GLIBC_2.26 pwritev2 F
GLIBC_2.26 pwritev64v2 F
GLIBC_2.26 reallocarray F
GLIBC_2.27 GLIBC_2.27 A
+GLIBC_2.27 copy_file_range F
GLIBC_2.27 glob F
GLIBC_2.27 glob64 F
GLIBC_2.27 memfd_create F
diff --git a/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist
index 2efac14a7d..11c7fc3242 100644
--- a/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist
@@ -1949,6 +1949,7 @@ GLIBC_2.26 pwritev2 F
GLIBC_2.26 pwritev64v2 F
GLIBC_2.26 reallocarray F
GLIBC_2.27 GLIBC_2.27 A
+GLIBC_2.27 copy_file_range F
GLIBC_2.27 glob F
GLIBC_2.27 glob64 F
GLIBC_2.27 memfd_create F
diff --git a/sysdeps/unix/sysv/linux/nios2/libc.abilist b/sysdeps/unix/sysv/linux/nios2/libc.abilist
index 9ef29e4e98..94933fd4dc 100644
--- a/sysdeps/unix/sysv/linux/nios2/libc.abilist
+++ b/sysdeps/unix/sysv/linux/nios2/libc.abilist
@@ -2145,6 +2145,7 @@ GLIBC_2.26 pwritev2 F
GLIBC_2.26 pwritev64v2 F
GLIBC_2.26 reallocarray F
GLIBC_2.27 GLIBC_2.27 A
+GLIBC_2.27 copy_file_range F
GLIBC_2.27 glob F
GLIBC_2.27 glob64 F
GLIBC_2.27 memfd_create F
diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist
index 60c024096f..7bb7b67dd1 100644
--- a/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist
+++ b/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist
@@ -1987,6 +1987,7 @@ GLIBC_2.26 pwritev2 F
GLIBC_2.26 pwritev64v2 F
GLIBC_2.26 reallocarray F
GLIBC_2.27 GLIBC_2.27 A
+GLIBC_2.27 copy_file_range F
GLIBC_2.27 glob F
GLIBC_2.27 glob64 F
GLIBC_2.27 memfd_create F
diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist
index 327933c973..f58fe26fe2 100644
--- a/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist
+++ b/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist
@@ -1992,6 +1992,7 @@ GLIBC_2.26 pwritev2 F
GLIBC_2.26 pwritev64v2 F
GLIBC_2.26 reallocarray F
GLIBC_2.27 GLIBC_2.27 A
+GLIBC_2.27 copy_file_range F
GLIBC_2.27 glob F
GLIBC_2.27 glob64 F
GLIBC_2.27 memfd_create F
diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc64/libc-le.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc64/libc-le.abilist
index b04c31bc10..9ab82af581 100644
--- a/sysdeps/unix/sysv/linux/powerpc/powerpc64/libc-le.abilist
+++ b/sysdeps/unix/sysv/linux/powerpc/powerpc64/libc-le.abilist
@@ -2199,6 +2199,7 @@ GLIBC_2.26 strtof128_l F
GLIBC_2.26 wcstof128 F
GLIBC_2.26 wcstof128_l F
GLIBC_2.27 GLIBC_2.27 A
+GLIBC_2.27 copy_file_range F
GLIBC_2.27 glob F
GLIBC_2.27 glob64 F
GLIBC_2.27 memfd_create F
diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc64/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc64/libc.abilist
index e0645e9e25..73f3dac107 100644
--- a/sysdeps/unix/sysv/linux/powerpc/powerpc64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/powerpc/powerpc64/libc.abilist
@@ -106,6 +106,7 @@ GLIBC_2.26 pwritev2 F
GLIBC_2.26 pwritev64v2 F
GLIBC_2.26 reallocarray F
GLIBC_2.27 GLIBC_2.27 A
+GLIBC_2.27 copy_file_range F
GLIBC_2.27 glob F
GLIBC_2.27 glob64 F
GLIBC_2.27 memfd_create F
diff --git a/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist b/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist
index ef434c61a7..69747dc09e 100644
--- a/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist
+++ b/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist
@@ -1987,6 +1987,7 @@ GLIBC_2.26 pwritev2 F
GLIBC_2.26 pwritev64v2 F
GLIBC_2.26 reallocarray F
GLIBC_2.27 GLIBC_2.27 A
+GLIBC_2.27 copy_file_range F
GLIBC_2.27 glob F
GLIBC_2.27 glob64 F
GLIBC_2.27 memfd_create F
diff --git a/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist b/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist
index 4114a4ce57..7121b9d7d4 100644
--- a/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist
@@ -1888,6 +1888,7 @@ GLIBC_2.26 pwritev2 F
GLIBC_2.26 pwritev64v2 F
GLIBC_2.26 reallocarray F
GLIBC_2.27 GLIBC_2.27 A
+GLIBC_2.27 copy_file_range F
GLIBC_2.27 glob F
GLIBC_2.27 glob64 F
GLIBC_2.27 memfd_create F
diff --git a/sysdeps/unix/sysv/linux/sh/libc.abilist b/sysdeps/unix/sysv/linux/sh/libc.abilist
index f4478b0cc5..c18d88f713 100644
--- a/sysdeps/unix/sysv/linux/sh/libc.abilist
+++ b/sysdeps/unix/sysv/linux/sh/libc.abilist
@@ -1873,6 +1873,7 @@ GLIBC_2.26 pwritev2 F
GLIBC_2.26 pwritev64v2 F
GLIBC_2.26 reallocarray F
GLIBC_2.27 GLIBC_2.27 A
+GLIBC_2.27 copy_file_range F
GLIBC_2.27 glob F
GLIBC_2.27 glob64 F
GLIBC_2.27 memfd_create F
diff --git a/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist b/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist
index 136a57fc0e..04e5483280 100644
--- a/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist
+++ b/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist
@@ -1980,6 +1980,7 @@ GLIBC_2.26 pwritev2 F
GLIBC_2.26 pwritev64v2 F
GLIBC_2.26 reallocarray F
GLIBC_2.27 GLIBC_2.27 A
+GLIBC_2.27 copy_file_range F
GLIBC_2.27 glob F
GLIBC_2.27 glob64 F
GLIBC_2.27 memfd_create F
diff --git a/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist b/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist
index 9ad0790829..f68b068c25 100644
--- a/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist
@@ -1917,6 +1917,7 @@ GLIBC_2.26 pwritev2 F
GLIBC_2.26 pwritev64v2 F
GLIBC_2.26 reallocarray F
GLIBC_2.27 GLIBC_2.27 A
+GLIBC_2.27 copy_file_range F
GLIBC_2.27 glob F
GLIBC_2.27 glob64 F
GLIBC_2.27 memfd_create F
diff --git a/sysdeps/unix/sysv/linux/tile/tilegx/tilegx32/libc.abilist b/sysdeps/unix/sysv/linux/tile/tilegx/tilegx32/libc.abilist
index d4f2094027..019814ed5b 100644
--- a/sysdeps/unix/sysv/linux/tile/tilegx/tilegx32/libc.abilist
+++ b/sysdeps/unix/sysv/linux/tile/tilegx/tilegx32/libc.abilist
@@ -2111,6 +2111,7 @@ GLIBC_2.26 pwritev2 F
GLIBC_2.26 pwritev64v2 F
GLIBC_2.26 reallocarray F
GLIBC_2.27 GLIBC_2.27 A
+GLIBC_2.27 copy_file_range F
GLIBC_2.27 glob F
GLIBC_2.27 glob64 F
GLIBC_2.27 memfd_create F
diff --git a/sysdeps/unix/sysv/linux/tile/tilegx/tilegx64/libc.abilist b/sysdeps/unix/sysv/linux/tile/tilegx/tilegx64/libc.abilist
index 4916dbabb5..3eac5368db 100644
--- a/sysdeps/unix/sysv/linux/tile/tilegx/tilegx64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/tile/tilegx/tilegx64/libc.abilist
@@ -2111,6 +2111,7 @@ GLIBC_2.26 pwritev2 F
GLIBC_2.26 pwritev64v2 F
GLIBC_2.26 reallocarray F
GLIBC_2.27 GLIBC_2.27 A
+GLIBC_2.27 copy_file_range F
GLIBC_2.27 glob F
GLIBC_2.27 glob64 F
GLIBC_2.27 memfd_create F
diff --git a/sysdeps/unix/sysv/linux/tile/tilepro/libc.abilist b/sysdeps/unix/sysv/linux/tile/tilepro/libc.abilist
index d4f2094027..019814ed5b 100644
--- a/sysdeps/unix/sysv/linux/tile/tilepro/libc.abilist
+++ b/sysdeps/unix/sysv/linux/tile/tilepro/libc.abilist
@@ -2111,6 +2111,7 @@ GLIBC_2.26 pwritev2 F
GLIBC_2.26 pwritev64v2 F
GLIBC_2.26 reallocarray F
GLIBC_2.27 GLIBC_2.27 A
+GLIBC_2.27 copy_file_range F
GLIBC_2.27 glob F
GLIBC_2.27 glob64 F
GLIBC_2.27 memfd_create F
diff --git a/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist b/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist
index 1ea74f9e8c..472d0fa013 100644
--- a/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist
@@ -1875,6 +1875,7 @@ GLIBC_2.26 strtof128_l F
GLIBC_2.26 wcstof128 F
GLIBC_2.26 wcstof128_l F
GLIBC_2.27 GLIBC_2.27 A
+GLIBC_2.27 copy_file_range F
GLIBC_2.27 glob F
GLIBC_2.27 glob64 F
GLIBC_2.27 memfd_create F
diff --git a/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist b/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist
index 1d3d598618..27b027aecc 100644
--- a/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist
+++ b/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist
@@ -2118,6 +2118,7 @@ GLIBC_2.26 strtof128_l F
GLIBC_2.26 wcstof128 F
GLIBC_2.26 wcstof128_l F
GLIBC_2.27 GLIBC_2.27 A
+GLIBC_2.27 copy_file_range F
GLIBC_2.27 glob F
GLIBC_2.27 glob64 F
GLIBC_2.27 memfd_create F
^ permalink raw reply [flat|nested] 24+ messages in thread
* Re: [PATCH] copy_file_range: New function to copy file data
2017-11-17 14:56 [PATCH] copy_file_range: New function to copy file data Florian Weimer
@ 2017-11-17 14:58 ` Florian Weimer
2017-11-19 3:13 ` Rical Jasan
2017-11-19 3:11 ` Rical Jasan
` (2 subsequent siblings)
3 siblings, 1 reply; 24+ messages in thread
From: Florian Weimer @ 2017-11-17 14:58 UTC (permalink / raw)
To: libc-alpha
On 11/17/2017 03:56 PM, Florian Weimer wrote:
> * io/tst-copy_file_range.c: Likewise.
The test depends on TEST_COMPARE from the MPK patch.
> * support/Makefile (libsupport-routines): Add support-xfstat,
> support_enter_mount_namespace, xftruncate, xlseek.
> * support/namespace.h (support_enter_mount_namespace): Declare.
> * support/support-xfstat.c: New file.
> * support/support_enter_mount_namespace.c: Likewise.
> * support/xftruncate.c: Likewise.
> * support/xlseek.c: Likewise.
As usual, I can commit the support/ additions separately if this is
desirable.
Thanks,
Florian
^ permalink raw reply [flat|nested] 24+ messages in thread
* Re: [PATCH] copy_file_range: New function to copy file data
2017-11-17 14:56 [PATCH] copy_file_range: New function to copy file data Florian Weimer
2017-11-17 14:58 ` Florian Weimer
@ 2017-11-19 3:11 ` Rical Jasan
2017-11-23 14:46 ` Florian Weimer
2017-11-23 15:13 ` Andreas Schwab
2017-12-05 14:03 ` Florian Weimer
3 siblings, 1 reply; 24+ messages in thread
From: Rical Jasan @ 2017-11-19 3:11 UTC (permalink / raw)
To: Florian Weimer; +Cc: libc-alpha
On 11/17/2017 06:56 AM, Florian Weimer wrote:
> diff --git a/manual/llio.texi b/manual/llio.texi
> index 825fd94e32..d56dadae12 100644
> --- a/manual/llio.texi
> +++ b/manual/llio.texi
> @@ -41,6 +41,7 @@ directly.)
> * Stream/Descriptor Precautions:: Precautions needed if you use both
> descriptors and streams.
> * Scatter-Gather:: Fast I/O to discontinuous buffers.
> +* Copying File Data:: Copying data between files.
> * Memory-mapped I/O:: Using files like memory.
> * Waiting for I/O:: How to check for input or output
> on multiple file descriptors.
> @@ -1363,6 +1364,80 @@ may be easier to use than these functions. However, @code{readv} and
> (as opposed to the total output), are large. In that case, a high-level
> stream would not be able to cache the data efficiently.
>
> +@node Copying File Data
> +@section Copying data between two files
> +@cindex copying files
> +@cindex file copy
> +
> +A special function is provided to copy data between two files on the
> +same file system. The system can optimize such copy operations. This
> +is particularly important on network file systems, where the data would
> +otherwise have to be transferred twice over the network.
> +
> +Note that this function only copies file data, but not metadata such as
> +file permissions or extended attributes.
> +
> +@deftypefun ssize_t copy_file_range (int @var{inputfd}, off64_t *@var{inputpos}, int @var{outputfd}, off64_t *@var{outputpos}, ssize_t @var{length}, unsigned int @var{flags})
> +@standards{GNU, unistd.h}
> +@safety{@prelim{}@mtsafe{}@assafe{}@acsafe{}}
> +
> +This function copies up to @var{length} bytes from the file descriptor
> +@var{inputfd} to the file descriptor @var{outputfd}.
> +
> +The function can operate on both the current file position (like
> +@code{read} and @code{write}) and an explicit offset (like @code{pread}
> +and @code{pwrite}). If the @var{inputpos} pointer is null, the file
> +position of @var{inputfd} is used as the starting point of the copy
> +operation, and the file position is advanced during it. If
> +@var{inputpos} is not null, then @code{*@var{inputpos}} is used as the
> +starting point of the copy operation, and @code{*@var{inputpos}} is
> +incremented by the number of copied bytes, but the file position remains
> +unchanged. Similar rules apply to @var{outputfd} and @var{outputpos}
> +for the output file position.
> +
> +The @var{flags} argument is currently reserved and must be zero.
> +
> +The @code{copy_file_range} function returns the number of bytes copied.
> +This can be less than the specified @var{length} in case the input file
> +contains fewer remaining bytes than @var{length}, or if a read or write
> +failure occurs. The return value is zero if the end of the input file
> +is encountered immediately.
> +
> +If no bytes can be copied, to report an error, @code{copy_file_range}
> +returns the value @math{-1} and sets @code{errno}. The following
Perhaps others will weigh in, but I lean towards @code for return
values. The manual clearly doesn't have a preference at this point,
though, and uses both heavily.
> +@code{errno} error conditions are specific to this function.
"function:"
> +
> +@table @code
> +@item EISDIR
> +At least one of the descriptors @var{inputfd} or @var{outputfd} refers
> +to a directory.
> +
> +@item EINVAL
> +At least one of the descriptors @var{inputfd} or @var{outputfd} refers
> +to a non-regular, non-directory file (such as a socket or a FIFO).
> +
> +The @var{flags} argument is not zero.
> +
> +@item EBADF
> +The argument @var{inputfd} is not a valid file descriptor open for
> +reading.
> +
> +The argument @var{outputfd} is not a valid file descriptor open for
> +writing, or @var{outputfd} has been opened with @code{O_APPEND}.
> +
> +@item EXDEV
> +The input and output files reside on different file systems.
> +@end table
> +
> +In addition, @code{copy_file_range} can result with the error codes
"can result in" (or maybe change "result" to something different)
> +which are used by @code{read}, @code{pread}, @code{write}, and
> +@code{pwrite}.
> +
> +The @code{copy_file_range} is a cancellation point. In case of
"The ... function" (or drop "The")
> +cancellation, the input location (the file position or the value at
> +@code{@var{inputpos}}) is indeterminate.
Did you mean @code{*@var{inputpos}}? (That isn't a stamp of approval on
how we should format dereferencing; I've been arguing with myself over
that for a long time, but this form at least has precedent, so is fine.)
Thank you,
Rical
^ permalink raw reply [flat|nested] 24+ messages in thread
* Re: [PATCH] copy_file_range: New function to copy file data
2017-11-17 14:58 ` Florian Weimer
@ 2017-11-19 3:13 ` Rical Jasan
2017-11-20 12:55 ` Florian Weimer
0 siblings, 1 reply; 24+ messages in thread
From: Rical Jasan @ 2017-11-19 3:13 UTC (permalink / raw)
To: Florian Weimer; +Cc: libc-alpha
On 11/17/2017 06:58 AM, Florian Weimer wrote:
> As usual, I can commit the support/ additions separately if this is
> desirable.
I would like to see those sorts of changes separated out. It keeps the
commits cleaner, and patches a little easier to review.
Rical
^ permalink raw reply [flat|nested] 24+ messages in thread
* Re: [PATCH] copy_file_range: New function to copy file data
2017-11-19 3:13 ` Rical Jasan
@ 2017-11-20 12:55 ` Florian Weimer
2017-11-21 0:36 ` Rical Jasan
0 siblings, 1 reply; 24+ messages in thread
From: Florian Weimer @ 2017-11-20 12:55 UTC (permalink / raw)
To: Rical Jasan; +Cc: libc-alpha
On 11/19/2017 04:20 AM, Rical Jasan wrote:
> On 11/17/2017 06:58 AM, Florian Weimer wrote:
>> As usual, I can commit the support/ additions separately if this is
>> desirable.
>
> I would like to see those sorts of changes separated out. It keeps the
> commits cleaner, and patches a little easier to review.
I brought this up earlier this month and received the opposite feedback:
<https://sourceware.org/ml/libc-alpha/2017-11/msg00097.html>
Florian
^ permalink raw reply [flat|nested] 24+ messages in thread
* Re: [PATCH] copy_file_range: New function to copy file data
2017-11-20 12:55 ` Florian Weimer
@ 2017-11-21 0:36 ` Rical Jasan
0 siblings, 0 replies; 24+ messages in thread
From: Rical Jasan @ 2017-11-21 0:36 UTC (permalink / raw)
To: Florian Weimer; +Cc: libc-alpha
On 11/20/2017 04:55 AM, Florian Weimer wrote:
> On 11/19/2017 04:20 AM, Rical Jasan wrote:
>> On 11/17/2017 06:58 AM, Florian Weimer wrote:
>>> As usual, I can commit the support/ additions separately if this is
>>> desirable.
>>
>> I would like to see those sorts of changes separated out. It keeps the
>> commits cleaner, and patches a little easier to review.
>
> I brought this up earlier this month and received the opposite feedback:
>
> <https://sourceware.org/ml/libc-alpha/2017-11/msg00097.html>
Ah, well, who am I to complain? Carry on. :)
Rical
^ permalink raw reply [flat|nested] 24+ messages in thread
* Re: [PATCH] copy_file_range: New function to copy file data
2017-11-19 3:11 ` Rical Jasan
@ 2017-11-23 14:46 ` Florian Weimer
0 siblings, 0 replies; 24+ messages in thread
From: Florian Weimer @ 2017-11-23 14:46 UTC (permalink / raw)
To: Rical Jasan; +Cc: libc-alpha
On 11/19/2017 04:17 AM, Rical Jasan wrote:
>> +@code{errno} error conditions are specific to this function.
>
> "function:"
Fixed.
>> +In addition, @code{copy_file_range} can result with the error codes
>
> "can result in" (or maybe change "result" to something different)
I meant to write âcan fail withâ. Fixed.
>
>> +which are used by @code{read}, @code{pread}, @code{write}, and
>> +@code{pwrite}.
>> +
>> +The @code{copy_file_range} is a cancellation point. In case of
>
> "The ... function" (or drop "The")
Fixed.
>> +cancellation, the input location (the file position or the value at
>> +@code{@var{inputpos}}) is indeterminate.
>
> Did you mean @code{*@var{inputpos}}? (That isn't a stamp of approval on
> how we should format dereferencing; I've been arguing with myself over
> that for a long time, but this form at least has precedent, so is fine.)
Yes, the asterisk was missing.
Thank you for your documentation review.
Does anyone want to comment on the implementation? 8-)
Thanks,
Florian
^ permalink raw reply [flat|nested] 24+ messages in thread
* Re: [PATCH] copy_file_range: New function to copy file data
2017-11-17 14:56 [PATCH] copy_file_range: New function to copy file data Florian Weimer
2017-11-17 14:58 ` Florian Weimer
2017-11-19 3:11 ` Rical Jasan
@ 2017-11-23 15:13 ` Andreas Schwab
2017-11-23 15:23 ` Florian Weimer
2017-12-05 14:03 ` Florian Weimer
3 siblings, 1 reply; 24+ messages in thread
From: Andreas Schwab @ 2017-11-23 15:13 UTC (permalink / raw)
To: Florian Weimer; +Cc: libc-alpha
On Nov 17 2017, fweimer@redhat.com (Florian Weimer) wrote:
> +COPY_FILE_RANGE_DECL
> +ssize_t
> +COPY_FILE_RANGE (int infd, __off64_t *pinoff,
> + int outfd, __off64_t *poutoff,
> + size_t length, unsigned int flags)
> +{
> + if (flags != 0)
> + {
> + __set_errno (EINVAL);
> + return -1;
> + }
> +
> + struct stat64 instat;
> + struct stat64 outstat;
> + if (fstat64 (infd, &instat) != 0 || fstat64 (outfd, &outstat))
!= 0
> + /* Write the buffer part which was read to the destination. */
> + char *end = buf + read_count;
> + for (char *p = buf; p < end; )
> + {
> + ssize_t write_count;
> + if (poutoff == NULL)
> + write_count = write (outfd, p, end - p);
> + else
> + write_count = __libc_pwrite64 (outfd, p, end - p, *poutoff);
> + if (write_count == 0)
> + {
> + /* Assume that this means no space on the target file
> + system, and use the error handling path below. */
I don't think write can ever return 0 when writing more than zero bytes.
Andreas.
--
Andreas Schwab, SUSE Labs, schwab@suse.de
GPG Key fingerprint = 0196 BAD8 1CE9 1970 F4BE 1748 E4D4 88E3 0EEA B9D7
"And now for something completely different."
^ permalink raw reply [flat|nested] 24+ messages in thread
* Re: [PATCH] copy_file_range: New function to copy file data
2017-11-23 15:13 ` Andreas Schwab
@ 2017-11-23 15:23 ` Florian Weimer
2017-11-23 15:39 ` Andreas Schwab
0 siblings, 1 reply; 24+ messages in thread
From: Florian Weimer @ 2017-11-23 15:23 UTC (permalink / raw)
To: Andreas Schwab; +Cc: libc-alpha
On 11/23/2017 04:13 PM, Andreas Schwab wrote:
> On Nov 17 2017, fweimer@redhat.com (Florian Weimer) wrote:
>
>> +COPY_FILE_RANGE_DECL
>> +ssize_t
>> +COPY_FILE_RANGE (int infd, __off64_t *pinoff,
>> + int outfd, __off64_t *poutoff,
>> + size_t length, unsigned int flags)
>> +{
>> + if (flags != 0)
>> + {
>> + __set_errno (EINVAL);
>> + return -1;
>> + }
>> +
>> + struct stat64 instat;
>> + struct stat64 outstat;
>> + if (fstat64 (infd, &instat) != 0 || fstat64 (outfd, &outstat))
>
> != 0
Thanks, fixed.
>> + /* Write the buffer part which was read to the destination. */
>> + char *end = buf + read_count;
>> + for (char *p = buf; p < end; )
>> + {
>> + ssize_t write_count;
>> + if (poutoff == NULL)
>> + write_count = write (outfd, p, end - p);
>> + else
>> + write_count = __libc_pwrite64 (outfd, p, end - p, *poutoff);
>> + if (write_count == 0)
>> + {
>> + /* Assume that this means no space on the target file
>> + system, and use the error handling path below. */
>
> I don't think write can ever return 0 when writing more than zero bytes.
I can drop the check. With the Linux VFS layer, it is difficult to tell
whether this condition can ever happen, and if it does, we would likely
enter an infinite loop without the check.
Thanks,
Florian
^ permalink raw reply [flat|nested] 24+ messages in thread
* Re: [PATCH] copy_file_range: New function to copy file data
2017-11-23 15:23 ` Florian Weimer
@ 2017-11-23 15:39 ` Andreas Schwab
2017-11-23 15:43 ` Florian Weimer
0 siblings, 1 reply; 24+ messages in thread
From: Andreas Schwab @ 2017-11-23 15:39 UTC (permalink / raw)
To: Florian Weimer; +Cc: libc-alpha
On Nov 23 2017, Florian Weimer <fweimer@redhat.com> wrote:
>> I don't think write can ever return 0 when writing more than zero bytes.
>
> I can drop the check. With the Linux VFS layer, it is difficult to tell
> whether this condition can ever happen, and if it does, we would likely
> enter an infinite loop without the check.
Or we get the real error in the next loop.
Andreas.
--
Andreas Schwab, SUSE Labs, schwab@suse.de
GPG Key fingerprint = 0196 BAD8 1CE9 1970 F4BE 1748 E4D4 88E3 0EEA B9D7
"And now for something completely different."
^ permalink raw reply [flat|nested] 24+ messages in thread
* Re: [PATCH] copy_file_range: New function to copy file data
2017-11-23 15:39 ` Andreas Schwab
@ 2017-11-23 15:43 ` Florian Weimer
2017-11-23 15:46 ` Andreas Schwab
0 siblings, 1 reply; 24+ messages in thread
From: Florian Weimer @ 2017-11-23 15:43 UTC (permalink / raw)
To: Andreas Schwab; +Cc: libc-alpha
On 11/23/2017 04:39 PM, Andreas Schwab wrote:
> On Nov 23 2017, Florian Weimer <fweimer@redhat.com> wrote:
>
>>> I don't think write can ever return 0 when writing more than zero bytes.
>>
>> I can drop the check. With the Linux VFS layer, it is difficult to tell
>> whether this condition can ever happen, and if it does, we would likely
>> enter an infinite loop without the check.
> > Or we get the real error in the next loop.
Tradition has it that it's a replacement for the ENOSPC condition, and
that would be sticky and happen again in the next loop iteration.
Thanks,
Florian
^ permalink raw reply [flat|nested] 24+ messages in thread
* Re: [PATCH] copy_file_range: New function to copy file data
2017-11-23 15:43 ` Florian Weimer
@ 2017-11-23 15:46 ` Andreas Schwab
2017-11-23 16:23 ` Florian Weimer
0 siblings, 1 reply; 24+ messages in thread
From: Andreas Schwab @ 2017-11-23 15:46 UTC (permalink / raw)
To: Florian Weimer; +Cc: libc-alpha
On Nov 23 2017, Florian Weimer <fweimer@redhat.com> wrote:
> On 11/23/2017 04:39 PM, Andreas Schwab wrote:
>> On Nov 23 2017, Florian Weimer <fweimer@redhat.com> wrote:
>>
>>>> I don't think write can ever return 0 when writing more than zero bytes.
>>>
>>> I can drop the check. With the Linux VFS layer, it is difficult to tell
>>> whether this condition can ever happen, and if it does, we would likely
>>> enter an infinite loop without the check.
>> > Or we get the real error in the next loop.
>
> Tradition has it that it's a replacement for the ENOSPC condition,
A short write, yes, but not a zero write.
Andreas.
--
Andreas Schwab, SUSE Labs, schwab@suse.de
GPG Key fingerprint = 0196 BAD8 1CE9 1970 F4BE 1748 E4D4 88E3 0EEA B9D7
"And now for something completely different."
^ permalink raw reply [flat|nested] 24+ messages in thread
* Re: [PATCH] copy_file_range: New function to copy file data
2017-11-23 15:46 ` Andreas Schwab
@ 2017-11-23 16:23 ` Florian Weimer
0 siblings, 0 replies; 24+ messages in thread
From: Florian Weimer @ 2017-11-23 16:23 UTC (permalink / raw)
To: Andreas Schwab; +Cc: libc-alpha
On 11/23/2017 04:46 PM, Andreas Schwab wrote:
> On Nov 23 2017, Florian Weimer <fweimer@redhat.com> wrote:
>
>> On 11/23/2017 04:39 PM, Andreas Schwab wrote:
>>> On Nov 23 2017, Florian Weimer <fweimer@redhat.com> wrote:
>>>
>>>>> I don't think write can ever return 0 when writing more than zero bytes.
>>>>
>>>> I can drop the check. With the Linux VFS layer, it is difficult to tell
>>>> whether this condition can ever happen, and if it does, we would likely
>>>> enter an infinite loop without the check.
>>> > Or we get the real error in the next loop.
>>
>> Tradition has it that it's a replacement for the ENOSPC condition,
>
> A short write, yes, but not a zero write.
Looking at _IO_new_file_write in libio/fileops.c (which is where a call
to fwrite eventually ends up if the buffer is full):
_IO_ssize_t to_do = n;
while (to_do > 0)
{
_IO_ssize_t count = (__builtin_expect (f->_flags2
& _IO_FLAGS2_NOTCANCEL, 0)
? __write_nocancel (f->_fileno, data, to_do)
: __write (f->_fileno, data, to_do));
if (count < 0)
{
f->_flags |= _IO_ERR_SEEN;
break;
}
to_do -= count;
data = (void *) ((char *) data + count);
}
I see that we do not have a count == 0 special case there, so I'm going
to drop the check from copy_file_range, as you proposed.
Thanks,
Florian
^ permalink raw reply [flat|nested] 24+ messages in thread
* Re: [PATCH] copy_file_range: New function to copy file data
2017-11-17 14:56 [PATCH] copy_file_range: New function to copy file data Florian Weimer
` (2 preceding siblings ...)
2017-11-23 15:13 ` Andreas Schwab
@ 2017-12-05 14:03 ` Florian Weimer
2017-12-14 14:26 ` Florian Weimer
2017-12-18 20:04 ` Adhemerval Zanella
3 siblings, 2 replies; 24+ messages in thread
From: Florian Weimer @ 2017-12-05 14:03 UTC (permalink / raw)
To: libc-alpha
[-- Attachment #1: Type: text/plain, Size: 338 bytes --]
The attached version of the patch fixes a few 32-bit issues in the code
(non-64 functions being called, and size_t-to-off64_t conversion issue).
I renamed the -common.c file into -compat.c, for consistency with how
the file is used.
It also drops the zero-as-ENOSPC special case Andreas recognized as
unnecessary.
Thanks,
Florian
[-- Attachment #2: copy_file_range.patch --]
[-- Type: text/x-patch, Size: 58722 bytes --]
Subject: [PATCH] copy_file_range: New function to copy file data
To: libc-alpha@sourceware.org
The semantics are based on the Linux system call, but a very close
emulation in user space is provided.
2017-12-05 Florian Weimer <fweimer@redhat.com>
* io/Makefile (routines): Add copy_file_range.
(tests): Add tst-copy_file_range.
(tests-static, tests-internal): Add tst-copy_file_range-compat.
* io/Versions (GLIBC_2.27): Export copy_file_range.
* io/copy_file_range-compat.c: New file.
* io/copy_file_range.c: Likewise.
* io/tst-copy_file_range-compat.c: Likewise.
* io/tst-copy_file_range.c: Likewise.
* manual/llio.texi (Copying File Data): New section.
* posix/unistd.h [__USE_GNU] (copy_file_range): Declare.
* support/Makefile (libsupport-routines): Add support-xfstat,
xftruncate, xlseek.
* support/support-xfstat.c: New file.
* support/xftruncate.c: Likewise.
* support/xlseek.c: Likewise.
* support/xunistd.h (xfstat, xftruncate, xlseek): Declare.
* sysdeps/unix/sysv/linux/**.abilist: Update.
* sysdeps/unix/sysv/linux/copy_file_range.c: New file.
diff --git a/io/Makefile b/io/Makefile
index 2f26bf56db..f2f0f0d040 100644
--- a/io/Makefile
+++ b/io/Makefile
@@ -52,7 +52,7 @@ routines := \
ftw ftw64 fts fts64 poll ppoll \
posix_fadvise posix_fadvise64 \
posix_fallocate posix_fallocate64 \
- sendfile sendfile64 \
+ sendfile sendfile64 copy_file_range \
utimensat futimens
# These routines will be omitted from the libc shared object.
@@ -70,7 +70,13 @@ tests := test-utime test-stat test-stat2 test-lfs tst-getcwd \
tst-symlinkat tst-linkat tst-readlinkat tst-mkdirat \
tst-mknodat tst-mkfifoat tst-ttyname_r bug-ftw5 \
tst-posix_fallocate tst-posix_fallocate64 \
- tst-fts tst-fts-lfs tst-open-tmpfile
+ tst-fts tst-fts-lfs tst-open-tmpfile \
+ tst-copy_file_range \
+
+# This test includes the compat implementation of copy_file_range,
+# which uses internal, unexported libc functions.
+tests-static += tst-copy_file_range-compat
+tests-internal += tst-copy_file_range-compat
ifeq ($(run-built-tests),yes)
tests-special += $(objpfx)ftwtest.out
diff --git a/io/Versions b/io/Versions
index 64316cd025..98898cb9d5 100644
--- a/io/Versions
+++ b/io/Versions
@@ -125,4 +125,7 @@ libc {
GLIBC_2.23 {
fts64_children; fts64_close; fts64_open; fts64_read; fts64_set;
}
+ GLIBC_2.27 {
+ copy_file_range;
+ }
}
diff --git a/io/copy_file_range-compat.c b/io/copy_file_range-compat.c
new file mode 100644
index 0000000000..ba6ea389df
--- /dev/null
+++ b/io/copy_file_range-compat.c
@@ -0,0 +1,158 @@
+/* Emulation of copy_file_range.
+ Copyright (C) 2017 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, see
+ <http://www.gnu.org/licenses/>. */
+
+/* The following macros should be defined before including this
+ file:
+
+ COPY_FILE_RANGE_DECL Declaration specifiers for the function below.
+ COPY_FILE_RANGE Name of the function to define. */
+
+#include <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <limits.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+COPY_FILE_RANGE_DECL
+ssize_t
+COPY_FILE_RANGE (int infd, __off64_t *pinoff,
+ int outfd, __off64_t *poutoff,
+ size_t length, unsigned int flags)
+{
+ if (flags != 0)
+ {
+ __set_errno (EINVAL);
+ return -1;
+ }
+
+ struct stat64 instat;
+ struct stat64 outstat;
+ if (fstat64 (infd, &instat) != 0 || fstat64 (outfd, &outstat) != 0)
+ return -1;
+ if (S_ISDIR (instat.st_mode) || S_ISDIR (outstat.st_mode))
+ {
+ __set_errno (EISDIR);
+ return -1;
+ }
+ if (!S_ISREG (instat.st_mode) || !S_ISREG (outstat.st_mode))
+ {
+ /* We need a regular input file so that the we can seek
+ backwards in case of a write failure. */
+ __set_errno (EINVAL);
+ return -1;
+ }
+ if (instat.st_dev != outstat.st_dev)
+ {
+ /* Cross-device copies are not supported. */
+ __set_errno (EXDEV);
+ return -1;
+ }
+
+ /* The output descriptor must not have O_APPEND set. */
+ {
+ int flags = __fcntl (outfd, F_GETFL);
+ if (flags & O_APPEND)
+ {
+ __set_errno (EBADF);
+ return -1;
+ }
+ }
+
+ /* Avoid an overflow in the result. */
+ if (length > SSIZE_MAX)
+ length = SSIZE_MAX;
+
+ /* Main copying loop. The buffer size is arbitrary and is a
+ trade-off between stack size consumption, cache usage, and
+ amortization of system call overhead. */
+ size_t copied = 0;
+ char buf[8192];
+ while (length > 0)
+ {
+ size_t to_read = length;
+ if (to_read > sizeof (buf))
+ to_read = sizeof (buf);
+
+ /* Fill the buffer. */
+ ssize_t read_count;
+ if (pinoff == NULL)
+ read_count = read (infd, buf, to_read);
+ else
+ read_count = __libc_pread64 (infd, buf, to_read, *pinoff);
+ if (read_count == 0)
+ /* End of file reached prematurely. */
+ return copied;
+ if (read_count < 0)
+ {
+ if (copied > 0)
+ /* Report the number of bytes copied so far. */
+ return copied;
+ return -1;
+ }
+ if (pinoff != 0)
+ *pinoff += read_count;
+
+ /* Write the buffer part which was read to the destination. */
+ char *end = buf + read_count;
+ for (char *p = buf; p < end; )
+ {
+ ssize_t write_count;
+ if (poutoff == NULL)
+ write_count = write (outfd, p, end - p);
+ else
+ write_count = __libc_pwrite64 (outfd, p, end - p, *poutoff);
+ if (write_count < 0)
+ {
+ /* Adjust the input read position to match what we have
+ written, so that the caller can pick up after the
+ error. */
+ size_t written = p - buf;
+ /* NB: This needs to be signed so that we can form the
+ negative value below. */
+ ssize_t overread = read_count - written;
+ if (pinoff == NULL)
+ {
+ if (overread > 0)
+ {
+ /* We are on an error recovery path, so we
+ cannot deal with failure here. */
+ int save_errno = errno;
+ (void) __libc_lseek64 (infd, -overread, SEEK_CUR);
+ __set_errno (save_errno);
+ }
+ }
+ else /* pinoff != NULL */
+ *pinoff -= overread;
+
+ if (copied > 0)
+ /* Report the number of bytes copied so far. */
+ return copied + written;
+ return -1;
+ }
+ p += write_count;
+ if (poutoff != NULL)
+ *poutoff += write_count;
+ } /* Write loop. */
+
+ copied += read_count;
+ length -= read_count;
+ }
+ return copied;
+}
diff --git a/io/copy_file_range.c b/io/copy_file_range.c
new file mode 100644
index 0000000000..61ee6871b4
--- /dev/null
+++ b/io/copy_file_range.c
@@ -0,0 +1,22 @@
+/* Generic implementation of copy_file_range.
+ Copyright (C) 2017 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, see
+ <http://www.gnu.org/licenses/>. */
+
+#define COPY_FILE_RANGE_DECL
+#define COPY_FILE_RANGE copy_file_range
+
+#include <io/copy_file_range-compat.c>
diff --git a/io/tst-copy_file_range-compat.c b/io/tst-copy_file_range-compat.c
new file mode 100644
index 0000000000..eb737946d9
--- /dev/null
+++ b/io/tst-copy_file_range-compat.c
@@ -0,0 +1,30 @@
+/* Test the fallback implementation of copy_file_range.
+ Copyright (C) 2017 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, see
+ <http://www.gnu.org/licenses/>. */
+
+/* Get the declaration of the official copy_of_range function. */
+#include <unistd.h>
+
+/* Compile a local version of copy_file_range. */
+#define COPY_FILE_RANGE_DECL static
+#define COPY_FILE_RANGE copy_file_range_compat
+#include <io/copy_file_range-compat.c>
+
+/* Re-use the test, but run it against copy_file_range_compat defined
+ above. */
+#define copy_file_range copy_file_range_compat
+#include "tst-copy_file_range.c"
diff --git a/io/tst-copy_file_range.c b/io/tst-copy_file_range.c
new file mode 100644
index 0000000000..d7532f288a
--- /dev/null
+++ b/io/tst-copy_file_range.c
@@ -0,0 +1,730 @@
+/* Tests for copy_file_range.
+ Copyright (C) 2017 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, see
+ <http://www.gnu.org/licenses/>. */
+
+#include <array_length.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <libgen.h>
+#include <poll.h>
+#include <sched.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <support/check.h>
+#include <support/namespace.h>
+#include <support/support.h>
+#include <support/temp_file.h>
+#include <support/test-driver.h>
+#include <support/xunistd.h>
+#include <sys/mount.h>
+
+/* Boolean flags which indicate whether to use pointers with explicit
+ output flags. */
+static int do_inoff;
+static int do_outoff;
+
+/* Name and descriptors of the input files. Files are truncated and
+ reopened (with O_RDWR) between tests. */
+static char *infile;
+static int infd;
+static char *outfile;
+static int outfd;
+
+/* Like the above, but on a different file system. xdevfile can be
+ NULL if no suitable file system has been found. */
+static char *xdevfile;
+
+/* Input and output offsets. Set according to do_inoff and do_outoff
+ before the test. The offsets themselves are always set to
+ zero. */
+static off64_t inoff;
+static off64_t *pinoff;
+static off64_t outoff;
+static off64_t *poutoff;
+
+/* These are a collection of copy sizes used in tests. The selection
+ takes into account that the fallback implementation uses an
+ internal buffer of 8192 bytes. */
+enum { maximum_size = 99999 };
+static const int typical_sizes[] =
+ { 0, 1, 2, 3, 1024, 2048, 4096, 8191, 8192, 8193, 16383, 16384, 16385,
+ maximum_size };
+
+/* The random contents of this array can be used as a pattern to check
+ for correct write operations. */
+static unsigned char random_data[maximum_size];
+
+/* The size chosen by the test harness. */
+static int current_size;
+
+/* Discover the maximum file size (writable offset) for outfd.
+ Truncate outfd to length zero as a side effect. */
+static off64_t
+find_maximum_offset (void)
+{
+ xftruncate (outfd, 0);
+ static off64_t cache = 0;
+ if (cache != 0)
+ return cache;
+
+ uint64_t upper = -1;
+ upper >>= 1; /* Maximum of off64_t. */
+ TEST_VERIFY ((off64_t) upper > 0);
+ TEST_VERIFY ((off64_t) (upper + 1) < 0);
+ if (lseek64 (outfd, upper, SEEK_SET) >= 0)
+ {
+ if (write (outfd, "", 1) == 1)
+ FAIL_EXIT1 ("created a file larger than the off64_t range");
+ }
+
+ uint64_t lower = 1024 * 1024; /* A reasonable minimum file size. */
+ /* Loop invariant: writing at lower succeeds, writing at upper fails. */
+ while (lower + 1 < upper)
+ {
+ uint64_t middle = (lower + upper) / 2;
+ if (test_verbose > 0)
+ printf ("info: %s: remaining test range %" PRIu64 " .. %" PRIu64
+ ", probe at %" PRIu64 "\n", __func__, lower, upper, middle);
+ xftruncate (outfd, 0);
+ if (lseek64 (outfd, middle, SEEK_SET) >= 0
+ && write (outfd, "", 1) == 1)
+ lower = middle;
+ else
+ upper = middle;
+ }
+ TEST_VERIFY (lower + 1 == upper);
+ xftruncate (outfd, 0);
+ cache = lower;
+ printf ("info: maximum writable file offset: %" PRIu64 " (%" PRIx64 ")\n",
+ lower, lower);
+ return lower;
+}
+
+/* Perform a copy of a file. */
+static void
+simple_file_copy (void)
+{
+ xwrite (infd, random_data, current_size);
+
+ int length;
+ int in_skipped; /* Expected skipped bytes in input. */
+ if (do_inoff)
+ {
+ xlseek (infd, 1, SEEK_SET);
+ inoff = 2;
+ length = current_size - 3;
+ in_skipped = 2;
+ }
+ else
+ {
+ xlseek (infd, 3, SEEK_SET);
+ length = current_size - 5;
+ in_skipped = 3;
+ }
+ int out_skipped; /* Expected skipped bytes before the written data. */
+ if (do_outoff)
+ {
+ xlseek (outfd, 4, SEEK_SET);
+ outoff = 5;
+ out_skipped = 5;
+ }
+ else
+ {
+ xlseek (outfd, 6, SEEK_SET);
+ length = current_size - 6;
+ out_skipped = 6;
+ }
+ if (length < 0)
+ length = 0;
+
+ TEST_COMPARE (copy_file_range (infd, pinoff, outfd, poutoff,
+ length, 0), length);
+ if (do_inoff)
+ {
+ TEST_COMPARE (inoff, 2 + length);
+ TEST_COMPARE (xlseek (infd, 0, SEEK_CUR), 1);
+ }
+ else
+ TEST_COMPARE (xlseek (infd, 0, SEEK_CUR), 3 + length);
+ if (do_outoff)
+ {
+ TEST_COMPARE (outoff, 5 + length);
+ TEST_COMPARE (xlseek (outfd, 0, SEEK_CUR), 4);
+ }
+ else
+ TEST_COMPARE (xlseek (outfd, 0, SEEK_CUR), 6 + length);
+
+ struct stat64 st;
+ xfstat (outfd, &st);
+ if (length > 0)
+ TEST_COMPARE (st.st_size, out_skipped + length);
+ else
+ {
+ /* If we did not write anything, we also did not add any
+ padding. */
+ TEST_COMPARE (st.st_size, 0);
+ return;
+ }
+
+ xlseek (outfd, 0, SEEK_SET);
+ char *bytes = xmalloc (st.st_size);
+ TEST_COMPARE (read (outfd, bytes, st.st_size), st.st_size);
+ for (int i = 0; i < out_skipped; ++i)
+ TEST_COMPARE (bytes[i], 0);
+ TEST_VERIFY (memcmp (bytes + out_skipped, random_data + in_skipped,
+ length) == 0);
+ free (bytes);
+}
+
+/* Test that reading from a pipe willfails. */
+static void
+pipe_as_source (void)
+{
+ int pipefds[2];
+ xpipe (pipefds);
+
+ for (int length = 0; length < 2; ++length)
+ {
+ if (test_verbose > 0)
+ printf ("info: %s: length=%d\n", __func__, length);
+
+ /* Make sure that there is something to copy in the pipe. */
+ xwrite (pipefds[1], "@", 1);
+
+ TEST_COMPARE (copy_file_range (pipefds[0], pinoff, outfd, poutoff,
+ length, 0), -1);
+ /* Linux 4.10 and later return EINVAL. Older kernels return
+ EXDEV. */
+ TEST_VERIFY (errno == EINVAL || errno == EXDEV);
+ TEST_COMPARE (inoff, 0);
+ TEST_COMPARE (outoff, 0);
+ TEST_COMPARE (xlseek (outfd, 0, SEEK_CUR), 0);
+
+ /* Make sure that nothing was read. */
+ char buf = 'A';
+ TEST_COMPARE (read (pipefds[0], &buf, 1), 1);
+ TEST_COMPARE (buf, '@');
+ }
+
+ xclose (pipefds[0]);
+ xclose (pipefds[1]);
+}
+
+/* Test that writing to a pipe fails. */
+static void
+pipe_as_destination (void)
+{
+ /* Make sure that there is something to read in the input file. */
+ xwrite (infd, "abc", 3);
+ xlseek (infd, 0, SEEK_SET);
+
+ int pipefds[2];
+ xpipe (pipefds);
+
+ for (int length = 0; length < 2; ++length)
+ {
+ if (test_verbose > 0)
+ printf ("info: %s: length=%d\n", __func__, length);
+
+ TEST_COMPARE (copy_file_range (infd, pinoff, pipefds[1], poutoff,
+ length, 0), -1);
+ /* Linux 4.10 and later return EINVAL. Older kernels return
+ EXDEV. */
+ TEST_VERIFY (errno == EINVAL || errno == EXDEV);
+ TEST_COMPARE (inoff, 0);
+ TEST_COMPARE (outoff, 0);
+ TEST_COMPARE (xlseek (infd, 0, SEEK_CUR), 0);
+
+ /* Make sure that nothing was written. */
+ struct pollfd pollfd = { .fd = pipefds[0], .events = POLLIN, };
+ TEST_COMPARE (poll (&pollfd, 1, 0), 0);
+ }
+
+ xclose (pipefds[0]);
+ xclose (pipefds[1]);
+}
+
+/* Test a write failure after (potentially) writing some bytes. */
+static void
+delayed_write_failure (void)
+{
+ /* We need to write something to provoke the error. */
+ if (current_size == 0)
+ return;
+ xwrite (infd, random_data, sizeof (random_data));
+ xlseek (infd, 0, SEEK_SET);
+
+ /* Write failure near the start. */
+ off64_t where = find_maximum_offset ();
+ if (current_size == 1)
+ ++where;
+ outoff = where;
+ if (do_outoff)
+ xlseek (outfd, 1, SEEK_SET);
+ else
+ xlseek (outfd, where, SEEK_SET);
+ TEST_COMPARE (copy_file_range (infd, pinoff, outfd, poutoff,
+ sizeof (random_data), 0), -1);
+ TEST_COMPARE (errno, EINVAL);
+ TEST_COMPARE (xlseek (infd, 0, SEEK_CUR), 0);
+ if (do_outoff)
+ TEST_COMPARE (xlseek (outfd, 0, SEEK_CUR), 1);
+ else
+ TEST_COMPARE (xlseek (outfd, 0, SEEK_CUR), where);
+ TEST_COMPARE (outoff, where);
+ struct stat64 st;
+ xfstat (outfd, &st);
+ TEST_COMPARE (st.st_size, 0);
+
+ /* Write failure near the end. */
+ if (current_size == 1)
+ /* This would be same as the first test because there is only one
+ byte. */
+ return;
+ where = find_maximum_offset () - current_size + 1;
+ if (current_size == sizeof (random_data))
+ /* Otherwise we do not reach the non-writable byte. */
+ ++where;
+ outoff = where;
+ if (do_outoff)
+ xlseek (outfd, 1, SEEK_SET);
+ else
+ xlseek (outfd, where, SEEK_SET);
+ ssize_t ret = copy_file_range (infd, pinoff, outfd, poutoff,
+ sizeof (random_data), 0);
+ if (ret < 0)
+ {
+ TEST_COMPARE (ret, -1);
+ TEST_COMPARE (errno, EINVAL);
+ xfstat (outfd, &st);
+ TEST_COMPARE (st.st_size, 0);
+ }
+ else
+ {
+ /* The first copy succeeded. This happens in the emulation
+ because the internal buffer of limited size does not
+ necessarily cross the off64_t boundary on the first write
+ operation. */
+ if (test_verbose > 0)
+ printf ("info: copy_file_range (%zu) returned %zd\n",
+ sizeof (random_data), ret);
+ TEST_VERIFY (ret > 0);
+ TEST_VERIFY (ret < maximum_size);
+ xfstat (outfd, &st);
+ TEST_COMPARE (st.st_size, where + ret);
+ if (do_inoff)
+ {
+ TEST_COMPARE (inoff, ret);
+ TEST_COMPARE (xlseek (infd, 0, SEEK_CUR), 0);
+ }
+ else
+ TEST_COMPARE (xlseek (infd, 0, SEEK_CUR), ret);
+
+ char *buffer = xmalloc (ret);
+ TEST_COMPARE (pread64 (outfd, buffer, ret, where), ret);
+ TEST_VERIFY (memcmp (buffer, random_data, ret) == 0);
+ free (buffer);
+
+ /* The second copy fails. */
+ TEST_COMPARE (copy_file_range (infd, pinoff, outfd, poutoff,
+ sizeof (random_data), 0), -1);
+ TEST_COMPARE (errno, EINVAL);
+ }
+}
+
+/* Test a write failure across devices. */
+static void
+cross_device_failure (void)
+{
+ if (xdevfile == NULL)
+ /* Subtest not supported due to missing cross-device file. */
+ return;
+
+ /* We need something to write. */
+ xwrite (infd, random_data, sizeof (random_data));
+ xlseek (infd, 0, SEEK_SET);
+
+ int xdevfd = xopen (xdevfile, O_RDWR | O_LARGEFILE, 0);
+ TEST_COMPARE (copy_file_range (infd, pinoff, xdevfd, poutoff,
+ current_size, 0), -1);
+ TEST_COMPARE (errno, EXDEV);
+ TEST_COMPARE (xlseek (infd, 0, SEEK_CUR), 0);
+ struct stat64 st;
+ xfstat (xdevfd, &st);
+ TEST_COMPARE (st.st_size, 0);
+
+ xclose (xdevfd);
+}
+
+/* Try to exercise ENOSPC behavior with a tempfs file system (so that
+ we do not have to fill up a regular file system to get the error).
+ This function runs in a subprocess, so that we do not change the
+ mount namespace of the actual test process. */
+static void
+enospc_failure_1 (void *closure)
+{
+#ifdef CLONE_NEWNS
+ support_become_root ();
+
+ /* Make sure that we do not alter the file system mounts of the
+ parents. */
+ if (! support_enter_mount_namespace ())
+ {
+ printf ("warning: ENOSPC test skipped\n");
+ return;
+ }
+
+ char *mountpoint = closure;
+ if (mount ("none", mountpoint, "tmpfs", MS_NODEV | MS_NOEXEC,
+ "size=500k") != 0)
+ {
+ printf ("warning: could not mount tmpfs at %s: %m\n", mountpoint);
+ return;
+ }
+
+ /* The source file must reside on the same file system. */
+ char *intmpfsfile = xasprintf ("%s/%s", mountpoint, "in");
+ int intmpfsfd = xopen (intmpfsfile, O_RDWR | O_CREAT | O_LARGEFILE, 0600);
+ xwrite (intmpfsfd, random_data, sizeof (random_data));
+ xlseek (intmpfsfd, 1, SEEK_SET);
+ inoff = 1;
+
+ char *outtmpfsfile = xasprintf ("%s/%s", mountpoint, "out");
+ int outtmpfsfd = xopen (outtmpfsfile, O_RDWR | O_CREAT | O_LARGEFILE, 0600);
+
+ /* Fill the file with data until ENOSPC is reached. */
+ while (true)
+ {
+ ssize_t ret = write (outtmpfsfd, random_data, sizeof (random_data));
+ if (ret < 0 && errno != ENOSPC)
+ FAIL_EXIT1 ("write to %s: %m", outtmpfsfile);
+ if (ret < sizeof (random_data))
+ break;
+ }
+ TEST_COMPARE (write (outtmpfsfd, "", 1), -1);
+ TEST_COMPARE (errno, ENOSPC);
+ off64_t maxsize = xlseek (outtmpfsfd, 0, SEEK_CUR);
+ TEST_VERIFY_EXIT (maxsize > sizeof (random_data));
+
+ /* Constructed the expected file contents. */
+ char *expected = xmalloc (maxsize);
+ TEST_COMPARE (pread64 (outtmpfsfd, expected, maxsize, 0), maxsize);
+ /* Go back a little, so some bytes can be written. */
+ enum { offset = 20000 };
+ TEST_VERIFY_EXIT (offset < maxsize);
+ TEST_VERIFY_EXIT (offset < sizeof (random_data));
+ memcpy (expected + maxsize - offset, random_data + 1, offset);
+
+ if (do_outoff)
+ {
+ outoff = maxsize - offset;
+ xlseek (outtmpfsfd, 2, SEEK_SET);
+ }
+ else
+ xlseek (outtmpfsfd, -offset, SEEK_CUR);
+
+ /* First call is expected to succeed because we made room for some
+ bytes. */
+ TEST_COMPARE (copy_file_range (intmpfsfd, pinoff, outtmpfsfd, poutoff,
+ maximum_size, 0), offset);
+ if (do_inoff)
+ {
+ TEST_COMPARE (inoff, 1 + offset);
+ TEST_COMPARE (xlseek (intmpfsfd, 0, SEEK_CUR), 1);
+ }
+ else
+ TEST_COMPARE (xlseek (intmpfsfd, 0, SEEK_CUR), 1 + offset);
+ if (do_outoff)
+ {
+ TEST_COMPARE (outoff, maxsize);
+ TEST_COMPARE (xlseek (outtmpfsfd, 0, SEEK_CUR), 2);
+ }
+ else
+ TEST_COMPARE (xlseek (outtmpfsfd, 0, SEEK_CUR), maxsize);
+ struct stat64 st;
+ xfstat (outtmpfsfd, &st);
+ TEST_COMPARE (st.st_size, maxsize);
+ char *actual = xmalloc (st.st_size);
+ TEST_COMPARE (pread64 (outtmpfsfd, actual, st.st_size, 0), st.st_size);
+ TEST_VERIFY (memcmp (expected, actual, maxsize) == 0);
+
+ /* Second call should fail with ENOSPC. */
+ TEST_COMPARE (copy_file_range (intmpfsfd, pinoff, outtmpfsfd, poutoff,
+ maximum_size, 0), -1);
+ TEST_COMPARE (errno, ENOSPC);
+
+ /* Offsets should be unchanged. */
+ if (do_inoff)
+ {
+ TEST_COMPARE (inoff, 1 + offset);
+ TEST_COMPARE (xlseek (intmpfsfd, 0, SEEK_CUR), 1);
+ }
+ else
+ TEST_COMPARE (xlseek (intmpfsfd, 0, SEEK_CUR), 1 + offset);
+ if (do_outoff)
+ {
+ TEST_COMPARE (outoff, maxsize);
+ TEST_COMPARE (xlseek (outtmpfsfd, 0, SEEK_CUR), 2);
+ }
+ else
+ TEST_COMPARE (xlseek (outtmpfsfd, 0, SEEK_CUR), maxsize);
+ TEST_COMPARE (xlseek (outtmpfsfd, 0, SEEK_END), maxsize);
+ TEST_COMPARE (pread64 (outtmpfsfd, actual, maxsize, 0), maxsize);
+ TEST_VERIFY (memcmp (expected, actual, maxsize) == 0);
+
+ free (actual);
+ free (expected);
+
+ xclose (intmpfsfd);
+ xclose (outtmpfsfd);
+ free (intmpfsfile);
+ free (outtmpfsfile);
+
+#else /* !CLONE_NEWNS */
+ puts ("warning: ENOSPC test skipped (no mount namespaces)");
+#endif
+}
+
+/* Call enospc_failure_1 in a subprocess. */
+static void
+enospc_failure (void)
+{
+ char *mountpoint
+ = support_create_temp_directory ("tst-copy_file_range-enospc-");
+ support_isolate_in_subprocess (enospc_failure_1, mountpoint);
+ free (mountpoint);
+}
+
+/* The target file descriptor must have O_APPEND enabled. */
+static void
+oappend_failure (void)
+{
+ /* Add data, to make sure we do not fail because there is
+ insufficient input data. */
+ xwrite (infd, random_data, current_size);
+ xlseek (infd, 0, SEEK_SET);
+
+ xclose (outfd);
+ outfd = xopen (outfile, O_RDWR | O_APPEND, 0);
+ TEST_COMPARE (copy_file_range (infd, pinoff, outfd, poutoff,
+ current_size, 0), -1);
+ TEST_COMPARE (errno, EBADF);
+}
+
+/* Test that a short input file results in a shortened copy. */
+static void
+short_copy (void)
+{
+ if (current_size == 0)
+ /* Nothing to shorten. */
+ return;
+
+ /* Two subtests, one with offset 0 and current_size - 1 bytes, and
+ another one with current_size bytes, but offset 1. */
+ for (int shift = 0; shift < 2; ++shift)
+ {
+ if (test_verbose > 0)
+ printf ("info: shift=%d\n", shift);
+ xftruncate (infd, 0);
+ xlseek (infd, 0, SEEK_SET);
+ xwrite (infd, random_data, current_size - !shift);
+
+ if (do_inoff)
+ {
+ inoff = shift;
+ xlseek (infd, 2, SEEK_SET);
+ }
+ else
+ {
+ inoff = 3;
+ xlseek (infd, shift, SEEK_SET);
+ }
+ ftruncate (outfd, 0);
+ xlseek (outfd, 0, SEEK_SET);
+ outoff = 0;
+
+ /* First call copies current_size - 1 bytes. */
+ TEST_COMPARE (copy_file_range (infd, pinoff, outfd, poutoff,
+ current_size, 0), current_size - 1);
+ char *buffer = xmalloc (current_size);
+ TEST_COMPARE (pread64 (outfd, buffer, current_size, 0),
+ current_size - 1);
+ TEST_VERIFY (memcmp (buffer, random_data + shift, current_size - 1)
+ == 0);
+ free (buffer);
+
+ if (do_inoff)
+ {
+ TEST_COMPARE (inoff, current_size - 1 + shift);
+ TEST_COMPARE (xlseek (infd, 0, SEEK_CUR), 2);
+ }
+ else
+ TEST_COMPARE (xlseek (infd, 0, SEEK_CUR), current_size - 1 + shift);
+ if (do_outoff)
+ {
+ TEST_COMPARE (outoff, current_size - 1);
+ TEST_COMPARE (xlseek (outfd, 0, SEEK_CUR), 0);
+ }
+ else
+ TEST_COMPARE (xlseek (outfd, 0, SEEK_CUR), current_size - 1);
+
+ /* First call copies zero bytes. */
+ TEST_COMPARE (copy_file_range (infd, pinoff, outfd, poutoff,
+ current_size, 0), 0);
+ /* And the offsets are unchanged. */
+ if (do_inoff)
+ {
+ TEST_COMPARE (inoff, current_size - 1 + shift);
+ TEST_COMPARE (xlseek (infd, 0, SEEK_CUR), 2);
+ }
+ else
+ TEST_COMPARE (xlseek (infd, 0, SEEK_CUR), current_size - 1 + shift);
+ if (do_outoff)
+ {
+ TEST_COMPARE (outoff, current_size - 1);
+ TEST_COMPARE (xlseek (outfd, 0, SEEK_CUR), 0);
+ }
+ else
+ TEST_COMPARE (xlseek (outfd, 0, SEEK_CUR), current_size - 1);
+ }
+}
+
+/* A named test function. */
+struct test_case
+{
+ const char *name;
+ void (*func) (void);
+ bool sizes; /* If true, call the test with different current_size values. */
+};
+
+/* The available test cases. */
+static struct test_case tests[] =
+ {
+ { "simple_file_copy", simple_file_copy, .sizes = true },
+ { "pipe_as_source", pipe_as_source, },
+ { "pipe_as_destination", pipe_as_destination, },
+ { "delayed_write_failure", delayed_write_failure, .sizes = true },
+ { "cross_device_failure", cross_device_failure, .sizes = true },
+ { "enospc_failure", enospc_failure, },
+ { "oappend_failure", oappend_failure, .sizes = true },
+ { "short_copy", short_copy, .sizes = true },
+ };
+
+static int
+do_test (void)
+{
+ for (unsigned char *p = random_data; p < array_end (random_data); ++p)
+ *p = rand () >> 24;
+
+ infd = create_temp_file ("tst-copy_file_range-in-", &infile);
+ xclose (create_temp_file ("tst-copy_file_range-out-", &outfile));
+
+ /* Try to find a different directory from the default input/output
+ file. */
+ {
+ struct stat64 instat;
+ xfstat (infd, &instat);
+ static const char *const candidates[] =
+ { NULL, "/var/tmp", "/dev/shm" };
+ for (const char *const *c = candidates; c < array_end (candidates); ++c)
+ {
+ const char *path = *c;
+ char *to_free = NULL;
+ if (path == NULL)
+ {
+ to_free = xreadlink ("/proc/self/exe");
+ path = dirname (to_free);
+ }
+
+ struct stat64 cstat;
+ xstat (path, &cstat);
+ if (cstat.st_dev == instat.st_dev)
+ {
+ free (to_free);
+ continue;
+ }
+
+ printf ("info: using alternate temporary files directory: %s\n", path);
+ xdevfile = xasprintf ("%s/tst-copy_file_range-xdev-XXXXXX", path);
+ free (to_free);
+ break;
+ }
+ if (xdevfile != NULL)
+ {
+ int xdevfd = mkstemp (xdevfile);
+ if (xdevfd < 0)
+ FAIL_EXIT1 ("mkstemp (\"%s\"): %m", xdevfile);
+ struct stat64 xdevst;
+ xfstat (xdevfd, &xdevst);
+ TEST_VERIFY (xdevst.st_dev != instat.st_dev);
+ add_temp_file (xdevfile);
+ xclose (xdevfd);
+ }
+ else
+ puts ("warning: no alternate directory on different file system found");
+ }
+ xclose (infd);
+
+ for (do_inoff = 0; do_inoff < 2; ++do_inoff)
+ for (do_outoff = 0; do_outoff < 2; ++do_outoff)
+ for (struct test_case *test = tests; test < array_end (tests); ++test)
+ for (const int *size = typical_sizes;
+ size < array_end (typical_sizes); ++size)
+ {
+ current_size = *size;
+ if (test_verbose > 0)
+ printf ("info: %s do_inoff=%d do_outoff=%d current_size=%d\n",
+ test->name, do_inoff, do_outoff, current_size);
+
+ inoff = 0;
+ if (do_inoff)
+ pinoff = &inoff;
+ else
+ pinoff = NULL;
+ outoff = 0;
+ if (do_outoff)
+ poutoff = &outoff;
+ else
+ poutoff = NULL;
+
+ infd = xopen (infile, O_RDWR | O_LARGEFILE, 0);
+ xftruncate (infd, 0);
+ outfd = xopen (outfile, O_RDWR | O_LARGEFILE, 0);
+ xftruncate (outfd, 0);
+
+ test->func ();
+
+ xclose (infd);
+ xclose (outfd);
+
+ if (!test->sizes)
+ /* Skip the other sizes unless they have been
+ requested. */
+ break;
+ }
+
+ free (infile);
+ free (outfile);
+ free (xdevfile);
+
+ return 0;
+}
+
+#include <support/test-driver.c>
diff --git a/manual/llio.texi b/manual/llio.texi
index 8b2f599c79..37593f18d4 100644
--- a/manual/llio.texi
+++ b/manual/llio.texi
@@ -41,6 +41,7 @@ directly.)
* Stream/Descriptor Precautions:: Precautions needed if you use both
descriptors and streams.
* Scatter-Gather:: Fast I/O to discontinuous buffers.
+* Copying File Data:: Copying data between files.
* Memory-mapped I/O:: Using files like memory.
* Waiting for I/O:: How to check for input or output
on multiple file descriptors.
@@ -1353,6 +1354,80 @@ When the source file is compiled using @code{_FILE_OFFSET_BITS == 64} on a
@code{pwritev2} and so transparently replaces the 32 bit interface.
@end deftypefun
+@node Copying File Data
+@section Copying data between two files
+@cindex copying files
+@cindex file copy
+
+A special function is provided to copy data between two files on the
+same file system. The system can optimize such copy operations. This
+is particularly important on network file systems, where the data would
+otherwise have to be transferred twice over the network.
+
+Note that this function only copies file data, but not metadata such as
+file permissions or extended attributes.
+
+@deftypefun ssize_t copy_file_range (int @var{inputfd}, off64_t *@var{inputpos}, int @var{outputfd}, off64_t *@var{outputpos}, ssize_t @var{length}, unsigned int @var{flags})
+@standards{GNU, unistd.h}
+@safety{@prelim{}@mtsafe{}@assafe{}@acsafe{}}
+
+This function copies up to @var{length} bytes from the file descriptor
+@var{inputfd} to the file descriptor @var{outputfd}.
+
+The function can operate on both the current file position (like
+@code{read} and @code{write}) and an explicit offset (like @code{pread}
+and @code{pwrite}). If the @var{inputpos} pointer is null, the file
+position of @var{inputfd} is used as the starting point of the copy
+operation, and the file position is advanced during it. If
+@var{inputpos} is not null, then @code{*@var{inputpos}} is used as the
+starting point of the copy operation, and @code{*@var{inputpos}} is
+incremented by the number of copied bytes, but the file position remains
+unchanged. Similar rules apply to @var{outputfd} and @var{outputpos}
+for the output file position.
+
+The @var{flags} argument is currently reserved and must be zero.
+
+The @code{copy_file_range} function returns the number of bytes copied.
+This can be less than the specified @var{length} in case the input file
+contains fewer remaining bytes than @var{length}, or if a read or write
+failure occurs. The return value is zero if the end of the input file
+is encountered immediately.
+
+If no bytes can be copied, to report an error, @code{copy_file_range}
+returns the value @math{-1} and sets @code{errno}. The following
+@code{errno} error conditions are specific to this function:
+
+@table @code
+@item EISDIR
+At least one of the descriptors @var{inputfd} or @var{outputfd} refers
+to a directory.
+
+@item EINVAL
+At least one of the descriptors @var{inputfd} or @var{outputfd} refers
+to a non-regular, non-directory file (such as a socket or a FIFO).
+
+The @var{flags} argument is not zero.
+
+@item EBADF
+The argument @var{inputfd} is not a valid file descriptor open for
+reading.
+
+The argument @var{outputfd} is not a valid file descriptor open for
+writing, or @var{outputfd} has been opened with @code{O_APPEND}.
+
+@item EXDEV
+The input and output files reside on different file systems.
+@end table
+
+In addition, @code{copy_file_range} can fail with the error codes
+which are used by @code{read}, @code{pread}, @code{write}, and
+@code{pwrite}.
+
+The @code{copy_file_range} function is a cancellation point. In case of
+cancellation, the input location (the file position or the value at
+@code{*@var{inputpos}}) is indeterminate.
+@end deftypefun
+
@node Memory-mapped I/O
@section Memory-mapped I/O
diff --git a/posix/unistd.h b/posix/unistd.h
index 32b0f4898f..65317c79fd 100644
--- a/posix/unistd.h
+++ b/posix/unistd.h
@@ -1105,7 +1105,12 @@ extern int lockf64 (int __fd, int __cmd, __off64_t __len) __wur;
do __result = (long int) (expression); \
while (__result == -1L && errno == EINTR); \
__result; }))
-#endif
+
+/* Copy LENGTH bytes from INFD to OUTFD. */
+ssize_t copy_file_range (int __infd, __off64_t *__pinoff,
+ int __outfd, __off64_t *__poutoff,
+ size_t __length, unsigned int __flags);
+#endif /* __USE_GNU */
#if defined __USE_POSIX199309 || defined __USE_UNIX98
/* Synchronize at least the data part of a file with the underlying
diff --git a/support/Makefile b/support/Makefile
index bb81825fc2..7aa8dd0bdd 100644
--- a/support/Makefile
+++ b/support/Makefile
@@ -36,6 +36,7 @@ libsupport-routines = \
oom_error \
resolv_test \
set_fortify_handler \
+ support-xfstat \
support-xstat \
support_become_root \
support_can_chroot \
@@ -73,8 +74,10 @@ libsupport-routines = \
xfclose \
xfopen \
xfork \
+ xftruncate \
xgetsockname \
xlisten \
+ xlseek \
xmalloc \
xmemstream \
xmkdir \
diff --git a/support/support-xfstat.c b/support/support-xfstat.c
new file mode 100644
index 0000000000..4c8ee9142b
--- /dev/null
+++ b/support/support-xfstat.c
@@ -0,0 +1,28 @@
+/* fstat64 with error checking.
+ Copyright (C) 2017 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, see
+ <http://www.gnu.org/licenses/>. */
+
+#include <support/check.h>
+#include <support/xunistd.h>
+#include <sys/stat.h>
+
+void
+xfstat (int fd, struct stat64 *result)
+{
+ if (fstat64 (fd, result) != 0)
+ FAIL_EXIT1 ("fstat64 (%d): %m", fd);
+}
diff --git a/support/xftruncate.c b/support/xftruncate.c
new file mode 100644
index 0000000000..9c4e9e3050
--- /dev/null
+++ b/support/xftruncate.c
@@ -0,0 +1,27 @@
+/* ftruncate with error checking.
+ Copyright (C) 2017 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, see
+ <http://www.gnu.org/licenses/>. */
+
+#include <support/check.h>
+#include <support/xunistd.h>
+
+void
+xftruncate (int fd, long long length)
+{
+ if (ftruncate64 (fd, length) != 0)
+ FAIL_EXIT1 ("ftruncate64 (%d, %lld): %m", fd, length);
+}
diff --git a/support/xlseek.c b/support/xlseek.c
new file mode 100644
index 0000000000..0a75a9f2e6
--- /dev/null
+++ b/support/xlseek.c
@@ -0,0 +1,29 @@
+/* lseek with error checking.
+ Copyright (C) 2017 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, see
+ <http://www.gnu.org/licenses/>. */
+
+#include <support/check.h>
+#include <support/xunistd.h>
+
+long long
+xlseek (int fd, long long offset, int whence)
+{
+ long long result = lseek64 (fd, offset, whence);
+ if (result < 0)
+ FAIL_EXIT1 ("lseek64 (%d, %lld, %d): %m", fd, offset, whence);
+ return result;
+}
diff --git a/support/xunistd.h b/support/xunistd.h
index 05c2626a7b..548f234788 100644
--- a/support/xunistd.h
+++ b/support/xunistd.h
@@ -36,9 +36,12 @@ void xpipe (int[2]);
void xdup2 (int, int);
int xopen (const char *path, int flags, mode_t);
void xstat (const char *path, struct stat64 *);
+void xfstat (int fd, struct stat64 *);
void xmkdir (const char *path, mode_t);
void xchroot (const char *path);
void xunlink (const char *path);
+long long xlseek (int fd, long long offset, int whence);
+void xftruncate (int fd, long long length);
/* Read the link at PATH. The caller should free the returned string
with free. */
diff --git a/sysdeps/unix/sysv/linux/aarch64/libc.abilist b/sysdeps/unix/sysv/linux/aarch64/libc.abilist
index 3448d62cee..4ffcd7fcfd 100644
--- a/sysdeps/unix/sysv/linux/aarch64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/aarch64/libc.abilist
@@ -2104,6 +2104,7 @@ GLIBC_2.26 pwritev2 F
GLIBC_2.26 pwritev64v2 F
GLIBC_2.26 reallocarray F
GLIBC_2.27 GLIBC_2.27 A
+GLIBC_2.27 copy_file_range F
GLIBC_2.27 glob F
GLIBC_2.27 glob64 F
GLIBC_2.27 memfd_create F
diff --git a/sysdeps/unix/sysv/linux/alpha/libc.abilist b/sysdeps/unix/sysv/linux/alpha/libc.abilist
index d064f5445e..32777457d9 100644
--- a/sysdeps/unix/sysv/linux/alpha/libc.abilist
+++ b/sysdeps/unix/sysv/linux/alpha/libc.abilist
@@ -2015,6 +2015,7 @@ GLIBC_2.26 pwritev2 F
GLIBC_2.26 pwritev64v2 F
GLIBC_2.26 reallocarray F
GLIBC_2.27 GLIBC_2.27 A
+GLIBC_2.27 copy_file_range F
GLIBC_2.27 glob F
GLIBC_2.27 glob64 F
GLIBC_2.27 memfd_create F
diff --git a/sysdeps/unix/sysv/linux/arm/libc.abilist b/sysdeps/unix/sysv/linux/arm/libc.abilist
index a5ce7964d0..a0fc14b0fc 100644
--- a/sysdeps/unix/sysv/linux/arm/libc.abilist
+++ b/sysdeps/unix/sysv/linux/arm/libc.abilist
@@ -105,6 +105,7 @@ GLIBC_2.26 pwritev2 F
GLIBC_2.26 pwritev64v2 F
GLIBC_2.26 reallocarray F
GLIBC_2.27 GLIBC_2.27 A
+GLIBC_2.27 copy_file_range F
GLIBC_2.27 glob F
GLIBC_2.27 glob64 F
GLIBC_2.27 memfd_create F
diff --git a/sysdeps/unix/sysv/linux/copy_file_range.c b/sysdeps/unix/sysv/linux/copy_file_range.c
new file mode 100644
index 0000000000..1ec5f9d8d8
--- /dev/null
+++ b/sysdeps/unix/sysv/linux/copy_file_range.c
@@ -0,0 +1,46 @@
+/* Linux implementation of copy_file_range.
+ Copyright (C) 2017 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, see
+ <http://www.gnu.org/licenses/>. */
+
+#include <errno.h>
+#include <sysdep-cancel.h>
+#include <unistd.h>
+
+/* Include the fallback implementation. */
+#ifndef __ASSUME_COPY_FILE_RANGE
+#define COPY_FILE_RANGE_DECL static
+#define COPY_FILE_RANGE copy_file_range_compat
+#include <io/copy_file_range-compat.c>
+#endif
+
+ssize_t
+copy_file_range (int infd, __off64_t *pinoff,
+ int outfd, __off64_t *poutoff,
+ size_t length, unsigned int flags)
+{
+#ifdef __NR_copy_file_range
+ ssize_t ret = SYSCALL_CANCEL (copy_file_range, infd, pinoff, outfd, poutoff,
+ length, flags);
+# ifndef __ASSUME_COPY_FILE_RANGE
+ if (ret == -1 && errno == ENOSYS)
+ ret = copy_file_range_compat (infd, pinoff, outfd, poutoff, length, flags);
+# endif
+ return ret;
+#else /* !__NR_copy_file_range */
+ return copy_file_range_compat (infd, pinoff, outfd, poutoff, length, flags);
+#endif
+}
diff --git a/sysdeps/unix/sysv/linux/hppa/libc.abilist b/sysdeps/unix/sysv/linux/hppa/libc.abilist
index 69ddf15361..12e2b0b206 100644
--- a/sysdeps/unix/sysv/linux/hppa/libc.abilist
+++ b/sysdeps/unix/sysv/linux/hppa/libc.abilist
@@ -1869,6 +1869,7 @@ GLIBC_2.26 pwritev2 F
GLIBC_2.26 pwritev64v2 F
GLIBC_2.26 reallocarray F
GLIBC_2.27 GLIBC_2.27 A
+GLIBC_2.27 copy_file_range F
GLIBC_2.27 glob F
GLIBC_2.27 glob64 F
GLIBC_2.27 memfd_create F
diff --git a/sysdeps/unix/sysv/linux/i386/libc.abilist b/sysdeps/unix/sysv/linux/i386/libc.abilist
index a140edd4a3..52964476d5 100644
--- a/sysdeps/unix/sysv/linux/i386/libc.abilist
+++ b/sysdeps/unix/sysv/linux/i386/libc.abilist
@@ -2034,6 +2034,7 @@ GLIBC_2.26 strtof128_l F
GLIBC_2.26 wcstof128 F
GLIBC_2.26 wcstof128_l F
GLIBC_2.27 GLIBC_2.27 A
+GLIBC_2.27 copy_file_range F
GLIBC_2.27 glob F
GLIBC_2.27 glob64 F
GLIBC_2.27 memfd_create F
diff --git a/sysdeps/unix/sysv/linux/ia64/libc.abilist b/sysdeps/unix/sysv/linux/ia64/libc.abilist
index 178c0a45ec..58c1b0add8 100644
--- a/sysdeps/unix/sysv/linux/ia64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/ia64/libc.abilist
@@ -1898,6 +1898,7 @@ GLIBC_2.26 strtof128_l F
GLIBC_2.26 wcstof128 F
GLIBC_2.26 wcstof128_l F
GLIBC_2.27 GLIBC_2.27 A
+GLIBC_2.27 copy_file_range F
GLIBC_2.27 glob F
GLIBC_2.27 glob64 F
GLIBC_2.27 memfd_create F
diff --git a/sysdeps/unix/sysv/linux/kernel-features.h b/sysdeps/unix/sysv/linux/kernel-features.h
index 59b613377f..b9864790c7 100644
--- a/sysdeps/unix/sysv/linux/kernel-features.h
+++ b/sysdeps/unix/sysv/linux/kernel-features.h
@@ -111,3 +111,7 @@
#if __LINUX_KERNEL_VERSION >= 0x040400
# define __ASSUME_MLOCK2 1
#endif
+
+#if __LINUX_KERNEL_VERSION >= 0x040500
+# define __ASSUME_COPY_FILE_RANGE 1
+#endif
diff --git a/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist b/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist
index 01d10d907c..915ffbb7e8 100644
--- a/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist
+++ b/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist
@@ -106,6 +106,7 @@ GLIBC_2.26 pwritev2 F
GLIBC_2.26 pwritev64v2 F
GLIBC_2.26 reallocarray F
GLIBC_2.27 GLIBC_2.27 A
+GLIBC_2.27 copy_file_range F
GLIBC_2.27 glob F
GLIBC_2.27 glob64 F
GLIBC_2.27 memfd_create F
diff --git a/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist b/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist
index 3ad08c20bf..d2e0ebfebe 100644
--- a/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist
+++ b/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist
@@ -1983,6 +1983,7 @@ GLIBC_2.26 pwritev2 F
GLIBC_2.26 pwritev64v2 F
GLIBC_2.26 reallocarray F
GLIBC_2.27 GLIBC_2.27 A
+GLIBC_2.27 copy_file_range F
GLIBC_2.27 glob F
GLIBC_2.27 glob64 F
GLIBC_2.27 memfd_create F
diff --git a/sysdeps/unix/sysv/linux/microblaze/libc.abilist b/sysdeps/unix/sysv/linux/microblaze/libc.abilist
index 6bd7be1929..c58669ba2e 100644
--- a/sysdeps/unix/sysv/linux/microblaze/libc.abilist
+++ b/sysdeps/unix/sysv/linux/microblaze/libc.abilist
@@ -2104,6 +2104,7 @@ GLIBC_2.26 pwritev2 F
GLIBC_2.26 pwritev64v2 F
GLIBC_2.26 reallocarray F
GLIBC_2.27 GLIBC_2.27 A
+GLIBC_2.27 copy_file_range F
GLIBC_2.27 glob F
GLIBC_2.27 glob64 F
GLIBC_2.27 memfd_create F
diff --git a/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist
index 9b1e890eda..dd120b02c9 100644
--- a/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist
+++ b/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist
@@ -1958,6 +1958,7 @@ GLIBC_2.26 pwritev2 F
GLIBC_2.26 pwritev64v2 F
GLIBC_2.26 reallocarray F
GLIBC_2.27 GLIBC_2.27 A
+GLIBC_2.27 copy_file_range F
GLIBC_2.27 glob F
GLIBC_2.27 glob64 F
GLIBC_2.27 memfd_create F
diff --git a/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist
index 3eb5b66f8b..2aeb46917c 100644
--- a/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist
+++ b/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist
@@ -1956,6 +1956,7 @@ GLIBC_2.26 pwritev2 F
GLIBC_2.26 pwritev64v2 F
GLIBC_2.26 reallocarray F
GLIBC_2.27 GLIBC_2.27 A
+GLIBC_2.27 copy_file_range F
GLIBC_2.27 glob F
GLIBC_2.27 glob64 F
GLIBC_2.27 memfd_create F
diff --git a/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist
index 543a725114..b24b4134ca 100644
--- a/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist
+++ b/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist
@@ -1954,6 +1954,7 @@ GLIBC_2.26 pwritev2 F
GLIBC_2.26 pwritev64v2 F
GLIBC_2.26 reallocarray F
GLIBC_2.27 GLIBC_2.27 A
+GLIBC_2.27 copy_file_range F
GLIBC_2.27 glob F
GLIBC_2.27 glob64 F
GLIBC_2.27 memfd_create F
diff --git a/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist
index a9198a3936..6e4f827b69 100644
--- a/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist
@@ -1949,6 +1949,7 @@ GLIBC_2.26 pwritev2 F
GLIBC_2.26 pwritev64v2 F
GLIBC_2.26 reallocarray F
GLIBC_2.27 GLIBC_2.27 A
+GLIBC_2.27 copy_file_range F
GLIBC_2.27 glob F
GLIBC_2.27 glob64 F
GLIBC_2.27 memfd_create F
diff --git a/sysdeps/unix/sysv/linux/nios2/libc.abilist b/sysdeps/unix/sysv/linux/nios2/libc.abilist
index afacf1ff2d..ff4a3b9515 100644
--- a/sysdeps/unix/sysv/linux/nios2/libc.abilist
+++ b/sysdeps/unix/sysv/linux/nios2/libc.abilist
@@ -2145,6 +2145,7 @@ GLIBC_2.26 pwritev2 F
GLIBC_2.26 pwritev64v2 F
GLIBC_2.26 reallocarray F
GLIBC_2.27 GLIBC_2.27 A
+GLIBC_2.27 copy_file_range F
GLIBC_2.27 glob F
GLIBC_2.27 glob64 F
GLIBC_2.27 memfd_create F
diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist
index 48af097b6a..71e49a13d8 100644
--- a/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist
+++ b/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist
@@ -1987,6 +1987,7 @@ GLIBC_2.26 pwritev2 F
GLIBC_2.26 pwritev64v2 F
GLIBC_2.26 reallocarray F
GLIBC_2.27 GLIBC_2.27 A
+GLIBC_2.27 copy_file_range F
GLIBC_2.27 glob F
GLIBC_2.27 glob64 F
GLIBC_2.27 memfd_create F
diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist
index e30535dac9..2e8ca8bd93 100644
--- a/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist
+++ b/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist
@@ -1992,6 +1992,7 @@ GLIBC_2.26 pwritev2 F
GLIBC_2.26 pwritev64v2 F
GLIBC_2.26 reallocarray F
GLIBC_2.27 GLIBC_2.27 A
+GLIBC_2.27 copy_file_range F
GLIBC_2.27 glob F
GLIBC_2.27 glob64 F
GLIBC_2.27 memfd_create F
diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc64/libc-le.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc64/libc-le.abilist
index f522700890..f0c7a65b39 100644
--- a/sysdeps/unix/sysv/linux/powerpc/powerpc64/libc-le.abilist
+++ b/sysdeps/unix/sysv/linux/powerpc/powerpc64/libc-le.abilist
@@ -2199,6 +2199,7 @@ GLIBC_2.26 strtof128_l F
GLIBC_2.26 wcstof128 F
GLIBC_2.26 wcstof128_l F
GLIBC_2.27 GLIBC_2.27 A
+GLIBC_2.27 copy_file_range F
GLIBC_2.27 glob F
GLIBC_2.27 glob64 F
GLIBC_2.27 memfd_create F
diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc64/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc64/libc.abilist
index d3092afd25..95db2aad5c 100644
--- a/sysdeps/unix/sysv/linux/powerpc/powerpc64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/powerpc/powerpc64/libc.abilist
@@ -106,6 +106,7 @@ GLIBC_2.26 pwritev2 F
GLIBC_2.26 pwritev64v2 F
GLIBC_2.26 reallocarray F
GLIBC_2.27 GLIBC_2.27 A
+GLIBC_2.27 copy_file_range F
GLIBC_2.27 glob F
GLIBC_2.27 glob64 F
GLIBC_2.27 memfd_create F
diff --git a/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist b/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist
index 752176108e..c77fa8e07f 100644
--- a/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist
+++ b/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist
@@ -1987,6 +1987,7 @@ GLIBC_2.26 pwritev2 F
GLIBC_2.26 pwritev64v2 F
GLIBC_2.26 reallocarray F
GLIBC_2.27 GLIBC_2.27 A
+GLIBC_2.27 copy_file_range F
GLIBC_2.27 glob F
GLIBC_2.27 glob64 F
GLIBC_2.27 memfd_create F
diff --git a/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist b/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist
index b6d4c73635..c48fdcb978 100644
--- a/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist
@@ -1888,6 +1888,7 @@ GLIBC_2.26 pwritev2 F
GLIBC_2.26 pwritev64v2 F
GLIBC_2.26 reallocarray F
GLIBC_2.27 GLIBC_2.27 A
+GLIBC_2.27 copy_file_range F
GLIBC_2.27 glob F
GLIBC_2.27 glob64 F
GLIBC_2.27 memfd_create F
diff --git a/sysdeps/unix/sysv/linux/sh/libc.abilist b/sysdeps/unix/sysv/linux/sh/libc.abilist
index 1ee21fe8e8..7c77f15c65 100644
--- a/sysdeps/unix/sysv/linux/sh/libc.abilist
+++ b/sysdeps/unix/sysv/linux/sh/libc.abilist
@@ -1873,6 +1873,7 @@ GLIBC_2.26 pwritev2 F
GLIBC_2.26 pwritev64v2 F
GLIBC_2.26 reallocarray F
GLIBC_2.27 GLIBC_2.27 A
+GLIBC_2.27 copy_file_range F
GLIBC_2.27 glob F
GLIBC_2.27 glob64 F
GLIBC_2.27 memfd_create F
diff --git a/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist b/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist
index e652191c60..c23a2d9ca7 100644
--- a/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist
+++ b/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist
@@ -1980,6 +1980,7 @@ GLIBC_2.26 pwritev2 F
GLIBC_2.26 pwritev64v2 F
GLIBC_2.26 reallocarray F
GLIBC_2.27 GLIBC_2.27 A
+GLIBC_2.27 copy_file_range F
GLIBC_2.27 glob F
GLIBC_2.27 glob64 F
GLIBC_2.27 memfd_create F
diff --git a/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist b/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist
index 37cf8713a5..1c035bceec 100644
--- a/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist
@@ -1917,6 +1917,7 @@ GLIBC_2.26 pwritev2 F
GLIBC_2.26 pwritev64v2 F
GLIBC_2.26 reallocarray F
GLIBC_2.27 GLIBC_2.27 A
+GLIBC_2.27 copy_file_range F
GLIBC_2.27 glob F
GLIBC_2.27 glob64 F
GLIBC_2.27 memfd_create F
diff --git a/sysdeps/unix/sysv/linux/tile/tilegx/tilegx32/libc.abilist b/sysdeps/unix/sysv/linux/tile/tilegx/tilegx32/libc.abilist
index 57427eb3ee..d843815aa6 100644
--- a/sysdeps/unix/sysv/linux/tile/tilegx/tilegx32/libc.abilist
+++ b/sysdeps/unix/sysv/linux/tile/tilegx/tilegx32/libc.abilist
@@ -2111,6 +2111,7 @@ GLIBC_2.26 pwritev2 F
GLIBC_2.26 pwritev64v2 F
GLIBC_2.26 reallocarray F
GLIBC_2.27 GLIBC_2.27 A
+GLIBC_2.27 copy_file_range F
GLIBC_2.27 glob F
GLIBC_2.27 glob64 F
GLIBC_2.27 memfd_create F
diff --git a/sysdeps/unix/sysv/linux/tile/tilegx/tilegx64/libc.abilist b/sysdeps/unix/sysv/linux/tile/tilegx/tilegx64/libc.abilist
index 321f65c600..9d8b6f7ed2 100644
--- a/sysdeps/unix/sysv/linux/tile/tilegx/tilegx64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/tile/tilegx/tilegx64/libc.abilist
@@ -2111,6 +2111,7 @@ GLIBC_2.26 pwritev2 F
GLIBC_2.26 pwritev64v2 F
GLIBC_2.26 reallocarray F
GLIBC_2.27 GLIBC_2.27 A
+GLIBC_2.27 copy_file_range F
GLIBC_2.27 glob F
GLIBC_2.27 glob64 F
GLIBC_2.27 memfd_create F
diff --git a/sysdeps/unix/sysv/linux/tile/tilepro/libc.abilist b/sysdeps/unix/sysv/linux/tile/tilepro/libc.abilist
index 57427eb3ee..d843815aa6 100644
--- a/sysdeps/unix/sysv/linux/tile/tilepro/libc.abilist
+++ b/sysdeps/unix/sysv/linux/tile/tilepro/libc.abilist
@@ -2111,6 +2111,7 @@ GLIBC_2.26 pwritev2 F
GLIBC_2.26 pwritev64v2 F
GLIBC_2.26 reallocarray F
GLIBC_2.27 GLIBC_2.27 A
+GLIBC_2.27 copy_file_range F
GLIBC_2.27 glob F
GLIBC_2.27 glob64 F
GLIBC_2.27 memfd_create F
diff --git a/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist b/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist
index f26c8b99d5..7365702953 100644
--- a/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist
@@ -1875,6 +1875,7 @@ GLIBC_2.26 strtof128_l F
GLIBC_2.26 wcstof128 F
GLIBC_2.26 wcstof128_l F
GLIBC_2.27 GLIBC_2.27 A
+GLIBC_2.27 copy_file_range F
GLIBC_2.27 glob F
GLIBC_2.27 glob64 F
GLIBC_2.27 memfd_create F
diff --git a/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist b/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist
index 2a6057154b..ebc228d4e7 100644
--- a/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist
+++ b/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist
@@ -2118,6 +2118,7 @@ GLIBC_2.26 strtof128_l F
GLIBC_2.26 wcstof128 F
GLIBC_2.26 wcstof128_l F
GLIBC_2.27 GLIBC_2.27 A
+GLIBC_2.27 copy_file_range F
GLIBC_2.27 glob F
GLIBC_2.27 glob64 F
GLIBC_2.27 memfd_create F
^ permalink raw reply [flat|nested] 24+ messages in thread
* Re: [PATCH] copy_file_range: New function to copy file data
2017-12-05 14:03 ` Florian Weimer
@ 2017-12-14 14:26 ` Florian Weimer
2017-12-18 20:04 ` Adhemerval Zanella
1 sibling, 0 replies; 24+ messages in thread
From: Florian Weimer @ 2017-12-14 14:26 UTC (permalink / raw)
To: libc-alpha
On 12/05/2017 03:03 PM, Florian Weimer wrote:
> 2017-12-05 Florian Weimer<fweimer@redhat.com>
>
> * io/Makefile (routines): Add copy_file_range.
> (tests): Add tst-copy_file_range.
> (tests-static, tests-internal): Add tst-copy_file_range-compat.
> * io/Versions (GLIBC_2.27): Export copy_file_range.
> * io/copy_file_range-compat.c: New file.
> * io/copy_file_range.c: Likewise.
> * io/tst-copy_file_range-compat.c: Likewise.
> * io/tst-copy_file_range.c: Likewise.
> * manual/llio.texi (Copying File Data): New section.
> * posix/unistd.h [__USE_GNU] (copy_file_range): Declare.
> * support/Makefile (libsupport-routines): Add support-xfstat,
> xftruncate, xlseek.
> * support/support-xfstat.c: New file.
> * support/xftruncate.c: Likewise.
> * support/xlseek.c: Likewise.
> * support/xunistd.h (xfstat, xftruncate, xlseek): Declare.
> * sysdeps/unix/sysv/linux/**.abilist: Update.
> * sysdeps/unix/sysv/linux/copy_file_range.c: New file.
Ping?
<https://sourceware.org/ml/libc-alpha/2017-12/msg00122.html>
Thanks,
Florian
^ permalink raw reply [flat|nested] 24+ messages in thread
* Re: [PATCH] copy_file_range: New function to copy file data
2017-12-05 14:03 ` Florian Weimer
2017-12-14 14:26 ` Florian Weimer
@ 2017-12-18 20:04 ` Adhemerval Zanella
2017-12-18 20:19 ` Florian Weimer
2017-12-18 21:54 ` Joseph Myers
1 sibling, 2 replies; 24+ messages in thread
From: Adhemerval Zanella @ 2017-12-18 20:04 UTC (permalink / raw)
To: libc-alpha; +Cc: Florian Weimer
[-- Attachment #1: Type: text/plain, Size: 67924 bytes --]
On 05/12/2017 12:03, Florian Weimer wrote:
> The attached version of the patch fixes a few 32-bit issues in the code (non-64 functions being called, and size_t-to-off64_t conversion issue). I renamed the -common.c file into -compat.c, for consistency with how the file is used.
>
> It also drops the zero-as-ENOSPC special case Andreas recognized as unnecessary.
>
> Thanks,
> Florian
>
> copy_file_range.patch
>
>
> Subject: [PATCH] copy_file_range: New function to copy file data
> To: libc-alpha@sourceware.org
>
> The semantics are based on the Linux system call, but a very close
> emulation in user space is provided.
>
> 2017-12-05 Florian Weimer <fweimer@redhat.com>
>
> * io/Makefile (routines): Add copy_file_range.
> (tests): Add tst-copy_file_range.
> (tests-static, tests-internal): Add tst-copy_file_range-compat.
> * io/Versions (GLIBC_2.27): Export copy_file_range.
> * io/copy_file_range-compat.c: New file.
> * io/copy_file_range.c: Likewise.
> * io/tst-copy_file_range-compat.c: Likewise.
> * io/tst-copy_file_range.c: Likewise.
> * manual/llio.texi (Copying File Data): New section.
> * posix/unistd.h [__USE_GNU] (copy_file_range): Declare.
> * support/Makefile (libsupport-routines): Add support-xfstat,
> xftruncate, xlseek.
> * support/support-xfstat.c: New file.
> * support/xftruncate.c: Likewise.
> * support/xlseek.c: Likewise.
> * support/xunistd.h (xfstat, xftruncate, xlseek): Declare.
> * sysdeps/unix/sysv/linux/**.abilist: Update.
> * sysdeps/unix/sysv/linux/copy_file_range.c: New file.
I am seeing a lot of failures from tst-copy_file_range on both x86_64 and i686
when using the fallback implementation (io/copy_file_range.c) due the fact
write is returning EFBIG (I attached the test output):
[...]
openat(AT_FDCWD, "/tmp/tst-copy_file_range-in-mT9I6j", O_RDWR) = 3
ftruncate(3, 0) = 0
openat(AT_FDCWD, "/tmp/tst-copy_file_range-out-0RbGw2", O_RDWR) = 4
ftruncate(4, 0)
[...]
fcntl(4, F_GETFL) = 0x8002 (flags O_RDWR|O_LARGEFILE)
read(3, "k2dft\31*b#F=P.AyuQ[\22M\2\37\21f\0243\20\r\177\33Ak"..., 8192) = 8192
write(4, "k2dft\31*b#F=P.AyuQ[\22M\2\37\21f\0243\20\r\177\33Ak"..., 8192) = -1 EFBIG (File too large)
lseek(3, -8192, SEEK_CUR) = 0
write(1, "tst-copy_file_range.c:285: numer"..., 54tst-copy_file_range.c:285: numeric comparison failure
[...]
Since the documentation does not explicit state this possible issue I think
we need to either remap this to another error code or document this possible
failure. I would say to update the documentation with EFBIG, since the syscall
also returns the same errno in this case.
I also noted it does not provided a non-LFS version and it a good way forward
imho, however I think we need to explicit handle the case where a non-LFS
invocation tries to use copy_file_range in a non-supported way. For instance
the snippet:
[...]
int fin = open ("/tmp/file.in", O_RDWR | O_CREAT | O_TRUNC, 0600); int fout = open ("/tmp/file.out", O_RDWR | O_CREAT | O_TRUNC, 0600); char buffer[8192] = { 0xcc }; const size_t size = 8192; pwrite (fin, buffer, size, 0); copy_file_range (fin, 0, fout, &(__off64_t) { INT32_MAX }, size, 0);
[...]
Will again return EFBIG. We have some options as 1. handle EFBIG
as an expected retuned error, 2. do not declare copy_file_range for
!__USE_FILE_OFFSET64, 3. add a dummy implementation for non-LFS
(which return ENOSYS).
>
> diff --git a/io/Makefile b/io/Makefile
> index 2f26bf56db..f2f0f0d040 100644
> --- a/io/Makefile
> +++ b/io/Makefile
> @@ -52,7 +52,7 @@ routines := \
> ftw ftw64 fts fts64 poll ppoll \
> posix_fadvise posix_fadvise64 \
> posix_fallocate posix_fallocate64 \
> - sendfile sendfile64 \
> + sendfile sendfile64 copy_file_range \
> utimensat futimens
>
> # These routines will be omitted from the libc shared object.
> @@ -70,7 +70,13 @@ tests := test-utime test-stat test-stat2 test-lfs tst-getcwd \
> tst-symlinkat tst-linkat tst-readlinkat tst-mkdirat \
> tst-mknodat tst-mkfifoat tst-ttyname_r bug-ftw5 \
> tst-posix_fallocate tst-posix_fallocate64 \
> - tst-fts tst-fts-lfs tst-open-tmpfile
> + tst-fts tst-fts-lfs tst-open-tmpfile \
> + tst-copy_file_range \
> +
> +# This test includes the compat implementation of copy_file_range,
> +# which uses internal, unexported libc functions.
> +tests-static += tst-copy_file_range-compat
> +tests-internal += tst-copy_file_range-compat
>
> ifeq ($(run-built-tests),yes)
> tests-special += $(objpfx)ftwtest.out
Ok.
> diff --git a/io/Versions b/io/Versions
> index 64316cd025..98898cb9d5 100644
> --- a/io/Versions
> +++ b/io/Versions
> @@ -125,4 +125,7 @@ libc {
> GLIBC_2.23 {
> fts64_children; fts64_close; fts64_open; fts64_read; fts64_set;
> }
> + GLIBC_2.27 {
> + copy_file_range;
> + }
> }
Ok.
> diff --git a/io/copy_file_range-compat.c b/io/copy_file_range-compat.c
> new file mode 100644
> index 0000000000..ba6ea389df
> --- /dev/null
> +++ b/io/copy_file_range-compat.c
> @@ -0,0 +1,158 @@
> +/* Emulation of copy_file_range.
> + Copyright (C) 2017 Free Software Foundation, Inc.
> + This file is part of the GNU C Library.
> +
> + The GNU C Library is free software; you can redistribute it and/or
> + modify it under the terms of the GNU Lesser General Public
> + License as published by the Free Software Foundation; either
> + version 2.1 of the License, or (at your option) any later version.
> +
> + The GNU C Library is distributed in the hope that it will be useful,
> + but WITHOUT ANY WARRANTY; without even the implied warranty of
> + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
> + Lesser General Public License for more details.
> +
> + You should have received a copy of the GNU Lesser General Public
> + License along with the GNU C Library; if not, see
> + <http://www.gnu.org/licenses/>. */
> +
> +/* The following macros should be defined before including this
> + file:
> +
> + COPY_FILE_RANGE_DECL Declaration specifiers for the function below.
> + COPY_FILE_RANGE Name of the function to define. */
> +
> +#include <errno.h>
> +#include <fcntl.h>
> +#include <inttypes.h>
> +#include <limits.h>
> +#include <sys/stat.h>
> +#include <sys/types.h>
> +#include <unistd.h>
> +
> +COPY_FILE_RANGE_DECL
> +ssize_t
> +COPY_FILE_RANGE (int infd, __off64_t *pinoff,
> + int outfd, __off64_t *poutoff,
> + size_t length, unsigned int flags)
> +{
> + if (flags != 0)
> + {
> + __set_errno (EINVAL);
> + return -1;
> + }
> +
> + struct stat64 instat;
> + struct stat64 outstat;
> + if (fstat64 (infd, &instat) != 0 || fstat64 (outfd, &outstat) != 0)
> + return -1;
> + if (S_ISDIR (instat.st_mode) || S_ISDIR (outstat.st_mode))
> + {
> + __set_errno (EISDIR);
> + return -1;
> + }
> + if (!S_ISREG (instat.st_mode) || !S_ISREG (outstat.st_mode))
> + {
> + /* We need a regular input file so that the we can seek
> + backwards in case of a write failure. */
> + __set_errno (EINVAL);
> + return -1;
> + }
> + if (instat.st_dev != outstat.st_dev)
> + {
> + /* Cross-device copies are not supported. */
> + __set_errno (EXDEV);
> + return -1;
> + }
> +
> + /* The output descriptor must not have O_APPEND set. */
> + {
> + int flags = __fcntl (outfd, F_GETFL);
> + if (flags & O_APPEND)
> + {
> + __set_errno (EBADF);
> + return -1;
> + }
> + }
> +
> + /* Avoid an overflow in the result. */
> + if (length > SSIZE_MAX)
> + length = SSIZE_MAX;
> +
> + /* Main copying loop. The buffer size is arbitrary and is a
> + trade-off between stack size consumption, cache usage, and
> + amortization of system call overhead. */
> + size_t copied = 0;
> + char buf[8192];
Do we you have any numbers with shorter sizes? Maybe
> + while (length > 0)
> + {
> + size_t to_read = length;
> + if (to_read > sizeof (buf))
> + to_read = sizeof (buf);
> +
> + /* Fill the buffer. */
> + ssize_t read_count;
> + if (pinoff == NULL)
> + read_count = read (infd, buf, to_read);
> + else
> + read_count = __libc_pread64 (infd, buf, to_read, *pinoff);
> + if (read_count == 0)
> + /* End of file reached prematurely. */
> + return copied;
> + if (read_count < 0)
> + {
> + if (copied > 0)
> + /* Report the number of bytes copied so far. */
> + return copied;
> + return -1;
> + }
> + if (pinoff != 0)
> + *pinoff += read_count;
> +
> + /* Write the buffer part which was read to the destination. */
> + char *end = buf + read_count;
> + for (char *p = buf; p < end; )
> + {
> + ssize_t write_count;
> + if (poutoff == NULL)
> + write_count = write (outfd, p, end - p);
> + else
> + write_count = __libc_pwrite64 (outfd, p, end - p, *poutoff);
> + if (write_count < 0)
> + {
> + /* Adjust the input read position to match what we have
> + written, so that the caller can pick up after the
> + error. */
> + size_t written = p - buf;
> + /* NB: This needs to be signed so that we can form the
> + negative value below. */
> + ssize_t overread = read_count - written;
> + if (pinoff == NULL)
> + {
> + if (overread > 0)
> + {
> + /* We are on an error recovery path, so we
> + cannot deal with failure here. */
> + int save_errno = errno;
> + (void) __libc_lseek64 (infd, -overread, SEEK_CUR);
> + __set_errno (save_errno);
> + }
> + }
> + else /* pinoff != NULL */
> + *pinoff -= overread;
> +
> + if (copied > 0)
> + /* Report the number of bytes copied so far. */
> + return copied + written;
> + return -1;
> + }
> + p += write_count;
> + if (poutoff != NULL)
> + *poutoff += write_count;
> + } /* Write loop. */
> +
> + copied += read_count;
> + length -= read_count;
> + }
> + return copied;
> +}
Ok.
> diff --git a/io/copy_file_range.c b/io/copy_file_range.c
> new file mode 100644
> index 0000000000..61ee6871b4
> --- /dev/null
> +++ b/io/copy_file_range.c
> @@ -0,0 +1,22 @@
> +/* Generic implementation of copy_file_range.
> + Copyright (C) 2017 Free Software Foundation, Inc.
> + This file is part of the GNU C Library.
> +
> + The GNU C Library is free software; you can redistribute it and/or
> + modify it under the terms of the GNU Lesser General Public
> + License as published by the Free Software Foundation; either
> + version 2.1 of the License, or (at your option) any later version.
> +
> + The GNU C Library is distributed in the hope that it will be useful,
> + but WITHOUT ANY WARRANTY; without even the implied warranty of
> + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
> + Lesser General Public License for more details.
> +
> + You should have received a copy of the GNU Lesser General Public
> + License along with the GNU C Library; if not, see
> + <http://www.gnu.org/licenses/>. */
> +
> +#define COPY_FILE_RANGE_DECL
> +#define COPY_FILE_RANGE copy_file_range
> +
> +#include <io/copy_file_range-compat.c>
Ok.
> diff --git a/io/tst-copy_file_range-compat.c b/io/tst-copy_file_range-compat.c
> new file mode 100644
> index 0000000000..eb737946d9
> --- /dev/null
> +++ b/io/tst-copy_file_range-compat.c
> @@ -0,0 +1,30 @@
> +/* Test the fallback implementation of copy_file_range.
> + Copyright (C) 2017 Free Software Foundation, Inc.
> + This file is part of the GNU C Library.
> +
> + The GNU C Library is free software; you can redistribute it and/or
> + modify it under the terms of the GNU Lesser General Public
> + License as published by the Free Software Foundation; either
> + version 2.1 of the License, or (at your option) any later version.
> +
> + The GNU C Library is distributed in the hope that it will be useful,
> + but WITHOUT ANY WARRANTY; without even the implied warranty of
> + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
> + Lesser General Public License for more details.
> +
> + You should have received a copy of the GNU Lesser General Public
> + License along with the GNU C Library; if not, see
> + <http://www.gnu.org/licenses/>. */
> +
> +/* Get the declaration of the official copy_of_range function. */
> +#include <unistd.h>
> +
> +/* Compile a local version of copy_file_range. */
> +#define COPY_FILE_RANGE_DECL static
> +#define COPY_FILE_RANGE copy_file_range_compat
> +#include <io/copy_file_range-compat.c>
> +
> +/* Re-use the test, but run it against copy_file_range_compat defined
> + above. */
> +#define copy_file_range copy_file_range_compat
> +#include "tst-copy_file_range.c"
> diff --git a/io/tst-copy_file_range.c b/io/tst-copy_file_range.c
> new file mode 100644
> index 0000000000..d7532f288a
> --- /dev/null
> +++ b/io/tst-copy_file_range.c
> @@ -0,0 +1,730 @@
> +/* Tests for copy_file_range.
> + Copyright (C) 2017 Free Software Foundation, Inc.
> + This file is part of the GNU C Library.
> +
> + The GNU C Library is free software; you can redistribute it and/or
> + modify it under the terms of the GNU Lesser General Public
> + License as published by the Free Software Foundation; either
> + version 2.1 of the License, or (at your option) any later version.
> +
> + The GNU C Library is distributed in the hope that it will be useful,
> + but WITHOUT ANY WARRANTY; without even the implied warranty of
> + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
> + Lesser General Public License for more details.
> +
> + You should have received a copy of the GNU Lesser General Public
> + License along with the GNU C Library; if not, see
> + <http://www.gnu.org/licenses/>. */
> +
> +#include <array_length.h>
> +#include <errno.h>
> +#include <fcntl.h>
> +#include <inttypes.h>
> +#include <libgen.h>
> +#include <poll.h>
> +#include <sched.h>
> +#include <stdbool.h>
> +#include <stdio.h>
> +#include <stdlib.h>
> +#include <string.h>
> +#include <support/check.h>
> +#include <support/namespace.h>
> +#include <support/support.h>
> +#include <support/temp_file.h>
> +#include <support/test-driver.h>
> +#include <support/xunistd.h>
> +#include <sys/mount.h>
> +
> +/* Boolean flags which indicate whether to use pointers with explicit
> + output flags. */
> +static int do_inoff;
> +static int do_outoff;
> +
> +/* Name and descriptors of the input files. Files are truncated and
> + reopened (with O_RDWR) between tests. */
> +static char *infile;
> +static int infd;
> +static char *outfile;
> +static int outfd;
> +
> +/* Like the above, but on a different file system. xdevfile can be
> + NULL if no suitable file system has been found. */
> +static char *xdevfile;
> +
> +/* Input and output offsets. Set according to do_inoff and do_outoff
> + before the test. The offsets themselves are always set to
> + zero. */
> +static off64_t inoff;
> +static off64_t *pinoff;
> +static off64_t outoff;
> +static off64_t *poutoff;
> +
> +/* These are a collection of copy sizes used in tests. The selection
> + takes into account that the fallback implementation uses an
> + internal buffer of 8192 bytes. */
> +enum { maximum_size = 99999 };
> +static const int typical_sizes[] =
> + { 0, 1, 2, 3, 1024, 2048, 4096, 8191, 8192, 8193, 16383, 16384, 16385,
> + maximum_size };
> +
> +/* The random contents of this array can be used as a pattern to check
> + for correct write operations. */
> +static unsigned char random_data[maximum_size];
> +
> +/* The size chosen by the test harness. */
> +static int current_size;
> +
> +/* Discover the maximum file size (writable offset) for outfd.
> + Truncate outfd to length zero as a side effect. */
> +static off64_t
> +find_maximum_offset (void)
> +{
> + xftruncate (outfd, 0);
> + static off64_t cache = 0;
> + if (cache != 0)
> + return cache;
> +
> + uint64_t upper = -1;
> + upper >>= 1; /* Maximum of off64_t. */
> + TEST_VERIFY ((off64_t) upper > 0);
> + TEST_VERIFY ((off64_t) (upper + 1) < 0);
> + if (lseek64 (outfd, upper, SEEK_SET) >= 0)
> + {
> + if (write (outfd, "", 1) == 1)
> + FAIL_EXIT1 ("created a file larger than the off64_t range");
> + }
> +
> + uint64_t lower = 1024 * 1024; /* A reasonable minimum file size. */
> + /* Loop invariant: writing at lower succeeds, writing at upper fails. */
> + while (lower + 1 < upper)
> + {
> + uint64_t middle = (lower + upper) / 2;
> + if (test_verbose > 0)
> + printf ("info: %s: remaining test range %" PRIu64 " .. %" PRIu64
> + ", probe at %" PRIu64 "\n", __func__, lower, upper, middle);
> + xftruncate (outfd, 0);
> + if (lseek64 (outfd, middle, SEEK_SET) >= 0
> + && write (outfd, "", 1) == 1)
> + lower = middle;
> + else
> + upper = middle;
> + }
> + TEST_VERIFY (lower + 1 == upper);
> + xftruncate (outfd, 0);
> + cache = lower;
> + printf ("info: maximum writable file offset: %" PRIu64 " (%" PRIx64 ")\n",
> + lower, lower);
> + return lower;
> +}
> +
> +/* Perform a copy of a file. */
> +static void
> +simple_file_copy (void)
> +{
> + xwrite (infd, random_data, current_size);
> +
> + int length;
> + int in_skipped; /* Expected skipped bytes in input. */
> + if (do_inoff)
> + {
> + xlseek (infd, 1, SEEK_SET);
> + inoff = 2;
> + length = current_size - 3;
> + in_skipped = 2;
> + }
> + else
> + {
> + xlseek (infd, 3, SEEK_SET);
> + length = current_size - 5;
> + in_skipped = 3;
> + }
> + int out_skipped; /* Expected skipped bytes before the written data. */
> + if (do_outoff)
> + {
> + xlseek (outfd, 4, SEEK_SET);
> + outoff = 5;
> + out_skipped = 5;
> + }
> + else
> + {
> + xlseek (outfd, 6, SEEK_SET);
> + length = current_size - 6;
> + out_skipped = 6;
> + }
> + if (length < 0)
> + length = 0;
> +
> + TEST_COMPARE (copy_file_range (infd, pinoff, outfd, poutoff,
> + length, 0), length);
> + if (do_inoff)
> + {
> + TEST_COMPARE (inoff, 2 + length);
> + TEST_COMPARE (xlseek (infd, 0, SEEK_CUR), 1);
> + }
> + else
> + TEST_COMPARE (xlseek (infd, 0, SEEK_CUR), 3 + length);
> + if (do_outoff)
> + {
> + TEST_COMPARE (outoff, 5 + length);
> + TEST_COMPARE (xlseek (outfd, 0, SEEK_CUR), 4);
> + }
> + else
> + TEST_COMPARE (xlseek (outfd, 0, SEEK_CUR), 6 + length);
> +
> + struct stat64 st;
> + xfstat (outfd, &st);
> + if (length > 0)
> + TEST_COMPARE (st.st_size, out_skipped + length);
> + else
> + {
> + /* If we did not write anything, we also did not add any
> + padding. */
> + TEST_COMPARE (st.st_size, 0);
> + return;
> + }
> +
> + xlseek (outfd, 0, SEEK_SET);
> + char *bytes = xmalloc (st.st_size);
> + TEST_COMPARE (read (outfd, bytes, st.st_size), st.st_size);
> + for (int i = 0; i < out_skipped; ++i)
> + TEST_COMPARE (bytes[i], 0);
> + TEST_VERIFY (memcmp (bytes + out_skipped, random_data + in_skipped,
> + length) == 0);
> + free (bytes);
> +}
> +
> +/* Test that reading from a pipe willfails. */
> +static void
> +pipe_as_source (void)
> +{
> + int pipefds[2];
> + xpipe (pipefds);
> +
> + for (int length = 0; length < 2; ++length)
> + {
> + if (test_verbose > 0)
> + printf ("info: %s: length=%d\n", __func__, length);
> +
> + /* Make sure that there is something to copy in the pipe. */
> + xwrite (pipefds[1], "@", 1);
> +
> + TEST_COMPARE (copy_file_range (pipefds[0], pinoff, outfd, poutoff,
> + length, 0), -1);
> + /* Linux 4.10 and later return EINVAL. Older kernels return
> + EXDEV. */
> + TEST_VERIFY (errno == EINVAL || errno == EXDEV);
> + TEST_COMPARE (inoff, 0);
> + TEST_COMPARE (outoff, 0);
> + TEST_COMPARE (xlseek (outfd, 0, SEEK_CUR), 0);
> +
> + /* Make sure that nothing was read. */
> + char buf = 'A';
> + TEST_COMPARE (read (pipefds[0], &buf, 1), 1);
> + TEST_COMPARE (buf, '@');
> + }
> +
> + xclose (pipefds[0]);
> + xclose (pipefds[1]);
> +}
> +
> +/* Test that writing to a pipe fails. */
> +static void
> +pipe_as_destination (void)
> +{
> + /* Make sure that there is something to read in the input file. */
> + xwrite (infd, "abc", 3);
> + xlseek (infd, 0, SEEK_SET);
> +
> + int pipefds[2];
> + xpipe (pipefds);
> +
> + for (int length = 0; length < 2; ++length)
> + {
> + if (test_verbose > 0)
> + printf ("info: %s: length=%d\n", __func__, length);
> +
> + TEST_COMPARE (copy_file_range (infd, pinoff, pipefds[1], poutoff,
> + length, 0), -1);
> + /* Linux 4.10 and later return EINVAL. Older kernels return
> + EXDEV. */
> + TEST_VERIFY (errno == EINVAL || errno == EXDEV);
> + TEST_COMPARE (inoff, 0);
> + TEST_COMPARE (outoff, 0);
> + TEST_COMPARE (xlseek (infd, 0, SEEK_CUR), 0);
> +
> + /* Make sure that nothing was written. */
> + struct pollfd pollfd = { .fd = pipefds[0], .events = POLLIN, };
> + TEST_COMPARE (poll (&pollfd, 1, 0), 0);
> + }
> +
> + xclose (pipefds[0]);
> + xclose (pipefds[1]);
> +}
> +
> +/* Test a write failure after (potentially) writing some bytes. */
> +static void
> +delayed_write_failure (void)
> +{
> + /* We need to write something to provoke the error. */
> + if (current_size == 0)
> + return;
> + xwrite (infd, random_data, sizeof (random_data));
> + xlseek (infd, 0, SEEK_SET);
> +
> + /* Write failure near the start. */
> + off64_t where = find_maximum_offset ();
> + if (current_size == 1)
> + ++where;
> + outoff = where;
> + if (do_outoff)
> + xlseek (outfd, 1, SEEK_SET);
> + else
> + xlseek (outfd, where, SEEK_SET);
> + TEST_COMPARE (copy_file_range (infd, pinoff, outfd, poutoff,
> + sizeof (random_data), 0), -1);
> + TEST_COMPARE (errno, EINVAL);
> + TEST_COMPARE (xlseek (infd, 0, SEEK_CUR), 0);
> + if (do_outoff)
> + TEST_COMPARE (xlseek (outfd, 0, SEEK_CUR), 1);
> + else
> + TEST_COMPARE (xlseek (outfd, 0, SEEK_CUR), where);
> + TEST_COMPARE (outoff, where);
> + struct stat64 st;
> + xfstat (outfd, &st);
> + TEST_COMPARE (st.st_size, 0);
> +
> + /* Write failure near the end. */
> + if (current_size == 1)
> + /* This would be same as the first test because there is only one
> + byte. */
> + return;
> + where = find_maximum_offset () - current_size + 1;
> + if (current_size == sizeof (random_data))
> + /* Otherwise we do not reach the non-writable byte. */
> + ++where;
> + outoff = where;
> + if (do_outoff)
> + xlseek (outfd, 1, SEEK_SET);
> + else
> + xlseek (outfd, where, SEEK_SET);
> + ssize_t ret = copy_file_range (infd, pinoff, outfd, poutoff,
> + sizeof (random_data), 0);
> + if (ret < 0)
> + {
> + TEST_COMPARE (ret, -1);
> + TEST_COMPARE (errno, EINVAL);
> + xfstat (outfd, &st);
> + TEST_COMPARE (st.st_size, 0);
> + }
> + else
> + {
> + /* The first copy succeeded. This happens in the emulation
> + because the internal buffer of limited size does not
> + necessarily cross the off64_t boundary on the first write
> + operation. */
> + if (test_verbose > 0)
> + printf ("info: copy_file_range (%zu) returned %zd\n",
> + sizeof (random_data), ret);
> + TEST_VERIFY (ret > 0);
> + TEST_VERIFY (ret < maximum_size);
> + xfstat (outfd, &st);
> + TEST_COMPARE (st.st_size, where + ret);
> + if (do_inoff)
> + {
> + TEST_COMPARE (inoff, ret);
> + TEST_COMPARE (xlseek (infd, 0, SEEK_CUR), 0);
> + }
> + else
> + TEST_COMPARE (xlseek (infd, 0, SEEK_CUR), ret);
> +
> + char *buffer = xmalloc (ret);
> + TEST_COMPARE (pread64 (outfd, buffer, ret, where), ret);
> + TEST_VERIFY (memcmp (buffer, random_data, ret) == 0);
> + free (buffer);
> +
> + /* The second copy fails. */
> + TEST_COMPARE (copy_file_range (infd, pinoff, outfd, poutoff,
> + sizeof (random_data), 0), -1);
> + TEST_COMPARE (errno, EINVAL);
> + }
> +}
> +
> +/* Test a write failure across devices. */
> +static void
> +cross_device_failure (void)
> +{
> + if (xdevfile == NULL)
> + /* Subtest not supported due to missing cross-device file. */
> + return;
> +
> + /* We need something to write. */
> + xwrite (infd, random_data, sizeof (random_data));
> + xlseek (infd, 0, SEEK_SET);
> +
> + int xdevfd = xopen (xdevfile, O_RDWR | O_LARGEFILE, 0);
> + TEST_COMPARE (copy_file_range (infd, pinoff, xdevfd, poutoff,
> + current_size, 0), -1);
> + TEST_COMPARE (errno, EXDEV);
> + TEST_COMPARE (xlseek (infd, 0, SEEK_CUR), 0);
> + struct stat64 st;
> + xfstat (xdevfd, &st);
> + TEST_COMPARE (st.st_size, 0);
> +
> + xclose (xdevfd);
> +}
> +
> +/* Try to exercise ENOSPC behavior with a tempfs file system (so that
> + we do not have to fill up a regular file system to get the error).
> + This function runs in a subprocess, so that we do not change the
> + mount namespace of the actual test process. */
> +static void
> +enospc_failure_1 (void *closure)
> +{
> +#ifdef CLONE_NEWNS
> + support_become_root ();
> +
> + /* Make sure that we do not alter the file system mounts of the
> + parents. */
> + if (! support_enter_mount_namespace ())
> + {
> + printf ("warning: ENOSPC test skipped\n");
> + return;
> + }
> +
> + char *mountpoint = closure;
> + if (mount ("none", mountpoint, "tmpfs", MS_NODEV | MS_NOEXEC,
> + "size=500k") != 0)
> + {
> + printf ("warning: could not mount tmpfs at %s: %m\n", mountpoint);
> + return;
> + }
> +
> + /* The source file must reside on the same file system. */
> + char *intmpfsfile = xasprintf ("%s/%s", mountpoint, "in");
> + int intmpfsfd = xopen (intmpfsfile, O_RDWR | O_CREAT | O_LARGEFILE, 0600);
> + xwrite (intmpfsfd, random_data, sizeof (random_data));
> + xlseek (intmpfsfd, 1, SEEK_SET);
> + inoff = 1;
> +
> + char *outtmpfsfile = xasprintf ("%s/%s", mountpoint, "out");
> + int outtmpfsfd = xopen (outtmpfsfile, O_RDWR | O_CREAT | O_LARGEFILE, 0600);
> +
> + /* Fill the file with data until ENOSPC is reached. */
> + while (true)
> + {
> + ssize_t ret = write (outtmpfsfd, random_data, sizeof (random_data));
> + if (ret < 0 && errno != ENOSPC)
> + FAIL_EXIT1 ("write to %s: %m", outtmpfsfile);
> + if (ret < sizeof (random_data))
> + break;
> + }
> + TEST_COMPARE (write (outtmpfsfd, "", 1), -1);
> + TEST_COMPARE (errno, ENOSPC);
> + off64_t maxsize = xlseek (outtmpfsfd, 0, SEEK_CUR);
> + TEST_VERIFY_EXIT (maxsize > sizeof (random_data));
> +
> + /* Constructed the expected file contents. */
> + char *expected = xmalloc (maxsize);
> + TEST_COMPARE (pread64 (outtmpfsfd, expected, maxsize, 0), maxsize);
> + /* Go back a little, so some bytes can be written. */
> + enum { offset = 20000 };
> + TEST_VERIFY_EXIT (offset < maxsize);
> + TEST_VERIFY_EXIT (offset < sizeof (random_data));
> + memcpy (expected + maxsize - offset, random_data + 1, offset);
> +
> + if (do_outoff)
> + {
> + outoff = maxsize - offset;
> + xlseek (outtmpfsfd, 2, SEEK_SET);
> + }
> + else
> + xlseek (outtmpfsfd, -offset, SEEK_CUR);
> +
> + /* First call is expected to succeed because we made room for some
> + bytes. */
> + TEST_COMPARE (copy_file_range (intmpfsfd, pinoff, outtmpfsfd, poutoff,
> + maximum_size, 0), offset);
> + if (do_inoff)
> + {
> + TEST_COMPARE (inoff, 1 + offset);
> + TEST_COMPARE (xlseek (intmpfsfd, 0, SEEK_CUR), 1);
> + }
> + else
> + TEST_COMPARE (xlseek (intmpfsfd, 0, SEEK_CUR), 1 + offset);
> + if (do_outoff)
> + {
> + TEST_COMPARE (outoff, maxsize);
> + TEST_COMPARE (xlseek (outtmpfsfd, 0, SEEK_CUR), 2);
> + }
> + else
> + TEST_COMPARE (xlseek (outtmpfsfd, 0, SEEK_CUR), maxsize);
> + struct stat64 st;
> + xfstat (outtmpfsfd, &st);
> + TEST_COMPARE (st.st_size, maxsize);
> + char *actual = xmalloc (st.st_size);
> + TEST_COMPARE (pread64 (outtmpfsfd, actual, st.st_size, 0), st.st_size);
> + TEST_VERIFY (memcmp (expected, actual, maxsize) == 0);
> +
> + /* Second call should fail with ENOSPC. */
> + TEST_COMPARE (copy_file_range (intmpfsfd, pinoff, outtmpfsfd, poutoff,
> + maximum_size, 0), -1);
> + TEST_COMPARE (errno, ENOSPC);
> +
> + /* Offsets should be unchanged. */
> + if (do_inoff)
> + {
> + TEST_COMPARE (inoff, 1 + offset);
> + TEST_COMPARE (xlseek (intmpfsfd, 0, SEEK_CUR), 1);
> + }
> + else
> + TEST_COMPARE (xlseek (intmpfsfd, 0, SEEK_CUR), 1 + offset);
> + if (do_outoff)
> + {
> + TEST_COMPARE (outoff, maxsize);
> + TEST_COMPARE (xlseek (outtmpfsfd, 0, SEEK_CUR), 2);
> + }
> + else
> + TEST_COMPARE (xlseek (outtmpfsfd, 0, SEEK_CUR), maxsize);
> + TEST_COMPARE (xlseek (outtmpfsfd, 0, SEEK_END), maxsize);
> + TEST_COMPARE (pread64 (outtmpfsfd, actual, maxsize, 0), maxsize);
> + TEST_VERIFY (memcmp (expected, actual, maxsize) == 0);
> +
> + free (actual);
> + free (expected);
> +
> + xclose (intmpfsfd);
> + xclose (outtmpfsfd);
> + free (intmpfsfile);
> + free (outtmpfsfile);
> +
> +#else /* !CLONE_NEWNS */
> + puts ("warning: ENOSPC test skipped (no mount namespaces)");
> +#endif
> +}
> +
> +/* Call enospc_failure_1 in a subprocess. */
> +static void
> +enospc_failure (void)
> +{
> + char *mountpoint
> + = support_create_temp_directory ("tst-copy_file_range-enospc-");
> + support_isolate_in_subprocess (enospc_failure_1, mountpoint);
> + free (mountpoint);
> +}
> +
> +/* The target file descriptor must have O_APPEND enabled. */
> +static void
> +oappend_failure (void)
> +{
> + /* Add data, to make sure we do not fail because there is
> + insufficient input data. */
> + xwrite (infd, random_data, current_size);
> + xlseek (infd, 0, SEEK_SET);
> +
> + xclose (outfd);
> + outfd = xopen (outfile, O_RDWR | O_APPEND, 0);
> + TEST_COMPARE (copy_file_range (infd, pinoff, outfd, poutoff,
> + current_size, 0), -1);
> + TEST_COMPARE (errno, EBADF);
> +}
> +
> +/* Test that a short input file results in a shortened copy. */
> +static void
> +short_copy (void)
> +{
> + if (current_size == 0)
> + /* Nothing to shorten. */
> + return;
> +
> + /* Two subtests, one with offset 0 and current_size - 1 bytes, and
> + another one with current_size bytes, but offset 1. */
> + for (int shift = 0; shift < 2; ++shift)
> + {
> + if (test_verbose > 0)
> + printf ("info: shift=%d\n", shift);
> + xftruncate (infd, 0);
> + xlseek (infd, 0, SEEK_SET);
> + xwrite (infd, random_data, current_size - !shift);
> +
> + if (do_inoff)
> + {
> + inoff = shift;
> + xlseek (infd, 2, SEEK_SET);
> + }
> + else
> + {
> + inoff = 3;
> + xlseek (infd, shift, SEEK_SET);
> + }
> + ftruncate (outfd, 0);
> + xlseek (outfd, 0, SEEK_SET);
> + outoff = 0;
> +
> + /* First call copies current_size - 1 bytes. */
> + TEST_COMPARE (copy_file_range (infd, pinoff, outfd, poutoff,
> + current_size, 0), current_size - 1);
> + char *buffer = xmalloc (current_size);
> + TEST_COMPARE (pread64 (outfd, buffer, current_size, 0),
> + current_size - 1);
> + TEST_VERIFY (memcmp (buffer, random_data + shift, current_size - 1)
> + == 0);
> + free (buffer);
> +
> + if (do_inoff)
> + {
> + TEST_COMPARE (inoff, current_size - 1 + shift);
> + TEST_COMPARE (xlseek (infd, 0, SEEK_CUR), 2);
> + }
> + else
> + TEST_COMPARE (xlseek (infd, 0, SEEK_CUR), current_size - 1 + shift);
> + if (do_outoff)
> + {
> + TEST_COMPARE (outoff, current_size - 1);
> + TEST_COMPARE (xlseek (outfd, 0, SEEK_CUR), 0);
> + }
> + else
> + TEST_COMPARE (xlseek (outfd, 0, SEEK_CUR), current_size - 1);
> +
> + /* First call copies zero bytes. */
> + TEST_COMPARE (copy_file_range (infd, pinoff, outfd, poutoff,
> + current_size, 0), 0);
> + /* And the offsets are unchanged. */
> + if (do_inoff)
> + {
> + TEST_COMPARE (inoff, current_size - 1 + shift);
> + TEST_COMPARE (xlseek (infd, 0, SEEK_CUR), 2);
> + }
> + else
> + TEST_COMPARE (xlseek (infd, 0, SEEK_CUR), current_size - 1 + shift);
> + if (do_outoff)
> + {
> + TEST_COMPARE (outoff, current_size - 1);
> + TEST_COMPARE (xlseek (outfd, 0, SEEK_CUR), 0);
> + }
> + else
> + TEST_COMPARE (xlseek (outfd, 0, SEEK_CUR), current_size - 1);
> + }
> +}
> +
> +/* A named test function. */
> +struct test_case
> +{
> + const char *name;
> + void (*func) (void);
> + bool sizes; /* If true, call the test with different current_size values. */
> +};
> +
> +/* The available test cases. */
> +static struct test_case tests[] =
> + {
> + { "simple_file_copy", simple_file_copy, .sizes = true },
> + { "pipe_as_source", pipe_as_source, },
> + { "pipe_as_destination", pipe_as_destination, },
> + { "delayed_write_failure", delayed_write_failure, .sizes = true },
> + { "cross_device_failure", cross_device_failure, .sizes = true },
> + { "enospc_failure", enospc_failure, },
> + { "oappend_failure", oappend_failure, .sizes = true },
> + { "short_copy", short_copy, .sizes = true },
> + };
> +
> +static int
> +do_test (void)
> +{
> + for (unsigned char *p = random_data; p < array_end (random_data); ++p)
> + *p = rand () >> 24;
> +
> + infd = create_temp_file ("tst-copy_file_range-in-", &infile);
> + xclose (create_temp_file ("tst-copy_file_range-out-", &outfile));
> +
> + /* Try to find a different directory from the default input/output
> + file. */
> + {
> + struct stat64 instat;
> + xfstat (infd, &instat);
> + static const char *const candidates[] =
> + { NULL, "/var/tmp", "/dev/shm" };
> + for (const char *const *c = candidates; c < array_end (candidates); ++c)
> + {
> + const char *path = *c;
> + char *to_free = NULL;
> + if (path == NULL)
> + {
> + to_free = xreadlink ("/proc/self/exe");
> + path = dirname (to_free);
> + }
> +
> + struct stat64 cstat;
> + xstat (path, &cstat);
> + if (cstat.st_dev == instat.st_dev)
> + {
> + free (to_free);
> + continue;
> + }
> +
> + printf ("info: using alternate temporary files directory: %s\n", path);
> + xdevfile = xasprintf ("%s/tst-copy_file_range-xdev-XXXXXX", path);
> + free (to_free);
> + break;
> + }
> + if (xdevfile != NULL)
> + {
> + int xdevfd = mkstemp (xdevfile);
> + if (xdevfd < 0)
> + FAIL_EXIT1 ("mkstemp (\"%s\"): %m", xdevfile);
> + struct stat64 xdevst;
> + xfstat (xdevfd, &xdevst);
> + TEST_VERIFY (xdevst.st_dev != instat.st_dev);
> + add_temp_file (xdevfile);
> + xclose (xdevfd);
> + }
> + else
> + puts ("warning: no alternate directory on different file system found");
> + }
> + xclose (infd);
> +
> + for (do_inoff = 0; do_inoff < 2; ++do_inoff)
> + for (do_outoff = 0; do_outoff < 2; ++do_outoff)
> + for (struct test_case *test = tests; test < array_end (tests); ++test)
> + for (const int *size = typical_sizes;
> + size < array_end (typical_sizes); ++size)
> + {
> + current_size = *size;
> + if (test_verbose > 0)
> + printf ("info: %s do_inoff=%d do_outoff=%d current_size=%d\n",
> + test->name, do_inoff, do_outoff, current_size);
> +
> + inoff = 0;
> + if (do_inoff)
> + pinoff = &inoff;
> + else
> + pinoff = NULL;
> + outoff = 0;
> + if (do_outoff)
> + poutoff = &outoff;
> + else
> + poutoff = NULL;
> +
> + infd = xopen (infile, O_RDWR | O_LARGEFILE, 0);
> + xftruncate (infd, 0);
> + outfd = xopen (outfile, O_RDWR | O_LARGEFILE, 0);
> + xftruncate (outfd, 0);
> +
> + test->func ();
> +
> + xclose (infd);
> + xclose (outfd);
> +
> + if (!test->sizes)
> + /* Skip the other sizes unless they have been
> + requested. */
> + break;
> + }
> +
> + free (infile);
> + free (outfile);
> + free (xdevfile);
> +
> + return 0;
> +}
> +
> +#include <support/test-driver.c>
> diff --git a/manual/llio.texi b/manual/llio.texi
> index 8b2f599c79..37593f18d4 100644
> --- a/manual/llio.texi
> +++ b/manual/llio.texi
> @@ -41,6 +41,7 @@ directly.)
> * Stream/Descriptor Precautions:: Precautions needed if you use both
> descriptors and streams.
> * Scatter-Gather:: Fast I/O to discontinuous buffers.
> +* Copying File Data:: Copying data between files.
> * Memory-mapped I/O:: Using files like memory.
> * Waiting for I/O:: How to check for input or output
> on multiple file descriptors.
> @@ -1353,6 +1354,80 @@ When the source file is compiled using @code{_FILE_OFFSET_BITS == 64} on a
> @code{pwritev2} and so transparently replaces the 32 bit interface.
> @end deftypefun
>
> +@node Copying File Data
> +@section Copying data between two files
> +@cindex copying files
> +@cindex file copy
> +
> +A special function is provided to copy data between two files on the
> +same file system. The system can optimize such copy operations. This
> +is particularly important on network file systems, where the data would
> +otherwise have to be transferred twice over the network.
> +
> +Note that this function only copies file data, but not metadata such as
> +file permissions or extended attributes.
> +
> +@deftypefun ssize_t copy_file_range (int @var{inputfd}, off64_t *@var{inputpos}, int @var{outputfd}, off64_t *@var{outputpos}, ssize_t @var{length}, unsigned int @var{flags})
> +@standards{GNU, unistd.h}
> +@safety{@prelim{}@mtsafe{}@assafe{}@acsafe{}}
> +
> +This function copies up to @var{length} bytes from the file descriptor
> +@var{inputfd} to the file descriptor @var{outputfd}.
> +
> +The function can operate on both the current file position (like
> +@code{read} and @code{write}) and an explicit offset (like @code{pread}
> +and @code{pwrite}). If the @var{inputpos} pointer is null, the file
> +position of @var{inputfd} is used as the starting point of the copy
> +operation, and the file position is advanced during it. If
> +@var{inputpos} is not null, then @code{*@var{inputpos}} is used as the
> +starting point of the copy operation, and @code{*@var{inputpos}} is
> +incremented by the number of copied bytes, but the file position remains
> +unchanged. Similar rules apply to @var{outputfd} and @var{outputpos}
> +for the output file position.
> +
> +The @var{flags} argument is currently reserved and must be zero.
> +
> +The @code{copy_file_range} function returns the number of bytes copied.
> +This can be less than the specified @var{length} in case the input file
> +contains fewer remaining bytes than @var{length}, or if a read or write
> +failure occurs. The return value is zero if the end of the input file
> +is encountered immediately.
> +
> +If no bytes can be copied, to report an error, @code{copy_file_range}
> +returns the value @math{-1} and sets @code{errno}. The following
> +@code{errno} error conditions are specific to this function:
> +
> +@table @code
> +@item EISDIR
> +At least one of the descriptors @var{inputfd} or @var{outputfd} refers
> +to a directory.
> +
> +@item EINVAL
> +At least one of the descriptors @var{inputfd} or @var{outputfd} refers
> +to a non-regular, non-directory file (such as a socket or a FIFO).
> +
> +The @var{flags} argument is not zero.
> +
> +@item EBADF
> +The argument @var{inputfd} is not a valid file descriptor open for
> +reading.
> +
> +The argument @var{outputfd} is not a valid file descriptor open for
> +writing, or @var{outputfd} has been opened with @code{O_APPEND}.
> +
> +@item EXDEV
> +The input and output files reside on different file systems.
> +@end table
> +
> +In addition, @code{copy_file_range} can fail with the error codes
> +which are used by @code{read}, @code{pread}, @code{write}, and
> +@code{pwrite}.
> +
> +The @code{copy_file_range} function is a cancellation point. In case of
> +cancellation, the input location (the file position or the value at
> +@code{*@var{inputpos}}) is indeterminate.
> +@end deftypefun
> +
> @node Memory-mapped I/O
> @section Memory-mapped I/O
>
> diff --git a/posix/unistd.h b/posix/unistd.h
> index 32b0f4898f..65317c79fd 100644
> --- a/posix/unistd.h
> +++ b/posix/unistd.h
> @@ -1105,7 +1105,12 @@ extern int lockf64 (int __fd, int __cmd, __off64_t __len) __wur;
> do __result = (long int) (expression); \
> while (__result == -1L && errno == EINTR); \
> __result; }))
> -#endif
> +
> +/* Copy LENGTH bytes from INFD to OUTFD. */
> +ssize_t copy_file_range (int __infd, __off64_t *__pinoff,
> + int __outfd, __off64_t *__poutoff,
> + size_t __length, unsigned int __flags);
> +#endif /* __USE_GNU */
>
> #if defined __USE_POSIX199309 || defined __USE_UNIX98
> /* Synchronize at least the data part of a file with the underlying
> diff --git a/support/Makefile b/support/Makefile
> index bb81825fc2..7aa8dd0bdd 100644
> --- a/support/Makefile
> +++ b/support/Makefile
> @@ -36,6 +36,7 @@ libsupport-routines = \
> oom_error \
> resolv_test \
> set_fortify_handler \
> + support-xfstat \
> support-xstat \
> support_become_root \
> support_can_chroot \
> @@ -73,8 +74,10 @@ libsupport-routines = \
> xfclose \
> xfopen \
> xfork \
> + xftruncate \
> xgetsockname \
> xlisten \
> + xlseek \
> xmalloc \
> xmemstream \
> xmkdir \
> diff --git a/support/support-xfstat.c b/support/support-xfstat.c
> new file mode 100644
> index 0000000000..4c8ee9142b
> --- /dev/null
> +++ b/support/support-xfstat.c
> @@ -0,0 +1,28 @@
> +/* fstat64 with error checking.
> + Copyright (C) 2017 Free Software Foundation, Inc.
> + This file is part of the GNU C Library.
> +
> + The GNU C Library is free software; you can redistribute it and/or
> + modify it under the terms of the GNU Lesser General Public
> + License as published by the Free Software Foundation; either
> + version 2.1 of the License, or (at your option) any later version.
> +
> + The GNU C Library is distributed in the hope that it will be useful,
> + but WITHOUT ANY WARRANTY; without even the implied warranty of
> + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
> + Lesser General Public License for more details.
> +
> + You should have received a copy of the GNU Lesser General Public
> + License along with the GNU C Library; if not, see
> + <http://www.gnu.org/licenses/>. */
> +
> +#include <support/check.h>
> +#include <support/xunistd.h>
> +#include <sys/stat.h>
> +
> +void
> +xfstat (int fd, struct stat64 *result)
> +{
> + if (fstat64 (fd, result) != 0)
> + FAIL_EXIT1 ("fstat64 (%d): %m", fd);
> +}
> diff --git a/support/xftruncate.c b/support/xftruncate.c
> new file mode 100644
> index 0000000000..9c4e9e3050
> --- /dev/null
> +++ b/support/xftruncate.c
> @@ -0,0 +1,27 @@
> +/* ftruncate with error checking.
> + Copyright (C) 2017 Free Software Foundation, Inc.
> + This file is part of the GNU C Library.
> +
> + The GNU C Library is free software; you can redistribute it and/or
> + modify it under the terms of the GNU Lesser General Public
> + License as published by the Free Software Foundation; either
> + version 2.1 of the License, or (at your option) any later version.
> +
> + The GNU C Library is distributed in the hope that it will be useful,
> + but WITHOUT ANY WARRANTY; without even the implied warranty of
> + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
> + Lesser General Public License for more details.
> +
> + You should have received a copy of the GNU Lesser General Public
> + License along with the GNU C Library; if not, see
> + <http://www.gnu.org/licenses/>. */
> +
> +#include <support/check.h>
> +#include <support/xunistd.h>
> +
> +void
> +xftruncate (int fd, long long length)
> +{
> + if (ftruncate64 (fd, length) != 0)
> + FAIL_EXIT1 ("ftruncate64 (%d, %lld): %m", fd, length);
> +}
> diff --git a/support/xlseek.c b/support/xlseek.c
> new file mode 100644
> index 0000000000..0a75a9f2e6
> --- /dev/null
> +++ b/support/xlseek.c
> @@ -0,0 +1,29 @@
> +/* lseek with error checking.
> + Copyright (C) 2017 Free Software Foundation, Inc.
> + This file is part of the GNU C Library.
> +
> + The GNU C Library is free software; you can redistribute it and/or
> + modify it under the terms of the GNU Lesser General Public
> + License as published by the Free Software Foundation; either
> + version 2.1 of the License, or (at your option) any later version.
> +
> + The GNU C Library is distributed in the hope that it will be useful,
> + but WITHOUT ANY WARRANTY; without even the implied warranty of
> + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
> + Lesser General Public License for more details.
> +
> + You should have received a copy of the GNU Lesser General Public
> + License along with the GNU C Library; if not, see
> + <http://www.gnu.org/licenses/>. */
> +
> +#include <support/check.h>
> +#include <support/xunistd.h>
> +
> +long long
> +xlseek (int fd, long long offset, int whence)
> +{
> + long long result = lseek64 (fd, offset, whence);
> + if (result < 0)
> + FAIL_EXIT1 ("lseek64 (%d, %lld, %d): %m", fd, offset, whence);
> + return result;
> +}
> diff --git a/support/xunistd.h b/support/xunistd.h
> index 05c2626a7b..548f234788 100644
> --- a/support/xunistd.h
> +++ b/support/xunistd.h
> @@ -36,9 +36,12 @@ void xpipe (int[2]);
> void xdup2 (int, int);
> int xopen (const char *path, int flags, mode_t);
> void xstat (const char *path, struct stat64 *);
> +void xfstat (int fd, struct stat64 *);
> void xmkdir (const char *path, mode_t);
> void xchroot (const char *path);
> void xunlink (const char *path);
> +long long xlseek (int fd, long long offset, int whence);
> +void xftruncate (int fd, long long length);
>
> /* Read the link at PATH. The caller should free the returned string
> with free. */
> diff --git a/sysdeps/unix/sysv/linux/aarch64/libc.abilist b/sysdeps/unix/sysv/linux/aarch64/libc.abilist
> index 3448d62cee..4ffcd7fcfd 100644
> --- a/sysdeps/unix/sysv/linux/aarch64/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/aarch64/libc.abilist
> @@ -2104,6 +2104,7 @@ GLIBC_2.26 pwritev2 F
> GLIBC_2.26 pwritev64v2 F
> GLIBC_2.26 reallocarray F
> GLIBC_2.27 GLIBC_2.27 A
> +GLIBC_2.27 copy_file_range F
> GLIBC_2.27 glob F
> GLIBC_2.27 glob64 F
> GLIBC_2.27 memfd_create F
> diff --git a/sysdeps/unix/sysv/linux/alpha/libc.abilist b/sysdeps/unix/sysv/linux/alpha/libc.abilist
> index d064f5445e..32777457d9 100644
> --- a/sysdeps/unix/sysv/linux/alpha/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/alpha/libc.abilist
> @@ -2015,6 +2015,7 @@ GLIBC_2.26 pwritev2 F
> GLIBC_2.26 pwritev64v2 F
> GLIBC_2.26 reallocarray F
> GLIBC_2.27 GLIBC_2.27 A
> +GLIBC_2.27 copy_file_range F
> GLIBC_2.27 glob F
> GLIBC_2.27 glob64 F
> GLIBC_2.27 memfd_create F
> diff --git a/sysdeps/unix/sysv/linux/arm/libc.abilist b/sysdeps/unix/sysv/linux/arm/libc.abilist
> index a5ce7964d0..a0fc14b0fc 100644
> --- a/sysdeps/unix/sysv/linux/arm/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/arm/libc.abilist
> @@ -105,6 +105,7 @@ GLIBC_2.26 pwritev2 F
> GLIBC_2.26 pwritev64v2 F
> GLIBC_2.26 reallocarray F
> GLIBC_2.27 GLIBC_2.27 A
> +GLIBC_2.27 copy_file_range F
> GLIBC_2.27 glob F
> GLIBC_2.27 glob64 F
> GLIBC_2.27 memfd_create F
> diff --git a/sysdeps/unix/sysv/linux/copy_file_range.c b/sysdeps/unix/sysv/linux/copy_file_range.c
> new file mode 100644
> index 0000000000..1ec5f9d8d8
> --- /dev/null
> +++ b/sysdeps/unix/sysv/linux/copy_file_range.c
> @@ -0,0 +1,46 @@
> +/* Linux implementation of copy_file_range.
> + Copyright (C) 2017 Free Software Foundation, Inc.
> + This file is part of the GNU C Library.
> +
> + The GNU C Library is free software; you can redistribute it and/or
> + modify it under the terms of the GNU Lesser General Public
> + License as published by the Free Software Foundation; either
> + version 2.1 of the License, or (at your option) any later version.
> +
> + The GNU C Library is distributed in the hope that it will be useful,
> + but WITHOUT ANY WARRANTY; without even the implied warranty of
> + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
> + Lesser General Public License for more details.
> +
> + You should have received a copy of the GNU Lesser General Public
> + License along with the GNU C Library; if not, see
> + <http://www.gnu.org/licenses/>. */
> +
> +#include <errno.h>
> +#include <sysdep-cancel.h>
> +#include <unistd.h>
> +
> +/* Include the fallback implementation. */
> +#ifndef __ASSUME_COPY_FILE_RANGE
> +#define COPY_FILE_RANGE_DECL static
> +#define COPY_FILE_RANGE copy_file_range_compat
> +#include <io/copy_file_range-compat.c>
> +#endif
> +
> +ssize_t
> +copy_file_range (int infd, __off64_t *pinoff,
> + int outfd, __off64_t *poutoff,
> + size_t length, unsigned int flags)
> +{
> +#ifdef __NR_copy_file_range
> + ssize_t ret = SYSCALL_CANCEL (copy_file_range, infd, pinoff, outfd, poutoff,
> + length, flags);
> +# ifndef __ASSUME_COPY_FILE_RANGE
> + if (ret == -1 && errno == ENOSYS)
> + ret = copy_file_range_compat (infd, pinoff, outfd, poutoff, length, flags);
> +# endif
> + return ret;
> +#else /* !__NR_copy_file_range */
> + return copy_file_range_compat (infd, pinoff, outfd, poutoff, length, flags);
> +#endif
> +}
> diff --git a/sysdeps/unix/sysv/linux/hppa/libc.abilist b/sysdeps/unix/sysv/linux/hppa/libc.abilist
> index 69ddf15361..12e2b0b206 100644
> --- a/sysdeps/unix/sysv/linux/hppa/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/hppa/libc.abilist
> @@ -1869,6 +1869,7 @@ GLIBC_2.26 pwritev2 F
> GLIBC_2.26 pwritev64v2 F
> GLIBC_2.26 reallocarray F
> GLIBC_2.27 GLIBC_2.27 A
> +GLIBC_2.27 copy_file_range F
> GLIBC_2.27 glob F
> GLIBC_2.27 glob64 F
> GLIBC_2.27 memfd_create F
> diff --git a/sysdeps/unix/sysv/linux/i386/libc.abilist b/sysdeps/unix/sysv/linux/i386/libc.abilist
> index a140edd4a3..52964476d5 100644
> --- a/sysdeps/unix/sysv/linux/i386/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/i386/libc.abilist
> @@ -2034,6 +2034,7 @@ GLIBC_2.26 strtof128_l F
> GLIBC_2.26 wcstof128 F
> GLIBC_2.26 wcstof128_l F
> GLIBC_2.27 GLIBC_2.27 A
> +GLIBC_2.27 copy_file_range F
> GLIBC_2.27 glob F
> GLIBC_2.27 glob64 F
> GLIBC_2.27 memfd_create F
> diff --git a/sysdeps/unix/sysv/linux/ia64/libc.abilist b/sysdeps/unix/sysv/linux/ia64/libc.abilist
> index 178c0a45ec..58c1b0add8 100644
> --- a/sysdeps/unix/sysv/linux/ia64/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/ia64/libc.abilist
> @@ -1898,6 +1898,7 @@ GLIBC_2.26 strtof128_l F
> GLIBC_2.26 wcstof128 F
> GLIBC_2.26 wcstof128_l F
> GLIBC_2.27 GLIBC_2.27 A
> +GLIBC_2.27 copy_file_range F
> GLIBC_2.27 glob F
> GLIBC_2.27 glob64 F
> GLIBC_2.27 memfd_create F
> diff --git a/sysdeps/unix/sysv/linux/kernel-features.h b/sysdeps/unix/sysv/linux/kernel-features.h
> index 59b613377f..b9864790c7 100644
> --- a/sysdeps/unix/sysv/linux/kernel-features.h
> +++ b/sysdeps/unix/sysv/linux/kernel-features.h
> @@ -111,3 +111,7 @@
> #if __LINUX_KERNEL_VERSION >= 0x040400
> # define __ASSUME_MLOCK2 1
> #endif
> +
> +#if __LINUX_KERNEL_VERSION >= 0x040500
> +# define __ASSUME_COPY_FILE_RANGE 1
> +#endif
> diff --git a/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist b/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist
> index 01d10d907c..915ffbb7e8 100644
> --- a/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist
> @@ -106,6 +106,7 @@ GLIBC_2.26 pwritev2 F
> GLIBC_2.26 pwritev64v2 F
> GLIBC_2.26 reallocarray F
> GLIBC_2.27 GLIBC_2.27 A
> +GLIBC_2.27 copy_file_range F
> GLIBC_2.27 glob F
> GLIBC_2.27 glob64 F
> GLIBC_2.27 memfd_create F
> diff --git a/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist b/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist
> index 3ad08c20bf..d2e0ebfebe 100644
> --- a/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist
> @@ -1983,6 +1983,7 @@ GLIBC_2.26 pwritev2 F
> GLIBC_2.26 pwritev64v2 F
> GLIBC_2.26 reallocarray F
> GLIBC_2.27 GLIBC_2.27 A
> +GLIBC_2.27 copy_file_range F
> GLIBC_2.27 glob F
> GLIBC_2.27 glob64 F
> GLIBC_2.27 memfd_create F
> diff --git a/sysdeps/unix/sysv/linux/microblaze/libc.abilist b/sysdeps/unix/sysv/linux/microblaze/libc.abilist
> index 6bd7be1929..c58669ba2e 100644
> --- a/sysdeps/unix/sysv/linux/microblaze/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/microblaze/libc.abilist
> @@ -2104,6 +2104,7 @@ GLIBC_2.26 pwritev2 F
> GLIBC_2.26 pwritev64v2 F
> GLIBC_2.26 reallocarray F
> GLIBC_2.27 GLIBC_2.27 A
> +GLIBC_2.27 copy_file_range F
> GLIBC_2.27 glob F
> GLIBC_2.27 glob64 F
> GLIBC_2.27 memfd_create F
> diff --git a/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist
> index 9b1e890eda..dd120b02c9 100644
> --- a/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist
> @@ -1958,6 +1958,7 @@ GLIBC_2.26 pwritev2 F
> GLIBC_2.26 pwritev64v2 F
> GLIBC_2.26 reallocarray F
> GLIBC_2.27 GLIBC_2.27 A
> +GLIBC_2.27 copy_file_range F
> GLIBC_2.27 glob F
> GLIBC_2.27 glob64 F
> GLIBC_2.27 memfd_create F
> diff --git a/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist
> index 3eb5b66f8b..2aeb46917c 100644
> --- a/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist
> @@ -1956,6 +1956,7 @@ GLIBC_2.26 pwritev2 F
> GLIBC_2.26 pwritev64v2 F
> GLIBC_2.26 reallocarray F
> GLIBC_2.27 GLIBC_2.27 A
> +GLIBC_2.27 copy_file_range F
> GLIBC_2.27 glob F
> GLIBC_2.27 glob64 F
> GLIBC_2.27 memfd_create F
> diff --git a/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist
> index 543a725114..b24b4134ca 100644
> --- a/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist
> @@ -1954,6 +1954,7 @@ GLIBC_2.26 pwritev2 F
> GLIBC_2.26 pwritev64v2 F
> GLIBC_2.26 reallocarray F
> GLIBC_2.27 GLIBC_2.27 A
> +GLIBC_2.27 copy_file_range F
> GLIBC_2.27 glob F
> GLIBC_2.27 glob64 F
> GLIBC_2.27 memfd_create F
> diff --git a/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist
> index a9198a3936..6e4f827b69 100644
> --- a/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist
> @@ -1949,6 +1949,7 @@ GLIBC_2.26 pwritev2 F
> GLIBC_2.26 pwritev64v2 F
> GLIBC_2.26 reallocarray F
> GLIBC_2.27 GLIBC_2.27 A
> +GLIBC_2.27 copy_file_range F
> GLIBC_2.27 glob F
> GLIBC_2.27 glob64 F
> GLIBC_2.27 memfd_create F
> diff --git a/sysdeps/unix/sysv/linux/nios2/libc.abilist b/sysdeps/unix/sysv/linux/nios2/libc.abilist
> index afacf1ff2d..ff4a3b9515 100644
> --- a/sysdeps/unix/sysv/linux/nios2/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/nios2/libc.abilist
> @@ -2145,6 +2145,7 @@ GLIBC_2.26 pwritev2 F
> GLIBC_2.26 pwritev64v2 F
> GLIBC_2.26 reallocarray F
> GLIBC_2.27 GLIBC_2.27 A
> +GLIBC_2.27 copy_file_range F
> GLIBC_2.27 glob F
> GLIBC_2.27 glob64 F
> GLIBC_2.27 memfd_create F
> diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist
> index 48af097b6a..71e49a13d8 100644
> --- a/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist
> @@ -1987,6 +1987,7 @@ GLIBC_2.26 pwritev2 F
> GLIBC_2.26 pwritev64v2 F
> GLIBC_2.26 reallocarray F
> GLIBC_2.27 GLIBC_2.27 A
> +GLIBC_2.27 copy_file_range F
> GLIBC_2.27 glob F
> GLIBC_2.27 glob64 F
> GLIBC_2.27 memfd_create F
> diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist
> index e30535dac9..2e8ca8bd93 100644
> --- a/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist
> @@ -1992,6 +1992,7 @@ GLIBC_2.26 pwritev2 F
> GLIBC_2.26 pwritev64v2 F
> GLIBC_2.26 reallocarray F
> GLIBC_2.27 GLIBC_2.27 A
> +GLIBC_2.27 copy_file_range F
> GLIBC_2.27 glob F
> GLIBC_2.27 glob64 F
> GLIBC_2.27 memfd_create F
> diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc64/libc-le.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc64/libc-le.abilist
> index f522700890..f0c7a65b39 100644
> --- a/sysdeps/unix/sysv/linux/powerpc/powerpc64/libc-le.abilist
> +++ b/sysdeps/unix/sysv/linux/powerpc/powerpc64/libc-le.abilist
> @@ -2199,6 +2199,7 @@ GLIBC_2.26 strtof128_l F
> GLIBC_2.26 wcstof128 F
> GLIBC_2.26 wcstof128_l F
> GLIBC_2.27 GLIBC_2.27 A
> +GLIBC_2.27 copy_file_range F
> GLIBC_2.27 glob F
> GLIBC_2.27 glob64 F
> GLIBC_2.27 memfd_create F
> diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc64/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc64/libc.abilist
> index d3092afd25..95db2aad5c 100644
> --- a/sysdeps/unix/sysv/linux/powerpc/powerpc64/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/powerpc/powerpc64/libc.abilist
> @@ -106,6 +106,7 @@ GLIBC_2.26 pwritev2 F
> GLIBC_2.26 pwritev64v2 F
> GLIBC_2.26 reallocarray F
> GLIBC_2.27 GLIBC_2.27 A
> +GLIBC_2.27 copy_file_range F
> GLIBC_2.27 glob F
> GLIBC_2.27 glob64 F
> GLIBC_2.27 memfd_create F
> diff --git a/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist b/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist
> index 752176108e..c77fa8e07f 100644
> --- a/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist
> @@ -1987,6 +1987,7 @@ GLIBC_2.26 pwritev2 F
> GLIBC_2.26 pwritev64v2 F
> GLIBC_2.26 reallocarray F
> GLIBC_2.27 GLIBC_2.27 A
> +GLIBC_2.27 copy_file_range F
> GLIBC_2.27 glob F
> GLIBC_2.27 glob64 F
> GLIBC_2.27 memfd_create F
> diff --git a/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist b/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist
> index b6d4c73635..c48fdcb978 100644
> --- a/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist
> @@ -1888,6 +1888,7 @@ GLIBC_2.26 pwritev2 F
> GLIBC_2.26 pwritev64v2 F
> GLIBC_2.26 reallocarray F
> GLIBC_2.27 GLIBC_2.27 A
> +GLIBC_2.27 copy_file_range F
> GLIBC_2.27 glob F
> GLIBC_2.27 glob64 F
> GLIBC_2.27 memfd_create F
> diff --git a/sysdeps/unix/sysv/linux/sh/libc.abilist b/sysdeps/unix/sysv/linux/sh/libc.abilist
> index 1ee21fe8e8..7c77f15c65 100644
> --- a/sysdeps/unix/sysv/linux/sh/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/sh/libc.abilist
> @@ -1873,6 +1873,7 @@ GLIBC_2.26 pwritev2 F
> GLIBC_2.26 pwritev64v2 F
> GLIBC_2.26 reallocarray F
> GLIBC_2.27 GLIBC_2.27 A
> +GLIBC_2.27 copy_file_range F
> GLIBC_2.27 glob F
> GLIBC_2.27 glob64 F
> GLIBC_2.27 memfd_create F
> diff --git a/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist b/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist
> index e652191c60..c23a2d9ca7 100644
> --- a/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist
> @@ -1980,6 +1980,7 @@ GLIBC_2.26 pwritev2 F
> GLIBC_2.26 pwritev64v2 F
> GLIBC_2.26 reallocarray F
> GLIBC_2.27 GLIBC_2.27 A
> +GLIBC_2.27 copy_file_range F
> GLIBC_2.27 glob F
> GLIBC_2.27 glob64 F
> GLIBC_2.27 memfd_create F
> diff --git a/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist b/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist
> index 37cf8713a5..1c035bceec 100644
> --- a/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist
> @@ -1917,6 +1917,7 @@ GLIBC_2.26 pwritev2 F
> GLIBC_2.26 pwritev64v2 F
> GLIBC_2.26 reallocarray F
> GLIBC_2.27 GLIBC_2.27 A
> +GLIBC_2.27 copy_file_range F
> GLIBC_2.27 glob F
> GLIBC_2.27 glob64 F
> GLIBC_2.27 memfd_create F
> diff --git a/sysdeps/unix/sysv/linux/tile/tilegx/tilegx32/libc.abilist b/sysdeps/unix/sysv/linux/tile/tilegx/tilegx32/libc.abilist
> index 57427eb3ee..d843815aa6 100644
> --- a/sysdeps/unix/sysv/linux/tile/tilegx/tilegx32/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/tile/tilegx/tilegx32/libc.abilist
> @@ -2111,6 +2111,7 @@ GLIBC_2.26 pwritev2 F
> GLIBC_2.26 pwritev64v2 F
> GLIBC_2.26 reallocarray F
> GLIBC_2.27 GLIBC_2.27 A
> +GLIBC_2.27 copy_file_range F
> GLIBC_2.27 glob F
> GLIBC_2.27 glob64 F
> GLIBC_2.27 memfd_create F
> diff --git a/sysdeps/unix/sysv/linux/tile/tilegx/tilegx64/libc.abilist b/sysdeps/unix/sysv/linux/tile/tilegx/tilegx64/libc.abilist
> index 321f65c600..9d8b6f7ed2 100644
> --- a/sysdeps/unix/sysv/linux/tile/tilegx/tilegx64/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/tile/tilegx/tilegx64/libc.abilist
> @@ -2111,6 +2111,7 @@ GLIBC_2.26 pwritev2 F
> GLIBC_2.26 pwritev64v2 F
> GLIBC_2.26 reallocarray F
> GLIBC_2.27 GLIBC_2.27 A
> +GLIBC_2.27 copy_file_range F
> GLIBC_2.27 glob F
> GLIBC_2.27 glob64 F
> GLIBC_2.27 memfd_create F
> diff --git a/sysdeps/unix/sysv/linux/tile/tilepro/libc.abilist b/sysdeps/unix/sysv/linux/tile/tilepro/libc.abilist
> index 57427eb3ee..d843815aa6 100644
> --- a/sysdeps/unix/sysv/linux/tile/tilepro/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/tile/tilepro/libc.abilist
> @@ -2111,6 +2111,7 @@ GLIBC_2.26 pwritev2 F
> GLIBC_2.26 pwritev64v2 F
> GLIBC_2.26 reallocarray F
> GLIBC_2.27 GLIBC_2.27 A
> +GLIBC_2.27 copy_file_range F
> GLIBC_2.27 glob F
> GLIBC_2.27 glob64 F
> GLIBC_2.27 memfd_create F
> diff --git a/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist b/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist
> index f26c8b99d5..7365702953 100644
> --- a/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist
> @@ -1875,6 +1875,7 @@ GLIBC_2.26 strtof128_l F
> GLIBC_2.26 wcstof128 F
> GLIBC_2.26 wcstof128_l F
> GLIBC_2.27 GLIBC_2.27 A
> +GLIBC_2.27 copy_file_range F
> GLIBC_2.27 glob F
> GLIBC_2.27 glob64 F
> GLIBC_2.27 memfd_create F
> diff --git a/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist b/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist
> index 2a6057154b..ebc228d4e7 100644
> --- a/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist
> @@ -2118,6 +2118,7 @@ GLIBC_2.26 strtof128_l F
> GLIBC_2.26 wcstof128 F
> GLIBC_2.26 wcstof128_l F
> GLIBC_2.27 GLIBC_2.27 A
> +GLIBC_2.27 copy_file_range F
> GLIBC_2.27 glob F
> GLIBC_2.27 glob64 F
> GLIBC_2.27 memfd_create F
>
[-- Attachment #2: tst-copy_file_range.out --]
[-- Type: text/plain, Size: 41260 bytes --]
info: using alternate temporary files directory: /dev/shm
info: maximum writable file offset: 17592186040319 (fffffffefff)
tst-copy_file_range.c:285: numeric comparison failure
left: 27 (0x1b); from: errno
right: 22 (0x16); from: EINVAL
tst-copy_file_range.c:285: numeric comparison failure
left: 27 (0x1b); from: errno
right: 22 (0x16); from: EINVAL
tst-copy_file_range.c:286: numeric comparison failure (widths 64 and 32)
left: 1 (0x1); from: xlseek (infd, 0, SEEK_CUR)
right: 0 (0x0); from: 0
tst-copy_file_range.c:290: numeric comparison failure
left: 17592186040320 (0xffffffff000); from: xlseek (outfd, 0, SEEK_CUR)
right: 17592186040319 (0xfffffffefff); from: where
tst-copy_file_range.c:294: numeric comparison failure (widths 64 and 32)
left: 17592186040320 (0xffffffff000); from: st.st_size
right: 0 (0x0); from: 0
tst-copy_file_range.c:315: numeric comparison failure
left: 27 (0x1b); from: errno
right: 22 (0x16); from: EINVAL
tst-copy_file_range.c:317: numeric comparison failure (widths 64 and 32)
left: 17592186040320 (0xffffffff000); from: st.st_size
right: 0 (0x0); from: 0
tst-copy_file_range.c:285: numeric comparison failure
left: 27 (0x1b); from: errno
right: 22 (0x16); from: EINVAL
tst-copy_file_range.c:286: numeric comparison failure (widths 64 and 32)
left: 1 (0x1); from: xlseek (infd, 0, SEEK_CUR)
right: 0 (0x0); from: 0
tst-copy_file_range.c:290: numeric comparison failure
left: 17592186040320 (0xffffffff000); from: xlseek (outfd, 0, SEEK_CUR)
right: 17592186040319 (0xfffffffefff); from: where
tst-copy_file_range.c:294: numeric comparison failure (widths 64 and 32)
left: 17592186040320 (0xffffffff000); from: st.st_size
right: 0 (0x0); from: 0
tst-copy_file_range.c:315: numeric comparison failure
left: 27 (0x1b); from: errno
right: 22 (0x16); from: EINVAL
tst-copy_file_range.c:317: numeric comparison failure (widths 64 and 32)
left: 17592186040320 (0xffffffff000); from: st.st_size
right: 0 (0x0); from: 0
tst-copy_file_range.c:285: numeric comparison failure
left: 27 (0x1b); from: errno
right: 22 (0x16); from: EINVAL
tst-copy_file_range.c:286: numeric comparison failure (widths 64 and 32)
left: 1 (0x1); from: xlseek (infd, 0, SEEK_CUR)
right: 0 (0x0); from: 0
tst-copy_file_range.c:290: numeric comparison failure
left: 17592186040320 (0xffffffff000); from: xlseek (outfd, 0, SEEK_CUR)
right: 17592186040319 (0xfffffffefff); from: where
tst-copy_file_range.c:294: numeric comparison failure (widths 64 and 32)
left: 17592186040320 (0xffffffff000); from: st.st_size
right: 0 (0x0); from: 0
tst-copy_file_range.c:315: numeric comparison failure
left: 27 (0x1b); from: errno
right: 22 (0x16); from: EINVAL
tst-copy_file_range.c:317: numeric comparison failure (widths 64 and 32)
left: 17592186040320 (0xffffffff000); from: st.st_size
right: 0 (0x0); from: 0
tst-copy_file_range.c:285: numeric comparison failure
left: 27 (0x1b); from: errno
right: 22 (0x16); from: EINVAL
tst-copy_file_range.c:286: numeric comparison failure (widths 64 and 32)
left: 1 (0x1); from: xlseek (infd, 0, SEEK_CUR)
right: 0 (0x0); from: 0
tst-copy_file_range.c:290: numeric comparison failure
left: 17592186040320 (0xffffffff000); from: xlseek (outfd, 0, SEEK_CUR)
right: 17592186040319 (0xfffffffefff); from: where
tst-copy_file_range.c:294: numeric comparison failure (widths 64 and 32)
left: 17592186040320 (0xffffffff000); from: st.st_size
right: 0 (0x0); from: 0
tst-copy_file_range.c:315: numeric comparison failure
left: 27 (0x1b); from: errno
right: 22 (0x16); from: EINVAL
tst-copy_file_range.c:317: numeric comparison failure (widths 64 and 32)
left: 17592186040320 (0xffffffff000); from: st.st_size
right: 0 (0x0); from: 0
tst-copy_file_range.c:285: numeric comparison failure
left: 27 (0x1b); from: errno
right: 22 (0x16); from: EINVAL
tst-copy_file_range.c:286: numeric comparison failure (widths 64 and 32)
left: 1 (0x1); from: xlseek (infd, 0, SEEK_CUR)
right: 0 (0x0); from: 0
tst-copy_file_range.c:290: numeric comparison failure
left: 17592186040320 (0xffffffff000); from: xlseek (outfd, 0, SEEK_CUR)
right: 17592186040319 (0xfffffffefff); from: where
tst-copy_file_range.c:294: numeric comparison failure (widths 64 and 32)
left: 17592186040320 (0xffffffff000); from: st.st_size
right: 0 (0x0); from: 0
tst-copy_file_range.c:315: numeric comparison failure
left: 27 (0x1b); from: errno
right: 22 (0x16); from: EINVAL
tst-copy_file_range.c:317: numeric comparison failure (widths 64 and 32)
left: 17592186040320 (0xffffffff000); from: st.st_size
right: 0 (0x0); from: 0
tst-copy_file_range.c:285: numeric comparison failure
left: 27 (0x1b); from: errno
right: 22 (0x16); from: EINVAL
tst-copy_file_range.c:286: numeric comparison failure (widths 64 and 32)
left: 1 (0x1); from: xlseek (infd, 0, SEEK_CUR)
right: 0 (0x0); from: 0
tst-copy_file_range.c:290: numeric comparison failure
left: 17592186040320 (0xffffffff000); from: xlseek (outfd, 0, SEEK_CUR)
right: 17592186040319 (0xfffffffefff); from: where
tst-copy_file_range.c:294: numeric comparison failure (widths 64 and 32)
left: 17592186040320 (0xffffffff000); from: st.st_size
right: 0 (0x0); from: 0
tst-copy_file_range.c:315: numeric comparison failure
left: 27 (0x1b); from: errno
right: 22 (0x16); from: EINVAL
tst-copy_file_range.c:317: numeric comparison failure (widths 64 and 32)
left: 17592186040320 (0xffffffff000); from: st.st_size
right: 0 (0x0); from: 0
tst-copy_file_range.c:285: numeric comparison failure
left: 27 (0x1b); from: errno
right: 22 (0x16); from: EINVAL
tst-copy_file_range.c:286: numeric comparison failure (widths 64 and 32)
left: 1 (0x1); from: xlseek (infd, 0, SEEK_CUR)
right: 0 (0x0); from: 0
tst-copy_file_range.c:290: numeric comparison failure
left: 17592186040320 (0xffffffff000); from: xlseek (outfd, 0, SEEK_CUR)
right: 17592186040319 (0xfffffffefff); from: where
tst-copy_file_range.c:294: numeric comparison failure (widths 64 and 32)
left: 17592186040320 (0xffffffff000); from: st.st_size
right: 0 (0x0); from: 0
tst-copy_file_range.c:338: numeric comparison failure
left: 8193 (0x2001); from: xlseek (infd, 0, SEEK_CUR)
right: 8192 (0x2000); from: ret
error: tst-copy_file_range.c:342: not true: memcmp (buffer, random_data, ret) == 0
tst-copy_file_range.c:348: numeric comparison failure
left: 27 (0x1b); from: errno
right: 22 (0x16); from: EINVAL
tst-copy_file_range.c:285: numeric comparison failure
left: 27 (0x1b); from: errno
right: 22 (0x16); from: EINVAL
tst-copy_file_range.c:286: numeric comparison failure (widths 64 and 32)
left: 1 (0x1); from: xlseek (infd, 0, SEEK_CUR)
right: 0 (0x0); from: 0
tst-copy_file_range.c:290: numeric comparison failure
left: 17592186040320 (0xffffffff000); from: xlseek (outfd, 0, SEEK_CUR)
right: 17592186040319 (0xfffffffefff); from: where
tst-copy_file_range.c:294: numeric comparison failure (widths 64 and 32)
left: 17592186040320 (0xffffffff000); from: st.st_size
right: 0 (0x0); from: 0
tst-copy_file_range.c:338: numeric comparison failure
left: 8194 (0x2002); from: xlseek (infd, 0, SEEK_CUR)
right: 8193 (0x2001); from: ret
error: tst-copy_file_range.c:342: not true: memcmp (buffer, random_data, ret) == 0
tst-copy_file_range.c:348: numeric comparison failure
left: 27 (0x1b); from: errno
right: 22 (0x16); from: EINVAL
tst-copy_file_range.c:285: numeric comparison failure
left: 27 (0x1b); from: errno
right: 22 (0x16); from: EINVAL
tst-copy_file_range.c:286: numeric comparison failure (widths 64 and 32)
left: 1 (0x1); from: xlseek (infd, 0, SEEK_CUR)
right: 0 (0x0); from: 0
tst-copy_file_range.c:290: numeric comparison failure
left: 17592186040320 (0xffffffff000); from: xlseek (outfd, 0, SEEK_CUR)
right: 17592186040319 (0xfffffffefff); from: where
tst-copy_file_range.c:294: numeric comparison failure (widths 64 and 32)
left: 17592186040320 (0xffffffff000); from: st.st_size
right: 0 (0x0); from: 0
tst-copy_file_range.c:338: numeric comparison failure
left: 16384 (0x4000); from: xlseek (infd, 0, SEEK_CUR)
right: 16383 (0x3fff); from: ret
error: tst-copy_file_range.c:342: not true: memcmp (buffer, random_data, ret) == 0
tst-copy_file_range.c:348: numeric comparison failure
left: 27 (0x1b); from: errno
right: 22 (0x16); from: EINVAL
tst-copy_file_range.c:285: numeric comparison failure
left: 27 (0x1b); from: errno
right: 22 (0x16); from: EINVAL
tst-copy_file_range.c:286: numeric comparison failure (widths 64 and 32)
left: 1 (0x1); from: xlseek (infd, 0, SEEK_CUR)
right: 0 (0x0); from: 0
tst-copy_file_range.c:290: numeric comparison failure
left: 17592186040320 (0xffffffff000); from: xlseek (outfd, 0, SEEK_CUR)
right: 17592186040319 (0xfffffffefff); from: where
tst-copy_file_range.c:294: numeric comparison failure (widths 64 and 32)
left: 17592186040320 (0xffffffff000); from: st.st_size
right: 0 (0x0); from: 0
tst-copy_file_range.c:338: numeric comparison failure
left: 16385 (0x4001); from: xlseek (infd, 0, SEEK_CUR)
right: 16384 (0x4000); from: ret
error: tst-copy_file_range.c:342: not true: memcmp (buffer, random_data, ret) == 0
tst-copy_file_range.c:348: numeric comparison failure
left: 27 (0x1b); from: errno
right: 22 (0x16); from: EINVAL
tst-copy_file_range.c:285: numeric comparison failure
left: 27 (0x1b); from: errno
right: 22 (0x16); from: EINVAL
tst-copy_file_range.c:286: numeric comparison failure (widths 64 and 32)
left: 1 (0x1); from: xlseek (infd, 0, SEEK_CUR)
right: 0 (0x0); from: 0
tst-copy_file_range.c:290: numeric comparison failure
left: 17592186040320 (0xffffffff000); from: xlseek (outfd, 0, SEEK_CUR)
right: 17592186040319 (0xfffffffefff); from: where
tst-copy_file_range.c:294: numeric comparison failure (widths 64 and 32)
left: 17592186040320 (0xffffffff000); from: st.st_size
right: 0 (0x0); from: 0
tst-copy_file_range.c:338: numeric comparison failure
left: 16386 (0x4002); from: xlseek (infd, 0, SEEK_CUR)
right: 16385 (0x4001); from: ret
error: tst-copy_file_range.c:342: not true: memcmp (buffer, random_data, ret) == 0
tst-copy_file_range.c:348: numeric comparison failure
left: 27 (0x1b); from: errno
right: 22 (0x16); from: EINVAL
tst-copy_file_range.c:285: numeric comparison failure
left: 27 (0x1b); from: errno
right: 22 (0x16); from: EINVAL
tst-copy_file_range.c:286: numeric comparison failure (widths 64 and 32)
left: 1 (0x1); from: xlseek (infd, 0, SEEK_CUR)
right: 0 (0x0); from: 0
tst-copy_file_range.c:290: numeric comparison failure
left: 17592186040320 (0xffffffff000); from: xlseek (outfd, 0, SEEK_CUR)
right: 17592186040319 (0xfffffffefff); from: where
tst-copy_file_range.c:294: numeric comparison failure (widths 64 and 32)
left: 17592186040320 (0xffffffff000); from: st.st_size
right: 0 (0x0); from: 0
tst-copy_file_range.c:338: numeric comparison failure
left: 99999 (0x1869f); from: xlseek (infd, 0, SEEK_CUR)
right: 99998 (0x1869e); from: ret
error: tst-copy_file_range.c:342: not true: memcmp (buffer, random_data, ret) == 0
tst-copy_file_range.c:347: numeric comparison failure (widths 64 and 32)
left: 0 (0x0); from: copy_file_range (infd, pinoff, outfd, poutoff, sizeof (random_data), 0)
right: -1 (0xffffffff); from: -1
tst-copy_file_range.c:348: numeric comparison failure
left: 38 (0x26); from: errno
right: 22 (0x16); from: EINVAL
tst-copy_file_range.c:285: numeric comparison failure
left: 27 (0x1b); from: errno
right: 22 (0x16); from: EINVAL
tst-copy_file_range.c:285: numeric comparison failure
left: 27 (0x1b); from: errno
right: 22 (0x16); from: EINVAL
tst-copy_file_range.c:286: numeric comparison failure (widths 64 and 32)
left: 1 (0x1); from: xlseek (infd, 0, SEEK_CUR)
right: 0 (0x0); from: 0
tst-copy_file_range.c:291: numeric comparison failure
left: 17592186040320 (0xffffffff000); from: outoff
right: 17592186040319 (0xfffffffefff); from: where
tst-copy_file_range.c:294: numeric comparison failure (widths 64 and 32)
left: 17592186040320 (0xffffffff000); from: st.st_size
right: 0 (0x0); from: 0
tst-copy_file_range.c:315: numeric comparison failure
left: 27 (0x1b); from: errno
right: 22 (0x16); from: EINVAL
tst-copy_file_range.c:317: numeric comparison failure (widths 64 and 32)
left: 17592186040320 (0xffffffff000); from: st.st_size
right: 0 (0x0); from: 0
tst-copy_file_range.c:285: numeric comparison failure
left: 27 (0x1b); from: errno
right: 22 (0x16); from: EINVAL
tst-copy_file_range.c:286: numeric comparison failure (widths 64 and 32)
left: 1 (0x1); from: xlseek (infd, 0, SEEK_CUR)
right: 0 (0x0); from: 0
tst-copy_file_range.c:291: numeric comparison failure
left: 17592186040320 (0xffffffff000); from: outoff
right: 17592186040319 (0xfffffffefff); from: where
tst-copy_file_range.c:294: numeric comparison failure (widths 64 and 32)
left: 17592186040320 (0xffffffff000); from: st.st_size
right: 0 (0x0); from: 0
tst-copy_file_range.c:315: numeric comparison failure
left: 27 (0x1b); from: errno
right: 22 (0x16); from: EINVAL
tst-copy_file_range.c:317: numeric comparison failure (widths 64 and 32)
left: 17592186040320 (0xffffffff000); from: st.st_size
right: 0 (0x0); from: 0
tst-copy_file_range.c:285: numeric comparison failure
left: 27 (0x1b); from: errno
right: 22 (0x16); from: EINVAL
tst-copy_file_range.c:286: numeric comparison failure (widths 64 and 32)
left: 1 (0x1); from: xlseek (infd, 0, SEEK_CUR)
right: 0 (0x0); from: 0
tst-copy_file_range.c:291: numeric comparison failure
left: 17592186040320 (0xffffffff000); from: outoff
right: 17592186040319 (0xfffffffefff); from: where
tst-copy_file_range.c:294: numeric comparison failure (widths 64 and 32)
left: 17592186040320 (0xffffffff000); from: st.st_size
right: 0 (0x0); from: 0
tst-copy_file_range.c:315: numeric comparison failure
left: 27 (0x1b); from: errno
right: 22 (0x16); from: EINVAL
tst-copy_file_range.c:317: numeric comparison failure (widths 64 and 32)
left: 17592186040320 (0xffffffff000); from: st.st_size
right: 0 (0x0); from: 0
tst-copy_file_range.c:285: numeric comparison failure
left: 27 (0x1b); from: errno
right: 22 (0x16); from: EINVAL
tst-copy_file_range.c:286: numeric comparison failure (widths 64 and 32)
left: 1 (0x1); from: xlseek (infd, 0, SEEK_CUR)
right: 0 (0x0); from: 0
tst-copy_file_range.c:291: numeric comparison failure
left: 17592186040320 (0xffffffff000); from: outoff
right: 17592186040319 (0xfffffffefff); from: where
tst-copy_file_range.c:294: numeric comparison failure (widths 64 and 32)
left: 17592186040320 (0xffffffff000); from: st.st_size
right: 0 (0x0); from: 0
tst-copy_file_range.c:315: numeric comparison failure
left: 27 (0x1b); from: errno
right: 22 (0x16); from: EINVAL
tst-copy_file_range.c:317: numeric comparison failure (widths 64 and 32)
left: 17592186040320 (0xffffffff000); from: st.st_size
right: 0 (0x0); from: 0
tst-copy_file_range.c:285: numeric comparison failure
left: 27 (0x1b); from: errno
right: 22 (0x16); from: EINVAL
tst-copy_file_range.c:286: numeric comparison failure (widths 64 and 32)
left: 1 (0x1); from: xlseek (infd, 0, SEEK_CUR)
right: 0 (0x0); from: 0
tst-copy_file_range.c:291: numeric comparison failure
left: 17592186040320 (0xffffffff000); from: outoff
right: 17592186040319 (0xfffffffefff); from: where
tst-copy_file_range.c:294: numeric comparison failure (widths 64 and 32)
left: 17592186040320 (0xffffffff000); from: st.st_size
right: 0 (0x0); from: 0
tst-copy_file_range.c:315: numeric comparison failure
left: 27 (0x1b); from: errno
right: 22 (0x16); from: EINVAL
tst-copy_file_range.c:317: numeric comparison failure (widths 64 and 32)
left: 17592186040320 (0xffffffff000); from: st.st_size
right: 0 (0x0); from: 0
tst-copy_file_range.c:285: numeric comparison failure
left: 27 (0x1b); from: errno
right: 22 (0x16); from: EINVAL
tst-copy_file_range.c:286: numeric comparison failure (widths 64 and 32)
left: 1 (0x1); from: xlseek (infd, 0, SEEK_CUR)
right: 0 (0x0); from: 0
tst-copy_file_range.c:291: numeric comparison failure
left: 17592186040320 (0xffffffff000); from: outoff
right: 17592186040319 (0xfffffffefff); from: where
tst-copy_file_range.c:294: numeric comparison failure (widths 64 and 32)
left: 17592186040320 (0xffffffff000); from: st.st_size
right: 0 (0x0); from: 0
tst-copy_file_range.c:315: numeric comparison failure
left: 27 (0x1b); from: errno
right: 22 (0x16); from: EINVAL
tst-copy_file_range.c:317: numeric comparison failure (widths 64 and 32)
left: 17592186040320 (0xffffffff000); from: st.st_size
right: 0 (0x0); from: 0
tst-copy_file_range.c:285: numeric comparison failure
left: 27 (0x1b); from: errno
right: 22 (0x16); from: EINVAL
tst-copy_file_range.c:286: numeric comparison failure (widths 64 and 32)
left: 1 (0x1); from: xlseek (infd, 0, SEEK_CUR)
right: 0 (0x0); from: 0
tst-copy_file_range.c:291: numeric comparison failure
left: 17592186040320 (0xffffffff000); from: outoff
right: 17592186040319 (0xfffffffefff); from: where
tst-copy_file_range.c:294: numeric comparison failure (widths 64 and 32)
left: 17592186040320 (0xffffffff000); from: st.st_size
right: 0 (0x0); from: 0
tst-copy_file_range.c:338: numeric comparison failure
left: 8193 (0x2001); from: xlseek (infd, 0, SEEK_CUR)
right: 8192 (0x2000); from: ret
error: tst-copy_file_range.c:342: not true: memcmp (buffer, random_data, ret) == 0
tst-copy_file_range.c:348: numeric comparison failure
left: 27 (0x1b); from: errno
right: 22 (0x16); from: EINVAL
tst-copy_file_range.c:285: numeric comparison failure
left: 27 (0x1b); from: errno
right: 22 (0x16); from: EINVAL
tst-copy_file_range.c:286: numeric comparison failure (widths 64 and 32)
left: 1 (0x1); from: xlseek (infd, 0, SEEK_CUR)
right: 0 (0x0); from: 0
tst-copy_file_range.c:291: numeric comparison failure
left: 17592186040320 (0xffffffff000); from: outoff
right: 17592186040319 (0xfffffffefff); from: where
tst-copy_file_range.c:294: numeric comparison failure (widths 64 and 32)
left: 17592186040320 (0xffffffff000); from: st.st_size
right: 0 (0x0); from: 0
tst-copy_file_range.c:338: numeric comparison failure
left: 8194 (0x2002); from: xlseek (infd, 0, SEEK_CUR)
right: 8193 (0x2001); from: ret
error: tst-copy_file_range.c:342: not true: memcmp (buffer, random_data, ret) == 0
tst-copy_file_range.c:348: numeric comparison failure
left: 27 (0x1b); from: errno
right: 22 (0x16); from: EINVAL
tst-copy_file_range.c:285: numeric comparison failure
left: 27 (0x1b); from: errno
right: 22 (0x16); from: EINVAL
tst-copy_file_range.c:286: numeric comparison failure (widths 64 and 32)
left: 1 (0x1); from: xlseek (infd, 0, SEEK_CUR)
right: 0 (0x0); from: 0
tst-copy_file_range.c:291: numeric comparison failure
left: 17592186040320 (0xffffffff000); from: outoff
right: 17592186040319 (0xfffffffefff); from: where
tst-copy_file_range.c:294: numeric comparison failure (widths 64 and 32)
left: 17592186040320 (0xffffffff000); from: st.st_size
right: 0 (0x0); from: 0
tst-copy_file_range.c:338: numeric comparison failure
left: 16384 (0x4000); from: xlseek (infd, 0, SEEK_CUR)
right: 16383 (0x3fff); from: ret
error: tst-copy_file_range.c:342: not true: memcmp (buffer, random_data, ret) == 0
tst-copy_file_range.c:348: numeric comparison failure
left: 27 (0x1b); from: errno
right: 22 (0x16); from: EINVAL
tst-copy_file_range.c:285: numeric comparison failure
left: 27 (0x1b); from: errno
right: 22 (0x16); from: EINVAL
tst-copy_file_range.c:286: numeric comparison failure (widths 64 and 32)
left: 1 (0x1); from: xlseek (infd, 0, SEEK_CUR)
right: 0 (0x0); from: 0
tst-copy_file_range.c:291: numeric comparison failure
left: 17592186040320 (0xffffffff000); from: outoff
right: 17592186040319 (0xfffffffefff); from: where
tst-copy_file_range.c:294: numeric comparison failure (widths 64 and 32)
left: 17592186040320 (0xffffffff000); from: st.st_size
right: 0 (0x0); from: 0
tst-copy_file_range.c:338: numeric comparison failure
left: 16385 (0x4001); from: xlseek (infd, 0, SEEK_CUR)
right: 16384 (0x4000); from: ret
error: tst-copy_file_range.c:342: not true: memcmp (buffer, random_data, ret) == 0
tst-copy_file_range.c:348: numeric comparison failure
left: 27 (0x1b); from: errno
right: 22 (0x16); from: EINVAL
tst-copy_file_range.c:285: numeric comparison failure
left: 27 (0x1b); from: errno
right: 22 (0x16); from: EINVAL
tst-copy_file_range.c:286: numeric comparison failure (widths 64 and 32)
left: 1 (0x1); from: xlseek (infd, 0, SEEK_CUR)
right: 0 (0x0); from: 0
tst-copy_file_range.c:291: numeric comparison failure
left: 17592186040320 (0xffffffff000); from: outoff
right: 17592186040319 (0xfffffffefff); from: where
tst-copy_file_range.c:294: numeric comparison failure (widths 64 and 32)
left: 17592186040320 (0xffffffff000); from: st.st_size
right: 0 (0x0); from: 0
tst-copy_file_range.c:338: numeric comparison failure
left: 16386 (0x4002); from: xlseek (infd, 0, SEEK_CUR)
right: 16385 (0x4001); from: ret
error: tst-copy_file_range.c:342: not true: memcmp (buffer, random_data, ret) == 0
tst-copy_file_range.c:348: numeric comparison failure
left: 27 (0x1b); from: errno
right: 22 (0x16); from: EINVAL
tst-copy_file_range.c:285: numeric comparison failure
left: 27 (0x1b); from: errno
right: 22 (0x16); from: EINVAL
tst-copy_file_range.c:286: numeric comparison failure (widths 64 and 32)
left: 1 (0x1); from: xlseek (infd, 0, SEEK_CUR)
right: 0 (0x0); from: 0
tst-copy_file_range.c:291: numeric comparison failure
left: 17592186040320 (0xffffffff000); from: outoff
right: 17592186040319 (0xfffffffefff); from: where
tst-copy_file_range.c:294: numeric comparison failure (widths 64 and 32)
left: 17592186040320 (0xffffffff000); from: st.st_size
right: 0 (0x0); from: 0
tst-copy_file_range.c:338: numeric comparison failure
left: 99999 (0x1869f); from: xlseek (infd, 0, SEEK_CUR)
right: 99998 (0x1869e); from: ret
error: tst-copy_file_range.c:342: not true: memcmp (buffer, random_data, ret) == 0
tst-copy_file_range.c:347: numeric comparison failure (widths 64 and 32)
left: 0 (0x0); from: copy_file_range (infd, pinoff, outfd, poutoff, sizeof (random_data), 0)
right: -1 (0xffffffff); from: -1
tst-copy_file_range.c:348: numeric comparison failure
left: 38 (0x26); from: errno
right: 22 (0x16); from: EINVAL
tst-copy_file_range.c:285: numeric comparison failure
left: 27 (0x1b); from: errno
right: 22 (0x16); from: EINVAL
tst-copy_file_range.c:285: numeric comparison failure
left: 27 (0x1b); from: errno
right: 22 (0x16); from: EINVAL
tst-copy_file_range.c:290: numeric comparison failure
left: 17592186040320 (0xffffffff000); from: xlseek (outfd, 0, SEEK_CUR)
right: 17592186040319 (0xfffffffefff); from: where
tst-copy_file_range.c:294: numeric comparison failure (widths 64 and 32)
left: 17592186040320 (0xffffffff000); from: st.st_size
right: 0 (0x0); from: 0
tst-copy_file_range.c:315: numeric comparison failure
left: 27 (0x1b); from: errno
right: 22 (0x16); from: EINVAL
tst-copy_file_range.c:317: numeric comparison failure (widths 64 and 32)
left: 17592186040320 (0xffffffff000); from: st.st_size
right: 0 (0x0); from: 0
tst-copy_file_range.c:285: numeric comparison failure
left: 27 (0x1b); from: errno
right: 22 (0x16); from: EINVAL
tst-copy_file_range.c:290: numeric comparison failure
left: 17592186040320 (0xffffffff000); from: xlseek (outfd, 0, SEEK_CUR)
right: 17592186040319 (0xfffffffefff); from: where
tst-copy_file_range.c:294: numeric comparison failure (widths 64 and 32)
left: 17592186040320 (0xffffffff000); from: st.st_size
right: 0 (0x0); from: 0
tst-copy_file_range.c:315: numeric comparison failure
left: 27 (0x1b); from: errno
right: 22 (0x16); from: EINVAL
tst-copy_file_range.c:317: numeric comparison failure (widths 64 and 32)
left: 17592186040320 (0xffffffff000); from: st.st_size
right: 0 (0x0); from: 0
tst-copy_file_range.c:285: numeric comparison failure
left: 27 (0x1b); from: errno
right: 22 (0x16); from: EINVAL
tst-copy_file_range.c:290: numeric comparison failure
left: 17592186040320 (0xffffffff000); from: xlseek (outfd, 0, SEEK_CUR)
right: 17592186040319 (0xfffffffefff); from: where
tst-copy_file_range.c:294: numeric comparison failure (widths 64 and 32)
left: 17592186040320 (0xffffffff000); from: st.st_size
right: 0 (0x0); from: 0
tst-copy_file_range.c:315: numeric comparison failure
left: 27 (0x1b); from: errno
right: 22 (0x16); from: EINVAL
tst-copy_file_range.c:317: numeric comparison failure (widths 64 and 32)
left: 17592186040320 (0xffffffff000); from: st.st_size
right: 0 (0x0); from: 0
tst-copy_file_range.c:285: numeric comparison failure
left: 27 (0x1b); from: errno
right: 22 (0x16); from: EINVAL
tst-copy_file_range.c:290: numeric comparison failure
left: 17592186040320 (0xffffffff000); from: xlseek (outfd, 0, SEEK_CUR)
right: 17592186040319 (0xfffffffefff); from: where
tst-copy_file_range.c:294: numeric comparison failure (widths 64 and 32)
left: 17592186040320 (0xffffffff000); from: st.st_size
right: 0 (0x0); from: 0
tst-copy_file_range.c:315: numeric comparison failure
left: 27 (0x1b); from: errno
right: 22 (0x16); from: EINVAL
tst-copy_file_range.c:317: numeric comparison failure (widths 64 and 32)
left: 17592186040320 (0xffffffff000); from: st.st_size
right: 0 (0x0); from: 0
tst-copy_file_range.c:285: numeric comparison failure
left: 27 (0x1b); from: errno
right: 22 (0x16); from: EINVAL
tst-copy_file_range.c:290: numeric comparison failure
left: 17592186040320 (0xffffffff000); from: xlseek (outfd, 0, SEEK_CUR)
right: 17592186040319 (0xfffffffefff); from: where
tst-copy_file_range.c:294: numeric comparison failure (widths 64 and 32)
left: 17592186040320 (0xffffffff000); from: st.st_size
right: 0 (0x0); from: 0
tst-copy_file_range.c:315: numeric comparison failure
left: 27 (0x1b); from: errno
right: 22 (0x16); from: EINVAL
tst-copy_file_range.c:317: numeric comparison failure (widths 64 and 32)
left: 17592186040320 (0xffffffff000); from: st.st_size
right: 0 (0x0); from: 0
tst-copy_file_range.c:285: numeric comparison failure
left: 27 (0x1b); from: errno
right: 22 (0x16); from: EINVAL
tst-copy_file_range.c:290: numeric comparison failure
left: 17592186040320 (0xffffffff000); from: xlseek (outfd, 0, SEEK_CUR)
right: 17592186040319 (0xfffffffefff); from: where
tst-copy_file_range.c:294: numeric comparison failure (widths 64 and 32)
left: 17592186040320 (0xffffffff000); from: st.st_size
right: 0 (0x0); from: 0
tst-copy_file_range.c:315: numeric comparison failure
left: 27 (0x1b); from: errno
right: 22 (0x16); from: EINVAL
tst-copy_file_range.c:317: numeric comparison failure (widths 64 and 32)
left: 17592186040320 (0xffffffff000); from: st.st_size
right: 0 (0x0); from: 0
tst-copy_file_range.c:285: numeric comparison failure
left: 27 (0x1b); from: errno
right: 22 (0x16); from: EINVAL
tst-copy_file_range.c:290: numeric comparison failure
left: 17592186040320 (0xffffffff000); from: xlseek (outfd, 0, SEEK_CUR)
right: 17592186040319 (0xfffffffefff); from: where
tst-copy_file_range.c:294: numeric comparison failure (widths 64 and 32)
left: 17592186040320 (0xffffffff000); from: st.st_size
right: 0 (0x0); from: 0
tst-copy_file_range.c:334: numeric comparison failure
left: 8193 (0x2001); from: inoff
right: 8192 (0x2000); from: ret
error: tst-copy_file_range.c:342: not true: memcmp (buffer, random_data, ret) == 0
tst-copy_file_range.c:348: numeric comparison failure
left: 27 (0x1b); from: errno
right: 22 (0x16); from: EINVAL
tst-copy_file_range.c:285: numeric comparison failure
left: 27 (0x1b); from: errno
right: 22 (0x16); from: EINVAL
tst-copy_file_range.c:290: numeric comparison failure
left: 17592186040320 (0xffffffff000); from: xlseek (outfd, 0, SEEK_CUR)
right: 17592186040319 (0xfffffffefff); from: where
tst-copy_file_range.c:294: numeric comparison failure (widths 64 and 32)
left: 17592186040320 (0xffffffff000); from: st.st_size
right: 0 (0x0); from: 0
tst-copy_file_range.c:334: numeric comparison failure
left: 8194 (0x2002); from: inoff
right: 8193 (0x2001); from: ret
error: tst-copy_file_range.c:342: not true: memcmp (buffer, random_data, ret) == 0
tst-copy_file_range.c:348: numeric comparison failure
left: 27 (0x1b); from: errno
right: 22 (0x16); from: EINVAL
tst-copy_file_range.c:285: numeric comparison failure
left: 27 (0x1b); from: errno
right: 22 (0x16); from: EINVAL
tst-copy_file_range.c:290: numeric comparison failure
left: 17592186040320 (0xffffffff000); from: xlseek (outfd, 0, SEEK_CUR)
right: 17592186040319 (0xfffffffefff); from: where
tst-copy_file_range.c:294: numeric comparison failure (widths 64 and 32)
left: 17592186040320 (0xffffffff000); from: st.st_size
right: 0 (0x0); from: 0
tst-copy_file_range.c:334: numeric comparison failure
left: 16384 (0x4000); from: inoff
right: 16383 (0x3fff); from: ret
error: tst-copy_file_range.c:342: not true: memcmp (buffer, random_data, ret) == 0
tst-copy_file_range.c:348: numeric comparison failure
left: 27 (0x1b); from: errno
right: 22 (0x16); from: EINVAL
tst-copy_file_range.c:285: numeric comparison failure
left: 27 (0x1b); from: errno
right: 22 (0x16); from: EINVAL
tst-copy_file_range.c:290: numeric comparison failure
left: 17592186040320 (0xffffffff000); from: xlseek (outfd, 0, SEEK_CUR)
right: 17592186040319 (0xfffffffefff); from: where
tst-copy_file_range.c:294: numeric comparison failure (widths 64 and 32)
left: 17592186040320 (0xffffffff000); from: st.st_size
right: 0 (0x0); from: 0
tst-copy_file_range.c:334: numeric comparison failure
left: 16385 (0x4001); from: inoff
right: 16384 (0x4000); from: ret
error: tst-copy_file_range.c:342: not true: memcmp (buffer, random_data, ret) == 0
tst-copy_file_range.c:348: numeric comparison failure
left: 27 (0x1b); from: errno
right: 22 (0x16); from: EINVAL
tst-copy_file_range.c:285: numeric comparison failure
left: 27 (0x1b); from: errno
right: 22 (0x16); from: EINVAL
tst-copy_file_range.c:290: numeric comparison failure
left: 17592186040320 (0xffffffff000); from: xlseek (outfd, 0, SEEK_CUR)
right: 17592186040319 (0xfffffffefff); from: where
tst-copy_file_range.c:294: numeric comparison failure (widths 64 and 32)
left: 17592186040320 (0xffffffff000); from: st.st_size
right: 0 (0x0); from: 0
tst-copy_file_range.c:334: numeric comparison failure
left: 16386 (0x4002); from: inoff
right: 16385 (0x4001); from: ret
error: tst-copy_file_range.c:342: not true: memcmp (buffer, random_data, ret) == 0
tst-copy_file_range.c:348: numeric comparison failure
left: 27 (0x1b); from: errno
right: 22 (0x16); from: EINVAL
tst-copy_file_range.c:285: numeric comparison failure
left: 27 (0x1b); from: errno
right: 22 (0x16); from: EINVAL
tst-copy_file_range.c:290: numeric comparison failure
left: 17592186040320 (0xffffffff000); from: xlseek (outfd, 0, SEEK_CUR)
right: 17592186040319 (0xfffffffefff); from: where
tst-copy_file_range.c:294: numeric comparison failure (widths 64 and 32)
left: 17592186040320 (0xffffffff000); from: st.st_size
right: 0 (0x0); from: 0
tst-copy_file_range.c:334: numeric comparison failure
left: 99999 (0x1869f); from: inoff
right: 99998 (0x1869e); from: ret
error: tst-copy_file_range.c:342: not true: memcmp (buffer, random_data, ret) == 0
tst-copy_file_range.c:347: numeric comparison failure (widths 64 and 32)
left: 0 (0x0); from: copy_file_range (infd, pinoff, outfd, poutoff, sizeof (random_data), 0)
right: -1 (0xffffffff); from: -1
tst-copy_file_range.c:348: numeric comparison failure
left: 38 (0x26); from: errno
right: 22 (0x16); from: EINVAL
tst-copy_file_range.c:285: numeric comparison failure
left: 27 (0x1b); from: errno
right: 22 (0x16); from: EINVAL
tst-copy_file_range.c:285: numeric comparison failure
left: 27 (0x1b); from: errno
right: 22 (0x16); from: EINVAL
tst-copy_file_range.c:291: numeric comparison failure
left: 17592186040320 (0xffffffff000); from: outoff
right: 17592186040319 (0xfffffffefff); from: where
tst-copy_file_range.c:294: numeric comparison failure (widths 64 and 32)
left: 17592186040320 (0xffffffff000); from: st.st_size
right: 0 (0x0); from: 0
tst-copy_file_range.c:315: numeric comparison failure
left: 27 (0x1b); from: errno
right: 22 (0x16); from: EINVAL
tst-copy_file_range.c:317: numeric comparison failure (widths 64 and 32)
left: 17592186040320 (0xffffffff000); from: st.st_size
right: 0 (0x0); from: 0
tst-copy_file_range.c:285: numeric comparison failure
left: 27 (0x1b); from: errno
right: 22 (0x16); from: EINVAL
tst-copy_file_range.c:291: numeric comparison failure
left: 17592186040320 (0xffffffff000); from: outoff
right: 17592186040319 (0xfffffffefff); from: where
tst-copy_file_range.c:294: numeric comparison failure (widths 64 and 32)
left: 17592186040320 (0xffffffff000); from: st.st_size
right: 0 (0x0); from: 0
tst-copy_file_range.c:315: numeric comparison failure
left: 27 (0x1b); from: errno
right: 22 (0x16); from: EINVAL
tst-copy_file_range.c:317: numeric comparison failure (widths 64 and 32)
left: 17592186040320 (0xffffffff000); from: st.st_size
right: 0 (0x0); from: 0
tst-copy_file_range.c:285: numeric comparison failure
left: 27 (0x1b); from: errno
right: 22 (0x16); from: EINVAL
tst-copy_file_range.c:291: numeric comparison failure
left: 17592186040320 (0xffffffff000); from: outoff
right: 17592186040319 (0xfffffffefff); from: where
tst-copy_file_range.c:294: numeric comparison failure (widths 64 and 32)
left: 17592186040320 (0xffffffff000); from: st.st_size
right: 0 (0x0); from: 0
tst-copy_file_range.c:315: numeric comparison failure
left: 27 (0x1b); from: errno
right: 22 (0x16); from: EINVAL
tst-copy_file_range.c:317: numeric comparison failure (widths 64 and 32)
left: 17592186040320 (0xffffffff000); from: st.st_size
right: 0 (0x0); from: 0
tst-copy_file_range.c:285: numeric comparison failure
left: 27 (0x1b); from: errno
right: 22 (0x16); from: EINVAL
tst-copy_file_range.c:291: numeric comparison failure
left: 17592186040320 (0xffffffff000); from: outoff
right: 17592186040319 (0xfffffffefff); from: where
tst-copy_file_range.c:294: numeric comparison failure (widths 64 and 32)
left: 17592186040320 (0xffffffff000); from: st.st_size
right: 0 (0x0); from: 0
tst-copy_file_range.c:315: numeric comparison failure
left: 27 (0x1b); from: errno
right: 22 (0x16); from: EINVAL
tst-copy_file_range.c:317: numeric comparison failure (widths 64 and 32)
left: 17592186040320 (0xffffffff000); from: st.st_size
right: 0 (0x0); from: 0
tst-copy_file_range.c:285: numeric comparison failure
left: 27 (0x1b); from: errno
right: 22 (0x16); from: EINVAL
tst-copy_file_range.c:291: numeric comparison failure
left: 17592186040320 (0xffffffff000); from: outoff
right: 17592186040319 (0xfffffffefff); from: where
tst-copy_file_range.c:294: numeric comparison failure (widths 64 and 32)
left: 17592186040320 (0xffffffff000); from: st.st_size
right: 0 (0x0); from: 0
tst-copy_file_range.c:315: numeric comparison failure
left: 27 (0x1b); from: errno
right: 22 (0x16); from: EINVAL
tst-copy_file_range.c:317: numeric comparison failure (widths 64 and 32)
left: 17592186040320 (0xffffffff000); from: st.st_size
right: 0 (0x0); from: 0
tst-copy_file_range.c:285: numeric comparison failure
left: 27 (0x1b); from: errno
right: 22 (0x16); from: EINVAL
tst-copy_file_range.c:291: numeric comparison failure
left: 17592186040320 (0xffffffff000); from: outoff
right: 17592186040319 (0xfffffffefff); from: where
tst-copy_file_range.c:294: numeric comparison failure (widths 64 and 32)
left: 17592186040320 (0xffffffff000); from: st.st_size
right: 0 (0x0); from: 0
tst-copy_file_range.c:315: numeric comparison failure
left: 27 (0x1b); from: errno
right: 22 (0x16); from: EINVAL
tst-copy_file_range.c:317: numeric comparison failure (widths 64 and 32)
left: 17592186040320 (0xffffffff000); from: st.st_size
right: 0 (0x0); from: 0
tst-copy_file_range.c:285: numeric comparison failure
left: 27 (0x1b); from: errno
right: 22 (0x16); from: EINVAL
tst-copy_file_range.c:291: numeric comparison failure
left: 17592186040320 (0xffffffff000); from: outoff
right: 17592186040319 (0xfffffffefff); from: where
tst-copy_file_range.c:294: numeric comparison failure (widths 64 and 32)
left: 17592186040320 (0xffffffff000); from: st.st_size
right: 0 (0x0); from: 0
tst-copy_file_range.c:334: numeric comparison failure
left: 8193 (0x2001); from: inoff
right: 8192 (0x2000); from: ret
error: tst-copy_file_range.c:342: not true: memcmp (buffer, random_data, ret) == 0
tst-copy_file_range.c:348: numeric comparison failure
left: 27 (0x1b); from: errno
right: 22 (0x16); from: EINVAL
tst-copy_file_range.c:285: numeric comparison failure
left: 27 (0x1b); from: errno
right: 22 (0x16); from: EINVAL
tst-copy_file_range.c:291: numeric comparison failure
left: 17592186040320 (0xffffffff000); from: outoff
right: 17592186040319 (0xfffffffefff); from: where
tst-copy_file_range.c:294: numeric comparison failure (widths 64 and 32)
left: 17592186040320 (0xffffffff000); from: st.st_size
right: 0 (0x0); from: 0
tst-copy_file_range.c:334: numeric comparison failure
left: 8194 (0x2002); from: inoff
right: 8193 (0x2001); from: ret
error: tst-copy_file_range.c:342: not true: memcmp (buffer, random_data, ret) == 0
tst-copy_file_range.c:348: numeric comparison failure
left: 27 (0x1b); from: errno
right: 22 (0x16); from: EINVAL
tst-copy_file_range.c:285: numeric comparison failure
left: 27 (0x1b); from: errno
right: 22 (0x16); from: EINVAL
tst-copy_file_range.c:291: numeric comparison failure
left: 17592186040320 (0xffffffff000); from: outoff
right: 17592186040319 (0xfffffffefff); from: where
tst-copy_file_range.c:294: numeric comparison failure (widths 64 and 32)
left: 17592186040320 (0xffffffff000); from: st.st_size
right: 0 (0x0); from: 0
tst-copy_file_range.c:334: numeric comparison failure
left: 16384 (0x4000); from: inoff
right: 16383 (0x3fff); from: ret
error: tst-copy_file_range.c:342: not true: memcmp (buffer, random_data, ret) == 0
tst-copy_file_range.c:348: numeric comparison failure
left: 27 (0x1b); from: errno
right: 22 (0x16); from: EINVAL
tst-copy_file_range.c:285: numeric comparison failure
left: 27 (0x1b); from: errno
right: 22 (0x16); from: EINVAL
tst-copy_file_range.c:291: numeric comparison failure
left: 17592186040320 (0xffffffff000); from: outoff
right: 17592186040319 (0xfffffffefff); from: where
tst-copy_file_range.c:294: numeric comparison failure (widths 64 and 32)
left: 17592186040320 (0xffffffff000); from: st.st_size
right: 0 (0x0); from: 0
tst-copy_file_range.c:334: numeric comparison failure
left: 16385 (0x4001); from: inoff
right: 16384 (0x4000); from: ret
error: tst-copy_file_range.c:342: not true: memcmp (buffer, random_data, ret) == 0
tst-copy_file_range.c:348: numeric comparison failure
left: 27 (0x1b); from: errno
right: 22 (0x16); from: EINVAL
tst-copy_file_range.c:285: numeric comparison failure
left: 27 (0x1b); from: errno
right: 22 (0x16); from: EINVAL
tst-copy_file_range.c:291: numeric comparison failure
left: 17592186040320 (0xffffffff000); from: outoff
right: 17592186040319 (0xfffffffefff); from: where
tst-copy_file_range.c:294: numeric comparison failure (widths 64 and 32)
left: 17592186040320 (0xffffffff000); from: st.st_size
right: 0 (0x0); from: 0
tst-copy_file_range.c:334: numeric comparison failure
left: 16386 (0x4002); from: inoff
right: 16385 (0x4001); from: ret
error: tst-copy_file_range.c:342: not true: memcmp (buffer, random_data, ret) == 0
tst-copy_file_range.c:348: numeric comparison failure
left: 27 (0x1b); from: errno
right: 22 (0x16); from: EINVAL
tst-copy_file_range.c:285: numeric comparison failure
left: 27 (0x1b); from: errno
right: 22 (0x16); from: EINVAL
tst-copy_file_range.c:291: numeric comparison failure
left: 17592186040320 (0xffffffff000); from: outoff
right: 17592186040319 (0xfffffffefff); from: where
tst-copy_file_range.c:294: numeric comparison failure (widths 64 and 32)
left: 17592186040320 (0xffffffff000); from: st.st_size
right: 0 (0x0); from: 0
tst-copy_file_range.c:334: numeric comparison failure
left: 99999 (0x1869f); from: inoff
right: 99998 (0x1869e); from: ret
error: tst-copy_file_range.c:342: not true: memcmp (buffer, random_data, ret) == 0
tst-copy_file_range.c:347: numeric comparison failure (widths 64 and 32)
left: 0 (0x0); from: copy_file_range (infd, pinoff, outfd, poutoff, sizeof (random_data), 0)
right: -1 (0xffffffff); from: -1
tst-copy_file_range.c:348: numeric comparison failure
left: 38 (0x26); from: errno
right: 22 (0x16); from: EINVAL
error: 296 test failures
^ permalink raw reply [flat|nested] 24+ messages in thread
* Re: [PATCH] copy_file_range: New function to copy file data
2017-12-18 20:04 ` Adhemerval Zanella
@ 2017-12-18 20:19 ` Florian Weimer
2017-12-19 11:02 ` Adhemerval Zanella
2017-12-18 21:54 ` Joseph Myers
1 sibling, 1 reply; 24+ messages in thread
From: Florian Weimer @ 2017-12-18 20:19 UTC (permalink / raw)
To: Adhemerval Zanella, libc-alpha
On 12/18/2017 09:04 PM, Adhemerval Zanella wrote:
> I am seeing a lot of failures from tst-copy_file_range on both x86_64 and i686
> when using the fallback implementation (io/copy_file_range.c) due the fact
> write is returning EFBIG (I attached the test output):
Which file system is this?
>
> [...]
> openat(AT_FDCWD, "/tmp/tst-copy_file_range-in-mT9I6j", O_RDWR) = 3
> ftruncate(3, 0) = 0
> openat(AT_FDCWD, "/tmp/tst-copy_file_range-out-0RbGw2", O_RDWR) = 4
> ftruncate(4, 0)
> [...]
> fcntl(4, F_GETFL) = 0x8002 (flags O_RDWR|O_LARGEFILE)
> read(3, "k2dft\31*b#F=P.AyuQ[\22M\2\37\21f\0243\20\r\177\33Ak"..., 8192) = 8192
> write(4, "k2dft\31*b#F=P.AyuQ[\22M\2\37\21f\0243\20\r\177\33Ak"..., 8192) = -1 EFBIG (File too large)
> lseek(3, -8192, SEEK_CUR) = 0
> write(1, "tst-copy_file_range.c:285: numer"..., 54tst-copy_file_range.c:285: numeric comparison failure
> [...]
It would be interesting to compare this with the real copy_file_range
system call. I don't think it remaps EFBIG, so this might also apply
there, too.
> I also noted it does not provided a non-LFS version and it a good way forward
> imho, however I think we need to explicit handle the case where a non-LFS
> invocation tries to use copy_file_range in a non-supported way. For instance
> the snippet:
>
> [...]
> int fin = open ("/tmp/file.in", O_RDWR | O_CREAT | O_TRUNC, 0600); int fout = open ("/tmp/file.out", O_RDWR | O_CREAT | O_TRUNC, 0600); char buffer[8192] = { 0xcc }; const size_t size = 8192; pwrite (fin, buffer, size, 0); copy_file_range (fin, 0, fout, &(__off64_t) { INT32_MAX }, size, 0);
> [...]
>
> Will again return EFBIG.
Hmm. I think this is just the EFBIG problem. I'd be more concerned
about EBADF here.
> We have some options as 1. handle EFBIG
> as an expected retuned error, 2. do not declare copy_file_range for
> !__USE_FILE_OFFSET64, 3. add a dummy implementation for non-LFS
> (which return ENOSYS).
I'd like to reproduce this with the file system you used on a kernel
with a copy_file_range system call, and see what the system call does there.
>> + /* Main copying loop. The buffer size is arbitrary and is a
>> + trade-off between stack size consumption, cache usage, and
>> + amortization of system call overhead. */
>> + size_t copied = 0;
>> + char buf[8192];
>
> Do we you have any numbers with shorter sizes? Maybe
Sorry, could you expand?
Choosing buffer sizes is notoriously difficult, I'm afraid.
Thanks,
Florian
^ permalink raw reply [flat|nested] 24+ messages in thread
* Re: [PATCH] copy_file_range: New function to copy file data
2017-12-18 20:04 ` Adhemerval Zanella
2017-12-18 20:19 ` Florian Weimer
@ 2017-12-18 21:54 ` Joseph Myers
2017-12-19 11:08 ` Adhemerval Zanella
1 sibling, 1 reply; 24+ messages in thread
From: Joseph Myers @ 2017-12-18 21:54 UTC (permalink / raw)
To: Adhemerval Zanella; +Cc: libc-alpha, Florian Weimer
On Mon, 18 Dec 2017, Adhemerval Zanella wrote:
> Will again return EFBIG. We have some options as 1. handle EFBIG
> as an expected retuned error, 2. do not declare copy_file_range for
> !__USE_FILE_OFFSET64, 3. add a dummy implementation for non-LFS
> (which return ENOSYS).
Since the default on all platforms, even those with 64-bit off_t
unconditionally, is !__USE_FILE_OFFSET64, I don't think 2. is a good
option; it would gratuitously result in the interface being undeclared on
64-bit platforms when _FILE_OFFSET_BITS is not defined.
(Even on 64-bit platforms, _FILE_OFFSET_BITS=64 results in some changes to
C++ name mangling, as discussed in bug 15766, and breaks namespace rules,
bug 14106; on mips64, it also changes struct stat layout. So, while I
think we should move to _FILE_OFFSET_BITS=64 by default on all platforms,
bug 13047, it's not a completely API-compatible change anywhere, and
fixing namespace issues would be desirable before making such a change.)
--
Joseph S. Myers
joseph@codesourcery.com
^ permalink raw reply [flat|nested] 24+ messages in thread
* Re: [PATCH] copy_file_range: New function to copy file data
2017-12-18 20:19 ` Florian Weimer
@ 2017-12-19 11:02 ` Adhemerval Zanella
2017-12-21 13:44 ` Florian Weimer
0 siblings, 1 reply; 24+ messages in thread
From: Adhemerval Zanella @ 2017-12-19 11:02 UTC (permalink / raw)
To: Florian Weimer, libc-alpha
On 18/12/2017 18:19, Florian Weimer wrote:
> On 12/18/2017 09:04 PM, Adhemerval Zanella wrote:
>
>> I am seeing a lot of failures from tst-copy_file_range on both x86_64 and i686
>> when using the fallback implementation (io/copy_file_range.c) due the fact
>> write is returning EFBIG (I attached the test output):
>
> Which file system is this?
It is a default ubuntu 16 installation: /tmp is ext4 (rw,relatime,errors=remount-ro,data=ordered)
with kernel 4.4.0-71-generic
>
>>
>> [...]
>> openat(AT_FDCWD, "/tmp/tst-copy_file_range-in-mT9I6j", O_RDWR) = 3
>> ftruncate(3, 0) = 0
>> openat(AT_FDCWD, "/tmp/tst-copy_file_range-out-0RbGw2", O_RDWR) = 4
>> ftruncate(4, 0)
>> [...]
>> fcntl(4, F_GETFL) = 0x8002 (flags O_RDWR|O_LARGEFILE)
>> read(3, "k2dft\31*b#F=P.AyuQ[\22M\2\37\21f\0243\20\r\177\33Ak"..., 8192) = 8192
>> write(4, "k2dft\31*b#F=P.AyuQ[\22M\2\37\21f\0243\20\r\177\33Ak"..., 8192) = -1 EFBIG (File too large)
>> lseek(3, -8192, SEEK_CUR) = 0
>> write(1, "tst-copy_file_range.c:285: numer"..., 54tst-copy_file_range.c:285: numeric comparison failure
>> [...]
>
> It would be interesting to compare this with the real copy_file_range system call. I don't think it remaps EFBIG, so this might also apply there, too.
I also does not, running example to trigger EFBIG on a 4.13.0-19-generic I see:
[...]
int fin = open ("/tmp/file.in", O_RDWR | O_CREAT | O_TRUNC, 0600);
assert (fin != -1);
int fout = open ("/tmp/file.out", O_RDWR | O_CREAT | O_TRUNC, 0600);
assert (fout != -1);
char buffer[8192];
const size_t size = 8192;
memset (buffer, 0xcc, size);
assert (pwrite (fin, buffer, size, 0) == size);
ssize_t ret = copy_file_range (fin, 0, fout, &(__off64_t) { INT32_MAX }, size, 0);
[...]
strace ...
[...]
openat(AT_FDCWD, "/tmp/file.in", O_RDWR|O_CREAT|O_TRUNC, 0600) = 3
openat(AT_FDCWD, "/tmp/file.out", O_RDWR|O_CREAT|O_TRUNC, 0600) = 4
pwrite64(3, "\314\314\314\314\314\314\314\314\314\314\314\314\314\314\314\314\314\314\314\314\314\314\314\314\314\314\314\314\314\314\314\314"..., 8192, 0) = 8192
copy_file_range(3, NULL, 4, [2147483647], 8192, 0) = -1 EFBIG (File too large)
fstat64(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 5), ...}) = 0
[...]
>
>> I also noted it does not provided a non-LFS version and it a good way forward
>> imho, however I think we need to explicit handle the case where a non-LFS
>> invocation tries to use copy_file_range in a non-supported way. For instance
>> the snippet:
>>
>> [...]
>> int fin = open ("/tmp/file.in", O_RDWR | O_CREAT | O_TRUNC, 0600); int fout = open ("/tmp/file.out", O_RDWR | O_CREAT | O_TRUNC, 0600); char buffer[8192] = { 0xcc
>> }; const size_t size = 8192; pwrite (fin, buffer, size, 0); copy_file_range (fin, 0, fout, &(__off64_t) { INT32_MAX }, size, 0);
>> [...]
>>
>> Will again return EFBIG.
>
> Hmm. I think this is just the EFBIG problem. I'd be more concerned about EBADF here.
>
>> We have some options as 1. handle EFBIG
>> as an expected retuned error, 2. do not declare copy_file_range for
>> !__USE_FILE_OFFSET64, 3. add a dummy implementation for non-LFS
>> (which return ENOSYS).
>
> I'd like to reproduce this with the file system you used on a kernel with a copy_file_range system call, and see what the system call does there.
>
>>> + /* Main copying loop. The buffer size is arbitrary and is a
>>> + trade-off between stack size consumption, cache usage, and
>>> + amortization of system call overhead. */
>>> + size_t copied = 0;
>>> + char buf[8192];
>>
>> Do we you have any numbers with shorter sizes? Maybe
>
> Sorry, could you expand?
>
> Choosing buffer sizes is notoriously difficult, I'm afraid.
I meant performance number, I am kind worries about the buffer size requirement
(although it align to BUFSIZ).
^ permalink raw reply [flat|nested] 24+ messages in thread
* Re: [PATCH] copy_file_range: New function to copy file data
2017-12-18 21:54 ` Joseph Myers
@ 2017-12-19 11:08 ` Adhemerval Zanella
0 siblings, 0 replies; 24+ messages in thread
From: Adhemerval Zanella @ 2017-12-19 11:08 UTC (permalink / raw)
To: Joseph Myers; +Cc: libc-alpha, Florian Weimer
On 18/12/2017 19:53, Joseph Myers wrote:
> On Mon, 18 Dec 2017, Adhemerval Zanella wrote:
>
>> Will again return EFBIG. We have some options as 1. handle EFBIG
>> as an expected retuned error, 2. do not declare copy_file_range for
>> !__USE_FILE_OFFSET64, 3. add a dummy implementation for non-LFS
>> (which return ENOSYS).
>
> Since the default on all platforms, even those with 64-bit off_t
> unconditionally, is !__USE_FILE_OFFSET64, I don't think 2. is a good
> option; it would gratuitously result in the interface being undeclared on
> 64-bit platforms when _FILE_OFFSET_BITS is not defined.
>
> (Even on 64-bit platforms, _FILE_OFFSET_BITS=64 results in some changes to
> C++ name mangling, as discussed in bug 15766, and breaks namespace rules,
> bug 14106; on mips64, it also changes struct stat layout. So, while I
> think we should move to _FILE_OFFSET_BITS=64 by default on all platforms,
> bug 13047, it's not a completely API-compatible change anywhere, and
> fixing namespace issues would be desirable before making such a change.)
>
Agreed, I prefer to 1. since the syscall also shows the same behaviour
with EFBIG.
^ permalink raw reply [flat|nested] 24+ messages in thread
* Re: [PATCH] copy_file_range: New function to copy file data
2017-12-19 11:02 ` Adhemerval Zanella
@ 2017-12-21 13:44 ` Florian Weimer
2017-12-21 17:04 ` Adhemerval Zanella
0 siblings, 1 reply; 24+ messages in thread
From: Florian Weimer @ 2017-12-21 13:44 UTC (permalink / raw)
To: Adhemerval Zanella; +Cc: libc-alpha, Joseph S. Myers
[-- Attachment #1: Type: text/plain, Size: 1004 bytes --]
On 12/19/2017 12:01 PM, Adhemerval Zanella wrote:
> It is a default ubuntu 16 installation: /tmp is ext4 (rw,relatime,errors=remount-ro,data=ordered)
> with kernel 4.4.0-71-generic
I could reproduce this with ext4. The different failure behavior
exposed a bug in the compat code: It did not handle short writes
correctly, after all. It would return if it encountered a short write.
The correct result in that case would be to return the length of the
short write. The fix was to change
+ if (copied > 0)
+ /* Report the number of bytes copied so far. */
+ return copied + written;
into:
+ if (copied + written > 0)
+ /* Report the number of bytes copied so far. */
+ return copied + written;
The test now handles EFBIG in addition to EINVAL. I updated the
documentation to mention EFBIG, too. I do not think it is necessary to
change the compat implementation to handle O_LARGEFILE.
Thanks,
Florian
[-- Attachment #2: copy_file_range.patch --]
[-- Type: text/x-patch, Size: 62038 bytes --]
Subject: [PATCH] copy_file_range: New function to copy file data
To: libc-alpha@sourceware.org
The semantics are based on the Linux system call, but a very close
emulation in user space is provided.
2017-12-21 Florian Weimer <fweimer@redhat.com>
* io/Makefile (routines): Add copy_file_range.
(tests): Add tst-copy_file_range.
(tests-static, tests-internal): Add tst-copy_file_range-compat.
* io/Versions (GLIBC_2.27): Export copy_file_range.
* io/copy_file_range-compat.c: New file.
* io/copy_file_range.c: Likewise.
* io/tst-copy_file_range-compat.c: Likewise.
* io/tst-copy_file_range.c: Likewise.
* manual/llio.texi (Copying File Data): New section.
* posix/unistd.h [__USE_GNU] (copy_file_range): Declare.
* support/Makefile (libsupport-routines): Add support-xfstat,
xftruncate, xlseek.
* support/support-xfstat.c: New file.
* support/xftruncate.c: Likewise.
* support/xlseek.c: Likewise.
* support/xunistd.h (xfstat, xftruncate, xlseek): Declare.
* sysdeps/unix/sysv/linux/**.abilist: Update.
* sysdeps/unix/sysv/linux/copy_file_range.c: New file.
diff --git a/io/Makefile b/io/Makefile
index c72519541f..85eb927d4c 100644
--- a/io/Makefile
+++ b/io/Makefile
@@ -52,7 +52,7 @@ routines := \
ftw ftw64 fts fts64 poll ppoll \
posix_fadvise posix_fadvise64 \
posix_fallocate posix_fallocate64 \
- sendfile sendfile64 \
+ sendfile sendfile64 copy_file_range \
utimensat futimens
# These routines will be omitted from the libc shared object.
@@ -70,7 +70,13 @@ tests := test-utime test-stat test-stat2 test-lfs tst-getcwd \
tst-symlinkat tst-linkat tst-readlinkat tst-mkdirat \
tst-mknodat tst-mkfifoat tst-ttyname_r bug-ftw5 \
tst-posix_fallocate tst-posix_fallocate64 \
- tst-fts tst-fts-lfs tst-open-tmpfile
+ tst-fts tst-fts-lfs tst-open-tmpfile \
+ tst-copy_file_range \
+
+# This test includes the compat implementation of copy_file_range,
+# which uses internal, unexported libc functions.
+tests-static += tst-copy_file_range-compat
+tests-internal += tst-copy_file_range-compat
ifeq ($(run-built-tests),yes)
tests-special += $(objpfx)ftwtest.out
diff --git a/io/Versions b/io/Versions
index 64316cd025..98898cb9d5 100644
--- a/io/Versions
+++ b/io/Versions
@@ -125,4 +125,7 @@ libc {
GLIBC_2.23 {
fts64_children; fts64_close; fts64_open; fts64_read; fts64_set;
}
+ GLIBC_2.27 {
+ copy_file_range;
+ }
}
diff --git a/io/copy_file_range-compat.c b/io/copy_file_range-compat.c
new file mode 100644
index 0000000000..275be97e57
--- /dev/null
+++ b/io/copy_file_range-compat.c
@@ -0,0 +1,158 @@
+/* Emulation of copy_file_range.
+ Copyright (C) 2017 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, see
+ <http://www.gnu.org/licenses/>. */
+
+/* The following macros should be defined before including this
+ file:
+
+ COPY_FILE_RANGE_DECL Declaration specifiers for the function below.
+ COPY_FILE_RANGE Name of the function to define. */
+
+#include <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <limits.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+COPY_FILE_RANGE_DECL
+ssize_t
+COPY_FILE_RANGE (int infd, __off64_t *pinoff,
+ int outfd, __off64_t *poutoff,
+ size_t length, unsigned int flags)
+{
+ if (flags != 0)
+ {
+ __set_errno (EINVAL);
+ return -1;
+ }
+
+ struct stat64 instat;
+ struct stat64 outstat;
+ if (fstat64 (infd, &instat) != 0 || fstat64 (outfd, &outstat) != 0)
+ return -1;
+ if (S_ISDIR (instat.st_mode) || S_ISDIR (outstat.st_mode))
+ {
+ __set_errno (EISDIR);
+ return -1;
+ }
+ if (!S_ISREG (instat.st_mode) || !S_ISREG (outstat.st_mode))
+ {
+ /* We need a regular input file so that the we can seek
+ backwards in case of a write failure. */
+ __set_errno (EINVAL);
+ return -1;
+ }
+ if (instat.st_dev != outstat.st_dev)
+ {
+ /* Cross-device copies are not supported. */
+ __set_errno (EXDEV);
+ return -1;
+ }
+
+ /* The output descriptor must not have O_APPEND set. */
+ {
+ int flags = __fcntl (outfd, F_GETFL);
+ if (flags & O_APPEND)
+ {
+ __set_errno (EBADF);
+ return -1;
+ }
+ }
+
+ /* Avoid an overflow in the result. */
+ if (length > SSIZE_MAX)
+ length = SSIZE_MAX;
+
+ /* Main copying loop. The buffer size is arbitrary and is a
+ trade-off between stack size consumption, cache usage, and
+ amortization of system call overhead. */
+ size_t copied = 0;
+ char buf[8192];
+ while (length > 0)
+ {
+ size_t to_read = length;
+ if (to_read > sizeof (buf))
+ to_read = sizeof (buf);
+
+ /* Fill the buffer. */
+ ssize_t read_count;
+ if (pinoff == NULL)
+ read_count = read (infd, buf, to_read);
+ else
+ read_count = __libc_pread64 (infd, buf, to_read, *pinoff);
+ if (read_count == 0)
+ /* End of file reached prematurely. */
+ return copied;
+ if (read_count < 0)
+ {
+ if (copied > 0)
+ /* Report the number of bytes copied so far. */
+ return copied;
+ return -1;
+ }
+ if (pinoff != 0)
+ *pinoff += read_count;
+
+ /* Write the buffer part which was read to the destination. */
+ char *end = buf + read_count;
+ for (char *p = buf; p < end; )
+ {
+ ssize_t write_count;
+ if (poutoff == NULL)
+ write_count = write (outfd, p, end - p);
+ else
+ write_count = __libc_pwrite64 (outfd, p, end - p, *poutoff);
+ if (write_count < 0)
+ {
+ /* Adjust the input read position to match what we have
+ written, so that the caller can pick up after the
+ error. */
+ size_t written = p - buf;
+ /* NB: This needs to be signed so that we can form the
+ negative value below. */
+ ssize_t overread = read_count - written;
+ if (pinoff == NULL)
+ {
+ if (overread > 0)
+ {
+ /* We are on an error recovery path, so we
+ cannot deal with failure here. */
+ int save_errno = errno;
+ (void) __libc_lseek64 (infd, -overread, SEEK_CUR);
+ __set_errno (save_errno);
+ }
+ }
+ else /* pinoff != NULL */
+ *pinoff -= overread;
+
+ if (copied + written > 0)
+ /* Report the number of bytes copied so far. */
+ return copied + written;
+ return -1;
+ }
+ p += write_count;
+ if (poutoff != NULL)
+ *poutoff += write_count;
+ } /* Write loop. */
+
+ copied += read_count;
+ length -= read_count;
+ }
+ return copied;
+}
diff --git a/io/copy_file_range.c b/io/copy_file_range.c
new file mode 100644
index 0000000000..61ee6871b4
--- /dev/null
+++ b/io/copy_file_range.c
@@ -0,0 +1,22 @@
+/* Generic implementation of copy_file_range.
+ Copyright (C) 2017 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, see
+ <http://www.gnu.org/licenses/>. */
+
+#define COPY_FILE_RANGE_DECL
+#define COPY_FILE_RANGE copy_file_range
+
+#include <io/copy_file_range-compat.c>
diff --git a/io/tst-copy_file_range-compat.c b/io/tst-copy_file_range-compat.c
new file mode 100644
index 0000000000..eb737946d9
--- /dev/null
+++ b/io/tst-copy_file_range-compat.c
@@ -0,0 +1,30 @@
+/* Test the fallback implementation of copy_file_range.
+ Copyright (C) 2017 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, see
+ <http://www.gnu.org/licenses/>. */
+
+/* Get the declaration of the official copy_of_range function. */
+#include <unistd.h>
+
+/* Compile a local version of copy_file_range. */
+#define COPY_FILE_RANGE_DECL static
+#define COPY_FILE_RANGE copy_file_range_compat
+#include <io/copy_file_range-compat.c>
+
+/* Re-use the test, but run it against copy_file_range_compat defined
+ above. */
+#define copy_file_range copy_file_range_compat
+#include "tst-copy_file_range.c"
diff --git a/io/tst-copy_file_range.c b/io/tst-copy_file_range.c
new file mode 100644
index 0000000000..d8f4e8ac04
--- /dev/null
+++ b/io/tst-copy_file_range.c
@@ -0,0 +1,833 @@
+/* Tests for copy_file_range.
+ Copyright (C) 2017 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, see
+ <http://www.gnu.org/licenses/>. */
+
+#include <array_length.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <libgen.h>
+#include <poll.h>
+#include <sched.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <support/check.h>
+#include <support/namespace.h>
+#include <support/support.h>
+#include <support/temp_file.h>
+#include <support/test-driver.h>
+#include <support/xunistd.h>
+#include <sys/mount.h>
+
+/* Boolean flags which indicate whether to use pointers with explicit
+ output flags. */
+static int do_inoff;
+static int do_outoff;
+
+/* Name and descriptors of the input files. Files are truncated and
+ reopened (with O_RDWR) between tests. */
+static char *infile;
+static int infd;
+static char *outfile;
+static int outfd;
+
+/* Like the above, but on a different file system. xdevfile can be
+ NULL if no suitable file system has been found. */
+static char *xdevfile;
+
+/* Input and output offsets. Set according to do_inoff and do_outoff
+ before the test. The offsets themselves are always set to
+ zero. */
+static off64_t inoff;
+static off64_t *pinoff;
+static off64_t outoff;
+static off64_t *poutoff;
+
+/* These are a collection of copy sizes used in tests. The selection
+ takes into account that the fallback implementation uses an
+ internal buffer of 8192 bytes. */
+enum { maximum_size = 99999 };
+static const int typical_sizes[] =
+ { 0, 1, 2, 3, 1024, 2048, 4096, 8191, 8192, 8193, 16383, 16384, 16385,
+ maximum_size };
+
+/* The random contents of this array can be used as a pattern to check
+ for correct write operations. */
+static unsigned char random_data[maximum_size];
+
+/* The size chosen by the test harness. */
+static int current_size;
+
+/* Maximum writable file offset. Updated by find_maximum_offset
+ below. */
+static off64_t maximum_offset;
+
+/* Error code when crossing the offset. */
+static int maximum_offset_errno;
+
+/* If true: Writes which cross the limit will fail. If false: Writes
+ which cross the limit will result in a partial write. */
+static bool maximum_offset_hard_limit;
+
+/* Fills maximum_offset etc. above. Truncates outfd as a side
+ effect. */
+static void
+find_maximum_offset (void)
+{
+ xftruncate (outfd, 0);
+ if (maximum_offset != 0)
+ return;
+
+ uint64_t upper = -1;
+ upper >>= 1; /* Maximum of off64_t. */
+ TEST_VERIFY ((off64_t) upper > 0);
+ TEST_VERIFY ((off64_t) (upper + 1) < 0);
+ if (lseek64 (outfd, upper, SEEK_SET) >= 0)
+ {
+ if (write (outfd, "", 1) == 1)
+ FAIL_EXIT1 ("created a file larger than the off64_t range");
+ }
+
+ uint64_t lower = 1024 * 1024; /* A reasonable minimum file size. */
+ /* Loop invariant: writing at lower succeeds, writing at upper fails. */
+ while (lower + 1 < upper)
+ {
+ uint64_t middle = (lower + upper) / 2;
+ if (test_verbose > 0)
+ printf ("info: %s: remaining test range %" PRIu64 " .. %" PRIu64
+ ", probe at %" PRIu64 "\n", __func__, lower, upper, middle);
+ xftruncate (outfd, 0);
+ if (lseek64 (outfd, middle, SEEK_SET) >= 0
+ && write (outfd, "", 1) == 1)
+ lower = middle;
+ else
+ upper = middle;
+ }
+ TEST_VERIFY (lower + 1 == upper);
+ maximum_offset = lower;
+ printf ("info: maximum writable file offset: %" PRIu64 " (%" PRIx64 ")\n",
+ lower, lower);
+
+ /* Check that writing at the valid offset actually works. */
+ xftruncate (outfd, 0);
+ xlseek (outfd, lower, SEEK_SET);
+ TEST_COMPARE (write (outfd, "", 1), 1);
+
+ /* Cross the boundary with a two-byte write. This can either result
+ in a short write, or a failure. */
+ xlseek (outfd, lower, SEEK_SET);
+ ssize_t ret = write (outfd, " ", 2);
+ if (ret < 0)
+ {
+ maximum_offset_errno = errno;
+ maximum_offset_hard_limit = true;
+ }
+ else
+ maximum_offset_hard_limit = false;
+
+ /* Check that writing at the next offset actually fails. This also
+ obtains the expected errno value. */
+ xftruncate (outfd, 0);
+ const char *action;
+ if (lseek64 (outfd, lower + 1, SEEK_SET) != 0)
+ {
+ if (write (outfd, "", 1) != -1)
+ FAIL_EXIT1 ("write to impossible offset %" PRIu64 " succeeded",
+ lower + 1);
+ action = "writing";
+ int errno_copy = errno;
+ if (maximum_offset_hard_limit)
+ TEST_COMPARE (errno_copy, maximum_offset_errno);
+ else
+ maximum_offset_errno = errno_copy;
+ }
+ else
+ {
+ action = "seeking";
+ maximum_offset_errno = errno;
+ }
+ printf ("info: %s out of range fails with %m (%d)\n",
+ action, maximum_offset_errno);
+
+ xftruncate (outfd, 0);
+ xlseek (outfd, 0, SEEK_SET);
+}
+
+/* Perform a copy of a file. */
+static void
+simple_file_copy (void)
+{
+ xwrite (infd, random_data, current_size);
+
+ int length;
+ int in_skipped; /* Expected skipped bytes in input. */
+ if (do_inoff)
+ {
+ xlseek (infd, 1, SEEK_SET);
+ inoff = 2;
+ length = current_size - 3;
+ in_skipped = 2;
+ }
+ else
+ {
+ xlseek (infd, 3, SEEK_SET);
+ length = current_size - 5;
+ in_skipped = 3;
+ }
+ int out_skipped; /* Expected skipped bytes before the written data. */
+ if (do_outoff)
+ {
+ xlseek (outfd, 4, SEEK_SET);
+ outoff = 5;
+ out_skipped = 5;
+ }
+ else
+ {
+ xlseek (outfd, 6, SEEK_SET);
+ length = current_size - 6;
+ out_skipped = 6;
+ }
+ if (length < 0)
+ length = 0;
+
+ TEST_COMPARE (copy_file_range (infd, pinoff, outfd, poutoff,
+ length, 0), length);
+ if (do_inoff)
+ {
+ TEST_COMPARE (inoff, 2 + length);
+ TEST_COMPARE (xlseek (infd, 0, SEEK_CUR), 1);
+ }
+ else
+ TEST_COMPARE (xlseek (infd, 0, SEEK_CUR), 3 + length);
+ if (do_outoff)
+ {
+ TEST_COMPARE (outoff, 5 + length);
+ TEST_COMPARE (xlseek (outfd, 0, SEEK_CUR), 4);
+ }
+ else
+ TEST_COMPARE (xlseek (outfd, 0, SEEK_CUR), 6 + length);
+
+ struct stat64 st;
+ xfstat (outfd, &st);
+ if (length > 0)
+ TEST_COMPARE (st.st_size, out_skipped + length);
+ else
+ {
+ /* If we did not write anything, we also did not add any
+ padding. */
+ TEST_COMPARE (st.st_size, 0);
+ return;
+ }
+
+ xlseek (outfd, 0, SEEK_SET);
+ char *bytes = xmalloc (st.st_size);
+ TEST_COMPARE (read (outfd, bytes, st.st_size), st.st_size);
+ for (int i = 0; i < out_skipped; ++i)
+ TEST_COMPARE (bytes[i], 0);
+ TEST_VERIFY (memcmp (bytes + out_skipped, random_data + in_skipped,
+ length) == 0);
+ free (bytes);
+}
+
+/* Test that reading from a pipe willfails. */
+static void
+pipe_as_source (void)
+{
+ int pipefds[2];
+ xpipe (pipefds);
+
+ for (int length = 0; length < 2; ++length)
+ {
+ if (test_verbose > 0)
+ printf ("info: %s: length=%d\n", __func__, length);
+
+ /* Make sure that there is something to copy in the pipe. */
+ xwrite (pipefds[1], "@", 1);
+
+ TEST_COMPARE (copy_file_range (pipefds[0], pinoff, outfd, poutoff,
+ length, 0), -1);
+ /* Linux 4.10 and later return EINVAL. Older kernels return
+ EXDEV. */
+ TEST_VERIFY (errno == EINVAL || errno == EXDEV);
+ TEST_COMPARE (inoff, 0);
+ TEST_COMPARE (outoff, 0);
+ TEST_COMPARE (xlseek (outfd, 0, SEEK_CUR), 0);
+
+ /* Make sure that nothing was read. */
+ char buf = 'A';
+ TEST_COMPARE (read (pipefds[0], &buf, 1), 1);
+ TEST_COMPARE (buf, '@');
+ }
+
+ xclose (pipefds[0]);
+ xclose (pipefds[1]);
+}
+
+/* Test that writing to a pipe fails. */
+static void
+pipe_as_destination (void)
+{
+ /* Make sure that there is something to read in the input file. */
+ xwrite (infd, "abc", 3);
+ xlseek (infd, 0, SEEK_SET);
+
+ int pipefds[2];
+ xpipe (pipefds);
+
+ for (int length = 0; length < 2; ++length)
+ {
+ if (test_verbose > 0)
+ printf ("info: %s: length=%d\n", __func__, length);
+
+ TEST_COMPARE (copy_file_range (infd, pinoff, pipefds[1], poutoff,
+ length, 0), -1);
+ /* Linux 4.10 and later return EINVAL. Older kernels return
+ EXDEV. */
+ TEST_VERIFY (errno == EINVAL || errno == EXDEV);
+ TEST_COMPARE (inoff, 0);
+ TEST_COMPARE (outoff, 0);
+ TEST_COMPARE (xlseek (infd, 0, SEEK_CUR), 0);
+
+ /* Make sure that nothing was written. */
+ struct pollfd pollfd = { .fd = pipefds[0], .events = POLLIN, };
+ TEST_COMPARE (poll (&pollfd, 1, 0), 0);
+ }
+
+ xclose (pipefds[0]);
+ xclose (pipefds[1]);
+}
+
+/* Test a write failure after (potentially) writing some bytes.
+ Failure occurs near the start of the buffer. */
+static void
+delayed_write_failure_beginning (void)
+{
+ /* We need to write something to provoke the error. */
+ if (current_size == 0)
+ return;
+ xwrite (infd, random_data, sizeof (random_data));
+ xlseek (infd, 0, SEEK_SET);
+
+ /* Write failure near the start. The actual error code varies among
+ file systems. */
+ find_maximum_offset ();
+ off64_t where = maximum_offset;
+
+ if (current_size == 1)
+ ++where;
+ outoff = where;
+ if (do_outoff)
+ xlseek (outfd, 1, SEEK_SET);
+ else
+ xlseek (outfd, where, SEEK_SET);
+ if (maximum_offset_hard_limit || where > maximum_offset)
+ {
+ TEST_COMPARE (copy_file_range (infd, pinoff, outfd, poutoff,
+ sizeof (random_data), 0), -1);
+ TEST_COMPARE (errno, maximum_offset_errno);
+ TEST_COMPARE (xlseek (infd, 0, SEEK_CUR), 0);
+ TEST_COMPARE (inoff, 0);
+ if (do_outoff)
+ TEST_COMPARE (xlseek (outfd, 0, SEEK_CUR), 1);
+ else
+ TEST_COMPARE (xlseek (outfd, 0, SEEK_CUR), where);
+ TEST_COMPARE (outoff, where);
+ struct stat64 st;
+ xfstat (outfd, &st);
+ TEST_COMPARE (st.st_size, 0);
+ }
+ else
+ {
+ /* The offset is not a hard limit. This means we write one
+ byte. */
+ TEST_COMPARE (copy_file_range (infd, pinoff, outfd, poutoff,
+ sizeof (random_data), 0), 1);
+ if (do_inoff)
+ {
+ TEST_COMPARE (inoff, 1);
+ TEST_COMPARE (xlseek (infd, 0, SEEK_CUR), 0);
+ }
+ else
+ {
+ TEST_COMPARE (xlseek (infd, 0, SEEK_CUR), 1);
+ TEST_COMPARE (inoff, 0);
+ }
+ if (do_outoff)
+ {
+ TEST_COMPARE (xlseek (outfd, 0, SEEK_CUR), 1);
+ TEST_COMPARE (outoff, where + 1);
+ }
+ else
+ {
+ TEST_COMPARE (xlseek (outfd, 0, SEEK_CUR), where + 1);
+ TEST_COMPARE (outoff, where);
+ }
+ struct stat64 st;
+ xfstat (outfd, &st);
+ TEST_COMPARE (st.st_size, where + 1);
+ }
+}
+
+/* Test a write failure after (potentially) writing some bytes.
+ Failure occurs near the end of the buffer. */
+static void
+delayed_write_failure_end (void)
+{
+ if (current_size <= 1)
+ /* This would be same as the first test because there is not
+ enough data to write to make a difference. */
+ return;
+ xwrite (infd, random_data, sizeof (random_data));
+ xlseek (infd, 0, SEEK_SET);
+
+ find_maximum_offset ();
+ off64_t where = maximum_offset - current_size + 1;
+ if (current_size == sizeof (random_data))
+ /* Otherwise we do not reach the non-writable byte. */
+ ++where;
+ outoff = where;
+ if (do_outoff)
+ xlseek (outfd, 1, SEEK_SET);
+ else
+ xlseek (outfd, where, SEEK_SET);
+ ssize_t ret = copy_file_range (infd, pinoff, outfd, poutoff,
+ sizeof (random_data), 0);
+ if (ret < 0)
+ {
+ TEST_COMPARE (ret, -1);
+ TEST_COMPARE (errno, maximum_offset_errno);
+ struct stat64 st;
+ xfstat (outfd, &st);
+ TEST_COMPARE (st.st_size, 0);
+ }
+ else
+ {
+ /* The first copy succeeded. This happens in the emulation
+ because the internal buffer of limited size does not
+ necessarily cross the off64_t boundary on the first write
+ operation. */
+ if (test_verbose > 0)
+ printf ("info: copy_file_range (%zu) returned %zd\n",
+ sizeof (random_data), ret);
+ TEST_VERIFY (ret > 0);
+ TEST_VERIFY (ret < maximum_size);
+ struct stat64 st;
+ xfstat (outfd, &st);
+ TEST_COMPARE (st.st_size, where + ret);
+ if (do_inoff)
+ {
+ TEST_COMPARE (inoff, ret);
+ TEST_COMPARE (xlseek (infd, 0, SEEK_CUR), 0);
+ }
+ else
+ TEST_COMPARE (xlseek (infd, 0, SEEK_CUR), ret);
+
+ char *buffer = xmalloc (ret);
+ TEST_COMPARE (pread64 (outfd, buffer, ret, where), ret);
+ TEST_VERIFY (memcmp (buffer, random_data, ret) == 0);
+ free (buffer);
+
+ /* The second copy fails. */
+ TEST_COMPARE (copy_file_range (infd, pinoff, outfd, poutoff,
+ sizeof (random_data), 0), -1);
+ TEST_COMPARE (errno, maximum_offset_errno);
+ }
+}
+
+/* Test a write failure across devices. */
+static void
+cross_device_failure (void)
+{
+ if (xdevfile == NULL)
+ /* Subtest not supported due to missing cross-device file. */
+ return;
+
+ /* We need something to write. */
+ xwrite (infd, random_data, sizeof (random_data));
+ xlseek (infd, 0, SEEK_SET);
+
+ int xdevfd = xopen (xdevfile, O_RDWR | O_LARGEFILE, 0);
+ TEST_COMPARE (copy_file_range (infd, pinoff, xdevfd, poutoff,
+ current_size, 0), -1);
+ TEST_COMPARE (errno, EXDEV);
+ TEST_COMPARE (xlseek (infd, 0, SEEK_CUR), 0);
+ struct stat64 st;
+ xfstat (xdevfd, &st);
+ TEST_COMPARE (st.st_size, 0);
+
+ xclose (xdevfd);
+}
+
+/* Try to exercise ENOSPC behavior with a tempfs file system (so that
+ we do not have to fill up a regular file system to get the error).
+ This function runs in a subprocess, so that we do not change the
+ mount namespace of the actual test process. */
+static void
+enospc_failure_1 (void *closure)
+{
+#ifdef CLONE_NEWNS
+ support_become_root ();
+
+ /* Make sure that we do not alter the file system mounts of the
+ parents. */
+ if (! support_enter_mount_namespace ())
+ {
+ printf ("warning: ENOSPC test skipped\n");
+ return;
+ }
+
+ char *mountpoint = closure;
+ if (mount ("none", mountpoint, "tmpfs", MS_NODEV | MS_NOEXEC,
+ "size=500k") != 0)
+ {
+ printf ("warning: could not mount tmpfs at %s: %m\n", mountpoint);
+ return;
+ }
+
+ /* The source file must reside on the same file system. */
+ char *intmpfsfile = xasprintf ("%s/%s", mountpoint, "in");
+ int intmpfsfd = xopen (intmpfsfile, O_RDWR | O_CREAT | O_LARGEFILE, 0600);
+ xwrite (intmpfsfd, random_data, sizeof (random_data));
+ xlseek (intmpfsfd, 1, SEEK_SET);
+ inoff = 1;
+
+ char *outtmpfsfile = xasprintf ("%s/%s", mountpoint, "out");
+ int outtmpfsfd = xopen (outtmpfsfile, O_RDWR | O_CREAT | O_LARGEFILE, 0600);
+
+ /* Fill the file with data until ENOSPC is reached. */
+ while (true)
+ {
+ ssize_t ret = write (outtmpfsfd, random_data, sizeof (random_data));
+ if (ret < 0 && errno != ENOSPC)
+ FAIL_EXIT1 ("write to %s: %m", outtmpfsfile);
+ if (ret < sizeof (random_data))
+ break;
+ }
+ TEST_COMPARE (write (outtmpfsfd, "", 1), -1);
+ TEST_COMPARE (errno, ENOSPC);
+ off64_t maxsize = xlseek (outtmpfsfd, 0, SEEK_CUR);
+ TEST_VERIFY_EXIT (maxsize > sizeof (random_data));
+
+ /* Constructed the expected file contents. */
+ char *expected = xmalloc (maxsize);
+ TEST_COMPARE (pread64 (outtmpfsfd, expected, maxsize, 0), maxsize);
+ /* Go back a little, so some bytes can be written. */
+ enum { offset = 20000 };
+ TEST_VERIFY_EXIT (offset < maxsize);
+ TEST_VERIFY_EXIT (offset < sizeof (random_data));
+ memcpy (expected + maxsize - offset, random_data + 1, offset);
+
+ if (do_outoff)
+ {
+ outoff = maxsize - offset;
+ xlseek (outtmpfsfd, 2, SEEK_SET);
+ }
+ else
+ xlseek (outtmpfsfd, -offset, SEEK_CUR);
+
+ /* First call is expected to succeed because we made room for some
+ bytes. */
+ TEST_COMPARE (copy_file_range (intmpfsfd, pinoff, outtmpfsfd, poutoff,
+ maximum_size, 0), offset);
+ if (do_inoff)
+ {
+ TEST_COMPARE (inoff, 1 + offset);
+ TEST_COMPARE (xlseek (intmpfsfd, 0, SEEK_CUR), 1);
+ }
+ else
+ TEST_COMPARE (xlseek (intmpfsfd, 0, SEEK_CUR), 1 + offset);
+ if (do_outoff)
+ {
+ TEST_COMPARE (outoff, maxsize);
+ TEST_COMPARE (xlseek (outtmpfsfd, 0, SEEK_CUR), 2);
+ }
+ else
+ TEST_COMPARE (xlseek (outtmpfsfd, 0, SEEK_CUR), maxsize);
+ struct stat64 st;
+ xfstat (outtmpfsfd, &st);
+ TEST_COMPARE (st.st_size, maxsize);
+ char *actual = xmalloc (st.st_size);
+ TEST_COMPARE (pread64 (outtmpfsfd, actual, st.st_size, 0), st.st_size);
+ TEST_VERIFY (memcmp (expected, actual, maxsize) == 0);
+
+ /* Second call should fail with ENOSPC. */
+ TEST_COMPARE (copy_file_range (intmpfsfd, pinoff, outtmpfsfd, poutoff,
+ maximum_size, 0), -1);
+ TEST_COMPARE (errno, ENOSPC);
+
+ /* Offsets should be unchanged. */
+ if (do_inoff)
+ {
+ TEST_COMPARE (inoff, 1 + offset);
+ TEST_COMPARE (xlseek (intmpfsfd, 0, SEEK_CUR), 1);
+ }
+ else
+ TEST_COMPARE (xlseek (intmpfsfd, 0, SEEK_CUR), 1 + offset);
+ if (do_outoff)
+ {
+ TEST_COMPARE (outoff, maxsize);
+ TEST_COMPARE (xlseek (outtmpfsfd, 0, SEEK_CUR), 2);
+ }
+ else
+ TEST_COMPARE (xlseek (outtmpfsfd, 0, SEEK_CUR), maxsize);
+ TEST_COMPARE (xlseek (outtmpfsfd, 0, SEEK_END), maxsize);
+ TEST_COMPARE (pread64 (outtmpfsfd, actual, maxsize, 0), maxsize);
+ TEST_VERIFY (memcmp (expected, actual, maxsize) == 0);
+
+ free (actual);
+ free (expected);
+
+ xclose (intmpfsfd);
+ xclose (outtmpfsfd);
+ free (intmpfsfile);
+ free (outtmpfsfile);
+
+#else /* !CLONE_NEWNS */
+ puts ("warning: ENOSPC test skipped (no mount namespaces)");
+#endif
+}
+
+/* Call enospc_failure_1 in a subprocess. */
+static void
+enospc_failure (void)
+{
+ char *mountpoint
+ = support_create_temp_directory ("tst-copy_file_range-enospc-");
+ support_isolate_in_subprocess (enospc_failure_1, mountpoint);
+ free (mountpoint);
+}
+
+/* The target file descriptor must have O_APPEND enabled. */
+static void
+oappend_failure (void)
+{
+ /* Add data, to make sure we do not fail because there is
+ insufficient input data. */
+ xwrite (infd, random_data, current_size);
+ xlseek (infd, 0, SEEK_SET);
+
+ xclose (outfd);
+ outfd = xopen (outfile, O_RDWR | O_APPEND, 0);
+ TEST_COMPARE (copy_file_range (infd, pinoff, outfd, poutoff,
+ current_size, 0), -1);
+ TEST_COMPARE (errno, EBADF);
+}
+
+/* Test that a short input file results in a shortened copy. */
+static void
+short_copy (void)
+{
+ if (current_size == 0)
+ /* Nothing to shorten. */
+ return;
+
+ /* Two subtests, one with offset 0 and current_size - 1 bytes, and
+ another one with current_size bytes, but offset 1. */
+ for (int shift = 0; shift < 2; ++shift)
+ {
+ if (test_verbose > 0)
+ printf ("info: shift=%d\n", shift);
+ xftruncate (infd, 0);
+ xlseek (infd, 0, SEEK_SET);
+ xwrite (infd, random_data, current_size - !shift);
+
+ if (do_inoff)
+ {
+ inoff = shift;
+ xlseek (infd, 2, SEEK_SET);
+ }
+ else
+ {
+ inoff = 3;
+ xlseek (infd, shift, SEEK_SET);
+ }
+ ftruncate (outfd, 0);
+ xlseek (outfd, 0, SEEK_SET);
+ outoff = 0;
+
+ /* First call copies current_size - 1 bytes. */
+ TEST_COMPARE (copy_file_range (infd, pinoff, outfd, poutoff,
+ current_size, 0), current_size - 1);
+ char *buffer = xmalloc (current_size);
+ TEST_COMPARE (pread64 (outfd, buffer, current_size, 0),
+ current_size - 1);
+ TEST_VERIFY (memcmp (buffer, random_data + shift, current_size - 1)
+ == 0);
+ free (buffer);
+
+ if (do_inoff)
+ {
+ TEST_COMPARE (inoff, current_size - 1 + shift);
+ TEST_COMPARE (xlseek (infd, 0, SEEK_CUR), 2);
+ }
+ else
+ TEST_COMPARE (xlseek (infd, 0, SEEK_CUR), current_size - 1 + shift);
+ if (do_outoff)
+ {
+ TEST_COMPARE (outoff, current_size - 1);
+ TEST_COMPARE (xlseek (outfd, 0, SEEK_CUR), 0);
+ }
+ else
+ TEST_COMPARE (xlseek (outfd, 0, SEEK_CUR), current_size - 1);
+
+ /* First call copies zero bytes. */
+ TEST_COMPARE (copy_file_range (infd, pinoff, outfd, poutoff,
+ current_size, 0), 0);
+ /* And the offsets are unchanged. */
+ if (do_inoff)
+ {
+ TEST_COMPARE (inoff, current_size - 1 + shift);
+ TEST_COMPARE (xlseek (infd, 0, SEEK_CUR), 2);
+ }
+ else
+ TEST_COMPARE (xlseek (infd, 0, SEEK_CUR), current_size - 1 + shift);
+ if (do_outoff)
+ {
+ TEST_COMPARE (outoff, current_size - 1);
+ TEST_COMPARE (xlseek (outfd, 0, SEEK_CUR), 0);
+ }
+ else
+ TEST_COMPARE (xlseek (outfd, 0, SEEK_CUR), current_size - 1);
+ }
+}
+
+/* A named test function. */
+struct test_case
+{
+ const char *name;
+ void (*func) (void);
+ bool sizes; /* If true, call the test with different current_size values. */
+};
+
+/* The available test cases. */
+static struct test_case tests[] =
+ {
+ { "simple_file_copy", simple_file_copy, .sizes = true },
+ { "pipe_as_source", pipe_as_source, },
+ { "pipe_as_destination", pipe_as_destination, },
+ { "delayed_write_failure_beginning", delayed_write_failure_beginning,
+ .sizes = true },
+ { "delayed_write_failure_end", delayed_write_failure_end, .sizes = true },
+ { "cross_device_failure", cross_device_failure, .sizes = true },
+ { "enospc_failure", enospc_failure, },
+ { "oappend_failure", oappend_failure, .sizes = true },
+ { "short_copy", short_copy, .sizes = true },
+ };
+
+static int
+do_test (void)
+{
+ for (unsigned char *p = random_data; p < array_end (random_data); ++p)
+ *p = rand () >> 24;
+
+ infd = create_temp_file ("tst-copy_file_range-in-", &infile);
+ xclose (create_temp_file ("tst-copy_file_range-out-", &outfile));
+
+ /* Try to find a different directory from the default input/output
+ file. */
+ {
+ struct stat64 instat;
+ xfstat (infd, &instat);
+ static const char *const candidates[] =
+ { NULL, "/var/tmp", "/dev/shm" };
+ for (const char *const *c = candidates; c < array_end (candidates); ++c)
+ {
+ const char *path = *c;
+ char *to_free = NULL;
+ if (path == NULL)
+ {
+ to_free = xreadlink ("/proc/self/exe");
+ path = dirname (to_free);
+ }
+
+ struct stat64 cstat;
+ xstat (path, &cstat);
+ if (cstat.st_dev == instat.st_dev)
+ {
+ free (to_free);
+ continue;
+ }
+
+ printf ("info: using alternate temporary files directory: %s\n", path);
+ xdevfile = xasprintf ("%s/tst-copy_file_range-xdev-XXXXXX", path);
+ free (to_free);
+ break;
+ }
+ if (xdevfile != NULL)
+ {
+ int xdevfd = mkstemp (xdevfile);
+ if (xdevfd < 0)
+ FAIL_EXIT1 ("mkstemp (\"%s\"): %m", xdevfile);
+ struct stat64 xdevst;
+ xfstat (xdevfd, &xdevst);
+ TEST_VERIFY (xdevst.st_dev != instat.st_dev);
+ add_temp_file (xdevfile);
+ xclose (xdevfd);
+ }
+ else
+ puts ("warning: no alternate directory on different file system found");
+ }
+ xclose (infd);
+
+ for (do_inoff = 0; do_inoff < 2; ++do_inoff)
+ for (do_outoff = 0; do_outoff < 2; ++do_outoff)
+ for (struct test_case *test = tests; test < array_end (tests); ++test)
+ for (const int *size = typical_sizes;
+ size < array_end (typical_sizes); ++size)
+ {
+ current_size = *size;
+ if (test_verbose > 0)
+ printf ("info: %s do_inoff=%d do_outoff=%d current_size=%d\n",
+ test->name, do_inoff, do_outoff, current_size);
+
+ inoff = 0;
+ if (do_inoff)
+ pinoff = &inoff;
+ else
+ pinoff = NULL;
+ outoff = 0;
+ if (do_outoff)
+ poutoff = &outoff;
+ else
+ poutoff = NULL;
+
+ infd = xopen (infile, O_RDWR | O_LARGEFILE, 0);
+ xftruncate (infd, 0);
+ outfd = xopen (outfile, O_RDWR | O_LARGEFILE, 0);
+ xftruncate (outfd, 0);
+
+ test->func ();
+
+ xclose (infd);
+ xclose (outfd);
+
+ if (!test->sizes)
+ /* Skip the other sizes unless they have been
+ requested. */
+ break;
+ }
+
+ free (infile);
+ free (outfile);
+ free (xdevfile);
+
+ return 0;
+}
+
+#include <support/test-driver.c>
diff --git a/manual/llio.texi b/manual/llio.texi
index 8b2f599c79..642e56e710 100644
--- a/manual/llio.texi
+++ b/manual/llio.texi
@@ -41,6 +41,7 @@ directly.)
* Stream/Descriptor Precautions:: Precautions needed if you use both
descriptors and streams.
* Scatter-Gather:: Fast I/O to discontinuous buffers.
+* Copying File Data:: Copying data between files.
* Memory-mapped I/O:: Using files like memory.
* Waiting for I/O:: How to check for input or output
on multiple file descriptors.
@@ -1353,6 +1354,93 @@ When the source file is compiled using @code{_FILE_OFFSET_BITS == 64} on a
@code{pwritev2} and so transparently replaces the 32 bit interface.
@end deftypefun
+@node Copying File Data
+@section Copying data between two files
+@cindex copying files
+@cindex file copy
+
+A special function is provided to copy data between two files on the
+same file system. The system can optimize such copy operations. This
+is particularly important on network file systems, where the data would
+otherwise have to be transferred twice over the network.
+
+Note that this function only copies file data, but not metadata such as
+file permissions or extended attributes.
+
+@deftypefun ssize_t copy_file_range (int @var{inputfd}, off64_t *@var{inputpos}, int @var{outputfd}, off64_t *@var{outputpos}, ssize_t @var{length}, unsigned int @var{flags})
+@standards{GNU, unistd.h}
+@safety{@prelim{}@mtsafe{}@assafe{}@acsafe{}}
+
+This function copies up to @var{length} bytes from the file descriptor
+@var{inputfd} to the file descriptor @var{outputfd}.
+
+The function can operate on both the current file position (like
+@code{read} and @code{write}) and an explicit offset (like @code{pread}
+and @code{pwrite}). If the @var{inputpos} pointer is null, the file
+position of @var{inputfd} is used as the starting point of the copy
+operation, and the file position is advanced during it. If
+@var{inputpos} is not null, then @code{*@var{inputpos}} is used as the
+starting point of the copy operation, and @code{*@var{inputpos}} is
+incremented by the number of copied bytes, but the file position remains
+unchanged. Similar rules apply to @var{outputfd} and @var{outputpos}
+for the output file position.
+
+The @var{flags} argument is currently reserved and must be zero.
+
+The @code{copy_file_range} function returns the number of bytes copied.
+This can be less than the specified @var{length} in case the input file
+contains fewer remaining bytes than @var{length}, or if a read or write
+failure occurs. The return value is zero if the end of the input file
+is encountered immediately.
+
+If no bytes can be copied, to report an error, @code{copy_file_range}
+returns the value @math{-1} and sets @code{errno}. The following
+@code{errno} error conditions are specific to this function:
+
+@table @code
+@item EISDIR
+At least one of the descriptors @var{inputfd} or @var{outputfd} refers
+to a directory.
+
+@item EINVAL
+At least one of the descriptors @var{inputfd} or @var{outputfd} refers
+to a non-regular, non-directory file (such as a socket or a FIFO).
+
+The input or output positions before are after the copy operations are
+outside of an implementation-defined limit.
+
+The @var{flags} argument is not zero.
+
+@item EFBIG
+The new file size would exceed the process file size limit.
+@xref{Limits on Resources}.
+
+The input or output positions before are after the copy operations are
+outside of an implementation-defined limit. This can happen if the file
+was not opened with large file support (LFS) on 32-bit machines, and the
+copy operation would create a file which is larger than what
+@code{off_t} could represent.
+
+@item EBADF
+The argument @var{inputfd} is not a valid file descriptor open for
+reading.
+
+The argument @var{outputfd} is not a valid file descriptor open for
+writing, or @var{outputfd} has been opened with @code{O_APPEND}.
+
+@item EXDEV
+The input and output files reside on different file systems.
+@end table
+
+In addition, @code{copy_file_range} can fail with the error codes
+which are used by @code{read}, @code{pread}, @code{write}, and
+@code{pwrite}.
+
+The @code{copy_file_range} function is a cancellation point. In case of
+cancellation, the input location (the file position or the value at
+@code{*@var{inputpos}}) is indeterminate.
+@end deftypefun
+
@node Memory-mapped I/O
@section Memory-mapped I/O
diff --git a/posix/unistd.h b/posix/unistd.h
index 32b0f4898f..65317c79fd 100644
--- a/posix/unistd.h
+++ b/posix/unistd.h
@@ -1105,7 +1105,12 @@ extern int lockf64 (int __fd, int __cmd, __off64_t __len) __wur;
do __result = (long int) (expression); \
while (__result == -1L && errno == EINTR); \
__result; }))
-#endif
+
+/* Copy LENGTH bytes from INFD to OUTFD. */
+ssize_t copy_file_range (int __infd, __off64_t *__pinoff,
+ int __outfd, __off64_t *__poutoff,
+ size_t __length, unsigned int __flags);
+#endif /* __USE_GNU */
#if defined __USE_POSIX199309 || defined __USE_UNIX98
/* Synchronize at least the data part of a file with the underlying
diff --git a/support/Makefile b/support/Makefile
index bfde79333e..8458840cd8 100644
--- a/support/Makefile
+++ b/support/Makefile
@@ -36,6 +36,7 @@ libsupport-routines = \
oom_error \
resolv_test \
set_fortify_handler \
+ support-xfstat \
support-xstat \
support_become_root \
support_can_chroot \
@@ -73,8 +74,10 @@ libsupport-routines = \
xfclose \
xfopen \
xfork \
+ xftruncate \
xgetsockname \
xlisten \
+ xlseek \
xmalloc \
xmemstream \
xmkdir \
diff --git a/support/support-xfstat.c b/support/support-xfstat.c
new file mode 100644
index 0000000000..4c8ee9142b
--- /dev/null
+++ b/support/support-xfstat.c
@@ -0,0 +1,28 @@
+/* fstat64 with error checking.
+ Copyright (C) 2017 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, see
+ <http://www.gnu.org/licenses/>. */
+
+#include <support/check.h>
+#include <support/xunistd.h>
+#include <sys/stat.h>
+
+void
+xfstat (int fd, struct stat64 *result)
+{
+ if (fstat64 (fd, result) != 0)
+ FAIL_EXIT1 ("fstat64 (%d): %m", fd);
+}
diff --git a/support/xftruncate.c b/support/xftruncate.c
new file mode 100644
index 0000000000..9c4e9e3050
--- /dev/null
+++ b/support/xftruncate.c
@@ -0,0 +1,27 @@
+/* ftruncate with error checking.
+ Copyright (C) 2017 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, see
+ <http://www.gnu.org/licenses/>. */
+
+#include <support/check.h>
+#include <support/xunistd.h>
+
+void
+xftruncate (int fd, long long length)
+{
+ if (ftruncate64 (fd, length) != 0)
+ FAIL_EXIT1 ("ftruncate64 (%d, %lld): %m", fd, length);
+}
diff --git a/support/xlseek.c b/support/xlseek.c
new file mode 100644
index 0000000000..0a75a9f2e6
--- /dev/null
+++ b/support/xlseek.c
@@ -0,0 +1,29 @@
+/* lseek with error checking.
+ Copyright (C) 2017 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, see
+ <http://www.gnu.org/licenses/>. */
+
+#include <support/check.h>
+#include <support/xunistd.h>
+
+long long
+xlseek (int fd, long long offset, int whence)
+{
+ long long result = lseek64 (fd, offset, whence);
+ if (result < 0)
+ FAIL_EXIT1 ("lseek64 (%d, %lld, %d): %m", fd, offset, whence);
+ return result;
+}
diff --git a/support/xunistd.h b/support/xunistd.h
index 00376f7aae..29da063c15 100644
--- a/support/xunistd.h
+++ b/support/xunistd.h
@@ -36,10 +36,13 @@ void xpipe (int[2]);
void xdup2 (int, int);
int xopen (const char *path, int flags, mode_t);
void xstat (const char *path, struct stat64 *);
+void xfstat (int fd, struct stat64 *);
void xmkdir (const char *path, mode_t);
void xchroot (const char *path);
void xunlink (const char *path);
long xsysconf (int name);
+long long xlseek (int fd, long long offset, int whence);
+void xftruncate (int fd, long long length);
/* Read the link at PATH. The caller should free the returned string
with free. */
diff --git a/sysdeps/unix/sysv/linux/aarch64/libc.abilist b/sysdeps/unix/sysv/linux/aarch64/libc.abilist
index ec0ead15dd..90c9bc84e1 100644
--- a/sysdeps/unix/sysv/linux/aarch64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/aarch64/libc.abilist
@@ -2104,6 +2104,7 @@ GLIBC_2.26 pwritev2 F
GLIBC_2.26 pwritev64v2 F
GLIBC_2.26 reallocarray F
GLIBC_2.27 GLIBC_2.27 A
+GLIBC_2.27 copy_file_range F
GLIBC_2.27 glob F
GLIBC_2.27 glob64 F
GLIBC_2.27 memfd_create F
diff --git a/sysdeps/unix/sysv/linux/alpha/libc.abilist b/sysdeps/unix/sysv/linux/alpha/libc.abilist
index 5355769974..fd2d81fb68 100644
--- a/sysdeps/unix/sysv/linux/alpha/libc.abilist
+++ b/sysdeps/unix/sysv/linux/alpha/libc.abilist
@@ -2015,6 +2015,7 @@ GLIBC_2.26 pwritev2 F
GLIBC_2.26 pwritev64v2 F
GLIBC_2.26 reallocarray F
GLIBC_2.27 GLIBC_2.27 A
+GLIBC_2.27 copy_file_range F
GLIBC_2.27 glob F
GLIBC_2.27 glob64 F
GLIBC_2.27 memfd_create F
diff --git a/sysdeps/unix/sysv/linux/arm/libc.abilist b/sysdeps/unix/sysv/linux/arm/libc.abilist
index 9bafe71b51..044ec102c2 100644
--- a/sysdeps/unix/sysv/linux/arm/libc.abilist
+++ b/sysdeps/unix/sysv/linux/arm/libc.abilist
@@ -105,6 +105,7 @@ GLIBC_2.26 pwritev2 F
GLIBC_2.26 pwritev64v2 F
GLIBC_2.26 reallocarray F
GLIBC_2.27 GLIBC_2.27 A
+GLIBC_2.27 copy_file_range F
GLIBC_2.27 glob F
GLIBC_2.27 glob64 F
GLIBC_2.27 memfd_create F
diff --git a/sysdeps/unix/sysv/linux/copy_file_range.c b/sysdeps/unix/sysv/linux/copy_file_range.c
new file mode 100644
index 0000000000..1ec5f9d8d8
--- /dev/null
+++ b/sysdeps/unix/sysv/linux/copy_file_range.c
@@ -0,0 +1,46 @@
+/* Linux implementation of copy_file_range.
+ Copyright (C) 2017 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, see
+ <http://www.gnu.org/licenses/>. */
+
+#include <errno.h>
+#include <sysdep-cancel.h>
+#include <unistd.h>
+
+/* Include the fallback implementation. */
+#ifndef __ASSUME_COPY_FILE_RANGE
+#define COPY_FILE_RANGE_DECL static
+#define COPY_FILE_RANGE copy_file_range_compat
+#include <io/copy_file_range-compat.c>
+#endif
+
+ssize_t
+copy_file_range (int infd, __off64_t *pinoff,
+ int outfd, __off64_t *poutoff,
+ size_t length, unsigned int flags)
+{
+#ifdef __NR_copy_file_range
+ ssize_t ret = SYSCALL_CANCEL (copy_file_range, infd, pinoff, outfd, poutoff,
+ length, flags);
+# ifndef __ASSUME_COPY_FILE_RANGE
+ if (ret == -1 && errno == ENOSYS)
+ ret = copy_file_range_compat (infd, pinoff, outfd, poutoff, length, flags);
+# endif
+ return ret;
+#else /* !__NR_copy_file_range */
+ return copy_file_range_compat (infd, pinoff, outfd, poutoff, length, flags);
+#endif
+}
diff --git a/sysdeps/unix/sysv/linux/hppa/libc.abilist b/sysdeps/unix/sysv/linux/hppa/libc.abilist
index 90aa8d034f..2360130abe 100644
--- a/sysdeps/unix/sysv/linux/hppa/libc.abilist
+++ b/sysdeps/unix/sysv/linux/hppa/libc.abilist
@@ -1869,6 +1869,7 @@ GLIBC_2.26 pwritev2 F
GLIBC_2.26 pwritev64v2 F
GLIBC_2.26 reallocarray F
GLIBC_2.27 GLIBC_2.27 A
+GLIBC_2.27 copy_file_range F
GLIBC_2.27 glob F
GLIBC_2.27 glob64 F
GLIBC_2.27 memfd_create F
diff --git a/sysdeps/unix/sysv/linux/i386/libc.abilist b/sysdeps/unix/sysv/linux/i386/libc.abilist
index 4d44c30c64..39c993fd79 100644
--- a/sysdeps/unix/sysv/linux/i386/libc.abilist
+++ b/sysdeps/unix/sysv/linux/i386/libc.abilist
@@ -2034,6 +2034,7 @@ GLIBC_2.26 strtof128_l F
GLIBC_2.26 wcstof128 F
GLIBC_2.26 wcstof128_l F
GLIBC_2.27 GLIBC_2.27 A
+GLIBC_2.27 copy_file_range F
GLIBC_2.27 glob F
GLIBC_2.27 glob64 F
GLIBC_2.27 memfd_create F
diff --git a/sysdeps/unix/sysv/linux/ia64/libc.abilist b/sysdeps/unix/sysv/linux/ia64/libc.abilist
index 112fc57634..68496aa6ac 100644
--- a/sysdeps/unix/sysv/linux/ia64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/ia64/libc.abilist
@@ -1898,6 +1898,7 @@ GLIBC_2.26 strtof128_l F
GLIBC_2.26 wcstof128 F
GLIBC_2.26 wcstof128_l F
GLIBC_2.27 GLIBC_2.27 A
+GLIBC_2.27 copy_file_range F
GLIBC_2.27 glob F
GLIBC_2.27 glob64 F
GLIBC_2.27 memfd_create F
diff --git a/sysdeps/unix/sysv/linux/kernel-features.h b/sysdeps/unix/sysv/linux/kernel-features.h
index 59b613377f..b9864790c7 100644
--- a/sysdeps/unix/sysv/linux/kernel-features.h
+++ b/sysdeps/unix/sysv/linux/kernel-features.h
@@ -111,3 +111,7 @@
#if __LINUX_KERNEL_VERSION >= 0x040400
# define __ASSUME_MLOCK2 1
#endif
+
+#if __LINUX_KERNEL_VERSION >= 0x040500
+# define __ASSUME_COPY_FILE_RANGE 1
+#endif
diff --git a/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist b/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist
index 2e8b6a4586..b676025261 100644
--- a/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist
+++ b/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist
@@ -106,6 +106,7 @@ GLIBC_2.26 pwritev2 F
GLIBC_2.26 pwritev64v2 F
GLIBC_2.26 reallocarray F
GLIBC_2.27 GLIBC_2.27 A
+GLIBC_2.27 copy_file_range F
GLIBC_2.27 glob F
GLIBC_2.27 glob64 F
GLIBC_2.27 memfd_create F
diff --git a/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist b/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist
index 3c33400f67..cdd1df55d0 100644
--- a/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist
+++ b/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist
@@ -1983,6 +1983,7 @@ GLIBC_2.26 pwritev2 F
GLIBC_2.26 pwritev64v2 F
GLIBC_2.26 reallocarray F
GLIBC_2.27 GLIBC_2.27 A
+GLIBC_2.27 copy_file_range F
GLIBC_2.27 glob F
GLIBC_2.27 glob64 F
GLIBC_2.27 memfd_create F
diff --git a/sysdeps/unix/sysv/linux/microblaze/libc.abilist b/sysdeps/unix/sysv/linux/microblaze/libc.abilist
index e1b1a579d2..e4265fd74d 100644
--- a/sysdeps/unix/sysv/linux/microblaze/libc.abilist
+++ b/sysdeps/unix/sysv/linux/microblaze/libc.abilist
@@ -2104,6 +2104,7 @@ GLIBC_2.26 pwritev2 F
GLIBC_2.26 pwritev64v2 F
GLIBC_2.26 reallocarray F
GLIBC_2.27 GLIBC_2.27 A
+GLIBC_2.27 copy_file_range F
GLIBC_2.27 glob F
GLIBC_2.27 glob64 F
GLIBC_2.27 memfd_create F
diff --git a/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist
index c1550323f3..3a7e0b4c29 100644
--- a/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist
+++ b/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist
@@ -1958,6 +1958,7 @@ GLIBC_2.26 pwritev2 F
GLIBC_2.26 pwritev64v2 F
GLIBC_2.26 reallocarray F
GLIBC_2.27 GLIBC_2.27 A
+GLIBC_2.27 copy_file_range F
GLIBC_2.27 glob F
GLIBC_2.27 glob64 F
GLIBC_2.27 memfd_create F
diff --git a/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist
index 3b3a172e4f..5e805924fa 100644
--- a/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist
+++ b/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist
@@ -1956,6 +1956,7 @@ GLIBC_2.26 pwritev2 F
GLIBC_2.26 pwritev64v2 F
GLIBC_2.26 reallocarray F
GLIBC_2.27 GLIBC_2.27 A
+GLIBC_2.27 copy_file_range F
GLIBC_2.27 glob F
GLIBC_2.27 glob64 F
GLIBC_2.27 memfd_create F
diff --git a/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist
index 101ca7a241..1973fac36d 100644
--- a/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist
+++ b/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist
@@ -1954,6 +1954,7 @@ GLIBC_2.26 pwritev2 F
GLIBC_2.26 pwritev64v2 F
GLIBC_2.26 reallocarray F
GLIBC_2.27 GLIBC_2.27 A
+GLIBC_2.27 copy_file_range F
GLIBC_2.27 glob F
GLIBC_2.27 glob64 F
GLIBC_2.27 memfd_create F
diff --git a/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist
index 2d129f7170..5e18ab83b4 100644
--- a/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist
@@ -1949,6 +1949,7 @@ GLIBC_2.26 pwritev2 F
GLIBC_2.26 pwritev64v2 F
GLIBC_2.26 reallocarray F
GLIBC_2.27 GLIBC_2.27 A
+GLIBC_2.27 copy_file_range F
GLIBC_2.27 glob F
GLIBC_2.27 glob64 F
GLIBC_2.27 memfd_create F
diff --git a/sysdeps/unix/sysv/linux/nios2/libc.abilist b/sysdeps/unix/sysv/linux/nios2/libc.abilist
index 8bc350aff8..cc5885ab9b 100644
--- a/sysdeps/unix/sysv/linux/nios2/libc.abilist
+++ b/sysdeps/unix/sysv/linux/nios2/libc.abilist
@@ -2145,6 +2145,7 @@ GLIBC_2.26 pwritev2 F
GLIBC_2.26 pwritev64v2 F
GLIBC_2.26 reallocarray F
GLIBC_2.27 GLIBC_2.27 A
+GLIBC_2.27 copy_file_range F
GLIBC_2.27 glob F
GLIBC_2.27 glob64 F
GLIBC_2.27 memfd_create F
diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist
index 127c426e1c..676aa50c81 100644
--- a/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist
+++ b/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist
@@ -1987,6 +1987,7 @@ GLIBC_2.26 pwritev2 F
GLIBC_2.26 pwritev64v2 F
GLIBC_2.26 reallocarray F
GLIBC_2.27 GLIBC_2.27 A
+GLIBC_2.27 copy_file_range F
GLIBC_2.27 glob F
GLIBC_2.27 glob64 F
GLIBC_2.27 memfd_create F
diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist
index a9411318e2..2016c7c1e5 100644
--- a/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist
+++ b/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist
@@ -1992,6 +1992,7 @@ GLIBC_2.26 pwritev2 F
GLIBC_2.26 pwritev64v2 F
GLIBC_2.26 reallocarray F
GLIBC_2.27 GLIBC_2.27 A
+GLIBC_2.27 copy_file_range F
GLIBC_2.27 glob F
GLIBC_2.27 glob64 F
GLIBC_2.27 memfd_create F
diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc64/libc-le.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc64/libc-le.abilist
index d7bf5db601..3d19e38dbd 100644
--- a/sysdeps/unix/sysv/linux/powerpc/powerpc64/libc-le.abilist
+++ b/sysdeps/unix/sysv/linux/powerpc/powerpc64/libc-le.abilist
@@ -2199,6 +2199,7 @@ GLIBC_2.26 strtof128_l F
GLIBC_2.26 wcstof128 F
GLIBC_2.26 wcstof128_l F
GLIBC_2.27 GLIBC_2.27 A
+GLIBC_2.27 copy_file_range F
GLIBC_2.27 glob F
GLIBC_2.27 glob64 F
GLIBC_2.27 memfd_create F
diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc64/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc64/libc.abilist
index a3415a72ac..c57ab21b82 100644
--- a/sysdeps/unix/sysv/linux/powerpc/powerpc64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/powerpc/powerpc64/libc.abilist
@@ -106,6 +106,7 @@ GLIBC_2.26 pwritev2 F
GLIBC_2.26 pwritev64v2 F
GLIBC_2.26 reallocarray F
GLIBC_2.27 GLIBC_2.27 A
+GLIBC_2.27 copy_file_range F
GLIBC_2.27 glob F
GLIBC_2.27 glob64 F
GLIBC_2.27 memfd_create F
diff --git a/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist b/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist
index 414338f9a2..25903720e3 100644
--- a/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist
+++ b/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist
@@ -1987,6 +1987,7 @@ GLIBC_2.26 pwritev2 F
GLIBC_2.26 pwritev64v2 F
GLIBC_2.26 reallocarray F
GLIBC_2.27 GLIBC_2.27 A
+GLIBC_2.27 copy_file_range F
GLIBC_2.27 glob F
GLIBC_2.27 glob64 F
GLIBC_2.27 memfd_create F
diff --git a/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist b/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist
index f0f7a69b64..5d6800c236 100644
--- a/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist
@@ -1888,6 +1888,7 @@ GLIBC_2.26 pwritev2 F
GLIBC_2.26 pwritev64v2 F
GLIBC_2.26 reallocarray F
GLIBC_2.27 GLIBC_2.27 A
+GLIBC_2.27 copy_file_range F
GLIBC_2.27 glob F
GLIBC_2.27 glob64 F
GLIBC_2.27 memfd_create F
diff --git a/sysdeps/unix/sysv/linux/sh/libc.abilist b/sysdeps/unix/sysv/linux/sh/libc.abilist
index 9f95aba898..c04872ca7f 100644
--- a/sysdeps/unix/sysv/linux/sh/libc.abilist
+++ b/sysdeps/unix/sysv/linux/sh/libc.abilist
@@ -1873,6 +1873,7 @@ GLIBC_2.26 pwritev2 F
GLIBC_2.26 pwritev64v2 F
GLIBC_2.26 reallocarray F
GLIBC_2.27 GLIBC_2.27 A
+GLIBC_2.27 copy_file_range F
GLIBC_2.27 glob F
GLIBC_2.27 glob64 F
GLIBC_2.27 memfd_create F
diff --git a/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist b/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist
index 83fbdf2d7e..85cbe308d6 100644
--- a/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist
+++ b/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist
@@ -1980,6 +1980,7 @@ GLIBC_2.26 pwritev2 F
GLIBC_2.26 pwritev64v2 F
GLIBC_2.26 reallocarray F
GLIBC_2.27 GLIBC_2.27 A
+GLIBC_2.27 copy_file_range F
GLIBC_2.27 glob F
GLIBC_2.27 glob64 F
GLIBC_2.27 memfd_create F
diff --git a/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist b/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist
index ee84ad10bc..f7a1ab8edb 100644
--- a/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist
@@ -1917,6 +1917,7 @@ GLIBC_2.26 pwritev2 F
GLIBC_2.26 pwritev64v2 F
GLIBC_2.26 reallocarray F
GLIBC_2.27 GLIBC_2.27 A
+GLIBC_2.27 copy_file_range F
GLIBC_2.27 glob F
GLIBC_2.27 glob64 F
GLIBC_2.27 memfd_create F
diff --git a/sysdeps/unix/sysv/linux/tile/tilegx32/libc.abilist b/sysdeps/unix/sysv/linux/tile/tilegx32/libc.abilist
index dcbfbc05ac..ab56ecee44 100644
--- a/sysdeps/unix/sysv/linux/tile/tilegx32/libc.abilist
+++ b/sysdeps/unix/sysv/linux/tile/tilegx32/libc.abilist
@@ -2111,6 +2111,7 @@ GLIBC_2.26 pwritev2 F
GLIBC_2.26 pwritev64v2 F
GLIBC_2.26 reallocarray F
GLIBC_2.27 GLIBC_2.27 A
+GLIBC_2.27 copy_file_range F
GLIBC_2.27 glob F
GLIBC_2.27 glob64 F
GLIBC_2.27 memfd_create F
diff --git a/sysdeps/unix/sysv/linux/tile/tilegx64/libc.abilist b/sysdeps/unix/sysv/linux/tile/tilegx64/libc.abilist
index 53dc99c45a..f2518c08ff 100644
--- a/sysdeps/unix/sysv/linux/tile/tilegx64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/tile/tilegx64/libc.abilist
@@ -2111,6 +2111,7 @@ GLIBC_2.26 pwritev2 F
GLIBC_2.26 pwritev64v2 F
GLIBC_2.26 reallocarray F
GLIBC_2.27 GLIBC_2.27 A
+GLIBC_2.27 copy_file_range F
GLIBC_2.27 glob F
GLIBC_2.27 glob64 F
GLIBC_2.27 memfd_create F
diff --git a/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist b/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist
index ae4dcaa47e..2a3cc40674 100644
--- a/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist
@@ -1875,6 +1875,7 @@ GLIBC_2.26 strtof128_l F
GLIBC_2.26 wcstof128 F
GLIBC_2.26 wcstof128_l F
GLIBC_2.27 GLIBC_2.27 A
+GLIBC_2.27 copy_file_range F
GLIBC_2.27 glob F
GLIBC_2.27 glob64 F
GLIBC_2.27 memfd_create F
diff --git a/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist b/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist
index 0dbda14796..8bc16b9004 100644
--- a/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist
+++ b/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist
@@ -2118,6 +2118,7 @@ GLIBC_2.26 strtof128_l F
GLIBC_2.26 wcstof128 F
GLIBC_2.26 wcstof128_l F
GLIBC_2.27 GLIBC_2.27 A
+GLIBC_2.27 copy_file_range F
GLIBC_2.27 glob F
GLIBC_2.27 glob64 F
GLIBC_2.27 memfd_create F
^ permalink raw reply [flat|nested] 24+ messages in thread
* Re: [PATCH] copy_file_range: New function to copy file data
2017-12-21 13:44 ` Florian Weimer
@ 2017-12-21 17:04 ` Adhemerval Zanella
2017-12-21 18:07 ` Florian Weimer
0 siblings, 1 reply; 24+ messages in thread
From: Adhemerval Zanella @ 2017-12-21 17:04 UTC (permalink / raw)
To: Florian Weimer; +Cc: libc-alpha, Joseph S. Myers
On 21/12/2017 11:44, Florian Weimer wrote:
> On 12/19/2017 12:01 PM, Adhemerval Zanella wrote:
>> It is a default ubuntu 16 installation: /tmp is ext4 (rw,relatime,errors=remount-ro,data=ordered)
>> with kernel 4.4.0-71-generic
>
> I could reproduce this with ext4. The different failure behavior exposed a bug in the compat code: It did not handle short writes correctly, after all. It would return if it encountered a short write. The correct result in that case would be to return the length of the short write. The fix was to change
>
> +Â Â Â Â Â Â Â Â Â Â Â Â Â if (copied > 0)
> +               /* Report the number of bytes copied so far. */
> +Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â return copied + written;
>
> into:
>
> +Â Â Â Â Â Â Â Â Â Â Â Â Â if (copied + written > 0)
> +               /* Report the number of bytes copied so far. */
> +Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â return copied + written;
>
> The test now handles EFBIG in addition to EINVAL. I updated the documentation to mention EFBIG, too. I do not think it is necessary to change the compat implementation to handle O_LARGEFILE.
Right, I think current aproach is good as is.
>
> Thanks,
> Florian
>
> copy_file_range.patch
>
>
> Subject: [PATCH] copy_file_range: New function to copy file data
> To: libc-alpha@sourceware.org
>
> The semantics are based on the Linux system call, but a very close
> emulation in user space is provided.
>
> 2017-12-21 Florian Weimer <fweimer@redhat.com>
>
> * io/Makefile (routines): Add copy_file_range.
> (tests): Add tst-copy_file_range.
> (tests-static, tests-internal): Add tst-copy_file_range-compat.
> * io/Versions (GLIBC_2.27): Export copy_file_range.
> * io/copy_file_range-compat.c: New file.
> * io/copy_file_range.c: Likewise.
> * io/tst-copy_file_range-compat.c: Likewise.
> * io/tst-copy_file_range.c: Likewise.
> * manual/llio.texi (Copying File Data): New section.
> * posix/unistd.h [__USE_GNU] (copy_file_range): Declare.
> * support/Makefile (libsupport-routines): Add support-xfstat,
> xftruncate, xlseek.
> * support/support-xfstat.c: New file.
> * support/xftruncate.c: Likewise.
> * support/xlseek.c: Likewise.
> * support/xunistd.h (xfstat, xftruncate, xlseek): Declare.
> * sysdeps/unix/sysv/linux/**.abilist: Update.
> * sysdeps/unix/sysv/linux/copy_file_range.c: New file.
LGTM with two nits below and a clarification about the requirement of a error check.
Reviewed-by: Adhemerval Zanella <adhemerval.zanella@linaro.org>
>
> diff --git a/io/Makefile b/io/Makefile
> index c72519541f..85eb927d4c 100644
> --- a/io/Makefile
> +++ b/io/Makefile
> @@ -52,7 +52,7 @@ routines := \
> ftw ftw64 fts fts64 poll ppoll \
> posix_fadvise posix_fadvise64 \
> posix_fallocate posix_fallocate64 \
> - sendfile sendfile64 \
> + sendfile sendfile64 copy_file_range \
> utimensat futimens
>
> # These routines will be omitted from the libc shared object.
> @@ -70,7 +70,13 @@ tests := test-utime test-stat test-stat2 test-lfs tst-getcwd \
> tst-symlinkat tst-linkat tst-readlinkat tst-mkdirat \
> tst-mknodat tst-mkfifoat tst-ttyname_r bug-ftw5 \
> tst-posix_fallocate tst-posix_fallocate64 \
> - tst-fts tst-fts-lfs tst-open-tmpfile
> + tst-fts tst-fts-lfs tst-open-tmpfile \
> + tst-copy_file_range \
> +
> +# This test includes the compat implementation of copy_file_range,
> +# which uses internal, unexported libc functions.
> +tests-static += tst-copy_file_range-compat
> +tests-internal += tst-copy_file_range-compat
>
> ifeq ($(run-built-tests),yes)
> tests-special += $(objpfx)ftwtest.out
> diff --git a/io/Versions b/io/Versions
> index 64316cd025..98898cb9d5 100644
> --- a/io/Versions
> +++ b/io/Versions
> @@ -125,4 +125,7 @@ libc {
> GLIBC_2.23 {
> fts64_children; fts64_close; fts64_open; fts64_read; fts64_set;
> }
> + GLIBC_2.27 {
> + copy_file_range;
> + }
> }
> diff --git a/io/copy_file_range-compat.c b/io/copy_file_range-compat.c
> new file mode 100644
> index 0000000000..275be97e57
> --- /dev/null
> +++ b/io/copy_file_range-compat.c
> @@ -0,0 +1,158 @@
> +/* Emulation of copy_file_range.
> + Copyright (C) 2017 Free Software Foundation, Inc.
> + This file is part of the GNU C Library.
> +
> + The GNU C Library is free software; you can redistribute it and/or
> + modify it under the terms of the GNU Lesser General Public
> + License as published by the Free Software Foundation; either
> + version 2.1 of the License, or (at your option) any later version.
> +
> + The GNU C Library is distributed in the hope that it will be useful,
> + but WITHOUT ANY WARRANTY; without even the implied warranty of
> + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
> + Lesser General Public License for more details.
> +
> + You should have received a copy of the GNU Lesser General Public
> + License along with the GNU C Library; if not, see
> + <http://www.gnu.org/licenses/>. */
> +
> +/* The following macros should be defined before including this
> + file:
> +
> + COPY_FILE_RANGE_DECL Declaration specifiers for the function below.
> + COPY_FILE_RANGE Name of the function to define. */
> +
> +#include <errno.h>
> +#include <fcntl.h>
> +#include <inttypes.h>
> +#include <limits.h>
> +#include <sys/stat.h>
> +#include <sys/types.h>
> +#include <unistd.h>
> +
> +COPY_FILE_RANGE_DECL
> +ssize_t
> +COPY_FILE_RANGE (int infd, __off64_t *pinoff,
> + int outfd, __off64_t *poutoff,
> + size_t length, unsigned int flags)
> +{
> + if (flags != 0)
> + {
> + __set_errno (EINVAL);
> + return -1;
> + }
> +
> + struct stat64 instat;
> + struct stat64 outstat;
> + if (fstat64 (infd, &instat) != 0 || fstat64 (outfd, &outstat) != 0)
> + return -1;
> + if (S_ISDIR (instat.st_mode) || S_ISDIR (outstat.st_mode))
> + {
> + __set_errno (EISDIR);
> + return -1;
> + }
To follow the pattern you can put 'instat' and 'outstat' in its own scope.
> + if (!S_ISREG (instat.st_mode) || !S_ISREG (outstat.st_mode))
> + {
> + /* We need a regular input file so that the we can seek
> + backwards in case of a write failure. */
> + __set_errno (EINVAL);
> + return -1;
> + }
> + if (instat.st_dev != outstat.st_dev)
> + {
> + /* Cross-device copies are not supported. */
> + __set_errno (EXDEV);
> + return -1;
> + }
> +
> + /* The output descriptor must not have O_APPEND set. */
> + {
> + int flags = __fcntl (outfd, F_GETFL);
> + if (flags & O_APPEND)
> + {
> + __set_errno (EBADF);
> + return -1;
> + }
> + }
> +
> + /* Avoid an overflow in the result. */
> + if (length > SSIZE_MAX)
> + length = SSIZE_MAX;
> +
> + /* Main copying loop. The buffer size is arbitrary and is a
> + trade-off between stack size consumption, cache usage, and
> + amortization of system call overhead. */
> + size_t copied = 0;
> + char buf[8192];
> + while (length > 0)
> + {
> + size_t to_read = length;
> + if (to_read > sizeof (buf))
> + to_read = sizeof (buf);
> +
> + /* Fill the buffer. */
> + ssize_t read_count;
> + if (pinoff == NULL)
> + read_count = read (infd, buf, to_read);
> + else
> + read_count = __libc_pread64 (infd, buf, to_read, *pinoff);
> + if (read_count == 0)
> + /* End of file reached prematurely. */
> + return copied;
> + if (read_count < 0)
> + {
> + if (copied > 0)
> + /* Report the number of bytes copied so far. */
> + return copied;
> + return -1;
> + }> + if (pinoff != 0)
> + *pinoff += read_count;
pinoff != NULL.
> +
> + /* Write the buffer part which was read to the destination. */
> + char *end = buf + read_count;
> + for (char *p = buf; p < end; )
> + {
> + ssize_t write_count;
> + if (poutoff == NULL)
> + write_count = write (outfd, p, end - p);
> + else
> + write_count = __libc_pwrite64 (outfd, p, end - p, *poutoff);
> + if (write_count < 0)
> + {
> + /* Adjust the input read position to match what we have
> + written, so that the caller can pick up after the
> + error. */
> + size_t written = p - buf;
> + /* NB: This needs to be signed so that we can form the
> + negative value below. */
> + ssize_t overread = read_count - written;
> + if (pinoff == NULL)
> + {
> + if (overread > 0)
> + {
> + /* We are on an error recovery path, so we
> + cannot deal with failure here. */
> + int save_errno = errno;
> + (void) __libc_lseek64 (infd, -overread, SEEK_CUR);
> + __set_errno (save_errno);
Should we really handle errors here? Using current man pages EBADF, ENXIO,
ESPIPE can't really happen because of previous checks. EINVAL and EOVERFLOW
due resulting file offset would be negative or beyond the end of a seekable
device is also unlikely due the fact we are using the results of a previous
partial write to calculate the required offset. I am not sure if it can
really fail here.
> + }
> + }
> + else /* pinoff != NULL */
> + *pinoff -= overread;
> +
> + if (copied + written > 0)
> + /* Report the number of bytes copied so far. */
> + return copied + written;
> + return -1;
> + }
> + p += write_count;
> + if (poutoff != NULL)
> + *poutoff += write_count;
> + } /* Write loop. */
> +
> + copied += read_count;
> + length -= read_count;
> + }
> + return copied;
> +}
> diff --git a/io/copy_file_range.c b/io/copy_file_range.c
> new file mode 100644
> index 0000000000..61ee6871b4
> --- /dev/null
> +++ b/io/copy_file_range.c
> @@ -0,0 +1,22 @@
> +/* Generic implementation of copy_file_range.
> + Copyright (C) 2017 Free Software Foundation, Inc.
> + This file is part of the GNU C Library.
> +
> + The GNU C Library is free software; you can redistribute it and/or
> + modify it under the terms of the GNU Lesser General Public
> + License as published by the Free Software Foundation; either
> + version 2.1 of the License, or (at your option) any later version.
> +
> + The GNU C Library is distributed in the hope that it will be useful,
> + but WITHOUT ANY WARRANTY; without even the implied warranty of
> + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
> + Lesser General Public License for more details.
> +
> + You should have received a copy of the GNU Lesser General Public
> + License along with the GNU C Library; if not, see
> + <http://www.gnu.org/licenses/>. */
> +
> +#define COPY_FILE_RANGE_DECL
> +#define COPY_FILE_RANGE copy_file_range
> +
> +#include <io/copy_file_range-compat.c>
Ok.
> diff --git a/io/tst-copy_file_range-compat.c b/io/tst-copy_file_range-compat.c
> new file mode 100644
> index 0000000000..eb737946d9
> --- /dev/null
> +++ b/io/tst-copy_file_range-compat.c
> @@ -0,0 +1,30 @@
> +/* Test the fallback implementation of copy_file_range.
> + Copyright (C) 2017 Free Software Foundation, Inc.
> + This file is part of the GNU C Library.
> +
> + The GNU C Library is free software; you can redistribute it and/or
> + modify it under the terms of the GNU Lesser General Public
> + License as published by the Free Software Foundation; either
> + version 2.1 of the License, or (at your option) any later version.
> +
> + The GNU C Library is distributed in the hope that it will be useful,
> + but WITHOUT ANY WARRANTY; without even the implied warranty of
> + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
> + Lesser General Public License for more details.
> +
> + You should have received a copy of the GNU Lesser General Public
> + License along with the GNU C Library; if not, see
> + <http://www.gnu.org/licenses/>. */
> +
> +/* Get the declaration of the official copy_of_range function. */
> +#include <unistd.h>
> +
> +/* Compile a local version of copy_file_range. */
> +#define COPY_FILE_RANGE_DECL static
> +#define COPY_FILE_RANGE copy_file_range_compat
> +#include <io/copy_file_range-compat.c>
> +
> +/* Re-use the test, but run it against copy_file_range_compat defined
> + above. */
> +#define copy_file_range copy_file_range_compat
> +#include "tst-copy_file_range.c"
Ok.
> diff --git a/io/tst-copy_file_range.c b/io/tst-copy_file_range.c
> new file mode 100644
> index 0000000000..d8f4e8ac04
> --- /dev/null
> +++ b/io/tst-copy_file_range.c
> @@ -0,0 +1,833 @@
> +/* Tests for copy_file_range.
> + Copyright (C) 2017 Free Software Foundation, Inc.
> + This file is part of the GNU C Library.
> +
> + The GNU C Library is free software; you can redistribute it and/or
> + modify it under the terms of the GNU Lesser General Public
> + License as published by the Free Software Foundation; either
> + version 2.1 of the License, or (at your option) any later version.
> +
> + The GNU C Library is distributed in the hope that it will be useful,
> + but WITHOUT ANY WARRANTY; without even the implied warranty of
> + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
> + Lesser General Public License for more details.
> +
> + You should have received a copy of the GNU Lesser General Public
> + License along with the GNU C Library; if not, see
> + <http://www.gnu.org/licenses/>. */
> +
> +#include <array_length.h>
> +#include <errno.h>
> +#include <fcntl.h>
> +#include <inttypes.h>
> +#include <libgen.h>
> +#include <poll.h>
> +#include <sched.h>
> +#include <stdbool.h>
> +#include <stdio.h>
> +#include <stdlib.h>
> +#include <string.h>
> +#include <support/check.h>
> +#include <support/namespace.h>
> +#include <support/support.h>
> +#include <support/temp_file.h>
> +#include <support/test-driver.h>
> +#include <support/xunistd.h>
> +#include <sys/mount.h>
> +
> +/* Boolean flags which indicate whether to use pointers with explicit
> + output flags. */
> +static int do_inoff;
> +static int do_outoff;
> +
> +/* Name and descriptors of the input files. Files are truncated and
> + reopened (with O_RDWR) between tests. */
> +static char *infile;
> +static int infd;
> +static char *outfile;
> +static int outfd;
> +
> +/* Like the above, but on a different file system. xdevfile can be
> + NULL if no suitable file system has been found. */
> +static char *xdevfile;
> +
> +/* Input and output offsets. Set according to do_inoff and do_outoff
> + before the test. The offsets themselves are always set to
> + zero. */
> +static off64_t inoff;
> +static off64_t *pinoff;
> +static off64_t outoff;
> +static off64_t *poutoff;
> +
> +/* These are a collection of copy sizes used in tests. The selection
> + takes into account that the fallback implementation uses an
> + internal buffer of 8192 bytes. */
> +enum { maximum_size = 99999 };
> +static const int typical_sizes[] =
> + { 0, 1, 2, 3, 1024, 2048, 4096, 8191, 8192, 8193, 16383, 16384, 16385,
> + maximum_size };
> +
> +/* The random contents of this array can be used as a pattern to check
> + for correct write operations. */
> +static unsigned char random_data[maximum_size];
> +
> +/* The size chosen by the test harness. */
> +static int current_size;
> +
> +/* Maximum writable file offset. Updated by find_maximum_offset
> + below. */
> +static off64_t maximum_offset;
> +
> +/* Error code when crossing the offset. */
> +static int maximum_offset_errno;
> +
> +/* If true: Writes which cross the limit will fail. If false: Writes
> + which cross the limit will result in a partial write. */
> +static bool maximum_offset_hard_limit;
> +
> +/* Fills maximum_offset etc. above. Truncates outfd as a side
> + effect. */
> +static void
> +find_maximum_offset (void)
> +{
> + xftruncate (outfd, 0);
> + if (maximum_offset != 0)
> + return;
> +
> + uint64_t upper = -1;
> + upper >>= 1; /* Maximum of off64_t. */
> + TEST_VERIFY ((off64_t) upper > 0);
> + TEST_VERIFY ((off64_t) (upper + 1) < 0);
> + if (lseek64 (outfd, upper, SEEK_SET) >= 0)
> + {
> + if (write (outfd, "", 1) == 1)
> + FAIL_EXIT1 ("created a file larger than the off64_t range");
> + }
> +
> + uint64_t lower = 1024 * 1024; /* A reasonable minimum file size. */
> + /* Loop invariant: writing at lower succeeds, writing at upper fails. */
> + while (lower + 1 < upper)
> + {
> + uint64_t middle = (lower + upper) / 2;
> + if (test_verbose > 0)
> + printf ("info: %s: remaining test range %" PRIu64 " .. %" PRIu64
> + ", probe at %" PRIu64 "\n", __func__, lower, upper, middle);
> + xftruncate (outfd, 0);
> + if (lseek64 (outfd, middle, SEEK_SET) >= 0
> + && write (outfd, "", 1) == 1)
> + lower = middle;
> + else
> + upper = middle;
> + }
> + TEST_VERIFY (lower + 1 == upper);
> + maximum_offset = lower;
> + printf ("info: maximum writable file offset: %" PRIu64 " (%" PRIx64 ")\n",
> + lower, lower);
> +
> + /* Check that writing at the valid offset actually works. */
> + xftruncate (outfd, 0);
> + xlseek (outfd, lower, SEEK_SET);
> + TEST_COMPARE (write (outfd, "", 1), 1);
> +
> + /* Cross the boundary with a two-byte write. This can either result
> + in a short write, or a failure. */
> + xlseek (outfd, lower, SEEK_SET);
> + ssize_t ret = write (outfd, " ", 2);
> + if (ret < 0)
> + {
> + maximum_offset_errno = errno;
> + maximum_offset_hard_limit = true;
> + }
> + else
> + maximum_offset_hard_limit = false;
> +
> + /* Check that writing at the next offset actually fails. This also
> + obtains the expected errno value. */
> + xftruncate (outfd, 0);
> + const char *action;
> + if (lseek64 (outfd, lower + 1, SEEK_SET) != 0)
> + {
> + if (write (outfd, "", 1) != -1)
> + FAIL_EXIT1 ("write to impossible offset %" PRIu64 " succeeded",
> + lower + 1);
> + action = "writing";
> + int errno_copy = errno;
> + if (maximum_offset_hard_limit)
> + TEST_COMPARE (errno_copy, maximum_offset_errno);
> + else
> + maximum_offset_errno = errno_copy;
> + }
> + else
> + {
> + action = "seeking";
> + maximum_offset_errno = errno;
> + }
> + printf ("info: %s out of range fails with %m (%d)\n",
> + action, maximum_offset_errno);
> +
> + xftruncate (outfd, 0);
> + xlseek (outfd, 0, SEEK_SET);
> +}
> +
> +/* Perform a copy of a file. */
> +static void
> +simple_file_copy (void)
> +{
> + xwrite (infd, random_data, current_size);
> +
> + int length;
> + int in_skipped; /* Expected skipped bytes in input. */
> + if (do_inoff)
> + {
> + xlseek (infd, 1, SEEK_SET);
> + inoff = 2;
> + length = current_size - 3;
> + in_skipped = 2;
> + }
> + else
> + {
> + xlseek (infd, 3, SEEK_SET);
> + length = current_size - 5;
> + in_skipped = 3;
> + }
> + int out_skipped; /* Expected skipped bytes before the written data. */
> + if (do_outoff)
> + {
> + xlseek (outfd, 4, SEEK_SET);
> + outoff = 5;
> + out_skipped = 5;
> + }
> + else
> + {
> + xlseek (outfd, 6, SEEK_SET);
> + length = current_size - 6;
> + out_skipped = 6;
> + }
> + if (length < 0)
> + length = 0;
> +
> + TEST_COMPARE (copy_file_range (infd, pinoff, outfd, poutoff,
> + length, 0), length);
> + if (do_inoff)
> + {
> + TEST_COMPARE (inoff, 2 + length);
> + TEST_COMPARE (xlseek (infd, 0, SEEK_CUR), 1);
> + }
> + else
> + TEST_COMPARE (xlseek (infd, 0, SEEK_CUR), 3 + length);
> + if (do_outoff)
> + {
> + TEST_COMPARE (outoff, 5 + length);
> + TEST_COMPARE (xlseek (outfd, 0, SEEK_CUR), 4);
> + }
> + else
> + TEST_COMPARE (xlseek (outfd, 0, SEEK_CUR), 6 + length);
> +
> + struct stat64 st;
> + xfstat (outfd, &st);
> + if (length > 0)
> + TEST_COMPARE (st.st_size, out_skipped + length);
> + else
> + {
> + /* If we did not write anything, we also did not add any
> + padding. */
> + TEST_COMPARE (st.st_size, 0);
> + return;
> + }
> +
> + xlseek (outfd, 0, SEEK_SET);
> + char *bytes = xmalloc (st.st_size);
> + TEST_COMPARE (read (outfd, bytes, st.st_size), st.st_size);
> + for (int i = 0; i < out_skipped; ++i)
> + TEST_COMPARE (bytes[i], 0);
> + TEST_VERIFY (memcmp (bytes + out_skipped, random_data + in_skipped,
> + length) == 0);
> + free (bytes);
> +}
> +
> +/* Test that reading from a pipe willfails. */
> +static void
> +pipe_as_source (void)
> +{
> + int pipefds[2];
> + xpipe (pipefds);
> +
> + for (int length = 0; length < 2; ++length)
> + {
> + if (test_verbose > 0)
> + printf ("info: %s: length=%d\n", __func__, length);
> +
> + /* Make sure that there is something to copy in the pipe. */
> + xwrite (pipefds[1], "@", 1);
> +
> + TEST_COMPARE (copy_file_range (pipefds[0], pinoff, outfd, poutoff,
> + length, 0), -1);
> + /* Linux 4.10 and later return EINVAL. Older kernels return
> + EXDEV. */
> + TEST_VERIFY (errno == EINVAL || errno == EXDEV);
> + TEST_COMPARE (inoff, 0);
> + TEST_COMPARE (outoff, 0);
> + TEST_COMPARE (xlseek (outfd, 0, SEEK_CUR), 0);
> +
> + /* Make sure that nothing was read. */
> + char buf = 'A';
> + TEST_COMPARE (read (pipefds[0], &buf, 1), 1);
> + TEST_COMPARE (buf, '@');
> + }
> +
> + xclose (pipefds[0]);
> + xclose (pipefds[1]);
> +}
> +
> +/* Test that writing to a pipe fails. */
> +static void
> +pipe_as_destination (void)
> +{
> + /* Make sure that there is something to read in the input file. */
> + xwrite (infd, "abc", 3);
> + xlseek (infd, 0, SEEK_SET);
> +
> + int pipefds[2];
> + xpipe (pipefds);
> +
> + for (int length = 0; length < 2; ++length)
> + {
> + if (test_verbose > 0)
> + printf ("info: %s: length=%d\n", __func__, length);
> +
> + TEST_COMPARE (copy_file_range (infd, pinoff, pipefds[1], poutoff,
> + length, 0), -1);
> + /* Linux 4.10 and later return EINVAL. Older kernels return
> + EXDEV. */
> + TEST_VERIFY (errno == EINVAL || errno == EXDEV);
> + TEST_COMPARE (inoff, 0);
> + TEST_COMPARE (outoff, 0);
> + TEST_COMPARE (xlseek (infd, 0, SEEK_CUR), 0);
> +
> + /* Make sure that nothing was written. */
> + struct pollfd pollfd = { .fd = pipefds[0], .events = POLLIN, };
> + TEST_COMPARE (poll (&pollfd, 1, 0), 0);
> + }
> +
> + xclose (pipefds[0]);
> + xclose (pipefds[1]);
> +}
> +
> +/* Test a write failure after (potentially) writing some bytes.
> + Failure occurs near the start of the buffer. */
> +static void
> +delayed_write_failure_beginning (void)
> +{
> + /* We need to write something to provoke the error. */
> + if (current_size == 0)
> + return;
> + xwrite (infd, random_data, sizeof (random_data));
> + xlseek (infd, 0, SEEK_SET);
> +
> + /* Write failure near the start. The actual error code varies among
> + file systems. */
> + find_maximum_offset ();
> + off64_t where = maximum_offset;
> +
> + if (current_size == 1)
> + ++where;
> + outoff = where;
> + if (do_outoff)
> + xlseek (outfd, 1, SEEK_SET);
> + else
> + xlseek (outfd, where, SEEK_SET);
> + if (maximum_offset_hard_limit || where > maximum_offset)
> + {
> + TEST_COMPARE (copy_file_range (infd, pinoff, outfd, poutoff,
> + sizeof (random_data), 0), -1);
> + TEST_COMPARE (errno, maximum_offset_errno);
> + TEST_COMPARE (xlseek (infd, 0, SEEK_CUR), 0);
> + TEST_COMPARE (inoff, 0);
> + if (do_outoff)
> + TEST_COMPARE (xlseek (outfd, 0, SEEK_CUR), 1);
> + else
> + TEST_COMPARE (xlseek (outfd, 0, SEEK_CUR), where);
> + TEST_COMPARE (outoff, where);
> + struct stat64 st;
> + xfstat (outfd, &st);
> + TEST_COMPARE (st.st_size, 0);
> + }
> + else
> + {
> + /* The offset is not a hard limit. This means we write one
> + byte. */
> + TEST_COMPARE (copy_file_range (infd, pinoff, outfd, poutoff,
> + sizeof (random_data), 0), 1);
> + if (do_inoff)
> + {
> + TEST_COMPARE (inoff, 1);
> + TEST_COMPARE (xlseek (infd, 0, SEEK_CUR), 0);
> + }
> + else
> + {
> + TEST_COMPARE (xlseek (infd, 0, SEEK_CUR), 1);
> + TEST_COMPARE (inoff, 0);
> + }
> + if (do_outoff)
> + {
> + TEST_COMPARE (xlseek (outfd, 0, SEEK_CUR), 1);
> + TEST_COMPARE (outoff, where + 1);
> + }
> + else
> + {
> + TEST_COMPARE (xlseek (outfd, 0, SEEK_CUR), where + 1);
> + TEST_COMPARE (outoff, where);
> + }
> + struct stat64 st;
> + xfstat (outfd, &st);
> + TEST_COMPARE (st.st_size, where + 1);
> + }
> +}
> +
> +/* Test a write failure after (potentially) writing some bytes.
> + Failure occurs near the end of the buffer. */
> +static void
> +delayed_write_failure_end (void)
> +{
> + if (current_size <= 1)
> + /* This would be same as the first test because there is not
> + enough data to write to make a difference. */
> + return;
> + xwrite (infd, random_data, sizeof (random_data));
> + xlseek (infd, 0, SEEK_SET);
> +
> + find_maximum_offset ();
> + off64_t where = maximum_offset - current_size + 1;
> + if (current_size == sizeof (random_data))
> + /* Otherwise we do not reach the non-writable byte. */
> + ++where;
> + outoff = where;
> + if (do_outoff)
> + xlseek (outfd, 1, SEEK_SET);
> + else
> + xlseek (outfd, where, SEEK_SET);
> + ssize_t ret = copy_file_range (infd, pinoff, outfd, poutoff,
> + sizeof (random_data), 0);
> + if (ret < 0)
> + {
> + TEST_COMPARE (ret, -1);
> + TEST_COMPARE (errno, maximum_offset_errno);
> + struct stat64 st;
> + xfstat (outfd, &st);
> + TEST_COMPARE (st.st_size, 0);
> + }
> + else
> + {
> + /* The first copy succeeded. This happens in the emulation
> + because the internal buffer of limited size does not
> + necessarily cross the off64_t boundary on the first write
> + operation. */
> + if (test_verbose > 0)
> + printf ("info: copy_file_range (%zu) returned %zd\n",
> + sizeof (random_data), ret);
> + TEST_VERIFY (ret > 0);
> + TEST_VERIFY (ret < maximum_size);
> + struct stat64 st;
> + xfstat (outfd, &st);
> + TEST_COMPARE (st.st_size, where + ret);
> + if (do_inoff)
> + {
> + TEST_COMPARE (inoff, ret);
> + TEST_COMPARE (xlseek (infd, 0, SEEK_CUR), 0);
> + }
> + else
> + TEST_COMPARE (xlseek (infd, 0, SEEK_CUR), ret);
> +
> + char *buffer = xmalloc (ret);
> + TEST_COMPARE (pread64 (outfd, buffer, ret, where), ret);
> + TEST_VERIFY (memcmp (buffer, random_data, ret) == 0);
> + free (buffer);
> +
> + /* The second copy fails. */
> + TEST_COMPARE (copy_file_range (infd, pinoff, outfd, poutoff,
> + sizeof (random_data), 0), -1);
> + TEST_COMPARE (errno, maximum_offset_errno);
> + }
> +}
> +
> +/* Test a write failure across devices. */
> +static void
> +cross_device_failure (void)
> +{
> + if (xdevfile == NULL)
> + /* Subtest not supported due to missing cross-device file. */
> + return;
> +
> + /* We need something to write. */
> + xwrite (infd, random_data, sizeof (random_data));
> + xlseek (infd, 0, SEEK_SET);
> +
> + int xdevfd = xopen (xdevfile, O_RDWR | O_LARGEFILE, 0);
> + TEST_COMPARE (copy_file_range (infd, pinoff, xdevfd, poutoff,
> + current_size, 0), -1);
> + TEST_COMPARE (errno, EXDEV);
> + TEST_COMPARE (xlseek (infd, 0, SEEK_CUR), 0);
> + struct stat64 st;
> + xfstat (xdevfd, &st);
> + TEST_COMPARE (st.st_size, 0);
> +
> + xclose (xdevfd);
> +}
> +
> +/* Try to exercise ENOSPC behavior with a tempfs file system (so that
> + we do not have to fill up a regular file system to get the error).
> + This function runs in a subprocess, so that we do not change the
> + mount namespace of the actual test process. */
> +static void
> +enospc_failure_1 (void *closure)
> +{
> +#ifdef CLONE_NEWNS
> + support_become_root ();
> +
> + /* Make sure that we do not alter the file system mounts of the
> + parents. */
> + if (! support_enter_mount_namespace ())
> + {
> + printf ("warning: ENOSPC test skipped\n");
> + return;
> + }
> +
> + char *mountpoint = closure;
> + if (mount ("none", mountpoint, "tmpfs", MS_NODEV | MS_NOEXEC,
> + "size=500k") != 0)
> + {
> + printf ("warning: could not mount tmpfs at %s: %m\n", mountpoint);
> + return;
> + }
> +
> + /* The source file must reside on the same file system. */
> + char *intmpfsfile = xasprintf ("%s/%s", mountpoint, "in");
> + int intmpfsfd = xopen (intmpfsfile, O_RDWR | O_CREAT | O_LARGEFILE, 0600);
> + xwrite (intmpfsfd, random_data, sizeof (random_data));
> + xlseek (intmpfsfd, 1, SEEK_SET);
> + inoff = 1;
> +
> + char *outtmpfsfile = xasprintf ("%s/%s", mountpoint, "out");
> + int outtmpfsfd = xopen (outtmpfsfile, O_RDWR | O_CREAT | O_LARGEFILE, 0600);
> +
> + /* Fill the file with data until ENOSPC is reached. */
> + while (true)
> + {
> + ssize_t ret = write (outtmpfsfd, random_data, sizeof (random_data));
> + if (ret < 0 && errno != ENOSPC)
> + FAIL_EXIT1 ("write to %s: %m", outtmpfsfile);
> + if (ret < sizeof (random_data))
> + break;
> + }
> + TEST_COMPARE (write (outtmpfsfd, "", 1), -1);
> + TEST_COMPARE (errno, ENOSPC);
> + off64_t maxsize = xlseek (outtmpfsfd, 0, SEEK_CUR);
> + TEST_VERIFY_EXIT (maxsize > sizeof (random_data));
> +
> + /* Constructed the expected file contents. */
> + char *expected = xmalloc (maxsize);
> + TEST_COMPARE (pread64 (outtmpfsfd, expected, maxsize, 0), maxsize);
> + /* Go back a little, so some bytes can be written. */
> + enum { offset = 20000 };
> + TEST_VERIFY_EXIT (offset < maxsize);
> + TEST_VERIFY_EXIT (offset < sizeof (random_data));
> + memcpy (expected + maxsize - offset, random_data + 1, offset);
> +
> + if (do_outoff)
> + {
> + outoff = maxsize - offset;
> + xlseek (outtmpfsfd, 2, SEEK_SET);
> + }
> + else
> + xlseek (outtmpfsfd, -offset, SEEK_CUR);
> +
> + /* First call is expected to succeed because we made room for some
> + bytes. */
> + TEST_COMPARE (copy_file_range (intmpfsfd, pinoff, outtmpfsfd, poutoff,
> + maximum_size, 0), offset);
> + if (do_inoff)
> + {
> + TEST_COMPARE (inoff, 1 + offset);
> + TEST_COMPARE (xlseek (intmpfsfd, 0, SEEK_CUR), 1);
> + }
> + else
> + TEST_COMPARE (xlseek (intmpfsfd, 0, SEEK_CUR), 1 + offset);
> + if (do_outoff)
> + {
> + TEST_COMPARE (outoff, maxsize);
> + TEST_COMPARE (xlseek (outtmpfsfd, 0, SEEK_CUR), 2);
> + }
> + else
> + TEST_COMPARE (xlseek (outtmpfsfd, 0, SEEK_CUR), maxsize);
> + struct stat64 st;
> + xfstat (outtmpfsfd, &st);
> + TEST_COMPARE (st.st_size, maxsize);
> + char *actual = xmalloc (st.st_size);
> + TEST_COMPARE (pread64 (outtmpfsfd, actual, st.st_size, 0), st.st_size);
> + TEST_VERIFY (memcmp (expected, actual, maxsize) == 0);
> +
> + /* Second call should fail with ENOSPC. */
> + TEST_COMPARE (copy_file_range (intmpfsfd, pinoff, outtmpfsfd, poutoff,
> + maximum_size, 0), -1);
> + TEST_COMPARE (errno, ENOSPC);
> +
> + /* Offsets should be unchanged. */
> + if (do_inoff)
> + {
> + TEST_COMPARE (inoff, 1 + offset);
> + TEST_COMPARE (xlseek (intmpfsfd, 0, SEEK_CUR), 1);
> + }
> + else
> + TEST_COMPARE (xlseek (intmpfsfd, 0, SEEK_CUR), 1 + offset);
> + if (do_outoff)
> + {
> + TEST_COMPARE (outoff, maxsize);
> + TEST_COMPARE (xlseek (outtmpfsfd, 0, SEEK_CUR), 2);
> + }
> + else
> + TEST_COMPARE (xlseek (outtmpfsfd, 0, SEEK_CUR), maxsize);
> + TEST_COMPARE (xlseek (outtmpfsfd, 0, SEEK_END), maxsize);
> + TEST_COMPARE (pread64 (outtmpfsfd, actual, maxsize, 0), maxsize);
> + TEST_VERIFY (memcmp (expected, actual, maxsize) == 0);
> +
> + free (actual);
> + free (expected);
> +
> + xclose (intmpfsfd);
> + xclose (outtmpfsfd);
> + free (intmpfsfile);
> + free (outtmpfsfile);
> +
> +#else /* !CLONE_NEWNS */
> + puts ("warning: ENOSPC test skipped (no mount namespaces)");
> +#endif
> +}
> +
> +/* Call enospc_failure_1 in a subprocess. */
> +static void
> +enospc_failure (void)
> +{
> + char *mountpoint
> + = support_create_temp_directory ("tst-copy_file_range-enospc-");
> + support_isolate_in_subprocess (enospc_failure_1, mountpoint);
> + free (mountpoint);
> +}
> +
> +/* The target file descriptor must have O_APPEND enabled. */
> +static void
> +oappend_failure (void)
> +{
> + /* Add data, to make sure we do not fail because there is
> + insufficient input data. */
> + xwrite (infd, random_data, current_size);
> + xlseek (infd, 0, SEEK_SET);
> +
> + xclose (outfd);
> + outfd = xopen (outfile, O_RDWR | O_APPEND, 0);
> + TEST_COMPARE (copy_file_range (infd, pinoff, outfd, poutoff,
> + current_size, 0), -1);
> + TEST_COMPARE (errno, EBADF);
> +}
> +
> +/* Test that a short input file results in a shortened copy. */
> +static void
> +short_copy (void)
> +{
> + if (current_size == 0)
> + /* Nothing to shorten. */
> + return;
> +
> + /* Two subtests, one with offset 0 and current_size - 1 bytes, and
> + another one with current_size bytes, but offset 1. */
> + for (int shift = 0; shift < 2; ++shift)
> + {
> + if (test_verbose > 0)
> + printf ("info: shift=%d\n", shift);
> + xftruncate (infd, 0);
> + xlseek (infd, 0, SEEK_SET);
> + xwrite (infd, random_data, current_size - !shift);
> +
> + if (do_inoff)
> + {
> + inoff = shift;
> + xlseek (infd, 2, SEEK_SET);
> + }
> + else
> + {
> + inoff = 3;
> + xlseek (infd, shift, SEEK_SET);
> + }
> + ftruncate (outfd, 0);
> + xlseek (outfd, 0, SEEK_SET);
> + outoff = 0;
> +
> + /* First call copies current_size - 1 bytes. */
> + TEST_COMPARE (copy_file_range (infd, pinoff, outfd, poutoff,
> + current_size, 0), current_size - 1);
> + char *buffer = xmalloc (current_size);
> + TEST_COMPARE (pread64 (outfd, buffer, current_size, 0),
> + current_size - 1);
> + TEST_VERIFY (memcmp (buffer, random_data + shift, current_size - 1)
> + == 0);
> + free (buffer);
> +
> + if (do_inoff)
> + {
> + TEST_COMPARE (inoff, current_size - 1 + shift);
> + TEST_COMPARE (xlseek (infd, 0, SEEK_CUR), 2);
> + }
> + else
> + TEST_COMPARE (xlseek (infd, 0, SEEK_CUR), current_size - 1 + shift);
> + if (do_outoff)
> + {
> + TEST_COMPARE (outoff, current_size - 1);
> + TEST_COMPARE (xlseek (outfd, 0, SEEK_CUR), 0);
> + }
> + else
> + TEST_COMPARE (xlseek (outfd, 0, SEEK_CUR), current_size - 1);
> +
> + /* First call copies zero bytes. */
> + TEST_COMPARE (copy_file_range (infd, pinoff, outfd, poutoff,
> + current_size, 0), 0);
> + /* And the offsets are unchanged. */
> + if (do_inoff)
> + {
> + TEST_COMPARE (inoff, current_size - 1 + shift);
> + TEST_COMPARE (xlseek (infd, 0, SEEK_CUR), 2);
> + }
> + else
> + TEST_COMPARE (xlseek (infd, 0, SEEK_CUR), current_size - 1 + shift);
> + if (do_outoff)
> + {
> + TEST_COMPARE (outoff, current_size - 1);
> + TEST_COMPARE (xlseek (outfd, 0, SEEK_CUR), 0);
> + }
> + else
> + TEST_COMPARE (xlseek (outfd, 0, SEEK_CUR), current_size - 1);
> + }
> +}
> +
> +/* A named test function. */
> +struct test_case
> +{
> + const char *name;
> + void (*func) (void);
> + bool sizes; /* If true, call the test with different current_size values. */
> +};
> +
> +/* The available test cases. */
> +static struct test_case tests[] =
> + {
> + { "simple_file_copy", simple_file_copy, .sizes = true },
> + { "pipe_as_source", pipe_as_source, },
> + { "pipe_as_destination", pipe_as_destination, },
> + { "delayed_write_failure_beginning", delayed_write_failure_beginning,
> + .sizes = true },
> + { "delayed_write_failure_end", delayed_write_failure_end, .sizes = true },
> + { "cross_device_failure", cross_device_failure, .sizes = true },
> + { "enospc_failure", enospc_failure, },
> + { "oappend_failure", oappend_failure, .sizes = true },
> + { "short_copy", short_copy, .sizes = true },
> + };
> +
> +static int
> +do_test (void)
> +{
> + for (unsigned char *p = random_data; p < array_end (random_data); ++p)
> + *p = rand () >> 24;
> +
> + infd = create_temp_file ("tst-copy_file_range-in-", &infile);
> + xclose (create_temp_file ("tst-copy_file_range-out-", &outfile));
> +
> + /* Try to find a different directory from the default input/output
> + file. */
> + {
> + struct stat64 instat;
> + xfstat (infd, &instat);
> + static const char *const candidates[] =
> + { NULL, "/var/tmp", "/dev/shm" };
> + for (const char *const *c = candidates; c < array_end (candidates); ++c)
> + {
> + const char *path = *c;
> + char *to_free = NULL;
> + if (path == NULL)
> + {
> + to_free = xreadlink ("/proc/self/exe");
> + path = dirname (to_free);
> + }
> +
> + struct stat64 cstat;
> + xstat (path, &cstat);
> + if (cstat.st_dev == instat.st_dev)
> + {
> + free (to_free);
> + continue;
> + }
> +
> + printf ("info: using alternate temporary files directory: %s\n", path);
> + xdevfile = xasprintf ("%s/tst-copy_file_range-xdev-XXXXXX", path);
> + free (to_free);
> + break;
> + }
> + if (xdevfile != NULL)
> + {
> + int xdevfd = mkstemp (xdevfile);
> + if (xdevfd < 0)
> + FAIL_EXIT1 ("mkstemp (\"%s\"): %m", xdevfile);
> + struct stat64 xdevst;
> + xfstat (xdevfd, &xdevst);
> + TEST_VERIFY (xdevst.st_dev != instat.st_dev);
> + add_temp_file (xdevfile);
> + xclose (xdevfd);
> + }
> + else
> + puts ("warning: no alternate directory on different file system found");
> + }
> + xclose (infd);
> +
> + for (do_inoff = 0; do_inoff < 2; ++do_inoff)
> + for (do_outoff = 0; do_outoff < 2; ++do_outoff)
> + for (struct test_case *test = tests; test < array_end (tests); ++test)
> + for (const int *size = typical_sizes;
> + size < array_end (typical_sizes); ++size)
> + {
> + current_size = *size;
> + if (test_verbose > 0)
> + printf ("info: %s do_inoff=%d do_outoff=%d current_size=%d\n",
> + test->name, do_inoff, do_outoff, current_size);
> +
> + inoff = 0;
> + if (do_inoff)
> + pinoff = &inoff;
> + else
> + pinoff = NULL;
> + outoff = 0;
> + if (do_outoff)
> + poutoff = &outoff;
> + else
> + poutoff = NULL;
> +
> + infd = xopen (infile, O_RDWR | O_LARGEFILE, 0);
> + xftruncate (infd, 0);
> + outfd = xopen (outfile, O_RDWR | O_LARGEFILE, 0);
> + xftruncate (outfd, 0);
> +
> + test->func ();
> +
> + xclose (infd);
> + xclose (outfd);
> +
> + if (!test->sizes)
> + /* Skip the other sizes unless they have been
> + requested. */
> + break;
> + }
> +
> + free (infile);
> + free (outfile);
> + free (xdevfile);
> +
> + return 0;
> +}
> +
> +#include <support/test-driver.c>
Ok.
> diff --git a/manual/llio.texi b/manual/llio.texi
> index 8b2f599c79..642e56e710 100644
> --- a/manual/llio.texi
> +++ b/manual/llio.texi
> @@ -41,6 +41,7 @@ directly.)
> * Stream/Descriptor Precautions:: Precautions needed if you use both
> descriptors and streams.
> * Scatter-Gather:: Fast I/O to discontinuous buffers.
> +* Copying File Data:: Copying data between files.
> * Memory-mapped I/O:: Using files like memory.
> * Waiting for I/O:: How to check for input or output
> on multiple file descriptors.
> @@ -1353,6 +1354,93 @@ When the source file is compiled using @code{_FILE_OFFSET_BITS == 64} on a
> @code{pwritev2} and so transparently replaces the 32 bit interface.
> @end deftypefun
>
> +@node Copying File Data
> +@section Copying data between two files
> +@cindex copying files
> +@cindex file copy
> +
> +A special function is provided to copy data between two files on the
> +same file system. The system can optimize such copy operations. This
> +is particularly important on network file systems, where the data would
> +otherwise have to be transferred twice over the network.
> +
> +Note that this function only copies file data, but not metadata such as
> +file permissions or extended attributes.
> +
> +@deftypefun ssize_t copy_file_range (int @var{inputfd}, off64_t *@var{inputpos}, int @var{outputfd}, off64_t *@var{outputpos}, ssize_t @var{length}, unsigned int @var{flags})
> +@standards{GNU, unistd.h}
> +@safety{@prelim{}@mtsafe{}@assafe{}@acsafe{}}
> +
> +This function copies up to @var{length} bytes from the file descriptor
> +@var{inputfd} to the file descriptor @var{outputfd}.
> +
> +The function can operate on both the current file position (like
> +@code{read} and @code{write}) and an explicit offset (like @code{pread}
> +and @code{pwrite}). If the @var{inputpos} pointer is null, the file
> +position of @var{inputfd} is used as the starting point of the copy
> +operation, and the file position is advanced during it. If
> +@var{inputpos} is not null, then @code{*@var{inputpos}} is used as the
> +starting point of the copy operation, and @code{*@var{inputpos}} is
> +incremented by the number of copied bytes, but the file position remains
> +unchanged. Similar rules apply to @var{outputfd} and @var{outputpos}
> +for the output file position.
> +
> +The @var{flags} argument is currently reserved and must be zero.
> +
> +The @code{copy_file_range} function returns the number of bytes copied.
> +This can be less than the specified @var{length} in case the input file
> +contains fewer remaining bytes than @var{length}, or if a read or write
> +failure occurs. The return value is zero if the end of the input file
> +is encountered immediately.
> +
> +If no bytes can be copied, to report an error, @code{copy_file_range}
> +returns the value @math{-1} and sets @code{errno}. The following
> +@code{errno} error conditions are specific to this function:
> +
> +@table @code
> +@item EISDIR
> +At least one of the descriptors @var{inputfd} or @var{outputfd} refers
> +to a directory.
> +
> +@item EINVAL
> +At least one of the descriptors @var{inputfd} or @var{outputfd} refers
> +to a non-regular, non-directory file (such as a socket or a FIFO).
> +
> +The input or output positions before are after the copy operations are
> +outside of an implementation-defined limit.
> +
> +The @var{flags} argument is not zero.
> +
> +@item EFBIG
> +The new file size would exceed the process file size limit.
> +@xref{Limits on Resources}.
> +
> +The input or output positions before are after the copy operations are
> +outside of an implementation-defined limit. This can happen if the file
> +was not opened with large file support (LFS) on 32-bit machines, and the
> +copy operation would create a file which is larger than what
> +@code{off_t} could represent.
> +
> +@item EBADF
> +The argument @var{inputfd} is not a valid file descriptor open for
> +reading.
> +
> +The argument @var{outputfd} is not a valid file descriptor open for
> +writing, or @var{outputfd} has been opened with @code{O_APPEND}.
> +
> +@item EXDEV
> +The input and output files reside on different file systems.
> +@end table
> +
> +In addition, @code{copy_file_range} can fail with the error codes
> +which are used by @code{read}, @code{pread}, @code{write}, and
> +@code{pwrite}.
> +
> +The @code{copy_file_range} function is a cancellation point. In case of
> +cancellation, the input location (the file position or the value at
> +@code{*@var{inputpos}}) is indeterminate.
> +@end deftypefun
> +
> @node Memory-mapped I/O
> @section Memory-mapped I/O
>
> diff --git a/posix/unistd.h b/posix/unistd.h
> index 32b0f4898f..65317c79fd 100644
> --- a/posix/unistd.h
> +++ b/posix/unistd.h
> @@ -1105,7 +1105,12 @@ extern int lockf64 (int __fd, int __cmd, __off64_t __len) __wur;
> do __result = (long int) (expression); \
> while (__result == -1L && errno == EINTR); \
> __result; }))
> -#endif
> +
> +/* Copy LENGTH bytes from INFD to OUTFD. */
> +ssize_t copy_file_range (int __infd, __off64_t *__pinoff,
> + int __outfd, __off64_t *__poutoff,
> + size_t __length, unsigned int __flags);
> +#endif /* __USE_GNU */
>
> #if defined __USE_POSIX199309 || defined __USE_UNIX98
> /* Synchronize at least the data part of a file with the underlying
> diff --git a/support/Makefile b/support/Makefile
> index bfde79333e..8458840cd8 100644
> --- a/support/Makefile
> +++ b/support/Makefile
> @@ -36,6 +36,7 @@ libsupport-routines = \
> oom_error \
> resolv_test \
> set_fortify_handler \
> + support-xfstat \
> support-xstat \
> support_become_root \
> support_can_chroot \
> @@ -73,8 +74,10 @@ libsupport-routines = \
> xfclose \
> xfopen \
> xfork \
> + xftruncate \
> xgetsockname \
> xlisten \
> + xlseek \
> xmalloc \
> xmemstream \
> xmkdir \
> diff --git a/support/support-xfstat.c b/support/support-xfstat.c
> new file mode 100644
> index 0000000000..4c8ee9142b
> --- /dev/null
> +++ b/support/support-xfstat.c
> @@ -0,0 +1,28 @@
> +/* fstat64 with error checking.
> + Copyright (C) 2017 Free Software Foundation, Inc.
> + This file is part of the GNU C Library.
> +
> + The GNU C Library is free software; you can redistribute it and/or
> + modify it under the terms of the GNU Lesser General Public
> + License as published by the Free Software Foundation; either
> + version 2.1 of the License, or (at your option) any later version.
> +
> + The GNU C Library is distributed in the hope that it will be useful,
> + but WITHOUT ANY WARRANTY; without even the implied warranty of
> + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
> + Lesser General Public License for more details.
> +
> + You should have received a copy of the GNU Lesser General Public
> + License along with the GNU C Library; if not, see
> + <http://www.gnu.org/licenses/>. */
> +
> +#include <support/check.h>
> +#include <support/xunistd.h>
> +#include <sys/stat.h>
> +
> +void
> +xfstat (int fd, struct stat64 *result)
> +{
> + if (fstat64 (fd, result) != 0)
> + FAIL_EXIT1 ("fstat64 (%d): %m", fd);
> +}
> diff --git a/support/xftruncate.c b/support/xftruncate.c
> new file mode 100644
> index 0000000000..9c4e9e3050
> --- /dev/null
> +++ b/support/xftruncate.c
> @@ -0,0 +1,27 @@
> +/* ftruncate with error checking.
> + Copyright (C) 2017 Free Software Foundation, Inc.
> + This file is part of the GNU C Library.
> +
> + The GNU C Library is free software; you can redistribute it and/or
> + modify it under the terms of the GNU Lesser General Public
> + License as published by the Free Software Foundation; either
> + version 2.1 of the License, or (at your option) any later version.
> +
> + The GNU C Library is distributed in the hope that it will be useful,
> + but WITHOUT ANY WARRANTY; without even the implied warranty of
> + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
> + Lesser General Public License for more details.
> +
> + You should have received a copy of the GNU Lesser General Public
> + License along with the GNU C Library; if not, see
> + <http://www.gnu.org/licenses/>. */
> +
> +#include <support/check.h>
> +#include <support/xunistd.h>
> +
> +void
> +xftruncate (int fd, long long length)
> +{
> + if (ftruncate64 (fd, length) != 0)
> + FAIL_EXIT1 ("ftruncate64 (%d, %lld): %m", fd, length);
> +}
> diff --git a/support/xlseek.c b/support/xlseek.c
> new file mode 100644
> index 0000000000..0a75a9f2e6
> --- /dev/null
> +++ b/support/xlseek.c
> @@ -0,0 +1,29 @@
> +/* lseek with error checking.
> + Copyright (C) 2017 Free Software Foundation, Inc.
> + This file is part of the GNU C Library.
> +
> + The GNU C Library is free software; you can redistribute it and/or
> + modify it under the terms of the GNU Lesser General Public
> + License as published by the Free Software Foundation; either
> + version 2.1 of the License, or (at your option) any later version.
> +
> + The GNU C Library is distributed in the hope that it will be useful,
> + but WITHOUT ANY WARRANTY; without even the implied warranty of
> + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
> + Lesser General Public License for more details.
> +
> + You should have received a copy of the GNU Lesser General Public
> + License along with the GNU C Library; if not, see
> + <http://www.gnu.org/licenses/>. */
> +
> +#include <support/check.h>
> +#include <support/xunistd.h>
> +
> +long long
> +xlseek (int fd, long long offset, int whence)
> +{
> + long long result = lseek64 (fd, offset, whence);
> + if (result < 0)
> + FAIL_EXIT1 ("lseek64 (%d, %lld, %d): %m", fd, offset, whence);
> + return result;
> +}
> diff --git a/support/xunistd.h b/support/xunistd.h
> index 00376f7aae..29da063c15 100644
> --- a/support/xunistd.h
> +++ b/support/xunistd.h
> @@ -36,10 +36,13 @@ void xpipe (int[2]);
> void xdup2 (int, int);
> int xopen (const char *path, int flags, mode_t);
> void xstat (const char *path, struct stat64 *);
> +void xfstat (int fd, struct stat64 *);
> void xmkdir (const char *path, mode_t);
> void xchroot (const char *path);
> void xunlink (const char *path);
> long xsysconf (int name);
> +long long xlseek (int fd, long long offset, int whence);
> +void xftruncate (int fd, long long length);
>
> /* Read the link at PATH. The caller should free the returned string
> with free. */
> diff --git a/sysdeps/unix/sysv/linux/aarch64/libc.abilist b/sysdeps/unix/sysv/linux/aarch64/libc.abilist
> index ec0ead15dd..90c9bc84e1 100644
> --- a/sysdeps/unix/sysv/linux/aarch64/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/aarch64/libc.abilist
> @@ -2104,6 +2104,7 @@ GLIBC_2.26 pwritev2 F
> GLIBC_2.26 pwritev64v2 F
> GLIBC_2.26 reallocarray F
> GLIBC_2.27 GLIBC_2.27 A
> +GLIBC_2.27 copy_file_range F
> GLIBC_2.27 glob F
> GLIBC_2.27 glob64 F
> GLIBC_2.27 memfd_create F
> diff --git a/sysdeps/unix/sysv/linux/alpha/libc.abilist b/sysdeps/unix/sysv/linux/alpha/libc.abilist
> index 5355769974..fd2d81fb68 100644
> --- a/sysdeps/unix/sysv/linux/alpha/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/alpha/libc.abilist
> @@ -2015,6 +2015,7 @@ GLIBC_2.26 pwritev2 F
> GLIBC_2.26 pwritev64v2 F
> GLIBC_2.26 reallocarray F
> GLIBC_2.27 GLIBC_2.27 A
> +GLIBC_2.27 copy_file_range F
> GLIBC_2.27 glob F
> GLIBC_2.27 glob64 F
> GLIBC_2.27 memfd_create F
> diff --git a/sysdeps/unix/sysv/linux/arm/libc.abilist b/sysdeps/unix/sysv/linux/arm/libc.abilist
> index 9bafe71b51..044ec102c2 100644
> --- a/sysdeps/unix/sysv/linux/arm/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/arm/libc.abilist
> @@ -105,6 +105,7 @@ GLIBC_2.26 pwritev2 F
> GLIBC_2.26 pwritev64v2 F
> GLIBC_2.26 reallocarray F
> GLIBC_2.27 GLIBC_2.27 A
> +GLIBC_2.27 copy_file_range F
> GLIBC_2.27 glob F
> GLIBC_2.27 glob64 F
> GLIBC_2.27 memfd_create F
> diff --git a/sysdeps/unix/sysv/linux/copy_file_range.c b/sysdeps/unix/sysv/linux/copy_file_range.c
> new file mode 100644
> index 0000000000..1ec5f9d8d8
> --- /dev/null
> +++ b/sysdeps/unix/sysv/linux/copy_file_range.c
> @@ -0,0 +1,46 @@
> +/* Linux implementation of copy_file_range.
> + Copyright (C) 2017 Free Software Foundation, Inc.
> + This file is part of the GNU C Library.
> +
> + The GNU C Library is free software; you can redistribute it and/or
> + modify it under the terms of the GNU Lesser General Public
> + License as published by the Free Software Foundation; either
> + version 2.1 of the License, or (at your option) any later version.
> +
> + The GNU C Library is distributed in the hope that it will be useful,
> + but WITHOUT ANY WARRANTY; without even the implied warranty of
> + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
> + Lesser General Public License for more details.
> +
> + You should have received a copy of the GNU Lesser General Public
> + License along with the GNU C Library; if not, see
> + <http://www.gnu.org/licenses/>. */
> +
> +#include <errno.h>
> +#include <sysdep-cancel.h>
> +#include <unistd.h>
> +
> +/* Include the fallback implementation. */
> +#ifndef __ASSUME_COPY_FILE_RANGE
> +#define COPY_FILE_RANGE_DECL static
> +#define COPY_FILE_RANGE copy_file_range_compat
> +#include <io/copy_file_range-compat.c>
> +#endif
> +
> +ssize_t
> +copy_file_range (int infd, __off64_t *pinoff,
> + int outfd, __off64_t *poutoff,
> + size_t length, unsigned int flags)
> +{
> +#ifdef __NR_copy_file_range
> + ssize_t ret = SYSCALL_CANCEL (copy_file_range, infd, pinoff, outfd, poutoff,
> + length, flags);
> +# ifndef __ASSUME_COPY_FILE_RANGE
> + if (ret == -1 && errno == ENOSYS)
> + ret = copy_file_range_compat (infd, pinoff, outfd, poutoff, length, flags);
> +# endif
> + return ret;
> +#else /* !__NR_copy_file_range */
> + return copy_file_range_compat (infd, pinoff, outfd, poutoff, length, flags);
> +#endif
> +}
Ok.
> diff --git a/sysdeps/unix/sysv/linux/hppa/libc.abilist b/sysdeps/unix/sysv/linux/hppa/libc.abilist
> index 90aa8d034f..2360130abe 100644
> --- a/sysdeps/unix/sysv/linux/hppa/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/hppa/libc.abilist
> @@ -1869,6 +1869,7 @@ GLIBC_2.26 pwritev2 F
> GLIBC_2.26 pwritev64v2 F
> GLIBC_2.26 reallocarray F
> GLIBC_2.27 GLIBC_2.27 A
> +GLIBC_2.27 copy_file_range F
> GLIBC_2.27 glob F
> GLIBC_2.27 glob64 F
> GLIBC_2.27 memfd_create F
> diff --git a/sysdeps/unix/sysv/linux/i386/libc.abilist b/sysdeps/unix/sysv/linux/i386/libc.abilist
> index 4d44c30c64..39c993fd79 100644
> --- a/sysdeps/unix/sysv/linux/i386/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/i386/libc.abilist
> @@ -2034,6 +2034,7 @@ GLIBC_2.26 strtof128_l F
> GLIBC_2.26 wcstof128 F
> GLIBC_2.26 wcstof128_l F
> GLIBC_2.27 GLIBC_2.27 A
> +GLIBC_2.27 copy_file_range F
> GLIBC_2.27 glob F
> GLIBC_2.27 glob64 F
> GLIBC_2.27 memfd_create F
> diff --git a/sysdeps/unix/sysv/linux/ia64/libc.abilist b/sysdeps/unix/sysv/linux/ia64/libc.abilist
> index 112fc57634..68496aa6ac 100644
> --- a/sysdeps/unix/sysv/linux/ia64/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/ia64/libc.abilist
> @@ -1898,6 +1898,7 @@ GLIBC_2.26 strtof128_l F
> GLIBC_2.26 wcstof128 F
> GLIBC_2.26 wcstof128_l F
> GLIBC_2.27 GLIBC_2.27 A
> +GLIBC_2.27 copy_file_range F
> GLIBC_2.27 glob F
> GLIBC_2.27 glob64 F
> GLIBC_2.27 memfd_create F
> diff --git a/sysdeps/unix/sysv/linux/kernel-features.h b/sysdeps/unix/sysv/linux/kernel-features.h
> index 59b613377f..b9864790c7 100644
> --- a/sysdeps/unix/sysv/linux/kernel-features.h
> +++ b/sysdeps/unix/sysv/linux/kernel-features.h
> @@ -111,3 +111,7 @@
> #if __LINUX_KERNEL_VERSION >= 0x040400
> # define __ASSUME_MLOCK2 1
> #endif
> +
> +#if __LINUX_KERNEL_VERSION >= 0x040500
> +# define __ASSUME_COPY_FILE_RANGE 1
> +#endif
Ok.
> diff --git a/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist b/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist
> index 2e8b6a4586..b676025261 100644
> --- a/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist
> @@ -106,6 +106,7 @@ GLIBC_2.26 pwritev2 F
> GLIBC_2.26 pwritev64v2 F
> GLIBC_2.26 reallocarray F
> GLIBC_2.27 GLIBC_2.27 A
> +GLIBC_2.27 copy_file_range F
> GLIBC_2.27 glob F
> GLIBC_2.27 glob64 F
> GLIBC_2.27 memfd_create F
> diff --git a/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist b/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist
> index 3c33400f67..cdd1df55d0 100644
> --- a/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist
> @@ -1983,6 +1983,7 @@ GLIBC_2.26 pwritev2 F
> GLIBC_2.26 pwritev64v2 F
> GLIBC_2.26 reallocarray F
> GLIBC_2.27 GLIBC_2.27 A
> +GLIBC_2.27 copy_file_range F
> GLIBC_2.27 glob F
> GLIBC_2.27 glob64 F
> GLIBC_2.27 memfd_create F
> diff --git a/sysdeps/unix/sysv/linux/microblaze/libc.abilist b/sysdeps/unix/sysv/linux/microblaze/libc.abilist
> index e1b1a579d2..e4265fd74d 100644
> --- a/sysdeps/unix/sysv/linux/microblaze/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/microblaze/libc.abilist
> @@ -2104,6 +2104,7 @@ GLIBC_2.26 pwritev2 F
> GLIBC_2.26 pwritev64v2 F
> GLIBC_2.26 reallocarray F
> GLIBC_2.27 GLIBC_2.27 A
> +GLIBC_2.27 copy_file_range F
> GLIBC_2.27 glob F
> GLIBC_2.27 glob64 F
> GLIBC_2.27 memfd_create F
> diff --git a/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist
> index c1550323f3..3a7e0b4c29 100644
> --- a/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist
> @@ -1958,6 +1958,7 @@ GLIBC_2.26 pwritev2 F
> GLIBC_2.26 pwritev64v2 F
> GLIBC_2.26 reallocarray F
> GLIBC_2.27 GLIBC_2.27 A
> +GLIBC_2.27 copy_file_range F
> GLIBC_2.27 glob F
> GLIBC_2.27 glob64 F
> GLIBC_2.27 memfd_create F
> diff --git a/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist
> index 3b3a172e4f..5e805924fa 100644
> --- a/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist
> @@ -1956,6 +1956,7 @@ GLIBC_2.26 pwritev2 F
> GLIBC_2.26 pwritev64v2 F
> GLIBC_2.26 reallocarray F
> GLIBC_2.27 GLIBC_2.27 A
> +GLIBC_2.27 copy_file_range F
> GLIBC_2.27 glob F
> GLIBC_2.27 glob64 F
> GLIBC_2.27 memfd_create F
> diff --git a/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist
> index 101ca7a241..1973fac36d 100644
> --- a/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist
> @@ -1954,6 +1954,7 @@ GLIBC_2.26 pwritev2 F
> GLIBC_2.26 pwritev64v2 F
> GLIBC_2.26 reallocarray F
> GLIBC_2.27 GLIBC_2.27 A
> +GLIBC_2.27 copy_file_range F
> GLIBC_2.27 glob F
> GLIBC_2.27 glob64 F
> GLIBC_2.27 memfd_create F
> diff --git a/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist
> index 2d129f7170..5e18ab83b4 100644
> --- a/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist
> @@ -1949,6 +1949,7 @@ GLIBC_2.26 pwritev2 F
> GLIBC_2.26 pwritev64v2 F
> GLIBC_2.26 reallocarray F
> GLIBC_2.27 GLIBC_2.27 A
> +GLIBC_2.27 copy_file_range F
> GLIBC_2.27 glob F
> GLIBC_2.27 glob64 F
> GLIBC_2.27 memfd_create F
> diff --git a/sysdeps/unix/sysv/linux/nios2/libc.abilist b/sysdeps/unix/sysv/linux/nios2/libc.abilist
> index 8bc350aff8..cc5885ab9b 100644
> --- a/sysdeps/unix/sysv/linux/nios2/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/nios2/libc.abilist
> @@ -2145,6 +2145,7 @@ GLIBC_2.26 pwritev2 F
> GLIBC_2.26 pwritev64v2 F
> GLIBC_2.26 reallocarray F
> GLIBC_2.27 GLIBC_2.27 A
> +GLIBC_2.27 copy_file_range F
> GLIBC_2.27 glob F
> GLIBC_2.27 glob64 F
> GLIBC_2.27 memfd_create F
> diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist
> index 127c426e1c..676aa50c81 100644
> --- a/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist
> @@ -1987,6 +1987,7 @@ GLIBC_2.26 pwritev2 F
> GLIBC_2.26 pwritev64v2 F
> GLIBC_2.26 reallocarray F
> GLIBC_2.27 GLIBC_2.27 A
> +GLIBC_2.27 copy_file_range F
> GLIBC_2.27 glob F
> GLIBC_2.27 glob64 F
> GLIBC_2.27 memfd_create F
> diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist
> index a9411318e2..2016c7c1e5 100644
> --- a/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist
> @@ -1992,6 +1992,7 @@ GLIBC_2.26 pwritev2 F
> GLIBC_2.26 pwritev64v2 F
> GLIBC_2.26 reallocarray F
> GLIBC_2.27 GLIBC_2.27 A
> +GLIBC_2.27 copy_file_range F
> GLIBC_2.27 glob F
> GLIBC_2.27 glob64 F
> GLIBC_2.27 memfd_create F
> diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc64/libc-le.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc64/libc-le.abilist
> index d7bf5db601..3d19e38dbd 100644
> --- a/sysdeps/unix/sysv/linux/powerpc/powerpc64/libc-le.abilist
> +++ b/sysdeps/unix/sysv/linux/powerpc/powerpc64/libc-le.abilist
> @@ -2199,6 +2199,7 @@ GLIBC_2.26 strtof128_l F
> GLIBC_2.26 wcstof128 F
> GLIBC_2.26 wcstof128_l F
> GLIBC_2.27 GLIBC_2.27 A
> +GLIBC_2.27 copy_file_range F
> GLIBC_2.27 glob F
> GLIBC_2.27 glob64 F
> GLIBC_2.27 memfd_create F
> diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc64/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc64/libc.abilist
> index a3415a72ac..c57ab21b82 100644
> --- a/sysdeps/unix/sysv/linux/powerpc/powerpc64/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/powerpc/powerpc64/libc.abilist
> @@ -106,6 +106,7 @@ GLIBC_2.26 pwritev2 F
> GLIBC_2.26 pwritev64v2 F
> GLIBC_2.26 reallocarray F
> GLIBC_2.27 GLIBC_2.27 A
> +GLIBC_2.27 copy_file_range F
> GLIBC_2.27 glob F
> GLIBC_2.27 glob64 F
> GLIBC_2.27 memfd_create F
> diff --git a/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist b/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist
> index 414338f9a2..25903720e3 100644
> --- a/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist
> @@ -1987,6 +1987,7 @@ GLIBC_2.26 pwritev2 F
> GLIBC_2.26 pwritev64v2 F
> GLIBC_2.26 reallocarray F
> GLIBC_2.27 GLIBC_2.27 A
> +GLIBC_2.27 copy_file_range F
> GLIBC_2.27 glob F
> GLIBC_2.27 glob64 F
> GLIBC_2.27 memfd_create F
> diff --git a/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist b/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist
> index f0f7a69b64..5d6800c236 100644
> --- a/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist
> @@ -1888,6 +1888,7 @@ GLIBC_2.26 pwritev2 F
> GLIBC_2.26 pwritev64v2 F
> GLIBC_2.26 reallocarray F
> GLIBC_2.27 GLIBC_2.27 A
> +GLIBC_2.27 copy_file_range F
> GLIBC_2.27 glob F
> GLIBC_2.27 glob64 F
> GLIBC_2.27 memfd_create F
> diff --git a/sysdeps/unix/sysv/linux/sh/libc.abilist b/sysdeps/unix/sysv/linux/sh/libc.abilist
> index 9f95aba898..c04872ca7f 100644
> --- a/sysdeps/unix/sysv/linux/sh/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/sh/libc.abilist
> @@ -1873,6 +1873,7 @@ GLIBC_2.26 pwritev2 F
> GLIBC_2.26 pwritev64v2 F
> GLIBC_2.26 reallocarray F
> GLIBC_2.27 GLIBC_2.27 A
> +GLIBC_2.27 copy_file_range F
> GLIBC_2.27 glob F
> GLIBC_2.27 glob64 F
> GLIBC_2.27 memfd_create F
> diff --git a/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist b/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist
> index 83fbdf2d7e..85cbe308d6 100644
> --- a/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist
> @@ -1980,6 +1980,7 @@ GLIBC_2.26 pwritev2 F
> GLIBC_2.26 pwritev64v2 F
> GLIBC_2.26 reallocarray F
> GLIBC_2.27 GLIBC_2.27 A
> +GLIBC_2.27 copy_file_range F
> GLIBC_2.27 glob F
> GLIBC_2.27 glob64 F
> GLIBC_2.27 memfd_create F
> diff --git a/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist b/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist
> index ee84ad10bc..f7a1ab8edb 100644
> --- a/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist
> @@ -1917,6 +1917,7 @@ GLIBC_2.26 pwritev2 F
> GLIBC_2.26 pwritev64v2 F
> GLIBC_2.26 reallocarray F
> GLIBC_2.27 GLIBC_2.27 A
> +GLIBC_2.27 copy_file_range F
> GLIBC_2.27 glob F
> GLIBC_2.27 glob64 F
> GLIBC_2.27 memfd_create F
> diff --git a/sysdeps/unix/sysv/linux/tile/tilegx32/libc.abilist b/sysdeps/unix/sysv/linux/tile/tilegx32/libc.abilist
> index dcbfbc05ac..ab56ecee44 100644
> --- a/sysdeps/unix/sysv/linux/tile/tilegx32/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/tile/tilegx32/libc.abilist
> @@ -2111,6 +2111,7 @@ GLIBC_2.26 pwritev2 F
> GLIBC_2.26 pwritev64v2 F
> GLIBC_2.26 reallocarray F
> GLIBC_2.27 GLIBC_2.27 A
> +GLIBC_2.27 copy_file_range F
> GLIBC_2.27 glob F
> GLIBC_2.27 glob64 F
> GLIBC_2.27 memfd_create F
> diff --git a/sysdeps/unix/sysv/linux/tile/tilegx64/libc.abilist b/sysdeps/unix/sysv/linux/tile/tilegx64/libc.abilist
> index 53dc99c45a..f2518c08ff 100644
> --- a/sysdeps/unix/sysv/linux/tile/tilegx64/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/tile/tilegx64/libc.abilist
> @@ -2111,6 +2111,7 @@ GLIBC_2.26 pwritev2 F
> GLIBC_2.26 pwritev64v2 F
> GLIBC_2.26 reallocarray F
> GLIBC_2.27 GLIBC_2.27 A
> +GLIBC_2.27 copy_file_range F
> GLIBC_2.27 glob F
> GLIBC_2.27 glob64 F
> GLIBC_2.27 memfd_create F
> diff --git a/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist b/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist
> index ae4dcaa47e..2a3cc40674 100644
> --- a/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist
> @@ -1875,6 +1875,7 @@ GLIBC_2.26 strtof128_l F
> GLIBC_2.26 wcstof128 F
> GLIBC_2.26 wcstof128_l F
> GLIBC_2.27 GLIBC_2.27 A
> +GLIBC_2.27 copy_file_range F
> GLIBC_2.27 glob F
> GLIBC_2.27 glob64 F
> GLIBC_2.27 memfd_create F
> diff --git a/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist b/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist
> index 0dbda14796..8bc16b9004 100644
> --- a/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist
> @@ -2118,6 +2118,7 @@ GLIBC_2.26 strtof128_l F
> GLIBC_2.26 wcstof128 F
> GLIBC_2.26 wcstof128_l F
> GLIBC_2.27 GLIBC_2.27 A
> +GLIBC_2.27 copy_file_range F
> GLIBC_2.27 glob F
> GLIBC_2.27 glob64 F
> GLIBC_2.27 memfd_create F
>
^ permalink raw reply [flat|nested] 24+ messages in thread
* Re: [PATCH] copy_file_range: New function to copy file data
2017-12-21 17:04 ` Adhemerval Zanella
@ 2017-12-21 18:07 ` Florian Weimer
2017-12-21 19:04 ` Adhemerval Zanella
0 siblings, 1 reply; 24+ messages in thread
From: Florian Weimer @ 2017-12-21 18:07 UTC (permalink / raw)
To: Adhemerval Zanella; +Cc: libc-alpha, Joseph S. Myers
On 12/21/2017 06:04 PM, Adhemerval Zanella wrote:
>> + struct stat64 instat;
>> + struct stat64 outstat;
>> + if (fstat64 (infd, &instat) != 0 || fstat64 (outfd, &outstat) != 0)
>> + return -1;
>> + if (S_ISDIR (instat.st_mode) || S_ISDIR (outstat.st_mode))
>> + {
>> + __set_errno (EISDIR);
>> + return -1;
>> + }
>
> To follow the pattern you can put 'instat' and 'outstat' in its own scope.
Agreed.
>> + if (read_count < 0)
>> + {
>> + if (copied > 0)
>> + /* Report the number of bytes copied so far. */
>> + return copied;
>> + return -1;
>> + }> + if (pinoff != 0)
>> + *pinoff += read_count;
>
> pinoff != NULL.
Oh, right.
>> +
>> + /* Write the buffer part which was read to the destination. */
>> + char *end = buf + read_count;
>> + for (char *p = buf; p < end; )
>> + {
>> + ssize_t write_count;
>> + if (poutoff == NULL)
>> + write_count = write (outfd, p, end - p);
>> + else
>> + write_count = __libc_pwrite64 (outfd, p, end - p, *poutoff);
>> + if (write_count < 0)
>> + {
>> + /* Adjust the input read position to match what we have
>> + written, so that the caller can pick up after the
>> + error. */
>> + size_t written = p - buf;
>> + /* NB: This needs to be signed so that we can form the
>> + negative value below. */
>> + ssize_t overread = read_count - written;
>> + if (pinoff == NULL)
>> + {
>> + if (overread > 0)
>> + {
>> + /* We are on an error recovery path, so we
>> + cannot deal with failure here. */
>> + int save_errno = errno;
>> + (void) __libc_lseek64 (infd, -overread, SEEK_CUR);
>> + __set_errno (save_errno);
>
> Should we really handle errors here? Using current man pages EBADF, ENXIO,
> ESPIPE can't really happen because of previous checks. EINVAL and EOVERFLOW
> due resulting file offset would be negative or beyond the end of a seekable
> device is also unlikely due the fact we are using the results of a previous
> partial write to calculate the required offset. I am not sure if it can
> really fail here.
Theoretically, I assume that with enough memory pressure, the seek might
have to re-read on-disk data structures, and then anything can happen.
This is why I don't want to assert on the error. Perhaps more likely is
a file descriptor race condition which closes the descriptor under us,
but then the application is screwed anyway.
We cannot report the error in all cases because with a partial write, we
need to report the number of written bytes (because that effect has
already happened and is visible by other means).
So I think the code is okay as it is now, all things considering.
Thanks,
Florian
^ permalink raw reply [flat|nested] 24+ messages in thread
* Re: [PATCH] copy_file_range: New function to copy file data
2017-12-21 18:07 ` Florian Weimer
@ 2017-12-21 19:04 ` Adhemerval Zanella
0 siblings, 0 replies; 24+ messages in thread
From: Adhemerval Zanella @ 2017-12-21 19:04 UTC (permalink / raw)
To: Florian Weimer; +Cc: libc-alpha, Joseph S. Myers
On 21/12/2017 16:07, Florian Weimer wrote:
> On 12/21/2017 06:04 PM, Adhemerval Zanella wrote:
>
>>> +Â struct stat64 instat;
>>> +Â struct stat64 outstat;
>>> +Â if (fstat64 (infd, &instat) != 0 || fstat64 (outfd, &outstat) != 0)
>>> +Â Â Â return -1;
>>> +Â if (S_ISDIR (instat.st_mode) || S_ISDIR (outstat.st_mode))
>>> +Â Â Â {
>>> +Â Â Â Â Â __set_errno (EISDIR);
>>> +Â Â Â Â Â return -1;
>>> +Â Â Â }
>>
>> To follow the pattern you can put 'instat' and 'outstat' in its own scope.
>
> Agreed.
>
>>> +Â Â Â Â Â if (read_count < 0)
>>> +Â Â Â Â Â Â Â {
>>> +Â Â Â Â Â Â Â Â Â if (copied > 0)
>>> +           /* Report the number of bytes copied so far. */
>>> +Â Â Â Â Â Â Â Â Â Â Â return copied;
>>> +Â Â Â Â Â Â Â Â Â return -1;
>>> +Â Â Â Â Â Â Â }> +Â Â Â Â Â if (pinoff != 0)
>>> +Â Â Â Â Â Â Â *pinoff += read_count;
>>
>> pinoff != NULL.
>
> Oh, right.
>
>>> +
>>> +     /* Write the buffer part which was read to the destination. */
>>> +Â Â Â Â Â char *end = buf + read_count;
>>> +Â Â Â Â Â for (char *p = buf; p < end; )
>>> +Â Â Â Â Â Â Â {
>>> +Â Â Â Â Â Â Â Â Â ssize_t write_count;
>>> +Â Â Â Â Â Â Â Â Â if (poutoff == NULL)
>>> +Â Â Â Â Â Â Â Â Â Â Â write_count = write (outfd, p, end - p);
>>> +Â Â Â Â Â Â Â Â Â else
>>> +Â Â Â Â Â Â Â Â Â Â Â write_count = __libc_pwrite64 (outfd, p, end - p, *poutoff);
>>> +Â Â Â Â Â Â Â Â Â if (write_count < 0)
>>> +Â Â Â Â Â Â Â Â Â Â Â {
>>> +Â Â Â Â Â Â Â Â Â Â Â Â Â /* Adjust the input read position to match what we have
>>> +Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â written, so that the caller can pick up after the
>>> +                error. */
>>> +Â Â Â Â Â Â Â Â Â Â Â Â Â size_t written = p - buf;
>>> +Â Â Â Â Â Â Â Â Â Â Â Â Â /* NB: This needs to be signed so that we can form the
>>> +                negative value below. */
>>> +Â Â Â Â Â Â Â Â Â Â Â Â Â ssize_t overread = read_count - written;
>>> +Â Â Â Â Â Â Â Â Â Â Â Â Â if (pinoff == NULL)
>>> +Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â {
>>> +Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â if (overread > 0)
>>> +Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â {
>>> +Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â /* We are on an error recovery path, so we
>>> +                        cannot deal with failure here. */
>>> +Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â int save_errno = errno;
>>> +Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â (void) __libc_lseek64 (infd, -overread, SEEK_CUR);
>>> +Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â __set_errno (save_errno);
>>
>> Should we really handle errors here? Using current man pages EBADF, ENXIO,
>> ESPIPE can't really happen because of previous checks. EINVAL and EOVERFLOW
>> due resulting file offset would be negative or beyond the end of a seekable
>> device is also unlikely due the fact we are using the results of a previous
>> partial write to calculate the required offset. I am not sure if it can
>> really fail here.
>
> Theoretically, I assume that with enough memory pressure, the seek might have to re-read on-disk data structures, and then anything can happen. This is why I don't want to assert on the error. Perhaps more likely is a file descriptor race condition which closes the descriptor under us, but then the application is screwed anyway.
>
> We cannot report the error in all cases because with a partial write, we need to report the number of written bytes (because that effect has already happened and is visible by other means).
>
> So I think the code is okay as it is now, all things considering.
I think for former it will hit the oom scenario where kernel will randomly
killing a process (assuming it is what Linux still does) which result the
process to continue execution or being killed. Anyway, I think I am think
I am over engineering things here, so your approach should be ok.
^ permalink raw reply [flat|nested] 24+ messages in thread
end of thread, other threads:[~2017-12-21 19:04 UTC | newest]
Thread overview: 24+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2017-11-17 14:56 [PATCH] copy_file_range: New function to copy file data Florian Weimer
2017-11-17 14:58 ` Florian Weimer
2017-11-19 3:13 ` Rical Jasan
2017-11-20 12:55 ` Florian Weimer
2017-11-21 0:36 ` Rical Jasan
2017-11-19 3:11 ` Rical Jasan
2017-11-23 14:46 ` Florian Weimer
2017-11-23 15:13 ` Andreas Schwab
2017-11-23 15:23 ` Florian Weimer
2017-11-23 15:39 ` Andreas Schwab
2017-11-23 15:43 ` Florian Weimer
2017-11-23 15:46 ` Andreas Schwab
2017-11-23 16:23 ` Florian Weimer
2017-12-05 14:03 ` Florian Weimer
2017-12-14 14:26 ` Florian Weimer
2017-12-18 20:04 ` Adhemerval Zanella
2017-12-18 20:19 ` Florian Weimer
2017-12-19 11:02 ` Adhemerval Zanella
2017-12-21 13:44 ` Florian Weimer
2017-12-21 17:04 ` Adhemerval Zanella
2017-12-21 18:07 ` Florian Weimer
2017-12-21 19:04 ` Adhemerval Zanella
2017-12-18 21:54 ` Joseph Myers
2017-12-19 11:08 ` Adhemerval Zanella
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).