kernel32: Add UDF support (try 2)

Alexandre Goujon ale.goujon at gmail.com
Thu Sep 1 09:30:15 CDT 2011


Based on Steven Wallace work.
Should fix the wine side of bug #26273
---
 dlls/kernel32/volume.c |  160 +++++++++++++++++++++++++++++++++++++++++++-----
 1 files changed, 144 insertions(+), 16 deletions(-)

diff --git a/dlls/kernel32/volume.c b/dlls/kernel32/volume.c
index 25affc3..4a2a7b5 100644
--- a/dlls/kernel32/volume.c
+++ b/dlls/kernel32/volume.c
@@ -46,7 +46,8 @@
 
 WINE_DEFAULT_DEBUG_CHANNEL(volume);
 
-#define SUPERBLOCK_SIZE 2048
+#define BLOCK_SIZE 2048
+#define SUPERBLOCK_SIZE BLOCK_SIZE
 #define SYMBOLIC_LINK_QUERY 0x0001
 
 #define CDFRAMES_PERSEC         75
@@ -63,7 +64,8 @@ enum fs_type
     FS_UNKNOWN,  /* unknown file system */
     FS_FAT1216,
     FS_FAT32,
-    FS_ISO9660
+    FS_ISO9660,
+    FS_UDF       /* For reference [E] = Ecma-167.pdf, [U] = udf260.pdf */
 };
 
 /* read a Unix symlink; returned buffer must be freed by caller */
@@ -387,30 +389,106 @@ static enum fs_type VOLUME_ReadFATSuperblock( HANDLE handle, BYTE *buff )
 
 
 /***********************************************************************
+ *           VOLUME_ReadCDBlock
+ */
+static BOOL VOLUME_ReadCDBlock( HANDLE handle, BYTE *buff, INT offs )
+{
+    DWORD size, whence = offs >= 0 ? FILE_BEGIN : FILE_END;
+
+    if (SetFilePointer( handle, offs, NULL, whence ) != offs ||
+        !ReadFile( handle, buff, SUPERBLOCK_SIZE, &size, NULL ) ||
+        size != SUPERBLOCK_SIZE)
+        return FALSE;
+
+    return TRUE;
+}
+
+
+/***********************************************************************
  *           VOLUME_ReadCDSuperblock
  */
 static enum fs_type VOLUME_ReadCDSuperblock( HANDLE handle, BYTE *buff )
 {
-    DWORD size, offs = VOLUME_FindCdRomDataBestVoldesc( handle );
+    int i;
+    DWORD offs;
+
+    /* Check UDF first as UDF and ISO9660 structures can coexist on the same medium
+     *  Starting from sector 16, we may find :
+     *  - a CD-ROM Volume Descriptor Set (ISO9660) containing one or more Volume Descriptors
+     *  - an Extented Area (UDF) -- [E] 2/8.3.1 and [U] 2.1.7
+     *  There is no explicit end so read 16 sectors and then give up */
+    for( i=16; i<16+16; i++)
+    {
+        if (!VOLUME_ReadCDBlock(handle, buff, i*BLOCK_SIZE))
+            continue;
+
+        /* We are supposed to check "BEA01", "NSR0x" and "TEA01" IDs + verify tag checksum
+         *  but we assume the volume is well-formatted */
+        if (!memcmp(&buff[1], "BEA01", 5)) return FS_UDF;
+    }
 
+    offs = VOLUME_FindCdRomDataBestVoldesc( handle );
     if (!offs) return FS_UNKNOWN;
 
-    if (SetFilePointer( handle, offs, NULL, FILE_BEGIN ) != offs ||
-        !ReadFile( handle, buff, SUPERBLOCK_SIZE, &size, NULL ) ||
-        size != SUPERBLOCK_SIZE)
+    if (!VOLUME_ReadCDBlock(handle, buff, offs))
         return FS_ERROR;
 
-    /* check for iso9660 present */
+    /* check for the iso9660 identifier */
     if (!memcmp(&buff[1], "CD001", 5)) return FS_ISO9660;
     return FS_UNKNOWN;
 }
 
 
 /**************************************************************************
+ *                        UDF_Find_PVD
+ * Find the Primary Volume Descriptor
+ */
+static BOOL UDF_Find_PVD( HANDLE handle, BYTE pvd[] )
+{
+    int i;
+    DWORD offset;
+    INT locations[] = { 256, -1, -257, 512 };
+
+    for(i=0; i<sizeof(locations)/sizeof(locations[0]); i++)
+    {
+        if (!VOLUME_ReadCDBlock(handle, pvd, locations[i]*BLOCK_SIZE))
+            return FALSE;
+
+        /* Tag Identifier of Anchor Volume Descriptor Pointer is 2 -- [E] 3/10.2.1 */
+        if (pvd[0]==2 && pvd[1]==0)
+        {
+            /* Tag location (Uint32) at offset 12, little-endian */
+            offset  = pvd[20 + 0];
+            offset |= pvd[20 + 1] << 8;
+            offset |= pvd[20 + 2] << 16;
+            offset |= pvd[20 + 3] << 24;
+            offset *= BLOCK_SIZE;
+
+            if (!VOLUME_ReadCDBlock(handle, pvd, offset))
+                return FALSE;
+
+            /* Check for the Primary Volume Descriptor Tag Id -- [E] 3/10.1.1 */
+            if (pvd[0]!=1 || pvd[1]!=0)
+                return FALSE;
+
+            /* 8 or 16 bits per character -- [U] 2.1.1 */
+            if (!(pvd[24]==8 || pvd[24]==16))
+                return FALSE;
+
+            return TRUE;
+        }
+    }
+
+    return FALSE;
+}
+
+
+/**************************************************************************
  *                              VOLUME_GetSuperblockLabel
  */
-static void VOLUME_GetSuperblockLabel( const UNICODE_STRING *device, enum fs_type type,
-                                       const BYTE *superblock, WCHAR *label, DWORD len )
+static void VOLUME_GetSuperblockLabel( const UNICODE_STRING *device, HANDLE handle,
+                                       enum fs_type type, const BYTE *superblock,
+                                       WCHAR *label, DWORD len )
 {
     const BYTE *label_ptr = NULL;
     DWORD label_len;
@@ -451,6 +529,34 @@ static void VOLUME_GetSuperblockLabel( const UNICODE_STRING *device, enum fs_typ
             label_len = 32;
             break;
         }
+    case FS_UDF:
+        {
+            BYTE pvd[BLOCK_SIZE];
+
+            if(!UDF_Find_PVD(handle, pvd))
+            {
+                label_len = 0;
+                break;
+            }
+
+            /* [E] 3/10.1.4 and [U] 2.1.1 */
+            if(pvd[24]==8)
+            {
+                label_ptr = pvd + 24 + 1;
+                label_len = pvd[24+32-1];
+                break;
+            }
+            else
+            {
+                int i;
+
+                label_len = 1 + pvd[24+32-1];
+                for(i=0; i<label_len && i<len; i+=2)
+                    label[i/2]  = (pvd[24+1 +i] << 8) | pvd[24+1 +i+1];
+                label[label_len] = 0;
+                return;
+            }
+        }
     }
     if (label_len) RtlMultiByteToUnicodeN( label, (len-1) * sizeof(WCHAR),
                                            &label_len, (LPCSTR)label_ptr, label_len );
@@ -463,8 +569,8 @@ static void VOLUME_GetSuperblockLabel( const UNICODE_STRING *device, enum fs_typ
 /**************************************************************************
  *                              VOLUME_GetSuperblockSerial
  */
-static DWORD VOLUME_GetSuperblockSerial( const UNICODE_STRING *device, enum fs_type type,
-                                         const BYTE *superblock )
+static DWORD VOLUME_GetSuperblockSerial( const UNICODE_STRING *device, HANDLE handle,
+                                         enum fs_type type, const BYTE *superblock )
 {
     switch(type)
     {
@@ -476,6 +582,17 @@ static DWORD VOLUME_GetSuperblockSerial( const UNICODE_STRING *device, enum fs_t
         return GETLONG( superblock, 0x27 );
     case FS_FAT32:
         return GETLONG( superblock, 0x33 );
+    case FS_UDF:
+        {
+            BYTE block[BLOCK_SIZE];
+
+            if (!VOLUME_ReadCDBlock(handle, block, 257*BLOCK_SIZE))
+                break;
+
+            superblock = block;
+
+            /* fallthrough */
+        }
     case FS_ISO9660:
         {
             BYTE sum[4];
@@ -495,7 +612,7 @@ static DWORD VOLUME_GetSuperblockSerial( const UNICODE_STRING *device, enum fs_t
              * Me$$ysoft chose to reverse the serial number in NT4/W2K.
              * It's true and nobody will ever be able to change it.
              */
-            if (GetVersion() & 0x80000000)
+            if (GetVersion() & 0x80000000 || type == FS_UDF)
                 return (sum[3] << 24) | (sum[2] << 16) | (sum[1] << 8) | sum[0];
             else
                 return (sum[0] << 24) | (sum[1] << 16) | (sum[2] << 8) | sum[3];
@@ -546,6 +663,7 @@ BOOL WINAPI GetVolumeInformationW( LPCWSTR root, LPWSTR label, DWORD label_len,
     static const WCHAR fat32W[] = {'F','A','T','3','2',0};
     static const WCHAR ntfsW[] = {'N','T','F','S',0};
     static const WCHAR cdfsW[] = {'C','D','F','S',0};
+    static const WCHAR udfW[] = {'U','D','F',0};
     static const WCHAR default_rootW[] = {'\\',0};
 
     HANDLE handle;
@@ -618,12 +736,16 @@ BOOL WINAPI GetVolumeInformationW( LPCWSTR root, LPWSTR label, DWORD label_len,
             type = VOLUME_ReadFATSuperblock( handle, superblock );
             if (type == FS_UNKNOWN) type = VOLUME_ReadCDSuperblock( handle, superblock );
         }
-        CloseHandle( handle );
         TRACE( "%s: found fs type %d\n", debugstr_w(nt_name.Buffer), type );
-        if (type == FS_ERROR) goto done;
+        if (type == FS_ERROR)
+        {
+            CloseHandle( handle );
+            goto done;
+        }
 
-        if (label && label_len) VOLUME_GetSuperblockLabel( &nt_name, type, superblock, label, label_len );
-        if (serial) *serial = VOLUME_GetSuperblockSerial( &nt_name, type, superblock );
+        if (label && label_len) VOLUME_GetSuperblockLabel( &nt_name, handle, type, superblock, label, label_len );
+        if (serial) *serial = VOLUME_GetSuperblockSerial( &nt_name, handle, type, superblock );
+        CloseHandle( handle );
         goto fill_fs_info;
     }
     else TRACE( "cannot open device %s: %x\n", debugstr_w(nt_name.Buffer), status );
@@ -657,6 +779,12 @@ fill_fs_info:  /* now fill in the information that depends on the file system ty
         if (filename_len) *filename_len = 221;
         if (flags) *flags = FILE_READ_ONLY_VOLUME;
         break;
+    case FS_UDF:
+        if (fsname) lstrcpynW( fsname, udfW, fsname_len );
+        if (filename_len) *filename_len = 255;
+        if (flags)
+            *flags = FILE_READ_ONLY_VOLUME | FILE_UNICODE_ON_DISK | FILE_CASE_SENSITIVE_SEARCH;
+        break;
     case FS_FAT1216:
         if (fsname) lstrcpynW( fsname, fatW, fsname_len );
     case FS_FAT32:
-- 
1.7.4.1




More information about the wine-patches mailing list