[PATCH v2 6/6] ntoskrnl/tests: Add a basic PnP test driver.

Zebediah Figura z.figura12 at gmail.com
Thu Apr 1 20:51:30 CDT 2021


Signed-off-by: Zebediah Figura <z.figura12 at gmail.com>
---
 dlls/ntoskrnl.exe/tests/Makefile.in     |   6 +-
 dlls/ntoskrnl.exe/tests/driver_pnp.c    | 143 ++++++++++++++++
 dlls/ntoskrnl.exe/tests/driver_pnp.spec |   1 +
 dlls/ntoskrnl.exe/tests/ntoskrnl.c      | 214 ++++++++++++++++++++++++
 4 files changed, 363 insertions(+), 1 deletion(-)
 create mode 100644 dlls/ntoskrnl.exe/tests/driver_pnp.c
 create mode 100644 dlls/ntoskrnl.exe/tests/driver_pnp.spec

diff --git a/dlls/ntoskrnl.exe/tests/Makefile.in b/dlls/ntoskrnl.exe/tests/Makefile.in
index 4b2052d43f4..c799dfcaf77 100644
--- a/dlls/ntoskrnl.exe/tests/Makefile.in
+++ b/dlls/ntoskrnl.exe/tests/Makefile.in
@@ -1,5 +1,5 @@
 TESTDLL   = ntoskrnl.exe
-IMPORTS   = advapi32 crypt32 wintrust ws2_32
+IMPORTS   = advapi32 crypt32 newdev setupapi wintrust ws2_32
 
 driver_IMPORTS = winecrt0 ntoskrnl
 driver_EXTRADLLFLAGS = -nodefaultlibs -nostartfiles -Wl,--subsystem,native
@@ -9,6 +9,8 @@ driver3_IMPORTS = winecrt0 ntoskrnl
 driver3_EXTRADLLFLAGS = -nodefaultlibs -nostartfiles -Wl,--subsystem,native
 driver4_IMPORTS = winecrt0 ntoskrnl netio
 driver4_EXTRADLLFLAGS = -nodefaultlibs -nostartfiles -Wl,--subsystem,native
+driver_pnp_IMPORTS = winecrt0 ntoskrnl
+driver_pnp_EXTRADLLFLAGS = -nodefaultlibs -nostartfiles -Wl,--subsystem,native
 
 SOURCES = \
 	driver.c \
@@ -19,4 +21,6 @@ SOURCES = \
 	driver3.spec \
 	driver4.c \
 	driver4.spec \
+	driver_pnp.c \
+	driver_pnp.spec \
 	ntoskrnl.c
diff --git a/dlls/ntoskrnl.exe/tests/driver_pnp.c b/dlls/ntoskrnl.exe/tests/driver_pnp.c
new file mode 100644
index 00000000000..53403d2fa7b
--- /dev/null
+++ b/dlls/ntoskrnl.exe/tests/driver_pnp.c
@@ -0,0 +1,143 @@
+/*
+ * ntoskrnl.exe testing framework
+ *
+ * Copyright 2020 Zebediah Figura
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#include <stdarg.h>
+
+#include "ntstatus.h"
+#define WIN32_NO_STATUS
+#include "windef.h"
+#include "winbase.h"
+#include "winternl.h"
+#include "winioctl.h"
+#include "ddk/wdm.h"
+
+#include "driver.h"
+
+static const GUID control_class = {0xdeadbeef, 0x29ef, 0x4538, {0xa5, 0xfd, 0xb6, 0x95, 0x73, 0xa3, 0x62, 0xc0}};
+static UNICODE_STRING control_symlink;
+
+static DEVICE_OBJECT *bus_fdo, *bus_pdo;
+
+static NTSTATUS fdo_pnp(IRP *irp)
+{
+    IO_STACK_LOCATION *stack = IoGetCurrentIrpStackLocation(irp);
+    NTSTATUS ret;
+
+    switch (stack->MinorFunction)
+    {
+        case IRP_MN_START_DEVICE:
+            irp->IoStatus.Status = IoSetDeviceInterfaceState(&control_symlink, TRUE);
+            break;
+
+        case IRP_MN_SURPRISE_REMOVAL:
+            irp->IoStatus.Status = STATUS_SUCCESS;
+            break;
+
+        case IRP_MN_REMOVE_DEVICE:
+            IoSetDeviceInterfaceState(&control_symlink, FALSE);
+            irp->IoStatus.Status = STATUS_SUCCESS;
+            IoSkipCurrentIrpStackLocation(irp);
+            ret = IoCallDriver(bus_pdo, irp);
+            IoDetachDevice(bus_pdo);
+            IoDeleteDevice(bus_fdo);
+            RtlFreeUnicodeString(&control_symlink);
+            return ret;
+    }
+
+    IoSkipCurrentIrpStackLocation(irp);
+    return IoCallDriver(bus_pdo, irp);
+}
+
+static NTSTATUS pdo_pnp(DEVICE_OBJECT *device_obj, IRP *irp)
+{
+    IO_STACK_LOCATION *stack = IoGetCurrentIrpStackLocation(irp);
+    NTSTATUS ret = irp->IoStatus.Status;
+
+    switch (stack->MinorFunction)
+    {
+        case IRP_MN_START_DEVICE:
+            ret = STATUS_SUCCESS;
+            break;
+
+        case IRP_MN_QUERY_CAPABILITIES:
+        case IRP_MN_SURPRISE_REMOVAL:
+            ret = STATUS_SUCCESS;
+            break;
+    }
+
+    irp->IoStatus.Status = ret;
+    IoCompleteRequest(irp, IO_NO_INCREMENT);
+    return ret;
+}
+
+static NTSTATUS WINAPI driver_pnp(DEVICE_OBJECT *device, IRP *irp)
+{
+    if (device == bus_fdo)
+        return fdo_pnp(irp);
+    return pdo_pnp(device, irp);
+}
+
+static NTSTATUS WINAPI driver_add_device(DRIVER_OBJECT *driver, DEVICE_OBJECT *pdo)
+{
+    NTSTATUS ret;
+
+    if ((ret = IoCreateDevice(driver, 0, NULL, FILE_DEVICE_BUS_EXTENDER, 0, FALSE, &bus_fdo)))
+        return ret;
+
+    if ((ret = IoRegisterDeviceInterface(pdo, &control_class, NULL, &control_symlink)))
+    {
+        IoDeleteDevice(bus_fdo);
+        return ret;
+    }
+
+    IoAttachDeviceToDeviceStack(bus_fdo, pdo);
+    bus_pdo = pdo;
+    bus_fdo->Flags &= ~DO_DEVICE_INITIALIZING;
+    return STATUS_SUCCESS;
+}
+
+static NTSTATUS WINAPI driver_create(DEVICE_OBJECT *device, IRP *irp)
+{
+    irp->IoStatus.Status = STATUS_SUCCESS;
+    IoCompleteRequest(irp, IO_NO_INCREMENT);
+    return STATUS_SUCCESS;
+}
+
+static NTSTATUS WINAPI driver_close(DEVICE_OBJECT *device, IRP *irp)
+{
+    irp->IoStatus.Status = STATUS_SUCCESS;
+    IoCompleteRequest(irp, IO_NO_INCREMENT);
+    return STATUS_SUCCESS;
+}
+
+static void WINAPI driver_unload(DRIVER_OBJECT *driver)
+{
+}
+
+NTSTATUS WINAPI DriverEntry(DRIVER_OBJECT *driver, UNICODE_STRING *registry)
+{
+    driver->DriverExtension->AddDevice = driver_add_device;
+    driver->DriverUnload = driver_unload;
+    driver->MajorFunction[IRP_MJ_PNP] = driver_pnp;
+    driver->MajorFunction[IRP_MJ_CREATE] = driver_create;
+    driver->MajorFunction[IRP_MJ_CLOSE] = driver_close;
+
+    return STATUS_SUCCESS;
+}
diff --git a/dlls/ntoskrnl.exe/tests/driver_pnp.spec b/dlls/ntoskrnl.exe/tests/driver_pnp.spec
new file mode 100644
index 00000000000..ad33444716a
--- /dev/null
+++ b/dlls/ntoskrnl.exe/tests/driver_pnp.spec
@@ -0,0 +1 @@
+# nothing here yet
diff --git a/dlls/ntoskrnl.exe/tests/ntoskrnl.c b/dlls/ntoskrnl.exe/tests/ntoskrnl.c
index c3ebe5b8ee1..fc0d8d7f1ed 100644
--- a/dlls/ntoskrnl.exe/tests/ntoskrnl.c
+++ b/dlls/ntoskrnl.exe/tests/ntoskrnl.c
@@ -32,12 +32,16 @@
 #include "ntsecapi.h"
 #include "mscat.h"
 #include "mssip.h"
+#include "setupapi.h"
+#include "newdev.h"
 #include "wine/test.h"
 #include "wine/heap.h"
 #include "wine/mssign.h"
 
 #include "driver.h"
 
+static const GUID GUID_NULL;
+
 static HANDLE device;
 
 static BOOL (WINAPI *pRtlDosPathNameToNtPathName_U)(const WCHAR *, UNICODE_STRING *, WCHAR **, CURDIR *);
@@ -934,6 +938,214 @@ static void test_driver4(struct testsign_context *ctx)
     ok(ret, "DeleteFile failed: %u\n", GetLastError());
 }
 
+static void add_file_to_catalog(HANDLE catalog, const WCHAR *file)
+{
+    SIP_SUBJECTINFO subject_info = {sizeof(SIP_SUBJECTINFO)};
+    SIP_INDIRECT_DATA *indirect_data;
+    const WCHAR *filepart = file;
+    CRYPTCATMEMBER *member;
+    WCHAR hash_buffer[100];
+    GUID subject_guid;
+    unsigned int i;
+    DWORD size;
+    BOOL ret;
+
+    ret = CryptSIPRetrieveSubjectGuidForCatalogFile(file, NULL, &subject_guid);
+    todo_wine ok(ret, "Failed to get subject guid, error %u\n", GetLastError());
+
+    size = 0;
+    subject_info.pgSubjectType = &subject_guid;
+    subject_info.pwsFileName = file;
+    subject_info.DigestAlgorithm.pszObjId = (char *)szOID_OIWSEC_sha1;
+    subject_info.dwFlags = SPC_INC_PE_RESOURCES_FLAG | SPC_INC_PE_IMPORT_ADDR_TABLE_FLAG | SPC_EXC_PE_PAGE_HASHES_FLAG | 0x10000;
+    ret = CryptSIPCreateIndirectData(&subject_info, &size, NULL);
+    todo_wine ok(ret, "Failed to get indirect data size, error %u\n", GetLastError());
+
+    indirect_data = malloc(size);
+    ret = CryptSIPCreateIndirectData(&subject_info, &size, indirect_data);
+    todo_wine ok(ret, "Failed to get indirect data, error %u\n", GetLastError());
+    if (ret)
+    {
+        memset(hash_buffer, 0, sizeof(hash_buffer));
+        for (i = 0; i < indirect_data->Digest.cbData; ++i)
+            swprintf(&hash_buffer[i * 2], 2, L"%02X", indirect_data->Digest.pbData[i]);
+
+        member = CryptCATPutMemberInfo(catalog, (WCHAR *)file,
+                hash_buffer, &subject_guid, 0, size, (BYTE *)indirect_data);
+        ok(!!member, "Failed to write member, error %u\n", GetLastError());
+
+        if (wcsrchr(file, '\\'))
+            filepart = wcsrchr(file, '\\') + 1;
+
+        ret = !!CryptCATPutAttrInfo(catalog, member, (WCHAR *)L"File",
+                CRYPTCAT_ATTR_NAMEASCII | CRYPTCAT_ATTR_DATAASCII | CRYPTCAT_ATTR_AUTHENTICATED,
+                (wcslen(filepart) + 1) * 2, (BYTE *)filepart);
+        ok(ret, "Failed to write attr, error %u\n", GetLastError());
+
+        ret = !!CryptCATPutAttrInfo(catalog, member, (WCHAR *)L"OSAttr",
+                CRYPTCAT_ATTR_NAMEASCII | CRYPTCAT_ATTR_DATAASCII | CRYPTCAT_ATTR_AUTHENTICATED,
+                sizeof(L"2:6.0"), (BYTE *)L"2:6.0");
+        ok(ret, "Failed to write attr, error %u\n", GetLastError());
+    }
+}
+
+static void test_pnp_driver(struct testsign_context *ctx)
+{
+    static const char hardware_id[] = "test_hardware_id\0";
+    char path[MAX_PATH], dest[MAX_PATH], *filepart;
+    SP_DEVINFO_DATA device = {sizeof(device)};
+    char cwd[MAX_PATH], tempdir[MAX_PATH];
+    WCHAR driver_filename[MAX_PATH];
+    SC_HANDLE manager, service;
+    BOOL ret, need_reboot;
+    HANDLE catalog, file;
+    HDEVINFO set;
+    FILE *f;
+
+    static const char inf_text[] =
+        "[Version]\n"
+        "Signature=$Chicago$\n"
+        "ClassGuid={4d36e97d-e325-11ce-bfc1-08002be10318}\n"
+        "CatalogFile=winetest.cat\n"
+        "DriverVer=09/21/2006,6.0.5736.1\n"
+
+        "[Manufacturer]\n"
+        "Wine=mfg_section,NTamd64\n"
+
+        "[mfg_section.NTamd64]\n"
+        "Wine test root driver=device_section,test_hardware_id\n"
+
+        "[device_section.NTamd64]\n"
+        "CopyFiles=file_section\n"
+
+        "[device_section.NTamd64.Services]\n"
+        "AddService=winetest,0x2,svc_section\n"
+
+        "[file_section]\n"
+        "winetest.sys\n"
+
+        "[SourceDisksFiles]\n"
+        "winetest.sys=1\n"
+
+        "[SourceDisksNames]\n"
+        "1=,winetest.sys\n"
+
+        "[DestinationDirs]\n"
+        "DefaultDestDir=12\n"
+
+        "[svc_section]\n"
+        "ServiceBinary=%12%\\winetest.sys\n"
+        "ServiceType=1\n"
+        "StartType=3\n"
+        "ErrorControl=1\n"
+        "LoadOrderGroup=Extended Base\n"
+        "DisplayName=\"winetest bus driver\"\n"
+        "; they don't sleep anymore, on the beach\n";
+
+    GetCurrentDirectoryA(ARRAY_SIZE(cwd), cwd);
+    GetTempPathA(ARRAY_SIZE(tempdir), tempdir);
+    SetCurrentDirectoryA(tempdir);
+
+    load_resource(L"driver_pnp.dll", driver_filename);
+    ret = MoveFileExW(driver_filename, L"winetest.sys", MOVEFILE_COPY_ALLOWED | MOVEFILE_REPLACE_EXISTING);
+    ok(ret, "failed to move file, error %u\n", GetLastError());
+
+    f = fopen("winetest.inf", "w");
+    ok(!!f, "failed to open winetest.inf: %s\n", strerror(errno));
+    fputs(inf_text, f);
+    fclose(f);
+
+    /* Create the catalog file. */
+
+    catalog = CryptCATOpen((WCHAR *)L"winetest.cat", CRYPTCAT_OPEN_CREATENEW, 0, CRYPTCAT_VERSION_1, 0);
+    ok(catalog != INVALID_HANDLE_VALUE, "Failed to create catalog, error %#x\n", GetLastError());
+
+    ret = !!CryptCATPutCatAttrInfo(catalog, (WCHAR *)L"HWID1",
+            CRYPTCAT_ATTR_NAMEASCII | CRYPTCAT_ATTR_DATAASCII | CRYPTCAT_ATTR_AUTHENTICATED,
+            sizeof(L"test_hardware_id"), (BYTE *)L"test_hardware_id");
+    todo_wine ok(ret, "failed to add attribute, error %#x\n", GetLastError());
+
+    ret = !!CryptCATPutCatAttrInfo(catalog, (WCHAR *)L"OS",
+            CRYPTCAT_ATTR_NAMEASCII | CRYPTCAT_ATTR_DATAASCII | CRYPTCAT_ATTR_AUTHENTICATED,
+            sizeof(L"VistaX64"), (BYTE *)L"VistaX64");
+    todo_wine ok(ret, "failed to add attribute, error %#x\n", GetLastError());
+
+    add_file_to_catalog(catalog, L"winetest.sys");
+    add_file_to_catalog(catalog, L"winetest.inf");
+
+    ret = CryptCATPersistStore(catalog);
+    todo_wine ok(ret, "Failed to write catalog, error %u\n", GetLastError());
+
+    ret = CryptCATClose(catalog);
+    ok(ret, "Failed to close catalog, error %u\n", GetLastError());
+
+    testsign_sign(ctx, L"winetest.cat");
+
+    /* Install the driver. */
+
+    set = SetupDiCreateDeviceInfoList(NULL, NULL);
+    ok(set != INVALID_HANDLE_VALUE, "failed to create device list, error %#x\n", GetLastError());
+
+    ret = SetupDiCreateDeviceInfoA(set, "root\\winetest\\0", &GUID_NULL, NULL, NULL, 0, &device);
+    ok(ret, "failed to create device, error %#x\n", GetLastError());
+
+    ret = SetupDiSetDeviceRegistryPropertyA( set, &device, SPDRP_HARDWAREID,
+            (const BYTE *)hardware_id, sizeof(hardware_id) );
+    ok(ret, "failed to create set hardware ID, error %#x\n", GetLastError());
+
+    ret = SetupDiCallClassInstaller(DIF_REGISTERDEVICE, set, &device);
+    ok(ret, "failed to register device, error %#x\n", GetLastError());
+
+    GetFullPathNameA("winetest.inf", sizeof(path), path, NULL);
+    ret = UpdateDriverForPlugAndPlayDevicesA(NULL, hardware_id, path, INSTALLFLAG_FORCE, &need_reboot);
+    ok(ret, "failed to install device, error %#x\n", GetLastError());
+    ok(!need_reboot, "expected no reboot necessary\n");
+
+    /* Tests. */
+
+    file = CreateFileA("\\\\?\\root#winetest#0#{deadbeef-29ef-4538-a5fd-b69573a362c0}", 0, 0, NULL, OPEN_EXISTING, 0, NULL);
+    ok(file != INVALID_HANDLE_VALUE, "got error %u\n", GetLastError());
+    CloseHandle(file);
+
+    /* Clean up. */
+
+    ret = SetupDiCallClassInstaller(DIF_REMOVE, set, &device);
+    ok(ret, "failed to remove device, error %#x\n", GetLastError());
+
+    file = CreateFileA("\\\\?\\root#winetest#0#{deadbeef-29ef-4538-a5fd-b69573a362c0}", 0, 0, NULL, OPEN_EXISTING, 0, NULL);
+    ok(file == INVALID_HANDLE_VALUE, "expected failure\n");
+    ok(GetLastError() == ERROR_FILE_NOT_FOUND, "got error %u\n", GetLastError());
+
+    ret = SetupDiDestroyDeviceInfoList(set);
+    ok(ret, "failed to destroy set, error %#x\n", GetLastError());
+
+    /* Windows stops the service but does not delete it. */
+    manager = OpenSCManagerA(NULL, NULL, SC_MANAGER_CONNECT);
+    ok(!!manager, "failed to open service manager, error %u\n", GetLastError());
+    service = OpenServiceA(manager, "winetest", SERVICE_STOP | DELETE);
+    ok(!!service, "failed to open service, error %u\n", GetLastError());
+    unload_driver(service);
+    CloseServiceHandle(manager);
+
+    GetFullPathNameA("winetest.inf", sizeof(path), path, NULL);
+    ret = SetupCopyOEMInfA(path, NULL, 0, 0, dest, sizeof(dest), NULL, &filepart);
+    ok(ret, "Failed to copy INF, error %#x\n", GetLastError());
+    ret = SetupUninstallOEMInfA(filepart, 0, NULL);
+    ok(ret, "Failed to uninstall INF, error %u\n", GetLastError());
+
+    ret = DeleteFileA("winetest.cat");
+    ok(ret, "Failed to delete file, error %u\n", GetLastError());
+    ret = DeleteFileA("winetest.inf");
+    ok(ret, "Failed to delete file, error %u\n", GetLastError());
+    ret = DeleteFileA("winetest.sys");
+    ok(ret, "Failed to delete file, error %u\n", GetLastError());
+    /* Windows 10 apparently deletes the image in SetupUninstallOEMInf(). */
+    ret = DeleteFileA("C:/windows/system32/drivers/winetest.sys");
+    ok(ret || GetLastError() == ERROR_FILE_NOT_FOUND, "Failed to delete file, error %u\n", GetLastError());
+
+    SetCurrentDirectoryA(cwd);
+}
+
 START_TEST(ntoskrnl)
 {
     WCHAR filename[MAX_PATH], filename2[MAX_PATH];
@@ -1000,5 +1212,7 @@ START_TEST(ntoskrnl)
     subtest("driver4");
     test_driver4(&ctx);
 
+    test_pnp_driver(&ctx);
+
     testsign_cleanup(&ctx);
 }
-- 
2.30.2




More information about the wine-devel mailing list