aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/fileio.c41
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