mirror of
https://github.com/reactos/reactos.git
synced 2025-02-28 19:32:59 +00:00
[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:
parent
bb264f6828
commit
cc1deb2902
5 changed files with 696 additions and 0 deletions
|
@ -1,5 +1,6 @@
|
|||
|
||||
list(APPEND SOURCE
|
||||
QueryDosVolumePaths.c
|
||||
QueryPoints.c
|
||||
utils.c)
|
||||
|
||||
|
|
635
modules/rostests/apitests/mountmgr/QueryDosVolumePaths.c
Normal file
635
modules/rostests/apitests/mountmgr/QueryDosVolumePaths.c
Normal 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);
|
||||
}
|
|
@ -25,4 +25,9 @@ LPCSTR wine_dbgstr_us(const UNICODE_STRING *us);
|
|||
HANDLE
|
||||
GetMountMgrHandle(VOID);
|
||||
|
||||
VOID
|
||||
DumpBuffer(
|
||||
_In_ PVOID Buffer,
|
||||
_In_ ULONG Length);
|
||||
|
||||
/* EOF */
|
||||
|
|
|
@ -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 }
|
||||
};
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue