From: "Erich E. Hoover" Subject: [PATCH v4 3/5] ntoskrnl.exe: Implement volume information queries for device files. Message-Id: Date: Wed, 28 Oct 2020 15:35:13 -0600 This patch implements volume information queries for device files, which allows NtQueryVolumeInformationFile to issue IRP_MJ_QUERY_VOLUME_INFORMATION to return volume information from a driver. The patch also now includes a simple test that issues FileFsVolumeInformation (for systems that support unsigned drivers). v4: Split out content from server/device.c and dlls/ntoskrnl.exe/ntoskrnl.c, fixed copy-paste error identified by Zeb, and added tests Best, Erich From 77f72ec32c64ebea4f83dea976908f5b6e9c9755 Mon Sep 17 00:00:00 2001 From: "Erich E. Hoover" Date: Wed, 28 Oct 2020 13:39:50 -0600 Subject: ntoskrnl.exe: Implement volume information queries for device files. Signed-off-by: Erich E. Hoover --- dlls/ntoskrnl.exe/ntoskrnl.c | 64 ++++++++++++++++++++++++++++++ dlls/ntoskrnl.exe/tests/driver.c | 42 ++++++++++++++++++++ dlls/ntoskrnl.exe/tests/ntoskrnl.c | 11 +++++ server/device.c | 18 ++++++++- server/protocol.def | 9 +++++ 5 files changed, 143 insertions(+), 1 deletion(-) diff --git a/dlls/ntoskrnl.exe/ntoskrnl.c b/dlls/ntoskrnl.exe/ntoskrnl.c index 4092e33db43..25c49e11ef2 100644 --- a/dlls/ntoskrnl.exe/ntoskrnl.c +++ b/dlls/ntoskrnl.exe/ntoskrnl.c @@ -760,6 +760,69 @@ static NTSTATUS dispatch_ioctl( struct dispatch_context *context ) return STATUS_SUCCESS; } +/* process a volume information request for a given device */ +static NTSTATUS dispatch_volume( struct dispatch_context *context ) +{ + IO_STACK_LOCATION *irpsp; + IRP *irp; + void *out_buff = NULL; + void *to_free = NULL; + DEVICE_OBJECT *device; + FILE_OBJECT *file = wine_server_get_ptr( context->params.volume.file ); + ULONG out_size = context->params.volume.out_size; + + if (!file) return STATUS_INVALID_HANDLE; + + device = IoGetAttachedDevice( file->DeviceObject ); + + TRACE( "class 0x%x device %p file %p in_size %u out_size %u\n", + context->params.volume.info_class, device, file, context->in_size, out_size ); + + if (out_size) + { + if (out_size > context->in_size) + { + if (!(out_buff = HeapAlloc( GetProcessHeap(), 0, out_size ))) return STATUS_NO_MEMORY; + memcpy( out_buff, context->in_buff, context->in_size ); + to_free = context->in_buff; + context->in_buff = out_buff; + } + else + out_buff = context->in_buff; + } + + + irp = IoAllocateIrp( device->StackSize, FALSE ); + if (!irp) + { + HeapFree( GetProcessHeap(), 0, out_buff ); + return STATUS_NO_MEMORY; + } + + irpsp = IoGetNextIrpStackLocation( irp ); + irpsp->MajorFunction = IRP_MJ_QUERY_VOLUME_INFORMATION; + irpsp->Parameters.QueryVolume.FsInformationClass = context->params.volume.info_class; + irpsp->Parameters.QueryVolume.Length = out_size; + irpsp->DeviceObject = NULL; + irpsp->CompletionRoutine = NULL; + irpsp->FileObject = file; + irp->AssociatedIrp.SystemBuffer = context->in_buff; + irp->RequestorMode = KernelMode; + irp->UserBuffer = out_buff; + irp->UserIosb = NULL; + irp->UserEvent = NULL; + irp->Tail.Overlay.Thread = (PETHREAD)KeGetCurrentThread(); + irp->Tail.Overlay.OriginalFileObject = file; + irp->RequestorMode = UserMode; + context->in_buff = NULL; + + irp->Flags |= IRP_DEALLOCATE_BUFFER; /* deallocate in_buff */ + dispatch_irp( device, irp, context ); + + HeapFree( GetProcessHeap(), 0, to_free ); + return STATUS_SUCCESS; +} + static NTSTATUS dispatch_free( struct dispatch_context *context ) { void *obj = wine_server_get_ptr( context->params.free.obj ); @@ -791,6 +854,7 @@ static const dispatch_func dispatch_funcs[] = dispatch_write, /* IRP_CALL_WRITE */ dispatch_flush, /* IRP_CALL_FLUSH */ dispatch_ioctl, /* IRP_CALL_IOCTL */ + dispatch_volume, /* IRP_CALL_VOLUME */ dispatch_free, /* IRP_CALL_FREE */ dispatch_cancel /* IRP_CALL_CANCEL */ }; diff --git a/dlls/ntoskrnl.exe/tests/driver.c b/dlls/ntoskrnl.exe/tests/driver.c index dfc742eec3b..372328bcaba 100644 --- a/dlls/ntoskrnl.exe/tests/driver.c +++ b/dlls/ntoskrnl.exe/tests/driver.c @@ -2470,6 +2470,47 @@ static NTSTATUS WINAPI driver_QueryInformation(DEVICE_OBJECT *device, IRP *irp) return ret; } +static NTSTATUS WINAPI driver_QueryVolumeInformation(DEVICE_OBJECT *device, IRP *irp) +{ + IO_STACK_LOCATION *stack = IoGetCurrentIrpStackLocation(irp); + ULONG length = stack->Parameters.QueryVolume.Length; + NTSTATUS ret; + + switch (stack->Parameters.QueryVolume.FsInformationClass) + { + case FileFsVolumeInformation: + { + FILE_FS_VOLUME_INFORMATION *info = irp->AssociatedIrp.SystemBuffer; + static WCHAR label[] = L"WineTestDriver"; + ULONG serial = 0xdeadbeef; + + if (length < sizeof(FILE_FS_VOLUME_INFORMATION)) + { + ret = STATUS_INFO_LENGTH_MISMATCH; + break; + } + + info->VolumeCreationTime.QuadPart = 0; /* FIXME */ + info->VolumeSerialNumber = serial; + info->VolumeLabelLength = min( lstrlenW(label) * sizeof(WCHAR), + length - offsetof( FILE_FS_VOLUME_INFORMATION, VolumeLabel ) ); + info->SupportsObjects = TRUE; + memcpy( info->VolumeLabel, label, info->VolumeLabelLength ); + + irp->IoStatus.Information = offsetof( FILE_FS_VOLUME_INFORMATION, VolumeLabel ) + info->VolumeLabelLength; + ret = STATUS_SUCCESS; + 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); @@ -2515,6 +2556,7 @@ NTSTATUS WINAPI DriverEntry(DRIVER_OBJECT *driver, PUNICODE_STRING registry) 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_QUERY_VOLUME_INFORMATION] = driver_QueryVolumeInformation; 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 c2285aef6fc..18a46b48e61 100644 --- a/dlls/ntoskrnl.exe/tests/ntoskrnl.c +++ b/dlls/ntoskrnl.exe/tests/ntoskrnl.c @@ -503,6 +503,7 @@ 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_FS_VOLUME_INFORMATION *volume_info = (FILE_FS_VOLUME_INFORMATION *)buffer; FILE_NAME_INFORMATION *file_info = (FILE_NAME_INFORMATION *)buffer; HANDLE file; NTSTATUS status; @@ -522,6 +523,9 @@ static void test_object_info(void) status = NtQueryInformationFile(device, &io, buffer, sizeof(buffer), FileNameInformation); todo_wine ok(status == STATUS_INVALID_DEVICE_REQUEST, "got %#x\n", status); + status = NtQueryVolumeInformationFile(device, &io, buffer, sizeof(buffer), FileFsVolumeInformation); + 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; @@ -555,6 +559,13 @@ static void test_object_info(void) 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))); + status = NtQueryVolumeInformationFile(file, &io, buffer, sizeof(buffer), FileFsVolumeInformation); + ok(!status, "got %#x\n", status); + ok(volume_info->VolumeSerialNumber == 0xdeadbeef, + "wrong serial number 0x%08x\n", volume_info->VolumeSerialNumber); + ok(compare_unicode_string(volume_info->VolumeLabel, volume_info->VolumeLabelLength, L"WineTestDriver"), + "wrong name %s\n", debugstr_wn(volume_info->VolumeLabel, volume_info->VolumeLabelLength / sizeof(WCHAR))); + CloseHandle(file); file = CreateFileA("\\\\.\\WineTestDriver\\notimpl", 0, 0, NULL, OPEN_EXISTING, 0, NULL); diff --git a/server/device.c b/server/device.c index 652da83e1e2..6e24a85dc89 100644 --- a/server/device.c +++ b/server/device.c @@ -194,6 +194,7 @@ static int device_file_write( struct fd *fd, struct async *async, file_pos_t pos static int device_file_flush( struct fd *fd, struct async *async ); static int device_file_ioctl( struct fd *fd, ioctl_code_t code, struct async *async ); static void device_file_reselect_async( struct fd *fd, struct async_queue *queue ); +static int device_file_get_volume_info( struct fd *fd, struct async *async, unsigned int info_class ); static const struct object_ops device_file_ops = { @@ -228,7 +229,7 @@ static const struct fd_ops device_file_fd_ops = device_file_write, /* write */ device_file_flush, /* flush */ default_fd_get_file_info, /* get_file_info */ - no_fd_get_volume_info, /* get_volume_info */ + device_file_get_volume_info, /* get_volume_info */ device_file_ioctl, /* ioctl */ default_fd_queue_async, /* queue_async */ device_file_reselect_async /* reselect_async */ @@ -586,6 +587,10 @@ static int fill_irp_params( struct device_manager *manager, struct irp_call *irp irp->params.ioctl.file = get_kernel_object_ptr( manager, &irp->file->obj ); irp->params.ioctl.out_size = irp->iosb->out_size; break; + case IRP_CALL_VOLUME: + irp->params.volume.file = get_kernel_object_ptr( manager, &irp->file->obj ); + irp->params.volume.out_size = irp->iosb->out_size; + break; } *params = irp->params; @@ -623,6 +628,17 @@ static enum server_fd_type device_file_get_fd_type( struct fd *fd ) return FD_TYPE_DEVICE; } +static int device_file_get_volume_info( struct fd *fd, struct async *async, unsigned int info_class ) +{ + struct device_file *file = get_fd_user( fd ); + irp_params_t params; + + memset( ¶ms, 0, sizeof(params) ); + params.volume.type = IRP_CALL_VOLUME; + params.volume.info_class = info_class; + return queue_irp( file, ¶ms, async ); +} + static int device_file_read( struct fd *fd, struct async *async, file_pos_t pos ) { struct device_file *file = get_fd_user( fd ); diff --git a/server/protocol.def b/server/protocol.def index c2f69856a4c..172bb981779 100644 --- a/server/protocol.def +++ b/server/protocol.def @@ -680,6 +680,7 @@ enum irp_type IRP_CALL_WRITE, IRP_CALL_FLUSH, IRP_CALL_IOCTL, + IRP_CALL_VOLUME, IRP_CALL_FREE, IRP_CALL_CANCEL }; @@ -733,6 +734,14 @@ typedef union client_ptr_t file; /* opaque ptr for the file object */ } ioctl; struct + { + enum irp_type type; /* IRP_CALL_VOLUME */ + unsigned int info_class;/* information class */ + data_size_t out_size; /* needed output size */ + int __pad; + client_ptr_t file; /* opaque ptr for the file object */ + } volume; + struct { enum irp_type type; /* IRP_CALL_FREE */ int __pad; -- 2.17.1