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 /src | |
| 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.
Diffstat (limited to 'src')
| -rw-r--r-- | src/fileio.c | 41 |
1 files changed, 33 insertions, 8 deletions
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 |