[PARTMGR_APITEST] Add a test suite for the partition manager (#7591)

CORE-13525

Add a test for IOCTL_STORAGE_GET_DEVICE_NUMBER.
This commit is contained in:
Hermès Bélusca-Maïto 2024-12-27 12:05:43 +01:00
parent c3e14b54ae
commit e0e45ffa1a
No known key found for this signature in database
GPG key ID: 3B2539C65E7B93D0
5 changed files with 354 additions and 0 deletions

View file

@ -39,6 +39,7 @@ add_subdirectory(netshell)
add_subdirectory(ntdll)
add_subdirectory(ole32)
add_subdirectory(opengl32)
add_subdirectory(partmgr)
add_subdirectory(pefile)
add_subdirectory(powrprof)
add_subdirectory(rtl)

View file

@ -0,0 +1,17 @@
list(APPEND SOURCE
StorDeviceNumber.c)
list(APPEND PCH_SKIP_SOURCE
testlist.c)
add_executable(partmgr_apitest
${SOURCE}
${PCH_SKIP_SOURCE})
target_link_libraries(partmgr_apitest wine ${PSEH_LIB})
set_module_type(partmgr_apitest win32cui)
add_importlibs(partmgr_apitest msvcrt kernel32 ntdll)
# TODO: Enable this when we get more than one source file to justify its use
#add_pch(partmgr_apitest precomp.h "${PCH_SKIP_SOURCE}")
add_rostests_file(TARGET partmgr_apitest)

View file

@ -0,0 +1,306 @@
/*
* PROJECT: ReactOS API Tests
* LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
* PURPOSE: Test for IOCTL_STORAGE_GET_DEVICE_NUMBER
* COPYRIGHT: Copyright 2024 Hermès Bélusca-Maïto <hermes.belusca-maito@reactos.org>
*/
#include "precomp.h"
#include <ntddstor.h>
static LPCSTR wine_dbgstr_us(const UNICODE_STRING *us)
{
if (!us) return "(null)";
return wine_dbgstr_wn(us->Buffer, us->Length / sizeof(WCHAR));
}
/* Flags combination allowing all the read, write and delete share modes.
* Currently similar to FILE_SHARE_VALID_FLAGS. */
#define FILE_SHARE_ALL \
(FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE)
static BOOLEAN
Test_Device_StorDeviceNumber(
_In_ PCWSTR NtDeviceName)
{
BOOLEAN Success = FALSE; // Suppose failure.
NTSTATUS Status;
HANDLE DeviceHandle1 = NULL, DeviceHandle2 = NULL;
FILE_FS_DEVICE_INFORMATION DeviceInfo;
STORAGE_DEVICE_NUMBER DeviceNumber;
UNICODE_STRING DeviceName;
OBJECT_ATTRIBUTES ObjectAttributes;
IO_STATUS_BLOCK IoStatusBlock;
WCHAR NtLegacyDeviceName[MAX_PATH];
ULONG BufferSize;
struct { OBJECT_NAME_INFORMATION; WCHAR Buffer[MAX_PATH]; } DeviceName1Buffer;
PUNICODE_STRING DeviceName1 = &DeviceName1Buffer.Name;
struct { OBJECT_NAME_INFORMATION; WCHAR Buffer[MAX_PATH]; } DeviceName2Buffer;
PUNICODE_STRING DeviceName2 = &DeviceName2Buffer.Name;
/* Open a handle to the device */
RtlInitUnicodeString(&DeviceName, NtDeviceName);
InitializeObjectAttributes(&ObjectAttributes,
&DeviceName,
OBJ_CASE_INSENSITIVE,
NULL,
NULL);
Status = NtOpenFile(&DeviceHandle1,
FILE_READ_ATTRIBUTES | SYNCHRONIZE,
&ObjectAttributes,
&IoStatusBlock,
FILE_SHARE_ALL,
/* FILE_NON_DIRECTORY_FILE | */ FILE_SYNCHRONOUS_IO_NONALERT);
ok_ntstatus(Status, STATUS_SUCCESS);
if (!NT_SUCCESS(Status))
{
skip("Device '%s': Opening failed\n", wine_dbgstr_us(&DeviceName));
goto Quit;
}
/* Verify the device information before proceeding further */
Status = NtQueryVolumeInformationFile(DeviceHandle1,
&IoStatusBlock,
&DeviceInfo,
sizeof(DeviceInfo),
FileFsDeviceInformation);
ok_ntstatus(Status, STATUS_SUCCESS);
if (!NT_SUCCESS(Status))
{
skip("FileFsDeviceInformation('%s') failed, Status 0x%08lx\n",
wine_dbgstr_us(&DeviceName), Status);
goto Quit;
}
/* Ignore volumes that are NOT on usual disks */
switch (DeviceInfo.DeviceType)
{
/* Testable devices */
case FILE_DEVICE_CD_ROM:
// case FILE_DEVICE_CD_ROM_FILE_SYSTEM:
case FILE_DEVICE_DISK:
// case FILE_DEVICE_DISK_FILE_SYSTEM:
// case FILE_DEVICE_NETWORK:
// case FILE_DEVICE_NETWORK_FILE_SYSTEM:
case FILE_DEVICE_VIRTUAL_DISK:
break;
/* Untestable devices */
default:
skip("Device '%s': Cannot test, device type %lu\n",
wine_dbgstr_us(&DeviceName), DeviceInfo.DeviceType);
goto Quit;
}
#if 0
if (DeviceInfo.DeviceType != FILE_DEVICE_DISK &&
DeviceInfo.DeviceType != FILE_DEVICE_VIRTUAL_DISK &&
DeviceInfo.DeviceType != FILE_DEVICE_CD_ROM)
{
skip("Device '%s': Cannot test, device type %lu\n",
wine_dbgstr_us(&DeviceName), DeviceInfo.DeviceType);
goto Quit;
}
#endif
/*
* Retrieve the storage device number.
* Note that this call is unsupported if this is a dynamic volume.
* NOTE: Usually fails for floppy disks.
*/
Status = NtDeviceIoControlFile(DeviceHandle1,
NULL, NULL, NULL,
&IoStatusBlock,
IOCTL_STORAGE_GET_DEVICE_NUMBER,
NULL, 0,
&DeviceNumber, sizeof(DeviceNumber));
if (!NT_SUCCESS(Status))
{
skip("Device '%s': Couldn't retrieve disk number\n", wine_dbgstr_us(&DeviceName));
goto Quit;
}
ok(DeviceNumber.DeviceType == DeviceInfo.DeviceType,
"Device '%s': Device type mismatch\n", wine_dbgstr_us(&DeviceName));
/* NOTE: this value is set to 0xFFFFFFFF (-1) for the disks that
* represent the physical paths of a multipath I/O (MPIO) disk. */
ok(DeviceNumber.DeviceNumber != ULONG_MAX,
"Device '%s': Invalid disk number reported\n", wine_dbgstr_us(&DeviceName));
if (DeviceNumber.DeviceNumber == ULONG_MAX)
goto Quit;
switch (DeviceInfo.DeviceType)
{
/* Testable devices */
case FILE_DEVICE_CD_ROM:
// case FILE_DEVICE_CD_ROM_FILE_SYSTEM:
{
/* CD-ROMs don't have partitions, their partition number is -1 */
ok(DeviceNumber.PartitionNumber == ULONG_MAX,
"Device '%s': Invalid partition number (%lu) reported, expected ULONG_MAX (-1)\n",
wine_dbgstr_us(&DeviceName), DeviceNumber.PartitionNumber);
/* Map to an NT device name */
RtlStringCchPrintfW(NtLegacyDeviceName, _countof(NtLegacyDeviceName),
L"\\Device\\CdRom%lu",
DeviceNumber.DeviceNumber);
break;
}
case FILE_DEVICE_DISK:
// case FILE_DEVICE_DISK_FILE_SYSTEM:
case FILE_DEVICE_VIRTUAL_DISK:
{
/* Check whether this is a floppy or a partitionable device */
if (DeviceInfo.Characteristics & FILE_FLOPPY_DISKETTE)
{
/* Floppies don't have partitions, their partition number is -1 */
ok(DeviceNumber.PartitionNumber == ULONG_MAX,
"Device '%s': Invalid partition number (%lu) reported, expected ULONG_MAX (-1)\n",
wine_dbgstr_us(&DeviceName), DeviceNumber.PartitionNumber);
/* Map to an NT device name */
RtlStringCchPrintfW(NtLegacyDeviceName, _countof(NtLegacyDeviceName),
L"\\Device\\Floppy%lu",
DeviceNumber.DeviceNumber);
}
else
{
/* The device is partitionable, so it must have a valid partition number */
ok(DeviceNumber.PartitionNumber != ULONG_MAX,
"Device '%s': Invalid partition number (%lu) reported; unpartitionable device?\n",
wine_dbgstr_us(&DeviceName), DeviceNumber.PartitionNumber);
if (DeviceNumber.PartitionNumber == ULONG_MAX)
goto Quit;
/* Map to an NT device name */
RtlStringCchPrintfW(NtLegacyDeviceName, _countof(NtLegacyDeviceName),
L"\\Device\\Harddisk%lu\\Partition%lu",
DeviceNumber.DeviceNumber, DeviceNumber.PartitionNumber);
}
break;
}
/* Untestable devices */
default:
skip("Device '%s': Cannot test, device type %lu\n",
wine_dbgstr_us(&DeviceName), DeviceInfo.DeviceType);
goto Quit;
}
/* Open the device using the legacy path */
RtlInitUnicodeString(&DeviceName, NtLegacyDeviceName);
InitializeObjectAttributes(&ObjectAttributes,
&DeviceName,
OBJ_CASE_INSENSITIVE,
NULL,
NULL);
Status = NtOpenFile(&DeviceHandle2,
FILE_READ_ATTRIBUTES | SYNCHRONIZE,
&ObjectAttributes,
&IoStatusBlock,
FILE_SHARE_ALL,
/* FILE_NON_DIRECTORY_FILE | */ FILE_SYNCHRONOUS_IO_NONALERT);
ok_ntstatus(Status, STATUS_SUCCESS);
if (!NT_SUCCESS(Status))
{
skip("Device '%s': Opening failed\n", wine_dbgstr_us(&DeviceName));
goto Quit;
}
/*
* Verify whether both retrieved handles refer to the same device.
* Since we're not running on Windows 10, we cannot use kernel32!CompareObjectHandles()
* or ntdll!NtCompareObjects(), therefore we have to rely on comparing
* whether the devices referred by both handles have the same canonical name.
*/
Status = NtQueryObject(DeviceHandle1,
ObjectNameInformation,
&DeviceName1Buffer,
sizeof(DeviceName1Buffer),
&BufferSize);
ok_ntstatus(Status, STATUS_SUCCESS);
Status = NtQueryObject(DeviceHandle2,
ObjectNameInformation,
&DeviceName2Buffer,
sizeof(DeviceName2Buffer),
&BufferSize);
ok_ntstatus(Status, STATUS_SUCCESS);
Success = RtlEqualUnicodeString(DeviceName1, DeviceName2, FALSE);
ok(Success, "Devices '%s' and '%s' are not the same!\n",
wine_dbgstr_us(DeviceName1), wine_dbgstr_us(DeviceName2));
Quit:
/* Cleanup */
if (DeviceHandle2)
NtClose(DeviceHandle2);
if (DeviceHandle1)
NtClose(DeviceHandle1);
return Success;
}
static BOOLEAN
Test_Drive_StorDeviceNumber(
_In_ WCHAR Drive)
{
WCHAR NtDeviceName[] = L"\\DosDevices\\?:";
NtDeviceName[sizeof("\\DosDevices\\")-1] = Drive;
return Test_Device_StorDeviceNumber(NtDeviceName);
}
START_TEST(StorDeviceNumber)
{
DWORD Drives;
UCHAR i;
/* Enumerate existing testable drives */
Drives = GetLogicalDrives();
if (Drives == 0)
{
skip("Drives map unavailable, error 0x%lx\n", GetLastError());
goto otherTests;
}
for (i = 0; i <= 'Z'-'A'; ++i)
{
WCHAR DriveName[] = L"?:\\";
UINT DriveType;
/* Skip non-existing drives */
if (!(Drives & (1 << i)))
continue;
/* Retrieve the drive type and see whether we can test it */
DriveName[0] = L'A' + i;
DriveType = GetDriveTypeW(DriveName);
switch (DriveType)
{
case DRIVE_REMOVABLE:
case DRIVE_FIXED:
case DRIVE_CDROM:
case DRIVE_RAMDISK:
{
Test_Drive_StorDeviceNumber(L'A' + i);
break;
}
case DRIVE_UNKNOWN:
case DRIVE_NO_ROOT_DIR:
case DRIVE_REMOTE:
default:
/* Unhandled drive type, just skip it silently */
trace("Drive %c with unhandled type %u\n", 'A' + i, DriveType);
break;
}
}
otherTests:
/* Test the drive containing SystemRoot */
Test_Drive_StorDeviceNumber(SharedUserData->NtSystemRoot[0]);
/* Test \??\PhysicalDrive0, if it exists */
Test_Device_StorDeviceNumber(L"\\??\\PhysicalDrive0");
}

View file

@ -0,0 +1,20 @@
/*
* PROJECT: ReactOS API Tests
* LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
* PURPOSE: Precompiled header
* COPYRIGHT: Copyright 2024 Hermès Bélusca-Maïto <hermes.belusca-maito@reactos.org>
*/
#pragma once
#define WIN32_NO_STATUS
#include <apitest.h>
#define NTOS_MODE_USER
#include <ndk/iofuncs.h>
#include <ndk/obfuncs.h>
#include <ndk/rtlfuncs.h>
#include <ntstrsafe.h>
/* EOF */

View file

@ -0,0 +1,10 @@
#define STANDALONE
#include <apitest.h>
extern void func_StorDeviceNumber(void);
const struct test winetest_testlist[] =
{
{ "StorDeviceNumber", func_StorDeviceNumber },
{ 0, 0 }
};