[PATCH 8/8] ntoskrnl/tests: Add a basic PnP test driver.
Zebediah Figura
z.figura12 at gmail.com
Wed Mar 31 11:16:38 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 | 209 ++++++++++++++++++++++++
4 files changed, 358 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..70398e90627 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,209 @@ 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;
+ HANDLE catalog, file;
+ HDEVINFO set;
+ BOOL ret;
+ 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 = MoveFileW(driver_filename, L"winetest.sys");
+ 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, NULL);
+ ok(ret, "failed to install device, error %#x\n", GetLastError());
+
+ /* 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());
+
+ 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 +1207,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