copy: with fiemap copy, only sync when needed
authorPádraig Brady <P@draigBrady.com>
Wed, 30 Mar 2011 21:50:05 +0000 (22:50 +0100)
committerPádraig Brady <P@draigBrady.com>
Thu, 31 Mar 2011 15:46:42 +0000 (16:46 +0100)
* src/extent-scan.h (struct extent_scan): Add the fm_flags member to
pass to the fiemap scan.
* src/extent-scan.c (extent_need_sync): A new function used to
detect Linux kernels before 2.6.38.
(extent_scan_init): Add FIEMAP_FLAG_SYNC when needed.
* tests/cp/sparse-fiemap: Adjust comment.
* NEWS: Mention the change in behavior.
Indirectly suggested by Mike Frysinger

NEWS
src/extent-scan.c
src/extent-scan.h
tests/cp/sparse-fiemap

diff --git a/NEWS b/NEWS
index e93f013..6d66355 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -28,6 +28,10 @@ GNU coreutils NEWS                                    -*- outline -*-
 
 ** Changes in behavior
 
+  cp now avoids syncing files when possible, when doing a FIEMAP copy.
+  The sync in only needed on Linux kernels before 2.6.38.
+  [The sync was introduced in coreutils-8.10]
+
   df now aligns columns consistently, and no longer wraps entries
   with longer device identifiers, over two lines.
 
index b9520b7..f10d8e0 100644 (file)
 #include <stdio.h>
 #include <sys/types.h>
 #include <sys/ioctl.h>
+#include <sys/utsname.h>
 #include <assert.h>
 
 #include "system.h"
 #include "extent-scan.h"
+#include "xstrtol.h"
 
 #ifndef HAVE_FIEMAP
 # include "fiemap.h"
 #endif
 
+/* Work around Linux kernel issues on BTRFS and EXT4 before 2.6.38.
+   FIXME: remove in 2013, or whenever we're pretty confident
+   that the offending, unpatched kernels are no longer in use.  */
+static bool
+extent_need_sync (void)
+{
+  static int need_sync = -1;
+
+  if (need_sync == -1)
+    {
+      struct utsname name;
+      need_sync = 0; /* No workaround by default.  */
+
+#ifdef __linux__
+      if (uname (&name) != -1 && strncmp (name.release, "2.6.", 4) == 0)
+        {
+           unsigned long val;
+           if (xstrtoul (name.release + 4, NULL, 10, &val, NULL) == LONGINT_OK)
+             {
+               if (val < 38)
+                 need_sync = 1;
+             }
+        }
+#endif
+    }
+
+  return need_sync;
+}
+
 /* Allocate space for struct extent_scan, initialize the entries if
    necessary and return it as the input argument of extent_scan_read().  */
 extern void
@@ -39,6 +70,7 @@ extent_scan_init (int src_fd, struct extent_scan *scan)
   scan->scan_start = 0;
   scan->initial_scan_failed = false;
   scan->hit_final_extent = false;
+  scan->fm_flags = extent_need_sync () ? FIEMAP_FLAG_SYNC : 0;
 }
 
 #ifdef __linux__
@@ -62,7 +94,7 @@ extent_scan_read (struct extent_scan *scan)
   memset (&fiemap_buf, 0, sizeof fiemap_buf);
 
   fiemap->fm_start = scan->scan_start;
-  fiemap->fm_flags = FIEMAP_FLAG_SYNC;
+  fiemap->fm_flags = scan->fm_flags;
   fiemap->fm_extent_count = count;
   fiemap->fm_length = FIEMAP_MAX_OFFSET - scan->scan_start;
 
index 4724b25..8728515 100644 (file)
@@ -41,6 +41,9 @@ struct extent_scan
   /* Next scan start offset.  */
   off_t scan_start;
 
+  /* Flags to use for scan.  */
+  uint32_t fm_flags;
+
   /* How many extent info returned for a scan.  */
   uint32_t ei_count;
 
index 73448ec..bac2890 100755 (executable)
@@ -69,8 +69,8 @@ for i in $(seq 1 2 21); do
           -e 'for (1..'$j') { sysseek (*F, $n, 1)' \
           -e '&& syswrite (*F, chr($_)x$n) or die "$!"}' > j1 || fail=1
 
-    # Note there is an implicit sync performed by cp to
-    # work around bugs in EXT4 and BTRFS before Linux 2.6.38
+    # Note there is an implicit sync performed by cp on Linux kernels
+    # before 2.6.38 to work around bugs in EXT4 and BTRFS.
     # Note also the -s parameter to the filefrag commands below
     # for the same reasons.
     cp --sparse=always j1 j2 || fail=1