QFileSystemEngine/Unix: use copy_file_range(2) in cloneFile()
For remote filesystems on Linux, this can invoke a server-side copy to avoid network traffic. For local filesystems, Linux can use an optimized FS-specific call (which may be the same operation as the FICLONE, but I'm not sure) or splice the data on its own. Tested with: - FreeBSD ufs - FreeBSD ufs-to-tmpfs - FreeBSD tmpfs (tested ENOSPC with it) - Linux btrfs - Linux ext4 - Linux tmpfs (tested ENOSPC with it) Change-Id: I3ae11d555882bdbb0487fffd81cb5568171cee3f Reviewed-by: Ahmad Samir <a.samirh78@gmail.com>
This commit is contained in:
parent
fe75526542
commit
9b006eb91a
@ -143,6 +143,19 @@ int pipes[2];
|
||||
}
|
||||
")
|
||||
|
||||
# copy_file_range
|
||||
qt_config_compile_test(copy_file_range
|
||||
LABEL "copy_file_range()"
|
||||
CODE
|
||||
"#include <unistd.h>
|
||||
|
||||
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"
|
||||
)
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
Loading…
x
Reference in New Issue
Block a user