QFile::rename: avoid two stat(2)/CreateFile in a row

QFileSystemEngine::id() will stat(2)/CreateFile in order to get the ID
of the file anyway, so we don't need to use QFile::exists() to check if
the destination exists. Instead, rely on id() returning a null value to
indicate error. On Windows, it's possible that the calls to either
GetFileInformationByHandle or GetFileInformationByHandleEx might fail,
but we ignore those.

Change-Id: I1eba2b016de74620bfc8fffd14ccaebcbed64419
Reviewed-by: Jędrzej Nowacki <jedrzej.nowacki@qt.io>
This commit is contained in:
Thiago Macieira 2017-06-29 12:35:01 -07:00
parent 353fb118c3
commit fd95ef765a
2 changed files with 7 additions and 4 deletions

View File

@ -567,9 +567,11 @@ QFile::rename(const QString &newName)
}
// If the file exists and it is a case-changing rename ("foo" -> "Foo"),
// compare Ids to make sure it really is a different file.
if (QFile::exists(newName)) {
if (d->fileName.compare(newName, Qt::CaseInsensitive)
|| QFileSystemEngine::id(QFileSystemEntry(d->fileName)) != QFileSystemEngine::id(QFileSystemEntry(newName))) {
// Note: this does not take file engines into account.
QByteArray targetId = QFileSystemEngine::id(QFileSystemEntry(newName));
if (!targetId.isNull()) {
QByteArray fileId = QFileSystemEngine::id(QFileSystemEntry(d->fileName));
if (fileId != targetId || d->fileName.compare(newName, Qt::CaseInsensitive)) {
// ### Race condition. If a file is moved in after this, it /will/ be
// overwritten. On Unix, the proper solution is to use hardlinks:
// return ::link(old, new) && ::remove(old);

View File

@ -338,7 +338,8 @@ QByteArray QFileSystemEngine::id(const QFileSystemEntry &entry)
{
QT_STATBUF statResult;
if (QT_STAT(entry.nativeFilePath().constData(), &statResult)) {
qErrnoWarning("stat() failed for '%s'", entry.nativeFilePath().constData());
if (errno != ENOENT)
qErrnoWarning("stat() failed for '%s'", entry.nativeFilePath().constData());
return QByteArray();
}
QByteArray result = QByteArray::number(quint64(statResult.st_dev), 16);