diff options
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 |