[PATCH v6 3/5] ntoskrnl.exe: Implement volume information queries for device files.

Zebediah Figura (she/her) zfigura at codeweavers.com
Wed Feb 10 22:32:55 CST 2021


Taking a closer look since the last time I looked at this patch...

> From: "Erich E. Hoover" <erich.e.hoover at gmail.com>
> Subject: [PATCH v6 3/5] ntoskrnl.exe: Implement volume information queries for device files.
> Message-Id: <CAEU2+vqrdb5u4fTQe80yUHBciFLwoF0RmiXMsFZ_s_wbndm+jQ at 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 at 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 at 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( &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 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
> 



More information about the wine-devel mailing list