From bf0008f98d8df3c8e5065dc86b75d4a3d90a9ec7 Mon Sep 17 00:00:00 2001
From: "Erich E. Hoover" <erich.e.hoover@gmail.com>
Date: Fri, 12 Jun 2020 14:48:25 -0600
Subject: mountmgr.sys: Add support for volume information queries.

Signed-off-by: Erich E. Hoover <erich.e.hoover@gmail.com>
---
 dlls/mountmgr.sys/device.c | 123 +++++++++++++++++++++++++++++++++++--
 1 file changed, 119 insertions(+), 4 deletions(-)

diff --git a/dlls/mountmgr.sys/device.c b/dlls/mountmgr.sys/device.c
index c9cee002bc0..dd2c7d70759 100644
--- a/dlls/mountmgr.sys/device.c
+++ b/dlls/mountmgr.sys/device.c
@@ -91,6 +91,7 @@ struct disk_device
     char                 *unix_device; /* unix device path */
     char                 *unix_mount;  /* unix mount point path */
     char                 *serial;      /* disk serial number */
+    struct volume        *volume;      /* associated volume */
 };
 
 struct volume
@@ -746,7 +747,7 @@ static DWORD VOLUME_GetAudioCDSerial( const CDROM_TOC *toc )
 
 
 /* create the disk device for a given volume */
-static NTSTATUS create_disk_device( enum device_type type, struct disk_device **device_ret )
+static NTSTATUS create_disk_device( enum device_type type, struct disk_device **device_ret, struct volume *volume )
 {
     static const WCHAR harddiskvolW[] = {'\\','D','e','v','i','c','e',
                                          '\\','H','a','r','d','d','i','s','k','V','o','l','u','m','e','%','u',0};
@@ -808,6 +809,7 @@ static NTSTATUS create_disk_device( enum device_type type, struct disk_device **
         device->unix_device    = NULL;
         device->unix_mount     = NULL;
         device->symlink.Buffer = NULL;
+        device->volume         = volume;
 
         if (link_format)
         {
@@ -930,7 +932,7 @@ static NTSTATUS create_volume( const char *udi, enum device_type type, struct vo
     if (!(volume = RtlAllocateHeap( GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*volume) )))
         return STATUS_NO_MEMORY;
 
-    if (!(status = create_disk_device( type, &volume->device )))
+    if (!(status = create_disk_device( type, &volume->device, volume )))
     {
         if (udi) set_volume_udi( volume, udi );
         list_add_tail( &volumes_list, &volume->entry );
@@ -1125,7 +1127,7 @@ static NTSTATUS set_volume_info( struct volume *volume, struct dos_drive *drive,
 
     if (type != disk_device->type)
     {
-        if ((status = create_disk_device( type, &disk_device ))) return status;
+        if ((status = create_disk_device( type, &disk_device, volume ))) return status;
         if (volume->mount)
         {
             delete_mount_point( volume->mount );
@@ -1826,6 +1828,118 @@ static NTSTATUS query_property( struct disk_device *device, IRP *irp )
     return status;
 }
 
+static NTSTATUS WINAPI harddisk_query_volume( DEVICE_OBJECT *device, IRP *irp )
+{
+    IO_STACK_LOCATION *irpsp = IoGetCurrentIrpStackLocation( irp );
+    int info_class = irpsp->Parameters.QueryVolume.FsInformationClass;
+    ULONG length = irpsp->Parameters.QueryVolume.Length;
+    struct disk_device *dev = device->DeviceExtension;
+    PIO_STATUS_BLOCK io = &irp->IoStatus;
+    struct volume *volume;
+    NTSTATUS status;
+
+    TRACE( "volume query %x length %u\n", info_class, length );
+
+    EnterCriticalSection( &device_section );
+    volume = dev->volume;
+    if (!volume)
+    {
+        status = STATUS_BAD_DEVICE_TYPE;
+        goto done;
+    }
+
+    switch(info_class)
+    {
+    case FileFsVolumeInformation:
+    {
+
+        FILE_FS_VOLUME_INFORMATION *info = irp->AssociatedIrp.SystemBuffer;
+
+        if (length < sizeof(FILE_FS_VOLUME_INFORMATION))
+        {
+            status = STATUS_INFO_LENGTH_MISMATCH;
+            break;
+        }
+
+        info->VolumeCreationTime.QuadPart = 0; /* FIXME */
+        info->VolumeSerialNumber = volume->serial;
+        info->VolumeLabelLength = min( strlenW(volume->label) * sizeof(WCHAR),
+                                       length - offsetof( FILE_FS_VOLUME_INFORMATION, VolumeLabel ) );
+        info->SupportsObjects = (get_mountmgr_fs_type(volume->fs_type) == MOUNTMGR_FS_TYPE_NTFS);
+        memcpy( info->VolumeLabel, volume->label, info->VolumeLabelLength );
+
+        io->Information = offsetof( FILE_FS_VOLUME_INFORMATION, VolumeLabel ) + info->VolumeLabelLength;
+        status = STATUS_SUCCESS;
+        break;
+    }
+    case FileFsAttributeInformation:
+    {
+        static const WCHAR fatW[] = {'F','A','T'};
+        static const WCHAR fat32W[] = {'F','A','T','3','2'};
+        static const WCHAR ntfsW[] = {'N','T','F','S'};
+        static const WCHAR cdfsW[] = {'C','D','F','S'};
+        static const WCHAR udfW[] = {'U','D','F'};
+
+        FILE_FS_ATTRIBUTE_INFORMATION *info = irp->AssociatedIrp.SystemBuffer;
+        enum mountmgr_fs_type fs_type = get_mountmgr_fs_type(volume->fs_type);
+
+        if (length < sizeof(FILE_FS_ATTRIBUTE_INFORMATION))
+        {
+            status = STATUS_INFO_LENGTH_MISMATCH;
+            break;
+        }
+
+        switch (fs_type)
+        {
+        case MOUNTMGR_FS_TYPE_ISO9660:
+            info->FileSystemAttributes = FILE_READ_ONLY_VOLUME;
+            info->MaximumComponentNameLength = 221;
+            info->FileSystemNameLength = min( sizeof(cdfsW), length - offsetof( FILE_FS_ATTRIBUTE_INFORMATION, FileSystemName ) );
+            memcpy(info->FileSystemName, cdfsW, info->FileSystemNameLength);
+            break;
+        case MOUNTMGR_FS_TYPE_UDF:
+            info->FileSystemAttributes = FILE_READ_ONLY_VOLUME | FILE_UNICODE_ON_DISK | FILE_CASE_SENSITIVE_SEARCH;
+            info->MaximumComponentNameLength = 255;
+            info->FileSystemNameLength = min( sizeof(udfW), length - offsetof( FILE_FS_ATTRIBUTE_INFORMATION, FileSystemName ) );
+            memcpy(info->FileSystemName, udfW, info->FileSystemNameLength);
+            break;
+        case MOUNTMGR_FS_TYPE_FAT:
+            info->FileSystemAttributes = FILE_CASE_PRESERVED_NAMES; /* FIXME */
+            info->MaximumComponentNameLength = 255;
+            info->FileSystemNameLength = min( sizeof(fatW), length - offsetof( FILE_FS_ATTRIBUTE_INFORMATION, FileSystemName ) );
+            memcpy(info->FileSystemName, fatW, info->FileSystemNameLength);
+            break;
+        case MOUNTMGR_FS_TYPE_FAT32:
+            info->FileSystemAttributes = FILE_CASE_PRESERVED_NAMES; /* FIXME */
+            info->MaximumComponentNameLength = 255;
+            info->FileSystemNameLength = min( sizeof(fat32W), length - offsetof( FILE_FS_ATTRIBUTE_INFORMATION, FileSystemName ) );
+            memcpy(info->FileSystemName, fat32W, info->FileSystemNameLength);
+            break;
+        default:
+            info->FileSystemAttributes = FILE_CASE_PRESERVED_NAMES | FILE_PERSISTENT_ACLS;
+            info->MaximumComponentNameLength = 255;
+            info->FileSystemNameLength = min( sizeof(ntfsW), length - offsetof( FILE_FS_ATTRIBUTE_INFORMATION, FileSystemName ) );
+            memcpy(info->FileSystemName, ntfsW, info->FileSystemNameLength);
+            break;
+        }
+
+        io->Information = offsetof( FILE_FS_ATTRIBUTE_INFORMATION, FileSystemName ) + info->FileSystemNameLength;
+        status = STATUS_SUCCESS;
+        break;
+    }
+    default:
+        FIXME("Unsupported volume query %x\n", irpsp->Parameters.QueryVolume.FsInformationClass);
+        status = STATUS_NOT_SUPPORTED;
+        break;
+    }
+
+done:
+    io->u.Status = status;
+    LeaveCriticalSection( &device_section );
+    IoCompleteRequest( irp, IO_NO_INCREMENT );
+    return status;
+}
+
 /* handler for ioctls on the harddisk device */
 static NTSTATUS WINAPI harddisk_ioctl( DEVICE_OBJECT *device, IRP *irp )
 {
@@ -1925,9 +2039,10 @@ NTSTATUS WINAPI harddisk_driver_entry( DRIVER_OBJECT *driver, UNICODE_STRING *pa
 
     harddisk_driver = driver;
     driver->MajorFunction[IRP_MJ_DEVICE_CONTROL] = harddisk_ioctl;
+    driver->MajorFunction[IRP_MJ_QUERY_VOLUME_INFORMATION] = harddisk_query_volume;
 
     /* create a harddisk0 device that isn't assigned to any drive */
-    create_disk_device( DEVICE_HARDDISK, &device );
+    create_disk_device( DEVICE_HARDDISK, &device, NULL );
 
     create_drive_devices();
 
-- 
2.17.1

