GNU bug report logs -
#79139
cp --reflink truncates sparse files on ZFS
Previous Next
Full log
Message #8 received at 79139 <at> debbugs.gnu.org (full text, mbox):
On 01/08/2025 16:00, Leah Neukirchen wrote:
> 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.
I haven't tried to repro yet.
The syscalls look OK, so the only thing I can think of
is that last large copy_file_range() syscall didn't propagate appropriately back up to cp,
as there is a fallback read() which should not have occurred.
What is the destination file system type?
It would give us a bit more info if you gave the output from cp
when run with the --debug option.
thank you,
Padraig
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.