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