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>
2008-04-17 11:47:48 +04:00
# include <linux/crc-itu-t.h>
2005-04-17 02:20:36 +04:00
# 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 */
2008-10-15 14:28:03 +04:00
crclen = sizeof ( struct extendedAttrHeaderDesc ) - sizeof ( struct tag ) ;
2005-04-17 02:20:36 +04:00
eahd - > descTag . descCRCLength = cpu_to_le16 ( crclen ) ;
2008-04-17 11:47:48 +04:00
eahd - > descTag . descCRC = cpu_to_le16 ( crc_itu_t ( 0 , ( char * ) eahd +
2008-10-15 14:28:03 +04:00
sizeof ( struct tag ) , crclen ) ) ;
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
{
2008-10-15 14:28:03 +04:00
struct tag * tag_p ;
2005-04-17 02:20:36 +04:00
struct buffer_head * bh = NULL ;
2011-10-10 12:08:02 +04:00
u8 checksum ;
2005-04-17 02:20:36 +04:00
/* Read the block */
if ( block = = 0xFFFFFFFF )
return NULL ;
2008-04-08 16:02:11 +04:00
bh = udf_tread ( sb , block ) ;
2007-07-19 12:47:43 +04:00
if ( ! bh ) {
2011-10-10 12:08:03 +04:00
udf_err ( sb , " read failed, block=%u, location=%d \n " ,
block , location ) ;
2005-04-17 02:20:36 +04:00
return NULL ;
}
2008-10-15 14:28:03 +04:00
tag_p = ( struct 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-04-08 16:02:11 +04:00
block , 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 */
2011-10-10 12:08:02 +04:00
checksum = udf_tag_checksum ( tag_p ) ;
if ( checksum ! = tag_p - > tagChecksum ) {
2011-10-10 12:08:03 +04:00
udf_err ( sb , " tag checksum failed, block %u: 0x%02x != 0x%02x \n " ,
block , checksum , tag_p - > tagChecksum ) ;
2005-04-17 02:20:36 +04:00
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 ) ) {
2011-10-10 12:08:03 +04:00
udf_err ( sb , " tag version 0x%04x != 0x0002 || 0x0003, block %u \n " ,
le16_to_cpu ( tag_p - > descVersion ) , block ) ;
2005-04-17 02:20:36 +04:00
goto error_out ;
}
/* Verify the descriptor CRC */
2008-10-15 14:28:03 +04:00
if ( le16_to_cpu ( tag_p - > descCRCLength ) + sizeof ( struct tag ) > sb - > s_blocksize | |
2008-04-17 11:47:48 +04:00
le16_to_cpu ( tag_p - > descCRC ) = = crc_itu_t ( 0 ,
2008-10-15 14:28:03 +04:00
bh - > b_data + sizeof ( struct tag ) ,
2008-04-17 11:47:48 +04:00
le16_to_cpu ( tag_p - > descCRCLength ) ) )
2005-04-17 02:20:36 +04:00
return bh ;
2008-02-08 15:20:36 +03:00
2008-04-08 16:02:11 +04:00
udf_debug ( " Crc failure block %d: crc = %d, crclen = %d \n " , block ,
2011-10-10 12:08:03 +04:00
le16_to_cpu ( tag_p - > descCRC ) ,
le16_to_cpu ( tag_p - > descCRCLength ) ) ;
2007-07-21 15:37:18 +04:00
error_out :
2005-04-17 02:20:36 +04:00
brelse ( bh ) ;
return NULL ;
}
2008-10-15 14:29:03 +04:00
struct buffer_head * udf_read_ptagged ( struct super_block * sb ,
struct 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 ) ,
2008-10-15 14:29:03 +04:00
loc - > logicalBlockNum + offset , ident ) ;
2005-04-17 02:20:36 +04:00
}
void udf_update_tag ( char * data , int length )
{
2008-10-15 14:28:03 +04:00
struct tag * tptr = ( struct tag * ) data ;
length - = sizeof ( struct tag ) ;
2005-04-17 02:20:36 +04:00
tptr - > descCRCLength = cpu_to_le16 ( length ) ;
2008-10-15 14:28:03 +04:00
tptr - > descCRC = cpu_to_le16 ( crc_itu_t ( 0 , data + sizeof ( struct tag ) , length ) ) ;
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
{
2008-10-15 14:28:03 +04:00
struct tag * tptr = ( struct 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
2008-10-15 14:28:03 +04:00
u8 udf_tag_checksum ( const struct tag * t )
2008-02-08 15:20:39 +03:00
{
u8 * data = ( u8 * ) t ;
u8 checksum = 0 ;
int i ;
2008-10-15 14:28:03 +04:00
for ( i = 0 ; i < sizeof ( struct tag ) ; + + i )
2008-02-08 15:20:39 +03:00
if ( i ! = 4 ) /* position of checksum */
checksum + = data [ i ] ;
return checksum ;
}