aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorPaul Eggert2025-01-22 11:06:06 -0800
committerPaul Eggert2025-07-13 21:09:38 -0700
commitffd65be2277b9a30e77a00ad69c9ba21459f72c5 (patch)
tree2a3cc5dc796280ec9cc86e1a374744b0b27169c0 /src
parentae30a61c74215c3e02fb3c0d603894457a4cfef5 (diff)
downloademacs-ffd65be2277b9a30e77a00ad69c9ba21459f72c5.tar.gz
emacs-ffd65be2277b9a30e77a00ad69c9ba21459f72c5.zip
copy-file no longer trusts st_size
In copy-file, do not trust st_size, since it might change as we run, or we might be in a /proc system where it is unreliable anyway. Also, fix some other unlikely copy-file bugs while we’re here. * src/fileio.c (Fcopy_file): Use O_TRUNC when opening a destination that already exists. This saves us the trouble of having to call ftruncate with a possibly-bogus st_size; the old motivation for using ftruncate is no longer compelling. Do not assume ptrdiff_t is as wide as ssize_t; although this is true on all known platforms, it’s easy to not assume it. Don’t trust st_size. Prefer SSIZE_MAX to TYPE_MAXIMUM (ssize_t). Always read+write, regardless of whether copy_file_range failed.
Diffstat (limited to 'src')
-rw-r--r--src/fileio.c64
1 files changed, 22 insertions, 42 deletions
diff --git a/src/fileio.c b/src/fileio.c
index 4c3bc5787cf..82ee22d2187 100644
--- a/src/fileio.c
+++ b/src/fileio.c
@@ -2369,15 +2369,13 @@ permissions. */)
2369 barf_or_query_if_file_exists (newname, true, "copy to it", 2369 barf_or_query_if_file_exists (newname, true, "copy to it",
2370 FIXNUMP (ok_if_already_exists), false); 2370 FIXNUMP (ok_if_already_exists), false);
2371 already_exists = true; 2371 already_exists = true;
2372 ofd = emacs_open (SSDATA (encoded_newname), O_WRONLY, 0); 2372 ofd = emacs_open (SSDATA (encoded_newname), O_WRONLY | O_TRUNC, 0);
2373 } 2373 }
2374 if (ofd < 0) 2374 if (ofd < 0)
2375 report_file_error ("Opening output file", newname); 2375 report_file_error ("Opening output file", newname);
2376 2376
2377 record_unwind_protect_int (close_file_unwind, ofd); 2377 record_unwind_protect_int (close_file_unwind, ofd);
2378 2378
2379 off_t oldsize = 0, newsize;
2380
2381 if (already_exists) 2379 if (already_exists)
2382 { 2380 {
2383 struct stat out_st; 2381 struct stat out_st;
@@ -2386,36 +2384,26 @@ permissions. */)
2386 if (st.st_dev == out_st.st_dev && st.st_ino == out_st.st_ino) 2384 if (st.st_dev == out_st.st_dev && st.st_ino == out_st.st_ino)
2387 report_file_errno ("Input and output files are the same", 2385 report_file_errno ("Input and output files are the same",
2388 list2 (file, newname), 0); 2386 list2 (file, newname), 0);
2389 if (S_ISREG (out_st.st_mode))
2390 oldsize = out_st.st_size;
2391 } 2387 }
2392 2388
2393 maybe_quit (); 2389 maybe_quit ();
2394 2390
2395 if (emacs_fd_to_int (ifd) != -1 2391 if (emacs_fd_to_int (ifd) == -1
2396 && clone_file (ofd, emacs_fd_to_int (ifd))) 2392 || !clone_file (ofd, emacs_fd_to_int (ifd)))
2397 newsize = st.st_size;
2398 else
2399 { 2393 {
2400 off_t insize = st.st_size; 2394 off_t newsize = 0;
2401 ssize_t copied;
2402 2395
2403#ifndef MSDOS 2396#ifndef MSDOS
2404 newsize = 0;
2405
2406 if (emacs_fd_to_int (ifd) != -1) 2397 if (emacs_fd_to_int (ifd) != -1)
2407 { 2398 {
2408 for (; newsize < insize; newsize += copied) 2399 for (ssize_t copied; ; newsize += copied)
2409 { 2400 {
2410 /* Copy at most COPY_MAX bytes at a time; this is min 2401 /* Copy at most COPY_MAX bytes at a time; this is min
2411 (PTRDIFF_MAX, SIZE_MAX) truncated to a value that is 2402 (SSIZE_MAX, SIZE_MAX) truncated to a value that is
2412 surely aligned well. */ 2403 surely aligned well. */
2413 ssize_t ssize_max = TYPE_MAXIMUM (ssize_t); 2404 ssize_t copy_max = min (SSIZE_MAX, SIZE_MAX) >> 30 << 30;
2414 ptrdiff_t copy_max = min (ssize_max, SIZE_MAX) >> 30 << 30;
2415 off_t intail = insize - newsize;
2416 ptrdiff_t len = min (intail, copy_max);
2417 copied = copy_file_range (emacs_fd_to_int (ifd), NULL, 2405 copied = copy_file_range (emacs_fd_to_int (ifd), NULL,
2418 ofd, NULL, len, 0); 2406 ofd, NULL, copy_max, 0);
2419 if (copied <= 0) 2407 if (copied <= 0)
2420 break; 2408 break;
2421 maybe_quit (); 2409 maybe_quit ();
@@ -2423,32 +2411,24 @@ permissions. */)
2423 } 2411 }
2424#endif /* MSDOS */ 2412#endif /* MSDOS */
2425 2413
2426 /* Fall back on read+write if copy_file_range failed, or if the 2414 /* Follow up with read+write regardless of any copy_file_range failure.
2427 input is empty and so could be a /proc file, or if ifd is an 2415 Many copy_file_range implementations fail for no good reason,
2428 invention of android.c. read+write will either succeed, or 2416 or "succeed" even when they did nothing (e.g., in /proc files).
2429 report an error more precisely than copy_file_range 2417 Also, if read+write fails it will report an error more
2430 would. */ 2418 precisely than copy_file_range would. */
2431 if (newsize != insize || insize == 0) 2419 char buf[MAX_ALLOCA];
2432 {
2433 char buf[MAX_ALLOCA];
2434 2420
2435 for (; (copied = emacs_fd_read (ifd, buf, sizeof buf)); 2421 for (ptrdiff_t copied;
2436 newsize += copied) 2422 (copied = emacs_fd_read (ifd, buf, sizeof buf));
2437 { 2423 newsize += copied)
2438 if (copied < 0) 2424 {
2439 report_file_error ("Read error", file); 2425 if (copied < 0)
2440 if (emacs_write_quit (ofd, buf, copied) != copied) 2426 report_file_error ("Read error", file);
2441 report_file_error ("Write error", newname); 2427 if (emacs_write_quit (ofd, buf, copied) != copied)
2442 } 2428 report_file_error ("Write error", newname);
2443 } 2429 }
2444 } 2430 }
2445 2431
2446 /* Truncate any existing output file after writing the data. This
2447 is more likely to work than truncation before writing, if the
2448 file system is out of space or the user is over disk quota. */
2449 if (newsize < oldsize && ftruncate (ofd, newsize) != 0)
2450 report_file_error ("Truncating output file", newname);
2451
2452#ifndef MSDOS 2432#ifndef MSDOS
2453 /* Preserve the original file permissions, and if requested, also its 2433 /* Preserve the original file permissions, and if requested, also its
2454 owner and group. */ 2434 owner and group. */