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