2005-04-17 02:20:36 +04:00
/*
* linux / fs / befs / datastream . c
*
* Copyright ( C ) 2001 Will Dyson < will_dyson @ pobox . com >
*
* Based on portions of file . c by Makoto Kato < m_kato @ ga2 . so - net . ne . jp >
*
* Many thanks to Dominic Giampaolo , author of " Practical File System
* Design with the Be File System " , for such a helpful book.
*
*/
# include <linux/kernel.h>
# include <linux/buffer_head.h>
# include <linux/string.h>
# include "befs.h"
# include "datastream.h"
# include "io.h"
const befs_inode_addr BAD_IADDR = { 0 , 0 , 0 } ;
static int befs_find_brun_direct ( struct super_block * sb ,
befs_data_stream * data ,
befs_blocknr_t blockno , befs_block_run * run ) ;
static int befs_find_brun_indirect ( struct super_block * sb ,
befs_data_stream * data ,
befs_blocknr_t blockno ,
befs_block_run * run ) ;
static int befs_find_brun_dblindirect ( struct super_block * sb ,
befs_data_stream * data ,
befs_blocknr_t blockno ,
befs_block_run * run ) ;
/**
* befs_read_datastream - get buffer_head containing data , starting from pos .
* @ sb : Filesystem superblock
* @ ds : datastrem to find data with
* @ pos : start of data
* @ off : offset of data in buffer_head - > b_data
*
* Returns pointer to buffer_head containing data starting with offset @ off ,
* if you don ' t need to know offset just set @ off = NULL .
*/
struct buffer_head *
befs_read_datastream ( struct super_block * sb , befs_data_stream * ds ,
befs_off_t pos , uint * off )
{
struct buffer_head * bh = NULL ;
befs_block_run run ;
befs_blocknr_t block ; /* block coresponding to pos */
2014-04-04 01:50:23 +04:00
befs_debug ( sb , " ---> %s %llu " , __func__ , pos ) ;
2005-04-17 02:20:36 +04:00
block = pos > > BEFS_SB ( sb ) - > block_shift ;
if ( off )
* off = pos - ( block < < BEFS_SB ( sb ) - > block_shift ) ;
if ( befs_fblock2brun ( sb , ds , block , & run ) ! = BEFS_OK ) {
befs_error ( sb , " BeFS: Error finding disk addr of block %lu " ,
2014-04-04 01:50:23 +04:00
( unsigned long ) block ) ;
befs_debug ( sb , " <--- %s ERROR " , __func__ ) ;
2005-04-17 02:20:36 +04:00
return NULL ;
}
bh = befs_bread_iaddr ( sb , run ) ;
if ( ! bh ) {
befs_error ( sb , " BeFS: Error reading block %lu from datastream " ,
2014-04-04 01:50:23 +04:00
( unsigned long ) block ) ;
2005-04-17 02:20:36 +04:00
return NULL ;
}
2014-04-04 01:50:23 +04:00
befs_debug ( sb , " <--- %s read data, starting at %llu " , __func__ , pos ) ;
2005-04-17 02:20:36 +04:00
return bh ;
}
/*
* Takes a file position and gives back a brun who ' s starting block
* is block number fblock of the file .
*
* Returns BEFS_OK or BEFS_ERR .
*
* Calls specialized functions for each of the three possible
* datastream regions .
*
* 2001 - 11 - 15 Will Dyson
*/
int
befs_fblock2brun ( struct super_block * sb , befs_data_stream * data ,
befs_blocknr_t fblock , befs_block_run * run )
{
int err ;
befs_off_t pos = fblock < < BEFS_SB ( sb ) - > block_shift ;
if ( pos < data - > max_direct_range ) {
err = befs_find_brun_direct ( sb , data , fblock , run ) ;
} else if ( pos < data - > max_indirect_range ) {
err = befs_find_brun_indirect ( sb , data , fblock , run ) ;
} else if ( pos < data - > max_double_indirect_range ) {
err = befs_find_brun_dblindirect ( sb , data , fblock , run ) ;
} else {
befs_error ( sb ,
" befs_fblock2brun() was asked to find block %lu, "
2014-04-04 01:50:23 +04:00
" which is not mapped by the datastream \n " ,
( unsigned long ) fblock ) ;
2005-04-17 02:20:36 +04:00
err = BEFS_ERR ;
}
return err ;
}
/**
* befs_read_lsmylink - read long symlink from datastream .
* @ sb : Filesystem superblock
* @ ds : Datastrem to read from
2006-03-24 20:23:14 +03:00
* @ buf : Buffer in which to place long symlink data
2005-04-17 02:20:36 +04:00
* @ len : Length of the long symlink in bytes
*
* Returns the number of bytes read
*/
size_t
befs_read_lsymlink ( struct super_block * sb , befs_data_stream * ds , void * buff ,
befs_off_t len )
{
befs_off_t bytes_read = 0 ; /* bytes readed */
u16 plen ;
struct buffer_head * bh = NULL ;
2014-04-04 01:50:23 +04:00
befs_debug ( sb , " ---> %s length: %llu " , __func__ , len ) ;
2005-04-17 02:20:36 +04:00
while ( bytes_read < len ) {
bh = befs_read_datastream ( sb , ds , bytes_read , NULL ) ;
if ( ! bh ) {
befs_error ( sb , " BeFS: Error reading datastream block "
2014-04-04 01:50:23 +04:00
" starting from %llu " , bytes_read ) ;
befs_debug ( sb , " <--- %s ERROR " , __func__ ) ;
2005-04-17 02:20:36 +04:00
return bytes_read ;
}
plen = ( ( bytes_read + BEFS_SB ( sb ) - > block_size ) < len ) ?
BEFS_SB ( sb ) - > block_size : len - bytes_read ;
memcpy ( buff + bytes_read , bh - > b_data , plen ) ;
brelse ( bh ) ;
bytes_read + = plen ;
}
2014-04-04 01:50:23 +04:00
befs_debug ( sb , " <--- %s read %u bytes " , __func__ , ( unsigned int )
bytes_read ) ;
2005-04-17 02:20:36 +04:00
return bytes_read ;
}
/**
* befs_count_blocks - blocks used by a file
* @ sb : Filesystem superblock
* @ ds : Datastream of the file
*
* Counts the number of fs blocks that the file represented by
* inode occupies on the filesystem , counting both regular file
* data and filesystem metadata ( and eventually attribute data
* when we support attributes )
*/
befs_blocknr_t
befs_count_blocks ( struct super_block * sb , befs_data_stream * ds )
{
befs_blocknr_t blocks ;
befs_blocknr_t datablocks ; /* File data blocks */
befs_blocknr_t metablocks ; /* FS metadata blocks */
befs_sb_info * befs_sb = BEFS_SB ( sb ) ;
2014-04-04 01:50:23 +04:00
befs_debug ( sb , " ---> %s " , __func__ ) ;
2005-04-17 02:20:36 +04:00
datablocks = ds - > size > > befs_sb - > block_shift ;
if ( ds - > size & ( befs_sb - > block_size - 1 ) )
datablocks + = 1 ;
metablocks = 1 ; /* Start with 1 block for inode */
/* Size of indirect block */
if ( ds - > size > ds - > max_direct_range )
metablocks + = ds - > indirect . len ;
/*
Double indir block , plus all the indirect blocks it mapps
In the double - indirect range , all block runs of data are
BEFS_DBLINDIR_BRUN_LEN blocks long . Therefore , we know
how many data block runs are in the double - indirect region ,
and from that we know how many indirect blocks it takes to
map them . We assume that the indirect blocks are also
BEFS_DBLINDIR_BRUN_LEN blocks long .
*/
if ( ds - > size > ds - > max_indirect_range & & ds - > max_indirect_range ! = 0 ) {
uint dbl_bytes ;
uint dbl_bruns ;
uint indirblocks ;
dbl_bytes =
ds - > max_double_indirect_range - ds - > max_indirect_range ;
dbl_bruns =
dbl_bytes / ( befs_sb - > block_size * BEFS_DBLINDIR_BRUN_LEN ) ;
indirblocks = dbl_bruns / befs_iaddrs_per_block ( sb ) ;
metablocks + = ds - > double_indirect . len ;
metablocks + = indirblocks ;
}
blocks = datablocks + metablocks ;
2014-04-04 01:50:23 +04:00
befs_debug ( sb , " <--- %s %u blocks " , __func__ , ( unsigned int ) blocks ) ;
2005-04-17 02:20:36 +04:00
return blocks ;
}
/*
Finds the block run that starts at file block number blockno
in the file represented by the datastream data , if that
blockno is in the direct region of the datastream .
sb : the superblock
data : the datastream
blockno : the blocknumber to find
run : The found run is passed back through this pointer
Return value is BEFS_OK if the blockrun is found , BEFS_ERR
otherwise .
Algorithm :
Linear search . Checks each element of array [ ] to see if it
contains the blockno - th filesystem block . This is necessary
because the block runs map variable amounts of data . Simply
keeps a count of the number of blocks searched so far ( sum ) ,
incrementing this by the length of each block run as we come
across it . Adds sum to * count before returning ( this is so
you can search multiple arrays that are logicaly one array ,
as in the indirect region code ) .
When / if blockno is found , if blockno is inside of a block
2008-02-03 16:42:53 +03:00
run as stored on disk , we offset the start and length members
2005-04-17 02:20:36 +04:00
of the block run , so that blockno is the start and len is
still valid ( the run ends in the same place ) .
2001 - 11 - 15 Will Dyson
*/
static int
befs_find_brun_direct ( struct super_block * sb , befs_data_stream * data ,
befs_blocknr_t blockno , befs_block_run * run )
{
int i ;
befs_block_run * array = data - > direct ;
befs_blocknr_t sum ;
befs_blocknr_t max_block =
data - > max_direct_range > > BEFS_SB ( sb ) - > block_shift ;
2014-04-04 01:50:23 +04:00
befs_debug ( sb , " ---> %s, find %lu " , __func__ , ( unsigned long ) blockno ) ;
2005-04-17 02:20:36 +04:00
if ( blockno > max_block ) {
2014-04-04 01:50:23 +04:00
befs_error ( sb , " %s passed block outside of direct region " ,
__func__ ) ;
2005-04-17 02:20:36 +04:00
return BEFS_ERR ;
}
for ( i = 0 , sum = 0 ; i < BEFS_NUM_DIRECT_BLOCKS ;
sum + = array [ i ] . len , i + + ) {
if ( blockno > = sum & & blockno < sum + ( array [ i ] . len ) ) {
int offset = blockno - sum ;
run - > allocation_group = array [ i ] . allocation_group ;
run - > start = array [ i ] . start + offset ;
run - > len = array [ i ] . len - offset ;
2014-04-04 01:50:23 +04:00
befs_debug ( sb , " ---> %s, "
" found %lu at direct[%d] " , __func__ ,
( unsigned long ) blockno , i ) ;
2005-04-17 02:20:36 +04:00
return BEFS_OK ;
}
}
2014-04-04 01:50:23 +04:00
befs_debug ( sb , " ---> %s ERROR " , __func__ ) ;
2005-04-17 02:20:36 +04:00
return BEFS_ERR ;
}
/*
Finds the block run that starts at file block number blockno
in the file represented by the datastream data , if that
blockno is in the indirect region of the datastream .
sb : the superblock
data : the datastream
blockno : the blocknumber to find
run : The found run is passed back through this pointer
Return value is BEFS_OK if the blockrun is found , BEFS_ERR
otherwise .
Algorithm :
For each block in the indirect run of the datastream , read
it in and search through it for search_blk .
XXX :
Really should check to make sure blockno is inside indirect
region .
2001 - 11 - 15 Will Dyson
*/
static int
befs_find_brun_indirect ( struct super_block * sb ,
befs_data_stream * data , befs_blocknr_t blockno ,
befs_block_run * run )
{
int i , j ;
befs_blocknr_t sum = 0 ;
befs_blocknr_t indir_start_blk ;
befs_blocknr_t search_blk ;
struct buffer_head * indirblock ;
2005-12-24 22:28:55 +03:00
befs_disk_block_run * array ;
2005-04-17 02:20:36 +04:00
befs_block_run indirect = data - > indirect ;
befs_blocknr_t indirblockno = iaddr2blockno ( sb , & indirect ) ;
int arraylen = befs_iaddrs_per_block ( sb ) ;
2014-04-04 01:50:23 +04:00
befs_debug ( sb , " ---> %s, find %lu " , __func__ , ( unsigned long ) blockno ) ;
2005-04-17 02:20:36 +04:00
indir_start_blk = data - > max_direct_range > > BEFS_SB ( sb ) - > block_shift ;
search_blk = blockno - indir_start_blk ;
/* Examine blocks of the indirect run one at a time */
for ( i = 0 ; i < indirect . len ; i + + ) {
indirblock = befs_bread ( sb , indirblockno + i ) ;
if ( indirblock = = NULL ) {
2014-04-04 01:50:23 +04:00
befs_debug ( sb , " ---> %s failed to read "
" disk block %lu from the indirect brun " ,
__func__ , ( unsigned long ) indirblockno + i ) ;
2005-04-17 02:20:36 +04:00
return BEFS_ERR ;
}
2005-12-24 22:28:55 +03:00
array = ( befs_disk_block_run * ) indirblock - > b_data ;
2005-04-17 02:20:36 +04:00
for ( j = 0 ; j < arraylen ; + + j ) {
int len = fs16_to_cpu ( sb , array [ j ] . len ) ;
if ( search_blk > = sum & & search_blk < sum + len ) {
int offset = search_blk - sum ;
run - > allocation_group =
fs32_to_cpu ( sb , array [ j ] . allocation_group ) ;
run - > start =
fs16_to_cpu ( sb , array [ j ] . start ) + offset ;
run - > len =
fs16_to_cpu ( sb , array [ j ] . len ) - offset ;
brelse ( indirblock ) ;
befs_debug ( sb ,
2014-04-04 01:50:23 +04:00
" <--- %s found file block "
" %lu at indirect[%d] " , __func__ ,
( unsigned long ) blockno ,
j + ( i * arraylen ) ) ;
2005-04-17 02:20:36 +04:00
return BEFS_OK ;
}
sum + = len ;
}
brelse ( indirblock ) ;
}
/* Only fallthrough is an error */
2014-04-04 01:50:23 +04:00
befs_error ( sb , " BeFS: %s failed to find "
" file block %lu " , __func__ , ( unsigned long ) blockno ) ;
2005-04-17 02:20:36 +04:00
2014-04-04 01:50:23 +04:00
befs_debug ( sb , " <--- %s ERROR " , __func__ ) ;
2005-04-17 02:20:36 +04:00
return BEFS_ERR ;
}
/*
Finds the block run that starts at file block number blockno
in the file represented by the datastream data , if that
blockno is in the double - indirect region of the datastream .
sb : the superblock
data : the datastream
blockno : the blocknumber to find
run : The found run is passed back through this pointer
Return value is BEFS_OK if the blockrun is found , BEFS_ERR
otherwise .
Algorithm :
The block runs in the double - indirect region are different .
They are always allocated 4 fs blocks at a time , so each
block run maps a constant amount of file data . This means
that we can directly calculate how many block runs into the
double - indirect region we need to go to get to the one that
maps a particular filesystem block .
We do this in two stages . First we calculate which of the
inode addresses in the double - indirect block will point us
to the indirect block that contains the mapping for the data ,
then we calculate which of the inode addresses in that
indirect block maps the data block we are after .
Oh , and once we ' ve done that , we actually read in the blocks
that contain the inode addresses we calculated above . Even
though the double - indirect run may be several blocks long ,
we can calculate which of those blocks will contain the index
we are after and only read that one . We then follow it to
the indirect block and perform a similar process to find
the actual block run that maps the data block we are interested
in .
Then we offset the run as in befs_find_brun_array ( ) and we are
done .
2001 - 11 - 15 Will Dyson
*/
static int
befs_find_brun_dblindirect ( struct super_block * sb ,
befs_data_stream * data , befs_blocknr_t blockno ,
befs_block_run * run )
{
int dblindir_indx ;
int indir_indx ;
int offset ;
int dbl_which_block ;
int which_block ;
int dbl_block_indx ;
int block_indx ;
off_t dblindir_leftover ;
befs_blocknr_t blockno_at_run_start ;
struct buffer_head * dbl_indir_block ;
struct buffer_head * indir_block ;
befs_block_run indir_run ;
2005-12-24 22:28:55 +03:00
befs_disk_inode_addr * iaddr_array = NULL ;
2005-04-17 02:20:36 +04:00
befs_sb_info * befs_sb = BEFS_SB ( sb ) ;
befs_blocknr_t indir_start_blk =
data - > max_indirect_range > > befs_sb - > block_shift ;
off_t dbl_indir_off = blockno - indir_start_blk ;
/* number of data blocks mapped by each of the iaddrs in
* the indirect block pointed to by the double indirect block
*/
size_t iblklen = BEFS_DBLINDIR_BRUN_LEN ;
/* number of data blocks mapped by each of the iaddrs in
* the double indirect block
*/
size_t diblklen = iblklen * befs_iaddrs_per_block ( sb )
* BEFS_DBLINDIR_BRUN_LEN ;
2014-04-04 01:50:23 +04:00
befs_debug ( sb , " ---> %s find %lu " , __func__ , ( unsigned long ) blockno ) ;
2005-04-17 02:20:36 +04:00
/* First, discover which of the double_indir->indir blocks
* contains pos . Then figure out how much of pos that
* accounted for . Then discover which of the iaddrs in
* the indirect block contains pos .
*/
dblindir_indx = dbl_indir_off / diblklen ;
dblindir_leftover = dbl_indir_off % diblklen ;
indir_indx = dblindir_leftover / diblklen ;
/* Read double indirect block */
dbl_which_block = dblindir_indx / befs_iaddrs_per_block ( sb ) ;
if ( dbl_which_block > data - > double_indirect . len ) {
befs_error ( sb , " The double-indirect index calculated by "
2014-04-04 01:50:23 +04:00
" %s, %d, is outside the range "
" of the double-indirect block " , __func__ ,
dblindir_indx ) ;
2005-04-17 02:20:36 +04:00
return BEFS_ERR ;
}
dbl_indir_block =
befs_bread ( sb , iaddr2blockno ( sb , & data - > double_indirect ) +
dbl_which_block ) ;
if ( dbl_indir_block = = NULL ) {
2014-04-04 01:50:23 +04:00
befs_error ( sb , " %s couldn't read the "
" double-indirect block at blockno %lu " , __func__ ,
( unsigned long )
iaddr2blockno ( sb , & data - > double_indirect ) +
2005-04-17 02:20:36 +04:00
dbl_which_block ) ;
brelse ( dbl_indir_block ) ;
return BEFS_ERR ;
}
dbl_block_indx =
dblindir_indx - ( dbl_which_block * befs_iaddrs_per_block ( sb ) ) ;
2005-12-24 22:28:55 +03:00
iaddr_array = ( befs_disk_inode_addr * ) dbl_indir_block - > b_data ;
2005-04-17 02:20:36 +04:00
indir_run = fsrun_to_cpu ( sb , iaddr_array [ dbl_block_indx ] ) ;
brelse ( dbl_indir_block ) ;
iaddr_array = NULL ;
/* Read indirect block */
which_block = indir_indx / befs_iaddrs_per_block ( sb ) ;
if ( which_block > indir_run . len ) {
befs_error ( sb , " The indirect index calculated by "
2014-04-04 01:50:23 +04:00
" %s, %d, is outside the range "
" of the indirect block " , __func__ , indir_indx ) ;
2005-04-17 02:20:36 +04:00
return BEFS_ERR ;
}
indir_block =
befs_bread ( sb , iaddr2blockno ( sb , & indir_run ) + which_block ) ;
if ( indir_block = = NULL ) {
2014-04-04 01:50:23 +04:00
befs_error ( sb , " %s couldn't read the indirect block "
" at blockno %lu " , __func__ , ( unsigned long )
2005-04-17 02:20:36 +04:00
iaddr2blockno ( sb , & indir_run ) + which_block ) ;
brelse ( indir_block ) ;
return BEFS_ERR ;
}
block_indx = indir_indx - ( which_block * befs_iaddrs_per_block ( sb ) ) ;
2005-12-24 22:28:55 +03:00
iaddr_array = ( befs_disk_inode_addr * ) indir_block - > b_data ;
2005-04-17 02:20:36 +04:00
* run = fsrun_to_cpu ( sb , iaddr_array [ block_indx ] ) ;
brelse ( indir_block ) ;
iaddr_array = NULL ;
blockno_at_run_start = indir_start_blk ;
blockno_at_run_start + = diblklen * dblindir_indx ;
blockno_at_run_start + = iblklen * indir_indx ;
offset = blockno - blockno_at_run_start ;
run - > start + = offset ;
run - > len - = offset ;
befs_debug ( sb , " Found file block %lu in double_indirect[%d][%d], "
2014-04-04 01:50:23 +04:00
" double_indirect_leftover = %lu " , ( unsigned long )
2005-04-17 02:20:36 +04:00
blockno , dblindir_indx , indir_indx , dblindir_leftover ) ;
return BEFS_OK ;
}