@ -83,6 +83,86 @@ static struct buffer_head *ext4_append(handle_t *handle,
return bh ;
}
static int ext4_dx_csum_verify ( struct inode * inode ,
struct ext4_dir_entry * dirent ) ;
typedef enum {
EITHER , INDEX , DIRENT
} dirblock_type_t ;
# define ext4_read_dirblock(inode, block, type) \
__ext4_read_dirblock ( ( inode ) , ( block ) , ( type ) , __LINE__ )
static struct buffer_head * __ext4_read_dirblock ( struct inode * inode ,
ext4_lblk_t block ,
dirblock_type_t type ,
unsigned int line )
{
struct buffer_head * bh ;
struct ext4_dir_entry * dirent ;
int err = 0 , is_dx_block = 0 ;
bh = ext4_bread ( NULL , inode , block , 0 , & err ) ;
if ( ! bh ) {
if ( err = = 0 ) {
ext4_error_inode ( inode , __func__ , line , block ,
" Directory hole found " ) ;
return ERR_PTR ( - EIO ) ;
}
__ext4_warning ( inode - > i_sb , __func__ , line ,
" error reading directory block "
" (ino %lu, block %lu) " , inode - > i_ino ,
( unsigned long ) block ) ;
return ERR_PTR ( err ) ;
}
dirent = ( struct ext4_dir_entry * ) bh - > b_data ;
/* Determine whether or not we have an index block */
if ( is_dx ( inode ) ) {
if ( block = = 0 )
is_dx_block = 1 ;
else if ( ext4_rec_len_from_disk ( dirent - > rec_len ,
inode - > i_sb - > s_blocksize ) = =
inode - > i_sb - > s_blocksize )
is_dx_block = 1 ;
}
if ( ! is_dx_block & & type = = INDEX ) {
ext4_error_inode ( inode , __func__ , line , block ,
" directory leaf block found instead of index block " ) ;
return ERR_PTR ( - EIO ) ;
}
if ( ! EXT4_HAS_RO_COMPAT_FEATURE ( inode - > i_sb ,
EXT4_FEATURE_RO_COMPAT_METADATA_CSUM ) | |
buffer_verified ( bh ) )
return bh ;
/*
* An empty leaf block can get mistaken for a index block ; for
* this reason , we can only check the index checksum when the
* caller is sure it should be an index block .
*/
if ( is_dx_block & & type = = INDEX ) {
if ( ext4_dx_csum_verify ( inode , dirent ) )
set_buffer_verified ( bh ) ;
else {
ext4_error_inode ( inode , __func__ , line , block ,
" Directory index failed checksum " ) ;
brelse ( bh ) ;
return ERR_PTR ( - EIO ) ;
}
}
if ( ! is_dx_block ) {
if ( ext4_dirent_csum_verify ( inode , dirent ) )
set_buffer_verified ( bh ) ;
else {
ext4_error_inode ( inode , __func__ , line , block ,
" Directory block failed checksum " ) ;
brelse ( bh ) ;
return ERR_PTR ( - EIO ) ;
}
}
return bh ;
}
# ifndef assert
# define assert(test) J_ASSERT(test)
# endif
@ -604,9 +684,9 @@ dx_probe(const struct qstr *d_name, struct inode *dir,
u32 hash ;
frame - > bh = NULL ;
if ( ! ( bh = ext4_bread ( NULL , dir , 0 , 0 , err ) ) ) {
if ( * err = = 0 )
* err = ERR_BAD_DX_DIR ;
bh = ext4_read_dirblock ( dir , 0 , INDEX ) ;
if ( IS_ERR ( bh ) ) {
* err = PTR_ERR ( bh ) ;
goto fail ;
}
root = ( struct dx_root * ) bh - > b_data ;
@ -643,15 +723,6 @@ dx_probe(const struct qstr *d_name, struct inode *dir,
goto fail ;
}
if ( ! buffer_verified ( bh ) & &
! ext4_dx_csum_verify ( dir , ( struct ext4_dir_entry * ) bh - > b_data ) ) {
ext4_warning ( dir - > i_sb , " Root failed checksum " ) ;
brelse ( bh ) ;
* err = ERR_BAD_DX_DIR ;
goto fail ;
}
set_buffer_verified ( bh ) ;
entries = ( struct dx_entry * ) ( ( ( char * ) & root - > info ) +
root - > info . info_length ) ;
@ -709,23 +780,13 @@ dx_probe(const struct qstr *d_name, struct inode *dir,
frame - > entries = entries ;
frame - > at = at ;
if ( ! indirect - - ) return frame ;
if ( ! ( bh = ext4_bread ( NULL , dir , dx_get_block ( at ) , 0 , err ) ) ) {
if ( ! ( * err ) )
* err = ERR_BAD_DX_DIR ;
bh = ext4_read_dirblock ( dir , dx_get_block ( at ) , INDEX ) ;
if ( IS_ERR ( bh ) ) {
* err = PTR_ERR ( bh ) ;
goto fail2 ;
}
entries = ( ( struct dx_node * ) bh - > b_data ) - > entries ;
if ( ! buffer_verified ( bh ) & &
! ext4_dx_csum_verify ( dir ,
( struct ext4_dir_entry * ) bh - > b_data ) ) {
ext4_warning ( dir - > i_sb , " Node failed checksum " ) ;
brelse ( bh ) ;
* err = ERR_BAD_DX_DIR ;
goto fail2 ;
}
set_buffer_verified ( bh ) ;
if ( dx_get_limit ( entries ) ! = dx_node_limit ( dir ) ) {
ext4_warning ( dir - > i_sb ,
" dx entry: limit != node limit " ) ;
@ -783,7 +844,7 @@ static int ext4_htree_next_block(struct inode *dir, __u32 hash,
{
struct dx_frame * p ;
struct buffer_head * bh ;
int err , num_frames = 0 ;
int num_frames = 0 ;
__u32 bhash ;
p = frame ;
@ -822,26 +883,9 @@ static int ext4_htree_next_block(struct inode *dir, __u32 hash,
* block so no check is necessary
*/
while ( num_frames - - ) {
if ( ! ( bh = ext4_bread ( NULL , dir , dx_get_block ( p - > at ) ,
0 , & err ) ) ) {
if ( ! err ) {
ext4_error ( dir - > i_sb ,
" Directory hole detected on inode %lu \n " ,
dir - > i_ino ) ;
return - EIO ;
}
return err ; /* Failure */
}
if ( ! buffer_verified ( bh ) & &
! ext4_dx_csum_verify ( dir ,
( struct ext4_dir_entry * ) bh - > b_data ) ) {
ext4_warning ( dir - > i_sb , " Node failed checksum " ) ;
brelse ( bh ) ;
return - EIO ;
}
set_buffer_verified ( bh ) ;
bh = ext4_read_dirblock ( dir , dx_get_block ( p - > at ) , INDEX ) ;
if ( IS_ERR ( bh ) )
return PTR_ERR ( bh ) ;
p + + ;
brelse ( p - > bh ) ;
p - > bh = bh ;
@ -867,23 +911,9 @@ static int htree_dirblock_to_tree(struct file *dir_file,
dxtrace ( printk ( KERN_INFO " In htree dirblock_to_tree: block %lu \n " ,
( unsigned long ) block ) ) ;
if ( ! ( bh = ext4_bread ( NULL , dir , block , 0 , & err ) ) ) {
if ( ! err ) {
err = - EIO ;
ext4_error ( dir - > i_sb ,
" Directory hole detected on inode %lu \n " ,
dir - > i_ino ) ;
}
return err ;
}
if ( ! buffer_verified ( bh ) & &
! ext4_dirent_csum_verify ( dir ,
( struct ext4_dir_entry * ) bh - > b_data ) ) {
brelse ( bh ) ;
return - EIO ;
}
set_buffer_verified ( bh ) ;
bh = ext4_read_dirblock ( dir , block , DIRENT ) ;
if ( IS_ERR ( bh ) )
return PTR_ERR ( bh ) ;
de = ( struct ext4_dir_entry_2 * ) bh - > b_data ;
top = ( struct ext4_dir_entry_2 * ) ( ( char * ) de +
@ -1337,26 +1367,11 @@ static struct buffer_head * ext4_dx_find_entry(struct inode *dir, const struct q
return NULL ;
do {
block = dx_get_block ( frame - > at ) ;
if ( ! ( bh = ext4_bread ( NULL , dir , block , 0 , err ) ) ) {
if ( ! ( * err ) ) {
* err = - EIO ;
ext4_error ( dir - > i_sb ,
" Directory hole detected on inode %lu \n " ,
dir - > i_ino ) ;
}
bh = ext4_read_dirblock ( dir , block , DIRENT ) ;
if ( IS_ERR ( bh ) ) {
* err = PTR_ERR ( bh ) ;
goto errout ;
}
if ( ! buffer_verified ( bh ) & &
! ext4_dirent_csum_verify ( dir ,
( struct ext4_dir_entry * ) bh - > b_data ) ) {
EXT4_ERROR_INODE ( dir , " checksumming directory "
" block %lu " , ( unsigned long ) block ) ;
brelse ( bh ) ;
* err = - EIO ;
goto errout ;
}
set_buffer_verified ( bh ) ;
retval = search_dirblock ( bh , dir , d_name ,
block < < EXT4_BLOCK_SIZE_BITS ( sb ) ,
res_dir ) ;
@ -1920,22 +1935,10 @@ static int ext4_add_entry(handle_t *handle, struct dentry *dentry,
}
blocks = dir - > i_size > > sb - > s_blocksize_bits ;
for ( block = 0 ; block < blocks ; block + + ) {
if ( ! ( bh = ext4_bread ( handle , dir , block , 0 , & retval ) ) ) {
if ( ! retval ) {
retval = - EIO ;
ext4_error ( inode - > i_sb ,
" Directory hole detected on inode %lu \n " ,
inode - > i_ino ) ;
}
return retval ;
}
if ( ! buffer_verified ( bh ) & &
! ext4_dirent_csum_verify ( dir ,
( struct ext4_dir_entry * ) bh - > b_data ) ) {
brelse ( bh ) ;
return - EIO ;
}
set_buffer_verified ( bh ) ;
bh = ext4_read_dirblock ( dir , block , DIRENT ) ;
if ( IS_ERR ( bh ) )
return PTR_ERR ( bh ) ;
retval = add_dirent_to_buf ( handle , dentry , inode , NULL , bh ) ;
if ( retval ! = - ENOSPC ) {
brelse ( bh ) ;
@ -1986,22 +1989,13 @@ static int ext4_dx_add_entry(handle_t *handle, struct dentry *dentry,
return err ;
entries = frame - > entries ;
at = frame - > at ;
if ( ! ( bh = ext4_bread ( handle , dir , dx_get_block ( frame - > at ) , 0 , & err ) ) ) {
if ( ! err ) {
err = - EIO ;
ext4_error ( dir - > i_sb ,
" Directory hole detected on inode %lu \n " ,
dir - > i_ino ) ;
}
bh = ext4_read_dirblock ( dir , dx_get_block ( frame - > at ) , DIRENT ) ;
if ( IS_ERR ( bh ) ) {
err = PTR_ERR ( bh ) ;
bh = NULL ;
goto cleanup ;
}
if ( ! buffer_verified ( bh ) & &
! ext4_dirent_csum_verify ( dir , ( struct ext4_dir_entry * ) bh - > b_data ) )
goto journal_error ;
set_buffer_verified ( bh ) ;
BUFFER_TRACE ( bh , " get_write_access " ) ;
err = ext4_journal_get_write_access ( handle , bh ) ;
if ( err )
@ -2352,6 +2346,7 @@ static int ext4_init_new_dir(handle_t *handle, struct inode *dir,
struct buffer_head * dir_block = NULL ;
struct ext4_dir_entry_2 * de ;
struct ext4_dir_entry_tail * t ;
ext4_lblk_t block = 0 ;
unsigned int blocksize = dir - > i_sb - > s_blocksize ;
int csum_size = 0 ;
int err ;
@ -2368,16 +2363,9 @@ static int ext4_init_new_dir(handle_t *handle, struct inode *dir,
goto out ;
}
inode - > i_size = EXT4_I ( inode ) - > i_disksize = blocksize ;
if ( ! ( dir_block = ext4_bread ( handle , inode , 0 , 1 , & err ) ) ) {
if ( ! err ) {
err = - EIO ;
ext4_error ( inode - > i_sb ,
" Directory hole detected on inode %lu \n " ,
inode - > i_ino ) ;
}
inode - > i_size = 0 ;
if ( ! ( dir_block = ext4_append ( handle , inode , & block , & err ) ) )
goto out ;
}
BUFFER_TRACE ( dir_block , " get_write_access " ) ;
err = ext4_journal_get_write_access ( handle , dir_block ) ;
if ( err )
@ -2477,26 +2465,14 @@ static int empty_dir(struct inode *inode)
}
sb = inode - > i_sb ;
if ( inode - > i_size < EXT4_DIR_REC_LEN ( 1 ) + EXT4_DIR_REC_LEN ( 2 ) | |
! ( bh = ext4_bread ( NULL , inode , 0 , 0 , & err ) ) ) {
if ( err )
EXT4_ERROR_INODE ( inode ,
" error %d reading directory lblock 0 " , err ) ;
else
ext4_warning ( inode - > i_sb ,
" bad directory (dir #%lu) - no data block " ,
inode - > i_ino ) ;
if ( inode - > i_size < EXT4_DIR_REC_LEN ( 1 ) + EXT4_DIR_REC_LEN ( 2 ) ) {
EXT4_ERROR_INODE ( inode , " invalid size " ) ;
return 1 ;
}
if ( ! buffer_verified ( bh ) & &
! ext4_dirent_csum_verify ( inode ,
( struct ext4_dir_entry * ) bh - > b_data ) ) {
EXT4_ERROR_INODE ( inode , " checksum error reading directory "
" lblock 0 " ) ;
brelse ( bh ) ;
return - EIO ;
}
set_buffer_verified ( bh ) ;
bh = ext4_read_dirblock ( inode , 0 , EITHER ) ;
if ( IS_ERR ( bh ) )
return 1 ;
de = ( struct ext4_dir_entry_2 * ) bh - > b_data ;
de1 = ext4_next_entry ( de , sb - > s_blocksize ) ;
if ( le32_to_cpu ( de - > inode ) ! = inode - > i_ino | |
@ -2519,29 +2495,9 @@ static int empty_dir(struct inode *inode)
err = 0 ;
brelse ( bh ) ;
lblock = offset > > EXT4_BLOCK_SIZE_BITS ( sb ) ;
bh = ext4_bread ( NULL , inode , lblock , 0 , & err ) ;
if ( ! bh ) {
if ( err )
EXT4_ERROR_INODE ( inode ,
" error %d reading directory "
" lblock %u " , err , lblock ) ;
else
ext4_warning ( inode - > i_sb ,
" bad directory (dir #%lu) - no data block " ,
inode - > i_ino ) ;
offset + = sb - > s_blocksize ;
continue ;
}
if ( ! buffer_verified ( bh ) & &
! ext4_dirent_csum_verify ( inode ,
( struct ext4_dir_entry * ) bh - > b_data ) ) {
EXT4_ERROR_INODE ( inode , " checksum error "
" reading directory lblock 0 " ) ;
brelse ( bh ) ;
return - EIO ;
}
set_buffer_verified ( bh ) ;
bh = ext4_read_dirblock ( inode , lblock , EITHER ) ;
if ( IS_ERR ( bh ) )
return 1 ;
de = ( struct ext4_dir_entry_2 * ) bh - > b_data ;
}
if ( ext4_check_dir_entry ( inode , NULL , de , bh ,
@ -3004,13 +2960,9 @@ static struct buffer_head *ext4_get_first_dir_block(handle_t *handle,
struct buffer_head * bh ;
if ( ! ext4_has_inline_data ( inode ) ) {
if ( ! ( bh = ext4_bread ( handle , inode , 0 , 0 , retval ) ) ) {
if ( ! * retval ) {
* retval = - EIO ;
ext4_error ( inode - > i_sb ,
" Directory hole detected on inode %lu \n " ,
inode - > i_ino ) ;
}
bh = ext4_read_dirblock ( inode , 0 , EITHER ) ;
if ( IS_ERR ( bh ) ) {
* retval = PTR_ERR ( bh ) ;
return NULL ;
}
* parent_de = ext4_next_entry (
@ -3089,11 +3041,6 @@ static int ext4_rename(struct inode *old_dir, struct dentry *old_dentry,
& inlined ) ;
if ( ! dir_bh )
goto end_rename ;
if ( ! inlined & & ! buffer_verified ( dir_bh ) & &
! ext4_dirent_csum_verify ( old_inode ,
( struct ext4_dir_entry * ) dir_bh - > b_data ) )
goto end_rename ;
set_buffer_verified ( dir_bh ) ;
if ( le32_to_cpu ( parent_de - > inode ) ! = old_dir - > i_ino )
goto end_rename ;
retval = - EMLINK ;