2007-06-12 17:07:21 +04:00
/*
* Copyright ( C ) 2007 Oracle . All rights reserved .
*
* This program is free software ; you can redistribute it and / or
* modify it under the terms of the GNU General Public
* License v2 as published by the Free Software Foundation .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the GNU
* General Public License for more details .
*
* You should have received a copy of the GNU General Public
* License along with this program ; if not , write to the
* Free Software Foundation , Inc . , 59 Temple Place - Suite 330 ,
* Boston , MA 021110 - 1307 , USA .
*/
2007-03-21 18:12:56 +03:00
# include <linux/module.h>
2007-03-16 02:03:33 +03:00
# include "ctree.h"
2007-03-27 00:00:06 +04:00
# include "disk-io.h"
2007-03-20 21:38:32 +03:00
# include "transaction.h"
2007-05-29 23:17:08 +04:00
# include "print-tree.h"
2007-03-16 02:03:33 +03:00
2007-04-16 17:22:45 +04:00
# define MAX_CSUM_ITEMS(r) ((((BTRFS_LEAF_DATA_SIZE(r) - \
2007-05-10 20:36:17 +04:00
sizeof ( struct btrfs_item ) * 2 ) / \
BTRFS_CRC32_SIZE ) - 1 ) )
2007-04-17 21:26:50 +04:00
int btrfs_insert_file_extent ( struct btrfs_trans_handle * trans ,
2007-03-27 00:00:06 +04:00
struct btrfs_root * root ,
2007-04-17 21:26:50 +04:00
u64 objectid , u64 pos ,
2007-05-24 21:35:57 +04:00
u64 offset , u64 disk_num_blocks ,
u64 num_blocks )
2007-03-20 21:38:32 +03:00
{
2007-03-27 00:00:06 +04:00
int ret = 0 ;
struct btrfs_file_extent_item * item ;
struct btrfs_key file_key ;
2007-04-02 19:20:42 +04:00
struct btrfs_path * path ;
2007-03-27 00:00:06 +04:00
2007-04-02 19:20:42 +04:00
path = btrfs_alloc_path ( ) ;
BUG_ON ( ! path ) ;
2007-03-27 00:00:06 +04:00
file_key . objectid = objectid ;
2007-04-17 21:26:50 +04:00
file_key . offset = pos ;
2007-03-27 00:00:06 +04:00
file_key . flags = 0 ;
btrfs_set_key_type ( & file_key , BTRFS_EXTENT_DATA_KEY ) ;
2007-04-02 19:20:42 +04:00
ret = btrfs_insert_empty_item ( trans , root , path , & file_key ,
2007-03-27 00:00:06 +04:00
sizeof ( * item ) ) ;
2007-03-27 19:26:26 +04:00
BUG_ON ( ret ) ;
2007-04-02 19:20:42 +04:00
item = btrfs_item_ptr ( btrfs_buffer_leaf ( path - > nodes [ 0 ] ) , path - > slots [ 0 ] ,
2007-03-27 00:00:06 +04:00
struct btrfs_file_extent_item ) ;
2007-04-17 21:26:50 +04:00
btrfs_set_file_extent_disk_blocknr ( item , offset ) ;
2007-05-24 21:35:57 +04:00
btrfs_set_file_extent_disk_num_blocks ( item , disk_num_blocks ) ;
2007-03-27 00:00:06 +04:00
btrfs_set_file_extent_offset ( item , 0 ) ;
2007-04-17 21:26:50 +04:00
btrfs_set_file_extent_num_blocks ( item , num_blocks ) ;
2007-03-27 17:16:29 +04:00
btrfs_set_file_extent_generation ( item , trans - > transid ) ;
2007-04-19 21:37:44 +04:00
btrfs_set_file_extent_type ( item , BTRFS_FILE_EXTENT_REG ) ;
2007-04-02 19:20:42 +04:00
btrfs_mark_buffer_dirty ( path - > nodes [ 0 ] ) ;
2007-04-19 00:15:28 +04:00
2007-04-02 19:20:42 +04:00
btrfs_release_path ( root , path ) ;
btrfs_free_path ( path ) ;
2007-03-20 21:38:32 +03:00
return 0 ;
}
2007-03-27 00:00:06 +04:00
2007-04-17 21:26:50 +04:00
struct btrfs_csum_item * btrfs_lookup_csum ( struct btrfs_trans_handle * trans ,
struct btrfs_root * root ,
struct btrfs_path * path ,
u64 objectid , u64 offset ,
int cow )
2007-04-16 17:22:45 +04:00
{
int ret ;
struct btrfs_key file_key ;
struct btrfs_key found_key ;
struct btrfs_csum_item * item ;
struct btrfs_leaf * leaf ;
u64 csum_offset = 0 ;
2007-04-19 00:15:28 +04:00
int csums_in_item ;
2007-04-16 17:22:45 +04:00
file_key . objectid = objectid ;
file_key . offset = offset ;
file_key . flags = 0 ;
btrfs_set_key_type ( & file_key , BTRFS_CSUM_ITEM_KEY ) ;
2007-04-17 21:26:50 +04:00
ret = btrfs_search_slot ( trans , root , & file_key , path , 0 , cow ) ;
2007-04-16 17:22:45 +04:00
if ( ret < 0 )
goto fail ;
leaf = btrfs_buffer_leaf ( path - > nodes [ 0 ] ) ;
if ( ret > 0 ) {
ret = 1 ;
2007-04-17 23:39:32 +04:00
if ( path - > slots [ 0 ] = = 0 )
2007-04-16 17:22:45 +04:00
goto fail ;
path - > slots [ 0 ] - - ;
btrfs_disk_key_to_cpu ( & found_key ,
& leaf - > items [ path - > slots [ 0 ] ] . key ) ;
if ( btrfs_key_type ( & found_key ) ! = BTRFS_CSUM_ITEM_KEY | |
found_key . objectid ! = objectid ) {
goto fail ;
}
csum_offset = ( offset - found_key . offset ) > >
root - > fs_info - > sb - > s_blocksize_bits ;
2007-04-19 00:15:28 +04:00
csums_in_item = btrfs_item_size ( leaf - > items + path - > slots [ 0 ] ) ;
2007-05-10 20:36:17 +04:00
csums_in_item / = BTRFS_CRC32_SIZE ;
2007-04-19 00:15:28 +04:00
if ( csum_offset > = csums_in_item ) {
ret = - EFBIG ;
2007-04-16 17:22:45 +04:00
goto fail ;
}
}
item = btrfs_item_ptr ( leaf , path - > slots [ 0 ] , struct btrfs_csum_item ) ;
2007-05-10 20:36:17 +04:00
item = ( struct btrfs_csum_item * ) ( ( unsigned char * ) item +
csum_offset * BTRFS_CRC32_SIZE ) ;
2007-04-16 17:22:45 +04:00
return item ;
fail :
if ( ret > 0 )
2007-04-17 21:26:50 +04:00
ret = - ENOENT ;
2007-04-16 17:22:45 +04:00
return ERR_PTR ( ret ) ;
}
2007-03-27 00:00:06 +04:00
int btrfs_lookup_file_extent ( struct btrfs_trans_handle * trans ,
struct btrfs_root * root ,
struct btrfs_path * path , u64 objectid ,
2007-03-27 19:26:26 +04:00
u64 offset , int mod )
2007-03-27 00:00:06 +04:00
{
int ret ;
struct btrfs_key file_key ;
int ins_len = mod < 0 ? - 1 : 0 ;
int cow = mod ! = 0 ;
file_key . objectid = objectid ;
2007-04-17 23:39:32 +04:00
file_key . offset = offset ;
2007-03-27 00:00:06 +04:00
file_key . flags = 0 ;
btrfs_set_key_type ( & file_key , BTRFS_EXTENT_DATA_KEY ) ;
ret = btrfs_search_slot ( trans , root , & file_key , path , ins_len , cow ) ;
return ret ;
}
2007-03-29 23:15:27 +04:00
int btrfs_csum_file_block ( struct btrfs_trans_handle * trans ,
struct btrfs_root * root ,
u64 objectid , u64 offset ,
char * data , size_t len )
{
int ret ;
struct btrfs_key file_key ;
2007-04-16 17:22:45 +04:00
struct btrfs_key found_key ;
2007-04-02 19:20:42 +04:00
struct btrfs_path * path ;
2007-03-29 23:15:27 +04:00
struct btrfs_csum_item * item ;
2007-04-16 17:22:45 +04:00
struct btrfs_leaf * leaf ;
u64 csum_offset ;
2007-03-29 23:15:27 +04:00
2007-04-02 19:20:42 +04:00
path = btrfs_alloc_path ( ) ;
BUG_ON ( ! path ) ;
2007-04-17 21:26:50 +04:00
2007-03-29 23:15:27 +04:00
file_key . objectid = objectid ;
file_key . offset = offset ;
file_key . flags = 0 ;
btrfs_set_key_type ( & file_key , BTRFS_CSUM_ITEM_KEY ) ;
2007-04-19 00:15:28 +04:00
item = btrfs_lookup_csum ( trans , root , path , objectid , offset , 1 ) ;
if ( ! IS_ERR ( item ) )
goto found ;
ret = PTR_ERR ( item ) ;
if ( ret = = - EFBIG ) {
u32 item_size ;
/* we found one, but it isn't big enough yet */
leaf = btrfs_buffer_leaf ( path - > nodes [ 0 ] ) ;
item_size = btrfs_item_size ( leaf - > items + path - > slots [ 0 ] ) ;
2007-05-10 20:36:17 +04:00
if ( ( item_size / BTRFS_CRC32_SIZE ) > = MAX_CSUM_ITEMS ( root ) ) {
2007-04-19 00:15:28 +04:00
/* already at max size, make a new one */
goto insert ;
}
} else {
/* we didn't find a csum item, insert one */
goto insert ;
}
/*
* at this point , we know the tree has an item , but it isn ' t big
* enough yet to put our csum in . Grow it
*/
btrfs_release_path ( root , path ) ;
2007-04-16 17:22:45 +04:00
ret = btrfs_search_slot ( trans , root , & file_key , path ,
2007-05-10 20:36:17 +04:00
BTRFS_CRC32_SIZE , 1 ) ;
2007-04-16 17:22:45 +04:00
if ( ret < 0 )
goto fail ;
if ( ret = = 0 ) {
2007-04-17 21:26:50 +04:00
BUG ( ) ;
2007-04-16 17:22:45 +04:00
}
if ( path - > slots [ 0 ] = = 0 ) {
goto insert ;
}
path - > slots [ 0 ] - - ;
leaf = btrfs_buffer_leaf ( path - > nodes [ 0 ] ) ;
btrfs_disk_key_to_cpu ( & found_key , & leaf - > items [ path - > slots [ 0 ] ] . key ) ;
csum_offset = ( offset - found_key . offset ) > >
root - > fs_info - > sb - > s_blocksize_bits ;
if ( btrfs_key_type ( & found_key ) ! = BTRFS_CSUM_ITEM_KEY | |
found_key . objectid ! = objectid | |
csum_offset > = MAX_CSUM_ITEMS ( root ) ) {
goto insert ;
}
if ( csum_offset > = btrfs_item_size ( leaf - > items + path - > slots [ 0 ] ) /
2007-05-10 20:36:17 +04:00
BTRFS_CRC32_SIZE ) {
u32 diff = ( csum_offset + 1 ) * BTRFS_CRC32_SIZE ;
2007-04-19 00:15:28 +04:00
diff = diff - btrfs_item_size ( leaf - > items + path - > slots [ 0 ] ) ;
2007-05-24 21:35:57 +04:00
if ( diff ! = BTRFS_CRC32_SIZE )
goto insert ;
2007-04-19 00:15:28 +04:00
ret = btrfs_extend_item ( trans , root , path , diff ) ;
2007-04-16 17:22:45 +04:00
BUG_ON ( ret ) ;
goto csum ;
}
insert :
2007-04-19 00:15:28 +04:00
btrfs_release_path ( root , path ) ;
2007-04-16 17:22:45 +04:00
csum_offset = 0 ;
2007-04-02 19:20:42 +04:00
ret = btrfs_insert_empty_item ( trans , root , path , & file_key ,
2007-05-10 20:36:17 +04:00
BTRFS_CRC32_SIZE ) ;
2007-04-19 00:15:28 +04:00
if ( ret ! = 0 ) {
WARN_ON ( 1 ) ;
2007-03-29 23:15:27 +04:00
goto fail ;
2007-04-19 00:15:28 +04:00
}
2007-04-16 17:22:45 +04:00
csum :
2007-04-02 19:20:42 +04:00
item = btrfs_item_ptr ( btrfs_buffer_leaf ( path - > nodes [ 0 ] ) , path - > slots [ 0 ] ,
2007-03-29 23:15:27 +04:00
struct btrfs_csum_item ) ;
ret = 0 ;
2007-05-10 20:36:17 +04:00
item = ( struct btrfs_csum_item * ) ( ( unsigned char * ) item +
csum_offset * BTRFS_CRC32_SIZE ) ;
2007-04-17 21:26:50 +04:00
found :
2007-05-10 20:36:17 +04:00
btrfs_check_bounds ( & item - > csum , BTRFS_CRC32_SIZE ,
path - > nodes [ 0 ] - > b_data ,
root - > fs_info - > sb - > s_blocksize ) ;
ret = btrfs_csum_data ( root , data , len , & item - > csum ) ;
2007-04-02 19:20:42 +04:00
btrfs_mark_buffer_dirty ( path - > nodes [ 0 ] ) ;
2007-03-29 23:15:27 +04:00
fail :
2007-04-02 19:20:42 +04:00
btrfs_release_path ( root , path ) ;
btrfs_free_path ( path ) ;
2007-03-29 23:15:27 +04:00
return ret ;
}
2007-05-29 23:17:08 +04:00
int btrfs_csum_truncate ( struct btrfs_trans_handle * trans ,
struct btrfs_root * root , struct btrfs_path * path ,
u64 isize )
{
struct btrfs_key key ;
struct btrfs_leaf * leaf = btrfs_buffer_leaf ( path - > nodes [ 0 ] ) ;
int slot = path - > slots [ 0 ] ;
int ret ;
u32 new_item_size ;
u64 new_item_span ;
u64 blocks ;
btrfs_disk_key_to_cpu ( & key , & leaf - > items [ slot ] . key ) ;
if ( isize < = key . offset )
return 0 ;
new_item_span = isize - key . offset ;
2007-06-12 15:43:08 +04:00
blocks = ( new_item_span + root - > blocksize - 1 ) > >
root - > fs_info - > sb - > s_blocksize_bits ;
2007-05-29 23:17:08 +04:00
new_item_size = blocks * BTRFS_CRC32_SIZE ;
if ( new_item_size > = btrfs_item_size ( leaf - > items + slot ) )
return 0 ;
ret = btrfs_truncate_item ( trans , root , path , new_item_size ) ;
BUG_ON ( ret ) ;
return ret ;
}
2007-03-29 23:15:27 +04:00
int btrfs_csum_verify_file_block ( struct btrfs_root * root ,
u64 objectid , u64 offset ,
char * data , size_t len )
{
int ret ;
struct btrfs_key file_key ;
2007-04-02 19:20:42 +04:00
struct btrfs_path * path ;
2007-03-29 23:15:27 +04:00
struct btrfs_csum_item * item ;
2007-05-10 20:36:17 +04:00
char result [ BTRFS_CRC32_SIZE ] ;
2007-03-29 23:15:27 +04:00
2007-04-02 19:20:42 +04:00
path = btrfs_alloc_path ( ) ;
BUG_ON ( ! path ) ;
2007-03-29 23:15:27 +04:00
file_key . objectid = objectid ;
file_key . offset = offset ;
file_key . flags = 0 ;
btrfs_set_key_type ( & file_key , BTRFS_CSUM_ITEM_KEY ) ;
2007-04-02 23:43:21 +04:00
mutex_lock ( & root - > fs_info - > fs_mutex ) ;
2007-04-16 17:22:45 +04:00
2007-04-17 21:26:50 +04:00
item = btrfs_lookup_csum ( NULL , root , path , objectid , offset , 0 ) ;
2007-04-16 17:22:45 +04:00
if ( IS_ERR ( item ) ) {
ret = PTR_ERR ( item ) ;
2007-04-19 00:15:28 +04:00
/* a csum that isn't present is a preallocated region. */
if ( ret = = - ENOENT | | ret = = - EFBIG )
2007-05-24 21:35:57 +04:00
ret = - ENOENT ;
2007-03-29 23:15:27 +04:00
goto fail ;
2007-04-16 17:22:45 +04:00
}
2007-03-29 23:15:27 +04:00
ret = btrfs_csum_data ( root , data , len , result ) ;
WARN_ON ( ret ) ;
2007-05-10 20:36:17 +04:00
if ( memcmp ( result , & item - > csum , BTRFS_CRC32_SIZE ) )
2007-03-29 23:15:27 +04:00
ret = 1 ;
fail :
2007-04-02 19:20:42 +04:00
btrfs_release_path ( root , path ) ;
btrfs_free_path ( path ) ;
2007-04-02 23:43:21 +04:00
mutex_unlock ( & root - > fs_info - > fs_mutex ) ;
2007-03-29 23:15:27 +04:00
return ret ;
}