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