diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/corelib/CMakeLists.txt | 1 | ||||
-rw-r--r-- | src/corelib/io/qntdll_p.h | 96 | ||||
-rw-r--r-- | src/corelib/io/qstorageinfo_p.h | 2 | ||||
-rw-r--r-- | src/corelib/io/qstorageinfo_win.cpp | 142 |
4 files changed, 240 insertions, 1 deletions
diff --git a/src/corelib/CMakeLists.txt b/src/corelib/CMakeLists.txt index e02f79b8f0..8bb040ca96 100644 --- a/src/corelib/CMakeLists.txt +++ b/src/corelib/CMakeLists.txt @@ -481,6 +481,7 @@ qt_internal_extend_target(Core CONDITION WIN32 io/qstorageinfo_win.cpp io/qwindowspipereader.cpp io/qwindowspipereader_p.h io/qwindowspipewriter.cpp io/qwindowspipewriter_p.h + io/qntdll_p.h kernel/qcoreapplication_win.cpp kernel/qelapsedtimer_win.cpp kernel/qeventdispatcher_win.cpp kernel/qeventdispatcher_win_p.h diff --git a/src/corelib/io/qntdll_p.h b/src/corelib/io/qntdll_p.h new file mode 100644 index 0000000000..caf34ddb5f --- /dev/null +++ b/src/corelib/io/qntdll_p.h @@ -0,0 +1,96 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QNTDLL_P_H +#define QNTDLL_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <winternl.h> + +QT_BEGIN_NAMESPACE + +// keep the following structure as is, taken from +// https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/ntddk/ns-ntddk-_file_fs_sector_size_information +// Unfortunately we can't include the ntddk.h header, so we duplicate the code here. +typedef struct _FILE_FS_SECTOR_SIZE_INFORMATION { + ULONG LogicalBytesPerSector; + ULONG PhysicalBytesPerSectorForAtomicity; + ULONG PhysicalBytesPerSectorForPerformance; + ULONG FileSystemEffectivePhysicalBytesPerSectorForAtomicity; + ULONG Flags; + ULONG ByteOffsetForSectorAlignment; + ULONG ByteOffsetForPartitionAlignment; +} FILE_FS_SECTOR_SIZE_INFORMATION, *PFILE_FS_SECTOR_SIZE_INFORMATION; + +#if !defined(Q_CC_CLANG) && !defined(Q_CC_MINGW) +// keep the following enumeration as is, taken from +// https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/wdm/ne-wdm-_fsinfoclass +// Unfortunately we can't include the wdm.h header, so we duplicate the code here. +typedef enum _FSINFOCLASS { + FileFsVolumeInformation, + FileFsLabelInformation, + FileFsSizeInformation, + FileFsDeviceInformation, + FileFsAttributeInformation, + FileFsControlInformation, + FileFsFullSizeInformation, + FileFsObjectIdInformation, + FileFsDriverPathInformation, + FileFsVolumeFlagsInformation, + FileFsSectorSizeInformation, + FileFsDataCopyInformation, + FileFsMetadataSizeInformation, + FileFsFullSizeInformationEx, + FileFsMaximumInformation +} FS_INFORMATION_CLASS, *PFS_INFORMATION_CLASS; +#endif + +QT_END_NAMESPACE + +#endif // QNTDLL_P_H diff --git a/src/corelib/io/qstorageinfo_p.h b/src/corelib/io/qstorageinfo_p.h index 421e364311..c0f7595c08 100644 --- a/src/corelib/io/qstorageinfo_p.h +++ b/src/corelib/io/qstorageinfo_p.h @@ -74,6 +74,8 @@ protected: #if defined(Q_OS_WIN) void retrieveVolumeInfo(); void retrieveDiskFreeSpace(); + bool queryStorageProperty(); + void queryFileFsSectorSizeInformation(); #elif defined(Q_OS_MAC) void retrievePosixInfo(); void retrieveUrlProperties(bool initRootPath = false); diff --git a/src/corelib/io/qstorageinfo_win.cpp b/src/corelib/io/qstorageinfo_win.cpp index 5a65b78d63..d594551425 100644 --- a/src/corelib/io/qstorageinfo_win.cpp +++ b/src/corelib/io/qstorageinfo_win.cpp @@ -41,11 +41,13 @@ #include <QtCore/qdir.h> #include <QtCore/qfileinfo.h> +#include <QtCore/qmutex.h> #include <QtCore/qvarlengtharray.h> #include "qfilesystementry_p.h" +#include "private/qsystemlibrary_p.h" -#include <qt_windows.h> +#include "qntdll_p.h" QT_BEGIN_NAMESPACE @@ -134,6 +136,9 @@ void QStorageInfoPrivate::doStat() return; device = getDevice(rootPath); retrieveDiskFreeSpace(); + + if (!queryStorageProperty()) + queryFileFsSectorSizeInformation(); } void QStorageInfoPrivate::retrieveVolumeInfo() @@ -204,4 +209,139 @@ QStorageInfo QStorageInfoPrivate::root() return QStorageInfo(QDir::fromNativeSeparators(QFile::decodeName(qgetenv("SystemDrive")))); } +bool QStorageInfoPrivate::queryStorageProperty() +{ + QString path = QDir::toNativeSeparators(uR"(\\.\)" + rootPath); + if (path.endsWith(u'\\')) + path.chop(1); + + HANDLE handle = CreateFile(reinterpret_cast<const wchar_t *>(path.utf16()), + 0, // no access to the drive + FILE_SHARE_READ | FILE_SHARE_WRITE, + nullptr, + OPEN_EXISTING, + 0, + nullptr); + if (handle == INVALID_HANDLE_VALUE) + return false; + + STORAGE_PROPERTY_QUERY spq; + memset(&spq, 0, sizeof(spq)); + spq.PropertyId = StorageAccessAlignmentProperty; + spq.QueryType = PropertyStandardQuery; + + STORAGE_ACCESS_ALIGNMENT_DESCRIPTOR saad; + memset(&saad, 0, sizeof(saad)); + + DWORD bytes = 0; + BOOL result = DeviceIoControl(handle, + IOCTL_STORAGE_QUERY_PROPERTY, + &spq, sizeof(spq), + &saad, sizeof(saad), + &bytes, + nullptr); + CloseHandle(handle); + if (result) + blockSize = saad.BytesPerPhysicalSector; + return result; +} + +struct Helper +{ + QBasicMutex mutex; + QSystemLibrary ntdll {u"ntdll"_qs}; +}; +Q_GLOBAL_STATIC(Helper, gNtdllHelper) + +inline QFunctionPointer resolveSymbol(QSystemLibrary *ntdll, const char *name) +{ + QFunctionPointer symbolFunctionPointer = ntdll->resolve(name); + if (Q_UNLIKELY(!symbolFunctionPointer)) + qWarning("Failed to resolve the symbol: %s", name); + return symbolFunctionPointer; +} + +#define GENERATE_SYMBOL(symbolName, returnType, ...) \ +using Qt##symbolName = returnType (NTAPI *) (__VA_ARGS__); \ +static Qt##symbolName qt##symbolName = nullptr; + +#define RESOLVE_SYMBOL(name) \ + do { \ + qt##name = reinterpret_cast<Qt##name>(resolveSymbol(ntdll, #name)); \ + if (!qt##name) \ + return false; \ + } while (false) + +GENERATE_SYMBOL(RtlInitUnicodeString, void, PUNICODE_STRING, PCWSTR); +GENERATE_SYMBOL(NtCreateFile, NTSTATUS, PHANDLE, ACCESS_MASK, POBJECT_ATTRIBUTES, + PIO_STATUS_BLOCK, PLARGE_INTEGER, ULONG, ULONG, ULONG, ULONG, PVOID, ULONG); +GENERATE_SYMBOL(NtQueryVolumeInformationFile, NTSTATUS, HANDLE, PIO_STATUS_BLOCK, PVOID, ULONG, + FS_INFORMATION_CLASS); + +void QStorageInfoPrivate::queryFileFsSectorSizeInformation() +{ + static bool symbolsResolved = [](auto ntdllHelper) { + QMutexLocker locker(&ntdllHelper->mutex); + auto ntdll = &ntdllHelper->ntdll; + if (!ntdll->isLoaded()) { + if (!ntdll->load()) { + qWarning("Unable to load ntdll.dll."); + return false; + } + } + + RESOLVE_SYMBOL(RtlInitUnicodeString); + RESOLVE_SYMBOL(NtCreateFile); + RESOLVE_SYMBOL(NtQueryVolumeInformationFile); + + return true; + }(gNtdllHelper()); + if (!symbolsResolved) + return; + + FILE_FS_SECTOR_SIZE_INFORMATION ffssi; + memset(&ffssi, 0, sizeof(ffssi)); + + HANDLE handle = nullptr; + + OBJECT_ATTRIBUTES attrs; + memset(&attrs, 0, sizeof(attrs)); + + IO_STATUS_BLOCK isb; + memset(&isb, 0, sizeof(isb)); + + QString path = QDir::toNativeSeparators(uR"(\??\\)" + rootPath); + if (!path.endsWith(u'\\')) + path.append(u'\\'); + + UNICODE_STRING name; + qtRtlInitUnicodeString(&name, reinterpret_cast<const wchar_t *>(path.utf16())); + + InitializeObjectAttributes(&attrs, &name, 0, nullptr, nullptr); + + NTSTATUS status = qtNtCreateFile(&handle, + FILE_READ_ATTRIBUTES, + &attrs, + &isb, + nullptr, + FILE_ATTRIBUTE_NORMAL, + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, + FILE_OPEN, + 0, + nullptr, + 0); + if (!NT_SUCCESS(status)) + return; + + memset(&isb, 0, sizeof(isb)); + status = qtNtQueryVolumeInformationFile(handle, + &isb, + &ffssi, + sizeof(ffssi), + FS_INFORMATION_CLASS(10)); // FileFsSectorSizeInformation + CloseHandle(handle); + if (NT_SUCCESS(status)) + blockSize = ffssi.PhysicalBytesPerSectorForAtomicity; +} + QT_END_NAMESPACE |