1 /*
2 * PROJECT: ReactOS API Tests
3 * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
4 * PURPOSE: Test for IOCTL_MOUNTMGR_QUERY_DOS_VOLUME_PATH(S)
5 * COPYRIGHT: Copyright 2025 Hermès Bélusca-Maïto <hermes.belusca-maito@reactos.org>
6 */
7
8 #include "precomp.h"
9
10
11 /**
12 * @brief
13 * Invokes either IOCTL_MOUNTMGR_QUERY_DOS_VOLUME_PATH or
14 * IOCTL_MOUNTMGR_QUERY_DOS_VOLUME_PATHS for testing, given
15 * the volume device name, and returns an allocated volume
16 * paths buffer. This buffer must be freed by the caller via
17 * RtlFreeHeap(RtlGetProcessHeap(), ...) .
18 *
19 * These IOCTLs return both the drive letter (if any) and the
20 * volume GUID symlink path, as well as any other file-system
21 * mount reparse points linking to the volume.
22 **/
23 static VOID
Call_QueryDosVolume_Path_Paths(_In_ HANDLE MountMgrHandle,_In_ PCWSTR NtVolumeName,_In_ ULONG IoctlPathOrPaths,_Out_ PMOUNTMGR_VOLUME_PATHS * pVolumePathPtr)24 Call_QueryDosVolume_Path_Paths(
25 _In_ HANDLE MountMgrHandle,
26 _In_ PCWSTR NtVolumeName,
27 _In_ ULONG IoctlPathOrPaths,
28 _Out_ PMOUNTMGR_VOLUME_PATHS* pVolumePathPtr)
29 {
30 NTSTATUS Status;
31 ULONG Length;
32 IO_STATUS_BLOCK IoStatusBlock;
33 UNICODE_STRING VolumeName;
34 MOUNTMGR_VOLUME_PATHS VolumePath;
35 PMOUNTMGR_VOLUME_PATHS VolumePathPtr;
36 ULONG DeviceNameLength;
37 /*
38 * This variable is used to query the device name.
39 * It's based on MOUNTMGR_TARGET_NAME (mountmgr.h).
40 * Doing it this way prevents memory allocation.
41 * The device name won't be longer.
42 */
43 struct
44 {
45 USHORT NameLength;
46 WCHAR DeviceName[256];
47 } DeviceName;
48
49
50 *pVolumePathPtr = NULL;
51
52 /* First, build the corresponding device name */
53 RtlInitUnicodeString(&VolumeName, NtVolumeName);
54 DeviceName.NameLength = VolumeName.Length;
55 RtlCopyMemory(&DeviceName.DeviceName, VolumeName.Buffer, VolumeName.Length);
56 DeviceNameLength = FIELD_OFFSET(MOUNTMGR_TARGET_NAME, DeviceName) + DeviceName.NameLength;
57
58 /* Now, query the MountMgr for the DOS path(s) */
59 Status = NtDeviceIoControlFile(MountMgrHandle,
60 NULL, NULL, NULL,
61 &IoStatusBlock,
62 IoctlPathOrPaths,
63 &DeviceName, DeviceNameLength,
64 &VolumePath, sizeof(VolumePath));
65
66 /* Check for unsupported device */
67 if (Status == STATUS_NO_MEDIA_IN_DEVICE || Status == STATUS_INVALID_DEVICE_REQUEST)
68 {
69 skip("Device '%S': Doesn't support MountMgr queries, Status 0x%08lx\n",
70 NtVolumeName, Status);
71 return;
72 }
73
74 /* The only tolerated failure here is buffer too small, which is expected */
75 ok(NT_SUCCESS(Status) || (Status == STATUS_BUFFER_OVERFLOW),
76 "Device '%S': IOCTL 0x%lx failed unexpectedly, Status 0x%08lx\n",
77 NtVolumeName, IoctlPathOrPaths, Status);
78 if (!NT_SUCCESS(Status) && (Status != STATUS_BUFFER_OVERFLOW))
79 {
80 skip("Device '%S': Wrong Status\n", NtVolumeName);
81 return;
82 }
83
84 /* Compute the needed size to store the DOS path(s).
85 * Even if MOUNTMGR_VOLUME_PATHS allows bigger name lengths
86 * than MAXUSHORT, we can't use them, because we have to return
87 * this in an UNICODE_STRING that stores length in a USHORT. */
88 Length = sizeof(VolumePath) + VolumePath.MultiSzLength;
89 ok(Length <= MAXUSHORT,
90 "Device '%S': DOS volume path too large: %lu\n",
91 NtVolumeName, Length);
92 if (Length > MAXUSHORT)
93 {
94 skip("Device '%S': Wrong Length\n", NtVolumeName);
95 return;
96 }
97
98 /* Allocate the buffer and fill it with test pattern */
99 VolumePathPtr = RtlAllocateHeap(RtlGetProcessHeap(), 0, Length);
100 if (!VolumePathPtr)
101 {
102 skip("Device '%S': Failed to allocate buffer with size %lu)\n",
103 NtVolumeName, Length);
104 return;
105 }
106 RtlFillMemory(VolumePathPtr, Length, 0xCC);
107
108 /* Re-query the DOS path(s) with the proper size */
109 Status = NtDeviceIoControlFile(MountMgrHandle,
110 NULL, NULL, NULL,
111 &IoStatusBlock,
112 IoctlPathOrPaths,
113 &DeviceName, DeviceNameLength,
114 VolumePathPtr, Length);
115 ok(NT_SUCCESS(Status),
116 "Device '%S': IOCTL 0x%lx failed unexpectedly, Status 0x%08lx\n",
117 NtVolumeName, IoctlPathOrPaths, Status);
118
119 if (winetest_debug > 1)
120 {
121 trace("Buffer:\n");
122 DumpBuffer(VolumePathPtr, Length);
123 printf("\n");
124 }
125
126 /* Return the buffer */
127 *pVolumePathPtr = VolumePathPtr;
128 }
129
130 /**
131 * @brief
132 * Invokes IOCTL_MOUNTMGR_QUERY_POINTS for testing, given
133 * the volume device name, and returns an allocated mount
134 * points buffer. This buffer must be freed by the caller
135 * via RtlFreeHeap(RtlGetProcessHeap(), ...) .
136 *
137 * This IOCTL only returns both the drive letter (if any)
138 * and the volume GUID symlink path, but does NOT return
139 * file-system mount reparse points.
140 **/
141 static VOID
Call_QueryPoints(_In_ HANDLE MountMgrHandle,_In_ PCWSTR NtVolumeName,_Out_ PMOUNTMGR_MOUNT_POINTS * pMountPointsPtr)142 Call_QueryPoints(
143 _In_ HANDLE MountMgrHandle,
144 _In_ PCWSTR NtVolumeName,
145 _Out_ PMOUNTMGR_MOUNT_POINTS* pMountPointsPtr)
146 {
147 NTSTATUS Status;
148 ULONG Length;
149 IO_STATUS_BLOCK IoStatusBlock;
150 UNICODE_STRING VolumeName;
151 MOUNTMGR_MOUNT_POINTS MountPoints;
152 PMOUNTMGR_MOUNT_POINTS MountPointsPtr;
153 /*
154 * This variable is used to query the device name.
155 * It's based on MOUNTMGR_MOUNT_POINT (mountmgr.h).
156 * Doing it this way prevents memory allocation.
157 * The device name won't be longer.
158 */
159 struct
160 {
161 MOUNTMGR_MOUNT_POINT;
162 WCHAR DeviceName[256];
163 } DeviceName;
164
165
166 *pMountPointsPtr = NULL;
167
168 /* First, build the corresponding device name */
169 RtlInitUnicodeString(&VolumeName, NtVolumeName);
170 DeviceName.SymbolicLinkNameOffset = DeviceName.UniqueIdOffset = 0;
171 DeviceName.SymbolicLinkNameLength = DeviceName.UniqueIdLength = 0;
172 DeviceName.DeviceNameOffset = ((ULONG_PTR)&DeviceName.DeviceName - (ULONG_PTR)&DeviceName);
173 DeviceName.DeviceNameLength = VolumeName.Length;
174 RtlCopyMemory(&DeviceName.DeviceName, VolumeName.Buffer, VolumeName.Length);
175
176 /* Now, query the MountMgr for the mount points */
177 Status = NtDeviceIoControlFile(MountMgrHandle,
178 NULL, NULL, NULL,
179 &IoStatusBlock,
180 IOCTL_MOUNTMGR_QUERY_POINTS,
181 &DeviceName, sizeof(DeviceName),
182 &MountPoints, sizeof(MountPoints));
183
184 /* Check for unsupported device */
185 if (Status == STATUS_NO_MEDIA_IN_DEVICE || Status == STATUS_INVALID_DEVICE_REQUEST)
186 {
187 skip("Device '%S': Doesn't support MountMgr queries, Status 0x%08lx\n",
188 NtVolumeName, Status);
189 return;
190 }
191
192 /* The only tolerated failure here is buffer too small, which is expected */
193 ok(NT_SUCCESS(Status) || (Status == STATUS_BUFFER_OVERFLOW),
194 "Device '%S': IOCTL 0x%lx failed unexpectedly, Status 0x%08lx\n",
195 NtVolumeName, IOCTL_MOUNTMGR_QUERY_POINTS, Status);
196 if (!NT_SUCCESS(Status) && (Status != STATUS_BUFFER_OVERFLOW))
197 {
198 skip("Device '%S': Wrong Status\n", NtVolumeName);
199 return;
200 }
201
202 /* Compute the needed size to retrieve the mount points */
203 Length = MountPoints.Size;
204
205 /* Allocate the buffer and fill it with test pattern */
206 MountPointsPtr = RtlAllocateHeap(RtlGetProcessHeap(), 0, Length);
207 if (!MountPointsPtr)
208 {
209 skip("Device '%S': Failed to allocate buffer with size %lu)\n",
210 NtVolumeName, Length);
211 return;
212 }
213 RtlFillMemory(MountPointsPtr, Length, 0xCC);
214
215 /* Re-query the mount points with the proper size */
216 Status = NtDeviceIoControlFile(MountMgrHandle,
217 NULL, NULL, NULL,
218 &IoStatusBlock,
219 IOCTL_MOUNTMGR_QUERY_POINTS,
220 &DeviceName, sizeof(DeviceName),
221 MountPointsPtr, Length);
222 ok(NT_SUCCESS(Status),
223 "Device '%S': IOCTL 0x%lx failed unexpectedly, Status 0x%08lx\n",
224 NtVolumeName, IOCTL_MOUNTMGR_QUERY_POINTS, Status);
225
226 if (winetest_debug > 1)
227 {
228 trace("IOCTL_MOUNTMGR_QUERY_POINTS returned:\n"
229 " Size: %lu\n"
230 " NumberOfMountPoints: %lu\n",
231 MountPointsPtr->Size,
232 MountPointsPtr->NumberOfMountPoints);
233
234 trace("Buffer:\n");
235 DumpBuffer(MountPointsPtr, Length);
236 printf("\n");
237 }
238
239 /* Return the buffer */
240 *pMountPointsPtr = MountPointsPtr;
241 }
242
243
244 #define IS_DRIVE_LETTER_PFX(s) \
245 ((s)->Length >= 2*sizeof(WCHAR) && (s)->Buffer[0] >= 'A' && \
246 (s)->Buffer[0] <= 'Z' && (s)->Buffer[1] == ':')
247
248 /* Differs from MOUNTMGR_IS_DRIVE_LETTER(): no '\DosDevices\' accounted for */
249 #define IS_DRIVE_LETTER(s) \
250 (IS_DRIVE_LETTER_PFX(s) && (s)->Length == 2*sizeof(WCHAR))
251
252
253 /**
254 * @brief Tests the output of IOCTL_MOUNTMGR_QUERY_DOS_VOLUME_PATH.
255 **/
256 static VOID
Test_QueryDosVolumePath(_In_ PCWSTR NtVolumeName,_In_ PMOUNTMGR_VOLUME_PATHS VolumePath)257 Test_QueryDosVolumePath(
258 _In_ PCWSTR NtVolumeName,
259 _In_ PMOUNTMGR_VOLUME_PATHS VolumePath)
260 {
261 UNICODE_STRING DosPath;
262
263 UNREFERENCED_PARAMETER(NtVolumeName);
264
265 /* The VolumePath should contain one NUL-terminated string (always there?),
266 * plus one final NUL-terminator */
267 ok(VolumePath->MultiSzLength >= 2 * sizeof(UNICODE_NULL),
268 "DOS volume path string too short (length: %lu)\n",
269 VolumePath->MultiSzLength / sizeof(WCHAR));
270 ok(VolumePath->MultiSz[VolumePath->MultiSzLength / sizeof(WCHAR) - 2] == UNICODE_NULL,
271 "Missing NUL-terminator (2)\n");
272 ok(VolumePath->MultiSz[VolumePath->MultiSzLength / sizeof(WCHAR) - 1] == UNICODE_NULL,
273 "Missing NUL-terminator (1)\n");
274
275 /* Build the result string */
276 // RtlInitUnicodeString(&DosPath, VolumePath->MultiSz);
277 DosPath.Length = (USHORT)VolumePath->MultiSzLength - 2 * sizeof(UNICODE_NULL);
278 DosPath.MaximumLength = DosPath.Length + sizeof(UNICODE_NULL);
279 DosPath.Buffer = VolumePath->MultiSz;
280
281 /* The returned DOS path is either a drive letter (*WITHOUT* any
282 * '\DosDevices\' prefix present) or a Win32 file-system reparse point
283 * path, or a volume GUID name in Win32 format, i.e. prefixed by '\\?\' */
284 ok(IS_DRIVE_LETTER_PFX(&DosPath) || MOUNTMGR_IS_DOS_VOLUME_NAME(&DosPath),
285 "Invalid DOS volume path returned '%s'\n", wine_dbgstr_us(&DosPath));
286 }
287
288 /**
289 * @brief Tests the output of IOCTL_MOUNTMGR_QUERY_DOS_VOLUME_PATHS.
290 **/
291 static VOID
Test_QueryDosVolumePaths(_In_ PCWSTR NtVolumeName,_In_ PMOUNTMGR_VOLUME_PATHS VolumePaths,_In_opt_ PMOUNTMGR_VOLUME_PATHS VolumePath)292 Test_QueryDosVolumePaths(
293 _In_ PCWSTR NtVolumeName,
294 _In_ PMOUNTMGR_VOLUME_PATHS VolumePaths,
295 _In_opt_ PMOUNTMGR_VOLUME_PATHS VolumePath)
296 {
297 UNICODE_STRING DosPath;
298 PCWSTR pMountPoint;
299
300 /* The VolumePaths should contain zero or more NUL-terminated strings,
301 * plus one final NUL-terminator */
302
303 ok(VolumePaths->MultiSzLength >= sizeof(UNICODE_NULL),
304 "DOS volume path string too short (length: %lu)\n",
305 VolumePaths->MultiSzLength / sizeof(WCHAR));
306
307 /* Check for correct double-NUL-termination, if there is at least one string */
308 if (VolumePaths->MultiSzLength >= 2 * sizeof(UNICODE_NULL),
309 VolumePaths->MultiSz[0] != UNICODE_NULL)
310 {
311 ok(VolumePaths->MultiSz[VolumePaths->MultiSzLength / sizeof(WCHAR) - 2] == UNICODE_NULL,
312 "Missing NUL-terminator (2)\n");
313 }
314 /* Check for the final NUL-terminator */
315 ok(VolumePaths->MultiSz[VolumePaths->MultiSzLength / sizeof(WCHAR) - 1] == UNICODE_NULL,
316 "Missing NUL-terminator (1)\n");
317
318 if (winetest_debug > 1)
319 {
320 trace("\n%S =>\n", NtVolumeName);
321 for (pMountPoint = VolumePaths->MultiSz; *pMountPoint;
322 pMountPoint += wcslen(pMountPoint) + 1)
323 {
324 printf(" '%S'\n", pMountPoint);
325 }
326 printf("\n");
327 }
328
329 for (pMountPoint = VolumePaths->MultiSz; *pMountPoint;
330 pMountPoint += wcslen(pMountPoint) + 1)
331 {
332 /* The returned DOS path is either a drive letter (*WITHOUT* any
333 * '\DosDevices\' prefix present) or a Win32 file-system reparse point
334 * path, or a volume GUID name in Win32 format, i.e. prefixed by '\\?\' */
335 RtlInitUnicodeString(&DosPath, pMountPoint);
336 ok(IS_DRIVE_LETTER_PFX(&DosPath) || MOUNTMGR_IS_DOS_VOLUME_NAME(&DosPath),
337 "Invalid DOS volume path returned '%s'\n", wine_dbgstr_us(&DosPath));
338 }
339
340 /*
341 * If provided, verify that the single VolumePath is found at the
342 * first position in the volume paths list, *IF* this is a DOS path;
343 * otherwise if it's a Volume{GUID} path, this means there is no
344 * DOS path associated, and none is listed in the volume paths list.
345 */
346 if (VolumePath)
347 {
348 RtlInitUnicodeString(&DosPath, VolumePath->MultiSz);
349 if (IS_DRIVE_LETTER_PFX(&DosPath))
350 {
351 /*
352 * The single path is a DOS path (single drive letter or Win32
353 * file-system reparse point path). It has to be listed first
354 * in the volume paths list.
355 */
356 UNICODE_STRING FirstPath;
357 BOOLEAN AreEqual;
358
359 ok(VolumePaths->MultiSzLength >= 2 * sizeof(UNICODE_NULL),
360 "DOS VolumePaths list isn't long enough\n");
361 ok(*VolumePaths->MultiSz != UNICODE_NULL,
362 "Empty DOS VolumePaths list\n");
363
364 RtlInitUnicodeString(&FirstPath, VolumePaths->MultiSz);
365 AreEqual = RtlEqualUnicodeString(&DosPath, &FirstPath, FALSE);
366 ok(AreEqual, "DOS paths '%s' and '%s' are not the same!\n",
367 wine_dbgstr_us(&DosPath), wine_dbgstr_us(&FirstPath));
368 }
369 else if (MOUNTMGR_IS_DOS_VOLUME_NAME(&DosPath))
370 {
371 /*
372 * The single "DOS" path is actually a volume name. This means
373 * that it wasn't really mounted, and the volume paths list must
374 * be empty. It contains only the last NUL-terminator.
375 */
376 ok(VolumePaths->MultiSzLength == sizeof(UNICODE_NULL),
377 "DOS VolumePaths list isn't 1 WCHAR long\n");
378 ok(*VolumePaths->MultiSz == UNICODE_NULL,
379 "Non-empty DOS VolumePaths list\n");
380 }
381 else
382 {
383 /* The volume path is invalid (shouldn't happen) */
384 ok(FALSE, "Invalid DOS volume path returned '%s'\n", wine_dbgstr_us(&DosPath));
385 }
386 }
387 }
388
389 static BOOLEAN
doesPathExistInMountPoints(_In_ PMOUNTMGR_MOUNT_POINTS MountPoints,_In_ PUNICODE_STRING DosPath)390 doesPathExistInMountPoints(
391 _In_ PMOUNTMGR_MOUNT_POINTS MountPoints,
392 _In_ PUNICODE_STRING DosPath)
393 {
394 UNICODE_STRING DosDevicesPrefix = RTL_CONSTANT_STRING(L"\\DosDevices\\");
395 ULONG i;
396 BOOLEAN IsDosVolName;
397 BOOLEAN Found = FALSE;
398
399 IsDosVolName = MOUNTMGR_IS_DOS_VOLUME_NAME(DosPath);
400 /* Temporarily patch \\?\ to \??\ in DosPath for comparison */
401 if (IsDosVolName)
402 DosPath->Buffer[1] = L'?';
403
404 for (i = 0; i < MountPoints->NumberOfMountPoints; ++i)
405 {
406 UNICODE_STRING SymLink;
407
408 SymLink.Length = SymLink.MaximumLength = MountPoints->MountPoints[i].SymbolicLinkNameLength;
409 SymLink.Buffer = (PWCHAR)((ULONG_PTR)MountPoints + MountPoints->MountPoints[i].SymbolicLinkNameOffset);
410
411 if (IS_DRIVE_LETTER(DosPath))
412 {
413 if (RtlPrefixUnicodeString(&DosDevicesPrefix, &SymLink, FALSE))
414 {
415 /* Advance past the prefix */
416 SymLink.Length -= DosDevicesPrefix.Length;
417 SymLink.MaximumLength -= DosDevicesPrefix.Length;
418 SymLink.Buffer += DosDevicesPrefix.Length / sizeof(WCHAR);
419
420 Found = RtlEqualUnicodeString(DosPath, &SymLink, FALSE);
421 }
422 }
423 else if (/*MOUNTMGR_IS_DOS_VOLUME_NAME(DosPath) ||*/ // See above
424 MOUNTMGR_IS_NT_VOLUME_NAME(DosPath))
425 {
426 Found = RtlEqualUnicodeString(DosPath, &SymLink, FALSE);
427 }
428 else
429 {
430 /* Just test for simple string comparison, the path should not be found */
431 Found = RtlEqualUnicodeString(DosPath, &SymLink, FALSE);
432 }
433
434 /* Stop searching if we've found something */
435 if (Found)
436 break;
437 }
438
439 /* Revert \??\ back to \\?\ */
440 if (IsDosVolName)
441 DosPath->Buffer[1] = L'\\';
442
443 return Found;
444 }
445
446 /**
447 * @brief Tests the output of IOCTL_MOUNTMGR_QUERY_POINTS.
448 **/
449 static VOID
Test_QueryPoints(_In_ PCWSTR NtVolumeName,_In_ PMOUNTMGR_MOUNT_POINTS MountPoints,_In_opt_ PMOUNTMGR_VOLUME_PATHS VolumePath,_In_opt_ PMOUNTMGR_VOLUME_PATHS VolumePaths)450 Test_QueryPoints(
451 _In_ PCWSTR NtVolumeName,
452 _In_ PMOUNTMGR_MOUNT_POINTS MountPoints,
453 _In_opt_ PMOUNTMGR_VOLUME_PATHS VolumePath,
454 _In_opt_ PMOUNTMGR_VOLUME_PATHS VolumePaths)
455 {
456 UNICODE_STRING DosPath;
457 PCWSTR pMountPoint;
458 BOOLEAN ExpectedFound, Found;
459
460 if (winetest_debug > 1)
461 {
462 ULONG i;
463 trace("\n%S =>\n", NtVolumeName);
464 for (i = 0; i < MountPoints->NumberOfMountPoints; ++i)
465 {
466 UNICODE_STRING DevName, SymLink;
467
468 DevName.Length = DevName.MaximumLength = MountPoints->MountPoints[i].DeviceNameLength;
469 DevName.Buffer = (PWCHAR)((ULONG_PTR)MountPoints + MountPoints->MountPoints[i].DeviceNameOffset);
470
471 SymLink.Length = SymLink.MaximumLength = MountPoints->MountPoints[i].SymbolicLinkNameLength;
472 SymLink.Buffer = (PWCHAR)((ULONG_PTR)MountPoints + MountPoints->MountPoints[i].SymbolicLinkNameOffset);
473
474 printf(" '%s' -- '%s'\n", wine_dbgstr_us(&DevName), wine_dbgstr_us(&SymLink));
475 }
476 printf("\n");
477 }
478
479 /*
480 * The Win32 file-system reparse point paths are NOT listed amongst
481 * the mount points. Only the drive letter and the volume GUID name
482 * are, but in an NT format (using '\DosDevices\' or '\??\' prefixes).
483 */
484
485 if (VolumePath)
486 {
487 /* VolumePath can either be a drive letter (usual case), a Win32
488 * reparse point path (if the volume is mounted only with these),
489 * or a volume GUID name (if the volume is NOT mounted). */
490 RtlInitUnicodeString(&DosPath, VolumePath->MultiSz);
491 ExpectedFound = (IS_DRIVE_LETTER(&DosPath) || MOUNTMGR_IS_DOS_VOLUME_NAME(&DosPath));
492 Found = doesPathExistInMountPoints(MountPoints, &DosPath);
493 ok(Found == ExpectedFound,
494 "DOS path '%s' %sfound in the mount points list, expected %sto be found\n",
495 wine_dbgstr_us(&DosPath), Found ? "" : "NOT ", ExpectedFound ? "" : "NOT ");
496 }
497
498 if (VolumePaths)
499 {
500 /* VolumePaths only contains a drive letter (usual case) or a Win32
501 * reparse point path (if the volume is mounted only with these).
502 * If the volume is NOT mounted, VolumePaths does not list the
503 * volume GUID name, contrary to VolumePath. */
504 for (pMountPoint = VolumePaths->MultiSz; *pMountPoint;
505 pMountPoint += wcslen(pMountPoint) + 1)
506 {
507 /* Only the drive letter (but NOT the volume GUID name!) can be found in the list */
508 RtlInitUnicodeString(&DosPath, pMountPoint);
509 ExpectedFound = IS_DRIVE_LETTER(&DosPath);
510 Found = doesPathExistInMountPoints(MountPoints, &DosPath);
511 ok(Found == ExpectedFound,
512 "DOS path '%s' %sfound in the mount points list, expected %sto be found\n",
513 wine_dbgstr_us(&DosPath), Found ? "" : "NOT ", ExpectedFound ? "" : "NOT ");
514 }
515 }
516 }
517
518 /**
519 * @brief
520 * Tests the consistency of IOCTL_MOUNTMGR_QUERY_DOS_VOLUME_PATH,
521 * IOCTL_MOUNTMGR_QUERY_DOS_VOLUME_PATHS and IOCTL_MOUNTMGR_QUERY_POINTS.
522 **/
523 static VOID
Test_QueryDosVolumePathAndPaths(_In_ HANDLE MountMgrHandle,_In_ PCWSTR NtVolumeName)524 Test_QueryDosVolumePathAndPaths(
525 _In_ HANDLE MountMgrHandle,
526 _In_ PCWSTR NtVolumeName)
527 {
528 PMOUNTMGR_VOLUME_PATHS VolumePath = NULL;
529 PMOUNTMGR_VOLUME_PATHS VolumePaths = NULL;
530 PMOUNTMGR_MOUNT_POINTS MountPoints = NULL;
531
532 if (winetest_debug > 1)
533 trace("%S\n", NtVolumeName);
534
535 Call_QueryDosVolume_Path_Paths(MountMgrHandle,
536 NtVolumeName,
537 IOCTL_MOUNTMGR_QUERY_DOS_VOLUME_PATH,
538 &VolumePath);
539 if (VolumePath)
540 Test_QueryDosVolumePath(NtVolumeName, VolumePath);
541 else
542 skip("Device '%S': IOCTL_MOUNTMGR_QUERY_DOS_VOLUME_PATH failed\n", NtVolumeName);
543
544 Call_QueryDosVolume_Path_Paths(MountMgrHandle,
545 NtVolumeName,
546 IOCTL_MOUNTMGR_QUERY_DOS_VOLUME_PATHS,
547 &VolumePaths);
548 if (VolumePaths)
549 Test_QueryDosVolumePaths(NtVolumeName, VolumePaths, VolumePath);
550 else
551 skip("Device '%S': IOCTL_MOUNTMGR_QUERY_DOS_VOLUME_PATHS failed\n", NtVolumeName);
552
553 Call_QueryPoints(MountMgrHandle, NtVolumeName, &MountPoints);
554 if (MountPoints)
555 Test_QueryPoints(NtVolumeName, MountPoints, VolumePath, VolumePaths);
556 else
557 skip("Device '%S': IOCTL_MOUNTMGR_QUERY_POINTS failed\n", NtVolumeName);
558
559 if (MountPoints)
560 RtlFreeHeap(RtlGetProcessHeap(), 0, MountPoints);
561 if (VolumePaths)
562 RtlFreeHeap(RtlGetProcessHeap(), 0, VolumePaths);
563 if (VolumePath)
564 RtlFreeHeap(RtlGetProcessHeap(), 0, VolumePath);
565 }
566
567
START_TEST(QueryDosVolumePaths)568 START_TEST(QueryDosVolumePaths)
569 {
570 HANDLE MountMgrHandle;
571 HANDLE hFindVolume;
572 WCHAR szVolumeName[MAX_PATH];
573
574 MountMgrHandle = GetMountMgrHandle(FILE_READ_ATTRIBUTES);
575 if (!MountMgrHandle)
576 {
577 win_skip("MountMgr unavailable: %lu\n", GetLastError());
578 return;
579 }
580
581 hFindVolume = FindFirstVolumeW(szVolumeName, _countof(szVolumeName));
582 if (hFindVolume == INVALID_HANDLE_VALUE)
583 goto otherTests;
584 do
585 {
586 UNICODE_STRING VolumeName;
587 USHORT Length;
588
589 /*
590 * The Win32 FindFirst/NextVolumeW() functions convert the '\??\'
591 * prefix in '\??\Volume{...}' to '\\?\' and append a trailing
592 * backslash. Test this behaviour.
593 *
594 * NOTE: these functions actively filter out anything that is NOT
595 * '\??\Volume{...}' returned from IOCTL_MOUNTMGR_QUERY_POINTS.
596 * Thus, it also excludes mount-points specified as drive letters,
597 * like '\DosDevices\C:' .
598 */
599
600 RtlInitUnicodeString(&VolumeName, szVolumeName);
601 Length = VolumeName.Length / sizeof(WCHAR);
602 ok(Length >= 1 && VolumeName.Buffer[Length - 1] == L'\\',
603 "No trailing backslash found\n");
604
605 /* Remove the trailing backslash */
606 if (Length >= 1)
607 {
608 VolumeName.Length -= sizeof(WCHAR);
609 if (szVolumeName[Length - 1] == L'\\')
610 szVolumeName[Length - 1] = UNICODE_NULL;
611 }
612
613 ok(MOUNTMGR_IS_DOS_VOLUME_NAME(&VolumeName),
614 "Invalid DOS volume path returned '%s'\n", wine_dbgstr_us(&VolumeName));
615
616 /* Patch '\\?\' back to '\??\' to convert to an NT path */
617 if (szVolumeName[0] == L'\\' && szVolumeName[1] == L'\\' &&
618 szVolumeName[2] == L'?' && szVolumeName[3] == L'\\')
619 {
620 szVolumeName[1] = L'?';
621 }
622
623 Test_QueryDosVolumePathAndPaths(MountMgrHandle, szVolumeName);
624 } while (FindNextVolumeW(hFindVolume, szVolumeName, _countof(szVolumeName)));
625 FindVolumeClose(hFindVolume);
626
627 otherTests:
628 /* Test the drive containing SystemRoot */
629 wcscpy(szVolumeName, L"\\DosDevices\\?:");
630 szVolumeName[sizeof("\\DosDevices\\")-1] = SharedUserData->NtSystemRoot[0];
631 Test_QueryDosVolumePathAndPaths(MountMgrHandle, szVolumeName);
632
633 /* We are done */
634 CloseHandle(MountMgrHandle);
635 }
636