https://git.reactos.org/?p=reactos.git;a=commitdiff;h=e0e45ffa1a59b6e228d2e3879ea986c30d9ead70

commit e0e45ffa1a59b6e228d2e3879ea986c30d9ead70
Author:     Hermès Bélusca-Maïto <hermes.belusca-ma...@reactos.org>
AuthorDate: Fri Dec 27 12:05:43 2024 +0100
Commit:     Hermès Bélusca-Maïto <hermes.belusca-ma...@reactos.org>
CommitDate: Mon Jan 6 21:26:12 2025 +0100

    [PARTMGR_APITEST] Add a test suite for the partition manager (#7591)
    
    CORE-13525
    
    Add a test for IOCTL_STORAGE_GET_DEVICE_NUMBER.
---
 modules/rostests/apitests/CMakeLists.txt           |   1 +
 modules/rostests/apitests/partmgr/CMakeLists.txt   |  17 ++
 .../rostests/apitests/partmgr/StorDeviceNumber.c   | 306 +++++++++++++++++++++
 modules/rostests/apitests/partmgr/precomp.h        |  20 ++
 modules/rostests/apitests/partmgr/testlist.c       |  10 +
 5 files changed, 354 insertions(+)

diff --git a/modules/rostests/apitests/CMakeLists.txt 
b/modules/rostests/apitests/CMakeLists.txt
index 17a36e94d23..92ea11342a9 100644
--- a/modules/rostests/apitests/CMakeLists.txt
+++ b/modules/rostests/apitests/CMakeLists.txt
@@ -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)
diff --git a/modules/rostests/apitests/partmgr/CMakeLists.txt 
b/modules/rostests/apitests/partmgr/CMakeLists.txt
new file mode 100644
index 00000000000..32684f3252e
--- /dev/null
+++ b/modules/rostests/apitests/partmgr/CMakeLists.txt
@@ -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)
diff --git a/modules/rostests/apitests/partmgr/StorDeviceNumber.c 
b/modules/rostests/apitests/partmgr/StorDeviceNumber.c
new file mode 100644
index 00000000000..8407b0475f2
--- /dev/null
+++ b/modules/rostests/apitests/partmgr/StorDeviceNumber.c
@@ -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-ma...@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");
+}
diff --git a/modules/rostests/apitests/partmgr/precomp.h 
b/modules/rostests/apitests/partmgr/precomp.h
new file mode 100644
index 00000000000..fc5eaa9b38b
--- /dev/null
+++ b/modules/rostests/apitests/partmgr/precomp.h
@@ -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-ma...@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 */
diff --git a/modules/rostests/apitests/partmgr/testlist.c 
b/modules/rostests/apitests/partmgr/testlist.c
new file mode 100644
index 00000000000..01ad0e3a04a
--- /dev/null
+++ b/modules/rostests/apitests/partmgr/testlist.c
@@ -0,0 +1,10 @@
+#define STANDALONE
+#include <apitest.h>
+
+extern void func_StorDeviceNumber(void);
+
+const struct test winetest_testlist[] =
+{
+    { "StorDeviceNumber", func_StorDeviceNumber },
+    { 0, 0 }
+};

Reply via email to