strace cp --sparse=always dd dd-sparse
[extraneous stuff skipped]
openat(AT_FDCWD, "dd", O_RDONLY) = 3
fstat(3, {st_mode=S_IFREG|0755, st_size=134248, ...}) = 0
openat(AT_FDCWD, "dd-sparse", O_WRONLY|O_CREAT|O_EXCL, 0755) = 4
ioctl(4, BTRFS_IOC_CLONE or FICLONE, 3) = 0
close(4) = 0
close(3) = 0
It makes no attempt to look for sparse regions in the file, just uses
FICLONE (which succeeds because it's on XFS).
I worked around this by copying to /tmp, which is on a different
filesystem (tmpfs):
openat(AT_FDCWD, "dd", O_RDONLY) = 3
fstat(3, {st_mode=S_IFREG|0755, st_size=134248, ...}) = 0
openat(AT_FDCWD, "/tmp/dd-sparse", O_WRONLY|O_CREAT|O_EXCL, 0755) = 4
ioctl(4, BTRFS_IOC_CLONE or FICLONE, 3) = -1 EXDEV (Invalid cross-device link)
fstat(4, {st_mode=S_IFREG|0755, st_size=0, ...}) = 0
fadvise64(3, 0, 0, POSIX_FADV_SEQUENTIAL) = 0
read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0\267\0\1\0\0\0@>\0\0\0\0\0\0"..., 131072) = 131072
write(4, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0\267\0\1\0\0\0@>\0\0\0\0\0\0"..., 81920) = 81920
lseek(4, 45056, SEEK_CUR) = 126976
fallocate(4, FALLOC_FL_KEEP_SIZE|FALLOC_FL_PUNCH_HOLE, 81920, 45056) = 0
write(4, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 4096) = 4096
read(3, "\320\34\0\0\0\0\0\0\320\34\0\0\0\0\0\0\320\34\0\0\0\0\0\0\320\34\0\0\0\0\0\0"..., 131072) = 3176
write(4, "\320\34\0\0\0\0\0\0\320\34\0\0\0\0\0\0\320\34\0\0\0\0\0\0\320\34\0\0\0\0\0\0"..., 3176) = 3176
read(3, "", 131072) = 0
close(4) = 0
close(3) = 0
Without looking at the source code, it seems likely that cp blindly
tries FICLONE without checking to see whether the sparse flag is set.
I suggest that setting --sparse=always should disable the FICLONE
optimisation.