2005-04-17 02:20:36 +04:00
/*
* misc . c
*
* PURPOSE
* Miscellaneous routines for the OSTA - UDF ( tm ) filesystem .
*
* COPYRIGHT
* This file is distributed under the terms of the GNU General Public
* License ( GPL ) . Copies of the GPL can be obtained from :
* ftp : //prep.ai.mit.edu/pub/gnu/GPL
* Each contributing author retains all rights to their own work .
*
* ( C ) 1998 Dave Boynton
* ( C ) 1998 - 2004 Ben Fennema
* ( C ) 1999 - 2000 Stelias Computing Inc
*
* HISTORY
*
* 04 / 19 / 99 blf partial support for reading / writing specific EA ' s
*/
# include "udfdecl.h"
# include <linux/fs.h>
# include <linux/string.h>
# include <linux/buffer_head.h>
# include "udf_i.h"
# include "udf_sb.h"
2007-07-19 12:47:43 +04:00
struct buffer_head * udf_tgetblk ( struct super_block * sb , int block )
2005-04-17 02:20:36 +04:00
{
if ( UDF_QUERY_FLAG ( sb , UDF_FLAG_VARCONV ) )
return sb_getblk ( sb , udf_fixed_to_variable ( block ) ) ;
else
return sb_getblk ( sb , block ) ;
}
2007-07-19 12:47:43 +04:00
struct buffer_head * udf_tread ( struct super_block * sb , int block )
2005-04-17 02:20:36 +04:00
{
if ( UDF_QUERY_FLAG ( sb , UDF_FLAG_VARCONV ) )
return sb_bread ( sb , udf_fixed_to_variable ( block ) ) ;
else
return sb_bread ( sb , block ) ;
}
2007-07-19 12:47:43 +04:00
struct genericFormat * udf_add_extendedattr ( struct inode * inode , uint32_t size ,
uint32_t type , uint8_t loc )
2005-04-17 02:20:36 +04:00
{
uint8_t * ea = NULL , * ad = NULL ;
int offset ;
uint16_t crclen ;
2008-02-08 15:20:44 +03:00
struct udf_inode_info * iinfo = UDF_I ( inode ) ;
2005-04-17 02:20:36 +04:00
2008-02-08 15:20:44 +03:00
ea = iinfo - > i_ext . i_data ;
if ( iinfo - > i_lenEAttr ) {
ad = iinfo - > i_ext . i_data + iinfo - > i_lenEAttr ;
2007-07-21 15:37:18 +04:00
} else {
2005-04-17 02:20:36 +04:00
ad = ea ;
size + = sizeof ( struct extendedAttrHeaderDesc ) ;
}
offset = inode - > i_sb - > s_blocksize - udf_file_entry_alloc_offset ( inode ) -
2008-02-08 15:20:44 +03:00
iinfo - > i_lenAlloc ;
2005-04-17 02:20:36 +04:00
/* TODO - Check for FreeEASpace */
2007-07-19 12:47:43 +04:00
if ( loc & 0x01 & & offset > = size ) {
2005-04-17 02:20:36 +04:00
struct extendedAttrHeaderDesc * eahd ;
eahd = ( struct extendedAttrHeaderDesc * ) ea ;
2008-02-08 15:20:44 +03:00
if ( iinfo - > i_lenAlloc )
memmove ( & ad [ size ] , ad , iinfo - > i_lenAlloc ) ;
2005-04-17 02:20:36 +04:00
2008-02-08 15:20:44 +03:00
if ( iinfo - > i_lenEAttr ) {
2005-04-17 02:20:36 +04:00
/* check checksum/crc */
2008-02-08 15:20:41 +03:00
if ( eahd - > descTag . tagIdent ! =
cpu_to_le16 ( TAG_IDENT_EAHD ) | |
2008-02-08 15:20:36 +03:00
le32_to_cpu ( eahd - > descTag . tagLocation ) ! =
2008-02-08 15:20:44 +03:00
iinfo - > i_location . logicalBlockNum )
2005-04-17 02:20:36 +04:00
return NULL ;
2007-07-19 12:47:43 +04:00
} else {
2008-02-08 15:20:30 +03:00
struct udf_sb_info * sbi = UDF_SB ( inode - > i_sb ) ;
2005-04-17 02:20:36 +04:00
size - = sizeof ( struct extendedAttrHeaderDesc ) ;
2008-02-08 15:20:44 +03:00
iinfo - > i_lenEAttr + =
2008-02-08 15:20:36 +03:00
sizeof ( struct extendedAttrHeaderDesc ) ;
2005-04-17 02:20:36 +04:00
eahd - > descTag . tagIdent = cpu_to_le16 ( TAG_IDENT_EAHD ) ;
2008-02-08 15:20:30 +03:00
if ( sbi - > s_udfrev > = 0x0200 )
2005-04-17 02:20:36 +04:00
eahd - > descTag . descVersion = cpu_to_le16 ( 3 ) ;
else
eahd - > descTag . descVersion = cpu_to_le16 ( 2 ) ;
2008-02-08 15:20:36 +03:00
eahd - > descTag . tagSerialNum =
cpu_to_le16 ( sbi - > s_serial_number ) ;
eahd - > descTag . tagLocation = cpu_to_le32 (
2008-02-08 15:20:44 +03:00
iinfo - > i_location . logicalBlockNum ) ;
2005-04-17 02:20:36 +04:00
eahd - > impAttrLocation = cpu_to_le32 ( 0xFFFFFFFF ) ;
eahd - > appAttrLocation = cpu_to_le32 ( 0xFFFFFFFF ) ;
}
2008-02-08 15:20:44 +03:00
offset = iinfo - > i_lenEAttr ;
2007-07-19 12:47:43 +04:00
if ( type < 2048 ) {
2008-02-08 15:20:36 +03:00
if ( le32_to_cpu ( eahd - > appAttrLocation ) <
2008-02-08 15:20:44 +03:00
iinfo - > i_lenEAttr ) {
2008-02-08 15:20:36 +03:00
uint32_t aal =
le32_to_cpu ( eahd - > appAttrLocation ) ;
2007-07-21 15:37:18 +04:00
memmove ( & ea [ offset - aal + size ] ,
& ea [ aal ] , offset - aal ) ;
2005-04-17 02:20:36 +04:00
offset - = aal ;
2008-02-08 15:20:36 +03:00
eahd - > appAttrLocation =
cpu_to_le32 ( aal + size ) ;
2005-04-17 02:20:36 +04:00
}
2008-02-08 15:20:36 +03:00
if ( le32_to_cpu ( eahd - > impAttrLocation ) <
2008-02-08 15:20:44 +03:00
iinfo - > i_lenEAttr ) {
2008-02-08 15:20:36 +03:00
uint32_t ial =
le32_to_cpu ( eahd - > impAttrLocation ) ;
2007-07-21 15:37:18 +04:00
memmove ( & ea [ offset - ial + size ] ,
& ea [ ial ] , offset - ial ) ;
2005-04-17 02:20:36 +04:00
offset - = ial ;
2008-02-08 15:20:36 +03:00
eahd - > impAttrLocation =
cpu_to_le32 ( ial + size ) ;
2005-04-17 02:20:36 +04:00
}
2007-07-19 12:47:43 +04:00
} else if ( type < 65536 ) {
2008-02-08 15:20:36 +03:00
if ( le32_to_cpu ( eahd - > appAttrLocation ) <
2008-02-08 15:20:44 +03:00
iinfo - > i_lenEAttr ) {
2008-02-08 15:20:36 +03:00
uint32_t aal =
le32_to_cpu ( eahd - > appAttrLocation ) ;
2007-07-21 15:37:18 +04:00
memmove ( & ea [ offset - aal + size ] ,
& ea [ aal ] , offset - aal ) ;
2005-04-17 02:20:36 +04:00
offset - = aal ;
2008-02-08 15:20:36 +03:00
eahd - > appAttrLocation =
cpu_to_le32 ( aal + size ) ;
2005-04-17 02:20:36 +04:00
}
}
/* rewrite CRC + checksum of eahd */
crclen = sizeof ( struct extendedAttrHeaderDesc ) - sizeof ( tag ) ;
eahd - > descTag . descCRCLength = cpu_to_le16 ( crclen ) ;
2007-07-21 15:37:18 +04:00
eahd - > descTag . descCRC = cpu_to_le16 ( udf_crc ( ( char * ) eahd +
2008-02-08 15:20:36 +03:00
sizeof ( tag ) , crclen , 0 ) ) ;
2008-02-08 15:20:39 +03:00
eahd - > descTag . tagChecksum = udf_tag_checksum ( & eahd - > descTag ) ;
2008-02-08 15:20:44 +03:00
iinfo - > i_lenEAttr + = size ;
2005-04-17 02:20:36 +04:00
return ( struct genericFormat * ) & ea [ offset ] ;
}
2008-02-08 15:20:36 +03:00
if ( loc & 0x02 )
;
2007-07-21 15:37:18 +04:00
2005-04-17 02:20:36 +04:00
return NULL ;
}
2007-07-19 12:47:43 +04:00
struct genericFormat * udf_get_extendedattr ( struct inode * inode , uint32_t type ,
uint8_t subtype )
2005-04-17 02:20:36 +04:00
{
struct genericFormat * gaf ;
uint8_t * ea = NULL ;
uint32_t offset ;
2008-02-08 15:20:44 +03:00
struct udf_inode_info * iinfo = UDF_I ( inode ) ;
2005-04-17 02:20:36 +04:00
2008-02-08 15:20:44 +03:00
ea = iinfo - > i_ext . i_data ;
2005-04-17 02:20:36 +04:00
2008-02-08 15:20:44 +03:00
if ( iinfo - > i_lenEAttr ) {
2005-04-17 02:20:36 +04:00
struct extendedAttrHeaderDesc * eahd ;
eahd = ( struct extendedAttrHeaderDesc * ) ea ;
/* check checksum/crc */
2008-02-08 15:20:41 +03:00
if ( eahd - > descTag . tagIdent ! =
cpu_to_le16 ( TAG_IDENT_EAHD ) | |
2008-02-08 15:20:36 +03:00
le32_to_cpu ( eahd - > descTag . tagLocation ) ! =
2008-02-08 15:20:44 +03:00
iinfo - > i_location . logicalBlockNum )
2005-04-17 02:20:36 +04:00
return NULL ;
2007-07-19 12:47:43 +04:00
2005-04-17 02:20:36 +04:00
if ( type < 2048 )
offset = sizeof ( struct extendedAttrHeaderDesc ) ;
else if ( type < 65536 )
offset = le32_to_cpu ( eahd - > impAttrLocation ) ;
else
offset = le32_to_cpu ( eahd - > appAttrLocation ) ;
2008-02-08 15:20:44 +03:00
while ( offset < iinfo - > i_lenEAttr ) {
2005-04-17 02:20:36 +04:00
gaf = ( struct genericFormat * ) & ea [ offset ] ;
2008-02-08 15:20:36 +03:00
if ( le32_to_cpu ( gaf - > attrType ) = = type & &
gaf - > attrSubtype = = subtype )
2005-04-17 02:20:36 +04:00
return gaf ;
else
offset + = le32_to_cpu ( gaf - > attrLength ) ;
}
}
2007-07-21 15:37:18 +04:00
2005-04-17 02:20:36 +04:00
return NULL ;
}
/*
* udf_read_tagged
*
* PURPOSE
* Read the first block of a tagged descriptor .
*
* HISTORY
* July 1 , 1997 - Andrew E . Mileski
* Written , tested , and released .
*/
2007-07-19 12:47:43 +04:00
struct buffer_head * udf_read_tagged ( struct super_block * sb , uint32_t block ,
2008-02-08 15:20:36 +03:00
uint32_t location , uint16_t * ident )
2005-04-17 02:20:36 +04:00
{
tag * tag_p ;
struct buffer_head * bh = NULL ;
2008-02-08 15:20:30 +03:00
struct udf_sb_info * sbi = UDF_SB ( sb ) ;
2005-04-17 02:20:36 +04:00
/* Read the block */
if ( block = = 0xFFFFFFFF )
return NULL ;
2008-02-08 15:20:30 +03:00
bh = udf_tread ( sb , block + sbi - > s_session ) ;
2007-07-19 12:47:43 +04:00
if ( ! bh ) {
udf_debug ( " block=%d, location=%d: read failed \n " ,
2008-02-08 15:20:30 +03:00
block + sbi - > s_session , location ) ;
2005-04-17 02:20:36 +04:00
return NULL ;
}
2007-07-21 15:37:18 +04:00
tag_p = ( tag * ) ( bh - > b_data ) ;
2005-04-17 02:20:36 +04:00
* ident = le16_to_cpu ( tag_p - > tagIdent ) ;
2007-07-19 12:47:43 +04:00
if ( location ! = le32_to_cpu ( tag_p - > tagLocation ) ) {
2005-04-17 02:20:36 +04:00
udf_debug ( " location mismatch block %u, tag %u != %u \n " ,
2008-02-08 15:20:36 +03:00
block + sbi - > s_session ,
le32_to_cpu ( tag_p - > tagLocation ) , location ) ;
2005-04-17 02:20:36 +04:00
goto error_out ;
}
2007-07-19 12:47:43 +04:00
2005-04-17 02:20:36 +04:00
/* Verify the tag checksum */
2008-02-08 15:20:39 +03:00
if ( udf_tag_checksum ( tag_p ) ! = tag_p - > tagChecksum ) {
2005-04-17 02:20:36 +04:00
printk ( KERN_ERR " udf: tag checksum failed block %d \n " , block ) ;
goto error_out ;
}
/* Verify the tag version */
2008-02-08 15:20:41 +03:00
if ( tag_p - > descVersion ! = cpu_to_le16 ( 0x0002U ) & &
tag_p - > descVersion ! = cpu_to_le16 ( 0x0003U ) ) {
2005-04-17 02:20:36 +04:00
udf_debug ( " tag version 0x%04x != 0x0002 || 0x0003 block %d \n " ,
2007-07-19 12:47:43 +04:00
le16_to_cpu ( tag_p - > descVersion ) , block ) ;
2005-04-17 02:20:36 +04:00
goto error_out ;
}
/* Verify the descriptor CRC */
if ( le16_to_cpu ( tag_p - > descCRCLength ) + sizeof ( tag ) > sb - > s_blocksize | |
2007-07-19 12:47:43 +04:00
le16_to_cpu ( tag_p - > descCRC ) = = udf_crc ( bh - > b_data + sizeof ( tag ) ,
2008-02-08 15:20:36 +03:00
le16_to_cpu ( tag_p - > descCRCLength ) , 0 ) )
2005-04-17 02:20:36 +04:00
return bh ;
2008-02-08 15:20:36 +03:00
2005-04-17 02:20:36 +04:00
udf_debug ( " Crc failure block %d: crc = %d, crclen = %d \n " ,
2008-02-08 15:20:30 +03:00
block + sbi - > s_session , le16_to_cpu ( tag_p - > descCRC ) ,
2007-07-19 12:47:43 +04:00
le16_to_cpu ( tag_p - > descCRCLength ) ) ;
2005-04-17 02:20:36 +04:00
2007-07-21 15:37:18 +04:00
error_out :
2005-04-17 02:20:36 +04:00
brelse ( bh ) ;
return NULL ;
}
2007-07-19 12:47:43 +04:00
struct buffer_head * udf_read_ptagged ( struct super_block * sb , kernel_lb_addr loc ,
2008-02-08 15:20:36 +03:00
uint32_t offset , uint16_t * ident )
2005-04-17 02:20:36 +04:00
{
return udf_read_tagged ( sb , udf_get_lb_pblock ( sb , loc , offset ) ,
2007-07-19 12:47:43 +04:00
loc . logicalBlockNum + offset , ident ) ;
2005-04-17 02:20:36 +04:00
}
void udf_update_tag ( char * data , int length )
{
2007-07-21 15:37:18 +04:00
tag * tptr = ( tag * ) data ;
2005-04-17 02:20:36 +04:00
length - = sizeof ( tag ) ;
tptr - > descCRCLength = cpu_to_le16 ( length ) ;
tptr - > descCRC = cpu_to_le16 ( udf_crc ( data + sizeof ( tag ) , length , 0 ) ) ;
2008-02-08 15:20:39 +03:00
tptr - > tagChecksum = udf_tag_checksum ( tptr ) ;
2005-04-17 02:20:36 +04:00
}
void udf_new_tag ( char * data , uint16_t ident , uint16_t version , uint16_t snum ,
2007-07-19 12:47:43 +04:00
uint32_t loc , int length )
2005-04-17 02:20:36 +04:00
{
2007-07-21 15:37:18 +04:00
tag * tptr = ( tag * ) data ;
2005-04-17 02:20:36 +04:00
tptr - > tagIdent = cpu_to_le16 ( ident ) ;
tptr - > descVersion = cpu_to_le16 ( version ) ;
tptr - > tagSerialNum = cpu_to_le16 ( snum ) ;
tptr - > tagLocation = cpu_to_le32 ( loc ) ;
udf_update_tag ( data , length ) ;
}
2008-02-08 15:20:39 +03:00
u8 udf_tag_checksum ( const tag * t )
{
u8 * data = ( u8 * ) t ;
u8 checksum = 0 ;
int i ;
for ( i = 0 ; i < sizeof ( tag ) ; + + i )
if ( i ! = 4 ) /* position of checksum */
checksum + = data [ i ] ;
return checksum ;
}