QFile::copy(): don't trust the old file metadata cache
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 <simon.hausmann@qt.io> Reviewed-by: Oswald Buddenhagen <oswald.buddenhagen@qt.io> Reviewed-by: Edward Welbourne <edward.welbourne@qt.io>
This commit is contained in:
parent
9c0d34da52
commit
04c57b6886
@ -1113,7 +1113,6 @@ bool QFileSystemEngine::cloneFile(int srcfd, int dstfd, const QFileSystemMetaDat
|
|||||||
QT_STATBUF statBuffer;
|
QT_STATBUF statBuffer;
|
||||||
if (knownData.hasFlags(QFileSystemMetaData::PosixStatFlags) &&
|
if (knownData.hasFlags(QFileSystemMetaData::PosixStatFlags) &&
|
||||||
knownData.isFile()) {
|
knownData.isFile()) {
|
||||||
statBuffer.st_size = knownData.size();
|
|
||||||
statBuffer.st_mode = S_IFREG;
|
statBuffer.st_mode = S_IFREG;
|
||||||
} else if (knownData.hasFlags(QFileSystemMetaData::PosixStatFlags) &&
|
} else if (knownData.hasFlags(QFileSystemMetaData::PosixStatFlags) &&
|
||||||
knownData.isDirectory()) {
|
knownData.isDirectory()) {
|
||||||
@ -1126,29 +1125,23 @@ bool QFileSystemEngine::cloneFile(int srcfd, int dstfd, const QFileSystemMetaDat
|
|||||||
}
|
}
|
||||||
|
|
||||||
#if defined(Q_OS_LINUX)
|
#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)
|
// first, try FICLONE (only works on regular files and only on certain fs)
|
||||||
if (::ioctl(dstfd, FICLONE, srcfd) == 0)
|
if (::ioctl(dstfd, FICLONE, srcfd) == 0)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
// Second, try sendfile (it can send to some special types too).
|
// Second, try sendfile (it can send to some special types too).
|
||||||
// sendfile(2) is limited in the kernel to 2G - 4k
|
// 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 (n == -1) {
|
||||||
// if we got an error here, give up and try at an upper layer
|
// if we got an error here, give up and try at an upper layer
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
statBuffer.st_size -= n;
|
while (n) {
|
||||||
while (statBuffer.st_size) {
|
n = ::sendfile(dstfd, srcfd, NULL, SendfileSize);
|
||||||
n = ::sendfile(dstfd, srcfd, NULL, sendfileSize(statBuffer.st_size));
|
if (n == -1) {
|
||||||
if (n == 0) {
|
|
||||||
// uh oh, this is probably a real error (like ENOSPC), but we have
|
// 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
|
// 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
|
// done (hopefully we won't get any errors, because there's nothing
|
||||||
@ -1158,9 +1151,6 @@ bool QFileSystemEngine::cloneFile(int srcfd, int dstfd, const QFileSystemMetaDat
|
|||||||
n = lseek(dstfd, 0, SEEK_SET);
|
n = lseek(dstfd, 0, SEEK_SET);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (n == 0)
|
|
||||||
return true;
|
|
||||||
statBuffer.st_size -= n;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user