[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
This commit is contained in:
Hermès Bélusca-Maïto 2025-01-06 21:54:20 +01:00
parent bb264f6828
commit cc1deb2902
No known key found for this signature in database
GPG key ID: 3B2539C65E7B93D0
5 changed files with 696 additions and 0 deletions

View file

@ -1,5 +1,6 @@
list(APPEND SOURCE
QueryDosVolumePaths.c
QueryPoints.c
utils.c)

View file

@ -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@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);
}

View file

@ -25,4 +25,9 @@ LPCSTR wine_dbgstr_us(const UNICODE_STRING *us);
HANDLE
GetMountMgrHandle(VOID);
VOID
DumpBuffer(
_In_ PVOID Buffer,
_In_ ULONG Length);
/* EOF */

View file

@ -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 }
};

View file

@ -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);
}
}