AuroraRuntime/Source/IO/FS/FSPlatformDevices.NT.cpp

1070 lines
37 KiB
C++
Raw Normal View History

/***
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 AuString(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 AuString(utf8Root);
}
auto hFile = Win32Open(widePath.c_str(),
0,
FILE_SHARE_WRITE | FILE_SHARE_READ,
false,
OPEN_EXISTING,
2024-04-10 07:20:47 +00:00
FILE_FLAG_BACKUP_SEMANTICS,
FILE_ATTRIBUTE_NORMAL);
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 AuString(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 = Win32Open(widePath.c_str(),
0,
0,
false,
OPEN_EXISTING);
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 = Win32Open(widePath.c_str(),
0,
0,
false,
OPEN_EXISTING);
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 = Win32Open(pInterfaceDetailData->DevicePath,
0,
0x00000007,
false,
OPEN_EXISTING);
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;
}
}
}
if (AuCodepointsToLower(device.devicePath).find("ven_nvme") != AuString::npos)
{
device.bus = EFSBusType::eBusNVMePCIe;
}
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 = Win32Open(strVolumePath.c_str(),
0,
FILE_SHARE_READ | FILE_SHARE_WRITE,
false,
OPEN_EXISTING);
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_GLOBAL_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 {};
}
}