mirror of
https://github.com/reactos/reactos.git
synced 2025-04-05 21:21:33 +00:00
[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:
parent
c3e14b54ae
commit
e0e45ffa1a
5 changed files with 354 additions and 0 deletions
|
@ -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)
|
||||
|
|
17
modules/rostests/apitests/partmgr/CMakeLists.txt
Normal file
17
modules/rostests/apitests/partmgr/CMakeLists.txt
Normal 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)
|
306
modules/rostests/apitests/partmgr/StorDeviceNumber.c
Normal file
306
modules/rostests/apitests/partmgr/StorDeviceNumber.c
Normal 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");
|
||||
}
|
20
modules/rostests/apitests/partmgr/precomp.h
Normal file
20
modules/rostests/apitests/partmgr/precomp.h
Normal 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 */
|
10
modules/rostests/apitests/partmgr/testlist.c
Normal file
10
modules/rostests/apitests/partmgr/testlist.c
Normal 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 }
|
||||
};
|
Loading…
Reference in a new issue