Commit c4986ee2 authored by Zebediah Figura's avatar Zebediah Figura Committed by Alexandre Julliard

ntoskrnl/tests: Add a basic PnP test driver.

parent eb5ff502
TESTDLL = ntoskrnl.exe TESTDLL = ntoskrnl.exe
IMPORTS = advapi32 crypt32 wintrust ws2_32 IMPORTS = advapi32 crypt32 newdev setupapi wintrust ws2_32
driver_IMPORTS = winecrt0 ntoskrnl driver_IMPORTS = winecrt0 ntoskrnl
driver_EXTRADLLFLAGS = -nodefaultlibs -nostartfiles -Wl,--subsystem,native driver_EXTRADLLFLAGS = -nodefaultlibs -nostartfiles -Wl,--subsystem,native
...@@ -9,6 +9,8 @@ driver3_IMPORTS = winecrt0 ntoskrnl ...@@ -9,6 +9,8 @@ driver3_IMPORTS = winecrt0 ntoskrnl
driver3_EXTRADLLFLAGS = -nodefaultlibs -nostartfiles -Wl,--subsystem,native driver3_EXTRADLLFLAGS = -nodefaultlibs -nostartfiles -Wl,--subsystem,native
driver4_IMPORTS = winecrt0 ntoskrnl netio driver4_IMPORTS = winecrt0 ntoskrnl netio
driver4_EXTRADLLFLAGS = -nodefaultlibs -nostartfiles -Wl,--subsystem,native driver4_EXTRADLLFLAGS = -nodefaultlibs -nostartfiles -Wl,--subsystem,native
driver_pnp_IMPORTS = winecrt0 ntoskrnl
driver_pnp_EXTRADLLFLAGS = -nodefaultlibs -nostartfiles -Wl,--subsystem,native
SOURCES = \ SOURCES = \
driver.c \ driver.c \
...@@ -19,4 +21,6 @@ SOURCES = \ ...@@ -19,4 +21,6 @@ SOURCES = \
driver3.spec \ driver3.spec \
driver4.c \ driver4.c \
driver4.spec \ driver4.spec \
driver_pnp.c \
driver_pnp.spec \
ntoskrnl.c ntoskrnl.c
/*
* 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;
}
...@@ -32,12 +32,16 @@ ...@@ -32,12 +32,16 @@
#include "ntsecapi.h" #include "ntsecapi.h"
#include "mscat.h" #include "mscat.h"
#include "mssip.h" #include "mssip.h"
#include "setupapi.h"
#include "newdev.h"
#include "wine/test.h" #include "wine/test.h"
#include "wine/heap.h" #include "wine/heap.h"
#include "wine/mssign.h" #include "wine/mssign.h"
#include "driver.h" #include "driver.h"
static const GUID GUID_NULL;
static HANDLE device; static HANDLE device;
static BOOL (WINAPI *pRtlDosPathNameToNtPathName_U)(const WCHAR *, UNICODE_STRING *, WCHAR **, CURDIR *); static BOOL (WINAPI *pRtlDosPathNameToNtPathName_U)(const WCHAR *, UNICODE_STRING *, WCHAR **, CURDIR *);
...@@ -935,6 +939,226 @@ static void test_driver4(struct testsign_context *ctx) ...@@ -935,6 +939,226 @@ static void test_driver4(struct testsign_context *ctx)
ok(ret, "DeleteFile failed: %u\n", GetLastError()); 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;
#ifdef __i386__
#define EXT "x86"
#elif defined(__x86_64__)
#define EXT "amd64"
#elif defined(__arm__)
#define EXT "arm"
#elif defined(__aarch64__)
#define EXT "arm64"
#else
#define EXT
#endif
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,NT" EXT "\n"
"[mfg_section.NT" EXT "]\n"
"Wine test root driver=device_section,test_hardware_id\n"
"[device_section.NT" EXT "]\n"
"CopyFiles=file_section\n"
"[device_section.NT" EXT ".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) START_TEST(ntoskrnl)
{ {
WCHAR filename[MAX_PATH], filename2[MAX_PATH]; WCHAR filename[MAX_PATH], filename2[MAX_PATH];
...@@ -1008,5 +1232,7 @@ START_TEST(ntoskrnl) ...@@ -1008,5 +1232,7 @@ START_TEST(ntoskrnl)
subtest("driver4"); subtest("driver4");
test_driver4(&ctx); test_driver4(&ctx);
test_pnp_driver(&ctx);
testsign_cleanup(&ctx); testsign_cleanup(&ctx);
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment