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
qstorageinfo_win.cpp
Go to the documentation of this file.
1// Copyright (C) 2014 Ivan Komissarov <ABBAPOH@gmail.com>
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include "qstorageinfo_p.h"
5
6#include <QtCore/qdir.h>
7#include <QtCore/qfileinfo.h>
8#include <QtCore/qmutex.h>
9#include <QtCore/qvarlengtharray.h>
10
11#include "qfilesystementry_p.h"
12#include "private/qsystemlibrary_p.h"
13
14#include "qntdll_p.h"
15
17
18using namespace Qt::StringLiterals;
19
20static const int defaultBufferSize = MAX_PATH + 1;
21
22static QString canonicalPath(const QString &rootPath)
23{
24 QString path = QDir::toNativeSeparators(QFileInfo(rootPath).canonicalFilePath());
25 if (path.isEmpty())
26 return path;
27
28 if (path.startsWith("\\\\?\\"_L1))
29 path.remove(0, 4);
30 if (path.length() < 2 || path.at(1) != u':')
31 return QString();
32
33 path[0] = path[0].toUpper();
34 if (!(path.at(0).unicode() >= 'A' && path.at(0).unicode() <= 'Z'))
35 return QString();
36 if (!path.endsWith(u'\\'))
37 path.append(u'\\');
38 return path;
39}
40
41void QStorageInfoPrivate::initRootPath()
42{
43 // Do not unnecessarily call QFileInfo::canonicalFilePath() if the path is
44 // already a drive root since it may hang on network drives.
45 const QString path = QFileSystemEntry::isDriveRootPath(rootPath)
46 ? QDir::toNativeSeparators(rootPath)
47 : canonicalPath(rootPath);
48
49 if (path.isEmpty()) {
50 valid = ready = false;
51 return;
52 }
53
54 // ### test if disk mounted to folder on other disk
56 if (::GetVolumePathName(reinterpret_cast<const wchar_t *>(path.utf16()), buffer, defaultBufferSize))
58 else
59 valid = ready = false;
60}
61
62static inline QByteArray getDevice(const QString &rootPath)
63{
64 const QString path = QDir::toNativeSeparators(rootPath);
65 const UINT type = ::GetDriveType(reinterpret_cast<const wchar_t *>(path.utf16()));
66 if (type == DRIVE_REMOTE) {
67 QVarLengthArray<char, 256> buffer(256);
68 DWORD bufferLength = buffer.size();
69 DWORD result;
70 UNIVERSAL_NAME_INFO *remoteNameInfo;
71 do {
72 buffer.resize(bufferLength);
73 remoteNameInfo = reinterpret_cast<UNIVERSAL_NAME_INFO *>(buffer.data());
74 result = ::WNetGetUniversalName(reinterpret_cast<const wchar_t *>(path.utf16()),
75 UNIVERSAL_NAME_INFO_LEVEL,
76 remoteNameInfo,
77 &bufferLength);
78 } while (result == ERROR_MORE_DATA);
79 if (result == NO_ERROR)
80 return QString::fromWCharArray(remoteNameInfo->lpUniversalName).toUtf8();
81 return QByteArray();
82 }
83
84 wchar_t deviceBuffer[51];
85 if (::GetVolumeNameForVolumeMountPoint(reinterpret_cast<const wchar_t *>(path.utf16()),
86 deviceBuffer,
87 sizeof(deviceBuffer) / sizeof(wchar_t))) {
88 return QString::fromWCharArray(deviceBuffer).toLatin1();
89 }
90 return QByteArray();
91}
92
93void QStorageInfoPrivate::doStat()
94{
95 valid = ready = true;
96 initRootPath();
97 if (!valid || !ready)
98 return;
99
100 retrieveVolumeInfo();
101 if (!valid || !ready)
102 return;
103 device = getDevice(rootPath);
104 retrieveDiskFreeSpace();
105
106 if (!queryStorageProperty())
107 queryFileFsSectorSizeInformation();
108}
109
110void QStorageInfoPrivate::retrieveVolumeInfo()
111{
112 const UINT oldmode = ::SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOOPENFILEERRORBOX);
113
114 const QString path = QDir::toNativeSeparators(rootPath);
115 wchar_t nameBuffer[defaultBufferSize];
116 wchar_t fileSystemTypeBuffer[defaultBufferSize];
117 DWORD fileSystemFlags = 0;
118 const bool result = ::GetVolumeInformation(reinterpret_cast<const wchar_t *>(path.utf16()),
119 nameBuffer,
121 nullptr,
122 nullptr,
123 &fileSystemFlags,
124 fileSystemTypeBuffer,
126 if (!result) {
127 ready = false;
128 valid = ::GetLastError() == ERROR_NOT_READY;
129 } else {
130 fileSystemType = QString::fromWCharArray(fileSystemTypeBuffer).toLatin1();
131 name = QString::fromWCharArray(nameBuffer);
132
133 readOnly = (fileSystemFlags & FILE_READ_ONLY_VOLUME) != 0;
134 }
135
136 ::SetErrorMode(oldmode);
137}
138
139void QStorageInfoPrivate::retrieveDiskFreeSpace()
140{
141 const UINT oldmode = ::SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOOPENFILEERRORBOX);
142
143 const QString path = QDir::toNativeSeparators(rootPath);
144 ready = ::GetDiskFreeSpaceEx(reinterpret_cast<const wchar_t *>(path.utf16()),
145 PULARGE_INTEGER(&bytesAvailable),
146 PULARGE_INTEGER(&bytesTotal),
147 PULARGE_INTEGER(&bytesFree));
148
149 ::SetErrorMode(oldmode);
150}
151
152QList<QStorageInfo> QStorageInfoPrivate::mountedVolumes()
153{
154 QList<QStorageInfo> volumes;
155
156 QString driveName = QStringLiteral("A:/");
157 const UINT oldmode = ::SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOOPENFILEERRORBOX);
158 quint32 driveBits = quint32(::GetLogicalDrives()) & 0x3ffffff;
159 ::SetErrorMode(oldmode);
160 while (driveBits) {
161 if (driveBits & 1) {
162 QStorageInfo drive(driveName);
163 if (!drive.rootPath().isEmpty()) // drive exists, but not mounted
164 volumes.append(drive);
165 }
166 driveName[0] = QChar(driveName[0].unicode() + 1);
167 driveBits = driveBits >> 1;
168 }
169
170 return volumes;
171}
172
173bool QStorageInfoPrivate::queryStorageProperty()
174{
175 QString path = QDir::toNativeSeparators(uR"(\\.\)" + rootPath);
176 if (path.endsWith(u'\\'))
177 path.chop(1);
178
179 HANDLE handle = CreateFile(reinterpret_cast<const wchar_t *>(path.utf16()),
180 0, // no access to the drive
181 FILE_SHARE_READ | FILE_SHARE_WRITE,
182 nullptr,
183 OPEN_EXISTING,
184 0,
185 nullptr);
186 if (handle == INVALID_HANDLE_VALUE)
187 return false;
188
189 STORAGE_PROPERTY_QUERY spq;
190 memset(&spq, 0, sizeof(spq));
191 spq.PropertyId = StorageAccessAlignmentProperty;
192 spq.QueryType = PropertyStandardQuery;
193
194 STORAGE_ACCESS_ALIGNMENT_DESCRIPTOR saad;
195 memset(&saad, 0, sizeof(saad));
196
197 DWORD bytes = 0;
198 BOOL result = DeviceIoControl(handle,
199 IOCTL_STORAGE_QUERY_PROPERTY,
200 &spq, sizeof(spq),
201 &saad, sizeof(saad),
202 &bytes,
203 nullptr);
204 CloseHandle(handle);
205 if (result)
206 blockSize = int(saad.BytesPerPhysicalSector);
207 return result;
208}
209
210struct Helper
211{
213 QSystemLibrary ntdll {u"ntdll"_s};
214};
216
217inline QFunctionPointer resolveSymbol(QSystemLibrary *ntdll, const char *name)
218{
219 QFunctionPointer symbolFunctionPointer = ntdll->resolve(name);
220 if (Q_UNLIKELY(!symbolFunctionPointer))
221 qWarning("Failed to resolve the symbol: %s", name);
222 return symbolFunctionPointer;
223}
224
225#define GENERATE_SYMBOL(symbolName, returnType, ...) \
226using Qt##symbolName = returnType (NTAPI *) (__VA_ARGS__); \
227static Qt##symbolName qt##symbolName = nullptr;
228
229#define RESOLVE_SYMBOL(name) \
230 do { \
231 qt##name = reinterpret_cast<Qt##name>(resolveSymbol(ntdll, #name)); \
232 if (!qt##name) \
233 return false; \
234 } while (false)
235
236GENERATE_SYMBOL(RtlInitUnicodeString, void, PUNICODE_STRING, PCWSTR);
237GENERATE_SYMBOL(NtCreateFile, NTSTATUS, PHANDLE, ACCESS_MASK, POBJECT_ATTRIBUTES,
238 PIO_STATUS_BLOCK, PLARGE_INTEGER, ULONG, ULONG, ULONG, ULONG, PVOID, ULONG);
239GENERATE_SYMBOL(NtQueryVolumeInformationFile, NTSTATUS, HANDLE, PIO_STATUS_BLOCK, PVOID, ULONG,
241
242void QStorageInfoPrivate::queryFileFsSectorSizeInformation()
243{
244 static bool symbolsResolved = [](auto ntdllHelper) {
245 QMutexLocker locker(&ntdllHelper->mutex);
246 auto ntdll = &ntdllHelper->ntdll;
247 if (!ntdll->isLoaded()) {
248 if (!ntdll->load()) {
249 qWarning("Unable to load ntdll.dll.");
250 return false;
251 }
252 }
253
254 RESOLVE_SYMBOL(RtlInitUnicodeString);
255 RESOLVE_SYMBOL(NtCreateFile);
256 RESOLVE_SYMBOL(NtQueryVolumeInformationFile);
257
258 return true;
259 }(gNtdllHelper());
260 if (!symbolsResolved)
261 return;
262
264 memset(&ffssi, 0, sizeof(ffssi));
265
266 HANDLE handle = nullptr;
267
268 OBJECT_ATTRIBUTES attrs;
269 memset(&attrs, 0, sizeof(attrs));
270
271 IO_STATUS_BLOCK isb;
272 memset(&isb, 0, sizeof(isb));
273
274 QString path = QDir::toNativeSeparators(uR"(\??\\)" + rootPath);
275 if (!path.endsWith(u'\\'))
276 path.append(u'\\');
277
278 UNICODE_STRING name;
279 qtRtlInitUnicodeString(&name, reinterpret_cast<const wchar_t *>(path.utf16()));
280
281 InitializeObjectAttributes(&attrs, &name, 0, nullptr, nullptr);
282
283 NTSTATUS status = qtNtCreateFile(&handle,
284 FILE_READ_ATTRIBUTES,
285 &attrs,
286 &isb,
287 nullptr,
288 FILE_ATTRIBUTE_NORMAL,
289 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
290 FILE_OPEN,
291 0,
292 nullptr,
293 0);
294 if (!NT_SUCCESS(status))
295 return;
296
297 memset(&isb, 0, sizeof(isb));
298 status = qtNtQueryVolumeInformationFile(handle,
299 &isb,
300 &ffssi,
301 sizeof(ffssi),
302 FS_INFORMATION_CLASS(10)); // FileFsSectorSizeInformation
303 CloseHandle(handle);
304 if (NT_SUCCESS(status))
305 blockSize = ffssi.PhysicalBytesPerSectorForAtomicity;
306}
307
IOBluetoothDevice * device
\inmodule QtCore
Definition qbytearray.h:57
\inmodule QtCore
static QString fromNativeSeparators(const QString &pathName)
Definition qdir.cpp:962
static QString toNativeSeparators(const QString &pathName)
Definition qdir.cpp:929
\inmodule QtCore
Definition qmutex.h:313
\inmodule QtCore
Definition qmutex.h:281
\inmodule QtCore
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:129
static QString fromWCharArray(const wchar_t *string, qsizetype size=-1)
Definition qstring.h:1309
Combined button and popup list for selecting options.
void * HANDLE
const int blockSize
#define Q_UNLIKELY(x)
static struct AttrInfo attrs[]
typedef QByteArray(EGLAPIENTRYP PFNQGSGETDISPLAYSPROC)()
#define Q_GLOBAL_STATIC(TYPE, NAME,...)
#define qWarning
Definition qlogging.h:166
QT_BEGIN_NAMESPACE struct _FILE_FS_SECTOR_SIZE_INFORMATION FILE_FS_SECTOR_SIZE_INFORMATION
enum _FSINFOCLASS FS_INFORMATION_CLASS
GLuint64 GLenum void * handle
GLenum GLuint buffer
GLenum type
GLuint name
GLsizei const GLchar *const * path
GLuint64EXT * result
[6]
#define MAX_PATH
static QString canonicalPath(const QString &rootPath)
const char * name
static QByteArray getDevice(const QString &rootPath)
static const int defaultBufferSize
#define QStringLiteral(str)
unsigned int quint32
Definition qtypes.h:50
QBasicMutex mutex
QSystemLibrary ntdll