The copy loop in sparse_copy() (src/uu/cp/src/platform/linux.rs) advances only by the bytes read and never handles a zero-byte read:
while current_offset < size {
let this_read = src_file.read(&mut buf)?;
...
current_offset += this_read;
}
size is captured once from the initial metadata(). If the source shrinks afterwards, read() hits EOF and returns 0 before current_offset reaches size, so the loop spins forever at 100% CPU.
Steps to reproduce
$ truncate -s 2G big
$ ( sleep 0.2; truncate -s 0 big ) & # shrink the source mid-copy
$ cp --reflink=never --sparse=always big copy
# never returns; cp pins a core. Interrupt with Ctrl-C.
It's a race window, so adjust the sleep / size if it completes; with a larger source it's easy to hit. GNU cp finishes.
Expected
cp stops at EOF instead of looping forever.
Reported by V12 (finding F-65898).
The copy loop in
sparse_copy()(src/uu/cp/src/platform/linux.rs) advances only by the bytes read and never handles a zero-byte read:sizeis captured once from the initialmetadata(). If the source shrinks afterwards,read()hits EOF and returns0beforecurrent_offsetreachessize, so the loop spins forever at 100% CPU.Steps to reproduce
It's a race window, so adjust the
sleep/ size if it completes; with a larger source it's easy to hit. GNUcpfinishes.Expected
cpstops at EOF instead of looping forever.Reported by V12 (finding F-65898).