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: Leah Neukirchen <leah <at> vuxu.org>
Subject: bug#79139: closed (Re: bug#79139: cp --reflink truncates sparse
 files on ZFS)
Date: Sat, 02 Aug 2025 00:18:02 +0000
[Message part 1 (text/plain, inline)]
Your bug report

#79139: cp --reflink truncates sparse files on ZFS

which was filed against the coreutils package, has been closed.

The explanation is attached below, along with your original report.
If you require more details, please reply to 79139 <at> debbugs.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: 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 3 (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)]
[Message part 6 (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/



This bug report was last modified 10 days ago.

Previous Next


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