This is the mail archive of the libc-alpha@sourceware.org mailing list for the glibc project.
Index Nav: | [Date Index] [Subject Index] [Author Index] [Thread Index] | |
---|---|---|
Message Nav: | [Date Prev] [Date Next] | [Thread Prev] [Thread Next] |
Other format: | [Raw text] |
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:
tst-copy_file_range.out
Description: Text document
Index Nav: | [Date Index] [Subject Index] [Author Index] [Thread Index] | |
---|---|---|
Message Nav: | [Date Prev] [Date Next] | [Thread Prev] [Thread Next] |