Package: coreutils
Version: 9.4-3.1

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.

Reply via email to