all messages for Emacs-related lists mirrored at yhetil.org
 help / color / mirror / code / Atom feed
From: Paul Eggert <eggert@cs.ucla.edu>
To: Dmitry Antipov <dmantipov@yandex.ru>, 23904@debbugs.gnu.org
Cc: sbaugh@catern.com, Kieran Colford <kieran@kcolford.com>
Subject: bug#23904: Btrfs clone support in copy operations
Date: Wed, 6 Jul 2016 13:48:26 +0200	[thread overview]
Message-ID: <577CF00A.80707@cs.ucla.edu> (raw)
In-Reply-To: <7d9c224b-e525-de33-426e-15bc328e7108@yandex.ru>

[-- Attachment #1: Type: text/plain, Size: 837 bytes --]

On 07/06/2016 04:45 AM, Dmitry Antipov wrote:
> IMHO this should use FICLONERANGE in a loop where you can
> handle errors and use process_pending_signals as usual. 

Thanks, that suggestion should resolve the FIXME about how FICLONE 
behaves when the output file is already larger than the input. However, 
what should the clone chunk size be,each time through the loop? I don't 
use btrfs so I don't know what a good size would be.

A downside of the suggestion is that the file copy won't be atomic. 
However, the existing code isn't atomic, so this is nothing new.

Revised patch attached. Basically untested, since I don't use any file 
systems that support FIOCLONERANGE. The patch still has FIXMEs for what 
happens if the FIOCLONERANGE ioctl is interrupted by a signal, and for a 
good clone chunk size (the patch guesses 1 GiB).

[-- Attachment #2: 0001-copy-file-now-uses-GNU-Linux-file-cloning.patch --]
[-- Type: text/x-patch, Size: 3674 bytes --]

From 9c60f6f603563c335737102fc54609b837f5d02a Mon Sep 17 00:00:00 2001
From: Paul Eggert <eggert@cs.ucla.edu>
Date: Wed, 6 Jul 2016 12:44:46 +0200
Subject: [PATCH] copy-file now uses GNU/Linux file cloning

From a suggestion by Kieran Colford (see Bug#23904).
* configure.ac: Check for linux/fs.h.
* src/fileio.c [HAVE_LINUX_FS_H]: Include sys/ioctl.h and linux/fs.h.
(clone_file_range): New function.
(Fcopy_file): Use it.
---
 configure.ac |  1 +
 src/fileio.c | 62 +++++++++++++++++++++++++++++++++++++++++++++++-------------
 2 files changed, 50 insertions(+), 13 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..f980247 100644
--- a/src/fileio.c
+++ b/src/fileio.c
@@ -52,6 +52,11 @@ along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.  */
 #include "region-cache.h"
 #include "frame.h"
 
+#ifdef HAVE_LINUX_FS_H
+# include <sys/ioctl.h>
+# include <linux/fs.h>
+#endif
+
 #ifdef WINDOWSNT
 #define NOMINMAX 1
 #include <windows.h>
@@ -1828,6 +1833,23 @@ barf_or_query_if_file_exists (Lisp_Object absname, bool known_to_exist,
     }
 }
 
+/* Copy data to DEST from SOURCE if possible, starting at byte offset
+   OFF and continuing for LENGTH bytes.  Return true if OK.  */
+static bool
+clone_file_range (int dest, int source, off_t off, off_t length)
+{
+#ifdef FICLONERANGE
+  /* FIXME: Is the ioctl interruptible?  If so, what happens if the
+     ioctl is interrupted by a signal?  Might it partially copy the
+     range?  */
+  struct file_clone_range range = { src_fd: source, src_offset: off,
+				    src_length: length, dest_offset: off };
+  if (ioctl (dest, FICLONERANGE, &range) == 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.
@@ -1974,7 +1996,7 @@ permissions.  */)
 
   record_unwind_protect_int (close_file_unwind, ofd);
 
-  off_t oldsize = 0, newsize = 0;
+  off_t oldsize = 0, newsize;
 
   if (already_exists)
     {
@@ -1990,18 +2012,32 @@ permissions.  */)
 
   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;
-    }
+
+  off_t insize = st.st_size;
+  /* FIXME: is 1 GiB a good number?  */
+  off_t max_clone_chunk_size = 1 << 30;
+  off_t chunk_size = min (insize, max_clone_chunk_size);
+
+  if (clone_file_range (ofd, ifd, 0, chunk_size))
+    for (newsize = chunk_size; newsize < insize; newsize += chunk_size)
+      {
+	chunk_size = min (insize - newsize, max_clone_chunk_size);
+	if (! clone_file_range (ofd, ifd, newsize, chunk_size))
+	  report_file_error ("Clone error", file);
+      }
+  else
+    for (newsize = 0; ; newsize += chunk_size)
+      {
+	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);
+	chunk_size = n;
+      }
 
   /* Truncate any existing output file after writing the data.  This
      is more likely to work than truncation before writing, if the
-- 
2.5.5


  parent reply	other threads:[~2016-07-06 11:48 UTC|newest]

Thread overview: 17+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2016-07-05 19:06 Btrfs clone support in copy operations Kieran Colford
2016-07-05 23:39 ` sbaugh
2016-07-06  0:57   ` Paul Eggert
2016-07-06  0:49 ` bug#23904: " Paul Eggert
2016-07-06  2:45   ` Dmitry Antipov
2016-07-06  5:52     ` Helmut Eller
2016-07-06 11:48     ` Paul Eggert [this message]
2016-07-06 14:58       ` Eli Zaretskii
2016-07-06 15:08         ` Paul Eggert
2016-07-06 15:26           ` Eli Zaretskii
2016-07-06 17:32             ` Paul Eggert
2016-09-11  2:16               ` Paul Eggert
2016-09-11 15:25                 ` Eli Zaretskii
2016-09-11 17:17                   ` Paul Eggert
2016-09-11 19:20                     ` Eli Zaretskii
2016-09-11 22:44                       ` Paul Eggert
2016-09-12  2:35                         ` Eli Zaretskii

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=577CF00A.80707@cs.ucla.edu \
    --to=eggert@cs.ucla.edu \
    --cc=23904@debbugs.gnu.org \
    --cc=dmantipov@yandex.ru \
    --cc=kieran@kcolford.com \
    --cc=sbaugh@catern.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
Code repositories for project(s) associated with this external index

	https://git.savannah.gnu.org/cgit/emacs.git
	https://git.savannah.gnu.org/cgit/emacs/org-mode.git

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.