Taking a closer look since the last time I looked at this patch...
From: "Erich E. Hoover" erich.e.hoover@gmail.com Subject: [PATCH v6 3/5] ntoskrnl.exe: Implement volume information queries for device files. Message-Id: CAEU2+vqrdb5u4fTQe80yUHBciFLwoF0RmiXMsFZ_s_wbndm+jQ@mail.gmail.com Date: Sat, 6 Feb 2021 11:25:22 -0700
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).
v6: No change v5: No change 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 ceb6cad8f6743f2e26db2da227162d8458973496 Mon Sep 17 00:00:00 2001 From: "Erich E. Hoover" erich.e.hoover@gmail.com 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 erich.e.hoover@gmail.com
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 9dc5809614a..b735be42c02 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;
- }
This looks wrong. No data is ever passed in to this IRP, so it should probably just always allocate an output buffer, like dispatch_read().
[There's probably an argument to reusing context.in_buff already allocated in dispatch_read() and here, but we'd need to store the capacity of the buffer somewhere. This patch will never reuse it, as in_size should always be 0.]
- 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 5617342d803..117b1ca3c64 100644 --- a/dlls/ntoskrnl.exe/tests/driver.c +++ b/dlls/ntoskrnl.exe/tests/driver.c @@ -2455,6 +2455,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";
Missing "const"?
ULONG serial = 0xdeadbeef;
if (length < sizeof(FILE_FS_VOLUME_INFORMATION))
{
ret = STATUS_INFO_LENGTH_MISMATCH;
break;
}
info->VolumeCreationTime.QuadPart = 0; /* FIXME */
"FIXME" seems a bit odd in a test like this, was that a copy/paste error?
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); @@ -2497,6 +2538,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, L"IoDriverObjectType");
diff --git a/dlls/ntoskrnl.exe/tests/ntoskrnl.c b/dlls/ntoskrnl.exe/tests/ntoskrnl.c index 2a7ebaa9599..1cfdeff0911 100644 --- a/dlls/ntoskrnl.exe/tests/ntoskrnl.c +++ b/dlls/ntoskrnl.exe/tests/ntoskrnl.c @@ -502,6 +502,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;
@@ -521,6 +522,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;
@@ -554,6 +558,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 843ba3423ca..6601a812142 100644 --- a/server/device.c +++ b/server/device.c @@ -207,6 +207,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 = { @@ -241,7 +242,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 */
@@ -592,6 +593,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;
@@ -629,6 +634,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 cd7ed35d9a3..1589eb57f98 100644 --- a/server/protocol.def +++ b/server/protocol.def @@ -696,6 +696,7 @@ enum irp_type IRP_CALL_WRITE, IRP_CALL_FLUSH, IRP_CALL_IOCTL,
- IRP_CALL_VOLUME, IRP_CALL_FREE, IRP_CALL_CANCEL
}; @@ -749,6 +750,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