Jamie Reece Wilson
b35b290afd
[+] AuFS::EFSDeviceType [+] AuFS::EFSBusType [+] AuFS::LogicalUsedResponse [+] AuFS::LogicalOffsetResponse [+] AuFS::FSLogicalPartition [+] AuFS::FSDevice [+] AuFS::GetRootFromPath [+] AuFS::GetDeviceFromPath [+] AuFS::GetDeviceFromRoot [+] AuFS::GetLogicalMountFromPath [+] AuFS::TrySimplifyDevicePath [+] AuFS::GetPerformanceBufferSizeFromPath [+] AuFS::GetPhysicalSectorSizeFromPath [+] AuFS::GetLogicalSectorSizeFromPath [+] AuFS::GetLogicalUsedFromPath [+] AuFS::GetLogicalUsedFromLogicalDevice [+] AuFS::GetDeviceSizeInBytes [+] AuFS::GetDeviceModel [+] AuFS::GetFSDeviceByFilePath [+] AuFS::GetFSDeviceByDevice [+] AuFS::GetFSDevices [+] AuFS::GetFSDevicesCachedUnsafe [+] AuFS::ResetDeviceCache [+] FSPlatformDevices.cpp [+] FSPlatformDevices.NT.cpp (holding back Linux)
1073 lines
37 KiB
C++
1073 lines
37 KiB
C++
/***
|
|
Copyright (C) 2023 Jamie Reece Wilson (a/k/a "Reece"). All rights reserved.
|
|
|
|
File: FSPlatformDevices.NT.cpp
|
|
Date: 2023-08-11
|
|
Date: 2023-12-05
|
|
Author: Reece
|
|
***/
|
|
#include <Source/RuntimeInternal.hpp>
|
|
#include "FS.hpp"
|
|
#include "FSPlatformDevices.hpp"
|
|
|
|
#if defined(AURORA_PLATFORM_WIN32)
|
|
#include <shlwapi.h>
|
|
#include <winioctl.h>
|
|
#include <setupapi.h>
|
|
#include <winternl.h>
|
|
#include <Ntddvol.h>
|
|
#else
|
|
#include <pathcch.h>
|
|
#endif
|
|
|
|
namespace Aurora::IO::FS
|
|
{
|
|
struct NetworkDrive
|
|
{
|
|
AuString comment;
|
|
AuString mountpoint;
|
|
AuString remoteOrigin;
|
|
AuString hostname;
|
|
};
|
|
|
|
static const auto kImScaredAndDisorientated { 4096u };
|
|
static const auto kBUsePerformantIoRecommendation { true };// false /*accurate for testing*/
|
|
static const auto kdUsePerformantIoRecommendationRequiresOrderOfMangitudeCoefficient { 1.f };
|
|
|
|
static AuString PathToMount(const AuString &root);
|
|
|
|
AUKN_SYM AuString GetRootFromPath(const AuString &path)
|
|
{
|
|
auto widePath = Locale::ConvertFromUTF8(NormalizePathRet(path));
|
|
#if defined(AURORA_PLATFORM_WIN32)
|
|
|
|
// PathStripToRoot is utterly worthless garbage that cannot parse network addresses
|
|
#if 0
|
|
// do not switch to PathCchStripToRoot!
|
|
// (wont remove support for Windows Vista/7!)
|
|
widePath.reserve(4096 * 3);
|
|
return PathStripToRootW(widePath.data()) ? Locale::ConvertFromWChar(widePath.c_str()) : "";
|
|
#else
|
|
std::wstring str2(32u * 1024u + 10, L'\x00');
|
|
// i perfer this Kernel32 api (desktop-class only)
|
|
// update: i was right. you need the Win8 shell api thats in UWP for network paths to work properly
|
|
auto almostBestPath = ::GetVolumePathNameW(widePath.c_str(), str2.data(), str2.size()) ?
|
|
Locale::ConvertFromWChar(str2.c_str()) :
|
|
"";
|
|
return PathToMount(almostBestPath); /* expand root mountpoints to a network address, if possible*/
|
|
#endif
|
|
#else
|
|
return PathCchStripToRoot(widePath.data(), widePath.size()) ? Locale::ConvertFromWChar(widePath.c_str()) : "";
|
|
#endif
|
|
}
|
|
|
|
AUKN_SYM AuResult<AuString> GetDeviceFromPath(const AuString &path)
|
|
{
|
|
return GetDeviceFromRoot(GetRootFromPath(path));
|
|
}
|
|
|
|
AUKN_SYM AuResult<AuString> GetLogicalMountFromPath(const AuString &fileOrDirPath)
|
|
{
|
|
auto root = GetRootFromPath(fileOrDirPath);
|
|
|
|
if (AuStartsWith(root, "\\\\"))
|
|
{
|
|
if (!AuStartsWith(root, "\\\\.\\"))
|
|
{
|
|
if (root.find("GLOBAL?") == root.npos)
|
|
{
|
|
return root;
|
|
}
|
|
}
|
|
}
|
|
|
|
auto utf8Root = fmt::format("\\\\.\\{}", root);
|
|
auto utf8Root2 = utf8Root;
|
|
if (utf8Root.ends_with("\\"))
|
|
{
|
|
utf8Root.pop_back();
|
|
}
|
|
|
|
return utf8Root;
|
|
}
|
|
|
|
AUKN_SYM AuResult<AuString> GetDeviceFromRoot(const AuString& root)
|
|
{
|
|
VOLUME_DISK_EXTENTS extents;
|
|
DWORD dwBytesWritten {};
|
|
|
|
auto utf8Root = fmt::format("\\\\.\\{}", root);
|
|
auto utf8Root2 = utf8Root;
|
|
if (utf8Root.ends_with("\\"))
|
|
{
|
|
utf8Root.pop_back();
|
|
}
|
|
|
|
auto widePath = Locale::ConvertFromUTF8(utf8Root);
|
|
|
|
if (::GetDriveTypeW(Locale::ConvertFromUTF8(utf8Root2).data()) != DRIVE_FIXED)
|
|
{
|
|
// Assume device is the same as the logical device
|
|
// This is generally true when the volume has no partitioning and only has one filesystem - think: CDs, network mounts, etc
|
|
// Only a subset of FIXED disks get a \\.\PhysicalDevice
|
|
// The path we currently have is usually good enough for whatever IOCTLs we need to send (the same cant be said for fixed disks tho)
|
|
// (they fail; they need the following...)
|
|
return utf8Root;
|
|
}
|
|
|
|
auto hFile = ::CreateFileW(widePath.c_str(),
|
|
0,
|
|
FILE_SHARE_WRITE | FILE_SHARE_READ,
|
|
nullptr,
|
|
OPEN_EXISTING,
|
|
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_BACKUP_SEMANTICS,
|
|
nullptr);
|
|
|
|
if (hFile == INVALID_HANDLE_VALUE)
|
|
{
|
|
SysPushErrorHAL("Couldn't find file device [1] {}", GetLastError());
|
|
return "";
|
|
}
|
|
|
|
if (!::DeviceIoControl(hFile,
|
|
IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS,
|
|
nullptr, 0,
|
|
&extents, sizeof(extents),
|
|
&dwBytesWritten,
|
|
nullptr))
|
|
{
|
|
AuWin32CloseHandle(hFile);
|
|
|
|
if (auto opt = GetFSDeviceByFilePath(root))
|
|
{
|
|
auto copy = opt->devicePath;
|
|
return AuMove(copy);
|
|
}
|
|
else
|
|
{
|
|
return {};
|
|
}
|
|
}
|
|
|
|
AuWin32CloseHandle(hFile);
|
|
|
|
if (extents.NumberOfDiskExtents)
|
|
{
|
|
return fmt::format("\\\\.\\PhysicalDrive{}", extents.Extents->DiskNumber);
|
|
}
|
|
else
|
|
{
|
|
return {};
|
|
}
|
|
}
|
|
|
|
static AuUInt32 GetSectorSizeFromPathFromDevice(const AuString &path, bool bIsLogical)
|
|
{
|
|
DISK_GEOMETRY_EX geo {};
|
|
DWORD cbBytesReturned;
|
|
bool bSuccess {};
|
|
|
|
auto rootPath = GetRootFromPath(path);
|
|
if (rootPath.empty())
|
|
{
|
|
SysPushErrorIO();
|
|
return kImScaredAndDisorientated;
|
|
}
|
|
|
|
auto physPath = GetDeviceFromRoot(rootPath);
|
|
if (!physPath)
|
|
{
|
|
SysPushErrorIO();
|
|
return kImScaredAndDisorientated;
|
|
}
|
|
|
|
auto widePath = Locale::ConvertFromUTF8(physPath.GetResult());
|
|
if (widePath.empty())
|
|
{
|
|
SysPushErrorIO();
|
|
return kImScaredAndDisorientated;
|
|
}
|
|
|
|
auto hDevice = ::CreateFileW(widePath.c_str(),
|
|
0,
|
|
0,
|
|
nullptr,
|
|
OPEN_EXISTING,
|
|
0,
|
|
nullptr);
|
|
|
|
if (hDevice == INVALID_HANDLE_VALUE)
|
|
{
|
|
SysPushErrorIO();
|
|
AuWin32CloseHandle(hDevice);
|
|
return kImScaredAndDisorientated;
|
|
}
|
|
|
|
bSuccess = ::DeviceIoControl(hDevice,
|
|
IOCTL_DISK_GET_DRIVE_GEOMETRY_EX,
|
|
NULL, 0,
|
|
(LPVOID)&geo, sizeof(geo),
|
|
&cbBytesReturned,
|
|
nullptr);
|
|
|
|
AuWin32CloseHandle(hDevice);
|
|
|
|
if (bSuccess)
|
|
{
|
|
return geo.Geometry.BytesPerSector;
|
|
}
|
|
else
|
|
{
|
|
return kImScaredAndDisorientated;
|
|
}
|
|
}
|
|
|
|
AUKN_SYM AuUInt32 GetLogicalSectorSizeFromPath(const AuString &path)
|
|
{
|
|
// TODO: ioctl BLKSSZGET under linux or GetPhysicalSectorSizeFromPath(path)
|
|
|
|
// Ports-Note: we cannot depend on FILE_STORAGE_INFO because that ioctl is dependent on Windows 8
|
|
// though, it is available under UWP. Might be useful
|
|
|
|
if (auto dwBestGuessLogicalSize = GetSectorSizeFromPathFromDevice(path, true))
|
|
{
|
|
return dwBestGuessLogicalSize;
|
|
}
|
|
else
|
|
{
|
|
auto rootPath = GetRootFromPath(path);
|
|
auto widePath = Locale::ConvertFromUTF8(rootPath);
|
|
DWORD dwSectorsPerCluster,
|
|
dwBytesPerSector,
|
|
dwNumberOfFreeClusters,
|
|
dwTotalNumberOfClusters;
|
|
|
|
if (::GetDiskFreeSpaceW(widePath.c_str(),
|
|
&dwSectorsPerCluster,
|
|
&dwBytesPerSector,
|
|
&dwNumberOfFreeClusters,
|
|
&dwTotalNumberOfClusters))
|
|
{
|
|
return dwBytesPerSector *
|
|
dwSectorsPerCluster; // this should equal `Bytes Per Cluster` under `fsutil fsinfo ntfsinfo C:`
|
|
}
|
|
else
|
|
{
|
|
return {};
|
|
}
|
|
}
|
|
}
|
|
|
|
AUKN_SYM AuUInt32 GetPerformanceBufferSizeFromPath(const AuString &path)
|
|
{
|
|
if (kBUsePerformantIoRecommendation)
|
|
{
|
|
return (float)GetPhysicalSectorSizeFromPath(path) * 2048.f // trust me bro. optimized for throughput and memory usage (requires 1-8MB)
|
|
// (based on crystal disk mark, nvme, real sata, spinning rust, and other real world values)
|
|
* (float)kdUsePerformantIoRecommendationRequiresOrderOfMangitudeCoefficient /* bruteforce dumb applications */;
|
|
}
|
|
else
|
|
{
|
|
// ...a more programmatic path. one cluster per queue, perhaps? well, that'll be our base recommendation for now.
|
|
auto rootPath = GetRootFromPath(path);
|
|
auto widePath = Locale::ConvertFromUTF8(rootPath);
|
|
DWORD dwSectorsPerCluster,
|
|
dwBytesPerSector,
|
|
dwNumberOfFreeClusters,
|
|
dwTotalNumberOfClusters;
|
|
if (::GetDiskFreeSpaceW(widePath.c_str(),
|
|
&dwSectorsPerCluster,
|
|
&dwBytesPerSector,
|
|
&dwNumberOfFreeClusters,
|
|
&dwTotalNumberOfClusters))
|
|
{
|
|
return dwBytesPerSector *
|
|
dwSectorsPerCluster; // this should equal `Bytes Per Cluster` under `fsutil fsinfo ntfsinfo C:` (old windows) or `fsutil fsinfo sectorinfo C:` (new windows)
|
|
}
|
|
else
|
|
{
|
|
return GetPhysicalSectorSizeFromPath(path);
|
|
}
|
|
}
|
|
}
|
|
|
|
AUKN_SYM AuUInt32 GetPhysicalSectorSizeFromPath(const AuString &path)
|
|
{
|
|
return GetSectorSizeFromPathFromDevice(path, false);
|
|
}
|
|
|
|
AUKN_SYM LogicalUsedResponse GetLogicalUsedFromPath(const AuString &path)
|
|
{
|
|
auto rootPath = GetLogicalMountFromPath(path);
|
|
if (!rootPath)
|
|
{
|
|
return {};
|
|
}
|
|
|
|
return GetLogicalUsedFromLogicalDevice(rootPath.GetResult());
|
|
}
|
|
|
|
AUKN_SYM LogicalUsedResponse GetLogicalUsedFromLogicalDevice(const AuString &logicalMountPath)
|
|
{
|
|
auto widePath = Locale::ConvertFromUTF8(logicalMountPath);
|
|
|
|
DWORD dwSectorsPerCluster,
|
|
dwBytesPerSector,
|
|
dwNumberOfFreeClusters,
|
|
dwTotalNumberOfClusters;
|
|
|
|
if (::GetDiskFreeSpaceW(widePath.c_str(),
|
|
&dwSectorsPerCluster,
|
|
&dwBytesPerSector,
|
|
&dwNumberOfFreeClusters,
|
|
&dwTotalNumberOfClusters))
|
|
{
|
|
AuUInt64 qwBytesPerCluster = AuUInt64(dwBytesPerSector) * AuUInt64(dwSectorsPerCluster);
|
|
AuUInt64 qwTotalBytes = qwBytesPerCluster * AuUInt64(dwTotalNumberOfClusters);
|
|
AuUInt64 qwFreeBytes = qwBytesPerCluster * AuUInt64(dwNumberOfFreeClusters);
|
|
AuUInt64 qwUsedBytes = qwTotalBytes - qwFreeBytes;
|
|
|
|
return LogicalUsedResponse { qwTotalBytes, qwUsedBytes };
|
|
}
|
|
else
|
|
{
|
|
return {};
|
|
}
|
|
}
|
|
|
|
AUKN_SYM AuUInt64 GetDeviceSizeInBytes(const AuString &physicalDevicePath)
|
|
{
|
|
DISK_GEOMETRY_EX geo {};
|
|
DWORD cbBytesReturned;
|
|
bool bSuccess {};
|
|
|
|
auto widePath = Locale::ConvertFromUTF8(physicalDevicePath);
|
|
if (widePath.empty())
|
|
{
|
|
SysPushErrorIO();
|
|
return 0;
|
|
}
|
|
|
|
auto hDevice = ::CreateFileW(widePath.c_str(),
|
|
0,
|
|
0,
|
|
nullptr,
|
|
OPEN_EXISTING,
|
|
0,
|
|
nullptr);
|
|
|
|
if (hDevice == INVALID_HANDLE_VALUE)
|
|
{
|
|
SysPushErrorIO();
|
|
AuWin32CloseHandle(hDevice);
|
|
return 0;
|
|
}
|
|
|
|
bSuccess = ::DeviceIoControl(hDevice,
|
|
IOCTL_DISK_GET_DRIVE_GEOMETRY_EX,
|
|
NULL, 0,
|
|
(LPVOID)&geo, sizeof(geo),
|
|
&cbBytesReturned,
|
|
nullptr);
|
|
|
|
AuWin32CloseHandle(hDevice);
|
|
|
|
if (bSuccess)
|
|
{
|
|
return geo.DiskSize.QuadPart;
|
|
}
|
|
else
|
|
{
|
|
SysPushErrorIO();
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
|
|
#if defined(AURORA_PLATFORM_WIN32)
|
|
|
|
struct VolumeExtendedDataInfo_t
|
|
{
|
|
|
|
};
|
|
|
|
using VolumeDatabaseEntry_t = AuTuple<AuString/*strVolumePath*/, AuUInt32 /*uDiskNumberNumber*/, AuUInt32 /*uPartitionNumber*/, AuUInt32 /*uDevType*/, AuUInt64 /*uSectionOffset*/, AuUInt64 /*uSectionLength*/, VolumeExtendedDataInfo_t>;
|
|
using VolumeDatabase_t = AuList<VolumeDatabaseEntry_t>;
|
|
|
|
static void CopyVolumes(FSDevice &device, const VolumeDatabaseEntry_t &entry);
|
|
|
|
static bool DoSetupDiRecursion(LPCGUID pGuidInferface,
|
|
PCWSTR pszEnumerator,
|
|
AuList<FSDevice> &devices,
|
|
VolumeDatabase_t &vols)
|
|
{
|
|
HDEVINFO hIntDevInfo {};
|
|
HANDLE hDevice = INVALID_HANDLE_VALUE;
|
|
PSP_DEVICE_INTERFACE_DETAIL_DATA_W pInterfaceDetailData {};
|
|
|
|
if (!pSetupDiGetClassDevsW)
|
|
{
|
|
return {};
|
|
}
|
|
|
|
{
|
|
hIntDevInfo = pSetupDiGetClassDevsW(pGuidInferface,
|
|
pszEnumerator,
|
|
NULL,
|
|
pGuidInferface ? DIGCF_PRESENT | DIGCF_DEVICEINTERFACE : DIGCF_ALLCLASSES | DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
|
|
if (hIntDevInfo == INVALID_HANDLE_VALUE)
|
|
{
|
|
return {};
|
|
}
|
|
|
|
for (DWORD dwIndex = {};
|
|
;
|
|
dwIndex++)
|
|
{
|
|
SP_DEVICE_INTERFACE_DATA interfaceData {};
|
|
SP_DEVINFO_DATA deviceInfoData {};
|
|
DWORD dwDataType, dwRequiredSize;
|
|
BOOL bSuccess;
|
|
FSDevice device;
|
|
|
|
deviceInfoData.cbSize = sizeof(deviceInfoData);
|
|
interfaceData.cbSize = sizeof(interfaceData);
|
|
|
|
bSuccess = pSetupDiEnumDeviceInterfaces(hIntDevInfo, NULL, pGuidInferface, dwIndex, &interfaceData);
|
|
if (!bSuccess)
|
|
{
|
|
if (GetLastError() == ERROR_NO_MORE_ITEMS)
|
|
{
|
|
break;
|
|
}
|
|
|
|
SysPushErrorIO();
|
|
break;
|
|
}
|
|
|
|
dwRequiredSize = 0;
|
|
bSuccess = pSetupDiGetDeviceInterfaceDetailW(hIntDevInfo, &interfaceData, NULL, 0, &dwRequiredSize, NULL);
|
|
if ((!bSuccess && GetLastError() != ERROR_INSUFFICIENT_BUFFER) || dwRequiredSize == 0)
|
|
{
|
|
SysPushErrorIO();
|
|
continue;
|
|
}
|
|
|
|
if (pInterfaceDetailData)
|
|
{
|
|
AuMemory::Free(pInterfaceDetailData);
|
|
pInterfaceDetailData = nullptr;
|
|
}
|
|
|
|
pInterfaceDetailData = (PSP_DEVICE_INTERFACE_DETAIL_DATA_W)AuMemory::_ZAlloc(dwRequiredSize);
|
|
if (!pInterfaceDetailData)
|
|
{
|
|
SysPushErrorMemory();
|
|
continue;
|
|
}
|
|
|
|
pInterfaceDetailData->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);
|
|
|
|
bSuccess = pSetupDiGetDeviceInterfaceDetailW(hIntDevInfo, &interfaceData,
|
|
pInterfaceDetailData, dwRequiredSize,
|
|
&dwRequiredSize, &deviceInfoData);
|
|
if (!bSuccess)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
hDevice = ::CreateFileW(pInterfaceDetailData->DevicePath,
|
|
0,
|
|
0x00000007,
|
|
nullptr,
|
|
OPEN_EXISTING,
|
|
0,
|
|
nullptr);
|
|
|
|
if (hDevice != INVALID_HANDLE_VALUE)
|
|
{
|
|
STORAGE_DEVICE_NUMBER sdn {};
|
|
DISK_GEOMETRY_EX geo {};
|
|
DWORD cbBytesReturned;
|
|
TCHAR szBuffer[4096];
|
|
|
|
device.devicePath = AuLocale::ConvertFromWChar(pInterfaceDetailData->DevicePath);
|
|
|
|
if (device.devicePath.size() && device.devicePath[device.devicePath.size() - 1] == '}')
|
|
{
|
|
auto itr = device.devicePath.find_last_of("#");
|
|
if (itr != AuString::npos)
|
|
{
|
|
device.uuid = uuids::uuid::from_string(device.devicePath.substr(itr + 2, 36)).value_or(uuids::uuid{});
|
|
}
|
|
}
|
|
|
|
bSuccess = ::DeviceIoControl(hDevice,
|
|
IOCTL_DISK_GET_DRIVE_GEOMETRY_EX,
|
|
nullptr,
|
|
0,
|
|
(LPVOID)&geo, sizeof(geo),
|
|
&cbBytesReturned,
|
|
nullptr);
|
|
if (bSuccess)
|
|
{
|
|
device.uFSDevicePageSizeInBytes = geo.Geometry.BytesPerSector;
|
|
device.uFSDeviceSizeInBytes = geo.DiskSize.QuadPart;
|
|
}
|
|
|
|
bSuccess = ::DeviceIoControl(hDevice,
|
|
IOCTL_STORAGE_GET_DEVICE_NUMBER,
|
|
NULL, 0,
|
|
(LPVOID)&sdn, sizeof(sdn),
|
|
&cbBytesReturned,
|
|
nullptr);
|
|
if (bSuccess)
|
|
{
|
|
for (const auto &volRef : vols)
|
|
{
|
|
if (AuGet<1>(volRef) == sdn.DeviceNumber &&
|
|
AuGet<3>(volRef) == sdn.DeviceType)
|
|
{
|
|
CopyVolumes(device, volRef);
|
|
}
|
|
}
|
|
}
|
|
|
|
bSuccess = pSetupDiGetDeviceRegistryPropertyA(hIntDevInfo, &deviceInfoData, SPDRP_CLASS, &dwDataType,
|
|
(PBYTE)szBuffer, sizeof(szBuffer), &dwRequiredSize);
|
|
if (bSuccess)
|
|
{
|
|
device.bus = EFSBusType::eBusGeneric;
|
|
|
|
if (strcmp("CDROM", szBuffer) == 0)
|
|
{
|
|
device.type = EFSDeviceType::eDeviceCD;
|
|
}
|
|
else if (strcmp("DiskDrive", szBuffer) == 0)
|
|
{
|
|
device.type = EFSDeviceType::eDeviceDisk;
|
|
}
|
|
else if (strcmp("USB", szBuffer) == 0)
|
|
{
|
|
device.type = EFSDeviceType::eDeviceUSBMass;
|
|
device.bus = EFSBusType::eBusUSB;
|
|
}
|
|
else
|
|
{
|
|
if (pGuidInferface == (GUID *)&GUID_DEVINTERFACE_DISK)
|
|
{
|
|
device.type = EFSDeviceType::eDeviceDisk;
|
|
}
|
|
else if (pGuidInferface == (GUID *)&GUID_DEVINTERFACE_FLOPPY)
|
|
{
|
|
device.type = EFSDeviceType::eDeviceFloppy;
|
|
}
|
|
else if (pGuidInferface == (GUID *)&GUID_DEVINTERFACE_CDROM)
|
|
{
|
|
device.type = EFSDeviceType::eDeviceCD;
|
|
}
|
|
else if (pGuidInferface == (GUID *)&GUID_DEVINTERFACE_VMLUN)
|
|
{
|
|
device.type = EFSDeviceType::eDeviceSCSI;
|
|
device.bus = EFSBusType::eBusSCSI;
|
|
}
|
|
}
|
|
}
|
|
|
|
bSuccess = pSetupDiGetDeviceRegistryPropertyA(hIntDevInfo, &deviceInfoData, SPDRP_HARDWAREID, &dwDataType,
|
|
(PBYTE)szBuffer, sizeof(szBuffer), &dwRequiredSize);
|
|
|
|
bSuccess = pSetupDiGetDeviceRegistryPropertyA(hIntDevInfo, &deviceInfoData, SPDRP_FRIENDLYNAME, &dwDataType,
|
|
(PBYTE)szBuffer, sizeof(szBuffer), &dwRequiredSize);
|
|
|
|
if (bSuccess)
|
|
{
|
|
device.productModel = szBuffer;
|
|
}
|
|
|
|
bSuccess = pSetupDiGetDeviceRegistryPropertyA(hIntDevInfo, &deviceInfoData, SPDRP_DEVICEDESC, &dwDataType,
|
|
(PBYTE)szBuffer, sizeof(szBuffer), &dwRequiredSize);
|
|
if (bSuccess)
|
|
{
|
|
device.altProductDescription = szBuffer;
|
|
}
|
|
|
|
if (!device.uFSDevicePageSizeInBytes)
|
|
{
|
|
device.uFSDevicePageSizeInBytes = kImScaredAndDisorientated;
|
|
}
|
|
|
|
devices.push_back(device);
|
|
}
|
|
|
|
::CloseHandle(hDevice);
|
|
hDevice = INVALID_HANDLE_VALUE;
|
|
}
|
|
}
|
|
|
|
{
|
|
if (pInterfaceDetailData)
|
|
{
|
|
AuMemory::Free(pInterfaceDetailData);
|
|
}
|
|
|
|
if (hDevice != INVALID_HANDLE_VALUE)
|
|
{
|
|
::CloseHandle(hDevice);
|
|
}
|
|
|
|
if (hIntDevInfo)
|
|
{
|
|
pSetupDiDestroyDeviceInfoList(hIntDevInfo);
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static AuString ResolveObjSymLink(const AuString &reeceWasHere)
|
|
{
|
|
OBJECT_ATTRIBUTES objectAttributes;
|
|
ULONG uReturnedLength {};
|
|
NTSTATUS uStatus;
|
|
HANDLE hHandle;
|
|
|
|
if (!pNtOpenSymbolicLinkObject)
|
|
{
|
|
return reeceWasHere;
|
|
}
|
|
|
|
auto path = reeceWasHere;
|
|
AuReplaceAll(path, "\\\\?", "\\GLOBAL??");
|
|
auto widePath = Locale::ConvertFromUTF8(path);
|
|
|
|
UNICODE_STRING targetDevice;
|
|
targetDevice.Buffer = widePath.data();
|
|
targetDevice.Length = (USHORT)widePath.size() * 2;
|
|
|
|
InitializeObjectAttributes(&objectAttributes, &targetDevice, OBJ_CASE_INSENSITIVE, NULL, NULL);
|
|
uStatus = pNtOpenSymbolicLinkObject(&hHandle, 1, &objectAttributes);
|
|
if (uStatus != 0)
|
|
{
|
|
return reeceWasHere;
|
|
}
|
|
|
|
if (hHandle == INVALID_HANDLE_VALUE)
|
|
{
|
|
return reeceWasHere;
|
|
}
|
|
|
|
std::wstring buffer(32 * 1024, L'\00');
|
|
|
|
UNICODE_STRING linkTarget;
|
|
linkTarget.Buffer = buffer.data();
|
|
linkTarget.Length = linkTarget.MaximumLength = (USHORT)buffer.size(); // meh, any big buffer will do. if it's too big, it wont work.
|
|
|
|
uStatus = pNtQuerySymbolicLinkObject(hHandle, &linkTarget, &uReturnedLength);
|
|
if (uStatus != 0)
|
|
{
|
|
AuWin32CloseHandle(hHandle);
|
|
return path;
|
|
}
|
|
|
|
if (uReturnedLength == 0)
|
|
{
|
|
AuWin32CloseHandle(hHandle);
|
|
AU_THROW_CONST_STRING("TrySimplifyDevicePath() failed.");
|
|
}
|
|
|
|
buffer.resize(uReturnedLength);
|
|
|
|
path = AuLocale::ConvertFromWChar(buffer.c_str());
|
|
AuWin32CloseHandle(hHandle);
|
|
|
|
return path;
|
|
}
|
|
|
|
bool DoLameAndSlowVolIteration(VolumeDatabase_t &database)
|
|
{
|
|
std::wstring strVolumePath;
|
|
|
|
strVolumePath.resize(16 * 1024);
|
|
auto hVolume = ::FindFirstVolumeW(strVolumePath.data(), strVolumePath.size());
|
|
|
|
if (hVolume == INVALID_HANDLE_VALUE)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
do
|
|
{
|
|
{
|
|
int i = wcslen(strVolumePath.c_str());
|
|
if (!i)
|
|
{
|
|
i = 1;
|
|
}
|
|
strVolumePath[i - 1] = '\x00';
|
|
}
|
|
|
|
auto hVolume = ::CreateFileW(strVolumePath.c_str(),
|
|
0,
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
|
NULL,
|
|
OPEN_EXISTING,
|
|
0,
|
|
0);
|
|
|
|
if (hVolume == INVALID_HANDLE_VALUE)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
static constexpr AuUInt kBRet = sizeof(VOLUME_DISK_EXTENTS) + 256 * sizeof(DISK_EXTENT);
|
|
char tempVolumeDiskExtents[kBRet];
|
|
|
|
typedef struct _VOLUME_DISK_EXTENTS
|
|
{
|
|
DWORD NumberOfDiskExtents;
|
|
DISK_EXTENT Extents[ANYSIZE_ARRAY];
|
|
} VOLUME_DISK_EXTENTS, *PVOLUME_DISK_EXTENTS;
|
|
|
|
PVOLUME_DISK_EXTENTS pVde = (PVOLUME_DISK_EXTENTS)tempVolumeDiskExtents;
|
|
DWORD dwBytesReturned {};
|
|
STORAGE_DEVICE_NUMBER sdn {};
|
|
|
|
auto bSuccess = ::DeviceIoControl(hVolume,
|
|
IOCTL_STORAGE_GET_DEVICE_NUMBER,
|
|
NULL, 0,
|
|
(LPVOID)&sdn, sizeof(sdn),
|
|
&dwBytesReturned,
|
|
nullptr);
|
|
if (!bSuccess)
|
|
{
|
|
AuResetMember(sdn);
|
|
}
|
|
|
|
if (::DeviceIoControl(hVolume, IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS, NULL, 0, (void *)pVde, AuSizeOf(tempVolumeDiskExtents), &dwBytesReturned, NULL))
|
|
{
|
|
for (AU_ITERATE_N(i, pVde->NumberOfDiskExtents))
|
|
{
|
|
const auto &refExtent = pVde->Extents[i];
|
|
database.push_back({ AuLocale::ConvertFromWChar(strVolumePath.c_str()/*intentionally lose the length.*/),
|
|
(AuUInt32)refExtent.DiskNumber,
|
|
(AuUInt32)sdn.PartitionNumber,
|
|
(AuUInt32)sdn.DeviceType,
|
|
(AuUInt64)refExtent.StartingOffset.QuadPart,
|
|
(AuUInt64)refExtent.ExtentLength.QuadPart,
|
|
{} });
|
|
}
|
|
}
|
|
|
|
AuWin32CloseHandle(hVolume);
|
|
}
|
|
while (::FindNextVolumeW(hVolume, strVolumePath.data(), strVolumePath.size()) != 0);
|
|
|
|
::FindVolumeClose(hVolume);
|
|
return true;
|
|
}
|
|
|
|
static AuString PathToMount(const AuString &root)
|
|
{
|
|
auto widePath = Locale::ConvertFromUTF8(root);
|
|
|
|
if (::GetDriveTypeW(widePath.c_str()) == DRIVE_REMOTE)
|
|
{
|
|
char temp[32 * 1024 * 3];
|
|
DWORD tempLength = sizeof(temp);
|
|
|
|
if (pWNetGetUniversalNameW &&
|
|
pWNetGetUniversalNameW(widePath.c_str(), REMOTE_NAME_INFO_LEVEL, temp, &tempLength) == NO_ERROR)
|
|
{
|
|
auto pDescriptionOfDrive = (LPREMOTE_NAME_INFOW)temp;
|
|
return AuLocale::ConvertFromWChar(pDescriptionOfDrive->lpUniversalName);
|
|
}
|
|
else
|
|
{
|
|
return root;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// anyone else? NTFS mountpoints? tbd
|
|
return root;
|
|
}
|
|
}
|
|
|
|
static AuOptional<AuString> GetBestVolLabel(const AuString &volumeName)
|
|
{
|
|
auto widePath = Locale::ConvertFromUTF8(volumeName);
|
|
wchar_t buffer[4096] {};
|
|
DWORD dwBufferSize { AuArraySize(buffer) };
|
|
|
|
if (!GetVolumeInformationW(widePath.c_str(), buffer, dwBufferSize, {}, {}, {}, {}, {}))
|
|
{
|
|
return {};
|
|
}
|
|
|
|
return AuLocale::ConvertFromWChar(buffer);
|
|
}
|
|
|
|
static AuList<AuString> GetMountpointsFromVolume(const AuString &volumeName)
|
|
{
|
|
DWORD dwCharCount { 4096 };
|
|
PWCHAR pNameIterator {};
|
|
BOOL bSuccess {};
|
|
AuList<wchar_t> names;
|
|
AuList<AuString> ret;
|
|
|
|
auto widePath = Locale::ConvertFromUTF8(volumeName);
|
|
|
|
while (true)
|
|
{
|
|
names.clear();
|
|
names.resize(dwCharCount);
|
|
|
|
if ((bSuccess = GetVolumePathNamesForVolumeNameW(widePath.c_str(), names.data(), dwCharCount, &dwCharCount)))
|
|
{
|
|
pNameIterator = names.data();
|
|
break;
|
|
}
|
|
|
|
if (GetLastError() != ERROR_MORE_DATA)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!bSuccess)
|
|
{
|
|
return {};
|
|
}
|
|
|
|
for (pNameIterator = names.data();
|
|
pNameIterator[0] != L'\0';
|
|
pNameIterator += wcslen(pNameIterator) + 1)
|
|
{
|
|
ret.push_back(AuLocale::ConvertFromWChar(pNameIterator));
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void CopyVolumes(FSDevice &device, const VolumeDatabaseEntry_t &entry)
|
|
{
|
|
FSLogicalPartition partition;
|
|
const auto &[strVolumePath, uDiskNumberNumber, uPartitionNumber, uDevType, uSectionOffset, uSectionLength, ex] = entry;
|
|
partition.devicePath = device.devicePath;
|
|
partition.logicalMount = strVolumePath + "\\";
|
|
partition.filesystemMountPoints = GetMountpointsFromVolume(partition.logicalMount);
|
|
partition.offset.uLogicalSize = uSectionLength;
|
|
partition.offset.uLogicalOffset = uSectionOffset;
|
|
|
|
if (partition.logicalMount.empty())
|
|
{
|
|
partition.logicalMount = partition.devicePath;
|
|
}
|
|
|
|
if (!partition.filesystemMountPoints.empty())
|
|
{
|
|
partition.space = GetLogicalUsedFromLogicalDevice(partition.filesystemMountPoints[0]);
|
|
}
|
|
|
|
if (AuStartsWith(strVolumePath, "\\\\?\\Volume{"))
|
|
{
|
|
partition.uuid = uuids::uuid::from_string(strVolumePath.substr(11, strVolumePath.size() - 11 - 1)).value_or(uuids::uuid{});
|
|
}
|
|
|
|
partition.name = GetBestVolLabel(partition.logicalMount);
|
|
|
|
device.partitions.push_back(partition);
|
|
}
|
|
|
|
static void ReadExtendedVolumeInfo(VolumeDatabase_t &vols)
|
|
{
|
|
|
|
}
|
|
|
|
static void EnumerateNetworkShares(NETRESOURCEW *pRsrc,
|
|
DWORD scope,
|
|
DWORD type,
|
|
DWORD usage,
|
|
DWORD displayType,
|
|
AuList<NetworkDrive> &netDrives)
|
|
{
|
|
AuList<char> buffer(512 * 1024);
|
|
DWORD dwBufferSize = buffer.size();
|
|
HANDLE hHandle {};
|
|
DWORD dwResult {};
|
|
DWORD dwEntries { (DWORD) -1 };
|
|
|
|
if (!pWNetOpenEnumW)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (pWNetOpenEnumW(scope, type, usage, pRsrc, &hHandle) != NO_ERROR)
|
|
{
|
|
return;
|
|
}
|
|
|
|
do
|
|
{
|
|
dwResult = pWNetEnumResourceW(hHandle, &dwEntries, buffer.data(), &dwBufferSize);
|
|
|
|
if (dwResult == NO_ERROR)
|
|
{
|
|
auto pNetResources = (NETRESOURCEW *)buffer.data();
|
|
|
|
for (AU_ITERATE_N(i, dwEntries))
|
|
{
|
|
auto pRsrc = &pNetResources[i];
|
|
|
|
if (pRsrc->dwDisplayType == displayType)
|
|
{
|
|
NetworkDrive drive;
|
|
|
|
if (pRsrc->lpRemoteName)
|
|
{
|
|
drive.remoteOrigin = AuLocale::ConvertFromWChar(pRsrc->lpRemoteName) + "\\";
|
|
|
|
if (AuStartsWith(drive.remoteOrigin, "\\\\"))
|
|
{
|
|
auto itr = drive.remoteOrigin.find('\\', 2);
|
|
if (itr != drive.remoteOrigin.npos)
|
|
{
|
|
drive.hostname = drive.remoteOrigin.substr(2, itr - 2);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (pRsrc->lpLocalName)
|
|
{
|
|
drive.mountpoint = AuLocale::ConvertFromWChar(pRsrc->lpLocalName) + "\\";
|
|
}
|
|
|
|
if (pRsrc->lpComment)
|
|
{
|
|
drive.comment = AuLocale::ConvertFromWChar(pRsrc->lpComment);
|
|
}
|
|
|
|
netDrives.push_back(drive);
|
|
}
|
|
|
|
if (pRsrc->dwUsage & RESOURCEUSAGE_CONTAINER)
|
|
{
|
|
EnumerateNetworkShares(pRsrc, scope, type, usage, displayType, netDrives);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
while (dwResult == NO_ERROR &&
|
|
dwEntries);
|
|
|
|
pWNetCloseEnum(hHandle);
|
|
}
|
|
|
|
static AuList<NetworkDrive> EnumerateNetworkShares()
|
|
{
|
|
AuList<NetworkDrive> netDrives;
|
|
EnumerateNetworkShares({}, RESOURCE_REMEMBERED, RESOURCETYPE_DISK, RESOURCEUSAGE_ALL, RESOURCEDISPLAYTYPE_SHARE, netDrives);
|
|
return netDrives;
|
|
}
|
|
|
|
static void EnumNetworkMounts(AuList<FSDevice> &devices)
|
|
{
|
|
for (const auto &a : EnumerateNetworkShares())
|
|
{
|
|
FSDevice *pDevice {};
|
|
AuArray<AuUInt8, 16> hash;
|
|
AuHashing::MD4(a.hostname, hash);
|
|
hash[8] &= 0xBF;
|
|
hash[8] |= 0x80;
|
|
hash[6] &= 0x4F;
|
|
hash[6] |= 0x40;
|
|
|
|
uuids::uuid uid(hash.begin(), hash.end());
|
|
|
|
for (auto &dev : devices)
|
|
{
|
|
if (dev.uuid == uid &&
|
|
dev.bus == EFSBusType::eBusNetwork)
|
|
{
|
|
pDevice = &dev;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!pDevice)
|
|
{
|
|
devices.push_back(FSDevice {});
|
|
pDevice = &devices[devices.size() - 1];
|
|
}
|
|
|
|
pDevice->uuid = uid;
|
|
pDevice->bus = EFSBusType::eBusNetwork;
|
|
pDevice->type = EFSDeviceType::eDeviceNetworkMount;
|
|
pDevice->altLabel = a.comment.size() ? a.comment : "Network Drive";
|
|
pDevice->altProductDescription = fmt::format("Server ({})", a.hostname);
|
|
pDevice->devicePath = a.hostname;
|
|
pDevice->uFSDevicePageSizeInBytes = 4096;
|
|
|
|
{
|
|
FSLogicalPartition partition;
|
|
|
|
AuArray<AuUInt8, 16> hash;
|
|
AuHashing::MD4(a.remoteOrigin, hash);
|
|
hash[8] &= 0xBF;
|
|
hash[8] |= 0x80;
|
|
hash[6] &= 0x4F;
|
|
hash[6] |= 0x40;
|
|
|
|
partition.uuid = uuids::uuid(hash.begin(), hash.end());
|
|
partition.filesystemMountPoints = { a.mountpoint };
|
|
partition.logicalMount = a.remoteOrigin;
|
|
partition.space = GetLogicalUsedFromLogicalDevice(a.mountpoint);
|
|
partition.offset = { partition.space.uLogicalSize, 0 };
|
|
partition.name = GetBestVolLabel(a.mountpoint);
|
|
partition.devicePath = pDevice->devicePath;
|
|
|
|
pDevice->partitions.push_back(partition);
|
|
pDevice->uFSDeviceSizeInBytes += partition.space.uLogicalSize;
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
AUKN_SYM AuString TrySimplifyDevicePath(const AuString &deviceOrLogicalMountPath)
|
|
{
|
|
#if defined(AURORA_PLATFORM_WIN32)
|
|
return ResolveObjSymLink(deviceOrLogicalMountPath);
|
|
#else
|
|
return deviceOrLogicalMountPath;
|
|
#endif
|
|
}
|
|
|
|
AuList<FSDevice> SysGetFSDevices()
|
|
{
|
|
AU_LOCK_GUARD(gFSDirMutex);
|
|
AuList<FSDevice> devices;
|
|
|
|
#if defined(AURORA_PLATFORM_WIN32)
|
|
VolumeDatabase_t vols;
|
|
|
|
(void)DoLameAndSlowVolIteration(vols);
|
|
ReadExtendedVolumeInfo(vols);
|
|
|
|
DoSetupDiRecursion((GUID *)&GUID_DEVINTERFACE_DISK, {}, devices, vols);
|
|
DoSetupDiRecursion((GUID *)&GUID_DEVINTERFACE_VMLUN, {}, devices, vols);
|
|
DoSetupDiRecursion((GUID *)&GUID_DEVINTERFACE_CDROM, {}, devices, vols);
|
|
DoSetupDiRecursion((GUID *)&GUID_DEVINTERFACE_FLOPPY, {}, devices, vols);
|
|
DoSetupDiRecursion((GUID *)&GUID_DEVINTERFACE_PARTITION, {}, devices, vols);
|
|
|
|
EnumNetworkMounts(devices);
|
|
|
|
return devices;
|
|
#endif
|
|
return {};
|
|
}
|
|
} |