diff --git a/src/fileio.c b/src/fileio.c index 796f08d3c58..a969d3b2c0f 100644 --- a/src/fileio.c +++ b/src/fileio.c @@ -1871,8 +1871,6 @@ permissions. */) bool already_exists = false; mode_t new_mask; int ifd, ofd; - int n; - char buf[16 * 1024]; struct stat st; #endif @@ -1974,6 +1972,8 @@ permissions. */) record_unwind_protect_int (close_file_unwind, ofd); + off_t oldsize = 0, newsize = 0; + if (already_exists) { struct stat out_st; @@ -1982,15 +1982,31 @@ permissions. */) if (st.st_dev == out_st.st_dev && st.st_ino == out_st.st_ino) report_file_errno ("Input and output files are the same", list2 (file, newname), 0); - if (ftruncate (ofd, 0) != 0) - report_file_error ("Truncating output file", newname); + if (S_ISREG (out_st.st_mode)) + oldsize = out_st.st_size; } immediate_quit = 1; QUIT; - while ((n = emacs_read (ifd, buf, sizeof buf)) > 0) - if (emacs_write_sig (ofd, buf, n) != n) - report_file_error ("Write error", newname); + while (true) + { + char buf[MAX_ALLOCA]; + ptrdiff_t n = emacs_read (ifd, buf, sizeof buf); + if (n < 0) + report_file_error ("Read error", file); + if (n == 0) + break; + if (emacs_write_sig (ofd, buf, n) != n) + report_file_error ("Write error", newname); + newsize += n; + } + + /* Truncate any existing output file after writing the data. This + is more likely to work than truncation before writing, if the + file system is out of space or the user is over disk quota. */ + if (newsize < oldsize && ftruncate (ofd, newsize) != 0) + report_file_error ("Truncating output file", newname); + immediate_quit = 0; #ifndef MSDOS