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