QFileSystemEngine/Unix: work around FreeBSD bug in copy_file_range(2)

Amends commit 9b006eb91a, which introduced
the use of this function. When I tested this with FreeBSD 14.2, this
code worked; now with 14.3 it doesn't, because on output the srcoffset
and dstoffset variables are still zero, in spite of having copied
content. That caused the copy_file_range() loop to copy the same files
over and over again, never exiting.

I've tracked this down to a change in 14.3[1] which showed that if both
inoffp and outoffp were non-NULL, it wouldn't update. FreeBSD
maintainers report this is already fixed[2] and will be made available
soon.

tst_qfile now passes again on FreeBSD.

strace output on Linux:
ioctl(5, BTRFS_IOC_CLONE or FICLONE, 4) = -1 EOPNOTSUPP (Operation not supported)
copy_file_range(4, [0], 5, NULL, 9223372036854775807, 0) = 13587
copy_file_range(4, [13587], 5, NULL, 9223372036854775807, 0) = 0

truss output on FreeBSD:
copy_file_range(0x4,0x32fb9588e360,0x5,0x0,0x7fffffffffffffff,0x0) = 624 (0x270)
copy_file_range(0x4,0x32fb9588e360,0x5,0x0,0x7fffffffffffffff,0x0) = 0 (0x0)

No changelog because the content is new in 6.10.

[1] 1d2fd8c9cf
[2] 4046ad6bb0

Pick-to: 6.10
Fixes: QTBUG-138570
Change-Id: I31ddcefb63bb738f0cc0fffd75d9f68030952ed5
Reviewed-by: Ahmad Samir <a.samirh78@gmail.com>
This commit is contained in:
Thiago Macieira 2025-08-20 11:15:24 -07:00
parent 66a0fa62bf
commit 35dbd1c3f4
1 changed files with 1 additions and 2 deletions

View File

@ -1178,10 +1178,9 @@ auto QFileSystemEngine::cloneFile(int srcfd, int dstfd, const QFileSystemMetaDat
// across mountpoints, Linux currently (6.12) can only if the source and // across mountpoints, Linux currently (6.12) can only if the source and
// destination mountpoints are the same filesystem type. // destination mountpoints are the same filesystem type.
QT_OFF_T srcoffset = 0; QT_OFF_T srcoffset = 0;
QT_OFF_T dstoffset = 0;
ssize_t copied; ssize_t copied;
do { do {
copied = ::copy_file_range(srcfd, &srcoffset, dstfd, &dstoffset, SSIZE_MAX, 0); copied = ::copy_file_range(srcfd, &srcoffset, dstfd, nullptr, SSIZE_MAX, 0);
} while (copied > 0 || (copied < 0 && errno == EINTR)); } while (copied > 0 || (copied < 0 && errno == EINTR));
if (copied == 0) if (copied == 0)
return TriStateResult::Success; // EOF -> success return TriStateResult::Success; // EOF -> success