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");
|
||||
}
|
||||
|
||||
static inline bool fileExists(const wchar_t *fileName)
|
||||
{
|
||||
WIN32_FILE_ATTRIBUTE_DATA data;
|
||||
return GetFileAttributesEx(fileName, GetFileExInfoStandard, &data);
|
||||
}
|
||||
|
||||
QLockFile::LockError QLockFilePrivate::tryLock_sys()
|
||||
{
|
||||
const QFileSystemEntry fileEntry(fileName);
|
||||
@ -79,8 +85,13 @@ QLockFile::LockError QLockFilePrivate::tryLock_sys()
|
||||
case ERROR_SHARING_VIOLATION:
|
||||
case ERROR_ALREADY_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;
|
||||
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:
|
||||
qWarning() << "Got unexpected locking error" << lastError;
|
||||
return QLockFile::UnknownError;
|
||||
|
@ -36,8 +36,11 @@
|
||||
#include <QtConcurrentRun>
|
||||
#include <qlockfile.h>
|
||||
#include <qtemporarydir.h>
|
||||
#include <qsysinfo.h>
|
||||
#if defined(Q_OS_UNIX) && !defined(Q_OS_VXWORKS)
|
||||
#include <unistd.h>
|
||||
#elif defined(Q_OS_WIN) && !defined(Q_OS_WINCE) && !defined(Q_OS_WINRT)
|
||||
# include <qt_windows.h>
|
||||
#endif
|
||||
|
||||
class tst_QLockFile : public QObject
|
||||
@ -58,6 +61,7 @@ private slots:
|
||||
void staleLongLockFromBusyProcess();
|
||||
void staleLockRace();
|
||||
void noPermissions();
|
||||
void noPermissionsWindows();
|
||||
|
||||
public:
|
||||
QString m_helperApp;
|
||||
@ -415,5 +419,66 @@ void tst_QLockFile::noPermissions()
|
||||
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)
|
||||
#include "tst_qlockfile.moc"
|
||||
|
@ -4,3 +4,4 @@ TARGET = tst_qlockfile
|
||||
SOURCES += tst_qlockfile.cpp
|
||||
|
||||
QT = core testlib concurrent
|
||||
win32:!wince:!winrt:LIBS += -ladvapi32
|
||||
|
Loading…
x
Reference in New Issue
Block a user