2005-04-16 15:20:36 -07:00
/*
* linux / fs / hpfs / anode . c
*
* Mikulas Patocka ( mikulas @ artax . karlin . mff . cuni . cz ) , 1998 - 1999
*
* handling HPFS anode tree that contains file allocation info
*/
# include "hpfs_fn.h"
/* Find a sector in allocation tree */
secno hpfs_bplus_lookup ( struct super_block * s , struct inode * inode ,
struct bplus_header * btree , unsigned sec ,
struct buffer_head * bh )
{
anode_secno a = - 1 ;
struct anode * anode ;
int i ;
int c1 , c2 = 0 ;
go_down :
if ( hpfs_sb ( s ) - > sb_chk ) if ( hpfs_stop_cycles ( s , a , & c1 , & c2 , " hpfs_bplus_lookup " ) ) return - 1 ;
if ( btree - > internal ) {
for ( i = 0 ; i < btree - > n_used_nodes ; i + + )
if ( btree - > u . internal [ i ] . file_secno > sec ) {
a = btree - > u . internal [ i ] . down ;
brelse ( bh ) ;
if ( ! ( anode = hpfs_map_anode ( s , a , & bh ) ) ) return - 1 ;
btree = & anode - > btree ;
goto go_down ;
}
hpfs_error ( s , " sector %08x not found in internal anode %08x " , sec , a ) ;
brelse ( bh ) ;
return - 1 ;
}
for ( i = 0 ; i < btree - > n_used_nodes ; i + + )
if ( btree - > u . external [ i ] . file_secno < = sec & &
btree - > u . external [ i ] . file_secno + btree - > u . external [ i ] . length > sec ) {
a = btree - > u . external [ i ] . disk_secno + sec - btree - > u . external [ i ] . file_secno ;
if ( hpfs_sb ( s ) - > sb_chk ) if ( hpfs_chk_sectors ( s , a , 1 , " data " ) ) {
brelse ( bh ) ;
return - 1 ;
}
if ( inode ) {
struct hpfs_inode_info * hpfs_inode = hpfs_i ( inode ) ;
hpfs_inode - > i_file_sec = btree - > u . external [ i ] . file_secno ;
hpfs_inode - > i_disk_sec = btree - > u . external [ i ] . disk_secno ;
hpfs_inode - > i_n_secs = btree - > u . external [ i ] . length ;
}
brelse ( bh ) ;
return a ;
}
hpfs_error ( s , " sector %08x not found in external anode %08x " , sec , a ) ;
brelse ( bh ) ;
return - 1 ;
}
/* Add a sector to tree */
secno hpfs_add_sector_to_btree ( struct super_block * s , secno node , int fnod , unsigned fsecno )
{
struct bplus_header * btree ;
struct anode * anode = NULL , * ranode = NULL ;
struct fnode * fnode ;
anode_secno a , na = - 1 , ra , up = - 1 ;
secno se ;
struct buffer_head * bh , * bh1 , * bh2 ;
int n ;
unsigned fs ;
int c1 , c2 = 0 ;
if ( fnod ) {
if ( ! ( fnode = hpfs_map_fnode ( s , node , & bh ) ) ) return - 1 ;
btree = & fnode - > btree ;
} else {
if ( ! ( anode = hpfs_map_anode ( s , node , & bh ) ) ) return - 1 ;
btree = & anode - > btree ;
}
a = node ;
go_down :
if ( ( n = btree - > n_used_nodes - 1 ) < - ! ! fnod ) {
hpfs_error ( s , " anode %08x has no entries " , a ) ;
brelse ( bh ) ;
return - 1 ;
}
if ( btree - > internal ) {
a = btree - > u . internal [ n ] . down ;
btree - > u . internal [ n ] . file_secno = - 1 ;
mark_buffer_dirty ( bh ) ;
brelse ( bh ) ;
if ( hpfs_sb ( s ) - > sb_chk )
if ( hpfs_stop_cycles ( s , a , & c1 , & c2 , " hpfs_add_sector_to_btree #1 " ) ) return - 1 ;
if ( ! ( anode = hpfs_map_anode ( s , a , & bh ) ) ) return - 1 ;
btree = & anode - > btree ;
goto go_down ;
}
if ( n > = 0 ) {
if ( btree - > u . external [ n ] . file_secno + btree - > u . external [ n ] . length ! = fsecno ) {
hpfs_error ( s , " allocated size %08x, trying to add sector %08x, %cnode %08x " ,
btree - > u . external [ n ] . file_secno + btree - > u . external [ n ] . length , fsecno ,
fnod ? ' f ' : ' a ' , node ) ;
brelse ( bh ) ;
return - 1 ;
}
if ( hpfs_alloc_if_possible ( s , se = btree - > u . external [ n ] . disk_secno + btree - > u . external [ n ] . length ) ) {
btree - > u . external [ n ] . length + + ;
mark_buffer_dirty ( bh ) ;
brelse ( bh ) ;
return se ;
}
} else {
if ( fsecno ) {
hpfs_error ( s , " empty file %08x, trying to add sector %08x " , node , fsecno ) ;
brelse ( bh ) ;
return - 1 ;
}
se = ! fnod ? node : ( node + 16384 ) & ~ 16383 ;
}
if ( ! ( se = hpfs_alloc_sector ( s , se , 1 , fsecno * ALLOC_M > ALLOC_FWD_MAX ? ALLOC_FWD_MAX : fsecno * ALLOC_M < ALLOC_FWD_MIN ? ALLOC_FWD_MIN : fsecno * ALLOC_M , 1 ) ) ) {
brelse ( bh ) ;
return - 1 ;
}
fs = n < 0 ? 0 : btree - > u . external [ n ] . file_secno + btree - > u . external [ n ] . length ;
if ( ! btree - > n_free_nodes ) {
up = a ! = node ? anode - > up : - 1 ;
if ( ! ( anode = hpfs_alloc_anode ( s , a , & na , & bh1 ) ) ) {
brelse ( bh ) ;
hpfs_free_sectors ( s , se , 1 ) ;
return - 1 ;
}
if ( a = = node & & fnod ) {
anode - > up = node ;
anode - > btree . fnode_parent = 1 ;
anode - > btree . n_used_nodes = btree - > n_used_nodes ;
anode - > btree . first_free = btree - > first_free ;
anode - > btree . n_free_nodes = 40 - anode - > btree . n_used_nodes ;
memcpy ( & anode - > u , & btree - > u , btree - > n_used_nodes * 12 ) ;
btree - > internal = 1 ;
btree - > n_free_nodes = 11 ;
btree - > n_used_nodes = 1 ;
btree - > first_free = ( char * ) & ( btree - > u . internal [ 1 ] ) - ( char * ) btree ;
btree - > u . internal [ 0 ] . file_secno = - 1 ;
btree - > u . internal [ 0 ] . down = na ;
mark_buffer_dirty ( bh ) ;
} else if ( ! ( ranode = hpfs_alloc_anode ( s , /*a*/ 0 , & ra , & bh2 ) ) ) {
brelse ( bh ) ;
brelse ( bh1 ) ;
hpfs_free_sectors ( s , se , 1 ) ;
hpfs_free_sectors ( s , na , 1 ) ;
return - 1 ;
}
brelse ( bh ) ;
bh = bh1 ;
btree = & anode - > btree ;
}
btree - > n_free_nodes - - ; n = btree - > n_used_nodes + + ;
btree - > first_free + = 12 ;
btree - > u . external [ n ] . disk_secno = se ;
btree - > u . external [ n ] . file_secno = fs ;
btree - > u . external [ n ] . length = 1 ;
mark_buffer_dirty ( bh ) ;
brelse ( bh ) ;
if ( ( a = = node & & fnod ) | | na = = - 1 ) return se ;
c2 = 0 ;
while ( up ! = - 1 ) {
struct anode * new_anode ;
if ( hpfs_sb ( s ) - > sb_chk )
if ( hpfs_stop_cycles ( s , up , & c1 , & c2 , " hpfs_add_sector_to_btree #2 " ) ) return - 1 ;
if ( up ! = node | | ! fnod ) {
if ( ! ( anode = hpfs_map_anode ( s , up , & bh ) ) ) return - 1 ;
btree = & anode - > btree ;
} else {
if ( ! ( fnode = hpfs_map_fnode ( s , up , & bh ) ) ) return - 1 ;
btree = & fnode - > btree ;
}
if ( btree - > n_free_nodes ) {
btree - > n_free_nodes - - ; n = btree - > n_used_nodes + + ;
btree - > first_free + = 8 ;
btree - > u . internal [ n ] . file_secno = - 1 ;
btree - > u . internal [ n ] . down = na ;
btree - > u . internal [ n - 1 ] . file_secno = fs ;
mark_buffer_dirty ( bh ) ;
brelse ( bh ) ;
brelse ( bh2 ) ;
hpfs_free_sectors ( s , ra , 1 ) ;
if ( ( anode = hpfs_map_anode ( s , na , & bh ) ) ) {
anode - > up = up ;
anode - > btree . fnode_parent = up = = node & & fnod ;
mark_buffer_dirty ( bh ) ;
brelse ( bh ) ;
}
return se ;
}
up = up ! = node ? anode - > up : - 1 ;
btree - > u . internal [ btree - > n_used_nodes - 1 ] . file_secno = /*fs*/ - 1 ;
mark_buffer_dirty ( bh ) ;
brelse ( bh ) ;
a = na ;
if ( ( new_anode = hpfs_alloc_anode ( s , a , & na , & bh ) ) ) {
anode = new_anode ;
/*anode->up = up != -1 ? up : ra;*/
anode - > btree . internal = 1 ;
anode - > btree . n_used_nodes = 1 ;
anode - > btree . n_free_nodes = 59 ;
anode - > btree . first_free = 16 ;
anode - > btree . u . internal [ 0 ] . down = a ;
anode - > btree . u . internal [ 0 ] . file_secno = - 1 ;
mark_buffer_dirty ( bh ) ;
brelse ( bh ) ;
if ( ( anode = hpfs_map_anode ( s , a , & bh ) ) ) {
anode - > up = na ;
mark_buffer_dirty ( bh ) ;
brelse ( bh ) ;
}
} else na = a ;
}
if ( ( anode = hpfs_map_anode ( s , na , & bh ) ) ) {
anode - > up = node ;
if ( fnod ) anode - > btree . fnode_parent = 1 ;
mark_buffer_dirty ( bh ) ;
brelse ( bh ) ;
}
if ( ! fnod ) {
if ( ! ( anode = hpfs_map_anode ( s , node , & bh ) ) ) {
brelse ( bh2 ) ;
return - 1 ;
}
btree = & anode - > btree ;
} else {
if ( ! ( fnode = hpfs_map_fnode ( s , node , & bh ) ) ) {
brelse ( bh2 ) ;
return - 1 ;
}
btree = & fnode - > btree ;
}
ranode - > up = node ;
memcpy ( & ranode - > btree , btree , btree - > first_free ) ;
if ( fnod ) ranode - > btree . fnode_parent = 1 ;
ranode - > btree . n_free_nodes = ( ranode - > btree . internal ? 60 : 40 ) - ranode - > btree . n_used_nodes ;
if ( ranode - > btree . internal ) for ( n = 0 ; n < ranode - > btree . n_used_nodes ; n + + ) {
struct anode * unode ;
if ( ( unode = hpfs_map_anode ( s , ranode - > u . internal [ n ] . down , & bh1 ) ) ) {
unode - > up = ra ;
unode - > btree . fnode_parent = 0 ;
mark_buffer_dirty ( bh1 ) ;
brelse ( bh1 ) ;
}
}
btree - > internal = 1 ;
btree - > n_free_nodes = fnod ? 10 : 58 ;
btree - > n_used_nodes = 2 ;
btree - > first_free = ( char * ) & btree - > u . internal [ 2 ] - ( char * ) btree ;
btree - > u . internal [ 0 ] . file_secno = fs ;
btree - > u . internal [ 0 ] . down = ra ;
btree - > u . internal [ 1 ] . file_secno = - 1 ;
btree - > u . internal [ 1 ] . down = na ;
mark_buffer_dirty ( bh ) ;
brelse ( bh ) ;
mark_buffer_dirty ( bh2 ) ;
brelse ( bh2 ) ;
return se ;
}
/*
* Remove allocation tree . Recursion would look much nicer but
* I want to avoid it because it can cause stack overflow .
*/
void hpfs_remove_btree ( struct super_block * s , struct bplus_header * btree )
{
struct bplus_header * btree1 = btree ;
struct anode * anode = NULL ;
anode_secno ano = 0 , oano ;
struct buffer_head * bh ;
int level = 0 ;
int pos = 0 ;
int i ;
int c1 , c2 = 0 ;
int d1 , d2 ;
go_down :
d2 = 0 ;
while ( btree1 - > internal ) {
ano = btree1 - > u . internal [ pos ] . down ;
if ( level ) brelse ( bh ) ;
if ( hpfs_sb ( s ) - > sb_chk )
if ( hpfs_stop_cycles ( s , ano , & d1 , & d2 , " hpfs_remove_btree #1 " ) )
return ;
if ( ! ( anode = hpfs_map_anode ( s , ano , & bh ) ) ) return ;
btree1 = & anode - > btree ;
level + + ;
pos = 0 ;
}
for ( i = 0 ; i < btree1 - > n_used_nodes ; i + + )
hpfs_free_sectors ( s , btree1 - > u . external [ i ] . disk_secno , btree1 - > u . external [ i ] . length ) ;
go_up :
if ( ! level ) return ;
brelse ( bh ) ;
if ( hpfs_sb ( s ) - > sb_chk )
if ( hpfs_stop_cycles ( s , ano , & c1 , & c2 , " hpfs_remove_btree #2 " ) ) return ;
hpfs_free_sectors ( s , ano , 1 ) ;
oano = ano ;
ano = anode - > up ;
if ( - - level ) {
if ( ! ( anode = hpfs_map_anode ( s , ano , & bh ) ) ) return ;
btree1 = & anode - > btree ;
} else btree1 = btree ;
for ( i = 0 ; i < btree1 - > n_used_nodes ; i + + ) {
if ( btree1 - > u . internal [ i ] . down = = oano ) {
if ( ( pos = i + 1 ) < btree1 - > n_used_nodes )
goto go_down ;
else
goto go_up ;
}
}
hpfs_error ( s ,
" reference to anode %08x not found in anode %08x "
" (probably bad up pointer) " ,
oano , level ? ano : - 1 ) ;
if ( level )
brelse ( bh ) ;
}
/* Just a wrapper around hpfs_bplus_lookup .. used for reading eas */
static secno anode_lookup ( struct super_block * s , anode_secno a , unsigned sec )
{
struct anode * anode ;
struct buffer_head * bh ;
if ( ! ( anode = hpfs_map_anode ( s , a , & bh ) ) ) return - 1 ;
return hpfs_bplus_lookup ( s , NULL , & anode - > btree , sec , bh ) ;
}
int hpfs_ea_read ( struct super_block * s , secno a , int ano , unsigned pos ,
unsigned len , char * buf )
{
struct buffer_head * bh ;
char * data ;
secno sec ;
unsigned l ;
while ( len ) {
if ( ano ) {
if ( ( sec = anode_lookup ( s , a , pos > > 9 ) ) = = - 1 )
return - 1 ;
} else sec = a + ( pos > > 9 ) ;
if ( hpfs_sb ( s ) - > sb_chk ) if ( hpfs_chk_sectors ( s , sec , 1 , " ea #1 " ) ) return - 1 ;
if ( ! ( data = hpfs_map_sector ( s , sec , & bh , ( len - 1 ) > > 9 ) ) )
return - 1 ;
l = 0x200 - ( pos & 0x1ff ) ; if ( l > len ) l = len ;
memcpy ( buf , data + ( pos & 0x1ff ) , l ) ;
brelse ( bh ) ;
buf + = l ; pos + = l ; len - = l ;
}
return 0 ;
}
int hpfs_ea_write ( struct super_block * s , secno a , int ano , unsigned pos ,
2010-01-31 17:09:29 -05:00
unsigned len , const char * buf )
2005-04-16 15:20:36 -07:00
{
struct buffer_head * bh ;
char * data ;
secno sec ;
unsigned l ;
while ( len ) {
if ( ano ) {
if ( ( sec = anode_lookup ( s , a , pos > > 9 ) ) = = - 1 )
return - 1 ;
} else sec = a + ( pos > > 9 ) ;
if ( hpfs_sb ( s ) - > sb_chk ) if ( hpfs_chk_sectors ( s , sec , 1 , " ea #2 " ) ) return - 1 ;
if ( ! ( data = hpfs_map_sector ( s , sec , & bh , ( len - 1 ) > > 9 ) ) )
return - 1 ;
l = 0x200 - ( pos & 0x1ff ) ; if ( l > len ) l = len ;
memcpy ( data + ( pos & 0x1ff ) , buf , l ) ;
mark_buffer_dirty ( bh ) ;
brelse ( bh ) ;
buf + = l ; pos + = l ; len - = l ;
}
return 0 ;
}
void hpfs_ea_remove ( struct super_block * s , secno a , int ano , unsigned len )
{
struct anode * anode ;
struct buffer_head * bh ;
if ( ano ) {
if ( ! ( anode = hpfs_map_anode ( s , a , & bh ) ) ) return ;
hpfs_remove_btree ( s , & anode - > btree ) ;
brelse ( bh ) ;
hpfs_free_sectors ( s , a , 1 ) ;
} else hpfs_free_sectors ( s , a , ( len + 511 ) > > 9 ) ;
}
/* Truncate allocation tree. Doesn't join anodes - I hope it doesn't matter */
void hpfs_truncate_btree ( struct super_block * s , secno f , int fno , unsigned secs )
{
struct fnode * fnode ;
struct anode * anode ;
struct buffer_head * bh ;
struct bplus_header * btree ;
anode_secno node = f ;
int i , j , nodes ;
int c1 , c2 = 0 ;
if ( fno ) {
if ( ! ( fnode = hpfs_map_fnode ( s , f , & bh ) ) ) return ;
btree = & fnode - > btree ;
} else {
if ( ! ( anode = hpfs_map_anode ( s , f , & bh ) ) ) return ;
btree = & anode - > btree ;
}
if ( ! secs ) {
hpfs_remove_btree ( s , btree ) ;
if ( fno ) {
btree - > n_free_nodes = 8 ;
btree - > n_used_nodes = 0 ;
btree - > first_free = 8 ;
btree - > internal = 0 ;
mark_buffer_dirty ( bh ) ;
} else hpfs_free_sectors ( s , f , 1 ) ;
brelse ( bh ) ;
return ;
}
while ( btree - > internal ) {
nodes = btree - > n_used_nodes + btree - > n_free_nodes ;
for ( i = 0 ; i < btree - > n_used_nodes ; i + + )
if ( btree - > u . internal [ i ] . file_secno > = secs ) goto f ;
brelse ( bh ) ;
hpfs_error ( s , " internal btree %08x doesn't end with -1 " , node ) ;
return ;
f :
for ( j = i + 1 ; j < btree - > n_used_nodes ; j + + )
hpfs_ea_remove ( s , btree - > u . internal [ j ] . down , 1 , 0 ) ;
btree - > n_used_nodes = i + 1 ;
btree - > n_free_nodes = nodes - btree - > n_used_nodes ;
btree - > first_free = 8 + 8 * btree - > n_used_nodes ;
mark_buffer_dirty ( bh ) ;
if ( btree - > u . internal [ i ] . file_secno = = secs ) {
brelse ( bh ) ;
return ;
}
node = btree - > u . internal [ i ] . down ;
brelse ( bh ) ;
if ( hpfs_sb ( s ) - > sb_chk )
if ( hpfs_stop_cycles ( s , node , & c1 , & c2 , " hpfs_truncate_btree " ) )
return ;
if ( ! ( anode = hpfs_map_anode ( s , node , & bh ) ) ) return ;
btree = & anode - > btree ;
}
nodes = btree - > n_used_nodes + btree - > n_free_nodes ;
for ( i = 0 ; i < btree - > n_used_nodes ; i + + )
if ( btree - > u . external [ i ] . file_secno + btree - > u . external [ i ] . length > = secs ) goto ff ;
brelse ( bh ) ;
return ;
ff :
if ( secs < = btree - > u . external [ i ] . file_secno ) {
hpfs_error ( s , " there is an allocation error in file %08x, sector %08x " , f , secs ) ;
if ( i ) i - - ;
}
else if ( btree - > u . external [ i ] . file_secno + btree - > u . external [ i ] . length > secs ) {
hpfs_free_sectors ( s , btree - > u . external [ i ] . disk_secno + secs -
btree - > u . external [ i ] . file_secno , btree - > u . external [ i ] . length
- secs + btree - > u . external [ i ] . file_secno ) ; /* I hope gcc optimizes this :-) */
btree - > u . external [ i ] . length = secs - btree - > u . external [ i ] . file_secno ;
}
for ( j = i + 1 ; j < btree - > n_used_nodes ; j + + )
hpfs_free_sectors ( s , btree - > u . external [ j ] . disk_secno , btree - > u . external [ j ] . length ) ;
btree - > n_used_nodes = i + 1 ;
btree - > n_free_nodes = nodes - btree - > n_used_nodes ;
btree - > first_free = 8 + 12 * btree - > n_used_nodes ;
mark_buffer_dirty ( bh ) ;
brelse ( bh ) ;
}
/* Remove file or directory and it's eas - note that directory must
be empty when this is called . */
void hpfs_remove_fnode ( struct super_block * s , fnode_secno fno )
{
struct buffer_head * bh ;
struct fnode * fnode ;
struct extended_attribute * ea ;
struct extended_attribute * ea_end ;
if ( ! ( fnode = hpfs_map_fnode ( s , fno , & bh ) ) ) return ;
if ( ! fnode - > dirflag ) hpfs_remove_btree ( s , & fnode - > btree ) ;
else hpfs_remove_dtree ( s , fnode - > u . external [ 0 ] . disk_secno ) ;
ea_end = fnode_end_ea ( fnode ) ;
for ( ea = fnode_ea ( fnode ) ; ea < ea_end ; ea = next_ea ( ea ) )
if ( ea - > indirect )
hpfs_ea_remove ( s , ea_sec ( ea ) , ea - > anode , ea_len ( ea ) ) ;
hpfs_ea_ext_remove ( s , fnode - > ea_secno , fnode - > ea_anode , fnode - > ea_size_l ) ;
brelse ( bh ) ;
hpfs_free_sectors ( s , fno , 1 ) ;
}