2005-04-16 15:20:36 -07:00
/*
* inode . c
*
* PURPOSE
* Inode handling routines for the OSTA - UDF ( tm ) filesystem .
*
* CONTACTS
* E - mail regarding any portion of the Linux UDF file system should be
* directed to the development team mailing list ( run by majordomo ) :
* linux_udf @ hpesjro . fc . hp . com
*
* 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
*
* 10 / 04 / 98 dgb Added rudimentary directory functions
* 10 / 07 / 98 Fully working udf_block_map ! It works !
* 11 / 25 / 98 bmap altered to better support extents
* 12 / 06 / 98 blf partition support in udf_iget , udf_block_map and udf_read_inode
* 12 / 12 / 98 rewrote udf_block_map to handle next extents and descs across
* block boundaries ( which is not actually allowed )
* 12 / 20 / 98 added support for strategy 4096
* 03 / 07 / 99 rewrote udf_block_map ( again )
* New funcs , inode_bmap , udf_next_aext
* 04 / 19 / 99 Support for writing device EA ' s for major / minor #
*/
# include "udfdecl.h"
# include <linux/mm.h>
# include <linux/smp_lock.h>
# include <linux/module.h>
# include <linux/pagemap.h>
# include <linux/buffer_head.h>
# include <linux/writeback.h>
# include <linux/slab.h>
# include "udf_i.h"
# include "udf_sb.h"
MODULE_AUTHOR ( " Ben Fennema " ) ;
MODULE_DESCRIPTION ( " Universal Disk Format Filesystem " ) ;
MODULE_LICENSE ( " GPL " ) ;
# define EXTENT_MERGE_SIZE 5
static mode_t udf_convert_permissions ( struct fileEntry * ) ;
static int udf_update_inode ( struct inode * , int ) ;
static void udf_fill_inode ( struct inode * , struct buffer_head * ) ;
static struct buffer_head * inode_getblk ( struct inode * , long , int * ,
long * , int * ) ;
static int8_t udf_insert_aext ( struct inode * , kernel_lb_addr , int ,
kernel_lb_addr , uint32_t , struct buffer_head * ) ;
static void udf_split_extents ( struct inode * , int * , int , int ,
kernel_long_ad [ EXTENT_MERGE_SIZE ] , int * ) ;
static void udf_prealloc_extents ( struct inode * , int , int ,
kernel_long_ad [ EXTENT_MERGE_SIZE ] , int * ) ;
static void udf_merge_extents ( struct inode * ,
kernel_long_ad [ EXTENT_MERGE_SIZE ] , int * ) ;
static void udf_update_extents ( struct inode * ,
kernel_long_ad [ EXTENT_MERGE_SIZE ] , int , int ,
kernel_lb_addr , uint32_t , struct buffer_head * * ) ;
static int udf_get_block ( struct inode * , sector_t , struct buffer_head * , int ) ;
/*
* udf_delete_inode
*
* PURPOSE
* Clean - up before the specified inode is destroyed .
*
* DESCRIPTION
* This routine is called when the kernel destroys an inode structure
* ie . when iput ( ) finds i_count = = 0.
*
* HISTORY
* July 1 , 1997 - Andrew E . Mileski
* Written , tested , and released .
*
* Called at the last iput ( ) if i_nlink is zero .
*/
void udf_delete_inode ( struct inode * inode )
{
if ( is_bad_inode ( inode ) )
goto no_delete ;
inode - > i_size = 0 ;
udf_truncate ( inode ) ;
lock_kernel ( ) ;
udf_update_inode ( inode , IS_SYNC ( inode ) ) ;
udf_free_inode ( inode ) ;
unlock_kernel ( ) ;
return ;
no_delete :
clear_inode ( inode ) ;
}
void udf_clear_inode ( struct inode * inode )
{
if ( ! ( inode - > i_sb - > s_flags & MS_RDONLY ) ) {
lock_kernel ( ) ;
udf_discard_prealloc ( inode ) ;
unlock_kernel ( ) ;
}
kfree ( UDF_I_DATA ( inode ) ) ;
UDF_I_DATA ( inode ) = NULL ;
}
static int udf_writepage ( struct page * page , struct writeback_control * wbc )
{
return block_write_full_page ( page , udf_get_block , wbc ) ;
}
static int udf_readpage ( struct file * file , struct page * page )
{
return block_read_full_page ( page , udf_get_block ) ;
}
static int udf_prepare_write ( struct file * file , struct page * page , unsigned from , unsigned to )
{
return block_prepare_write ( page , from , to , udf_get_block ) ;
}
static sector_t udf_bmap ( struct address_space * mapping , sector_t block )
{
return generic_block_bmap ( mapping , block , udf_get_block ) ;
}
struct address_space_operations udf_aops = {
. readpage = udf_readpage ,
. writepage = udf_writepage ,
. sync_page = block_sync_page ,
. prepare_write = udf_prepare_write ,
. commit_write = generic_commit_write ,
. bmap = udf_bmap ,
} ;
void udf_expand_file_adinicb ( struct inode * inode , int newsize , int * err )
{
struct page * page ;
char * kaddr ;
struct writeback_control udf_wbc = {
. sync_mode = WB_SYNC_NONE ,
. nr_to_write = 1 ,
} ;
/* from now on we have normal address_space methods */
inode - > i_data . a_ops = & udf_aops ;
if ( ! UDF_I_LENALLOC ( inode ) )
{
if ( UDF_QUERY_FLAG ( inode - > i_sb , UDF_FLAG_USE_SHORT_AD ) )
UDF_I_ALLOCTYPE ( inode ) = ICBTAG_FLAG_AD_SHORT ;
else
UDF_I_ALLOCTYPE ( inode ) = ICBTAG_FLAG_AD_LONG ;
mark_inode_dirty ( inode ) ;
return ;
}
page = grab_cache_page ( inode - > i_mapping , 0 ) ;
2005-05-01 08:59:01 -07:00
BUG_ON ( ! PageLocked ( page ) ) ;
2005-04-16 15:20:36 -07:00
if ( ! PageUptodate ( page ) )
{
kaddr = kmap ( page ) ;
memset ( kaddr + UDF_I_LENALLOC ( inode ) , 0x00 ,
PAGE_CACHE_SIZE - UDF_I_LENALLOC ( inode ) ) ;
memcpy ( kaddr , UDF_I_DATA ( inode ) + UDF_I_LENEATTR ( inode ) ,
UDF_I_LENALLOC ( inode ) ) ;
flush_dcache_page ( page ) ;
SetPageUptodate ( page ) ;
kunmap ( page ) ;
}
memset ( UDF_I_DATA ( inode ) + UDF_I_LENEATTR ( inode ) , 0x00 ,
UDF_I_LENALLOC ( inode ) ) ;
UDF_I_LENALLOC ( inode ) = 0 ;
if ( UDF_QUERY_FLAG ( inode - > i_sb , UDF_FLAG_USE_SHORT_AD ) )
UDF_I_ALLOCTYPE ( inode ) = ICBTAG_FLAG_AD_SHORT ;
else
UDF_I_ALLOCTYPE ( inode ) = ICBTAG_FLAG_AD_LONG ;
inode - > i_data . a_ops - > writepage ( page , & udf_wbc ) ;
page_cache_release ( page ) ;
mark_inode_dirty ( inode ) ;
}
struct buffer_head * udf_expand_dir_adinicb ( struct inode * inode , int * block , int * err )
{
int newblock ;
struct buffer_head * sbh = NULL , * dbh = NULL ;
kernel_lb_addr bloc , eloc ;
uint32_t elen , extoffset ;
uint8_t alloctype ;
struct udf_fileident_bh sfibh , dfibh ;
loff_t f_pos = udf_ext0_offset ( inode ) > > 2 ;
int size = ( udf_ext0_offset ( inode ) + inode - > i_size ) > > 2 ;
struct fileIdentDesc cfi , * sfi , * dfi ;
if ( UDF_QUERY_FLAG ( inode - > i_sb , UDF_FLAG_USE_SHORT_AD ) )
alloctype = ICBTAG_FLAG_AD_SHORT ;
else
alloctype = ICBTAG_FLAG_AD_LONG ;
if ( ! inode - > i_size )
{
UDF_I_ALLOCTYPE ( inode ) = alloctype ;
mark_inode_dirty ( inode ) ;
return NULL ;
}
/* alloc block, and copy data to it */
* block = udf_new_block ( inode - > i_sb , inode ,
UDF_I_LOCATION ( inode ) . partitionReferenceNum ,
UDF_I_LOCATION ( inode ) . logicalBlockNum , err ) ;
if ( ! ( * block ) )
return NULL ;
newblock = udf_get_pblock ( inode - > i_sb , * block ,
UDF_I_LOCATION ( inode ) . partitionReferenceNum , 0 ) ;
if ( ! newblock )
return NULL ;
dbh = udf_tgetblk ( inode - > i_sb , newblock ) ;
if ( ! dbh )
return NULL ;
lock_buffer ( dbh ) ;
memset ( dbh - > b_data , 0x00 , inode - > i_sb - > s_blocksize ) ;
set_buffer_uptodate ( dbh ) ;
unlock_buffer ( dbh ) ;
mark_buffer_dirty_inode ( dbh , inode ) ;
sfibh . soffset = sfibh . eoffset = ( f_pos & ( ( inode - > i_sb - > s_blocksize - 1 ) > > 2 ) ) < < 2 ;
sbh = sfibh . sbh = sfibh . ebh = NULL ;
dfibh . soffset = dfibh . eoffset = 0 ;
dfibh . sbh = dfibh . ebh = dbh ;
while ( ( f_pos < size ) )
{
UDF_I_ALLOCTYPE ( inode ) = ICBTAG_FLAG_AD_IN_ICB ;
sfi = udf_fileident_read ( inode , & f_pos , & sfibh , & cfi , NULL , NULL , NULL , NULL , NULL , NULL ) ;
if ( ! sfi )
{
udf_release_data ( dbh ) ;
return NULL ;
}
UDF_I_ALLOCTYPE ( inode ) = alloctype ;
sfi - > descTag . tagLocation = cpu_to_le32 ( * block ) ;
dfibh . soffset = dfibh . eoffset ;
dfibh . eoffset + = ( sfibh . eoffset - sfibh . soffset ) ;
dfi = ( struct fileIdentDesc * ) ( dbh - > b_data + dfibh . soffset ) ;
if ( udf_write_fi ( inode , sfi , dfi , & dfibh , sfi - > impUse ,
sfi - > fileIdent + le16_to_cpu ( sfi - > lengthOfImpUse ) ) )
{
UDF_I_ALLOCTYPE ( inode ) = ICBTAG_FLAG_AD_IN_ICB ;
udf_release_data ( dbh ) ;
return NULL ;
}
}
mark_buffer_dirty_inode ( dbh , inode ) ;
memset ( UDF_I_DATA ( inode ) + UDF_I_LENEATTR ( inode ) , 0 , UDF_I_LENALLOC ( inode ) ) ;
UDF_I_LENALLOC ( inode ) = 0 ;
bloc = UDF_I_LOCATION ( inode ) ;
eloc . logicalBlockNum = * block ;
eloc . partitionReferenceNum = UDF_I_LOCATION ( inode ) . partitionReferenceNum ;
elen = inode - > i_size ;
UDF_I_LENEXTENTS ( inode ) = elen ;
extoffset = udf_file_entry_alloc_offset ( inode ) ;
udf_add_aext ( inode , & bloc , & extoffset , eloc , elen , & sbh , 0 ) ;
/* UniqueID stuff */
udf_release_data ( sbh ) ;
mark_inode_dirty ( inode ) ;
return dbh ;
}
static int udf_get_block ( struct inode * inode , sector_t block , struct buffer_head * bh_result , int create )
{
int err , new ;
struct buffer_head * bh ;
unsigned long phys ;
if ( ! create )
{
phys = udf_block_map ( inode , block ) ;
if ( phys )
map_bh ( bh_result , inode - > i_sb , phys ) ;
return 0 ;
}
err = - EIO ;
new = 0 ;
bh = NULL ;
lock_kernel ( ) ;
if ( block < 0 )
goto abort_negative ;
if ( block = = UDF_I_NEXT_ALLOC_BLOCK ( inode ) + 1 )
{
UDF_I_NEXT_ALLOC_BLOCK ( inode ) + + ;
UDF_I_NEXT_ALLOC_GOAL ( inode ) + + ;
}
err = 0 ;
bh = inode_getblk ( inode , block , & err , & phys , & new ) ;
if ( bh )
BUG ( ) ;
if ( err )
goto abort ;
if ( ! phys )
BUG ( ) ;
if ( new )
set_buffer_new ( bh_result ) ;
map_bh ( bh_result , inode - > i_sb , phys ) ;
abort :
unlock_kernel ( ) ;
return err ;
abort_negative :
udf_warning ( inode - > i_sb , " udf_get_block " , " block < 0 " ) ;
goto abort ;
}
static struct buffer_head *
udf_getblk ( struct inode * inode , long block , int create , int * err )
{
struct buffer_head dummy ;
dummy . b_state = 0 ;
dummy . b_blocknr = - 1000 ;
* err = udf_get_block ( inode , block , & dummy , create ) ;
if ( ! * err & & buffer_mapped ( & dummy ) )
{
struct buffer_head * bh ;
bh = sb_getblk ( inode - > i_sb , dummy . b_blocknr ) ;
if ( buffer_new ( & dummy ) )
{
lock_buffer ( bh ) ;
memset ( bh - > b_data , 0x00 , inode - > i_sb - > s_blocksize ) ;
set_buffer_uptodate ( bh ) ;
unlock_buffer ( bh ) ;
mark_buffer_dirty_inode ( bh , inode ) ;
}
return bh ;
}
return NULL ;
}
static struct buffer_head * inode_getblk ( struct inode * inode , long block ,
int * err , long * phys , int * new )
{
struct buffer_head * pbh = NULL , * cbh = NULL , * nbh = NULL , * result = NULL ;
kernel_long_ad laarr [ EXTENT_MERGE_SIZE ] ;
uint32_t pextoffset = 0 , cextoffset = 0 , nextoffset = 0 ;
int count = 0 , startnum = 0 , endnum = 0 ;
uint32_t elen = 0 ;
kernel_lb_addr eloc , pbloc , cbloc , nbloc ;
int c = 1 ;
uint64_t lbcount = 0 , b_off = 0 ;
uint32_t newblocknum , newblock , offset = 0 ;
int8_t etype ;
int goal = 0 , pgoal = UDF_I_LOCATION ( inode ) . logicalBlockNum ;
char lastblock = 0 ;
pextoffset = cextoffset = nextoffset = udf_file_entry_alloc_offset ( inode ) ;
b_off = ( uint64_t ) block < < inode - > i_sb - > s_blocksize_bits ;
pbloc = cbloc = nbloc = UDF_I_LOCATION ( inode ) ;
/* find the extent which contains the block we are looking for.
alternate between laarr [ 0 ] and laarr [ 1 ] for locations of the
current extent , and the previous extent */
do
{
if ( pbh ! = cbh )
{
udf_release_data ( pbh ) ;
atomic_inc ( & cbh - > b_count ) ;
pbh = cbh ;
}
if ( cbh ! = nbh )
{
udf_release_data ( cbh ) ;
atomic_inc ( & nbh - > b_count ) ;
cbh = nbh ;
}
lbcount + = elen ;
pbloc = cbloc ;
cbloc = nbloc ;
pextoffset = cextoffset ;
cextoffset = nextoffset ;
if ( ( etype = udf_next_aext ( inode , & nbloc , & nextoffset , & eloc , & elen , & nbh , 1 ) ) = = - 1 )
break ;
c = ! c ;
laarr [ c ] . extLength = ( etype < < 30 ) | elen ;
laarr [ c ] . extLocation = eloc ;
if ( etype ! = ( EXT_NOT_RECORDED_NOT_ALLOCATED > > 30 ) )
pgoal = eloc . logicalBlockNum +
( ( elen + inode - > i_sb - > s_blocksize - 1 ) > >
inode - > i_sb - > s_blocksize_bits ) ;
count + + ;
} while ( lbcount + elen < = b_off ) ;
b_off - = lbcount ;
offset = b_off > > inode - > i_sb - > s_blocksize_bits ;
/* if the extent is allocated and recorded, return the block
if the extent is not a multiple of the blocksize , round up */
if ( etype = = ( EXT_RECORDED_ALLOCATED > > 30 ) )
{
if ( elen & ( inode - > i_sb - > s_blocksize - 1 ) )
{
elen = EXT_RECORDED_ALLOCATED |
( ( elen + inode - > i_sb - > s_blocksize - 1 ) &
~ ( inode - > i_sb - > s_blocksize - 1 ) ) ;
etype = udf_write_aext ( inode , nbloc , & cextoffset , eloc , elen , nbh , 1 ) ;
}
udf_release_data ( pbh ) ;
udf_release_data ( cbh ) ;
udf_release_data ( nbh ) ;
newblock = udf_get_lb_pblock ( inode - > i_sb , eloc , offset ) ;
* phys = newblock ;
return NULL ;
}
if ( etype = = - 1 )
{
endnum = startnum = ( ( count > 1 ) ? 1 : count ) ;
if ( laarr [ c ] . extLength & ( inode - > i_sb - > s_blocksize - 1 ) )
{
laarr [ c ] . extLength =
( laarr [ c ] . extLength & UDF_EXTENT_FLAG_MASK ) |
( ( ( laarr [ c ] . extLength & UDF_EXTENT_LENGTH_MASK ) +
inode - > i_sb - > s_blocksize - 1 ) &
~ ( inode - > i_sb - > s_blocksize - 1 ) ) ;
UDF_I_LENEXTENTS ( inode ) =
( UDF_I_LENEXTENTS ( inode ) + inode - > i_sb - > s_blocksize - 1 ) &
~ ( inode - > i_sb - > s_blocksize - 1 ) ;
}
c = ! c ;
laarr [ c ] . extLength = EXT_NOT_RECORDED_NOT_ALLOCATED |
( ( offset + 1 ) < < inode - > i_sb - > s_blocksize_bits ) ;
memset ( & laarr [ c ] . extLocation , 0x00 , sizeof ( kernel_lb_addr ) ) ;
count + + ;
endnum + + ;
lastblock = 1 ;
}
else
endnum = startnum = ( ( count > 2 ) ? 2 : count ) ;
/* if the current extent is in position 0, swap it with the previous */
if ( ! c & & count ! = 1 )
{
laarr [ 2 ] = laarr [ 0 ] ;
laarr [ 0 ] = laarr [ 1 ] ;
laarr [ 1 ] = laarr [ 2 ] ;
c = 1 ;
}
/* if the current block is located in a extent, read the next extent */
if ( etype ! = - 1 )
{
if ( ( etype = udf_next_aext ( inode , & nbloc , & nextoffset , & eloc , & elen , & nbh , 0 ) ) ! = - 1 )
{
laarr [ c + 1 ] . extLength = ( etype < < 30 ) | elen ;
laarr [ c + 1 ] . extLocation = eloc ;
count + + ;
startnum + + ;
endnum + + ;
}
else
lastblock = 1 ;
}
udf_release_data ( cbh ) ;
udf_release_data ( nbh ) ;
/* if the current extent is not recorded but allocated, get the
block in the extent corresponding to the requested block */
if ( ( laarr [ c ] . extLength > > 30 ) = = ( EXT_NOT_RECORDED_ALLOCATED > > 30 ) )
newblocknum = laarr [ c ] . extLocation . logicalBlockNum + offset ;
else /* otherwise, allocate a new block */
{
if ( UDF_I_NEXT_ALLOC_BLOCK ( inode ) = = block )
goal = UDF_I_NEXT_ALLOC_GOAL ( inode ) ;
if ( ! goal )
{
if ( ! ( goal = pgoal ) )
goal = UDF_I_LOCATION ( inode ) . logicalBlockNum + 1 ;
}
if ( ! ( newblocknum = udf_new_block ( inode - > i_sb , inode ,
UDF_I_LOCATION ( inode ) . partitionReferenceNum , goal , err ) ) )
{
udf_release_data ( pbh ) ;
* err = - ENOSPC ;
return NULL ;
}
UDF_I_LENEXTENTS ( inode ) + = inode - > i_sb - > s_blocksize ;
}
/* if the extent the requsted block is located in contains multiple blocks,
split the extent into at most three extents . blocks prior to requested
block , requested block , and blocks after requested block */
udf_split_extents ( inode , & c , offset , newblocknum , laarr , & endnum ) ;
# ifdef UDF_PREALLOCATE
/* preallocate blocks */
udf_prealloc_extents ( inode , c , lastblock , laarr , & endnum ) ;
# endif
/* merge any continuous blocks in laarr */
udf_merge_extents ( inode , laarr , & endnum ) ;
/* write back the new extents, inserting new extents if the new number
of extents is greater than the old number , and deleting extents if
the new number of extents is less than the old number */
udf_update_extents ( inode , laarr , startnum , endnum , pbloc , pextoffset , & pbh ) ;
udf_release_data ( pbh ) ;
if ( ! ( newblock = udf_get_pblock ( inode - > i_sb , newblocknum ,
UDF_I_LOCATION ( inode ) . partitionReferenceNum , 0 ) ) )
{
return NULL ;
}
* phys = newblock ;
* err = 0 ;
* new = 1 ;
UDF_I_NEXT_ALLOC_BLOCK ( inode ) = block ;
UDF_I_NEXT_ALLOC_GOAL ( inode ) = newblocknum ;
inode - > i_ctime = current_fs_time ( inode - > i_sb ) ;
if ( IS_SYNC ( inode ) )
udf_sync_inode ( inode ) ;
else
mark_inode_dirty ( inode ) ;
return result ;
}
static void udf_split_extents ( struct inode * inode , int * c , int offset , int newblocknum ,
kernel_long_ad laarr [ EXTENT_MERGE_SIZE ] , int * endnum )
{
if ( ( laarr [ * c ] . extLength > > 30 ) = = ( EXT_NOT_RECORDED_ALLOCATED > > 30 ) | |
( laarr [ * c ] . extLength > > 30 ) = = ( EXT_NOT_RECORDED_NOT_ALLOCATED > > 30 ) )
{
int curr = * c ;
int blen = ( ( laarr [ curr ] . extLength & UDF_EXTENT_LENGTH_MASK ) +
inode - > i_sb - > s_blocksize - 1 ) > > inode - > i_sb - > s_blocksize_bits ;
int8_t etype = ( laarr [ curr ] . extLength > > 30 ) ;
if ( blen = = 1 )
;
else if ( ! offset | | blen = = offset + 1 )
{
laarr [ curr + 2 ] = laarr [ curr + 1 ] ;
laarr [ curr + 1 ] = laarr [ curr ] ;
}
else
{
laarr [ curr + 3 ] = laarr [ curr + 1 ] ;
laarr [ curr + 2 ] = laarr [ curr + 1 ] = laarr [ curr ] ;
}
if ( offset )
{
if ( etype = = ( EXT_NOT_RECORDED_ALLOCATED > > 30 ) )
{
udf_free_blocks ( inode - > i_sb , inode , laarr [ curr ] . extLocation , 0 , offset ) ;
laarr [ curr ] . extLength = EXT_NOT_RECORDED_NOT_ALLOCATED |
( offset < < inode - > i_sb - > s_blocksize_bits ) ;
laarr [ curr ] . extLocation . logicalBlockNum = 0 ;
laarr [ curr ] . extLocation . partitionReferenceNum = 0 ;
}
else
laarr [ curr ] . extLength = ( etype < < 30 ) |
( offset < < inode - > i_sb - > s_blocksize_bits ) ;
curr + + ;
( * c ) + + ;
( * endnum ) + + ;
}
laarr [ curr ] . extLocation . logicalBlockNum = newblocknum ;
if ( etype = = ( EXT_NOT_RECORDED_NOT_ALLOCATED > > 30 ) )
laarr [ curr ] . extLocation . partitionReferenceNum =
UDF_I_LOCATION ( inode ) . partitionReferenceNum ;
laarr [ curr ] . extLength = EXT_RECORDED_ALLOCATED |
inode - > i_sb - > s_blocksize ;
curr + + ;
if ( blen ! = offset + 1 )
{
if ( etype = = ( EXT_NOT_RECORDED_ALLOCATED > > 30 ) )
laarr [ curr ] . extLocation . logicalBlockNum + = ( offset + 1 ) ;
laarr [ curr ] . extLength = ( etype < < 30 ) |
( ( blen - ( offset + 1 ) ) < < inode - > i_sb - > s_blocksize_bits ) ;
curr + + ;
( * endnum ) + + ;
}
}
}
static void udf_prealloc_extents ( struct inode * inode , int c , int lastblock ,
kernel_long_ad laarr [ EXTENT_MERGE_SIZE ] , int * endnum )
{
int start , length = 0 , currlength = 0 , i ;
if ( * endnum > = ( c + 1 ) )
{
if ( ! lastblock )
return ;
else
start = c ;
}
else
{
if ( ( laarr [ c + 1 ] . extLength > > 30 ) = = ( EXT_NOT_RECORDED_ALLOCATED > > 30 ) )
{
start = c + 1 ;
length = currlength = ( ( ( laarr [ c + 1 ] . extLength & UDF_EXTENT_LENGTH_MASK ) +
inode - > i_sb - > s_blocksize - 1 ) > > inode - > i_sb - > s_blocksize_bits ) ;
}
else
start = c ;
}
for ( i = start + 1 ; i < = * endnum ; i + + )
{
if ( i = = * endnum )
{
if ( lastblock )
length + = UDF_DEFAULT_PREALLOC_BLOCKS ;
}
else if ( ( laarr [ i ] . extLength > > 30 ) = = ( EXT_NOT_RECORDED_NOT_ALLOCATED > > 30 ) )
length + = ( ( ( laarr [ i ] . extLength & UDF_EXTENT_LENGTH_MASK ) +
inode - > i_sb - > s_blocksize - 1 ) > > inode - > i_sb - > s_blocksize_bits ) ;
else
break ;
}
if ( length )
{
int next = laarr [ start ] . extLocation . logicalBlockNum +
( ( ( laarr [ start ] . extLength & UDF_EXTENT_LENGTH_MASK ) +
inode - > i_sb - > s_blocksize - 1 ) > > inode - > i_sb - > s_blocksize_bits ) ;
int numalloc = udf_prealloc_blocks ( inode - > i_sb , inode ,
laarr [ start ] . extLocation . partitionReferenceNum ,
next , ( UDF_DEFAULT_PREALLOC_BLOCKS > length ? length :
UDF_DEFAULT_PREALLOC_BLOCKS ) - currlength ) ;
if ( numalloc )
{
if ( start = = ( c + 1 ) )
laarr [ start ] . extLength + =
( numalloc < < inode - > i_sb - > s_blocksize_bits ) ;
else
{
memmove ( & laarr [ c + 2 ] , & laarr [ c + 1 ] ,
sizeof ( long_ad ) * ( * endnum - ( c + 1 ) ) ) ;
( * endnum ) + + ;
laarr [ c + 1 ] . extLocation . logicalBlockNum = next ;
laarr [ c + 1 ] . extLocation . partitionReferenceNum =
laarr [ c ] . extLocation . partitionReferenceNum ;
laarr [ c + 1 ] . extLength = EXT_NOT_RECORDED_ALLOCATED |
( numalloc < < inode - > i_sb - > s_blocksize_bits ) ;
start = c + 1 ;
}
for ( i = start + 1 ; numalloc & & i < * endnum ; i + + )
{
int elen = ( ( laarr [ i ] . extLength & UDF_EXTENT_LENGTH_MASK ) +
inode - > i_sb - > s_blocksize - 1 ) > > inode - > i_sb - > s_blocksize_bits ;
if ( elen > numalloc )
{
laarr [ i ] . extLength - =
( numalloc < < inode - > i_sb - > s_blocksize_bits ) ;
numalloc = 0 ;
}
else
{
numalloc - = elen ;
if ( * endnum > ( i + 1 ) )
memmove ( & laarr [ i ] , & laarr [ i + 1 ] ,
sizeof ( long_ad ) * ( * endnum - ( i + 1 ) ) ) ;
i - - ;
( * endnum ) - - ;
}
}
UDF_I_LENEXTENTS ( inode ) + = numalloc < < inode - > i_sb - > s_blocksize_bits ;
}
}
}
static void udf_merge_extents ( struct inode * inode ,
kernel_long_ad laarr [ EXTENT_MERGE_SIZE ] , int * endnum )
{
int i ;
for ( i = 0 ; i < ( * endnum - 1 ) ; i + + )
{
if ( ( laarr [ i ] . extLength > > 30 ) = = ( laarr [ i + 1 ] . extLength > > 30 ) )
{
if ( ( ( laarr [ i ] . extLength > > 30 ) = = ( EXT_NOT_RECORDED_NOT_ALLOCATED > > 30 ) ) | |
( ( laarr [ i + 1 ] . extLocation . logicalBlockNum - laarr [ i ] . extLocation . logicalBlockNum ) = =
( ( ( laarr [ i ] . extLength & UDF_EXTENT_LENGTH_MASK ) +
inode - > i_sb - > s_blocksize - 1 ) > > inode - > i_sb - > s_blocksize_bits ) ) )
{
if ( ( ( laarr [ i ] . extLength & UDF_EXTENT_LENGTH_MASK ) +
( laarr [ i + 1 ] . extLength & UDF_EXTENT_LENGTH_MASK ) +
inode - > i_sb - > s_blocksize - 1 ) & ~ UDF_EXTENT_LENGTH_MASK )
{
laarr [ i + 1 ] . extLength = ( laarr [ i + 1 ] . extLength -
( laarr [ i ] . extLength & UDF_EXTENT_LENGTH_MASK ) +
UDF_EXTENT_LENGTH_MASK ) & ~ ( inode - > i_sb - > s_blocksize - 1 ) ;
laarr [ i ] . extLength = ( laarr [ i ] . extLength & UDF_EXTENT_FLAG_MASK ) +
( UDF_EXTENT_LENGTH_MASK + 1 ) - inode - > i_sb - > s_blocksize ;
laarr [ i + 1 ] . extLocation . logicalBlockNum =
laarr [ i ] . extLocation . logicalBlockNum +
( ( laarr [ i ] . extLength & UDF_EXTENT_LENGTH_MASK ) > >
inode - > i_sb - > s_blocksize_bits ) ;
}
else
{
laarr [ i ] . extLength = laarr [ i + 1 ] . extLength +
( ( ( laarr [ i ] . extLength & UDF_EXTENT_LENGTH_MASK ) +
inode - > i_sb - > s_blocksize - 1 ) & ~ ( inode - > i_sb - > s_blocksize - 1 ) ) ;
if ( * endnum > ( i + 2 ) )
memmove ( & laarr [ i + 1 ] , & laarr [ i + 2 ] ,
sizeof ( long_ad ) * ( * endnum - ( i + 2 ) ) ) ;
i - - ;
( * endnum ) - - ;
}
}
}
else if ( ( ( laarr [ i ] . extLength > > 30 ) = = ( EXT_NOT_RECORDED_ALLOCATED > > 30 ) ) & &
( ( laarr [ i + 1 ] . extLength > > 30 ) = = ( EXT_NOT_RECORDED_NOT_ALLOCATED > > 30 ) ) )
{
udf_free_blocks ( inode - > i_sb , inode , laarr [ i ] . extLocation , 0 ,
( ( laarr [ i ] . extLength & UDF_EXTENT_LENGTH_MASK ) +
inode - > i_sb - > s_blocksize - 1 ) > > inode - > i_sb - > s_blocksize_bits ) ;
laarr [ i ] . extLocation . logicalBlockNum = 0 ;
laarr [ i ] . extLocation . partitionReferenceNum = 0 ;
if ( ( ( laarr [ i ] . extLength & UDF_EXTENT_LENGTH_MASK ) +
( laarr [ i + 1 ] . extLength & UDF_EXTENT_LENGTH_MASK ) +
inode - > i_sb - > s_blocksize - 1 ) & ~ UDF_EXTENT_LENGTH_MASK )
{
laarr [ i + 1 ] . extLength = ( laarr [ i + 1 ] . extLength -
( laarr [ i ] . extLength & UDF_EXTENT_LENGTH_MASK ) +
UDF_EXTENT_LENGTH_MASK ) & ~ ( inode - > i_sb - > s_blocksize - 1 ) ;
laarr [ i ] . extLength = ( laarr [ i ] . extLength & UDF_EXTENT_FLAG_MASK ) +
( UDF_EXTENT_LENGTH_MASK + 1 ) - inode - > i_sb - > s_blocksize ;
}
else
{
laarr [ i ] . extLength = laarr [ i + 1 ] . extLength +
( ( ( laarr [ i ] . extLength & UDF_EXTENT_LENGTH_MASK ) +
inode - > i_sb - > s_blocksize - 1 ) & ~ ( inode - > i_sb - > s_blocksize - 1 ) ) ;
if ( * endnum > ( i + 2 ) )
memmove ( & laarr [ i + 1 ] , & laarr [ i + 2 ] ,
sizeof ( long_ad ) * ( * endnum - ( i + 2 ) ) ) ;
i - - ;
( * endnum ) - - ;
}
}
else if ( ( laarr [ i ] . extLength > > 30 ) = = ( EXT_NOT_RECORDED_ALLOCATED > > 30 ) )
{
udf_free_blocks ( inode - > i_sb , inode , laarr [ i ] . extLocation , 0 ,
( ( laarr [ i ] . extLength & UDF_EXTENT_LENGTH_MASK ) +
inode - > i_sb - > s_blocksize - 1 ) > > inode - > i_sb - > s_blocksize_bits ) ;
laarr [ i ] . extLocation . logicalBlockNum = 0 ;
laarr [ i ] . extLocation . partitionReferenceNum = 0 ;
laarr [ i ] . extLength = ( laarr [ i ] . extLength & UDF_EXTENT_LENGTH_MASK ) |
EXT_NOT_RECORDED_NOT_ALLOCATED ;
}
}
}
static void udf_update_extents ( struct inode * inode ,
kernel_long_ad laarr [ EXTENT_MERGE_SIZE ] , int startnum , int endnum ,
kernel_lb_addr pbloc , uint32_t pextoffset , struct buffer_head * * pbh )
{
int start = 0 , i ;
kernel_lb_addr tmploc ;
uint32_t tmplen ;
if ( startnum > endnum )
{
for ( i = 0 ; i < ( startnum - endnum ) ; i + + )
{
udf_delete_aext ( inode , pbloc , pextoffset , laarr [ i ] . extLocation ,
laarr [ i ] . extLength , * pbh ) ;
}
}
else if ( startnum < endnum )
{
for ( i = 0 ; i < ( endnum - startnum ) ; i + + )
{
udf_insert_aext ( inode , pbloc , pextoffset , laarr [ i ] . extLocation ,
laarr [ i ] . extLength , * pbh ) ;
udf_next_aext ( inode , & pbloc , & pextoffset , & laarr [ i ] . extLocation ,
& laarr [ i ] . extLength , pbh , 1 ) ;
start + + ;
}
}
for ( i = start ; i < endnum ; i + + )
{
udf_next_aext ( inode , & pbloc , & pextoffset , & tmploc , & tmplen , pbh , 0 ) ;
udf_write_aext ( inode , pbloc , & pextoffset , laarr [ i ] . extLocation ,
laarr [ i ] . extLength , * pbh , 1 ) ;
}
}
struct buffer_head * udf_bread ( struct inode * inode , int block ,
int create , int * err )
{
struct buffer_head * bh = NULL ;
bh = udf_getblk ( inode , block , create , err ) ;
if ( ! bh )
return NULL ;
if ( buffer_uptodate ( bh ) )
return bh ;
ll_rw_block ( READ , 1 , & bh ) ;
wait_on_buffer ( bh ) ;
if ( buffer_uptodate ( bh ) )
return bh ;
brelse ( bh ) ;
* err = - EIO ;
return NULL ;
}
void udf_truncate ( struct inode * inode )
{
int offset ;
int err ;
if ( ! ( S_ISREG ( inode - > i_mode ) | | S_ISDIR ( inode - > i_mode ) | |
S_ISLNK ( inode - > i_mode ) ) )
return ;
if ( IS_APPEND ( inode ) | | IS_IMMUTABLE ( inode ) )
return ;
lock_kernel ( ) ;
if ( UDF_I_ALLOCTYPE ( inode ) = = ICBTAG_FLAG_AD_IN_ICB )
{
if ( inode - > i_sb - > s_blocksize < ( udf_file_entry_alloc_offset ( inode ) +
inode - > i_size ) )
{
udf_expand_file_adinicb ( inode , inode - > i_size , & err ) ;
if ( UDF_I_ALLOCTYPE ( inode ) = = ICBTAG_FLAG_AD_IN_ICB )
{
inode - > i_size = UDF_I_LENALLOC ( inode ) ;
unlock_kernel ( ) ;
return ;
}
else
udf_truncate_extents ( inode ) ;
}
else
{
offset = inode - > i_size & ( inode - > i_sb - > s_blocksize - 1 ) ;
memset ( UDF_I_DATA ( inode ) + UDF_I_LENEATTR ( inode ) + offset , 0x00 , inode - > i_sb - > s_blocksize - offset - udf_file_entry_alloc_offset ( inode ) ) ;
UDF_I_LENALLOC ( inode ) = inode - > i_size ;
}
}
else
{
block_truncate_page ( inode - > i_mapping , inode - > i_size , udf_get_block ) ;
udf_truncate_extents ( inode ) ;
}
inode - > i_mtime = inode - > i_ctime = current_fs_time ( inode - > i_sb ) ;
if ( IS_SYNC ( inode ) )
udf_sync_inode ( inode ) ;
else
mark_inode_dirty ( inode ) ;
unlock_kernel ( ) ;
}
static void
__udf_read_inode ( struct inode * inode )
{
struct buffer_head * bh = NULL ;
struct fileEntry * fe ;
uint16_t ident ;
/*
* Set defaults , but the inode is still incomplete !
* Note : get_new_inode ( ) sets the following on a new inode :
* i_sb = sb
* i_no = ino
* i_flags = sb - > s_flags
* i_state = 0
* clean_inode ( ) : zero fills and sets
* i_count = 1
* i_nlink = 1
* i_op = NULL ;
*/
inode - > i_blksize = PAGE_SIZE ;
bh = udf_read_ptagged ( inode - > i_sb , UDF_I_LOCATION ( inode ) , 0 , & ident ) ;
if ( ! bh )
{
printk ( KERN_ERR " udf: udf_read_inode(ino %ld) failed !bh \n " ,
inode - > i_ino ) ;
make_bad_inode ( inode ) ;
return ;
}
if ( ident ! = TAG_IDENT_FE & & ident ! = TAG_IDENT_EFE & &
ident ! = TAG_IDENT_USE )
{
printk ( KERN_ERR " udf: udf_read_inode(ino %ld) failed ident=%d \n " ,
inode - > i_ino , ident ) ;
udf_release_data ( bh ) ;
make_bad_inode ( inode ) ;
return ;
}
fe = ( struct fileEntry * ) bh - > b_data ;
if ( le16_to_cpu ( fe - > icbTag . strategyType ) = = 4096 )
{
struct buffer_head * ibh = NULL , * nbh = NULL ;
struct indirectEntry * ie ;
ibh = udf_read_ptagged ( inode - > i_sb , UDF_I_LOCATION ( inode ) , 1 , & ident ) ;
if ( ident = = TAG_IDENT_IE )
{
if ( ibh )
{
kernel_lb_addr loc ;
ie = ( struct indirectEntry * ) ibh - > b_data ;
loc = lelb_to_cpu ( ie - > indirectICB . extLocation ) ;
if ( ie - > indirectICB . extLength & &
( nbh = udf_read_ptagged ( inode - > i_sb , loc , 0 , & ident ) ) )
{
if ( ident = = TAG_IDENT_FE | |
ident = = TAG_IDENT_EFE )
{
memcpy ( & UDF_I_LOCATION ( inode ) , & loc , sizeof ( kernel_lb_addr ) ) ;
udf_release_data ( bh ) ;
udf_release_data ( ibh ) ;
udf_release_data ( nbh ) ;
__udf_read_inode ( inode ) ;
return ;
}
else
{
udf_release_data ( nbh ) ;
udf_release_data ( ibh ) ;
}
}
else
udf_release_data ( ibh ) ;
}
}
else
udf_release_data ( ibh ) ;
}
else if ( le16_to_cpu ( fe - > icbTag . strategyType ) ! = 4 )
{
printk ( KERN_ERR " udf: unsupported strategy type: %d \n " ,
le16_to_cpu ( fe - > icbTag . strategyType ) ) ;
udf_release_data ( bh ) ;
make_bad_inode ( inode ) ;
return ;
}
udf_fill_inode ( inode , bh ) ;
udf_release_data ( bh ) ;
}
static void udf_fill_inode ( struct inode * inode , struct buffer_head * bh )
{
struct fileEntry * fe ;
struct extendedFileEntry * efe ;
time_t convtime ;
long convtime_usec ;
int offset ;
fe = ( struct fileEntry * ) bh - > b_data ;
efe = ( struct extendedFileEntry * ) bh - > b_data ;
if ( le16_to_cpu ( fe - > icbTag . strategyType ) = = 4 )
UDF_I_STRAT4096 ( inode ) = 0 ;
else /* if (le16_to_cpu(fe->icbTag.strategyType) == 4096) */
UDF_I_STRAT4096 ( inode ) = 1 ;
UDF_I_ALLOCTYPE ( inode ) = le16_to_cpu ( fe - > icbTag . flags ) & ICBTAG_FLAG_AD_MASK ;
UDF_I_UNIQUE ( inode ) = 0 ;
UDF_I_LENEATTR ( inode ) = 0 ;
UDF_I_LENEXTENTS ( inode ) = 0 ;
UDF_I_LENALLOC ( inode ) = 0 ;
UDF_I_NEXT_ALLOC_BLOCK ( inode ) = 0 ;
UDF_I_NEXT_ALLOC_GOAL ( inode ) = 0 ;
if ( le16_to_cpu ( fe - > descTag . tagIdent ) = = TAG_IDENT_EFE )
{
UDF_I_EFE ( inode ) = 1 ;
UDF_I_USE ( inode ) = 0 ;
UDF_I_DATA ( inode ) = kmalloc ( inode - > i_sb - > s_blocksize - sizeof ( struct extendedFileEntry ) , GFP_KERNEL ) ;
memcpy ( UDF_I_DATA ( inode ) , bh - > b_data + sizeof ( struct extendedFileEntry ) , inode - > i_sb - > s_blocksize - sizeof ( struct extendedFileEntry ) ) ;
}
else if ( le16_to_cpu ( fe - > descTag . tagIdent ) = = TAG_IDENT_FE )
{
UDF_I_EFE ( inode ) = 0 ;
UDF_I_USE ( inode ) = 0 ;
UDF_I_DATA ( inode ) = kmalloc ( inode - > i_sb - > s_blocksize - sizeof ( struct fileEntry ) , GFP_KERNEL ) ;
memcpy ( UDF_I_DATA ( inode ) , bh - > b_data + sizeof ( struct fileEntry ) , inode - > i_sb - > s_blocksize - sizeof ( struct fileEntry ) ) ;
}
else if ( le16_to_cpu ( fe - > descTag . tagIdent ) = = TAG_IDENT_USE )
{
UDF_I_EFE ( inode ) = 0 ;
UDF_I_USE ( inode ) = 1 ;
UDF_I_LENALLOC ( inode ) =
le32_to_cpu (
( ( struct unallocSpaceEntry * ) bh - > b_data ) - > lengthAllocDescs ) ;
UDF_I_DATA ( inode ) = kmalloc ( inode - > i_sb - > s_blocksize - sizeof ( struct unallocSpaceEntry ) , GFP_KERNEL ) ;
memcpy ( UDF_I_DATA ( inode ) , bh - > b_data + sizeof ( struct unallocSpaceEntry ) , inode - > i_sb - > s_blocksize - sizeof ( struct unallocSpaceEntry ) ) ;
return ;
}
inode - > i_uid = le32_to_cpu ( fe - > uid ) ;
if ( inode - > i_uid = = - 1 ) inode - > i_uid = UDF_SB ( inode - > i_sb ) - > s_uid ;
inode - > i_gid = le32_to_cpu ( fe - > gid ) ;
if ( inode - > i_gid = = - 1 ) inode - > i_gid = UDF_SB ( inode - > i_sb ) - > s_gid ;
inode - > i_nlink = le16_to_cpu ( fe - > fileLinkCount ) ;
if ( ! inode - > i_nlink )
inode - > i_nlink = 1 ;
inode - > i_size = le64_to_cpu ( fe - > informationLength ) ;
UDF_I_LENEXTENTS ( inode ) = inode - > i_size ;
inode - > i_mode = udf_convert_permissions ( fe ) ;
inode - > i_mode & = ~ UDF_SB ( inode - > i_sb ) - > s_umask ;
if ( UDF_I_EFE ( inode ) = = 0 )
{
inode - > i_blocks = le64_to_cpu ( fe - > logicalBlocksRecorded ) < <
( inode - > i_sb - > s_blocksize_bits - 9 ) ;
if ( udf_stamp_to_time ( & convtime , & convtime_usec ,
lets_to_cpu ( fe - > accessTime ) ) )
{
inode - > i_atime . tv_sec = convtime ;
inode - > i_atime . tv_nsec = convtime_usec * 1000 ;
}
else
{
inode - > i_atime = UDF_SB_RECORDTIME ( inode - > i_sb ) ;
}
if ( udf_stamp_to_time ( & convtime , & convtime_usec ,
lets_to_cpu ( fe - > modificationTime ) ) )
{
inode - > i_mtime . tv_sec = convtime ;
inode - > i_mtime . tv_nsec = convtime_usec * 1000 ;
}
else
{
inode - > i_mtime = UDF_SB_RECORDTIME ( inode - > i_sb ) ;
}
if ( udf_stamp_to_time ( & convtime , & convtime_usec ,
lets_to_cpu ( fe - > attrTime ) ) )
{
inode - > i_ctime . tv_sec = convtime ;
inode - > i_ctime . tv_nsec = convtime_usec * 1000 ;
}
else
{
inode - > i_ctime = UDF_SB_RECORDTIME ( inode - > i_sb ) ;
}
UDF_I_UNIQUE ( inode ) = le64_to_cpu ( fe - > uniqueID ) ;
UDF_I_LENEATTR ( inode ) = le32_to_cpu ( fe - > lengthExtendedAttr ) ;
UDF_I_LENALLOC ( inode ) = le32_to_cpu ( fe - > lengthAllocDescs ) ;
offset = sizeof ( struct fileEntry ) + UDF_I_LENEATTR ( inode ) ;
}
else
{
inode - > i_blocks = le64_to_cpu ( efe - > logicalBlocksRecorded ) < <
( inode - > i_sb - > s_blocksize_bits - 9 ) ;
if ( udf_stamp_to_time ( & convtime , & convtime_usec ,
lets_to_cpu ( efe - > accessTime ) ) )
{
inode - > i_atime . tv_sec = convtime ;
inode - > i_atime . tv_nsec = convtime_usec * 1000 ;
}
else
{
inode - > i_atime = UDF_SB_RECORDTIME ( inode - > i_sb ) ;
}
if ( udf_stamp_to_time ( & convtime , & convtime_usec ,
lets_to_cpu ( efe - > modificationTime ) ) )
{
inode - > i_mtime . tv_sec = convtime ;
inode - > i_mtime . tv_nsec = convtime_usec * 1000 ;
}
else
{
inode - > i_mtime = UDF_SB_RECORDTIME ( inode - > i_sb ) ;
}
if ( udf_stamp_to_time ( & convtime , & convtime_usec ,
lets_to_cpu ( efe - > createTime ) ) )
{
UDF_I_CRTIME ( inode ) . tv_sec = convtime ;
UDF_I_CRTIME ( inode ) . tv_nsec = convtime_usec * 1000 ;
}
else
{
UDF_I_CRTIME ( inode ) = UDF_SB_RECORDTIME ( inode - > i_sb ) ;
}
if ( udf_stamp_to_time ( & convtime , & convtime_usec ,
lets_to_cpu ( efe - > attrTime ) ) )
{
inode - > i_ctime . tv_sec = convtime ;
inode - > i_ctime . tv_nsec = convtime_usec * 1000 ;
}
else
{
inode - > i_ctime = UDF_SB_RECORDTIME ( inode - > i_sb ) ;
}
UDF_I_UNIQUE ( inode ) = le64_to_cpu ( efe - > uniqueID ) ;
UDF_I_LENEATTR ( inode ) = le32_to_cpu ( efe - > lengthExtendedAttr ) ;
UDF_I_LENALLOC ( inode ) = le32_to_cpu ( efe - > lengthAllocDescs ) ;
offset = sizeof ( struct extendedFileEntry ) + UDF_I_LENEATTR ( inode ) ;
}
switch ( fe - > icbTag . fileType )
{
case ICBTAG_FILE_TYPE_DIRECTORY :
{
inode - > i_op = & udf_dir_inode_operations ;
inode - > i_fop = & udf_dir_operations ;
inode - > i_mode | = S_IFDIR ;
inode - > i_nlink + + ;
break ;
}
case ICBTAG_FILE_TYPE_REALTIME :
case ICBTAG_FILE_TYPE_REGULAR :
case ICBTAG_FILE_TYPE_UNDEF :
{
if ( UDF_I_ALLOCTYPE ( inode ) = = ICBTAG_FLAG_AD_IN_ICB )
inode - > i_data . a_ops = & udf_adinicb_aops ;
else
inode - > i_data . a_ops = & udf_aops ;
inode - > i_op = & udf_file_inode_operations ;
inode - > i_fop = & udf_file_operations ;
inode - > i_mode | = S_IFREG ;
break ;
}
case ICBTAG_FILE_TYPE_BLOCK :
{
inode - > i_mode | = S_IFBLK ;
break ;
}
case ICBTAG_FILE_TYPE_CHAR :
{
inode - > i_mode | = S_IFCHR ;
break ;
}
case ICBTAG_FILE_TYPE_FIFO :
{
init_special_inode ( inode , inode - > i_mode | S_IFIFO , 0 ) ;
break ;
}
case ICBTAG_FILE_TYPE_SOCKET :
{
init_special_inode ( inode , inode - > i_mode | S_IFSOCK , 0 ) ;
break ;
}
case ICBTAG_FILE_TYPE_SYMLINK :
{
inode - > i_data . a_ops = & udf_symlink_aops ;
inode - > i_op = & page_symlink_inode_operations ;
inode - > i_mode = S_IFLNK | S_IRWXUGO ;
break ;
}
default :
{
printk ( KERN_ERR " udf: udf_fill_inode(ino %ld) failed unknown file type=%d \n " ,
inode - > i_ino , fe - > icbTag . fileType ) ;
make_bad_inode ( inode ) ;
return ;
}
}
if ( S_ISCHR ( inode - > i_mode ) | | S_ISBLK ( inode - > i_mode ) )
{
struct deviceSpec * dsea =
( struct deviceSpec * )
udf_get_extendedattr ( inode , 12 , 1 ) ;
if ( dsea )
{
init_special_inode ( inode , inode - > i_mode , MKDEV (
le32_to_cpu ( dsea - > majorDeviceIdent ) ,
le32_to_cpu ( dsea - > minorDeviceIdent ) ) ) ;
/* Developer ID ??? */
}
else
{
make_bad_inode ( inode ) ;
}
}
}
static mode_t
udf_convert_permissions ( struct fileEntry * fe )
{
mode_t mode ;
uint32_t permissions ;
uint32_t flags ;
permissions = le32_to_cpu ( fe - > permissions ) ;
flags = le16_to_cpu ( fe - > icbTag . flags ) ;
mode = ( ( permissions ) & S_IRWXO ) |
( ( permissions > > 2 ) & S_IRWXG ) |
( ( permissions > > 4 ) & S_IRWXU ) |
( ( flags & ICBTAG_FLAG_SETUID ) ? S_ISUID : 0 ) |
( ( flags & ICBTAG_FLAG_SETGID ) ? S_ISGID : 0 ) |
( ( flags & ICBTAG_FLAG_STICKY ) ? S_ISVTX : 0 ) ;
return mode ;
}
/*
* udf_write_inode
*
* PURPOSE
* Write out the specified inode .
*
* DESCRIPTION
* This routine is called whenever an inode is synced .
* Currently this routine is just a placeholder .
*
* HISTORY
* July 1 , 1997 - Andrew E . Mileski
* Written , tested , and released .
*/
int udf_write_inode ( struct inode * inode , int sync )
{
int ret ;
lock_kernel ( ) ;
ret = udf_update_inode ( inode , sync ) ;
unlock_kernel ( ) ;
return ret ;
}
int udf_sync_inode ( struct inode * inode )
{
return udf_update_inode ( inode , 1 ) ;
}
static int
udf_update_inode ( struct inode * inode , int do_sync )
{
struct buffer_head * bh = NULL ;
struct fileEntry * fe ;
struct extendedFileEntry * efe ;
uint32_t udfperms ;
uint16_t icbflags ;
uint16_t crclen ;
int i ;
kernel_timestamp cpu_time ;
int err = 0 ;
bh = udf_tread ( inode - > i_sb ,
udf_get_lb_pblock ( inode - > i_sb , UDF_I_LOCATION ( inode ) , 0 ) ) ;
if ( ! bh )
{
udf_debug ( " bread failure \n " ) ;
return - EIO ;
}
memset ( bh - > b_data , 0x00 , inode - > i_sb - > s_blocksize ) ;
fe = ( struct fileEntry * ) bh - > b_data ;
efe = ( struct extendedFileEntry * ) bh - > b_data ;
if ( le16_to_cpu ( fe - > descTag . tagIdent ) = = TAG_IDENT_USE )
{
struct unallocSpaceEntry * use =
( struct unallocSpaceEntry * ) bh - > b_data ;
use - > lengthAllocDescs = cpu_to_le32 ( UDF_I_LENALLOC ( inode ) ) ;
memcpy ( bh - > b_data + sizeof ( struct unallocSpaceEntry ) , UDF_I_DATA ( inode ) , inode - > i_sb - > s_blocksize - sizeof ( struct unallocSpaceEntry ) ) ;
crclen = sizeof ( struct unallocSpaceEntry ) + UDF_I_LENALLOC ( inode ) -
sizeof ( tag ) ;
use - > descTag . tagLocation = cpu_to_le32 ( UDF_I_LOCATION ( inode ) . logicalBlockNum ) ;
use - > descTag . descCRCLength = cpu_to_le16 ( crclen ) ;
use - > descTag . descCRC = cpu_to_le16 ( udf_crc ( ( char * ) use + sizeof ( tag ) , crclen , 0 ) ) ;
use - > descTag . tagChecksum = 0 ;
for ( i = 0 ; i < 16 ; i + + )
if ( i ! = 4 )
use - > descTag . tagChecksum + = ( ( uint8_t * ) & ( use - > descTag ) ) [ i ] ;
mark_buffer_dirty ( bh ) ;
udf_release_data ( bh ) ;
return err ;
}
if ( inode - > i_uid ! = UDF_SB ( inode - > i_sb ) - > s_uid )
fe - > uid = cpu_to_le32 ( inode - > i_uid ) ;
if ( inode - > i_gid ! = UDF_SB ( inode - > i_sb ) - > s_gid )
fe - > gid = cpu_to_le32 ( inode - > i_gid ) ;
udfperms = ( ( inode - > i_mode & S_IRWXO ) ) |
( ( inode - > i_mode & S_IRWXG ) < < 2 ) |
( ( inode - > i_mode & S_IRWXU ) < < 4 ) ;
udfperms | = ( le32_to_cpu ( fe - > permissions ) &
( FE_PERM_O_DELETE | FE_PERM_O_CHATTR |
FE_PERM_G_DELETE | FE_PERM_G_CHATTR |
FE_PERM_U_DELETE | FE_PERM_U_CHATTR ) ) ;
fe - > permissions = cpu_to_le32 ( udfperms ) ;
if ( S_ISDIR ( inode - > i_mode ) )
fe - > fileLinkCount = cpu_to_le16 ( inode - > i_nlink - 1 ) ;
else
fe - > fileLinkCount = cpu_to_le16 ( inode - > i_nlink ) ;
fe - > informationLength = cpu_to_le64 ( inode - > i_size ) ;
if ( S_ISCHR ( inode - > i_mode ) | | S_ISBLK ( inode - > i_mode ) )
{
regid * eid ;
struct deviceSpec * dsea =
( struct deviceSpec * )
udf_get_extendedattr ( inode , 12 , 1 ) ;
if ( ! dsea )
{
dsea = ( struct deviceSpec * )
udf_add_extendedattr ( inode ,
sizeof ( struct deviceSpec ) +
sizeof ( regid ) , 12 , 0x3 ) ;
dsea - > attrType = cpu_to_le32 ( 12 ) ;
dsea - > attrSubtype = 1 ;
dsea - > attrLength = cpu_to_le32 ( sizeof ( struct deviceSpec ) +
sizeof ( regid ) ) ;
dsea - > impUseLength = cpu_to_le32 ( sizeof ( regid ) ) ;
}
eid = ( regid * ) dsea - > impUse ;
memset ( eid , 0 , sizeof ( regid ) ) ;
strcpy ( eid - > ident , UDF_ID_DEVELOPER ) ;
eid - > identSuffix [ 0 ] = UDF_OS_CLASS_UNIX ;
eid - > identSuffix [ 1 ] = UDF_OS_ID_LINUX ;
dsea - > majorDeviceIdent = cpu_to_le32 ( imajor ( inode ) ) ;
dsea - > minorDeviceIdent = cpu_to_le32 ( iminor ( inode ) ) ;
}
if ( UDF_I_EFE ( inode ) = = 0 )
{
memcpy ( bh - > b_data + sizeof ( struct fileEntry ) , UDF_I_DATA ( inode ) , inode - > i_sb - > s_blocksize - sizeof ( struct fileEntry ) ) ;
fe - > logicalBlocksRecorded = cpu_to_le64 (
( inode - > i_blocks + ( 1 < < ( inode - > i_sb - > s_blocksize_bits - 9 ) ) - 1 ) > >
( inode - > i_sb - > s_blocksize_bits - 9 ) ) ;
if ( udf_time_to_stamp ( & cpu_time , inode - > i_atime ) )
fe - > accessTime = cpu_to_lets ( cpu_time ) ;
if ( udf_time_to_stamp ( & cpu_time , inode - > i_mtime ) )
fe - > modificationTime = cpu_to_lets ( cpu_time ) ;
if ( udf_time_to_stamp ( & cpu_time , inode - > i_ctime ) )
fe - > attrTime = cpu_to_lets ( cpu_time ) ;
memset ( & ( fe - > impIdent ) , 0 , sizeof ( regid ) ) ;
strcpy ( fe - > impIdent . ident , UDF_ID_DEVELOPER ) ;
fe - > impIdent . identSuffix [ 0 ] = UDF_OS_CLASS_UNIX ;
fe - > impIdent . identSuffix [ 1 ] = UDF_OS_ID_LINUX ;
fe - > uniqueID = cpu_to_le64 ( UDF_I_UNIQUE ( inode ) ) ;
fe - > lengthExtendedAttr = cpu_to_le32 ( UDF_I_LENEATTR ( inode ) ) ;
fe - > lengthAllocDescs = cpu_to_le32 ( UDF_I_LENALLOC ( inode ) ) ;
fe - > descTag . tagIdent = cpu_to_le16 ( TAG_IDENT_FE ) ;
crclen = sizeof ( struct fileEntry ) ;
}
else
{
memcpy ( bh - > b_data + sizeof ( struct extendedFileEntry ) , UDF_I_DATA ( inode ) , inode - > i_sb - > s_blocksize - sizeof ( struct extendedFileEntry ) ) ;
efe - > objectSize = cpu_to_le64 ( inode - > i_size ) ;
efe - > logicalBlocksRecorded = cpu_to_le64 (
( inode - > i_blocks + ( 1 < < ( inode - > i_sb - > s_blocksize_bits - 9 ) ) - 1 ) > >
( inode - > i_sb - > s_blocksize_bits - 9 ) ) ;
if ( UDF_I_CRTIME ( inode ) . tv_sec > inode - > i_atime . tv_sec | |
( UDF_I_CRTIME ( inode ) . tv_sec = = inode - > i_atime . tv_sec & &
UDF_I_CRTIME ( inode ) . tv_nsec > inode - > i_atime . tv_nsec ) )
{
UDF_I_CRTIME ( inode ) = inode - > i_atime ;
}
if ( UDF_I_CRTIME ( inode ) . tv_sec > inode - > i_mtime . tv_sec | |
( UDF_I_CRTIME ( inode ) . tv_sec = = inode - > i_mtime . tv_sec & &
UDF_I_CRTIME ( inode ) . tv_nsec > inode - > i_mtime . tv_nsec ) )
{
UDF_I_CRTIME ( inode ) = inode - > i_mtime ;
}
if ( UDF_I_CRTIME ( inode ) . tv_sec > inode - > i_ctime . tv_sec | |
( UDF_I_CRTIME ( inode ) . tv_sec = = inode - > i_ctime . tv_sec & &
UDF_I_CRTIME ( inode ) . tv_nsec > inode - > i_ctime . tv_nsec ) )
{
UDF_I_CRTIME ( inode ) = inode - > i_ctime ;
}
if ( udf_time_to_stamp ( & cpu_time , inode - > i_atime ) )
efe - > accessTime = cpu_to_lets ( cpu_time ) ;
if ( udf_time_to_stamp ( & cpu_time , inode - > i_mtime ) )
efe - > modificationTime = cpu_to_lets ( cpu_time ) ;
if ( udf_time_to_stamp ( & cpu_time , UDF_I_CRTIME ( inode ) ) )
efe - > createTime = cpu_to_lets ( cpu_time ) ;
if ( udf_time_to_stamp ( & cpu_time , inode - > i_ctime ) )
efe - > attrTime = cpu_to_lets ( cpu_time ) ;
memset ( & ( efe - > impIdent ) , 0 , sizeof ( regid ) ) ;
strcpy ( efe - > impIdent . ident , UDF_ID_DEVELOPER ) ;
efe - > impIdent . identSuffix [ 0 ] = UDF_OS_CLASS_UNIX ;
efe - > impIdent . identSuffix [ 1 ] = UDF_OS_ID_LINUX ;
efe - > uniqueID = cpu_to_le64 ( UDF_I_UNIQUE ( inode ) ) ;
efe - > lengthExtendedAttr = cpu_to_le32 ( UDF_I_LENEATTR ( inode ) ) ;
efe - > lengthAllocDescs = cpu_to_le32 ( UDF_I_LENALLOC ( inode ) ) ;
efe - > descTag . tagIdent = cpu_to_le16 ( TAG_IDENT_EFE ) ;
crclen = sizeof ( struct extendedFileEntry ) ;
}
if ( UDF_I_STRAT4096 ( inode ) )
{
fe - > icbTag . strategyType = cpu_to_le16 ( 4096 ) ;
fe - > icbTag . strategyParameter = cpu_to_le16 ( 1 ) ;
fe - > icbTag . numEntries = cpu_to_le16 ( 2 ) ;
}
else
{
fe - > icbTag . strategyType = cpu_to_le16 ( 4 ) ;
fe - > icbTag . numEntries = cpu_to_le16 ( 1 ) ;
}
if ( S_ISDIR ( inode - > i_mode ) )
fe - > icbTag . fileType = ICBTAG_FILE_TYPE_DIRECTORY ;
else if ( S_ISREG ( inode - > i_mode ) )
fe - > icbTag . fileType = ICBTAG_FILE_TYPE_REGULAR ;
else if ( S_ISLNK ( inode - > i_mode ) )
fe - > icbTag . fileType = ICBTAG_FILE_TYPE_SYMLINK ;
else if ( S_ISBLK ( inode - > i_mode ) )
fe - > icbTag . fileType = ICBTAG_FILE_TYPE_BLOCK ;
else if ( S_ISCHR ( inode - > i_mode ) )
fe - > icbTag . fileType = ICBTAG_FILE_TYPE_CHAR ;
else if ( S_ISFIFO ( inode - > i_mode ) )
fe - > icbTag . fileType = ICBTAG_FILE_TYPE_FIFO ;
else if ( S_ISSOCK ( inode - > i_mode ) )
fe - > icbTag . fileType = ICBTAG_FILE_TYPE_SOCKET ;
icbflags = UDF_I_ALLOCTYPE ( inode ) |
( ( inode - > i_mode & S_ISUID ) ? ICBTAG_FLAG_SETUID : 0 ) |
( ( inode - > i_mode & S_ISGID ) ? ICBTAG_FLAG_SETGID : 0 ) |
( ( inode - > i_mode & S_ISVTX ) ? ICBTAG_FLAG_STICKY : 0 ) |
( le16_to_cpu ( fe - > icbTag . flags ) &
~ ( ICBTAG_FLAG_AD_MASK | ICBTAG_FLAG_SETUID |
ICBTAG_FLAG_SETGID | ICBTAG_FLAG_STICKY ) ) ;
fe - > icbTag . flags = cpu_to_le16 ( icbflags ) ;
if ( UDF_SB_UDFREV ( inode - > i_sb ) > = 0x0200 )
fe - > descTag . descVersion = cpu_to_le16 ( 3 ) ;
else
fe - > descTag . descVersion = cpu_to_le16 ( 2 ) ;
fe - > descTag . tagSerialNum = cpu_to_le16 ( UDF_SB_SERIALNUM ( inode - > i_sb ) ) ;
fe - > descTag . tagLocation = cpu_to_le32 ( UDF_I_LOCATION ( inode ) . logicalBlockNum ) ;
crclen + = UDF_I_LENEATTR ( inode ) + UDF_I_LENALLOC ( inode ) - sizeof ( tag ) ;
fe - > descTag . descCRCLength = cpu_to_le16 ( crclen ) ;
fe - > descTag . descCRC = cpu_to_le16 ( udf_crc ( ( char * ) fe + sizeof ( tag ) , crclen , 0 ) ) ;
fe - > descTag . tagChecksum = 0 ;
for ( i = 0 ; i < 16 ; i + + )
if ( i ! = 4 )
fe - > descTag . tagChecksum + = ( ( uint8_t * ) & ( fe - > descTag ) ) [ i ] ;
/* write the data blocks */
mark_buffer_dirty ( bh ) ;
if ( do_sync )
{
sync_dirty_buffer ( bh ) ;
if ( buffer_req ( bh ) & & ! buffer_uptodate ( bh ) )
{
printk ( " IO error syncing udf inode [%s:%08lx] \n " ,
inode - > i_sb - > s_id , inode - > i_ino ) ;
err = - EIO ;
}
}
udf_release_data ( bh ) ;
return err ;
}
struct inode *
udf_iget ( struct super_block * sb , kernel_lb_addr ino )
{
unsigned long block = udf_get_lb_pblock ( sb , ino , 0 ) ;
struct inode * inode = iget_locked ( sb , block ) ;
if ( ! inode )
return NULL ;
if ( inode - > i_state & I_NEW ) {
memcpy ( & UDF_I_LOCATION ( inode ) , & ino , sizeof ( kernel_lb_addr ) ) ;
__udf_read_inode ( inode ) ;
unlock_new_inode ( inode ) ;
}
if ( is_bad_inode ( inode ) )
goto out_iput ;
if ( ino . logicalBlockNum > = UDF_SB_PARTLEN ( sb , ino . partitionReferenceNum ) ) {
udf_debug ( " block=%d, partition=%d out of range \n " ,
ino . logicalBlockNum , ino . partitionReferenceNum ) ;
make_bad_inode ( inode ) ;
goto out_iput ;
}
return inode ;
out_iput :
iput ( inode ) ;
return NULL ;
}
int8_t udf_add_aext ( struct inode * inode , kernel_lb_addr * bloc , int * extoffset ,
kernel_lb_addr eloc , uint32_t elen , struct buffer_head * * bh , int inc )
{
int adsize ;
short_ad * sad = NULL ;
long_ad * lad = NULL ;
struct allocExtDesc * aed ;
int8_t etype ;
uint8_t * ptr ;
if ( ! * bh )
ptr = UDF_I_DATA ( inode ) + * extoffset - udf_file_entry_alloc_offset ( inode ) + UDF_I_LENEATTR ( inode ) ;
else
ptr = ( * bh ) - > b_data + * extoffset ;
if ( UDF_I_ALLOCTYPE ( inode ) = = ICBTAG_FLAG_AD_SHORT )
adsize = sizeof ( short_ad ) ;
else if ( UDF_I_ALLOCTYPE ( inode ) = = ICBTAG_FLAG_AD_LONG )
adsize = sizeof ( long_ad ) ;
else
return - 1 ;
if ( * extoffset + ( 2 * adsize ) > inode - > i_sb - > s_blocksize )
{
char * sptr , * dptr ;
struct buffer_head * nbh ;
int err , loffset ;
kernel_lb_addr obloc = * bloc ;
if ( ! ( bloc - > logicalBlockNum = udf_new_block ( inode - > i_sb , NULL ,
obloc . partitionReferenceNum , obloc . logicalBlockNum , & err ) ) )
{
return - 1 ;
}
if ( ! ( nbh = udf_tgetblk ( inode - > i_sb , udf_get_lb_pblock ( inode - > i_sb ,
* bloc , 0 ) ) ) )
{
return - 1 ;
}
lock_buffer ( nbh ) ;
memset ( nbh - > b_data , 0x00 , inode - > i_sb - > s_blocksize ) ;
set_buffer_uptodate ( nbh ) ;
unlock_buffer ( nbh ) ;
mark_buffer_dirty_inode ( nbh , inode ) ;
aed = ( struct allocExtDesc * ) ( nbh - > b_data ) ;
if ( ! UDF_QUERY_FLAG ( inode - > i_sb , UDF_FLAG_STRICT ) )
aed - > previousAllocExtLocation = cpu_to_le32 ( obloc . logicalBlockNum ) ;
if ( * extoffset + adsize > inode - > i_sb - > s_blocksize )
{
loffset = * extoffset ;
aed - > lengthAllocDescs = cpu_to_le32 ( adsize ) ;
sptr = ptr - adsize ;
dptr = nbh - > b_data + sizeof ( struct allocExtDesc ) ;
memcpy ( dptr , sptr , adsize ) ;
* extoffset = sizeof ( struct allocExtDesc ) + adsize ;
}
else
{
loffset = * extoffset + adsize ;
aed - > lengthAllocDescs = cpu_to_le32 ( 0 ) ;
sptr = ptr ;
* extoffset = sizeof ( struct allocExtDesc ) ;
if ( * bh )
{
aed = ( struct allocExtDesc * ) ( * bh ) - > b_data ;
aed - > lengthAllocDescs =
cpu_to_le32 ( le32_to_cpu ( aed - > lengthAllocDescs ) + adsize ) ;
}
else
{
UDF_I_LENALLOC ( inode ) + = adsize ;
mark_inode_dirty ( inode ) ;
}
}
if ( UDF_SB_UDFREV ( inode - > i_sb ) > = 0x0200 )
udf_new_tag ( nbh - > b_data , TAG_IDENT_AED , 3 , 1 ,
bloc - > logicalBlockNum , sizeof ( tag ) ) ;
else
udf_new_tag ( nbh - > b_data , TAG_IDENT_AED , 2 , 1 ,
bloc - > logicalBlockNum , sizeof ( tag ) ) ;
switch ( UDF_I_ALLOCTYPE ( inode ) )
{
case ICBTAG_FLAG_AD_SHORT :
{
sad = ( short_ad * ) sptr ;
sad - > extLength = cpu_to_le32 (
EXT_NEXT_EXTENT_ALLOCDECS |
inode - > i_sb - > s_blocksize ) ;
sad - > extPosition = cpu_to_le32 ( bloc - > logicalBlockNum ) ;
break ;
}
case ICBTAG_FLAG_AD_LONG :
{
lad = ( long_ad * ) sptr ;
lad - > extLength = cpu_to_le32 (
EXT_NEXT_EXTENT_ALLOCDECS |
inode - > i_sb - > s_blocksize ) ;
lad - > extLocation = cpu_to_lelb ( * bloc ) ;
memset ( lad - > impUse , 0x00 , sizeof ( lad - > impUse ) ) ;
break ;
}
}
if ( * bh )
{
if ( ! UDF_QUERY_FLAG ( inode - > i_sb , UDF_FLAG_STRICT ) | | UDF_SB_UDFREV ( inode - > i_sb ) > = 0x0201 )
udf_update_tag ( ( * bh ) - > b_data , loffset ) ;
else
udf_update_tag ( ( * bh ) - > b_data , sizeof ( struct allocExtDesc ) ) ;
mark_buffer_dirty_inode ( * bh , inode ) ;
udf_release_data ( * bh ) ;
}
else
mark_inode_dirty ( inode ) ;
* bh = nbh ;
}
etype = udf_write_aext ( inode , * bloc , extoffset , eloc , elen , * bh , inc ) ;
if ( ! * bh )
{
UDF_I_LENALLOC ( inode ) + = adsize ;
mark_inode_dirty ( inode ) ;
}
else
{
aed = ( struct allocExtDesc * ) ( * bh ) - > b_data ;
aed - > lengthAllocDescs =
cpu_to_le32 ( le32_to_cpu ( aed - > lengthAllocDescs ) + adsize ) ;
if ( ! UDF_QUERY_FLAG ( inode - > i_sb , UDF_FLAG_STRICT ) | | UDF_SB_UDFREV ( inode - > i_sb ) > = 0x0201 )
udf_update_tag ( ( * bh ) - > b_data , * extoffset + ( inc ? 0 : adsize ) ) ;
else
udf_update_tag ( ( * bh ) - > b_data , sizeof ( struct allocExtDesc ) ) ;
mark_buffer_dirty_inode ( * bh , inode ) ;
}
return etype ;
}
int8_t udf_write_aext ( struct inode * inode , kernel_lb_addr bloc , int * extoffset ,
kernel_lb_addr eloc , uint32_t elen , struct buffer_head * bh , int inc )
{
int adsize ;
uint8_t * ptr ;
if ( ! bh )
ptr = UDF_I_DATA ( inode ) + * extoffset - udf_file_entry_alloc_offset ( inode ) + UDF_I_LENEATTR ( inode ) ;
else
{
ptr = bh - > b_data + * extoffset ;
atomic_inc ( & bh - > b_count ) ;
}
switch ( UDF_I_ALLOCTYPE ( inode ) )
{
case ICBTAG_FLAG_AD_SHORT :
{
short_ad * sad = ( short_ad * ) ptr ;
sad - > extLength = cpu_to_le32 ( elen ) ;
sad - > extPosition = cpu_to_le32 ( eloc . logicalBlockNum ) ;
adsize = sizeof ( short_ad ) ;
break ;
}
case ICBTAG_FLAG_AD_LONG :
{
long_ad * lad = ( long_ad * ) ptr ;
lad - > extLength = cpu_to_le32 ( elen ) ;
lad - > extLocation = cpu_to_lelb ( eloc ) ;
memset ( lad - > impUse , 0x00 , sizeof ( lad - > impUse ) ) ;
adsize = sizeof ( long_ad ) ;
break ;
}
default :
return - 1 ;
}
if ( bh )
{
if ( ! UDF_QUERY_FLAG ( inode - > i_sb , UDF_FLAG_STRICT ) | | UDF_SB_UDFREV ( inode - > i_sb ) > = 0x0201 )
{
struct allocExtDesc * aed = ( struct allocExtDesc * ) ( bh ) - > b_data ;
udf_update_tag ( ( bh ) - > b_data ,
le32_to_cpu ( aed - > lengthAllocDescs ) + sizeof ( struct allocExtDesc ) ) ;
}
mark_buffer_dirty_inode ( bh , inode ) ;
udf_release_data ( bh ) ;
}
else
mark_inode_dirty ( inode ) ;
if ( inc )
* extoffset + = adsize ;
return ( elen > > 30 ) ;
}
int8_t udf_next_aext ( struct inode * inode , kernel_lb_addr * bloc , int * extoffset ,
kernel_lb_addr * eloc , uint32_t * elen , struct buffer_head * * bh , int inc )
{
int8_t etype ;
while ( ( etype = udf_current_aext ( inode , bloc , extoffset , eloc , elen , bh , inc ) ) = =
( EXT_NEXT_EXTENT_ALLOCDECS > > 30 ) )
{
* bloc = * eloc ;
* extoffset = sizeof ( struct allocExtDesc ) ;
udf_release_data ( * bh ) ;
if ( ! ( * bh = udf_tread ( inode - > i_sb , udf_get_lb_pblock ( inode - > i_sb , * bloc , 0 ) ) ) )
{
udf_debug ( " reading block %d failed! \n " ,
udf_get_lb_pblock ( inode - > i_sb , * bloc , 0 ) ) ;
return - 1 ;
}
}
return etype ;
}
int8_t udf_current_aext ( struct inode * inode , kernel_lb_addr * bloc , int * extoffset ,
kernel_lb_addr * eloc , uint32_t * elen , struct buffer_head * * bh , int inc )
{
int alen ;
int8_t etype ;
uint8_t * ptr ;
if ( ! * bh )
{
if ( ! ( * extoffset ) )
* extoffset = udf_file_entry_alloc_offset ( inode ) ;
ptr = UDF_I_DATA ( inode ) + * extoffset - udf_file_entry_alloc_offset ( inode ) + UDF_I_LENEATTR ( inode ) ;
alen = udf_file_entry_alloc_offset ( inode ) + UDF_I_LENALLOC ( inode ) ;
}
else
{
if ( ! ( * extoffset ) )
* extoffset = sizeof ( struct allocExtDesc ) ;
ptr = ( * bh ) - > b_data + * extoffset ;
alen = sizeof ( struct allocExtDesc ) + le32_to_cpu ( ( ( struct allocExtDesc * ) ( * bh ) - > b_data ) - > lengthAllocDescs ) ;
}
switch ( UDF_I_ALLOCTYPE ( inode ) )
{
case ICBTAG_FLAG_AD_SHORT :
{
short_ad * sad ;
if ( ! ( sad = udf_get_fileshortad ( ptr , alen , extoffset , inc ) ) )
return - 1 ;
etype = le32_to_cpu ( sad - > extLength ) > > 30 ;
eloc - > logicalBlockNum = le32_to_cpu ( sad - > extPosition ) ;
eloc - > partitionReferenceNum = UDF_I_LOCATION ( inode ) . partitionReferenceNum ;
* elen = le32_to_cpu ( sad - > extLength ) & UDF_EXTENT_LENGTH_MASK ;
break ;
}
case ICBTAG_FLAG_AD_LONG :
{
long_ad * lad ;
if ( ! ( lad = udf_get_filelongad ( ptr , alen , extoffset , inc ) ) )
return - 1 ;
etype = le32_to_cpu ( lad - > extLength ) > > 30 ;
* eloc = lelb_to_cpu ( lad - > extLocation ) ;
* elen = le32_to_cpu ( lad - > extLength ) & UDF_EXTENT_LENGTH_MASK ;
break ;
}
default :
{
udf_debug ( " alloc_type = %d unsupported \n " , UDF_I_ALLOCTYPE ( inode ) ) ;
return - 1 ;
}
}
return etype ;
}
static int8_t
udf_insert_aext ( struct inode * inode , kernel_lb_addr bloc , int extoffset ,
kernel_lb_addr neloc , uint32_t nelen , struct buffer_head * bh )
{
kernel_lb_addr oeloc ;
uint32_t oelen ;
int8_t etype ;
if ( bh )
atomic_inc ( & bh - > b_count ) ;
while ( ( etype = udf_next_aext ( inode , & bloc , & extoffset , & oeloc , & oelen , & bh , 0 ) ) ! = - 1 )
{
udf_write_aext ( inode , bloc , & extoffset , neloc , nelen , bh , 1 ) ;
neloc = oeloc ;
nelen = ( etype < < 30 ) | oelen ;
}
udf_add_aext ( inode , & bloc , & extoffset , neloc , nelen , & bh , 1 ) ;
udf_release_data ( bh ) ;
return ( nelen > > 30 ) ;
}
int8_t udf_delete_aext ( struct inode * inode , kernel_lb_addr nbloc , int nextoffset ,
kernel_lb_addr eloc , uint32_t elen , struct buffer_head * nbh )
{
struct buffer_head * obh ;
kernel_lb_addr obloc ;
int oextoffset , adsize ;
int8_t etype ;
struct allocExtDesc * aed ;
if ( nbh )
{
atomic_inc ( & nbh - > b_count ) ;
atomic_inc ( & nbh - > b_count ) ;
}
if ( UDF_I_ALLOCTYPE ( inode ) = = ICBTAG_FLAG_AD_SHORT )
adsize = sizeof ( short_ad ) ;
else if ( UDF_I_ALLOCTYPE ( inode ) = = ICBTAG_FLAG_AD_LONG )
adsize = sizeof ( long_ad ) ;
else
adsize = 0 ;
obh = nbh ;
obloc = nbloc ;
oextoffset = nextoffset ;
if ( udf_next_aext ( inode , & nbloc , & nextoffset , & eloc , & elen , & nbh , 1 ) = = - 1 )
return - 1 ;
while ( ( etype = udf_next_aext ( inode , & nbloc , & nextoffset , & eloc , & elen , & nbh , 1 ) ) ! = - 1 )
{
udf_write_aext ( inode , obloc , & oextoffset , eloc , ( etype < < 30 ) | elen , obh , 1 ) ;
if ( obh ! = nbh )
{
obloc = nbloc ;
udf_release_data ( obh ) ;
atomic_inc ( & nbh - > b_count ) ;
obh = nbh ;
oextoffset = nextoffset - adsize ;
}
}
memset ( & eloc , 0x00 , sizeof ( kernel_lb_addr ) ) ;
elen = 0 ;
if ( nbh ! = obh )
{
udf_free_blocks ( inode - > i_sb , inode , nbloc , 0 , 1 ) ;
udf_write_aext ( inode , obloc , & oextoffset , eloc , elen , obh , 1 ) ;
udf_write_aext ( inode , obloc , & oextoffset , eloc , elen , obh , 1 ) ;
if ( ! obh )
{
UDF_I_LENALLOC ( inode ) - = ( adsize * 2 ) ;
mark_inode_dirty ( inode ) ;
}
else
{
aed = ( struct allocExtDesc * ) ( obh ) - > b_data ;
aed - > lengthAllocDescs =
cpu_to_le32 ( le32_to_cpu ( aed - > lengthAllocDescs ) - ( 2 * adsize ) ) ;
if ( ! UDF_QUERY_FLAG ( inode - > i_sb , UDF_FLAG_STRICT ) | | UDF_SB_UDFREV ( inode - > i_sb ) > = 0x0201 )
udf_update_tag ( ( obh ) - > b_data , oextoffset - ( 2 * adsize ) ) ;
else
udf_update_tag ( ( obh ) - > b_data , sizeof ( struct allocExtDesc ) ) ;
mark_buffer_dirty_inode ( obh , inode ) ;
}
}
else
{
udf_write_aext ( inode , obloc , & oextoffset , eloc , elen , obh , 1 ) ;
if ( ! obh )
{
UDF_I_LENALLOC ( inode ) - = adsize ;
mark_inode_dirty ( inode ) ;
}
else
{
aed = ( struct allocExtDesc * ) ( obh ) - > b_data ;
aed - > lengthAllocDescs =
cpu_to_le32 ( le32_to_cpu ( aed - > lengthAllocDescs ) - adsize ) ;
if ( ! UDF_QUERY_FLAG ( inode - > i_sb , UDF_FLAG_STRICT ) | | UDF_SB_UDFREV ( inode - > i_sb ) > = 0x0201 )
udf_update_tag ( ( obh ) - > b_data , oextoffset - adsize ) ;
else
udf_update_tag ( ( obh ) - > b_data , sizeof ( struct allocExtDesc ) ) ;
mark_buffer_dirty_inode ( obh , inode ) ;
}
}
udf_release_data ( nbh ) ;
udf_release_data ( obh ) ;
return ( elen > > 30 ) ;
}
int8_t inode_bmap ( struct inode * inode , int block , kernel_lb_addr * bloc , uint32_t * extoffset ,
kernel_lb_addr * eloc , uint32_t * elen , uint32_t * offset , struct buffer_head * * bh )
{
uint64_t lbcount = 0 , bcount = ( uint64_t ) block < < inode - > i_sb - > s_blocksize_bits ;
int8_t etype ;
if ( block < 0 )
{
printk ( KERN_ERR " udf: inode_bmap: block < 0 \n " ) ;
return - 1 ;
}
if ( ! inode )
{
printk ( KERN_ERR " udf: inode_bmap: NULL inode \n " ) ;
return - 1 ;
}
* extoffset = 0 ;
* elen = 0 ;
* bloc = UDF_I_LOCATION ( inode ) ;
do
{
if ( ( etype = udf_next_aext ( inode , bloc , extoffset , eloc , elen , bh , 1 ) ) = = - 1 )
{
* offset = bcount - lbcount ;
UDF_I_LENEXTENTS ( inode ) = lbcount ;
return - 1 ;
}
lbcount + = * elen ;
} while ( lbcount < = bcount ) ;
* offset = bcount + * elen - lbcount ;
return etype ;
}
long udf_block_map ( struct inode * inode , long block )
{
kernel_lb_addr eloc , bloc ;
uint32_t offset , extoffset , elen ;
struct buffer_head * bh = NULL ;
int ret ;
lock_kernel ( ) ;
if ( inode_bmap ( inode , block , & bloc , & extoffset , & eloc , & elen , & offset , & bh ) = = ( EXT_RECORDED_ALLOCATED > > 30 ) )
ret = udf_get_lb_pblock ( inode - > i_sb , eloc , offset > > inode - > i_sb - > s_blocksize_bits ) ;
else
ret = 0 ;
unlock_kernel ( ) ;
udf_release_data ( bh ) ;
if ( UDF_QUERY_FLAG ( inode - > i_sb , UDF_FLAG_VARCONV ) )
return udf_fixed_to_variable ( ret ) ;
else
return ret ;
}