GNU bug report logs - #79139
cp --reflink truncates sparse files on ZFS

Previous Next

Package: coreutils;

Reported by: Leah Neukirchen <leah <at> vuxu.org>

Date: Fri, 1 Aug 2025 15:02:02 UTC

Severity: normal

Done: Paul Eggert <eggert <at> cs.ucla.edu>

Full log


View this message in rfc822 format

From: help-debbugs <at> gnu.org (GNU bug Tracking System)
To: Paul Eggert <eggert <at> cs.ucla.edu>
Cc: tracker <at> debbugs.gnu.org
Subject: bug#79139: closed (cp --reflink truncates sparse files on ZFS)
Date: Sat, 02 Aug 2025 00:18:02 +0000
[Message part 1 (text/plain, inline)]
Your message dated Fri, 1 Aug 2025 17:17:16 -0700
with message-id <322feded-cf4b-4599-8e4a-21f8defd0f47 <at> cs.ucla.edu>
and subject line Re: bug#79139: cp --reflink truncates sparse files on ZFS
has caused the debbugs.gnu.org bug report #79139,
regarding cp --reflink truncates sparse files on ZFS
to be marked as done.

(If you believe you have received this mail in error, please contact
help-debbugs <at> gnu.org.)


-- 
79139: https://debbugs.gnu.org/cgi/bugreport.cgi?bug=79139
GNU Bug Tracking System
Contact help-debbugs <at> gnu.org with problems
[Message part 2 (message/rfc822, inline)]
From: Leah Neukirchen <leah <at> vuxu.org>
To: bug-coreutils <at> gnu.org
Subject: cp --reflink truncates sparse files on ZFS
Date: Fri, 01 Aug 2025 17:00:36 +0200
Hello,

I found the following issue with coreutils 9.7, Linux 6.12.40-1-lts,
zfs 2.3.3 on Arch x86_64, glibc 2.42:

Copying a file with sparse holes using "cp --reflink=auto" truncates
the file before the final segment.  The relevant strace is:

openat(AT_FDCWD, "celestis.img", O_RDONLY|O_PATH|O_DIRECTORY) = -1 ENOENT (No such file or directory)
newfstatat(AT_FDCWD, "/.zfs/snapshot/pre-fixup/var/lib/libvirt/images/celestis.img", {st_mode=S_IFREG|0644,>
openat(AT_FDCWD, "/.zfs/snapshot/pre-fixup/var/lib/libvirt/images/celestis.img", O_RDONLY) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=137438953472, ...}) = 0
openat(AT_FDCWD, "celestis.img", O_WRONLY|O_CREAT|O_EXCL, 0644) = 4
ioctl(4, BTRFS_IOC_CLONE or FICLONE, 3) = -1 EXDEV (Invalid cross-device link)
fstat(4, {st_mode=S_IFREG|0644, st_size=0, ...}) = 0
lseek(3, 0, SEEK_DATA)                  = 0
fadvise64(3, 0, 0, POSIX_FADV_SEQUENTIAL) = 0
lseek(3, 0, SEEK_HOLE)                  = 131072
lseek(3, 0, SEEK_SET)                   = 0
copy_file_range(3, NULL, 4, NULL, 131072, 0) = 131072
lseek(3, 131072, SEEK_DATA)             = 1048576
lseek(3, 1048576, SEEK_HOLE)            = 1179648
lseek(3, 1048576, SEEK_SET)             = 1048576
lseek(4, 917504, SEEK_CUR)              = 1048576
copy_file_range(3, NULL, 4, NULL, 131072, 0) = 131072
lseek(3, 1179648, SEEK_DATA)            = 4194304
lseek(3, 4194304, SEEK_HOLE)            = 16646144
lseek(3, 4194304, SEEK_SET)             = 4194304
lseek(4, 3014656, SEEK_CUR)             = 4194304
copy_file_range(3, NULL, 4, NULL, 12451840, 0) = 12451840
lseek(3, 16646144, SEEK_DATA)           = 134217728
lseek(3, 134217728, SEEK_HOLE)          = 137438953472
lseek(3, 134217728, SEEK_SET)           = 134217728
lseek(4, 117571584, SEEK_CUR)           = 134217728
copy_file_range(3, NULL, 4, NULL, 137304735744, 0) = 137304735744
mmap(NULL, 270336, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x76c5df0ce000
read(3, "", 262144)                     = 0
ftruncate(4, 134217728)                 = 0
close(4)                                = 0
close(3)                                = 0

As we can see, there's a hole from 16646144 to 134217728, then data up
to the end at 137438953472 (= the total file size).  Both fd are thus
moved to 134217728, and a copy_file_range for the rest of the file is
issued and successful.

However, in the end the file is truncated to the first 128MB... why?

For comparison, a plain cat simply does this:

fstat(1, {st_mode=S_IFREG|0644, st_size=0, ...}) = 0
openat(AT_FDCWD, "/.zfs/snapshot/pre-fixup/var/lib/libvirt/images/celestis.img", O_RDONLY) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=137438953472, ...}) = 0
fadvise64(3, 0, 0, POSIX_FADV_SEQUENTIAL) = 0
copy_file_range(3, NULL, 1, NULL, 9223372035781033984, 0) = 137438953472
mmap(NULL, 270336, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7e7af83a9000
read(3, "", 262144)                     = 0
munmap(0x7e7af83a9000, 270336)          = 0
close(3)                                = 0
close(1)                                = 0
close(2)                                = 0

This works correctly and the source and destination agree in the end.
Likewise for xcp(1), which uses copy_file_range in 1MB blocks by
default and does not care for holes.

Thus I think this is a logic bug in cp and not a ZFS issue.

Do not hesitate to contact me if you inquire further details.

Thanks,
-- 
Leah Neukirchen  <leah <at> vuxu.org>  https://leahneukirchen.org/


[Message part 3 (message/rfc822, inline)]
From: Paul Eggert <eggert <at> cs.ucla.edu>
To: Collin Funk <collin.funk1 <at> gmail.com>
Cc: 79139-done <at> debbugs.gnu.org,
 Pádraig Brady <P <at> draigBrady.com>,
 Leah Neukirchen <leah <at> vuxu.org>
Subject: Re: bug#79139: cp --reflink truncates sparse files on ZFS
Date: Fri, 1 Aug 2025 17:17:16 -0700
[Message part 4 (text/plain, inline)]
On 2025-08-01 15:05, Collin Funk wrote:
> I was hoping that file could be made a tiny stub, due to the
> workarounds for Linux 4.19 being mostly unnecessary now that it is EOL.
> But now we have a new problem to deal with. :)

That we do. But we can more thorougly stubify the old Linux kernel bug 
workaround while we're in the neighborhood. Probably best not to remove 
it entirely as RHEL 8 still uses the no-longer-supported kernel.

To do that, I installed the attached patches into Gnulib and propagated 
them into coreutils.

Boldly closing the bug report. Thanks, Leah, for reporting it. That one 
was quite a whopper.
[0001-copy-file-range-tune-for-more-modern-kernels.patch (text/x-patch, attachment)]
[0002-copy-file-range-work-around-glibc-bug-33245.patch (text/x-patch, attachment)]

This bug report was last modified 9 days ago.

Previous Next


GNU bug tracking system
Copyright (C) 1999 Darren O. Benham, 1997,2003 nCipher Corporation Ltd, 1994-97 Ian Jackson.