https://git.reactos.org/?p=reactos.git;a=commitdiff;h=cc1deb29024ae58e52832…
commit cc1deb29024ae58e52832f8bb07ac23fdbafe281
Author: Hermès Bélusca-Maïto <hermes.belusca-maito(a)reactos.org>
AuthorDate: Mon Jan 6 21:54:20 2025 +0100
Commit: Hermès Bélusca-Maïto <hermes.belusca-maito(a)reactos.org>
CommitDate: Tue Jan 21 19:15:57 2025 +0100
[MOUNTMGR_APITEST] Add tests for mountmgr IOCTL_MOUNTMGR_QUERY_DOS_VOLUME_PATH(S)
(#6990)
These tests verify the output of IOCTL_MOUNTMGR_QUERY_DOS_VOLUME_PATH,
IOCTL_MOUNTMGR_QUERY_DOS_VOLUME_PATHS and IOCTL_MOUNTMGR_QUERY_POINTS,
as well as their consistency.
References:
https://www.osronline.com/article.cfm%5Eid=107.htm
https://community.osr.com/t/obtaining-volume-name-with-deviceobject/15121/3
---
modules/rostests/apitests/mountmgr/CMakeLists.txt | 1 +
.../apitests/mountmgr/QueryDosVolumePaths.c | 635 +++++++++++++++++++++
modules/rostests/apitests/mountmgr/precomp.h | 5 +
modules/rostests/apitests/mountmgr/testlist.c | 2 +
modules/rostests/apitests/mountmgr/utils.c | 53 ++
5 files changed, 696 insertions(+)
diff --git a/modules/rostests/apitests/mountmgr/CMakeLists.txt
b/modules/rostests/apitests/mountmgr/CMakeLists.txt
index dbba2775f56..c53e4d79aff 100644
--- a/modules/rostests/apitests/mountmgr/CMakeLists.txt
+++ b/modules/rostests/apitests/mountmgr/CMakeLists.txt
@@ -1,5 +1,6 @@
list(APPEND SOURCE
+ QueryDosVolumePaths.c
QueryPoints.c
utils.c)
diff --git a/modules/rostests/apitests/mountmgr/QueryDosVolumePaths.c
b/modules/rostests/apitests/mountmgr/QueryDosVolumePaths.c
new file mode 100644
index 00000000000..138e45bd56e
--- /dev/null
+++ b/modules/rostests/apitests/mountmgr/QueryDosVolumePaths.c
@@ -0,0 +1,635 @@
+/*
+ * PROJECT: ReactOS API Tests
+ * LICENSE: GPL-2.0-or-later (
https://spdx.org/licenses/GPL-2.0-or-later)
+ * PURPOSE: Test for IOCTL_MOUNTMGR_QUERY_DOS_VOLUME_PATH(S)
+ * COPYRIGHT: Copyright 2025 Hermès Bélusca-Maïto
<hermes.belusca-maito(a)reactos.org>
+ */
+
+#include "precomp.h"
+
+
+/**
+ * @brief
+ * Invokes either IOCTL_MOUNTMGR_QUERY_DOS_VOLUME_PATH or
+ * IOCTL_MOUNTMGR_QUERY_DOS_VOLUME_PATHS for testing, given
+ * the volume device name, and returns an allocated volume
+ * paths buffer. This buffer must be freed by the caller via
+ * RtlFreeHeap(RtlGetProcessHeap(), ...) .
+ *
+ * These IOCTLs return both the drive letter (if any) and the
+ * volume GUID symlink path, as well as any other file-system
+ * mount reparse points linking to the volume.
+ **/
+static VOID
+Call_QueryDosVolume_Path_Paths(
+ _In_ HANDLE MountMgrHandle,
+ _In_ PCWSTR NtVolumeName,
+ _In_ ULONG IoctlPathOrPaths,
+ _Out_ PMOUNTMGR_VOLUME_PATHS* pVolumePathPtr)
+{
+ NTSTATUS Status;
+ ULONG Length;
+ IO_STATUS_BLOCK IoStatusBlock;
+ UNICODE_STRING VolumeName;
+ MOUNTMGR_VOLUME_PATHS VolumePath;
+ PMOUNTMGR_VOLUME_PATHS VolumePathPtr;
+ ULONG DeviceNameLength;
+ /*
+ * This variable is used to query the device name.
+ * It's based on MOUNTMGR_TARGET_NAME (mountmgr.h).
+ * Doing it this way prevents memory allocation.
+ * The device name won't be longer.
+ */
+ struct
+ {
+ USHORT NameLength;
+ WCHAR DeviceName[256];
+ } DeviceName;
+
+
+ *pVolumePathPtr = NULL;
+
+ /* First, build the corresponding device name */
+ RtlInitUnicodeString(&VolumeName, NtVolumeName);
+ DeviceName.NameLength = VolumeName.Length;
+ RtlCopyMemory(&DeviceName.DeviceName, VolumeName.Buffer, VolumeName.Length);
+ DeviceNameLength = FIELD_OFFSET(MOUNTMGR_TARGET_NAME, DeviceName) +
DeviceName.NameLength;
+
+ /* Now, query the MountMgr for the DOS path(s) */
+ Status = NtDeviceIoControlFile(MountMgrHandle,
+ NULL, NULL, NULL,
+ &IoStatusBlock,
+ IoctlPathOrPaths,
+ &DeviceName, DeviceNameLength,
+ &VolumePath, sizeof(VolumePath));
+
+ /* Check for unsupported device */
+ if (Status == STATUS_NO_MEDIA_IN_DEVICE || Status == STATUS_INVALID_DEVICE_REQUEST)
+ {
+ skip("Device '%S': Doesn't support MountMgr queries, Status
0x%08lx\n",
+ NtVolumeName, Status);
+ return;
+ }
+
+ /* The only tolerated failure here is buffer too small, which is expected */
+ ok(NT_SUCCESS(Status) || (Status == STATUS_BUFFER_OVERFLOW),
+ "Device '%S': IOCTL 0x%lx failed unexpectedly, Status
0x%08lx\n",
+ NtVolumeName, IoctlPathOrPaths, Status);
+ if (!NT_SUCCESS(Status) && (Status != STATUS_BUFFER_OVERFLOW))
+ {
+ skip("Device '%S': Wrong Status\n", NtVolumeName);
+ return;
+ }
+
+ /* Compute the needed size to store the DOS path(s).
+ * Even if MOUNTMGR_VOLUME_PATHS allows bigger name lengths
+ * than MAXUSHORT, we can't use them, because we have to return
+ * this in an UNICODE_STRING that stores length in a USHORT. */
+ Length = sizeof(VolumePath) + VolumePath.MultiSzLength;
+ ok(Length <= MAXUSHORT,
+ "Device '%S': DOS volume path too large: %lu\n",
+ NtVolumeName, Length);
+ if (Length > MAXUSHORT)
+ {
+ skip("Device '%S': Wrong Length\n", NtVolumeName);
+ return;
+ }
+
+ /* Allocate the buffer and fill it with test pattern */
+ VolumePathPtr = RtlAllocateHeap(RtlGetProcessHeap(), 0, Length);
+ if (!VolumePathPtr)
+ {
+ skip("Device '%S': Failed to allocate buffer with size
%lu)\n",
+ NtVolumeName, Length);
+ return;
+ }
+ RtlFillMemory(VolumePathPtr, Length, 0xCC);
+
+ /* Re-query the DOS path(s) with the proper size */
+ Status = NtDeviceIoControlFile(MountMgrHandle,
+ NULL, NULL, NULL,
+ &IoStatusBlock,
+ IoctlPathOrPaths,
+ &DeviceName, DeviceNameLength,
+ VolumePathPtr, Length);
+ ok(NT_SUCCESS(Status),
+ "Device '%S': IOCTL 0x%lx failed unexpectedly, Status
0x%08lx\n",
+ NtVolumeName, IoctlPathOrPaths, Status);
+
+ if (winetest_debug > 1)
+ {
+ trace("Buffer:\n");
+ DumpBuffer(VolumePathPtr, Length);
+ printf("\n");
+ }
+
+ /* Return the buffer */
+ *pVolumePathPtr = VolumePathPtr;
+}
+
+/**
+ * @brief
+ * Invokes IOCTL_MOUNTMGR_QUERY_POINTS for testing, given
+ * the volume device name, and returns an allocated mount
+ * points buffer. This buffer must be freed by the caller
+ * via RtlFreeHeap(RtlGetProcessHeap(), ...) .
+ *
+ * This IOCTL only returns both the drive letter (if any)
+ * and the volume GUID symlink path, but does NOT return
+ * file-system mount reparse points.
+ **/
+static VOID
+Call_QueryPoints(
+ _In_ HANDLE MountMgrHandle,
+ _In_ PCWSTR NtVolumeName,
+ _Out_ PMOUNTMGR_MOUNT_POINTS* pMountPointsPtr)
+{
+ NTSTATUS Status;
+ ULONG Length;
+ IO_STATUS_BLOCK IoStatusBlock;
+ UNICODE_STRING VolumeName;
+ MOUNTMGR_MOUNT_POINTS MountPoints;
+ PMOUNTMGR_MOUNT_POINTS MountPointsPtr;
+ /*
+ * This variable is used to query the device name.
+ * It's based on MOUNTMGR_MOUNT_POINT (mountmgr.h).
+ * Doing it this way prevents memory allocation.
+ * The device name won't be longer.
+ */
+ struct
+ {
+ MOUNTMGR_MOUNT_POINT;
+ WCHAR DeviceName[256];
+ } DeviceName;
+
+
+ *pMountPointsPtr = NULL;
+
+ /* First, build the corresponding device name */
+ RtlInitUnicodeString(&VolumeName, NtVolumeName);
+ DeviceName.SymbolicLinkNameOffset = DeviceName.UniqueIdOffset = 0;
+ DeviceName.SymbolicLinkNameLength = DeviceName.UniqueIdLength = 0;
+ DeviceName.DeviceNameOffset = ((ULONG_PTR)&DeviceName.DeviceName -
(ULONG_PTR)&DeviceName);
+ DeviceName.DeviceNameLength = VolumeName.Length;
+ RtlCopyMemory(&DeviceName.DeviceName, VolumeName.Buffer, VolumeName.Length);
+
+ /* Now, query the MountMgr for the mount points */
+ Status = NtDeviceIoControlFile(MountMgrHandle,
+ NULL, NULL, NULL,
+ &IoStatusBlock,
+ IOCTL_MOUNTMGR_QUERY_POINTS,
+ &DeviceName, sizeof(DeviceName),
+ &MountPoints, sizeof(MountPoints));
+
+ /* Check for unsupported device */
+ if (Status == STATUS_NO_MEDIA_IN_DEVICE || Status == STATUS_INVALID_DEVICE_REQUEST)
+ {
+ skip("Device '%S': Doesn't support MountMgr queries, Status
0x%08lx\n",
+ NtVolumeName, Status);
+ return;
+ }
+
+ /* The only tolerated failure here is buffer too small, which is expected */
+ ok(NT_SUCCESS(Status) || (Status == STATUS_BUFFER_OVERFLOW),
+ "Device '%S': IOCTL 0x%lx failed unexpectedly, Status
0x%08lx\n",
+ NtVolumeName, IOCTL_MOUNTMGR_QUERY_POINTS, Status);
+ if (!NT_SUCCESS(Status) && (Status != STATUS_BUFFER_OVERFLOW))
+ {
+ skip("Device '%S': Wrong Status\n", NtVolumeName);
+ return;
+ }
+
+ /* Compute the needed size to retrieve the mount points */
+ Length = MountPoints.Size;
+
+ /* Allocate the buffer and fill it with test pattern */
+ MountPointsPtr = RtlAllocateHeap(RtlGetProcessHeap(), 0, Length);
+ if (!MountPointsPtr)
+ {
+ skip("Device '%S': Failed to allocate buffer with size
%lu)\n",
+ NtVolumeName, Length);
+ return;
+ }
+ RtlFillMemory(MountPointsPtr, Length, 0xCC);
+
+ /* Re-query the mount points with the proper size */
+ Status = NtDeviceIoControlFile(MountMgrHandle,
+ NULL, NULL, NULL,
+ &IoStatusBlock,
+ IOCTL_MOUNTMGR_QUERY_POINTS,
+ &DeviceName, sizeof(DeviceName),
+ MountPointsPtr, Length);
+ ok(NT_SUCCESS(Status),
+ "Device '%S': IOCTL 0x%lx failed unexpectedly, Status
0x%08lx\n",
+ NtVolumeName, IOCTL_MOUNTMGR_QUERY_POINTS, Status);
+
+ if (winetest_debug > 1)
+ {
+ trace("IOCTL_MOUNTMGR_QUERY_POINTS returned:\n"
+ " Size: %lu\n"
+ " NumberOfMountPoints: %lu\n",
+ MountPointsPtr->Size,
+ MountPointsPtr->NumberOfMountPoints);
+
+ trace("Buffer:\n");
+ DumpBuffer(MountPointsPtr, Length);
+ printf("\n");
+ }
+
+ /* Return the buffer */
+ *pMountPointsPtr = MountPointsPtr;
+}
+
+
+#define IS_DRIVE_LETTER_PFX(s) \
+ ((s)->Length >= 2*sizeof(WCHAR) && (s)->Buffer[0] >= 'A'
&& \
+ (s)->Buffer[0] <= 'Z' && (s)->Buffer[1] == ':')
+
+/* Differs from MOUNTMGR_IS_DRIVE_LETTER(): no '\DosDevices\' accounted for */
+#define IS_DRIVE_LETTER(s) \
+ (IS_DRIVE_LETTER_PFX(s) && (s)->Length == 2*sizeof(WCHAR))
+
+
+/**
+ * @brief Tests the output of IOCTL_MOUNTMGR_QUERY_DOS_VOLUME_PATH.
+ **/
+static VOID
+Test_QueryDosVolumePath(
+ _In_ PCWSTR NtVolumeName,
+ _In_ PMOUNTMGR_VOLUME_PATHS VolumePath)
+{
+ UNICODE_STRING DosPath;
+
+ UNREFERENCED_PARAMETER(NtVolumeName);
+
+ /* The VolumePath should contain one NUL-terminated string (always there?),
+ * plus one final NUL-terminator */
+ ok(VolumePath->MultiSzLength >= 2 * sizeof(UNICODE_NULL),
+ "DOS volume path string too short (length: %lu)\n",
+ VolumePath->MultiSzLength / sizeof(WCHAR));
+ ok(VolumePath->MultiSz[VolumePath->MultiSzLength / sizeof(WCHAR) - 2] ==
UNICODE_NULL,
+ "Missing NUL-terminator (2)\n");
+ ok(VolumePath->MultiSz[VolumePath->MultiSzLength / sizeof(WCHAR) - 1] ==
UNICODE_NULL,
+ "Missing NUL-terminator (1)\n");
+
+ /* Build the result string */
+ // RtlInitUnicodeString(&DosPath, VolumePath->MultiSz);
+ DosPath.Length = (USHORT)VolumePath->MultiSzLength - 2 * sizeof(UNICODE_NULL);
+ DosPath.MaximumLength = DosPath.Length + sizeof(UNICODE_NULL);
+ DosPath.Buffer = VolumePath->MultiSz;
+
+ /* The returned DOS path is either a drive letter (*WITHOUT* any
+ * '\DosDevices\' prefix present) or a Win32 file-system reparse point
+ * path, or a volume GUID name in Win32 format, i.e. prefixed by '\\?\' */
+ ok(IS_DRIVE_LETTER_PFX(&DosPath) || MOUNTMGR_IS_DOS_VOLUME_NAME(&DosPath),
+ "Invalid DOS volume path returned '%s'\n",
wine_dbgstr_us(&DosPath));
+}
+
+/**
+ * @brief Tests the output of IOCTL_MOUNTMGR_QUERY_DOS_VOLUME_PATHS.
+ **/
+static VOID
+Test_QueryDosVolumePaths(
+ _In_ PCWSTR NtVolumeName,
+ _In_ PMOUNTMGR_VOLUME_PATHS VolumePaths,
+ _In_opt_ PMOUNTMGR_VOLUME_PATHS VolumePath)
+{
+ UNICODE_STRING DosPath;
+ PCWSTR pMountPoint;
+
+ /* The VolumePaths should contain zero or more NUL-terminated strings,
+ * plus one final NUL-terminator */
+
+ ok(VolumePaths->MultiSzLength >= sizeof(UNICODE_NULL),
+ "DOS volume path string too short (length: %lu)\n",
+ VolumePaths->MultiSzLength / sizeof(WCHAR));
+
+ /* Check for correct double-NUL-termination, if there is at least one string */
+ if (VolumePaths->MultiSzLength >= 2 * sizeof(UNICODE_NULL),
+ VolumePaths->MultiSz[0] != UNICODE_NULL)
+ {
+ ok(VolumePaths->MultiSz[VolumePaths->MultiSzLength / sizeof(WCHAR) - 2] ==
UNICODE_NULL,
+ "Missing NUL-terminator (2)\n");
+ }
+ /* Check for the final NUL-terminator */
+ ok(VolumePaths->MultiSz[VolumePaths->MultiSzLength / sizeof(WCHAR) - 1] ==
UNICODE_NULL,
+ "Missing NUL-terminator (1)\n");
+
+ if (winetest_debug > 1)
+ {
+ trace("\n%S =>\n", NtVolumeName);
+ for (pMountPoint = VolumePaths->MultiSz; *pMountPoint;
+ pMountPoint += wcslen(pMountPoint) + 1)
+ {
+ printf(" '%S'\n", pMountPoint);
+ }
+ printf("\n");
+ }
+
+ for (pMountPoint = VolumePaths->MultiSz; *pMountPoint;
+ pMountPoint += wcslen(pMountPoint) + 1)
+ {
+ /* The returned DOS path is either a drive letter (*WITHOUT* any
+ * '\DosDevices\' prefix present) or a Win32 file-system reparse point
+ * path, or a volume GUID name in Win32 format, i.e. prefixed by '\\?\'
*/
+ RtlInitUnicodeString(&DosPath, pMountPoint);
+ ok(IS_DRIVE_LETTER_PFX(&DosPath) ||
MOUNTMGR_IS_DOS_VOLUME_NAME(&DosPath),
+ "Invalid DOS volume path returned '%s'\n",
wine_dbgstr_us(&DosPath));
+ }
+
+ /*
+ * If provided, verify that the single VolumePath is found at the
+ * first position in the volume paths list, *IF* this is a DOS path;
+ * otherwise if it's a Volume{GUID} path, this means there is no
+ * DOS path associated, and none is listed in the volume paths list.
+ */
+ if (VolumePath)
+ {
+ RtlInitUnicodeString(&DosPath, VolumePath->MultiSz);
+ if (IS_DRIVE_LETTER_PFX(&DosPath))
+ {
+ /*
+ * The single path is a DOS path (single drive letter or Win32
+ * file-system reparse point path). It has to be listed first
+ * in the volume paths list.
+ */
+ UNICODE_STRING FirstPath;
+ BOOLEAN AreEqual;
+
+ ok(VolumePaths->MultiSzLength >= 2 * sizeof(UNICODE_NULL),
+ "DOS VolumePaths list isn't long enough\n");
+ ok(*VolumePaths->MultiSz != UNICODE_NULL,
+ "Empty DOS VolumePaths list\n");
+
+ RtlInitUnicodeString(&FirstPath, VolumePaths->MultiSz);
+ AreEqual = RtlEqualUnicodeString(&DosPath, &FirstPath, FALSE);
+ ok(AreEqual, "DOS paths '%s' and '%s' are not the
same!\n",
+ wine_dbgstr_us(&DosPath), wine_dbgstr_us(&FirstPath));
+ }
+ else if (MOUNTMGR_IS_DOS_VOLUME_NAME(&DosPath))
+ {
+ /*
+ * The single "DOS" path is actually a volume name. This means
+ * that it wasn't really mounted, and the volume paths list must
+ * be empty. It contains only the last NUL-terminator.
+ */
+ ok(VolumePaths->MultiSzLength == sizeof(UNICODE_NULL),
+ "DOS VolumePaths list isn't 1 WCHAR long\n");
+ ok(*VolumePaths->MultiSz == UNICODE_NULL,
+ "Non-empty DOS VolumePaths list\n");
+ }
+ else
+ {
+ /* The volume path is invalid (shouldn't happen) */
+ ok(FALSE, "Invalid DOS volume path returned '%s'\n",
wine_dbgstr_us(&DosPath));
+ }
+ }
+}
+
+static BOOLEAN
+doesPathExistInMountPoints(
+ _In_ PMOUNTMGR_MOUNT_POINTS MountPoints,
+ _In_ PUNICODE_STRING DosPath)
+{
+ UNICODE_STRING DosDevicesPrefix = RTL_CONSTANT_STRING(L"\\DosDevices\\");
+ ULONG i;
+ BOOLEAN IsDosVolName;
+ BOOLEAN Found = FALSE;
+
+ IsDosVolName = MOUNTMGR_IS_DOS_VOLUME_NAME(DosPath);
+ /* Temporarily patch \\?\ to \??\ in DosPath for comparison */
+ if (IsDosVolName)
+ DosPath->Buffer[1] = L'?';
+
+ for (i = 0; i < MountPoints->NumberOfMountPoints; ++i)
+ {
+ UNICODE_STRING SymLink;
+
+ SymLink.Length = SymLink.MaximumLength =
MountPoints->MountPoints[i].SymbolicLinkNameLength;
+ SymLink.Buffer = (PWCHAR)((ULONG_PTR)MountPoints +
MountPoints->MountPoints[i].SymbolicLinkNameOffset);
+
+ if (IS_DRIVE_LETTER(DosPath))
+ {
+ if (RtlPrefixUnicodeString(&DosDevicesPrefix, &SymLink, FALSE))
+ {
+ /* Advance past the prefix */
+ SymLink.Length -= DosDevicesPrefix.Length;
+ SymLink.MaximumLength -= DosDevicesPrefix.Length;
+ SymLink.Buffer += DosDevicesPrefix.Length / sizeof(WCHAR);
+
+ Found = RtlEqualUnicodeString(DosPath, &SymLink, FALSE);
+ }
+ }
+ else if (/*MOUNTMGR_IS_DOS_VOLUME_NAME(DosPath) ||*/ // See above
+ MOUNTMGR_IS_NT_VOLUME_NAME(DosPath))
+ {
+ Found = RtlEqualUnicodeString(DosPath, &SymLink, FALSE);
+ }
+ else
+ {
+ /* Just test for simple string comparison, the path should not be found */
+ Found = RtlEqualUnicodeString(DosPath, &SymLink, FALSE);
+ }
+
+ /* Stop searching if we've found something */
+ if (Found)
+ break;
+ }
+
+ /* Revert \??\ back to \\?\ */
+ if (IsDosVolName)
+ DosPath->Buffer[1] = L'\\';
+
+ return Found;
+}
+
+/**
+ * @brief Tests the output of IOCTL_MOUNTMGR_QUERY_POINTS.
+ **/
+static VOID
+Test_QueryPoints(
+ _In_ PCWSTR NtVolumeName,
+ _In_ PMOUNTMGR_MOUNT_POINTS MountPoints,
+ _In_opt_ PMOUNTMGR_VOLUME_PATHS VolumePath,
+ _In_opt_ PMOUNTMGR_VOLUME_PATHS VolumePaths)
+{
+ UNICODE_STRING DosPath;
+ PCWSTR pMountPoint;
+ BOOLEAN ExpectedFound, Found;
+
+ if (winetest_debug > 1)
+ {
+ ULONG i;
+ trace("\n%S =>\n", NtVolumeName);
+ for (i = 0; i < MountPoints->NumberOfMountPoints; ++i)
+ {
+ UNICODE_STRING DevName, SymLink;
+
+ DevName.Length = DevName.MaximumLength =
MountPoints->MountPoints[i].DeviceNameLength;
+ DevName.Buffer = (PWCHAR)((ULONG_PTR)MountPoints +
MountPoints->MountPoints[i].DeviceNameOffset);
+
+ SymLink.Length = SymLink.MaximumLength =
MountPoints->MountPoints[i].SymbolicLinkNameLength;
+ SymLink.Buffer = (PWCHAR)((ULONG_PTR)MountPoints +
MountPoints->MountPoints[i].SymbolicLinkNameOffset);
+
+ printf(" '%s' -- '%s'\n",
wine_dbgstr_us(&DevName), wine_dbgstr_us(&SymLink));
+ }
+ printf("\n");
+ }
+
+ /*
+ * The Win32 file-system reparse point paths are NOT listed amongst
+ * the mount points. Only the drive letter and the volume GUID name
+ * are, but in an NT format (using '\DosDevices\' or '\??\'
prefixes).
+ */
+
+ if (VolumePath)
+ {
+ /* VolumePath can either be a drive letter (usual case), a Win32
+ * reparse point path (if the volume is mounted only with these),
+ * or a volume GUID name (if the volume is NOT mounted). */
+ RtlInitUnicodeString(&DosPath, VolumePath->MultiSz);
+ ExpectedFound = (IS_DRIVE_LETTER(&DosPath) ||
MOUNTMGR_IS_DOS_VOLUME_NAME(&DosPath));
+ Found = doesPathExistInMountPoints(MountPoints, &DosPath);
+ ok(Found == ExpectedFound,
+ "DOS path '%s' %sfound in the mount points list, expected %sto be
found\n",
+ wine_dbgstr_us(&DosPath), Found ? "" : "NOT ",
ExpectedFound ? "" : "NOT ");
+ }
+
+ if (VolumePaths)
+ {
+ /* VolumePaths only contains a drive letter (usual case) or a Win32
+ * reparse point path (if the volume is mounted only with these).
+ * If the volume is NOT mounted, VolumePaths does not list the
+ * volume GUID name, contrary to VolumePath. */
+ for (pMountPoint = VolumePaths->MultiSz; *pMountPoint;
+ pMountPoint += wcslen(pMountPoint) + 1)
+ {
+ /* Only the drive letter (but NOT the volume GUID name!) can be found in the
list */
+ RtlInitUnicodeString(&DosPath, pMountPoint);
+ ExpectedFound = IS_DRIVE_LETTER(&DosPath);
+ Found = doesPathExistInMountPoints(MountPoints, &DosPath);
+ ok(Found == ExpectedFound,
+ "DOS path '%s' %sfound in the mount points list, expected
%sto be found\n",
+ wine_dbgstr_us(&DosPath), Found ? "" : "NOT ",
ExpectedFound ? "" : "NOT ");
+ }
+ }
+}
+
+/**
+ * @brief
+ * Tests the consistency of IOCTL_MOUNTMGR_QUERY_DOS_VOLUME_PATH,
+ * IOCTL_MOUNTMGR_QUERY_DOS_VOLUME_PATHS and IOCTL_MOUNTMGR_QUERY_POINTS.
+ **/
+static VOID
+Test_QueryDosVolumePathAndPaths(
+ _In_ HANDLE MountMgrHandle,
+ _In_ PCWSTR NtVolumeName)
+{
+ PMOUNTMGR_VOLUME_PATHS VolumePath = NULL;
+ PMOUNTMGR_VOLUME_PATHS VolumePaths = NULL;
+ PMOUNTMGR_MOUNT_POINTS MountPoints = NULL;
+
+ if (winetest_debug > 1)
+ trace("%S\n", NtVolumeName);
+
+ Call_QueryDosVolume_Path_Paths(MountMgrHandle,
+ NtVolumeName,
+ IOCTL_MOUNTMGR_QUERY_DOS_VOLUME_PATH,
+ &VolumePath);
+ if (VolumePath)
+ Test_QueryDosVolumePath(NtVolumeName, VolumePath);
+ else
+ skip("Device '%S': IOCTL_MOUNTMGR_QUERY_DOS_VOLUME_PATH
failed\n", NtVolumeName);
+
+ Call_QueryDosVolume_Path_Paths(MountMgrHandle,
+ NtVolumeName,
+ IOCTL_MOUNTMGR_QUERY_DOS_VOLUME_PATHS,
+ &VolumePaths);
+ if (VolumePaths)
+ Test_QueryDosVolumePaths(NtVolumeName, VolumePaths, VolumePath);
+ else
+ skip("Device '%S': IOCTL_MOUNTMGR_QUERY_DOS_VOLUME_PATHS
failed\n", NtVolumeName);
+
+ Call_QueryPoints(MountMgrHandle, NtVolumeName, &MountPoints);
+ if (MountPoints)
+ Test_QueryPoints(NtVolumeName, MountPoints, VolumePath, VolumePaths);
+ else
+ skip("Device '%S': IOCTL_MOUNTMGR_QUERY_POINTS failed\n",
NtVolumeName);
+
+ if (MountPoints)
+ RtlFreeHeap(RtlGetProcessHeap(), 0, MountPoints);
+ if (VolumePaths)
+ RtlFreeHeap(RtlGetProcessHeap(), 0, VolumePaths);
+ if (VolumePath)
+ RtlFreeHeap(RtlGetProcessHeap(), 0, VolumePath);
+}
+
+
+START_TEST(QueryDosVolumePaths)
+{
+ HANDLE MountMgrHandle;
+ HANDLE hFindVolume;
+ WCHAR szVolumeName[MAX_PATH];
+
+ MountMgrHandle = GetMountMgrHandle();
+ if (!MountMgrHandle)
+ {
+ win_skip("MountMgr unavailable: %lu\n", GetLastError());
+ return;
+ }
+
+ hFindVolume = FindFirstVolumeW(szVolumeName, _countof(szVolumeName));
+ if (hFindVolume == INVALID_HANDLE_VALUE)
+ goto otherTests;
+ do
+ {
+ UNICODE_STRING VolumeName;
+ USHORT Length;
+
+ /*
+ * The Win32 FindFirst/NextVolumeW() functions convert the '\??\'
+ * prefix in '\??\Volume{...}' to '\\?\' and append a trailing
+ * backslash. Test this behaviour.
+ *
+ * NOTE: these functions actively filter out anything that is NOT
+ * '\??\Volume{...}' returned from IOCTL_MOUNTMGR_QUERY_POINTS.
+ * Thus, it also excludes mount-points specified as drive letters,
+ * like '\DosDevices\C:' .
+ */
+
+ RtlInitUnicodeString(&VolumeName, szVolumeName);
+ Length = VolumeName.Length / sizeof(WCHAR);
+ ok(Length >= 1 && VolumeName.Buffer[Length - 1] == L'\\',
+ "No trailing backslash found\n");
+
+ /* Remove the trailing backslash */
+ if (Length >= 1)
+ {
+ VolumeName.Length -= sizeof(WCHAR);
+ if (szVolumeName[Length - 1] == L'\\')
+ szVolumeName[Length - 1] = UNICODE_NULL;
+ }
+
+ ok(MOUNTMGR_IS_DOS_VOLUME_NAME(&VolumeName),
+ "Invalid DOS volume path returned '%s'\n",
wine_dbgstr_us(&VolumeName));
+
+ /* Patch '\\?\' back to '\??\' to convert to an NT path */
+ if (szVolumeName[0] == L'\\' && szVolumeName[1] == L'\\'
&&
+ szVolumeName[2] == L'?' && szVolumeName[3] ==
L'\\')
+ {
+ szVolumeName[1] = L'?';
+ }
+
+ Test_QueryDosVolumePathAndPaths(MountMgrHandle, szVolumeName);
+ } while (FindNextVolumeW(hFindVolume, szVolumeName, _countof(szVolumeName)));
+ FindVolumeClose(hFindVolume);
+
+otherTests:
+ /* Test the drive containing SystemRoot */
+ wcscpy(szVolumeName, L"\\DosDevices\\?:");
+ szVolumeName[sizeof("\\DosDevices\\")-1] =
SharedUserData->NtSystemRoot[0];
+ Test_QueryDosVolumePathAndPaths(MountMgrHandle, szVolumeName);
+
+ /* We are done */
+ CloseHandle(MountMgrHandle);
+}
diff --git a/modules/rostests/apitests/mountmgr/precomp.h
b/modules/rostests/apitests/mountmgr/precomp.h
index 462f62e1a38..8031f379b24 100644
--- a/modules/rostests/apitests/mountmgr/precomp.h
+++ b/modules/rostests/apitests/mountmgr/precomp.h
@@ -25,4 +25,9 @@ LPCSTR wine_dbgstr_us(const UNICODE_STRING *us);
HANDLE
GetMountMgrHandle(VOID);
+VOID
+DumpBuffer(
+ _In_ PVOID Buffer,
+ _In_ ULONG Length);
+
/* EOF */
diff --git a/modules/rostests/apitests/mountmgr/testlist.c
b/modules/rostests/apitests/mountmgr/testlist.c
index ab7c0921a26..9c96445bbcd 100644
--- a/modules/rostests/apitests/mountmgr/testlist.c
+++ b/modules/rostests/apitests/mountmgr/testlist.c
@@ -1,10 +1,12 @@
#define STANDALONE
#include <apitest.h>
+extern void func_QueryDosVolumePaths(void);
extern void func_QueryPoints(void);
const struct test winetest_testlist[] =
{
+ { "QueryDosVolumePaths", func_QueryDosVolumePaths },
{ "QueryPoints", func_QueryPoints },
{ 0, 0 }
};
diff --git a/modules/rostests/apitests/mountmgr/utils.c
b/modules/rostests/apitests/mountmgr/utils.c
index 50755581f04..7fa0794d2aa 100644
--- a/modules/rostests/apitests/mountmgr/utils.c
+++ b/modules/rostests/apitests/mountmgr/utils.c
@@ -47,3 +47,56 @@ GetMountMgrHandle(VOID)
return MountMgrHandle;
}
+
+VOID
+DumpBuffer(
+ _In_ PVOID Buffer,
+ _In_ ULONG Length)
+{
+#define LINE_SIZE (75 + 2)
+ ULONG i;
+ PBYTE Ptr1, Ptr2;
+ CHAR LineBuffer[LINE_SIZE];
+ PCHAR Line;
+ ULONG LineSize;
+
+ Ptr1 = Ptr2 = Buffer;
+ while ((ULONG_PTR)Buffer + Length - (ULONG_PTR)Ptr1 > 0)
+ {
+ Ptr1 = Ptr2;
+ Line = LineBuffer;
+
+ /* Print the address */
+ Line += _snprintf(Line, LINE_SIZE + LineBuffer - Line, "%08Ix ",
(ULONG_PTR)Ptr1);
+
+ /* Print up to 16 bytes... */
+
+ /* ... in hexadecimal form first... */
+ i = 0;
+ while (i++ <= 0x0F && ((ULONG_PTR)Buffer + Length - (ULONG_PTR)Ptr1
> 0))
+ {
+ Line += _snprintf(Line, LINE_SIZE + LineBuffer - Line, " %02x",
*Ptr1);
+ ++Ptr1;
+ }
+
+ /* ... align with spaces if needed... */
+ RtlFillMemory(Line, (0x0F + 2 - i) * 3 + 2, ' ');
+ Line += (0x0F + 2 - i) * 3 + 2;
+
+ /* ... then in character form. */
+ i = 0;
+ while (i++ <= 0x0F && ((ULONG_PTR)Buffer + Length - (ULONG_PTR)Ptr2
> 0))
+ {
+ *Line++ = ((*Ptr2 >= 0x20 && *Ptr2 <= 0x7E) || (*Ptr2 >=
0x80 && *Ptr2 < 0xFF) ? *Ptr2 : '.');
+ ++Ptr2;
+ }
+
+ /* Newline */
+ *Line++ = '\r';
+ *Line++ = '\n';
+
+ /* Finally display the line */
+ LineSize = Line - LineBuffer;
+ printf("%.*s", (int)LineSize, LineBuffer);
+ }
+}