kernel32: Add UDF support.

Based on Steven Wallace work.
This commit is contained in:
Alexandre Goujon 2011-09-01 16:30:15 +02:00 committed by Alexandre Julliard
parent b0dfa932b6
commit 73f4e23938
1 changed files with 144 additions and 16 deletions

View File

@ -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 */
@ -386,31 +388,107 @@ 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: