Windows: Fix QLockFile hanging when file cannot be created.
Return QLockFile::PermissionError when file does not exist, preventing the stale file detection logic from triggering. Add Windows-only autotest trying to create a lock file in a system folder guarded with checks for elevated processes and UAC virtualization. Task-number: QTBUG-45631 Change-Id: I1790f8f925660f6bf1df94c2ced901e6ec57cbb0 Reviewed-by: Joerg Bornemann <joerg.bornemann@theqtcompany.com> Reviewed-by: Oliver Wolff <oliver.wolff@theqtcompany.com>
This commit is contained in:
parent
1f2ce78f16
commit
6545404afa
@ -48,6 +48,12 @@ static inline QByteArray localHostName()
|
|||||||
return qgetenv("COMPUTERNAME");
|
return qgetenv("COMPUTERNAME");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline bool fileExists(const wchar_t *fileName)
|
||||||
|
{
|
||||||
|
WIN32_FILE_ATTRIBUTE_DATA data;
|
||||||
|
return GetFileAttributesEx(fileName, GetFileExInfoStandard, &data);
|
||||||
|
}
|
||||||
|
|
||||||
QLockFile::LockError QLockFilePrivate::tryLock_sys()
|
QLockFile::LockError QLockFilePrivate::tryLock_sys()
|
||||||
{
|
{
|
||||||
const QFileSystemEntry fileEntry(fileName);
|
const QFileSystemEntry fileEntry(fileName);
|
||||||
@ -79,8 +85,13 @@ QLockFile::LockError QLockFilePrivate::tryLock_sys()
|
|||||||
case ERROR_SHARING_VIOLATION:
|
case ERROR_SHARING_VIOLATION:
|
||||||
case ERROR_ALREADY_EXISTS:
|
case ERROR_ALREADY_EXISTS:
|
||||||
case ERROR_FILE_EXISTS:
|
case ERROR_FILE_EXISTS:
|
||||||
case ERROR_ACCESS_DENIED: // readonly file, or file still in use by another process. Assume the latter, since we don't create it readonly.
|
|
||||||
return QLockFile::LockFailedError;
|
return QLockFile::LockFailedError;
|
||||||
|
case ERROR_ACCESS_DENIED:
|
||||||
|
// readonly file, or file still in use by another process.
|
||||||
|
// Assume the latter if the file exists, since we don't create it readonly.
|
||||||
|
return fileExists((const wchar_t*)fileEntry.nativeFilePath().utf16())
|
||||||
|
? QLockFile::LockFailedError
|
||||||
|
: QLockFile::PermissionError;
|
||||||
default:
|
default:
|
||||||
qWarning() << "Got unexpected locking error" << lastError;
|
qWarning() << "Got unexpected locking error" << lastError;
|
||||||
return QLockFile::UnknownError;
|
return QLockFile::UnknownError;
|
||||||
|
@ -36,8 +36,11 @@
|
|||||||
#include <QtConcurrentRun>
|
#include <QtConcurrentRun>
|
||||||
#include <qlockfile.h>
|
#include <qlockfile.h>
|
||||||
#include <qtemporarydir.h>
|
#include <qtemporarydir.h>
|
||||||
|
#include <qsysinfo.h>
|
||||||
#if defined(Q_OS_UNIX) && !defined(Q_OS_VXWORKS)
|
#if defined(Q_OS_UNIX) && !defined(Q_OS_VXWORKS)
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
#elif defined(Q_OS_WIN) && !defined(Q_OS_WINCE) && !defined(Q_OS_WINRT)
|
||||||
|
# include <qt_windows.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
class tst_QLockFile : public QObject
|
class tst_QLockFile : public QObject
|
||||||
@ -58,6 +61,7 @@ private slots:
|
|||||||
void staleLongLockFromBusyProcess();
|
void staleLongLockFromBusyProcess();
|
||||||
void staleLockRace();
|
void staleLockRace();
|
||||||
void noPermissions();
|
void noPermissions();
|
||||||
|
void noPermissionsWindows();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
QString m_helperApp;
|
QString m_helperApp;
|
||||||
@ -415,5 +419,66 @@ void tst_QLockFile::noPermissions()
|
|||||||
QCOMPARE(int(lockFile.error()), int(QLockFile::PermissionError));
|
QCOMPARE(int(lockFile.error()), int(QLockFile::PermissionError));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum ProcessProperty {
|
||||||
|
ElevatedProcess = 0x1,
|
||||||
|
VirtualStore = 0x2
|
||||||
|
};
|
||||||
|
|
||||||
|
Q_DECLARE_FLAGS(ProcessProperties, ProcessProperty)
|
||||||
|
Q_DECLARE_OPERATORS_FOR_FLAGS(ProcessProperties)
|
||||||
|
|
||||||
|
static inline ProcessProperties processProperties()
|
||||||
|
{
|
||||||
|
ProcessProperties result;
|
||||||
|
#if defined(Q_OS_WIN) && !defined(Q_OS_WINCE) && !defined(Q_OS_WINRT)
|
||||||
|
HANDLE processToken = NULL;
|
||||||
|
if (OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &processToken)) {
|
||||||
|
DWORD elevation; // struct containing a DWORD, not present in some MinGW headers.
|
||||||
|
DWORD cbSize = sizeof(elevation);
|
||||||
|
if (GetTokenInformation(processToken, TokenElevation, &elevation, cbSize, &cbSize)
|
||||||
|
&& elevation) {
|
||||||
|
result |= ElevatedProcess;
|
||||||
|
}
|
||||||
|
// Check for UAC virtualization (compatibility mode for old software
|
||||||
|
// allowing it to write to system folders by mirroring them under
|
||||||
|
// "\Users\...\AppData\Local\VirtualStore\", which is typically the case
|
||||||
|
// for MinGW).
|
||||||
|
DWORD virtualStoreEnabled = 0;
|
||||||
|
cbSize = sizeof(virtualStoreEnabled);
|
||||||
|
if (GetTokenInformation(processToken, TokenVirtualizationEnabled, &virtualStoreEnabled, cbSize, &cbSize)
|
||||||
|
&& virtualStoreEnabled) {
|
||||||
|
result |= VirtualStore;
|
||||||
|
}
|
||||||
|
CloseHandle(processToken);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void tst_QLockFile::noPermissionsWindows()
|
||||||
|
{
|
||||||
|
// Windows: Do the permissions test in a system directory in which
|
||||||
|
// files cannot be created.
|
||||||
|
#if !defined(Q_OS_WIN) || defined(Q_OS_WINCE) || defined(Q_OS_WINRT)
|
||||||
|
QSKIP("This test is for desktop Windows only");
|
||||||
|
#endif
|
||||||
|
#ifdef Q_OS_WIN
|
||||||
|
if (QSysInfo::windowsVersion() < QSysInfo::WV_WINDOWS7)
|
||||||
|
QSKIP("This test requires at least Windows 7");
|
||||||
|
#endif
|
||||||
|
if (const int p = processProperties()) {
|
||||||
|
const QByteArray message = "This test cannot be run (properties=0x"
|
||||||
|
+ QByteArray::number(p, 16) + ')';
|
||||||
|
QSKIP(message.constData());
|
||||||
|
}
|
||||||
|
|
||||||
|
const QString fileName = QFile::decodeName(qgetenv("ProgramFiles"))
|
||||||
|
+ QLatin1Char('/') + QCoreApplication::applicationName()
|
||||||
|
+ QDateTime::currentDateTime().toString(QStringLiteral("yyMMddhhmm"));
|
||||||
|
QLockFile lockFile(fileName);
|
||||||
|
QVERIFY(!lockFile.lock());
|
||||||
|
QCOMPARE(int(lockFile.error()), int(QLockFile::PermissionError));
|
||||||
|
}
|
||||||
|
|
||||||
QTEST_MAIN(tst_QLockFile)
|
QTEST_MAIN(tst_QLockFile)
|
||||||
#include "tst_qlockfile.moc"
|
#include "tst_qlockfile.moc"
|
||||||
|
@ -4,3 +4,4 @@ TARGET = tst_qlockfile
|
|||||||
SOURCES += tst_qlockfile.cpp
|
SOURCES += tst_qlockfile.cpp
|
||||||
|
|
||||||
QT = core testlib concurrent
|
QT = core testlib concurrent
|
||||||
|
win32:!wince:!winrt:LIBS += -ladvapi32
|
||||||
|
Loading…
x
Reference in New Issue
Block a user