From ddd4ee3e7239efa5da14249ba137de1cf70752b3 Mon Sep 17 00:00:00 2001
From: Dan Kegel <dank@kegel.com>
Date: Thu, 9 Feb 2012 14:49:20 -0800
Subject: [PATCH 6/6] ntdll: Fix IOCTL_DVD_READ_STRUCTURE parameter checking
 and output format

The output buffer always starts with _DVD_DESCRIPTOR_HEADER,
odd length output buffers are not allowed, and
overly long output buffers are zero-filled on the end up to offset 2052,
as on windows.
---
 dlls/kernel32/tests/cdrom.c |  157 ++++++++++++++++++++++++++++++++++
 dlls/ntdll/cdrom.c          |  196 ++++++++++++++++++++++++++-----------------
 2 files changed, 275 insertions(+), 78 deletions(-)

diff --git a/dlls/kernel32/tests/cdrom.c b/dlls/kernel32/tests/cdrom.c
index 022a1c6..889ebef 100644
--- a/dlls/kernel32/tests/cdrom.c
+++ b/dlls/kernel32/tests/cdrom.c
@@ -26,6 +26,12 @@
 
 #include "wine/test.h"
 
+#ifdef WORDS_BIGENDIAN
+#define GET_BE_SHORT(x) (x)
+#else
+static unsigned short GET_BE_SHORT(unsigned short x) { return ((x & 0xff) << 8) | ((x & 0xff00) >> 8); }
+#endif
+
 #include <pshpack1.h>
 
 typedef struct xDVD_LAYER_DESCRIPTOR {
@@ -39,6 +45,16 @@ typedef struct xDVD_LAYER_DESCRIPTOR {
     char reserved1;
 } xDVD_LAYER_DESCRIPTOR;
 
+struct xDVD_MANUFACTURER_DESCRIPTOR {
+    struct _DVD_DESCRIPTOR_HEADER h;
+    struct _DVD_MANUFACTURER_DESCRIPTOR manufacturer;
+};
+
+struct xDVD_COPYRIGHT_DESCRIPTOR {
+    struct _DVD_DESCRIPTOR_HEADER h;
+    struct _DVD_COPYRIGHT_DESCRIPTOR copyright;
+};
+
 #include <poppack.h>
 
 #define HUGEBUF 8192   /* Larger than ever needed */
@@ -80,6 +96,80 @@ static void test_readStruct_input(HANDLE hdevice)
     }
 }
 
+#define BACKGROUND_VAL 0x77
+
+/* Returns true if all bytes are zero */
+static int all_zeroes(char *buf, int len)
+{
+    while (len-- > 0)
+        if (*buf++) return FALSE;
+    return TRUE;
+}
+
+/* Try IOCTL_DVD_READ_STRUCTURE.DvdPhysicalDescriptor
+ * with various size output buffers
+ */
+#define OSIZE sizeof(xDVD_LAYER_DESCRIPTOR)
+static void test_readStruct_layer(HANDLE hdevice)
+{
+    static struct {
+        int olen;
+        BOOL ret;
+        DWORD nread;
+        DWORD err;
+    } cases[] = {
+       {3,         FALSE, 0,       ERROR_INSUFFICIENT_BUFFER },
+       {4,         TRUE,  4,       0,                        },
+       {5,         FALSE, 0,       ERROR_INVALID_PARAMETER,  },
+       {6,         TRUE,  6,       0,                        },
+       {OSIZE-2,   TRUE,  OSIZE-2, 0,                        },
+       {OSIZE-1,   FALSE, 0,       ERROR_INVALID_PARAMETER,  },
+       {OSIZE,     TRUE,  OSIZE,   0,                        },
+       {OSIZE-1,   FALSE, 0,       ERROR_INVALID_PARAMETER,  },
+       {OSIZE+2,   TRUE,  OSIZE+2, 0,                        },
+       {2052,      TRUE,  2052,    0,                        },
+       {2054,      TRUE,  2054,    0,                        },
+       {4096,      TRUE,  4096,    0,                        },
+       {4098,      TRUE,  4098,    0,                        },
+       {0}
+    };
+    DVD_READ_STRUCTURE ibuf;
+    int i;
+
+    ZeroMemory(&ibuf, sizeof(ibuf));
+    ibuf.Format = DvdPhysicalDescriptor;
+
+    for (i=0; cases[i].olen; i++) {
+        char obuf[HUGEBUF];
+        DWORD bytes_read = 0;
+        BOOL bResult;
+        int all_clear;
+
+        memset(obuf, BACKGROUND_VAL, sizeof(obuf));
+        bResult = DeviceIoControl(hdevice, IOCTL_DVD_READ_STRUCTURE,
+                                  &ibuf, sizeof(ibuf),
+                                  obuf, cases[i].olen, &bytes_read, 0);
+        ok(bResult == cases[i].ret,
+           "case %d: DeviceIoControl returned %d, expected %d\n",
+           i, bResult, cases[i].ret);
+        ok(bytes_read == cases[i].nread,
+           "case %d: DeviceIoControl read %d bytes, expected %d\n",
+           i, bytes_read, cases[i].nread);
+        /* Windows zeroes out all bytes [OSIZE..2052).  Bytes past 2052 are set to undefined values. */
+        if (cases[i].olen > OSIZE) {
+            all_clear = all_zeroes(obuf + OSIZE, min(2052, bytes_read) - OSIZE);
+            ok(all_clear, "case %d: not all trailing bytes zero\n", i);
+        }
+        if (cases[i].err) {
+            DWORD err = GetLastError();
+            ok(err == cases[i].err,
+               "case %d: Last error was %d, expected %d\n",
+               i, err, cases[i].err);
+        }
+    }
+}
+#undef OSIZE
+
 /* Backdoor way to do the same thing as IOCTL_DVD_READ_STRUCTURE */
 DWORD read_discStructure(HANDLE device, int format, void *obuf, int obuflen)
 {
@@ -125,6 +215,9 @@ static void test_readStruct_layer_vs_passthrough(HANDLE hdevice)
     bRet = read_discStructure(hdevice, DvdPhysicalDescriptor, &xlayer1, sizeof(xlayer1));
     ok(bRet, "can't read layer via SCSI_PASS_THROUGH_DIRECT\n");
 
+    /* Linux 3.0 and earlier fail IOCTL_DVD_READ_STRUCTURE
+     * See https://bugzilla.kernel.org/show_bug.cgi?id=39062
+     */
     ZeroMemory(&ibuf, sizeof(ibuf));
     ibuf.Format = DvdPhysicalDescriptor;
     bRet = DeviceIoControl(hdevice, IOCTL_DVD_READ_STRUCTURE,
@@ -134,6 +227,67 @@ static void test_readStruct_layer_vs_passthrough(HANDLE hdevice)
 
     ok(memcmp(&xlayer1.layer, &xlayer2.layer, sizeof(xlayer1.layer)) == 0,
        "SCSI_PASS_THROUGH_DIRECT and IOCTL_DVD_READ_STRUCTURE report different results for DvdPhysicalDescriptor\n");
+
+    ok(GET_BE_SHORT(xlayer1.h.Length) == xlayer2.h.Length,
+       "SCSI_PASS_THROUGH_DIRECT reports length %d, IOCTL_DVD_READ_STRUCTURE report length %d for DvdPhysicalDescriptor\n",
+       GET_BE_SHORT(xlayer1.h.Length), xlayer2.h.Length);
+}
+
+/* Compare dvd manufacturer descriptor as read via two different APIs */
+static void test_readStruct_manufacturer_vs_passthrough(HANDLE hdevice)
+{
+    struct xDVD_MANUFACTURER_DESCRIPTOR xmanufacturer1;
+    struct xDVD_MANUFACTURER_DESCRIPTOR xmanufacturer2;
+    DVD_READ_STRUCTURE ibuf;
+    DWORD bytes_read;
+    BOOL bRet;
+
+    bRet = read_discStructure(hdevice, DvdManufacturerDescriptor, &xmanufacturer1, sizeof(xmanufacturer1));
+    ok(bRet, "can't read manufacturer via SCSI_PASS_THROUGH_DIRECT\n");
+
+    /* Linux 3.0 and earlier fail IOCTL_DVD_READ_STRUCTURE
+     * See https://bugzilla.kernel.org/show_bug.cgi?id=39062
+     */
+    ZeroMemory(&ibuf, sizeof(ibuf));
+    ibuf.Format = DvdManufacturerDescriptor;
+    bRet = DeviceIoControl(hdevice, IOCTL_DVD_READ_STRUCTURE,
+                           &ibuf, sizeof(ibuf),
+                           &xmanufacturer2, sizeof(xmanufacturer2), &bytes_read, 0);
+    ok(bRet, "can't read manufacturer via IOCTL_DVD_READ_STRUCTURE\n");
+
+    ok(memcmp(&xmanufacturer1.manufacturer, &xmanufacturer2.manufacturer, sizeof(xmanufacturer1.manufacturer)) == 0,
+       "SCSI_PASS_THROUGH_DIRECT and IOCTL_DVD_READ_STRUCTURE report different results for DvdManufacturerDescriptor\n");
+
+    ok(GET_BE_SHORT(xmanufacturer1.h.Length) == xmanufacturer2.h.Length,
+       "SCSI_PASS_THROUGH_DIRECT reports length %d, IOCTL_DVD_READ_STRUCTURE report length %d for DvdManufacturerDescriptor\n",
+       GET_BE_SHORT(xmanufacturer1.h.Length), xmanufacturer2.h.Length);
+}
+
+/* Compare dvd copyright descriptor as read via two different APIs */
+static void test_readStruct_copyright_vs_passthrough(HANDLE hdevice)
+{
+    struct xDVD_COPYRIGHT_DESCRIPTOR xcopyright1;
+    struct xDVD_COPYRIGHT_DESCRIPTOR xcopyright2;
+    DVD_READ_STRUCTURE ibuf;
+    DWORD bytes_read;
+    BOOL bRet;
+
+    bRet = read_discStructure(hdevice, DvdCopyrightDescriptor, &xcopyright1, sizeof(xcopyright1));
+    ok(bRet, "can't read copyright via SCSI_PASS_THROUGH_DIRECT\n");
+
+    ZeroMemory(&ibuf, sizeof(ibuf));
+    ibuf.Format = DvdCopyrightDescriptor;
+    bRet = DeviceIoControl(hdevice, IOCTL_DVD_READ_STRUCTURE,
+                           &ibuf, sizeof(ibuf),
+                           &xcopyright2, sizeof(xcopyright2), &bytes_read, 0);
+    ok(bRet, "can't read copyright via IOCTL_DVD_READ_STRUCTURE, err %d\n", GetLastError());
+
+    ok(memcmp(&xcopyright1.copyright, &xcopyright2.copyright, sizeof(xcopyright1.copyright)) == 0,
+       "SCSI_PASS_THROUGH_DIRECT and IOCTL_DVD_READ_STRUCTURE report different results for DvdCopyrightDescriptor\n");
+
+    ok(GET_BE_SHORT(xcopyright1.h.Length) == xcopyright2.h.Length,
+       "SCSI_PASS_THROUGH_DIRECT reports length %d, IOCTL_DVD_READ_STRUCTURE report length %d for DvdCopyrightDescriptor\n",
+       GET_BE_SHORT(xcopyright1.h.Length), xcopyright2.h.Length);
 }
 
 START_TEST(cdrom)
@@ -178,6 +332,9 @@ START_TEST(cdrom)
         return;
     }
     test_readStruct_input(hdevice);
+    test_readStruct_layer(hdevice);
     test_readStruct_layer_vs_passthrough(hdevice);
+    test_readStruct_manufacturer_vs_passthrough(hdevice);
+    test_readStruct_copyright_vs_passthrough(hdevice);
     CloseHandle(hdevice);
 }
diff --git a/dlls/ntdll/cdrom.c b/dlls/ntdll/cdrom.c
index b151983..e2eb4db 100644
--- a/dlls/ntdll/cdrom.c
+++ b/dlls/ntdll/cdrom.c
@@ -218,33 +218,59 @@ static const char *iocodex(DWORD code)
 #define FRAME_OF_TOC(toc, idx)  FRAME_OF_ADDR((toc).TrackData[idx - (toc).FirstTrack].Address)
 #define MSF_OF_FRAME(m,fr) {int f=(fr); ((UCHAR *)&(m))[2]=f%CD_FRAMES;f/=CD_FRAMES;((UCHAR *)&(m))[1]=f%CD_SECS;((UCHAR *)&(m))[0]=f/CD_SECS;}
 
-/* The documented format of DVD_LAYER_DESCRIPTOR is wrong. Even the format in the
- * DDK's header is wrong. There are four bytes at the start  defined by
- * MMC-5. The first two are the size of the structure in big-endian order as
- * defined by MMC-5. The other two are reserved.
+/* The results from DVD_READ_STRUCTURE are
+ * DVD_DESCRIPTOR_HEADER followed by a DVD_*_DESCRIPTOR.
+ * The Length field of DVD_DESCRIPTOR_HEADER
+ * appears to mean "number of bytes following this Length field,
+ * including the remaining two bytes of DVD_DESCRIPTOR_HEADER".
+ * There are two ioctls that can handle DVD_READ_STRUCTURE requests:
+ * IOCTL_DVD_READ_STRUCTURE which sets Length in host byte order, and
+ * SCSI_PASS_THROUGH_DIRECT which sets Length in big-endian byte order.
  */
-typedef struct
-{
-    UCHAR DataLength[2];
-    UCHAR Reserved0[2];
-    UCHAR BookVersion : 4;
-    UCHAR BookType : 4;
-    UCHAR MinimumRate : 4;
-    UCHAR DiskSize : 4;
-    UCHAR LayerType : 4;
-    UCHAR TrackPath : 1;
-    UCHAR NumberOfLayers : 2;
-    UCHAR Reserved1 : 1;
-    UCHAR TrackDensity : 4;
-    UCHAR LinearDensity : 4;
-    ULONG StartingDataSector;
-    ULONG EndDataSector;
-    ULONG EndLayerZeroSector;
-    UCHAR Reserved5 : 7;
-    UCHAR BCAFlag : 1;
-    UCHAR Reserved6;
-} internal_dvd_layer_descriptor;
 
+#include <pshpack1.h>
+
+typedef struct xDVD_LAYER_DESCRIPTOR {
+    struct _DVD_DESCRIPTOR_HEADER h;
+    struct _DVD_LAYER_DESCRIPTOR layer;
+    /* On Windows, _DVD_LAYER_DESCRIPTOR is 17 bytes, but
+     * IOCTL_DVD_READ_STRUCTURE wants 22 bytes, so it needs
+     * the header plus at least one byte of padding.
+     */
+    char Reserved;
+} xDVD_LAYER_DESCRIPTOR;
+
+struct xDVD_MANUFACTURER_DESCRIPTOR {
+    struct _DVD_DESCRIPTOR_HEADER h;
+    struct _DVD_MANUFACTURER_DESCRIPTOR manufacturer;
+};
+
+struct xDVD_COPYRIGHT_DESCRIPTOR {
+    struct _DVD_DESCRIPTOR_HEADER h;
+    struct _DVD_COPYRIGHT_DESCRIPTOR copyright;
+};
+
+struct xDVD_DISK_KEY_DESCRIPTOR {
+    struct _DVD_DESCRIPTOR_HEADER h;
+    struct _DVD_DISK_KEY_DESCRIPTOR disk_key;
+};
+
+struct xDVD_BCA_DESCRIPTOR {
+    struct _DVD_DESCRIPTOR_HEADER h;
+    struct _DVD_BCA_DESCRIPTOR bca;
+};
+
+#include <poppack.h>
+ 
+union xDVD_DESCRIPTOR {
+    struct _DVD_DESCRIPTOR_HEADER h;
+    struct xDVD_LAYER_DESCRIPTOR xlayer;
+    struct xDVD_MANUFACTURER_DESCRIPTOR xmanufacturer;
+    struct xDVD_COPYRIGHT_DESCRIPTOR xcopyright;
+    struct xDVD_DISK_KEY_DESCRIPTOR xdisk_key;
+    struct xDVD_BCA_DESCRIPTOR xbca;
+    char   size[2052];
+};
 
 static NTSTATUS CDROM_ReadTOC(int, int, CDROM_TOC*);
 static NTSTATUS CDROM_GetStatusCode(int);
@@ -2503,8 +2529,10 @@ static NTSTATUS DVD_GetRegion(int fd, PDVD_REGION region)
  *
  *
  */
-static NTSTATUS DVD_ReadStructure(int dev, const DVD_READ_STRUCTURE *structure, PDVD_LAYER_DESCRIPTOR layer)
+static NTSTATUS DVD_ReadStructure(int dev, const DVD_READ_STRUCTURE *structure, union xDVD_DESCRIPTOR *obuf, DWORD obuflen)
 {
+    union xDVD_DESCRIPTOR tmp;
+
 #ifdef DVD_READ_STRUCT
     /* dvd_struct is not defined consistently across platforms */
     union
@@ -2553,17 +2581,16 @@ static NTSTATUS DVD_ReadStructure(int dev, const DVD_READ_STRUCTURE *structure,
     if (ioctl(dev, DVD_READ_STRUCT, &s) < 0)
        return STATUS_INVALID_PARAMETER;
 
+    ZeroMemory(&tmp, sizeof(tmp));
+
     switch (structure->Format)
     {
     case DvdPhysicalDescriptor:
         {
-            internal_dvd_layer_descriptor *p = (internal_dvd_layer_descriptor *) layer;
+            DVD_LAYER_DESCRIPTOR *p = &tmp.xlayer.layer;
             struct dvd_layer *l = &s.physical.layer[s.physical.layer_num];
 
-            p->DataLength[0] = 2;
-            p->DataLength[1] = 8;
-            p->Reserved0[0] = 0;
-            p->Reserved0[1] = 0;
+            tmp.h.Length = 2050;
             p->BookVersion = l->book_version;
             p->BookType = l->book_type;
             p->MinimumRate = l->min_rate;
@@ -2579,13 +2606,14 @@ static NTSTATUS DVD_ReadStructure(int dev, const DVD_READ_STRUCTURE *structure,
             p->EndLayerZeroSector = GET_BE_DWORD(l->end_sector_l0);
             p->Reserved5 = 0;
             p->BCAFlag = l->bca;
-            p->Reserved6 = 0;
         }
         break;
 
     case DvdCopyrightDescriptor:
         {
-            PDVD_COPYRIGHT_DESCRIPTOR p = (PDVD_COPYRIGHT_DESCRIPTOR) layer;
+            PDVD_COPYRIGHT_DESCRIPTOR p = &tmp.xcopyright.copyright;
+            /* Windows sets Length to 6 */
+            tmp.h.Length = 6;
 
             p->CopyrightProtectionType = s.copyright.cpst;
             p->RegionManagementInformation = s.copyright.rmi;
@@ -2595,7 +2623,8 @@ static NTSTATUS DVD_ReadStructure(int dev, const DVD_READ_STRUCTURE *structure,
 
     case DvdDiskKeyDescriptor:
         {
-            PDVD_DISK_KEY_DESCRIPTOR p = (PDVD_DISK_KEY_DESCRIPTOR) layer;
+            PDVD_DISK_KEY_DESCRIPTOR p = &tmp.xdisk_key.disk_key;
+            tmp.h.Length = 2050;
 
             memcpy(p->DiskKeyData, s.disckey.value, 2048);
         }
@@ -2603,7 +2632,8 @@ static NTSTATUS DVD_ReadStructure(int dev, const DVD_READ_STRUCTURE *structure,
 
     case DvdBCADescriptor:
         {
-            PDVD_BCA_DESCRIPTOR p = (PDVD_BCA_DESCRIPTOR) layer;
+            PDVD_BCA_DESCRIPTOR p = &tmp.xbca.bca;
+            tmp.h.Length = s.bca.len + sizeof(tmp.h.Length);   /* ? */
 
             memcpy(p->BCAInformation, s.bca.value, s.bca.len);
         }
@@ -2611,7 +2641,9 @@ static NTSTATUS DVD_ReadStructure(int dev, const DVD_READ_STRUCTURE *structure,
 
     case DvdManufacturerDescriptor:
         {
-            PDVD_MANUFACTURER_DESCRIPTOR p = (PDVD_MANUFACTURER_DESCRIPTOR) layer;
+            PDVD_MANUFACTURER_DESCRIPTOR p = &tmp.xmanufacturer.manufacturer;
+            /* Windows sets Length to 2050 */
+            tmp.h.Length = 2050;
 
             memcpy(p->ManufacturingInformation, s.manufact.value, 2048);
         }
@@ -2621,7 +2653,7 @@ static NTSTATUS DVD_ReadStructure(int dev, const DVD_READ_STRUCTURE *structure,
 	break;
     }
 #elif defined(__APPLE__)
-    NTSTATUS ret = STATUS_NOT_SUPPORTED;
+    NTSTATUS ret;
     dk_dvd_read_structure_t dvdrs;
     union
     {
@@ -2630,14 +2662,6 @@ static NTSTATUS DVD_ReadStructure(int dev, const DVD_READ_STRUCTURE *structure,
         DVDDiscKeyInfo disk_key;
         DVDManufacturingInfo manf;
     } desc;
-    union
-    {
-        PDVD_LAYER_DESCRIPTOR layer;
-        internal_dvd_layer_descriptor *xlayer;
-        PDVD_COPYRIGHT_DESCRIPTOR copy;
-        PDVD_DISK_KEY_DESCRIPTOR disk_key;
-        PDVD_MANUFACTURER_DESCRIPTOR manf;
-    } nt_desc;
 
     nt_desc.layer = layer;
     dvdrs.address = (uint32_t)(structure->BlockByteOffset.QuadPart>>11);
@@ -2679,66 +2703,80 @@ static NTSTATUS DVD_ReadStructure(int dev, const DVD_READ_STRUCTURE *structure,
         return STATUS_NOT_SUPPORTED;
     }
     ret = CDROM_GetStatusCode(ioctl(dev, DKIOCDVDREADSTRUCTURE, &dvdrs));
-    if(ret == STATUS_SUCCESS)
+    if (ret != STATUS_SUCCESS)
+        return ret;
+    ZeroMemory(&tmp, sizeof(tmp));
+    switch(structure->Format)
     {
-        switch(structure->Format)
-        {
         case DvdPhysicalDescriptor:
-            nt_desc.xlayer->DataLength[0] = 2;
-            nt_desc.xlayer->DataLength[1] = 8;
-            nt_desc.xlayer->Reserved0[0] = 0;
-            nt_desc.xlayer->Reserved0[1] = 0;
-            nt_desc.xlayer->BookVersion = desc.phys.partVersion;
-            nt_desc.xlayer->BookType = desc.phys.bookType;
-            nt_desc.xlayer->MinimumRate = desc.phys.minimumRate;
-            nt_desc.xlayer->DiskSize = desc.phys.discSize;
-            nt_desc.xlayer->LayerType = desc.phys.layerType;
-            nt_desc.xlayer->TrackPath = desc.phys.trackPath;
-            nt_desc.xlayer->NumberOfLayers = desc.phys.numberOfLayers;
-            nt_desc.xlayer->Reserved1 = 0;
-            nt_desc.xlayer->TrackDensity = desc.phys.trackDensity;
-            nt_desc.xlayer->LinearDensity = desc.phys.linearDensity;
-            nt_desc.xlayer->BCAFlag = desc.phys.bcaFlag;
-            nt_desc.xlayer->StartingDataSector = OSReadBigInt32(&desc.phys.zero1, 0);
-            nt_desc.xlayer->EndDataSector = OSReadBigInt32(&desc.phys.zero2, 0);
-            nt_desc.xlayer->EndLayerZeroSector = OSReadBigInt32(&desc.phys.zero3, 0);
-            nt_desc.xlayer->Reserved5 = 0;
-            nt_desc.xlayer->Reserved6 = 0;
+          {
+            DVD_LAYER_DESCRIPTOR *p = &tmp.xlayer.layer;
+            tmp.h.Length = 2050;
+            p->BookVersion = desc.phys.partVersion;
+            p->BookType = desc.phys.bookType;
+            p->MinimumRate = desc.phys.minimumRate;
+            p->DiskSize = desc.phys.discSize;
+            p->LayerType = desc.phys.layerType;
+            p->TrackPath = desc.phys.trackPath;
+            p->NumberOfLayers = desc.phys.numberOfLayers;
+            p->Reserved1 = 0;
+            p->TrackDensity = desc.phys.trackDensity;
+            p->LinearDensity = desc.phys.linearDensity;
+            p->BCAFlag = desc.phys.bcaFlag;
+            p->StartingDataSector = OSReadBigInt32(&desc.phys.zero1, 0);
+            p->EndDataSector = OSReadBigInt32(&desc.phys.zero2, 0);
+            p->EndLayerZeroSector = OSReadBigInt32(&desc.phys.zero3, 0);
+            p->Reserved5 = 0;
             break;
+          }
 
         case DvdCopyrightDescriptor:
-            nt_desc.copy->CopyrightProtectionType =
+          {
+            PDVD_COPYRIGHT_DESCRIPTOR p = &tmp.xcopyright.copyright;
+            /* Windows sets Length to 6 */
+            tmp.h.Length = 6;
+            p->CopyrightProtectionType =
                 desc.copy.copyrightProtectionSystemType;
-            nt_desc.copy->RegionManagementInformation =
+            p->RegionManagementInformation =
                 desc.copy.regionMask;
-            nt_desc.copy->Reserved = 0;
+            p->Reserved = 0;
             break;
+          }
 
         case DvdDiskKeyDescriptor:
+          {
+            PDVD_DISK_KEY_DESCRIPTOR p = &tmp.xdisk_key.disk_key;
+            tmp.h.Length = 2050;
             memcpy(
-                nt_desc.disk_key->DiskKeyData,
+                p->DiskKeyData,
                 desc.disk_key.discKeyStructures,
                 2048);
             break;
+          }
 
         case DvdManufacturerDescriptor:
+          {
+            PDVD_MANUFACTURER_DESCRIPTOR p = &tmp.xmanufacturer.manufacturer;
+            /* Windows sets Length to 2050 */
+            tmp.h.Length = 2050;
             memcpy(
-                nt_desc.manf->ManufacturingInformation,
+                p->ManufacturingInformation,
                 desc.manf.discManufacturingInfo,
                 2048);
             break;
+          }
 
         case DvdBCADescriptor:
         case DvdMaxDescriptor:
         default:
             /* Silence warning */
             break;
-        }
     }
-    return ret;
 #else
+    tmp.h.Length = 4;
     FIXME("\n");
 #endif
+    memcpy(obuf, &tmp, min(obuflen, sizeof(tmp)));
     return STATUS_SUCCESS;
 
 }
@@ -3098,13 +3136,15 @@ NTSTATUS CDROM_DeviceIoControl(HANDLE hDevice,
         status = DVD_GetRegion(fd, lpOutBuffer);
         break;
     case IOCTL_DVD_READ_STRUCTURE:
-        sz = sizeof(DVD_LAYER_DESCRIPTOR);
         if (lpInBuffer == NULL || nInBufferSize < sizeof(DVD_READ_STRUCTURE)) status = STATUS_INVALID_PARAMETER;
-        else if (nOutBufferSize < sz) status = STATUS_BUFFER_TOO_SMALL;
+        else if (nOutBufferSize < sizeof(DVD_DESCRIPTOR_HEADER)) status = STATUS_BUFFER_TOO_SMALL;
+        else if (nOutBufferSize & 1) status = STATUS_INVALID_PARAMETER;
         else
         {
             TRACE("doing DVD_READ_STRUCTURE\n");
-            status = DVD_ReadStructure(fd, lpInBuffer, lpOutBuffer);
+            status = DVD_ReadStructure(fd, lpInBuffer, lpOutBuffer, nOutBufferSize);
+            if (status == STATUS_SUCCESS)
+                sz = nOutBufferSize;
         }
         break;
 
-- 
1.7.8.3

