Signed-off-by: Zebediah Figura z.figura12@gmail.com --- I know of no application yet that requires this behaviour, or in fact even tries to query file/object information from a device file. These tests are rather meant to illustrate how ObjectNameInformation is implemented for files, which in turn may help to inform our implementation for server-based objects such as regular files and named pipes.
dlls/ntoskrnl.exe/tests/driver.c | 120 +++++++++++++++++++++++++++-- dlls/ntoskrnl.exe/tests/ntoskrnl.c | 114 +++++++++++++++++++++++++++ 2 files changed, 226 insertions(+), 8 deletions(-)
diff --git a/dlls/ntoskrnl.exe/tests/driver.c b/dlls/ntoskrnl.exe/tests/driver.c index 0671a56efc7..d3f62879a40 100644 --- a/dlls/ntoskrnl.exe/tests/driver.c +++ b/dlls/ntoskrnl.exe/tests/driver.c @@ -37,6 +37,19 @@
#include "utils.h"
+/* memcmp() isn't exported from ntoskrnl on i386 */ +static int kmemcmp( const void *ptr1, const void *ptr2, size_t n ) +{ + const unsigned char *p1, *p2; + + for (p1 = ptr1, p2 = ptr2; n; n--, p1++, p2++) + { + if (*p1 < *p2) return -1; + if (*p1 > *p2) return 1; + } + return 0; +} + static const WCHAR device_name[] = {'\','D','e','v','i','c','e', '\','W','i','n','e','T','e','s','t','D','r','i','v','e','r',0}; static const WCHAR upper_name[] = {'\','D','e','v','i','c','e', @@ -55,6 +68,13 @@ static PETHREAD create_irp_thread;
NTSTATUS WINAPI ZwQueryInformationProcess(HANDLE,PROCESSINFOCLASS,void*,ULONG,ULONG*);
+struct file_context +{ + DWORD id; + ULONG namelen; + WCHAR name[10]; +}; + static void *get_proc_address(const char *name) { UNICODE_STRING name_u; @@ -2175,15 +2195,15 @@ static NTSTATUS get_fscontext(IRP *irp, IO_STACK_LOCATION *stack, ULONG_PTR *inf { ULONG length = stack->Parameters.DeviceIoControl.OutputBufferLength; char *buffer = irp->AssociatedIrp.SystemBuffer; - DWORD *context = stack->FileObject->FsContext; + struct file_context *context = stack->FileObject->FsContext;
- if (!buffer || !context) + if (!buffer) return STATUS_ACCESS_VIOLATION;
if (length < sizeof(DWORD)) return STATUS_BUFFER_TOO_SMALL;
- *(DWORD*)buffer = *context; + *(DWORD*)buffer = context->id; *info = sizeof(DWORD); return STATUS_SUCCESS; } @@ -2271,13 +2291,21 @@ static NTSTATUS test_completion_ioctl(DEVICE_OBJECT *device, IRP *irp) static NTSTATUS WINAPI driver_Create(DEVICE_OBJECT *device, IRP *irp) { IO_STACK_LOCATION *irpsp = IoGetCurrentIrpStackLocation( irp ); - DWORD *context = ExAllocatePool(PagedPool, sizeof(*context)); + struct file_context *context = ExAllocatePool(PagedPool, sizeof(*context));
- last_created_file = irpsp->FileObject; - ++create_count; - if (context) - *context = create_count; + if (!context) + { + irp->IoStatus.Status = STATUS_NO_MEMORY; + IoCompleteRequest(irp, IO_NO_INCREMENT); + return STATUS_NO_MEMORY; + } + + context->id = ++create_count; + context->namelen = min(irpsp->FileObject->FileName.Length, sizeof(context->name)); + memcpy(context->name, irpsp->FileObject->FileName.Buffer, context->namelen); irpsp->FileObject->FsContext = context; + + last_created_file = irpsp->FileObject; create_caller_thread = KeGetCurrentThread(); create_irp_thread = irp->Tail.Overlay.Thread;
@@ -2356,6 +2384,81 @@ static NTSTATUS WINAPI driver_FlushBuffers(DEVICE_OBJECT *device, IRP *irp) return STATUS_PENDING; }
+static BOOL compare_file_name(const struct file_context *context, const WCHAR *expect) +{ + return context->namelen == wcslen(expect) * sizeof(WCHAR) + && !kmemcmp(context->name, expect, context->namelen); +} + +static NTSTATUS WINAPI driver_QueryInformation(DEVICE_OBJECT *device, IRP *irp) +{ + IO_STACK_LOCATION *stack = IoGetCurrentIrpStackLocation(irp); + NTSTATUS ret; + + switch (stack->Parameters.QueryFile.FileInformationClass) + { + case FileNameInformation: + { + const struct file_context *context = stack->FileObject->FsContext; + FILE_NAME_INFORMATION *info = irp->AssociatedIrp.SystemBuffer; + ULONG len; + + if (stack->Parameters.QueryFile.Length < sizeof(*info)) + { + ret = STATUS_INFO_LENGTH_MISMATCH; + break; + } + + if (compare_file_name(context, L"\notimpl")) + { + ret = STATUS_NOT_IMPLEMENTED; + break; + } + else if (compare_file_name(context, L"")) + { + ret = STATUS_INVALID_DEVICE_REQUEST; + break; + } + else if (compare_file_name(context, L"\badparam")) + { + ret = STATUS_INVALID_PARAMETER; + break; + } + else if (compare_file_name(context, L"\genfail")) + { + ret = STATUS_UNSUCCESSFUL; + break; + } + else if (compare_file_name(context, L"\badtype")) + { + ret = STATUS_OBJECT_TYPE_MISMATCH; + break; + } + + len = stack->Parameters.QueryFile.Length - FIELD_OFFSET(FILE_NAME_INFORMATION, FileName); + if (len < context->namelen) + ret = STATUS_BUFFER_OVERFLOW; + else + { + len = context->namelen; + ret = STATUS_SUCCESS; + } + irp->IoStatus.Information = FIELD_OFFSET(FILE_NAME_INFORMATION, FileName) + len; + info->FileNameLength = context->namelen; + memcpy(info->FileName, context->name, len); + break; + } + + default: + ret = STATUS_NOT_IMPLEMENTED; + break; + } + + irp->IoStatus.Status = ret; + IoCompleteRequest(irp, IO_NO_INCREMENT); + return ret; +} + static NTSTATUS WINAPI driver_Close(DEVICE_OBJECT *device, IRP *irp) { IO_STACK_LOCATION *stack = IoGetCurrentIrpStackLocation(irp); @@ -2400,6 +2503,7 @@ NTSTATUS WINAPI DriverEntry(DRIVER_OBJECT *driver, PUNICODE_STRING registry) driver->MajorFunction[IRP_MJ_CREATE] = driver_Create; driver->MajorFunction[IRP_MJ_DEVICE_CONTROL] = driver_IoControl; driver->MajorFunction[IRP_MJ_FLUSH_BUFFERS] = driver_FlushBuffers; + driver->MajorFunction[IRP_MJ_QUERY_INFORMATION] = driver_QueryInformation; driver->MajorFunction[IRP_MJ_CLOSE] = driver_Close;
RtlInitUnicodeString(&nameW, IoDriverObjectTypeW); diff --git a/dlls/ntoskrnl.exe/tests/ntoskrnl.c b/dlls/ntoskrnl.exe/tests/ntoskrnl.c index b4ef9d0dcb7..61b1e401c56 100644 --- a/dlls/ntoskrnl.exe/tests/ntoskrnl.c +++ b/dlls/ntoskrnl.exe/tests/ntoskrnl.c @@ -493,6 +493,119 @@ static void test_return_status(void) ok(ret_size == 3, "got size %u\n", ret_size); }
+static BOOL compare_unicode_string(const WCHAR *buffer, ULONG len, const WCHAR *expect) +{ + return len == wcslen(expect) * sizeof(WCHAR) && !memcmp(buffer, expect, len); +} + +static void test_object_info(void) +{ + char buffer[200]; + OBJECT_NAME_INFORMATION *name_info = (OBJECT_NAME_INFORMATION *)buffer; + OBJECT_TYPE_INFORMATION *type_info = (OBJECT_TYPE_INFORMATION *)buffer; + FILE_NAME_INFORMATION *file_info = (FILE_NAME_INFORMATION *)buffer; + HANDLE file; + NTSTATUS status; + IO_STATUS_BLOCK io; + ULONG size; + + status = NtQueryObject(device, ObjectNameInformation, buffer, sizeof(buffer), NULL); + ok(!status, "got %#x\n", status); + todo_wine ok(compare_unicode_string(name_info->Name.Buffer, name_info->Name.Length, L"\Device\WineTestDriver"), + "wrong name %s\n", debugstr_w(name_info->Name.Buffer)); + + status = NtQueryObject(device, ObjectTypeInformation, buffer, sizeof(buffer), NULL); + ok(!status, "got %#x\n", status); + ok(compare_unicode_string(type_info->TypeName.Buffer, type_info->TypeName.Length, L"File"), + "wrong name %s\n", debugstr_wn(type_info->TypeName.Buffer, type_info->TypeName.Length / sizeof(WCHAR))); + + status = NtQueryInformationFile(device, &io, buffer, sizeof(buffer), FileNameInformation); + todo_wine ok(status == STATUS_INVALID_DEVICE_REQUEST, "got %#x\n", status); + + file = CreateFileA("\\.\WineTestDriver\subfile", 0, 0, NULL, OPEN_EXISTING, 0, NULL); + todo_wine ok(file != INVALID_HANDLE_VALUE, "got error %u\n", GetLastError()); + if (file == INVALID_HANDLE_VALUE) return; + + memset(buffer, 0xcc, sizeof(buffer)); + status = NtQueryObject(file, ObjectNameInformation, buffer, sizeof(buffer), &size); + ok(!status, "got %#x\n", status); + ok(size == sizeof(*name_info) + sizeof(L"\Device\WineTestDriver\subfile"), "wrong size %u\n", size); + ok(compare_unicode_string(name_info->Name.Buffer, name_info->Name.Length, L"\Device\WineTestDriver\subfile"), + "wrong name %s\n", debugstr_w(name_info->Name.Buffer)); + + memset(buffer, 0xcc, sizeof(buffer)); + status = NtQueryObject(file, ObjectNameInformation, buffer, size - 2, &size); + ok(status == STATUS_BUFFER_OVERFLOW, "got %#x\n", status); + ok(size == sizeof(*name_info) + sizeof(L"\Device\WineTestDriver\subfile"), "wrong size %u\n", size); + ok(compare_unicode_string(name_info->Name.Buffer, name_info->Name.Length, L"\Device\WineTestDriver\subfil"), + "wrong name %s\n", debugstr_w(name_info->Name.Buffer)); + + memset(buffer, 0xcc, sizeof(buffer)); + status = NtQueryObject(file, ObjectNameInformation, buffer, sizeof(*name_info), &size); + ok(status == STATUS_BUFFER_OVERFLOW, "got %#x\n", status); + ok(size == sizeof(*name_info) + sizeof(L"\Device\WineTestDriver\subfile"), "wrong size %u\n", size); + + status = NtQueryObject(file, ObjectTypeInformation, buffer, sizeof(buffer), NULL); + ok(!status, "got %#x\n", status); + ok(compare_unicode_string(type_info->TypeName.Buffer, type_info->TypeName.Length, L"File"), + "wrong name %s\n", debugstr_wn(type_info->TypeName.Buffer, type_info->TypeName.Length / sizeof(WCHAR))); + + status = NtQueryInformationFile(file, &io, buffer, sizeof(buffer), FileNameInformation); + ok(!status, "got %#x\n", status); + ok(compare_unicode_string(file_info->FileName, file_info->FileNameLength, L"\subfile"), + "wrong name %s\n", debugstr_wn(file_info->FileName, file_info->FileNameLength / sizeof(WCHAR))); + + CloseHandle(file); + + file = CreateFileA("\\.\WineTestDriver\notimpl", 0, 0, NULL, OPEN_EXISTING, 0, NULL); + ok(file != INVALID_HANDLE_VALUE, "got error %u\n", GetLastError()); + + status = NtQueryObject(file, ObjectNameInformation, buffer, sizeof(buffer), NULL); + ok(!status, "got %#x\n", status); + ok(compare_unicode_string(name_info->Name.Buffer, name_info->Name.Length, L"\Device\WineTestDriver"), + "wrong name %s\n", debugstr_w(name_info->Name.Buffer)); + + status = NtQueryInformationFile(file, &io, buffer, sizeof(buffer), FileNameInformation); + ok(status == STATUS_NOT_IMPLEMENTED, "got %#x\n", status); + + CloseHandle(file); + + file = CreateFileA("\\.\WineTestDriver\badparam", 0, 0, NULL, OPEN_EXISTING, 0, NULL); + ok(file != INVALID_HANDLE_VALUE, "got error %u\n", GetLastError()); + + status = NtQueryObject(file, ObjectNameInformation, buffer, sizeof(buffer), NULL); + ok(!status, "got %#x\n", status); + ok(compare_unicode_string(name_info->Name.Buffer, name_info->Name.Length, L"\Device\WineTestDriver"), + "wrong name %s\n", debugstr_w(name_info->Name.Buffer)); + + status = NtQueryInformationFile(file, &io, buffer, sizeof(buffer), FileNameInformation); + ok(status == STATUS_INVALID_PARAMETER, "got %#x\n", status); + + CloseHandle(file); + + file = CreateFileA("\\.\WineTestDriver\genfail", 0, 0, NULL, OPEN_EXISTING, 0, NULL); + ok(file != INVALID_HANDLE_VALUE, "got error %u\n", GetLastError()); + + status = NtQueryObject(file, ObjectNameInformation, buffer, sizeof(buffer), NULL); + ok(status == STATUS_UNSUCCESSFUL, "got %#x\n", status); + + status = NtQueryInformationFile(file, &io, buffer, sizeof(buffer), FileNameInformation); + ok(status == STATUS_UNSUCCESSFUL, "got %#x\n", status); + + CloseHandle(file); + + file = CreateFileA("\\.\WineTestDriver\badtype", 0, 0, NULL, OPEN_EXISTING, 0, NULL); + ok(file != INVALID_HANDLE_VALUE, "got error %u\n", GetLastError()); + + status = NtQueryObject(file, ObjectNameInformation, buffer, sizeof(buffer), NULL); + ok(status == STATUS_OBJECT_TYPE_MISMATCH, "got %#x\n", status); + + status = NtQueryInformationFile(file, &io, buffer, sizeof(buffer), FileNameInformation); + ok(status == STATUS_OBJECT_TYPE_MISMATCH, "got %#x\n", status); + + CloseHandle(file); +} + static void test_driver3(void) { char filename[MAX_PATH]; @@ -644,6 +757,7 @@ START_TEST(ntoskrnl) test_load_driver(service2); test_file_handles(); test_return_status(); + test_object_info();
/* We need a separate ioctl to call IoDetachDevice(); calling it in the * driver unload routine causes a live-lock. */