summaryrefslogtreecommitdiffstats
path: root/src/corelib/io/qstorageinfo_win.cpp
blob: 3582612508ce16e2b8dac8801b10de0665c15806 (plain)
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260
// Copyright (C) 2014 Ivan Komissarov <ABBAPOH@gmail.com>// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only#include"qstorageinfo_p.h"#include <QtCore/qdir.h>#include <QtCore/qfileinfo.h>#include <QtCore/qmutex.h>#include <QtCore/qvarlengtharray.h>#include <QtCore/private/wcharhelpers_win_p.h>#include"qfilesystementry_p.h"#include"qntdll_p.h"extern"C" NTSTATUS NTSYSCALLAPI NTAPI NtQueryVolumeInformationFile(HANDLE, PIO_STATUS_BLOCK, PVOID, ULONG, FS_INFORMATION_CLASS); QT_BEGIN_NAMESPACE using namespaceQt::StringLiterals;static const int defaultBufferSize = MAX_PATH +1;static QString canonicalPath(const QString &rootPath){ QString path =QDir::toNativeSeparators(QFileInfo(rootPath).canonicalFilePath());if(path.isEmpty())return path;if(path.startsWith("\\\\?\\"_L1)) path.remove(0,4);if(path.length() <2|| path.at(1) != u':')returnQString(); path[0] = path[0].toUpper();if(!(path.at(0).unicode() >='A'&& path.at(0).unicode() <='Z'))returnQString();if(!path.endsWith(u'\\')) path.append(u'\\');return path;}voidQStorageInfoPrivate::initRootPath(){// Do not unnecessarily call QFileInfo::canonicalFilePath() if the path is// already a drive root since it may hang on network drives.const QString path =QFileSystemEntry::isDriveRootPath(rootPath)?QDir::toNativeSeparators(rootPath):canonicalPath(rootPath);if(path.isEmpty()) { valid = ready =false;return;}// ### test if disk mounted to folder on other diskwchar_t buffer[defaultBufferSize];if(::GetVolumePathName(reinterpret_cast<const wchar_t*>(path.utf16()), buffer, defaultBufferSize)) rootPath =QDir::fromNativeSeparators(QString::fromWCharArray(buffer));else valid = ready =false;}staticinline QByteArray getDevice(const QString &rootPath){const QString path =QDir::toNativeSeparators(rootPath);const UINT type = ::GetDriveType(reinterpret_cast<const wchar_t*>(path.utf16()));if(type == DRIVE_REMOTE) { QVarLengthArray<char,256>buffer(256); DWORD bufferLength = buffer.size(); DWORD result; UNIVERSAL_NAME_INFO *remoteNameInfo;do{ buffer.resize(bufferLength); remoteNameInfo =reinterpret_cast<UNIVERSAL_NAME_INFO *>(buffer.data()); result = ::WNetGetUniversalName(reinterpret_cast<const wchar_t*>(path.utf16()), UNIVERSAL_NAME_INFO_LEVEL, remoteNameInfo,&bufferLength);}while(result == ERROR_MORE_DATA);if(result == NO_ERROR)returnQString::fromWCharArray(remoteNameInfo->lpUniversalName).toUtf8();returnQByteArray();}wchar_t deviceBuffer[51];if(::GetVolumeNameForVolumeMountPoint(reinterpret_cast<const wchar_t*>(path.utf16()), deviceBuffer,sizeof(deviceBuffer) /sizeof(wchar_t))) {returnQString::fromWCharArray(deviceBuffer).toLatin1();}returnQByteArray();}voidQStorageInfoPrivate::doStat(){ valid = ready =true;initRootPath();if(!valid || !ready)return;retrieveVolumeInfo();if(!valid || !ready)return; device =getDevice(rootPath);retrieveDiskFreeSpace();if(!queryStorageProperty())queryFileFsSectorSizeInformation();}voidQStorageInfoPrivate::retrieveVolumeInfo(){const UINT oldmode = ::SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOOPENFILEERRORBOX);const QString path =QDir::toNativeSeparators(rootPath);wchar_t nameBuffer[defaultBufferSize];wchar_t fileSystemTypeBuffer[defaultBufferSize]; DWORD fileSystemFlags =0;const bool result = ::GetVolumeInformation(reinterpret_cast<const wchar_t*>(path.utf16()), nameBuffer, defaultBufferSize,nullptr,nullptr,&fileSystemFlags, fileSystemTypeBuffer, defaultBufferSize);if(!result) { ready =false; valid = ::GetLastError() == ERROR_NOT_READY;}else{ fileSystemType =QString::fromWCharArray(fileSystemTypeBuffer).toLatin1(); name =QString::fromWCharArray(nameBuffer); readOnly = (fileSystemFlags & FILE_READ_ONLY_VOLUME) !=0;}::SetErrorMode(oldmode);}voidQStorageInfoPrivate::retrieveDiskFreeSpace(){const UINT oldmode = ::SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOOPENFILEERRORBOX);const QString path =QDir::toNativeSeparators(rootPath); ready = ::GetDiskFreeSpaceEx(reinterpret_cast<const wchar_t*>(path.utf16()),PULARGE_INTEGER(&bytesAvailable),PULARGE_INTEGER(&bytesTotal),PULARGE_INTEGER(&bytesFree));::SetErrorMode(oldmode);} QList<QStorageInfo>QStorageInfoPrivate::mountedVolumes(){ QList<QStorageInfo> volumes; QString driveName =QStringLiteral("A:/");const UINT oldmode = ::SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOOPENFILEERRORBOX); quint32 driveBits =quint32(::GetLogicalDrives()) &0x3ffffff;::SetErrorMode(oldmode);while(driveBits) {if(driveBits &1) { QStorageInfo drive(driveName);if(!drive.rootPath().isEmpty())// drive exists, but not mounted volumes.append(drive);} driveName[0] =QChar(driveName[0].unicode() +1); driveBits = driveBits >>1;}return volumes;}boolQStorageInfoPrivate::queryStorageProperty(){ QString path =QDir::toNativeSeparators(uR"(\\.\)"+ rootPath);if(path.endsWith(u'\\')) path.chop(1); HANDLE handle =CreateFile(qt_castToWchar(path),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 =int(saad.BytesPerPhysicalSector);return result;}voidQStorageInfoPrivate::queryFileFsSectorSizeInformation(){ 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;::RtlInitUnicodeString(&name,qt_castToWchar(path));InitializeObjectAttributes(&attrs, &name,0,nullptr,nullptr); NTSTATUS status = ::NtCreateFile(&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 = ::NtQueryVolumeInformationFile(handle,&isb,&ffssi,sizeof(ffssi),FS_INFORMATION_CLASS(10));// FileFsSectorSizeInformationCloseHandle(handle);if(NT_SUCCESS(status)) blockSize = ffssi.PhysicalBytesPerSectorForAtomicity;} QT_END_NAMESPACE 
close