Speed up QFile::copy on Linux file systems that support cloning

Originally inherited from Btrfs, recent Linux kernels have a system call
that allows cloning the contents of a file from another one if the
underlying file system supports it.

Change-Id: I9df66b65faef99f3bbed8a88fb6b6009baeef32e
Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
This commit is contained in:
Simon Hausmann 2017-02-22 12:46:59 +01:00
parent f971a0d65c
commit d5078161aa
7 changed files with 62 additions and 18 deletions

View File

@ -831,6 +831,19 @@ bool QAbstractFileEngine::unmap(uchar *address)
return extension(UnMapExtension, &options);
}
/*!
\since 5.10
Copies the contents from the file specified by \a sourceHandle to this file
by cloning it.
Returns \c true on success; otherwise, \c false is returned.
*/
bool QAbstractFileEngine::clone(int sourceHandle)
{
Q_UNUSED(sourceHandle);
return false;
}
/*!
\since 4.3
\class QAbstractFileEngineIterator

View File

@ -147,6 +147,7 @@ public:
virtual QDateTime fileTime(FileTime time) const;
virtual void setFileName(const QString &file);
virtual int handle() const;
virtual bool clone(int sourceHandle);
bool atEnd() const;
uchar *map(qint64 offset, qint64 size, QFile::MemoryMapFlags flags);
bool unmap(uchar *ptr);

View File

@ -42,6 +42,7 @@
#include "qfile.h"
#include "qfsfileengine_p.h"
#include "qtemporaryfile.h"
#include "qtemporaryfile_p.h"
#include "qlist.h"
#include "qfileinfo.h"
#include "private/qiodevice_p.h"
@ -790,25 +791,27 @@ QFile::copy(const QString &newName)
close();
d->setError(QFile::CopyError, tr("Cannot open for output"));
} else {
char block[4096];
qint64 totalRead = 0;
while(!atEnd()) {
qint64 in = read(block, sizeof(block));
if (in <= 0)
break;
totalRead += in;
if(in != out.write(block, in)) {
close();
d->setError(QFile::CopyError, tr("Failure to write block"));
error = true;
break;
if (!out.d_func()->engine()->clone(d->engine()->handle())) {
char block[4096];
qint64 totalRead = 0;
while (!atEnd()) {
qint64 in = read(block, sizeof(block));
if (in <= 0)
break;
totalRead += in;
if (in != out.write(block, in)) {
close();
d->setError(QFile::CopyError, tr("Failure to write block"));
error = true;
break;
}
}
}
if (totalRead != size()) {
// Unable to read from the source. The error string is
// already set from read().
error = true;
if (totalRead != size()) {
// Unable to read from the source. The error string is
// already set from read().
error = true;
}
}
if (!error && !out.rename(newName)) {
error = true;

View File

@ -108,6 +108,7 @@ public:
qint64 read(char *data, qint64 maxlen) Q_DECL_OVERRIDE;
qint64 readLine(char *data, qint64 maxlen) Q_DECL_OVERRIDE;
qint64 write(const char *data, qint64 len) Q_DECL_OVERRIDE;
bool clone(int sourceHandle) override;
bool extension(Extension extension, const ExtensionOption *option = 0, ExtensionReturn *output = 0) Q_DECL_OVERRIDE;
bool supportsExtension(Extension extension) const Q_DECL_OVERRIDE;

View File

@ -780,6 +780,23 @@ bool QFSFileEnginePrivate::unmap(uchar *ptr)
#endif
}
/*!
\reimp
*/
bool QFSFileEngine::clone(int sourceHandle)
{
#if defined(Q_OS_LINUX)
Q_D(QFSFileEngine);
# if !defined FICLONE
# define FICLONE _IOW (0x94, 9, int)
# endif
return ::ioctl(d->fd, FICLONE, sourceHandle) == 0;
#else
Q_UNUSED(sourceHandle);
return false;
#endif
}
QT_END_NAMESPACE
#endif // QT_NO_FSFILEENGINE

View File

@ -984,4 +984,13 @@ bool QFSFileEnginePrivate::unmap(uchar *ptr)
return true;
}
/*!
\reimp
*/
bool QFSFileEngine::clone(int sourceHandle)
{
Q_UNUSED(sourceHandle);
return false;
}
QT_END_NAMESPACE

View File

@ -65,7 +65,7 @@ class QTemporaryFilePrivate : public QFilePrivate
{
Q_DECLARE_PUBLIC(QTemporaryFile)
protected:
public:
QTemporaryFilePrivate();
explicit QTemporaryFilePrivate(const QString &templateNameIn);
~QTemporaryFilePrivate();