diff options
| author | Paul Eggert | 2019-06-06 21:18:11 -0700 |
|---|---|---|
| committer | Paul Eggert | 2019-06-07 00:44:45 -0700 |
| commit | 486a81f387bb59b2fbbc6aff7b41adbe1621394e (patch) | |
| tree | 7b61ed0a44666f10e4da54ef3e0905974c861944 | |
| parent | 111408a0e9eb3a9492c4057ac7d6ddbb8b365aa9 (diff) | |
| download | emacs-486a81f387bb59b2fbbc6aff7b41adbe1621394e.tar.gz emacs-486a81f387bb59b2fbbc6aff7b41adbe1621394e.zip | |
Use copy_file_range to copy files
The copy_file_range syscall (introduced in Linux kernel
version 4.5) can copy files more efficiently via server-side
copy etc.
* admin/merge-gnulib (GNULIB_MODULES): Add copy-file-range.
* lib/copy-file-range.c, m4/copy-file-range.m4:
New files, copied from Gnulib.
* lib/gnulib.mk.in, m4/gnulib-comp.m4: Regenerate.
* src/fileio.c (Fcopy_file): Try copy_file_range first,
falling back on read+write only if copy_file_range failed or
if the input is empty and so could be a /proc file.
| -rwxr-xr-x | admin/merge-gnulib | 2 | ||||
| -rw-r--r-- | lib/copy-file-range.c | 33 | ||||
| -rw-r--r-- | lib/gnulib.mk.in | 12 | ||||
| -rw-r--r-- | m4/copy-file-range.m4 | 36 | ||||
| -rw-r--r-- | m4/gnulib-comp.m4 | 8 | ||||
| -rw-r--r-- | src/fileio.c | 41 |
6 files changed, 123 insertions, 9 deletions
diff --git a/admin/merge-gnulib b/admin/merge-gnulib index 4a69310d83c..c2d9291b843 100755 --- a/admin/merge-gnulib +++ b/admin/merge-gnulib | |||
| @@ -27,7 +27,7 @@ GNULIB_URL=git://git.savannah.gnu.org/gnulib.git | |||
| 27 | 27 | ||
| 28 | GNULIB_MODULES=' | 28 | GNULIB_MODULES=' |
| 29 | alloca-opt binary-io byteswap c-ctype c-strcase | 29 | alloca-opt binary-io byteswap c-ctype c-strcase |
| 30 | careadlinkat close-stream | 30 | careadlinkat close-stream copy-file-range |
| 31 | count-leading-zeros count-one-bits count-trailing-zeros | 31 | count-leading-zeros count-one-bits count-trailing-zeros |
| 32 | crypto/md5-buffer crypto/sha1-buffer crypto/sha256-buffer crypto/sha512-buffer | 32 | crypto/md5-buffer crypto/sha1-buffer crypto/sha256-buffer crypto/sha512-buffer |
| 33 | d-type diffseq dosname dtoastr dtotimespec dup2 | 33 | d-type diffseq dosname dtoastr dtotimespec dup2 |
diff --git a/lib/copy-file-range.c b/lib/copy-file-range.c new file mode 100644 index 00000000000..39b5efbc1cc --- /dev/null +++ b/lib/copy-file-range.c | |||
| @@ -0,0 +1,33 @@ | |||
| 1 | /* Stub for copy_file_range | ||
| 2 | Copyright 2019 Free Software Foundation, Inc. | ||
| 3 | |||
| 4 | This program is free software: you can redistribute it and/or modify | ||
| 5 | it under the terms of the GNU General Public License as published by | ||
| 6 | the Free Software Foundation; either version 3 of the License, or | ||
| 7 | (at your option) any later version. | ||
| 8 | |||
| 9 | This program is distributed in the hope that it will be useful, | ||
| 10 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 12 | GNU General Public License for more details. | ||
| 13 | |||
| 14 | You should have received a copy of the GNU General Public License | ||
| 15 | along with this program. If not, see <https://www.gnu.org/licenses/>. */ | ||
| 16 | |||
| 17 | #include <config.h> | ||
| 18 | |||
| 19 | #include <unistd.h> | ||
| 20 | |||
| 21 | #include <errno.h> | ||
| 22 | |||
| 23 | ssize_t | ||
| 24 | copy_file_range (int infd, off_t *pinoff, | ||
| 25 | int outfd, off_t *poutoff, | ||
| 26 | size_t length, unsigned int flags) | ||
| 27 | { | ||
| 28 | /* There is little need to emulate copy_file_range with read+write, | ||
| 29 | since programs that use copy_file_range must fall back on | ||
| 30 | read+write anyway. */ | ||
| 31 | errno = ENOSYS; | ||
| 32 | return -1; | ||
| 33 | } | ||
diff --git a/lib/gnulib.mk.in b/lib/gnulib.mk.in index 403d83829cd..6e817fa6897 100644 --- a/lib/gnulib.mk.in +++ b/lib/gnulib.mk.in | |||
| @@ -74,6 +74,7 @@ | |||
| 74 | # c-strcase \ | 74 | # c-strcase \ |
| 75 | # careadlinkat \ | 75 | # careadlinkat \ |
| 76 | # close-stream \ | 76 | # close-stream \ |
| 77 | # copy-file-range \ | ||
| 77 | # count-leading-zeros \ | 78 | # count-leading-zeros \ |
| 78 | # count-one-bits \ | 79 | # count-one-bits \ |
| 79 | # count-trailing-zeros \ | 80 | # count-trailing-zeros \ |
| @@ -1274,6 +1275,17 @@ EXTRA_DIST += close-stream.h | |||
| 1274 | endif | 1275 | endif |
| 1275 | ## end gnulib module close-stream | 1276 | ## end gnulib module close-stream |
| 1276 | 1277 | ||
| 1278 | ## begin gnulib module copy-file-range | ||
| 1279 | ifeq (,$(OMIT_GNULIB_MODULE_copy-file-range)) | ||
| 1280 | |||
| 1281 | |||
| 1282 | EXTRA_DIST += copy-file-range.c | ||
| 1283 | |||
| 1284 | EXTRA_libgnu_a_SOURCES += copy-file-range.c | ||
| 1285 | |||
| 1286 | endif | ||
| 1287 | ## end gnulib module copy-file-range | ||
| 1288 | |||
| 1277 | ## begin gnulib module count-leading-zeros | 1289 | ## begin gnulib module count-leading-zeros |
| 1278 | ifeq (,$(OMIT_GNULIB_MODULE_count-leading-zeros)) | 1290 | ifeq (,$(OMIT_GNULIB_MODULE_count-leading-zeros)) |
| 1279 | 1291 | ||
diff --git a/m4/copy-file-range.m4 b/m4/copy-file-range.m4 new file mode 100644 index 00000000000..20fd05697fe --- /dev/null +++ b/m4/copy-file-range.m4 | |||
| @@ -0,0 +1,36 @@ | |||
| 1 | # copy-file-range.m4 | ||
| 2 | dnl Copyright 2019 Free Software Foundation, Inc. | ||
| 3 | dnl This file is free software; the Free Software Foundation | ||
| 4 | dnl gives unlimited permission to copy and/or distribute it, | ||
| 5 | dnl with or without modifications, as long as this notice is preserved. | ||
| 6 | |||
| 7 | AC_DEFUN([gl_FUNC_COPY_FILE_RANGE], | ||
| 8 | [ | ||
| 9 | AC_REQUIRE([gl_UNISTD_H_DEFAULTS]) | ||
| 10 | |||
| 11 | dnl Persuade glibc <unistd.h> to declare copy_file_range. | ||
| 12 | AC_REQUIRE([AC_USE_SYSTEM_EXTENSIONS]) | ||
| 13 | |||
| 14 | dnl Use AC_LINK_IFELSE, rather than AC_CHECK_FUNCS or a variant, | ||
| 15 | dnl since we don't want AC_CHECK_FUNCS's checks for glibc stubs. | ||
| 16 | dnl Programs that use copy_file_range must fall back on read+write | ||
| 17 | dnl anyway, and there's little point to substituting the Gnulib stub | ||
| 18 | dnl for a glibc stub. | ||
| 19 | AC_CACHE_CHECK([for copy_file_range], [gl_cv_func_copy_file_range], | ||
| 20 | [AC_LINK_IFELSE( | ||
| 21 | [AC_LANG_PROGRAM( | ||
| 22 | [[#include <unistd.h> | ||
| 23 | ]], | ||
| 24 | [[ssize_t (*func) (int, off_t *, int, off_t, size_t, unsigned) | ||
| 25 | = copy_file_range; | ||
| 26 | return func (0, 0, 0, 0, 0, 0) & 127; | ||
| 27 | ]]) | ||
| 28 | ], | ||
| 29 | [gl_cv_func_copy_file_range=yes], | ||
| 30 | [gl_cv_func_copy_file_range=no]) | ||
| 31 | ]) | ||
| 32 | |||
| 33 | if test "$gl_cv_func_copy_file_range" != yes; then | ||
| 34 | HAVE_COPY_FILE_RANGE=0 | ||
| 35 | fi | ||
| 36 | ]) | ||
diff --git a/m4/gnulib-comp.m4 b/m4/gnulib-comp.m4 index 0a7a30e5ae2..4627575bf63 100644 --- a/m4/gnulib-comp.m4 +++ b/m4/gnulib-comp.m4 | |||
| @@ -56,6 +56,7 @@ AC_DEFUN([gl_EARLY], | |||
| 56 | # Code from module clock-time: | 56 | # Code from module clock-time: |
| 57 | # Code from module cloexec: | 57 | # Code from module cloexec: |
| 58 | # Code from module close-stream: | 58 | # Code from module close-stream: |
| 59 | # Code from module copy-file-range: | ||
| 59 | # Code from module count-leading-zeros: | 60 | # Code from module count-leading-zeros: |
| 60 | # Code from module count-one-bits: | 61 | # Code from module count-one-bits: |
| 61 | # Code from module count-trailing-zeros: | 62 | # Code from module count-trailing-zeros: |
| @@ -201,6 +202,11 @@ AC_DEFUN([gl_INIT], | |||
| 201 | AC_CHECK_FUNCS_ONCE([readlinkat]) | 202 | AC_CHECK_FUNCS_ONCE([readlinkat]) |
| 202 | gl_CLOCK_TIME | 203 | gl_CLOCK_TIME |
| 203 | gl_MODULE_INDICATOR([close-stream]) | 204 | gl_MODULE_INDICATOR([close-stream]) |
| 205 | gl_FUNC_COPY_FILE_RANGE | ||
| 206 | if test $HAVE_COPY_FILE_RANGE = 0; then | ||
| 207 | AC_LIBOBJ([copy-file-range]) | ||
| 208 | fi | ||
| 209 | gl_UNISTD_MODULE_INDICATOR([copy-file-range]) | ||
| 204 | gl_COUNT_LEADING_ZEROS | 210 | gl_COUNT_LEADING_ZEROS |
| 205 | gl_COUNT_ONE_BITS | 211 | gl_COUNT_ONE_BITS |
| 206 | gl_COUNT_TRAILING_ZEROS | 212 | gl_COUNT_TRAILING_ZEROS |
| @@ -846,6 +852,7 @@ AC_DEFUN([gl_FILE_LIST], [ | |||
| 846 | lib/cloexec.h | 852 | lib/cloexec.h |
| 847 | lib/close-stream.c | 853 | lib/close-stream.c |
| 848 | lib/close-stream.h | 854 | lib/close-stream.h |
| 855 | lib/copy-file-range.c | ||
| 849 | lib/count-leading-zeros.c | 856 | lib/count-leading-zeros.c |
| 850 | lib/count-leading-zeros.h | 857 | lib/count-leading-zeros.h |
| 851 | lib/count-one-bits.c | 858 | lib/count-one-bits.c |
| @@ -995,6 +1002,7 @@ AC_DEFUN([gl_FILE_LIST], [ | |||
| 995 | m4/builtin-expect.m4 | 1002 | m4/builtin-expect.m4 |
| 996 | m4/byteswap.m4 | 1003 | m4/byteswap.m4 |
| 997 | m4/clock_time.m4 | 1004 | m4/clock_time.m4 |
| 1005 | m4/copy-file-range.m4 | ||
| 998 | m4/count-leading-zeros.m4 | 1006 | m4/count-leading-zeros.m4 |
| 999 | m4/count-one-bits.m4 | 1007 | m4/count-one-bits.m4 |
| 1000 | m4/count-trailing-zeros.m4 | 1008 | m4/count-trailing-zeros.m4 |
diff --git a/src/fileio.c b/src/fileio.c index 9e9779967dd..f7376ce74fe 100644 --- a/src/fileio.c +++ b/src/fileio.c | |||
| @@ -2117,14 +2117,39 @@ permissions. */) | |||
| 2117 | newsize = st.st_size; | 2117 | newsize = st.st_size; |
| 2118 | else | 2118 | else |
| 2119 | { | 2119 | { |
| 2120 | char buf[MAX_ALLOCA]; | 2120 | off_t insize = st.st_size; |
| 2121 | ptrdiff_t n; | 2121 | ssize_t copied; |
| 2122 | for (newsize = 0; 0 < (n = emacs_read_quit (ifd, buf, sizeof buf)); | 2122 | |
| 2123 | newsize += n) | 2123 | for (newsize = 0; newsize < insize; newsize += copied) |
| 2124 | if (emacs_write_quit (ofd, buf, n) != n) | 2124 | { |
| 2125 | report_file_error ("Write error", newname); | 2125 | /* Copy at most COPY_MAX bytes at a time; this is min |
| 2126 | if (n < 0) | 2126 | (PTRDIFF_MAX, SIZE_MAX) truncated to a value that is |
| 2127 | report_file_error ("Read error", file); | 2127 | surely aligned well. */ |
| 2128 | ptrdiff_t copy_max = min (PTRDIFF_MAX, SIZE_MAX) >> 30 << 30; | ||
| 2129 | off_t intail = insize - newsize; | ||
| 2130 | ptrdiff_t len = min (intail, copy_max); | ||
| 2131 | copied = copy_file_range (ifd, NULL, ofd, NULL, len, 0); | ||
| 2132 | if (copied <= 0) | ||
| 2133 | break; | ||
| 2134 | maybe_quit (); | ||
| 2135 | } | ||
| 2136 | |||
| 2137 | /* Fall back on read+write if copy_file_range failed, or if the | ||
| 2138 | input is empty and so could be a /proc file. read+write will | ||
| 2139 | either succeed, or report an error more precisely than | ||
| 2140 | copy_file_range would. */ | ||
| 2141 | if (newsize != insize || insize == 0) | ||
| 2142 | { | ||
| 2143 | char buf[MAX_ALLOCA]; | ||
| 2144 | for (; (copied = emacs_read_quit (ifd, buf, sizeof buf)); | ||
| 2145 | newsize += copied) | ||
| 2146 | { | ||
| 2147 | if (copied < 0) | ||
| 2148 | report_file_error ("Read error", file); | ||
| 2149 | if (emacs_write_quit (ofd, buf, copied) != copied) | ||
| 2150 | report_file_error ("Write error", newname); | ||
| 2151 | } | ||
| 2152 | } | ||
| 2128 | } | 2153 | } |
| 2129 | 2154 | ||
| 2130 | /* Truncate any existing output file after writing the data. This | 2155 | /* Truncate any existing output file after writing the data. This |