Stroika Library 3.0d16
 
Loading...
Searching...
No Matches
Disk.cpp
1/*
2 * Copyright(c) Sophist Solutions, Inc. 1990-2025. All rights reserved
3 */
4#include "Stroika/Foundation/StroikaPreComp.h"
5
6#if qStroika_Foundation_Common_Platform_Windows
7#include <Windows.h>
8#include <winioctl.h>
9#endif
10
15
16#include "Disk.h"
17
18using namespace Stroika::Foundation;
21using namespace Stroika::Foundation::IO;
23
24/*
25 * I thought this might be useful. maybe at some point? But so far it doesn't appear super useful
26 * or needed.
27 * But we can use this to find out the disk kind for physical devices
28 * --LGP 2015-09-30
29 */
30#ifndef qCaptureDiskDeviceInfoWindows_
31#define qCaptureDiskDeviceInfoWindows_ 0
32#endif
33
34#if qCaptureDiskDeviceInfoWindows_
35#include <devguid.h>
36#include <regstr.h>
37#include <setupapi.h>
38#pragma comment(lib, "Setupapi.lib")
39DEFINE_GUID (GUID_DEVINTERFACE_DISK, 0x53f56307L, 0xb6bf, 0x11d0, 0x94, 0xf2, 0x00, 0xa0, 0xc9, 0x1e, 0xfb, 0x8b);
40#endif
41
42/*
43 ********************************************************************************
44 ************************** IO::FileSystem::DiskInfoType ************************
45 ********************************************************************************
46 */
48{
50 sb << "{"sv;
51 sb << "Device-Name: "sv << fDeviceName;
52 if (fDeviceKind) {
53 sb << ", Device-Kind: '"sv << *fDeviceKind << "'"sv;
54 }
55 if (fSizeInBytes) {
56 sb << ", Size-In-Bytes: "sv << *fSizeInBytes;
57 }
58 sb << "}"sv;
59 return sb;
60}
61
62/*
63 ********************************************************************************
64 **************************** FileSystem::GetAvailableDisks *********************
65 ********************************************************************************
66 */
67#if qCaptureDiskDeviceInfoWindows_
68namespace {
69 list<wstring> GetPhysicalDiskDeviceInfo_ ()
70 {
71 HDEVINFO hDeviceInfoSet;
72 ULONG ulMemberIndex;
73 ULONG ulErrorCode;
74 BOOL bFound = FALSE;
75 BOOL bOk;
76 list<wstring> disks;
77
78 // create a HDEVINFO with all present devices
79 hDeviceInfoSet = ::SetupDiGetClassDevs (&GUID_DEVINTERFACE_DISK, NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
80 if (hDeviceInfoSet == INVALID_HANDLE_VALUE) {
81 _ASSERT (FALSE);
82 return disks;
83 }
84
85 // enumerate through all devices in the set
86 ulMemberIndex = 0;
87 while (TRUE) {
88 // get device info
89 SP_DEVINFO_DATA deviceInfoData;
90 deviceInfoData.cbSize = sizeof (SP_DEVINFO_DATA);
91 if (!::SetupDiEnumDeviceInfo (hDeviceInfoSet, ulMemberIndex, &deviceInfoData)) {
92 if (::GetLastError () == ERROR_NO_MORE_ITEMS) {
93 // ok, reached end of the device enumeration
94 break;
95 }
96 else {
97 // error
98 _ASSERT (FALSE);
99 ::SetupDiDestroyDeviceInfoList (hDeviceInfoSet);
100 return disks;
101 }
102 }
103
104 // get device interfaces
105 SP_DEVICE_INTERFACE_DATA deviceInterfaceData;
106 deviceInterfaceData.cbSize = sizeof (SP_DEVICE_INTERFACE_DATA);
107 if (!::SetupDiEnumDeviceInterfaces (hDeviceInfoSet, NULL, &GUID_DEVINTERFACE_DISK, ulMemberIndex, &deviceInterfaceData)) {
108 if (::GetLastError () == ERROR_NO_MORE_ITEMS) {
109 // ok, reached end of the device enumeration
110 break;
111 }
112 else {
113 // error
114 _ASSERT (FALSE);
115 ::SetupDiDestroyDeviceInfoList (hDeviceInfoSet);
116 return disks;
117 }
118 }
119
120 // process the next device next time
121 ulMemberIndex++;
122
123 // get hardware id of the device
124 ULONG ulPropertyRegDataType = 0;
125 ULONG ulRequiredSize = 0;
126 ULONG ulBufferSize = 0;
127 BYTE* pbyBuffer = NULL;
128 if (!::SetupDiGetDeviceRegistryProperty (hDeviceInfoSet, &deviceInfoData, SPDRP_HARDWAREID, &ulPropertyRegDataType, NULL, 0, &ulRequiredSize)) {
129 if (::GetLastError () == ERROR_INSUFFICIENT_BUFFER) {
130 pbyBuffer = (BYTE*)::malloc (ulRequiredSize);
131 ulBufferSize = ulRequiredSize;
132 if (!::SetupDiGetDeviceRegistryProperty (hDeviceInfoSet, &deviceInfoData, SPDRP_HARDWAREID, &ulPropertyRegDataType,
133 pbyBuffer, ulBufferSize, &ulRequiredSize)) {
134 // getting the hardware id failed
135 _ASSERT (FALSE);
136 ::SetupDiDestroyDeviceInfoList (hDeviceInfoSet);
137 ::free (pbyBuffer);
138 return disks;
139 }
140 }
141 else {
142 // getting device registry property failed
143 _ASSERT (FALSE);
144 ::SetupDiDestroyDeviceInfoList (hDeviceInfoSet);
145 return disks;
146 }
147 }
148 else {
149 // getting hardware id of the device succeeded unexpectedly
150 _ASSERT (FALSE);
151 ::SetupDiDestroyDeviceInfoList (hDeviceInfoSet);
152 return disks;
153 }
154
155 // pbyBuffer is initialized now!
156 LPCWSTR pszHardwareId = (LPCWSTR)pbyBuffer;
157
158 // retrieve detailed information about the device
159 // (especially the device path which is needed to create the device object)
160 SP_DEVICE_INTERFACE_DETAIL_DATA* pDeviceInterfaceDetailData = NULL;
161 ULONG ulDeviceInterfaceDetailDataSize = 0;
162 ulRequiredSize = 0;
163 bOk = ::SetupDiGetDeviceInterfaceDetail (hDeviceInfoSet, &deviceInterfaceData, pDeviceInterfaceDetailData,
164 ulDeviceInterfaceDetailDataSize, &ulRequiredSize, NULL);
165 if (!bOk) {
166 ulErrorCode = ::GetLastError ();
167 if (ulErrorCode == ERROR_INSUFFICIENT_BUFFER) {
168 // insufficient buffer space
169 // => that's ok, allocate enough space and try again
170 pDeviceInterfaceDetailData = (SP_DEVICE_INTERFACE_DETAIL_DATA*)::malloc (ulRequiredSize);
171 pDeviceInterfaceDetailData->cbSize = sizeof (SP_DEVICE_INTERFACE_DETAIL_DATA);
172 ulDeviceInterfaceDetailDataSize = ulRequiredSize;
173 deviceInfoData.cbSize = sizeof (SP_DEVINFO_DATA);
174 bOk = ::SetupDiGetDeviceInterfaceDetail (hDeviceInfoSet, &deviceInterfaceData, pDeviceInterfaceDetailData,
175 ulDeviceInterfaceDetailDataSize, &ulRequiredSize, &deviceInfoData);
176 ulErrorCode = ::GetLastError ();
177 }
178
179 if (!bOk) {
180 // retrieving detailed information about the device failed
181 _ASSERT (FALSE);
182 ::free (pbyBuffer);
183 ::free (pDeviceInterfaceDetailData);
184 ::SetupDiDestroyDeviceInfoList (hDeviceInfoSet);
185 return disks;
186 }
187 }
188 else {
189 // retrieving detailed information about the device succeeded unexpectedly
190 _ASSERT (FALSE);
191 ::free (pbyBuffer);
192 ::SetupDiDestroyDeviceInfoList (hDeviceInfoSet);
193 return disks;
194 }
195
196 disks.push_back (pDeviceInterfaceDetailData->DevicePath);
197
198 // free buffer for device interface details
199 ::free (pDeviceInterfaceDetailData);
200
201 // free buffer
202 ::free (pbyBuffer);
203 }
204
205 // destroy device info list
206 ::SetupDiDestroyDeviceInfoList (hDeviceInfoSet);
207
208 return disks;
209 }
210}
211#endif
212
213namespace {
214 filesystem::path GetPhysNameForDriveNumber_ (unsigned int i)
215 {
216 // This format is NOT super well documented, and was mostly derived from reading the remarks section
217 // of https://msdn.microsoft.com/en-us/library/windows/desktop/aa363216%28v=vs.85%29.aspx?f=255&MSPPError=-2147217396
218 // (DeviceIoControl function)
219 return "\\\\.\\PhysicalDrive{}"_f(i).As<filesystem::path> ();
220 }
221}
223{
224 KeyedCollection<DiskInfoType, filesystem::path> result{[] (DiskInfoType e) { return e.fDeviceName; }};
225
226#if qStroika_Foundation_Common_Platform_Windows
227#if qCaptureDiskDeviceInfoWindows_ && 0
228 for (const auto& s : GetPhysicalDiskDeviceInfo_ ()) {
229 DbgTrace (L"s=%s", s.c_str ());
230 }
231#endif
232
233 for (int i = 0; i < 64; ++i) {
234 HANDLE hHandle = ::CreateFileW (GetPhysNameForDriveNumber_ (i).c_str (), GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING,
235 FILE_ATTRIBUTE_NORMAL, nullptr);
236 if (hHandle == INVALID_HANDLE_VALUE) {
237 break;
238 }
239 GET_LENGTH_INFORMATION li{};
240 {
241 DWORD dwBytesReturned{};
242 BOOL bResult = ::DeviceIoControl (hHandle, IOCTL_DISK_GET_LENGTH_INFO, nullptr, 0, &li, sizeof (li), &dwBytesReturned, nullptr);
243 if (bResult == 0) {
244 DbgTrace ("failed - DeviceIoControl - IOCTL_DISK_GET_LENGTH_INFO - ignored"_f);
245 continue;
246 }
247 }
248 DISK_GEOMETRY driveInfo{};
249 {
250 DWORD dwBytesReturned{};
251 BOOL bResult =
252 ::DeviceIoControl (hHandle, IOCTL_DISK_GET_DRIVE_GEOMETRY, nullptr, 0, &driveInfo, sizeof (driveInfo), &dwBytesReturned, nullptr);
253 if (bResult == 0) {
254 DbgTrace ("failed - DeviceIoControl - IOCTL_DISK_GET_DRIVE_GEOMETRY - ignored"_f);
255 continue;
256 }
257 }
258 ::CloseHandle (hHandle);
259
260 /*
261 * Is the 'disk' a 'remote' device (network), CD-ROM, direct-attached hard disk (e.g. internal) or removable drive,
262 */
263 optional<BlockDeviceKind> deviceKind;
264 switch (driveInfo.MediaType) {
265 case FixedMedia:
266 deviceKind = BlockDeviceKind::eLocalDisk;
267 break;
268 case RemovableMedia:
270 break;
271 }
272 DiskInfoType di{GetPhysNameForDriveNumber_ (i), deviceKind, static_cast<uint64_t> (li.Length.QuadPart)};
273 result.Add (di);
274 }
275#endif
276 return result;
277}
#define DbgTrace
Definition Trace.h:309
Similar to String, but intended to more efficiently construct a String. Mutable type (String is large...
String is like std::u32string, except it is much easier to use, often much more space efficient,...
Definition String.h:201
a cross between Mapping<KEY, T> and Collection<T> and Set<T>
Containers::KeyedCollection< DiskInfoType, filesystem::path > GetAvailableDisks()
Definition Disk.cpp:222