]> code.delx.au - gnu-emacs/commitdiff
copy-file now truncates output after writing
authorPaul Eggert <eggert@cs.ucla.edu>
Sat, 30 May 2015 05:55:25 +0000 (22:55 -0700)
committerPaul Eggert <eggert@cs.ucla.edu>
Sat, 30 May 2015 06:02:38 +0000 (23:02 -0700)
* src/fileio.c (Fcopy_file): Truncate output after writing rather
than before.  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 (Bug#20595).  Also, check for read errors.

src/fileio.c

index 796f08d3c58526c298ae6b700564cf072667ccf9..a969d3b2c0fc41741a141e0eb89818183610b801 100644 (file)
@@ -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