Signed-off-by: Zebediah Figura z.figura12@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 | 226 ++++++++++++++++++++++++ 4 files changed, 375 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 e65dac38cec..c9f680f01e8 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 *); @@ -935,6 +939,226 @@ 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; + +#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) { WCHAR filename[MAX_PATH], filename2[MAX_PATH]; @@ -1008,5 +1232,7 @@ START_TEST(ntoskrnl) subtest("driver4"); test_driver4(&ctx);
+ test_pnp_driver(&ctx); + testsign_cleanup(&ctx); }