2005-04-17 02:20:36 +04:00
/*
* partition . c
*
* PURPOSE
* Partition handling 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 - 2001 Ben Fennema
*
* HISTORY
*
2007-07-21 15:37:18 +04:00
* 12 / 06 / 98 blf Created file .
2005-04-17 02:20:36 +04:00
*
*/
# include "udfdecl.h"
# include "udf_sb.h"
# include "udf_i.h"
# include <linux/fs.h>
# include <linux/string.h>
# include <linux/buffer_head.h>
2010-11-16 20:40:48 +03:00
# include <linux/mutex.h>
2005-04-17 02:20:36 +04:00
2008-04-28 19:38:49 +04:00
uint32_t udf_get_pblock ( struct super_block * sb , uint32_t block ,
uint16_t partition , uint32_t offset )
2005-04-17 02:20:36 +04:00
{
2008-02-08 15:20:30 +03:00
struct udf_sb_info * sbi = UDF_SB ( sb ) ;
struct udf_part_map * map ;
if ( partition > = sbi - > s_partitions ) {
2011-10-10 12:08:07 +04:00
udf_debug ( " block=%d, partition=%d, offset=%d: invalid partition \n " ,
block , partition , offset ) ;
2005-04-17 02:20:36 +04:00
return 0xFFFFFFFF ;
}
2008-02-08 15:20:30 +03:00
map = & sbi - > s_partmaps [ partition ] ;
if ( map - > s_partition_func )
return map - > s_partition_func ( sb , block , partition , offset ) ;
2005-04-17 02:20:36 +04:00
else
2008-02-08 15:20:30 +03:00
return map - > s_partition_root + block + offset ;
2005-04-17 02:20:36 +04:00
}
2007-07-21 15:37:18 +04:00
uint32_t udf_get_pblock_virt15 ( struct super_block * sb , uint32_t block ,
2007-07-19 12:47:43 +04:00
uint16_t partition , uint32_t offset )
2005-04-17 02:20:36 +04:00
{
struct buffer_head * bh = NULL ;
uint32_t newblock ;
uint32_t index ;
uint32_t loc ;
2008-02-08 15:20:30 +03:00
struct udf_sb_info * sbi = UDF_SB ( sb ) ;
struct udf_part_map * map ;
2008-02-08 15:20:36 +03:00
struct udf_virtual_data * vdata ;
2008-04-08 04:08:53 +04:00
struct udf_inode_info * iinfo = UDF_I ( sbi - > s_vat_inode ) ;
2005-04-17 02:20:36 +04:00
2008-02-08 15:20:30 +03:00
map = & sbi - > s_partmaps [ partition ] ;
2008-02-08 15:20:36 +03:00
vdata = & map - > s_type_specific . s_virtual ;
2005-04-17 02:20:36 +04:00
2008-02-08 15:20:36 +03:00
if ( block > vdata - > s_num_entries ) {
2011-10-10 12:08:07 +04:00
udf_debug ( " Trying to access block beyond end of VAT (%d max %d) \n " ,
block , vdata - > s_num_entries ) ;
2005-04-17 02:20:36 +04:00
return 0xFFFFFFFF ;
}
2008-04-08 04:08:53 +04:00
if ( iinfo - > i_alloc_type = = ICBTAG_FLAG_AD_IN_ICB ) {
2008-04-14 19:06:36 +04:00
loc = le32_to_cpu ( ( ( __le32 * ) ( iinfo - > i_ext . i_data +
vdata - > s_start_offset ) ) [ block ] ) ;
2008-04-08 04:08:53 +04:00
goto translate ;
}
index = ( sb - > s_blocksize - vdata - > s_start_offset ) / sizeof ( uint32_t ) ;
2007-07-19 12:47:43 +04:00
if ( block > = index ) {
2005-04-17 02:20:36 +04:00
block - = index ;
newblock = 1 + ( block / ( sb - > s_blocksize / sizeof ( uint32_t ) ) ) ;
index = block % ( sb - > s_blocksize / sizeof ( uint32_t ) ) ;
2007-07-19 12:47:43 +04:00
} else {
2005-04-17 02:20:36 +04:00
newblock = 0 ;
2008-02-08 15:20:36 +03:00
index = vdata - > s_start_offset / sizeof ( uint32_t ) + block ;
2005-04-17 02:20:36 +04:00
}
2008-02-08 15:20:30 +03:00
loc = udf_block_map ( sbi - > s_vat_inode , newblock ) ;
2005-04-17 02:20:36 +04:00
2008-02-08 15:20:36 +03:00
bh = sb_bread ( sb , loc ) ;
if ( ! bh ) {
2005-04-17 02:20:36 +04:00
udf_debug ( " get_pblock(UDF_VIRTUAL_MAP:%p,%d,%d) VAT: %d[%d] \n " ,
2007-07-19 12:47:43 +04:00
sb , block , partition , loc , index ) ;
2005-04-17 02:20:36 +04:00
return 0xFFFFFFFF ;
}
2007-07-21 15:37:18 +04:00
loc = le32_to_cpu ( ( ( __le32 * ) bh - > b_data ) [ index ] ) ;
2005-04-17 02:20:36 +04:00
2007-05-08 11:35:16 +04:00
brelse ( bh ) ;
2005-04-17 02:20:36 +04:00
2008-04-08 04:08:53 +04:00
translate :
2008-02-08 15:20:44 +03:00
if ( iinfo - > i_location . partitionReferenceNum = = partition ) {
2005-04-17 02:20:36 +04:00
udf_debug ( " recursive call to udf_get_pblock! \n " ) ;
return 0xFFFFFFFF ;
}
2007-07-19 12:47:43 +04:00
return udf_get_pblock ( sb , loc ,
2008-02-08 15:20:44 +03:00
iinfo - > i_location . partitionReferenceNum ,
2007-07-21 15:37:18 +04:00
offset ) ;
2005-04-17 02:20:36 +04:00
}
2008-02-08 15:20:36 +03:00
inline uint32_t udf_get_pblock_virt20 ( struct super_block * sb , uint32_t block ,
2007-07-19 12:47:43 +04:00
uint16_t partition , uint32_t offset )
2005-04-17 02:20:36 +04:00
{
return udf_get_pblock_virt15 ( sb , block , partition , offset ) ;
}
2008-02-08 15:20:30 +03:00
uint32_t udf_get_pblock_spar15 ( struct super_block * sb , uint32_t block ,
2007-07-19 12:47:43 +04:00
uint16_t partition , uint32_t offset )
2005-04-17 02:20:36 +04:00
{
int i ;
struct sparingTable * st = NULL ;
2008-02-08 15:20:30 +03:00
struct udf_sb_info * sbi = UDF_SB ( sb ) ;
struct udf_part_map * map ;
uint32_t packet ;
2008-02-08 15:20:36 +03:00
struct udf_sparing_data * sdata ;
2008-02-08 15:20:30 +03:00
map = & sbi - > s_partmaps [ partition ] ;
2008-02-08 15:20:36 +03:00
sdata = & map - > s_type_specific . s_sparing ;
packet = ( block + offset ) & ~ ( sdata - > s_packet_len - 1 ) ;
2005-04-17 02:20:36 +04:00
2007-07-19 12:47:43 +04:00
for ( i = 0 ; i < 4 ; i + + ) {
2008-02-08 15:20:36 +03:00
if ( sdata - > s_spar_map [ i ] ! = NULL ) {
st = ( struct sparingTable * )
sdata - > s_spar_map [ i ] - > b_data ;
2005-04-17 02:20:36 +04:00
break ;
}
}
2007-07-19 12:47:43 +04:00
if ( st ) {
for ( i = 0 ; i < le16_to_cpu ( st - > reallocationTableLen ) ; i + + ) {
2008-02-08 15:20:36 +03:00
struct sparingEntry * entry = & st - > mapEntry [ i ] ;
u32 origLoc = le32_to_cpu ( entry - > origLocation ) ;
if ( origLoc > = 0xFFFFFFF0 )
2005-04-17 02:20:36 +04:00
break ;
2008-02-08 15:20:36 +03:00
else if ( origLoc = = packet )
return le32_to_cpu ( entry - > mappedLocation ) +
( ( block + offset ) &
( sdata - > s_packet_len - 1 ) ) ;
else if ( origLoc > packet )
2005-04-17 02:20:36 +04:00
break ;
}
}
2007-07-21 15:37:18 +04:00
2008-02-08 15:20:30 +03:00
return map - > s_partition_root + block + offset ;
2005-04-17 02:20:36 +04:00
}
int udf_relocate_blocks ( struct super_block * sb , long old_block , long * new_block )
{
struct udf_sparing_data * sdata ;
struct sparingTable * st = NULL ;
struct sparingEntry mapEntry ;
uint32_t packet ;
int i , j , k , l ;
2008-02-08 15:20:30 +03:00
struct udf_sb_info * sbi = UDF_SB ( sb ) ;
2008-02-08 15:20:36 +03:00
u16 reallocationTableLen ;
struct buffer_head * bh ;
2010-11-16 20:40:48 +03:00
int ret = 0 ;
2005-04-17 02:20:36 +04:00
2010-11-16 20:40:48 +03:00
mutex_lock ( & sbi - > s_alloc_mutex ) ;
2008-02-08 15:20:30 +03:00
for ( i = 0 ; i < sbi - > s_partitions ; i + + ) {
struct udf_part_map * map = & sbi - > s_partmaps [ i ] ;
if ( old_block > map - > s_partition_root & &
old_block < map - > s_partition_root + map - > s_partition_len ) {
sdata = & map - > s_type_specific . s_sparing ;
2008-02-08 15:20:36 +03:00
packet = ( old_block - map - > s_partition_root ) &
~ ( sdata - > s_packet_len - 1 ) ;
2007-07-19 12:47:43 +04:00
2008-02-08 15:20:36 +03:00
for ( j = 0 ; j < 4 ; j + + )
if ( sdata - > s_spar_map [ j ] ! = NULL ) {
st = ( struct sparingTable * )
sdata - > s_spar_map [ j ] - > b_data ;
2005-04-17 02:20:36 +04:00
break ;
}
2010-11-16 20:40:48 +03:00
if ( ! st ) {
ret = 1 ;
goto out ;
}
2005-04-17 02:20:36 +04:00
2008-02-08 15:20:36 +03:00
reallocationTableLen =
le16_to_cpu ( st - > reallocationTableLen ) ;
for ( k = 0 ; k < reallocationTableLen ; k + + ) {
struct sparingEntry * entry = & st - > mapEntry [ k ] ;
u32 origLoc = le32_to_cpu ( entry - > origLocation ) ;
if ( origLoc = = 0xFFFFFFFF ) {
2007-07-19 12:47:43 +04:00
for ( ; j < 4 ; j + + ) {
2008-02-08 15:20:36 +03:00
int len ;
bh = sdata - > s_spar_map [ j ] ;
if ( ! bh )
continue ;
st = ( struct sparingTable * )
bh - > b_data ;
entry - > origLocation =
cpu_to_le32 ( packet ) ;
len =
sizeof ( struct sparingTable ) +
reallocationTableLen *
sizeof ( struct sparingEntry ) ;
udf_update_tag ( ( char * ) st , len ) ;
mark_buffer_dirty ( bh ) ;
2005-04-17 02:20:36 +04:00
}
2008-02-08 15:20:36 +03:00
* new_block = le32_to_cpu (
entry - > mappedLocation ) +
( ( old_block -
map - > s_partition_root ) &
( sdata - > s_packet_len - 1 ) ) ;
2010-11-16 20:40:48 +03:00
ret = 0 ;
goto out ;
2008-02-08 15:20:36 +03:00
} else if ( origLoc = = packet ) {
* new_block = le32_to_cpu (
entry - > mappedLocation ) +
( ( old_block -
map - > s_partition_root ) &
( sdata - > s_packet_len - 1 ) ) ;
2010-11-16 20:40:48 +03:00
ret = 0 ;
goto out ;
2008-02-08 15:20:36 +03:00
} else if ( origLoc > packet )
2005-04-17 02:20:36 +04:00
break ;
}
2007-07-21 15:37:18 +04:00
2008-02-08 15:20:36 +03:00
for ( l = k ; l < reallocationTableLen ; l + + ) {
struct sparingEntry * entry = & st - > mapEntry [ l ] ;
u32 origLoc = le32_to_cpu ( entry - > origLocation ) ;
if ( origLoc ! = 0xFFFFFFFF )
continue ;
for ( ; j < 4 ; j + + ) {
bh = sdata - > s_spar_map [ j ] ;
if ( ! bh )
continue ;
st = ( struct sparingTable * ) bh - > b_data ;
mapEntry = st - > mapEntry [ l ] ;
mapEntry . origLocation =
cpu_to_le32 ( packet ) ;
memmove ( & st - > mapEntry [ k + 1 ] ,
& st - > mapEntry [ k ] ,
( l - k ) *
sizeof ( struct sparingEntry ) ) ;
st - > mapEntry [ k ] = mapEntry ;
udf_update_tag ( ( char * ) st ,
sizeof ( struct sparingTable ) +
reallocationTableLen *
sizeof ( struct sparingEntry ) ) ;
mark_buffer_dirty ( bh ) ;
2005-04-17 02:20:36 +04:00
}
2008-02-08 15:20:36 +03:00
* new_block =
le32_to_cpu (
st - > mapEntry [ k ] . mappedLocation ) +
( ( old_block - map - > s_partition_root ) &
( sdata - > s_packet_len - 1 ) ) ;
2010-11-16 20:40:48 +03:00
ret = 0 ;
goto out ;
2005-04-17 02:20:36 +04:00
}
2007-07-21 15:37:18 +04:00
2010-11-16 20:40:48 +03:00
ret = 1 ;
goto out ;
2007-07-21 15:37:18 +04:00
} /* if old_block */
2005-04-17 02:20:36 +04:00
}
2007-07-21 15:37:18 +04:00
2008-02-08 15:20:30 +03:00
if ( i = = sbi - > s_partitions ) {
2005-04-17 02:20:36 +04:00
/* outside of partitions */
/* for now, fail =) */
2010-11-16 20:40:48 +03:00
ret = 1 ;
2005-04-17 02:20:36 +04:00
}
2010-11-16 20:40:48 +03:00
out :
mutex_unlock ( & sbi - > s_alloc_mutex ) ;
return ret ;
2005-04-17 02:20:36 +04:00
}
2008-04-08 22:37:21 +04:00
static uint32_t udf_try_read_meta ( struct inode * inode , uint32_t block ,
uint16_t partition , uint32_t offset )
{
struct super_block * sb = inode - > i_sb ;
struct udf_part_map * map ;
2008-10-15 14:28:03 +04:00
struct kernel_lb_addr eloc ;
2008-04-08 22:37:21 +04:00
uint32_t elen ;
sector_t ext_offset ;
struct extent_position epos = { } ;
uint32_t phyblock ;
if ( inode_bmap ( inode , block , & epos , & eloc , & elen , & ext_offset ) ! =
( EXT_RECORDED_ALLOCATED > > 30 ) )
phyblock = 0xFFFFFFFF ;
else {
map = & UDF_SB ( sb ) - > s_partmaps [ partition ] ;
/* map to sparable/physical partition desc */
phyblock = udf_get_pblock ( sb , eloc . logicalBlockNum ,
map - > s_partition_num , ext_offset + offset ) ;
}
brelse ( epos . bh ) ;
return phyblock ;
}
uint32_t udf_get_pblock_meta25 ( struct super_block * sb , uint32_t block ,
uint16_t partition , uint32_t offset )
{
struct udf_sb_info * sbi = UDF_SB ( sb ) ;
struct udf_part_map * map ;
struct udf_meta_data * mdata ;
uint32_t retblk ;
struct inode * inode ;
udf_debug ( " READING from METADATA \n " ) ;
map = & sbi - > s_partmaps [ partition ] ;
mdata = & map - > s_type_specific . s_metadata ;
inode = mdata - > s_metadata_fe ? : mdata - > s_mirror_fe ;
/* We shouldn't mount such media... */
BUG_ON ( ! inode ) ;
retblk = udf_try_read_meta ( inode , block , partition , offset ) ;
2011-10-23 14:28:32 +04:00
if ( retblk = = 0xFFFFFFFF & & mdata - > s_metadata_fe ) {
2011-10-10 12:08:04 +04:00
udf_warn ( sb , " error reading from METADATA, trying to read from MIRROR \n " ) ;
2011-10-24 18:47:48 +04:00
if ( ! ( mdata - > s_flags & MF_MIRROR_FE_LOADED ) ) {
2011-10-23 14:28:32 +04:00
mdata - > s_mirror_fe = udf_find_metadata_inode_efe ( sb ,
mdata - > s_mirror_file_loc , map - > s_partition_num ) ;
2011-10-24 18:47:48 +04:00
mdata - > s_flags | = MF_MIRROR_FE_LOADED ;
2011-10-23 14:28:32 +04:00
}
2008-04-08 22:37:21 +04:00
inode = mdata - > s_mirror_fe ;
if ( ! inode )
return 0xFFFFFFFF ;
retblk = udf_try_read_meta ( inode , block , partition , offset ) ;
}
return retblk ;
}