Subject: bug#8411: due to missing sync even on 2.6.39, cp fails to copy an odd file

From: Jim Meyering <[email-masked]>
Date: Sat, 02 Apr 2011 13:16:39 +0200

Hi Pádraig,

As of this change,

    copy: with fiemap copy, only sync when needed
    http://git.savannah.gnu.org/cgit/coreutils.git/commit/?id=f69876e55

fiemap copy with extents beyond EOF can fail on ext4 even with
Fedora 15 (2.6.38) and rawhide's 2.6.39 kernel.

Here we construct an odd file.  First, preallocate 10MB of space,
and then write 5KiB of random data into the beginning of that:

    $ fallocate -l 10000000 -n k
    $ dd count=10 if=/dev/urandom conv=notrunc iflag=fullblock of=k

However, when we try to copy "k", we get a file, "k2" of the
expected size, but with only NUL bytes for contents:

    $ strace -o log ./cp k k2
    $ cmp -l k k2|head -2
       1  60   0
       2 164   0
    $ uname -r
    2.6.39-0.rc0.git11.0.fc16.x86_64

The strace showed no reads were required, so to make that test succeed,
we would have to sync k's metadata to disk before invoking cp.

This change (to make cp require FIEMAP sync for kernels before 2.6.40)
solves the problem, but I hope there's a better way...

diff --git a/src/extent-scan.c b/src/extent-scan.c
index c0a5de6..1d9d06b 100644
--- a/src/extent-scan.c
+++ b/src/extent-scan.c
@@ -50,7 +50,7 @@ extent_need_sync (void)
            unsigned long val;
            if (xstrtoul (name.release + 4, NULL, 10, &val, NULL) == LONGINT_OK)
              {
-               if (val < 38)
+               if (val < 40)
                  need_sync = 1;
              }
         }

--------------------------------
The above is part of Pádraig's new fiemap-empty test,
which fails on both F15 and rawhide:

  FAIL: cp/fiemap-empty (exit: 1)
  ...
  + fallocate -l 10000000 -n unwritten.withdata
  + dd count=10 if=/dev/urandom conv=notrunc iflag=fullblock of=unwritten.withdata
  10+0 records in
  10+0 records out
  5120 bytes (5.1 kB) copied, 0.00248136 s, 2.1 MB/s
  + cp unwritten.withdata cp.test
  ++ stat -c %s unwritten.withdata
  ++ stat -c %s cp.test
  + test 5120 = 5120
  + cmp unwritten.withdata cp.test
  unwritten.withdata cp.test differ: char 1, line 1
  + fail=1