Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
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
6#include "private/qlockfile_p.h"
7#include "private/qfilesystementry_p.h"
8#include <qt_windows.h>
9#include <psapi.h>
10
11#include "QtCore/qfileinfo.h"
12#include "QtCore/qdatetime.h"
13#include "QtCore/qdebug.h"
14#include "QtCore/qthread.h"
15
17
18static inline bool fileExists(const wchar_t *fileName)
19{
20 WIN32_FILE_ATTRIBUTE_DATA data;
21 return GetFileAttributesEx(fileName, GetFileExInfoStandard, &data);
22}
23
25{
26 const QFileSystemEntry fileEntry(fileName);
27 // When writing, allow others to read.
28 // When reading, QFile will allow others to read and write, all good.
29 // Adding FILE_SHARE_DELETE would allow forceful deletion of stale files,
30 // but Windows doesn't allow recreating it while this handle is open anyway,
31 // so this would only create confusion (can't lock, but no lock file to read from).
32 const DWORD dwShareMode = FILE_SHARE_READ;
33 SECURITY_ATTRIBUTES securityAtts = { sizeof(SECURITY_ATTRIBUTES), NULL, FALSE };
34 HANDLE fh = CreateFile((const wchar_t*)fileEntry.nativeFilePath().utf16(),
35 GENERIC_READ | GENERIC_WRITE,
36 dwShareMode,
37 &securityAtts,
38 CREATE_NEW, // error if already exists
39 FILE_ATTRIBUTE_NORMAL,
40 NULL);
41 if (fh == INVALID_HANDLE_VALUE) {
42 const DWORD lastError = GetLastError();
43 switch (lastError) {
44 case ERROR_SHARING_VIOLATION:
45 case ERROR_ALREADY_EXISTS:
46 case ERROR_FILE_EXISTS:
48 case ERROR_ACCESS_DENIED:
49 // readonly file, or file still in use by another process.
50 // Assume the latter if the file exists, since we don't create it readonly.
51 return fileExists((const wchar_t*)fileEntry.nativeFilePath().utf16())
54 default:
55 qWarning("Got unexpected locking error %llu", quint64(lastError));
57 }
58 }
59
60 // We hold the lock, continue.
61 fileHandle = fh;
62 QByteArray fileData = lockFileContents();
63 DWORD bytesWritten = 0;
65 if (!WriteFile(fh, fileData.constData(), fileData.size(), &bytesWritten, NULL) || !FlushFileBuffers(fh))
66 error = QLockFile::UnknownError; // partition full
67 return error;
68}
69
71{
72 // QFile::remove fails on Windows if the other process is still using the file, so it's not stale.
73 return QFile::remove(fileName);
74}
75
77{
78 HANDLE procHandle = ::OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, pid);
79 if (!procHandle)
80 return false;
81
82 // We got a handle but check if process is still alive
83 DWORD exitCode = 0;
84 if (!::GetExitCodeProcess(procHandle, &exitCode))
85 exitCode = 0;
86 ::CloseHandle(procHandle);
87 if (exitCode != STILL_ACTIVE)
88 return false;
89
90 const QString processName = processNameByPid(pid);
91 if (!processName.isEmpty() && processName != appname)
92 return false; // PID got reused by a different application.
93
94 return true;
95}
96
98{
99 HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, DWORD(pid));
100 if (!hProcess) {
101 return QString();
102 }
103 wchar_t buf[MAX_PATH];
104 const DWORD length = GetModuleFileNameExW(hProcess, NULL, buf, sizeof(buf) / sizeof(wchar_t));
105 CloseHandle(hProcess);
106 if (!length)
107 return QString();
109 int i = name.lastIndexOf(u'\\');
110 if (i >= 0)
111 name.remove(0, i + 1);
112 i = name.lastIndexOf(u'.');
113 if (i >= 0)
114 name.truncate(i);
115 return name;
116}
117
119{
120 Q_D(QLockFile);
121 if (!d->isLocked)
122 return;
123 CloseHandle(d->fileHandle);
124 int attempts = 0;
125 static const int maxAttempts = 500; // 500ms
126 while (!QFile::remove(d->fileName) && ++attempts < maxAttempts) {
127 // Someone is reading the lock file right now (on Windows this prevents deleting it).
129 }
130 if (attempts == maxAttempts) {
131 qWarning() << "Could not remove our own lock file" << d->fileName
132 << ". Either other users of the lock file are reading it constantly for 500 ms, "
133 "or we (no longer) have permissions to delete the file";
134 // This is bad because other users of this lock file will now have to wait for the
135 // stale-lock-timeout...
136 }
137 d->lockError = QLockFile::NoError;
138 d->isLocked = false;
139}
140
\inmodule QtCore
Definition qbytearray.h:57
qsizetype size() const noexcept
Returns the number of bytes in this byte array.
Definition qbytearray.h:494
const char * constData() const noexcept
Returns a pointer to the const data stored in the byte array.
Definition qbytearray.h:124
bool remove()
Removes the file specified by fileName().
Definition qfile.cpp:419
QLockFile::LockError tryLock_sys()
static bool isProcessRunning(qint64 pid, const QString &appname)
static Q_CORE_EXPORT QString processNameByPid(qint64 pid)
QByteArray lockFileContents() const
\inmodule QtCore
Definition qlockfile.h:17
LockError
This enum describes the result of the last call to lock() or tryLock().
Definition qlockfile.h:40
@ LockFailedError
Definition qlockfile.h:42
@ UnknownError
Definition qlockfile.h:44
@ PermissionError
Definition qlockfile.h:43
void unlock()
Releases the lock, by deleting the lock file.
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:129
static QString fromWCharArray(const wchar_t *string, qsizetype size=-1)
Definition qstring.h:1309
static void msleep(unsigned long)
Combined button and popup list for selecting options.
void * HANDLE
DBusConnection const char DBusError * error
static QT_BEGIN_NAMESPACE bool fileExists(const wchar_t *fileName)
#define qWarning
Definition qlogging.h:166
GLenum GLsizei GLuint GLint * bytesWritten
GLenum GLuint GLenum GLsizei length
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
GLenum GLuint GLenum GLsizei const GLchar * buf
GLuint name
#define MAX_PATH
unsigned long long quint64
Definition qtypes.h:61
long long qint64
Definition qtypes.h:60