diff options
| author | Florian Weimer <fweimer@redhat.com> | 2017-12-22 10:55:40 +0100 |
|---|---|---|
| committer | Florian Weimer <fweimer@redhat.com> | 2017-12-22 10:55:40 +0100 |
| commit | bad7a0c81f501fbbcc79af9eaa4b8254441c4a1f (patch) | |
| tree | 2734074b2ca53301953e238e6ce362bdc94f9604 | |
| parent | 6cb86fd21ca6fdfc31042cda8c37f96c46b8a4da (diff) | |
| download | glibc-bad7a0c81f501fbbcc79af9eaa4b8254441c4a1f.tar.xz glibc-bad7a0c81f501fbbcc79af9eaa4b8254441c4a1f.zip | |
copy_file_range: New function to copy file data
The semantics are based on the Linux system call, but a very close
emulation in user space is provided.
44 files changed, 1340 insertions, 3 deletions
@@ -1,3 +1,24 @@ +2017-12-22 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. + 2017-12-21 Szabolcs Nagy <szabolcs.nagy@arm.com> * scripts/build-many-glibcs.py (Context.add_all_configs): Add @@ -61,6 +61,8 @@ Major new features: declares the functions pkey_alloc, pkey_free, pkey_mprotect, pkey_set, pkey_get. +* The copy_file_range function was added. + * Optimized memcpy, mempcpy, memmove, and memset for sparc M7. * The ldconfig utility now processes `include' directives using the C/POSIX 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..5c1b7b3258 --- /dev/null +++ b/io/copy_file_range-compat.c @@ -0,0 +1,160 @@ +/* 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 != NULL) + *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> |
