Skip to content

Commit

Permalink
QFile::copy(): don't trust the old file metadata cache
Browse files Browse the repository at this point in the history
Instead, let's just use sendfile(2) in a loop until it returns 0, which
indicates EOF.

[ChangeLog][QtCore][QFile] Fixed a regression in QFile::copy() that
caused the original file not to be copied entirely if it was modified
outside of this QFile object between the last time we checked its size
and the copy() call. Note this is not a prevention against race
conditions.

Task-number: QTBUG-69417
Change-Id: Id59bdd8f1a804b809e22fffd15406c8aa31f4a1e
Reviewed-by: Simon Hausmann <[email protected]>
Reviewed-by: Oswald Buddenhagen <[email protected]>
Reviewed-by: Edward Welbourne <[email protected]>
  • Loading branch information
thiagomacieira committed Jul 12, 2018
1 parent 9c0d34d commit 04c57b6
Showing 1 changed file with 5 additions and 15 deletions.
20 changes: 5 additions & 15 deletions src/corelib/io/qfilesystemengine_unix.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1113,7 +1113,6 @@ bool QFileSystemEngine::cloneFile(int srcfd, int dstfd, const QFileSystemMetaDat
QT_STATBUF statBuffer;
if (knownData.hasFlags(QFileSystemMetaData::PosixStatFlags) &&
knownData.isFile()) {
statBuffer.st_size = knownData.size();
statBuffer.st_mode = S_IFREG;
} else if (knownData.hasFlags(QFileSystemMetaData::PosixStatFlags) &&
knownData.isDirectory()) {
Expand All @@ -1126,29 +1125,23 @@ bool QFileSystemEngine::cloneFile(int srcfd, int dstfd, const QFileSystemMetaDat
}

#if defined(Q_OS_LINUX)
if (statBuffer.st_size == 0) {
// empty file? we're done.
return true;
}

// first, try FICLONE (only works on regular files and only on certain fs)
if (::ioctl(dstfd, FICLONE, srcfd) == 0)
return true;

// Second, try sendfile (it can send to some special types too).
// sendfile(2) is limited in the kernel to 2G - 4k
auto sendfileSize = [](QT_OFF_T size) { return size_t(qMin<qint64>(0x7ffff000, size)); };
const size_t SendfileSize = 0x7ffff000;

ssize_t n = ::sendfile(dstfd, srcfd, NULL, sendfileSize(statBuffer.st_size));
ssize_t n = ::sendfile(dstfd, srcfd, NULL, SendfileSize);
if (n == -1) {
// if we got an error here, give up and try at an upper layer
return false;
}

statBuffer.st_size -= n;
while (statBuffer.st_size) {
n = ::sendfile(dstfd, srcfd, NULL, sendfileSize(statBuffer.st_size));
if (n == 0) {
while (n) {
n = ::sendfile(dstfd, srcfd, NULL, SendfileSize);
if (n == -1) {
// uh oh, this is probably a real error (like ENOSPC), but we have
// no way to notify QFile of partial success, so just erase any work
// done (hopefully we won't get any errors, because there's nothing
Expand All @@ -1158,9 +1151,6 @@ bool QFileSystemEngine::cloneFile(int srcfd, int dstfd, const QFileSystemMetaDat
n = lseek(dstfd, 0, SEEK_SET);
return false;
}
if (n == 0)
return true;
statBuffer.st_size -= n;
}

return true;
Expand Down

0 comments on commit 04c57b6

Please sign in to comment.