QLockFile: Don't deadlock if the lock file has a mtime in the future

Stale Lock files in the future can happen in some situations. For exemple
two computers with different clocks access the same file system. It could
be that one of the timestamp is totaly off (several years into the future).

[ChangeLog][QtCore][QLockFile] Fixed a deadlock occurring if a corrupted
lock file's modification time is in the future.

Change-Id: I8dac98a0e898c76bcef67f8c195e126c996b6add
Reviewed-by: David Faure <david.faure@kdab.com>
bb10
Olivier Goffart 2017-02-27 14:03:15 +01:00 committed by Olivier Goffart (Woboq GmbH)
parent 137e6632c8
commit b52b509ae4
4 changed files with 33 additions and 2 deletions

View File

@ -44,6 +44,7 @@
#include <QtCore/qthread.h>
#include <QtCore/qdeadlinetimer.h>
#include <QtCore/qdatetime.h>
#include <QtCore/qfileinfo.h>
QT_BEGIN_NAMESPACE
@ -226,6 +227,8 @@ bool QLockFile::tryLock(int timeout)
return false;
case LockFailedError:
if (!d->isLocked && d->isApparentlyStale()) {
if (Q_UNLIKELY(QFileInfo(d->fileName).lastModified() > QDateTime::currentDateTime()))
qInfo("QLockFile: Lock file '%ls' has a modification time in the future", qUtf16Printable(d->fileName));
// Stale lock from another thread/process
// Ensure two processes don't remove it at the same time
QLockFile rmlock(d->fileName + QLatin1String(".rmlock"));

View File

@ -247,7 +247,7 @@ bool QLockFilePrivate::isApparentlyStale() const
}
}
const qint64 age = QFileInfo(fileName).lastModified().msecsTo(QDateTime::currentDateTime());
return staleLockTime > 0 && age > staleLockTime;
return staleLockTime > 0 && qAbs(age) > staleLockTime;
}
QString QLockFilePrivate::processNameByPid(qint64 pid)

View File

@ -160,7 +160,7 @@ bool QLockFilePrivate::isApparentlyStale() const
Q_UNUSED(appname);
#endif // Q_OS_WINRT
const qint64 age = QFileInfo(fileName).lastModified().msecsTo(QDateTime::currentDateTime());
return staleLockTime > 0 && age > staleLockTime;
return staleLockTime > 0 && qAbs(age) > staleLockTime;
}
QString QLockFilePrivate::processNameByPid(qint64 pid)

View File

@ -34,6 +34,7 @@
#include <qsysinfo.h>
#if defined(Q_OS_UNIX) && !defined(Q_OS_VXWORKS)
#include <unistd.h>
#include <sys/time.h>
#elif defined(Q_OS_WIN) && !defined(Q_OS_WINRT)
# include <qt_windows.h>
#endif
@ -59,6 +60,7 @@ private slots:
void noPermissions();
void noPermissionsWindows();
void corruptedLockFile();
void corruptedLockFileInTheFuture();
private:
static bool overwritePidInLockFile(const QString &filePath, qint64 pid);
@ -521,6 +523,32 @@ void tst_QLockFile::corruptedLockFile()
QCOMPARE(int(secondLock.error()), int(QLockFile::NoError));
}
void tst_QLockFile::corruptedLockFileInTheFuture()
{
#if !defined(Q_OS_UNIX)
QSKIP("This tests needs utimes");
#else
// This test is the same as the previous one, but the corruption was so there is a corrupted
// .rmlock whose timestamp is in the future
const QString fileName = dir.path() + "/corruptedLockFile.rmlock";
{
QFile file(fileName);
QVERIFY(file.open(QFile::WriteOnly));
}
struct timeval times[2];
gettimeofday(times, 0);
times[1].tv_sec = (times[0].tv_sec += 600);
times[1].tv_usec = times[0].tv_usec;
utimes(fileName.toLocal8Bit(), times);
QTest::ignoreMessage(QtInfoMsg, "QLockFile: Lock file '" + fileName.toUtf8() + "' has a modification time in the future");
corruptedLockFile();
#endif
}
bool tst_QLockFile::overwritePidInLockFile(const QString &filePath, qint64 pid)
{
QFile f(filePath);