>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