diff --git a/src/corelib/configure.cmake b/src/corelib/configure.cmake index 799c1e8b8bc..f9fb7b9dfab 100644 --- a/src/corelib/configure.cmake +++ b/src/corelib/configure.cmake @@ -143,6 +143,19 @@ int pipes[2]; } ") +# copy_file_range +qt_config_compile_test(copy_file_range + LABEL "copy_file_range()" + CODE +"#include + +int main() +{ + off_t off_in = 0, off_out = 1024; + return copy_file_range(0, &off_in, 1, &off_out, 2147483647, 0) != 0; +} +") + # Check if __cxa_thread_atexit{,_impl} are present in the C library (hence why # PROJECT_PATH instead of CODE for C++). Either one suffices to disable # FEATURE_broken_threadlocal_dtors. See details in qthread_unix.cpp. @@ -615,6 +628,11 @@ qt_feature("close_range" PRIVATE CONDITION QT_FEATURE_process AND TEST_close_range AUTODETECT UNIX ) +qt_feature("copy_file_range" PRIVATE + LABEL "copy_file_range()" + CONDITION QT_FEATURE_process AND TEST_copy_file_range + AUTODETECT UNIX AND NOT DARWIN +) qt_feature("doubleconversion" PRIVATE LABEL "DoubleConversion" ) diff --git a/src/corelib/global/minimum-linux_p.h b/src/corelib/global/minimum-linux_p.h index d52df474fb5..86226326fe5 100644 --- a/src/corelib/global/minimum-linux_p.h +++ b/src/corelib/global/minimum-linux_p.h @@ -41,6 +41,7 @@ QT_BEGIN_NAMESPACE * - accept4 2.6.28 * - renameat2 3.16 QT_CONFIG(renameat2) * - getrandom 3.17 QT_CONFIG(getentropy) + * - copy_file_range 4.5 QT_CONFIG(copy_file_range) * - statx 4.11 STATX_BASIC_STATS */ @@ -50,6 +51,10 @@ QT_BEGIN_NAMESPACE # define QT_ELF_NOTE_OS_MAJOR 4 # define QT_ELF_NOTE_OS_MINOR 11 # define QT_ELF_NOTE_OS_PATCH 0 +#elif QT_CONFIG(copy_file_range) +# define QT_ELF_NOTE_OS_MAJOR 4 +# define QT_ELF_NOTE_OS_MINOR 5 +# define QT_ELF_NOTE_OS_PATCH 0 #elif QT_CONFIG(getentropy) # define QT_ELF_NOTE_OS_MAJOR 3 # define QT_ELF_NOTE_OS_MINOR 17 @@ -59,7 +64,6 @@ QT_BEGIN_NAMESPACE # define QT_ELF_NOTE_OS_MINOR 16 # define QT_ELF_NOTE_OS_PATCH 0 #else - # define QT_ELF_NOTE_OS_MAJOR 2 # define QT_ELF_NOTE_OS_MINOR 6 # define QT_ELF_NOTE_OS_PATCH 28 diff --git a/src/corelib/global/qconfig-bootstrapped.h b/src/corelib/global/qconfig-bootstrapped.h index 6212fea25ca..41ac6f42e99 100644 --- a/src/corelib/global/qconfig-bootstrapped.h +++ b/src/corelib/global/qconfig-bootstrapped.h @@ -47,6 +47,7 @@ #define QT_FEATURE_cborstreamwriter 1 #define QT_FEATURE_commandlineparser 1 #define QT_NO_COMPRESS +#define QT_FEATURE_copy_file_range -1 #define QT_FEATURE_cxx17_filesystem -1 #define QT_NO_DATASTREAM #define QT_FEATURE_datestring 1 diff --git a/src/corelib/io/qfilesystemengine_unix.cpp b/src/corelib/io/qfilesystemengine_unix.cpp index a040a60de29..a5eb60ca933 100644 --- a/src/corelib/io/qfilesystemengine_unix.cpp +++ b/src/corelib/io/qfilesystemengine_unix.cpp @@ -1096,8 +1096,41 @@ auto QFileSystemEngine::cloneFile(int srcfd, int dstfd, const QFileSystemMetaDat // first, try FICLONE (only works on regular files and only on certain fs) if (::ioctl(dstfd, FICLONE, srcfd) == 0) return TriStateResult::Success; +#elif defined(Q_OS_DARWIN) + // try fcopyfile + if (fcopyfile(srcfd, dstfd, nullptr, COPYFILE_DATA | COPYFILE_STAT) == 0) + return TriStateResult::Success; + switch (errno) { + case ENOTSUP: + case ENOMEM: + return TriStateResult::NotSupported; // let QFile try + } + return TriStateResult::Failed; +#endif - // Second, try sendfile (it can send to some special types too). +#if QT_CONFIG(copy_file_range) + // Second, try copy_file_range. Tested on Linux & FreeBSD: FreeBSD can copy + // across mountpoints, Linux currently (6.12) can only if the source and + // destination mountpoints are the same filesystem type. + QT_OFF_T srcoffset = 0; + QT_OFF_T dstoffset = 0; + ssize_t copied; + do { + copied = ::copy_file_range(srcfd, &srcoffset, dstfd, &dstoffset, SSIZE_MAX, 0); + } while (copied > 0 || (copied < 0 && errno == EINTR)); + if (copied == 0) + return TriStateResult::Success; // EOF -> success + if (srcoffset) { + // some bytes were copied, so this is a real error (like ENOSPC). + copied = ftruncate(dstfd, 0); + return TriStateResult::Failed; + } + if (errno != EXDEV) + return TriStateResult::Failed; +#endif + +#if defined(Q_OS_LINUX) + // For Linux, try sendfile (it can send to some special types too). // sendfile(2) is limited in the kernel to 2G - 4k const size_t SendfileSize = 0x7ffff000; @@ -1125,16 +1158,6 @@ auto QFileSystemEngine::cloneFile(int srcfd, int dstfd, const QFileSystemMetaDat } return TriStateResult::Success; -#elif defined(Q_OS_DARWIN) - // try fcopyfile - if (fcopyfile(srcfd, dstfd, nullptr, COPYFILE_DATA | COPYFILE_STAT) == 0) - return TriStateResult::Success; - switch (errno) { - case ENOTSUP: - case ENOMEM: - return TriStateResult::NotSupported; // let QFile try - } - return TriStateResult::Failed; #else Q_UNUSED(dstfd); return TriStateResult::NotSupported;