From 5a841c99e7bb22fbc4cd496bb5b30c8dca82f082 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       | 50 ++++++++++++++++++++++++++++++
 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, 129 insertions(+), 1 deletion(-)

diff --git a/dlls/ntoskrnl.exe/ntoskrnl.c b/dlls/ntoskrnl.exe/ntoskrnl.c
index 9dc5809614a..51029df03dc 100644
--- a/dlls/ntoskrnl.exe/ntoskrnl.c
+++ b/dlls/ntoskrnl.exe/ntoskrnl.c
@@ -760,6 +760,55 @@ 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;
+    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_buff = HeapAlloc( GetProcessHeap(), 0, out_size ))) return STATUS_NO_MEMORY;
+
+    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 = out_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 out_buff */
+    dispatch_irp( device, irp, context );
+
+    return STATUS_SUCCESS;
+}
+
 static NTSTATUS dispatch_free( struct dispatch_context *context )
 {
     void *obj = wine_server_get_ptr( context->params.free.obj );
@@ -791,6 +840,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..bf524e130f3 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 const WCHAR label[] = L"WineTestDriver";
+        ULONG serial = 0xdeadbeef;
+
+        if (length < sizeof(FILE_FS_VOLUME_INFORMATION))
+        {
+            ret = STATUS_INFO_LENGTH_MISMATCH;
+            break;
+        }
+
+        info->VolumeCreationTime.QuadPart = 0;
+        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 5e936b05f0f..6400751e339 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 */
@@ -594,6 +595,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;
@@ -631,6 +636,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( &params, 0, sizeof(params) );
+    params.volume.type = IRP_CALL_VOLUME;
+    params.volume.info_class = info_class;
+    return queue_irp( file, &params, 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 c1dc6cc84d1..8a93e16d3d4 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

