Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qlockfile_win.cpp
Go to the documentation of this file.
1// Copyright (C) 2013 David Faure <faure+bluesystems@kde.org>
2// Copyright (C) 2016 The Qt Company Ltd.
3// Copyright (C) 2017 Intel Corporation.
4// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
5// Qt-Security score:significant reason:default
6
7#include "private/qlockfile_p.h"
8#include "private/qfilesystementry_p.h"
10
11#include "QtCore/qdatetime.h"
12#include "QtCore/qdir.h"
13#include "QtCore/qdebug.h"
14#include "QtCore/qfileinfo.h"
15#include "QtCore/qthread.h"
16
17#include <qt_windows.h>
18#include <psapi.h>
19
21
22static inline bool fileExists(const wchar_t *fileName)
23{
24 WIN32_FILE_ATTRIBUTE_DATA data;
25 return GetFileAttributesEx(fileName, GetFileExInfoStandard, &data);
26}
27
28static bool deleteFile(const QString &fileName)
29{
30 const DWORD dwShareMode = 0; // no sharing
31 SECURITY_ATTRIBUTES securityAtts = { sizeof(SECURITY_ATTRIBUTES), NULL, FALSE };
32 HANDLE fh = CreateFile(qt_castToWchar(QDir::toNativeSeparators(fileName)),
33 GENERIC_READ | GENERIC_WRITE,
34 dwShareMode,
35 &securityAtts,
36 OPEN_EXISTING, // error if it doesn't exist
37 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_DELETE_ON_CLOSE,
38 NULL);
39 bool success = (fh != INVALID_HANDLE_VALUE);
40 if (success) {
41 CloseHandle(fh);
42 // the file is now deleted
43 } else {
44 const DWORD lastError = GetLastError();
45 if (lastError == ERROR_FILE_NOT_FOUND)
46 success = true;
47 }
48 return success;
49}
50
51QLockFile::LockError QLockFilePrivate::tryLock_sys()
52{
53 const QFileSystemEntry fileEntry(fileName);
54 // When writing, allow others to read.
55 // When reading, QFile will allow others to read and write, all good.
56 // Adding FILE_SHARE_DELETE would allow forceful deletion of stale files,
57 // but Windows doesn't allow recreating it while this handle is open anyway,
58 // so this would only create confusion (can't lock, but no lock file to read from).
59 const DWORD dwShareMode = FILE_SHARE_READ;
60 SECURITY_ATTRIBUTES securityAtts = { sizeof(SECURITY_ATTRIBUTES), NULL, FALSE };
61 HANDLE fh = CreateFile((const wchar_t*)fileEntry.nativeFilePath().utf16(),
62 GENERIC_READ | GENERIC_WRITE,
63 dwShareMode,
64 &securityAtts,
65 CREATE_NEW, // error if already exists
66 FILE_ATTRIBUTE_NORMAL,
67 NULL);
68 if (fh == INVALID_HANDLE_VALUE) {
69 const DWORD lastError = GetLastError();
70 switch (lastError) {
71 case ERROR_SHARING_VIOLATION:
72 case ERROR_ALREADY_EXISTS:
73 case ERROR_FILE_EXISTS:
74 return QLockFile::LockFailedError;
75 case ERROR_ACCESS_DENIED:
76 // readonly file, or file still in use by another process.
77 // Assume the latter if the file exists, since we don't create it readonly.
78 return fileExists((const wchar_t*)fileEntry.nativeFilePath().utf16())
79 ? QLockFile::LockFailedError
80 : QLockFile::PermissionError;
81 default:
82 qWarning("Got unexpected locking error %llu", quint64(lastError));
83 return QLockFile::UnknownError;
84 }
85 }
86
87 // We hold the lock, continue.
88 fileHandle = fh;
89 QByteArray fileData = lockFileContents();
90 DWORD bytesWritten = 0;
91 QLockFile::LockError error = QLockFile::NoError;
92 if (!WriteFile(fh, fileData.constData(), fileData.size(), &bytesWritten, NULL) || !FlushFileBuffers(fh))
93 error = QLockFile::UnknownError; // partition full
94 return error;
95}
96
97bool QLockFilePrivate::removeStaleLock()
98{
99 // DeleteFile fails if the other process is still using the file, so it's not stale.
100 return deleteFile(fileName);
101}
102
103bool QLockFilePrivate::isProcessRunning(qint64 pid, const QString &appname)
104{
105 HANDLE procHandle = ::OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, pid);
106 if (!procHandle)
107 return false;
108
109 // We got a handle but check if process is still alive
110 DWORD exitCode = 0;
111 if (!::GetExitCodeProcess(procHandle, &exitCode))
112 exitCode = 0;
113 ::CloseHandle(procHandle);
114 if (exitCode != STILL_ACTIVE)
115 return false;
116
117 const QString processName = processNameByPid(pid);
118 if (!processName.isEmpty() && processName != appname)
119 return false; // PID got reused by a different application.
120
121 return true;
122}
123
124QString QLockFilePrivate::processNameByPid(qint64 pid)
125{
126 HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, DWORD(pid));
127 if (!hProcess) {
128 return QString();
129 }
130 wchar_t buf[MAX_PATH];
131 const DWORD length = GetModuleFileNameExW(hProcess, NULL, buf, sizeof(buf) / sizeof(wchar_t));
132 CloseHandle(hProcess);
133 if (!length)
134 return QString();
135 QString name = QString::fromWCharArray(buf, length);
136 int i = name.lastIndexOf(u'\\');
137 if (i >= 0)
138 name.remove(0, i + 1);
139 i = name.lastIndexOf(u'.');
140 if (i >= 0)
141 name.truncate(i);
142 return name;
143}
144
145void QLockFile::unlock()
146{
147 Q_D(QLockFile);
148 if (!d->isLocked)
149 return;
150 CloseHandle(d->fileHandle);
151 int attempts = 0;
152 static const int maxAttempts = 500; // 500ms
153 while (!deleteFile(d->fileName) && ++attempts < maxAttempts) {
154 // Someone is reading the lock file right now (on Windows this prevents deleting it).
155 QThread::msleep(1);
156 }
157 if (attempts == maxAttempts) {
158 qWarning() << "Could not remove our own lock file" << d->fileName
159 << ". Either other users of the lock file are reading it constantly for 500 ms, "
160 "or we (no longer) have permissions to delete the file";
161 // This is bad because other users of this lock file will now have to wait for the
162 // stale-lock-timeout...
163 }
164 d->lockError = QLockFile::NoError;
165 d->isLocked = false;
166}
167
168QT_END_NAMESPACE
static bool deleteFile(const QString &fileName)
static QT_BEGIN_NAMESPACE bool fileExists(const wchar_t *fileName)