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-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-10-16 00:15:53 +04:00
u64 offset , u64 disk_num_bytes ,
u64 num_bytes )
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-10-16 00:14:19 +04:00
struct extent_buffer * leaf ;
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
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-06-22 22:16:25 +04:00
if ( ret < 0 )
goto out ;
2007-03-27 19:26:26 +04:00
BUG_ON ( ret ) ;
2007-10-16 00:14:19 +04:00
leaf = path - > nodes [ 0 ] ;
item = btrfs_item_ptr ( leaf , path - > slots [ 0 ] ,
2007-03-27 00:00:06 +04:00
struct btrfs_file_extent_item ) ;
2007-10-16 00:15:53 +04:00
btrfs_set_file_extent_disk_bytenr ( leaf , item , offset ) ;
btrfs_set_file_extent_disk_num_bytes ( leaf , item , disk_num_bytes ) ;
2007-10-16 00:14:19 +04:00
btrfs_set_file_extent_offset ( leaf , item , 0 ) ;
2007-10-16 00:15:53 +04:00
btrfs_set_file_extent_num_bytes ( leaf , item , num_bytes ) ;
2007-10-16 00:14:19 +04:00
btrfs_set_file_extent_generation ( leaf , item , trans - > transid ) ;
btrfs_set_file_extent_type ( leaf , item , BTRFS_FILE_EXTENT_REG ) ;
btrfs_mark_buffer_dirty ( leaf ) ;
2007-06-22 22:16:25 +04:00
out :
2007-04-02 19:20:42 +04:00
btrfs_free_path ( path ) ;
2007-06-22 22:16:25 +04:00
return ret ;
2007-03-20 21:38:32 +03:00
}
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 ;
2007-10-16 00:14:19 +04:00
struct extent_buffer * leaf ;
2007-04-16 17:22:45 +04:00
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 ;
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 ;
2007-10-16 00:14:19 +04:00
leaf = path - > nodes [ 0 ] ;
2007-04-16 17:22:45 +04:00
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 ] - - ;
2007-10-16 00:14:19 +04:00
btrfs_item_key_to_cpu ( leaf , & found_key , path - > slots [ 0 ] ) ;
2007-04-16 17:22:45 +04:00
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-10-16 00:14:19 +04:00
csums_in_item = btrfs_item_size_nr ( leaf , 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
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 ,
2007-10-25 23:42:56 +04:00
struct inode * inode ,
2007-03-29 23:15:27 +04:00
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-10-25 23:42:56 +04:00
u64 next_offset = ( u64 ) - 1 ;
int found_next = 0 ;
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-10-16 00:22:25 +04:00
struct extent_buffer * leaf = NULL ;
2007-04-16 17:22:45 +04:00
u64 csum_offset ;
2007-10-16 00:22:25 +04:00
u32 csum_result = ~ ( u32 ) 0 ;
2007-10-25 23:42:56 +04:00
u32 nritems ;
u32 ins_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-04-17 21:26:50 +04:00
2007-03-29 23:15:27 +04:00
file_key . objectid = objectid ;
file_key . offset = offset ;
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 ) ;
2007-10-16 00:22:25 +04:00
if ( ! IS_ERR ( item ) ) {
leaf = path - > nodes [ 0 ] ;
2007-04-19 00:15:28 +04:00
goto found ;
2007-10-16 00:22:25 +04:00
}
2007-04-19 00:15:28 +04:00
ret = PTR_ERR ( item ) ;
if ( ret = = - EFBIG ) {
u32 item_size ;
/* we found one, but it isn't big enough yet */
2007-10-16 00:14:19 +04:00
leaf = path - > nodes [ 0 ] ;
item_size = btrfs_item_size_nr ( leaf , 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 {
2007-10-25 23:42:56 +04:00
int slot = path - > slots [ 0 ] + 1 ;
2007-04-19 00:15:28 +04:00
/* we didn't find a csum item, insert one */
2007-10-25 23:42:56 +04:00
nritems = btrfs_header_nritems ( path - > nodes [ 0 ] ) ;
if ( path - > slots [ 0 ] > = nritems - 1 ) {
ret = btrfs_next_leaf ( root , path ) ;
2007-10-29 19:01:05 +03:00
if ( ret = = 1 )
2007-10-25 23:42:56 +04:00
found_next = 1 ;
2007-10-29 19:01:05 +03:00
if ( ret ! = 0 )
2007-10-25 23:42:56 +04:00
goto insert ;
2007-10-29 19:01:05 +03:00
slot = 0 ;
2007-10-25 23:42:56 +04:00
}
btrfs_item_key_to_cpu ( path - > nodes [ 0 ] , & found_key , slot ) ;
if ( found_key . objectid ! = objectid | |
found_key . type ! = BTRFS_CSUM_ITEM_KEY ) {
found_next = 1 ;
goto insert ;
}
next_offset = found_key . offset ;
found_next = 1 ;
2007-04-19 00:15:28 +04:00
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 ] - - ;
2007-10-16 00:14:19 +04:00
leaf = path - > nodes [ 0 ] ;
btrfs_item_key_to_cpu ( leaf , & found_key , path - > slots [ 0 ] ) ;
2007-04-16 17:22:45 +04:00
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 ;
}
2007-10-16 00:14:19 +04:00
if ( csum_offset > = btrfs_item_size_nr ( leaf , path - > slots [ 0 ] ) /
2007-05-10 20:36:17 +04:00
BTRFS_CRC32_SIZE ) {
u32 diff = ( csum_offset + 1 ) * BTRFS_CRC32_SIZE ;
2007-10-16 00:14:19 +04:00
diff = diff - btrfs_item_size_nr ( leaf , 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-10-25 23:42:56 +04:00
if ( found_next ) {
u64 tmp = min ( ( u64 ) i_size_read ( inode ) , next_offset ) ;
2007-10-29 19:01:05 +03:00
tmp - = offset & ~ ( ( u64 ) root - > sectorsize - 1 ) ;
2007-10-25 23:42:56 +04:00
tmp > > = root - > fs_info - > sb - > s_blocksize_bits ;
tmp = max ( ( u64 ) 1 , tmp ) ;
tmp = min ( tmp , ( u64 ) MAX_CSUM_ITEMS ( root ) ) ;
ins_size = BTRFS_CRC32_SIZE * tmp ;
} else {
ins_size = BTRFS_CRC32_SIZE ;
}
2007-04-02 19:20:42 +04:00
ret = btrfs_insert_empty_item ( trans , root , path , & file_key ,
2007-10-25 23:42:56 +04:00
ins_size ) ;
2007-06-22 22:16:25 +04:00
if ( ret < 0 )
goto fail ;
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-10-16 00:14:19 +04:00
leaf = path - > nodes [ 0 ] ;
item = btrfs_item_ptr ( leaf , path - > slots [ 0 ] , struct btrfs_csum_item ) ;
2007-03-29 23:15:27 +04:00
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-10-16 00:22:25 +04:00
csum_result = btrfs_csum_data ( root , data , csum_result , len ) ;
btrfs_csum_final ( csum_result , ( char * ) & csum_result ) ;
write_extent_buffer ( leaf , & csum_result , ( unsigned long ) item ,
BTRFS_CRC32_SIZE ) ;
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 ;
2007-10-16 00:14:19 +04:00
struct extent_buffer * leaf = path - > nodes [ 0 ] ;
2007-05-29 23:17:08 +04:00
int slot = path - > slots [ 0 ] ;
int ret ;
u32 new_item_size ;
u64 new_item_span ;
u64 blocks ;
2007-10-16 00:14:19 +04:00
btrfs_item_key_to_cpu ( leaf , & key , slot ) ;
2007-05-29 23:17:08 +04:00
if ( isize < = key . offset )
return 0 ;
new_item_span = isize - key . offset ;
2007-10-16 00:14:19 +04:00
blocks = ( new_item_span + root - > sectorsize - 1 ) > >
2007-06-12 15:43:08 +04:00
root - > fs_info - > sb - > s_blocksize_bits ;
2007-05-29 23:17:08 +04:00
new_item_size = blocks * BTRFS_CRC32_SIZE ;
2007-10-16 00:14:19 +04:00
if ( new_item_size > = btrfs_item_size_nr ( leaf , slot ) )
2007-05-29 23:17:08 +04:00
return 0 ;
ret = btrfs_truncate_item ( trans , root , path , new_item_size ) ;
BUG_ON ( ret ) ;
return ret ;
}