>From 4b2a532a87ec2a21afc205b83db4a779664ccd20 Mon Sep 17 00:00:00 2001 From: Paul Eggert Date: Wed, 6 Jul 2016 02:42:58 +0200 Subject: [PATCH] copy-file now uses GNU/Linux file cloning >From a suggestion by Kieran Colford in: http://lists.gnu.org/archive/html/emacs-devel/2016-07/msg00191.html * configure.ac: Check for linux/fs.h. * src/fileio.c [HAVE_LINUX_FS_H]: Include sys/ioctl.h and linux/fs.h. (clone_file): New function. (Fcopy_file): Use it. --- configure.ac | 1 + src/fileio.c | 64 ++++++++++++++++++++++++++++++++++++++++++------------------ 2 files changed, 46 insertions(+), 19 deletions(-) diff --git a/configure.ac b/configure.ac index aaddfcd..1798fa3 100644 --- a/configure.ac +++ b/configure.ac @@ -1636,6 +1636,7 @@ AC_DEFUN dnl checks for header files AC_CHECK_HEADERS_ONCE( + linux/fs.h malloc.h sys/systeminfo.h sys/sysinfo.h diff --git a/src/fileio.c b/src/fileio.c index b1f9d3c..c2e3a18 100644 --- a/src/fileio.c +++ b/src/fileio.c @@ -52,6 +52,11 @@ along with GNU Emacs. If not, see . */ #include "region-cache.h" #include "frame.h" +#ifdef HAVE_LINUX_FS_H +# include +# include +#endif + #ifdef WINDOWSNT #define NOMINMAX 1 #include @@ -1828,6 +1833,24 @@ barf_or_query_if_file_exists (Lisp_Object absname, bool known_to_exist, } } +/* Copy all data to DEST from SOURCE if possible. Return true if OK. */ +static bool +clone_file (int dest, int source) +{ +#ifdef FICLONE + /* FIXME: Might this ioctl take a long time if the file is very + large? Is the ioctl interruptible? If so, what happens if the + ioctl is interrupted by a signal? Might it partially copy the + file? */ + /* FIXME: Does this ioctl truncate the output file to be the same + size as the input file, if the output file already exists and is + larger than the input file? */ + if (ioctl (dest, FICLONE, source) == 0) + return true; +#endif + return false; +} + DEFUN ("copy-file", Fcopy_file, Scopy_file, 2, 6, "fCopy file: \nGCopy %s to file: \np\nP", doc: /* Copy FILE to NEWNAME. Both args must be strings. @@ -1988,28 +2011,31 @@ permissions. */) oldsize = out_st.st_size; } - immediate_quit = 1; - QUIT; - while (true) + if (! clone_file (ofd, ifd)) { - 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; - } + immediate_quit = 1; + QUIT; + 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); + /* 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; + immediate_quit = 0; + } #ifndef MSDOS /* Preserve the original file permissions, and if requested, also its -- 2.5.5