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
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// Qt-Security score:significant reason:default
4
6
7#include <QtCore/qdir.h>
8#include <QtCore/qfileinfo.h>
9#include <QtCore/qmutex.h>
10#include <QtCore/qvarlengtharray.h>
11#include <QtCore/private/wcharhelpers_win_p.h>
12
14
15#include "qntdll_p.h"
16
17extern "C" NTSTATUS NTSYSCALLAPI NTAPI NtQueryVolumeInformationFile(HANDLE, PIO_STATUS_BLOCK, PVOID, ULONG,
18 FS_INFORMATION_CLASS);
19
20QT_BEGIN_NAMESPACE
21
22using namespace Qt::StringLiterals;
23
24static const int defaultBufferSize = MAX_PATH + 1;
25
26static QString canonicalPath(const QString &rootPath)
27{
28 QString path = QDir::toNativeSeparators(QFileInfo(rootPath).canonicalFilePath());
29 if (path.isEmpty())
30 return path;
31
32 if (path.startsWith("\\\\?\\"_L1))
33 path.remove(0, 4);
34 if (path.length() < 2 || path.at(1) != u':')
35 return QString();
36
37 path[0] = path[0].toUpper();
38 if (!(path.at(0).unicode() >= 'A' && path.at(0).unicode() <= 'Z'))
39 return QString();
40 if (!path.endsWith(u'\\'))
41 path.append(u'\\');
42 return path;
43}
44
45void QStorageInfoPrivate::initRootPath()
46{
47 // Do not unnecessarily call QFileInfo::canonicalFilePath() if the path is
48 // already a drive root since it may hang on network drives.
49 const QString path = QFileSystemEntry::isDriveRootPath(rootPath)
50 ? QDir::toNativeSeparators(rootPath)
51 : canonicalPath(rootPath);
52
53 if (path.isEmpty()) {
54 valid = ready = false;
55 return;
56 }
57
58 // ### test if disk mounted to folder on other disk
59 wchar_t buffer[defaultBufferSize];
60 if (::GetVolumePathName(reinterpret_cast<const wchar_t *>(path.utf16()), buffer, defaultBufferSize))
61 rootPath = QDir::fromNativeSeparators(QString::fromWCharArray(buffer));
62 else
63 valid = ready = false;
64}
65
66static inline QByteArray getDevice(const QString &rootPath)
67{
68 const QString path = QDir::toNativeSeparators(rootPath);
69 const UINT type = ::GetDriveType(reinterpret_cast<const wchar_t *>(path.utf16()));
70 if (type == DRIVE_REMOTE) {
71 QVarLengthArray<char, 256> buffer(256);
72 DWORD bufferLength = buffer.size();
73 DWORD result;
74 UNIVERSAL_NAME_INFO *remoteNameInfo;
75 do {
76 buffer.resize(bufferLength);
77 remoteNameInfo = reinterpret_cast<UNIVERSAL_NAME_INFO *>(buffer.data());
78 result = ::WNetGetUniversalName(reinterpret_cast<const wchar_t *>(path.utf16()),
79 UNIVERSAL_NAME_INFO_LEVEL,
80 remoteNameInfo,
81 &bufferLength);
82 } while (result == ERROR_MORE_DATA);
83 if (result == NO_ERROR)
84 return QString::fromWCharArray(remoteNameInfo->lpUniversalName).toUtf8();
85 return QByteArray();
86 }
87
88 wchar_t deviceBuffer[51];
89 if (::GetVolumeNameForVolumeMountPoint(reinterpret_cast<const wchar_t *>(path.utf16()),
90 deviceBuffer,
91 sizeof(deviceBuffer) / sizeof(wchar_t))) {
92 return QString::fromWCharArray(deviceBuffer).toLatin1();
93 }
94 return QByteArray();
95}
96
97void QStorageInfoPrivate::doStat()
98{
99 valid = ready = true;
100 initRootPath();
101 if (!valid || !ready)
102 return;
103
104 retrieveVolumeInfo();
105 if (!valid || !ready)
106 return;
107 device = getDevice(rootPath);
108 retrieveDiskFreeSpace();
109
110 if (!queryStorageProperty())
111 queryFileFsSectorSizeInformation();
112}
113
114void QStorageInfoPrivate::retrieveVolumeInfo()
115{
116 const UINT oldmode = ::SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOOPENFILEERRORBOX);
117
118 const QString path = QDir::toNativeSeparators(rootPath);
119 wchar_t nameBuffer[defaultBufferSize];
120 wchar_t fileSystemTypeBuffer[defaultBufferSize];
121 DWORD fileSystemFlags = 0;
122 const bool result = ::GetVolumeInformation(reinterpret_cast<const wchar_t *>(path.utf16()),
123 nameBuffer,
124 defaultBufferSize,
125 nullptr,
126 nullptr,
127 &fileSystemFlags,
128 fileSystemTypeBuffer,
129 defaultBufferSize);
130 if (!result) {
131 ready = false;
132 valid = ::GetLastError() == ERROR_NOT_READY;
133 } else {
134 fileSystemType = QString::fromWCharArray(fileSystemTypeBuffer).toLatin1();
135 name = QString::fromWCharArray(nameBuffer);
136
137 readOnly = (fileSystemFlags & FILE_READ_ONLY_VOLUME) != 0;
138 }
139
140 ::SetErrorMode(oldmode);
141}
142
143void QStorageInfoPrivate::retrieveDiskFreeSpace()
144{
145 const UINT oldmode = ::SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOOPENFILEERRORBOX);
146
147 const QString path = QDir::toNativeSeparators(rootPath);
148 ready = ::GetDiskFreeSpaceEx(reinterpret_cast<const wchar_t *>(path.utf16()),
149 PULARGE_INTEGER(&bytesAvailable),
150 PULARGE_INTEGER(&bytesTotal),
151 PULARGE_INTEGER(&bytesFree));
152
153 ::SetErrorMode(oldmode);
154}
155
156QList<QStorageInfo> QStorageInfoPrivate::mountedVolumes()
157{
158 QList<QStorageInfo> volumes;
159
160 QString driveName = QStringLiteral("A:/");
161 const UINT oldmode = ::SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOOPENFILEERRORBOX);
162 quint32 driveBits = quint32(::GetLogicalDrives()) & 0x3ffffff;
163 ::SetErrorMode(oldmode);
164 while (driveBits) {
165 if (driveBits & 1) {
166 QStorageInfo drive(driveName);
167 if (!drive.rootPath().isEmpty()) // drive exists, but not mounted
168 volumes.append(drive);
169 }
170 driveName[0] = QChar(driveName[0].unicode() + 1);
171 driveBits = driveBits >> 1;
172 }
173
174 return volumes;
175}
176
177bool QStorageInfoPrivate::queryStorageProperty()
178{
179 QString path = QDir::toNativeSeparators(uR"(\\.\‍)" + rootPath);
180 if (path.endsWith(u'\\'))
181 path.chop(1);
182
183 HANDLE handle = CreateFile(qt_castToWchar(path),
184 0, // no access to the drive
185 FILE_SHARE_READ | FILE_SHARE_WRITE,
186 nullptr,
187 OPEN_EXISTING,
188 0,
189 nullptr);
190 if (handle == INVALID_HANDLE_VALUE)
191 return false;
192
193 STORAGE_PROPERTY_QUERY spq;
194 memset(&spq, 0, sizeof(spq));
195 spq.PropertyId = StorageAccessAlignmentProperty;
196 spq.QueryType = PropertyStandardQuery;
197
198 STORAGE_ACCESS_ALIGNMENT_DESCRIPTOR saad;
199 memset(&saad, 0, sizeof(saad));
200
201 DWORD bytes = 0;
202 BOOL result = DeviceIoControl(handle,
203 IOCTL_STORAGE_QUERY_PROPERTY,
204 &spq, sizeof(spq),
205 &saad, sizeof(saad),
206 &bytes,
207 nullptr);
208 CloseHandle(handle);
209 if (result)
210 blockSize = int(saad.BytesPerPhysicalSector);
211 return result;
212}
213
214void QStorageInfoPrivate::queryFileFsSectorSizeInformation()
215{
216 FILE_FS_SECTOR_SIZE_INFORMATION ffssi;
217 memset(&ffssi, 0, sizeof(ffssi));
218
219 HANDLE handle = nullptr;
220
221 OBJECT_ATTRIBUTES attrs;
222 memset(&attrs, 0, sizeof(attrs));
223
224 IO_STATUS_BLOCK isb;
225 memset(&isb, 0, sizeof(isb));
226
227 QString path = QDir::toNativeSeparators(uR"(\??\\‍)" + rootPath);
228 if (!path.endsWith(u'\\'))
229 path.append(u'\\');
230
231 UNICODE_STRING name;
232 ::RtlInitUnicodeString(&name, qt_castToWchar(path));
233
234 InitializeObjectAttributes(&attrs, &name, 0, nullptr, nullptr);
235
236 NTSTATUS status = ::NtCreateFile(&handle,
237 FILE_READ_ATTRIBUTES,
238 &attrs,
239 &isb,
240 nullptr,
241 FILE_ATTRIBUTE_NORMAL,
242 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
243 FILE_OPEN,
244 0,
245 nullptr,
246 0);
247 if (!NT_SUCCESS(status))
248 return;
249
250 memset(&isb, 0, sizeof(isb));
251 status = ::NtQueryVolumeInformationFile(handle,
252 &isb,
253 &ffssi,
254 sizeof(ffssi),
255 FS_INFORMATION_CLASS(10)); // FileFsSectorSizeInformation
256 CloseHandle(handle);
257 if (NT_SUCCESS(status))
258 blockSize = ffssi.PhysicalBytesPerSectorForAtomicity;
259}
260
261QT_END_NAMESPACE
static QString canonicalPath(const QString &rootPath)
static QByteArray getDevice(const QString &rootPath)
static const int defaultBufferSize