2007-03-22 19:13:20 +03:00
# include <linux/module.h>
# include <linux/fs.h>
2007-03-28 21:57:48 +04:00
# include <linux/blkdev.h>
2007-03-29 03:44:27 +04:00
# include <linux/crypto.h>
# include <linux/scatterlist.h>
2007-03-30 16:47:31 +04:00
# include <linux/swap.h>
2007-04-09 18:42:37 +04:00
# include <linux/radix-tree.h>
2007-02-02 17:18:22 +03:00
# include "ctree.h"
# include "disk-io.h"
2007-03-16 23:20:31 +03:00
# include "transaction.h"
2007-04-09 18:42:37 +04:00
# include "btrfs_inode.h"
2007-02-02 17:18:22 +03:00
2007-04-11 23:53:25 +04:00
struct dev_lookup {
u64 block_start ;
u64 num_blocks ;
2007-04-12 20:14:00 +04:00
u64 device_id ;
2007-04-11 23:53:25 +04:00
struct block_device * bdev ;
} ;
2007-04-12 18:43:05 +04:00
int btrfs_insert_dev_radix ( struct btrfs_root * root ,
struct block_device * bdev ,
2007-04-12 20:14:00 +04:00
u64 device_id ,
2007-04-12 18:43:05 +04:00
u64 block_start ,
u64 num_blocks )
{
struct dev_lookup * lookup ;
int ret ;
lookup = kmalloc ( sizeof ( * lookup ) , GFP_NOFS ) ;
if ( ! lookup )
return - ENOMEM ;
lookup - > block_start = block_start ;
lookup - > num_blocks = num_blocks ;
lookup - > bdev = bdev ;
2007-04-12 20:14:00 +04:00
lookup - > device_id = device_id ;
2007-04-12 18:43:05 +04:00
ret = radix_tree_insert ( & root - > fs_info - > dev_radix , block_start +
num_blocks - 1 , lookup ) ;
return ret ;
}
2007-04-11 23:53:25 +04:00
u64 bh_blocknr ( struct buffer_head * bh )
{
int blkbits = bh - > b_page - > mapping - > host - > i_blkbits ;
u64 blocknr = bh - > b_page - > index < < ( PAGE_CACHE_SHIFT - blkbits ) ;
unsigned long offset ;
if ( PageHighMem ( bh - > b_page ) )
offset = ( unsigned long ) bh - > b_data ;
else
offset = bh - > b_data - ( char * ) page_address ( bh - > b_page ) ;
blocknr + = offset > > ( PAGE_CACHE_SHIFT - blkbits ) ;
return blocknr ;
}
2007-03-22 19:13:20 +03:00
static int check_tree_block ( struct btrfs_root * root , struct buffer_head * buf )
2007-02-02 17:18:22 +03:00
{
2007-03-22 19:13:20 +03:00
struct btrfs_node * node = btrfs_buffer_node ( buf ) ;
2007-04-11 23:53:25 +04:00
if ( bh_blocknr ( buf ) ! = btrfs_header_blocknr ( & node - > header ) ) {
2007-04-12 18:43:05 +04:00
printk ( KERN_CRIT " bh_blocknr(buf) is %Lu, header is %Lu \n " ,
bh_blocknr ( buf ) , btrfs_header_blocknr ( & node - > header ) ) ;
2007-02-23 16:38:36 +03:00
BUG ( ) ;
2007-03-28 21:57:48 +04:00
}
2007-02-23 16:38:36 +03:00
return 0 ;
2007-02-02 17:18:22 +03:00
}
2007-03-28 21:57:48 +04:00
struct buffer_head * btrfs_find_tree_block ( struct btrfs_root * root , u64 blocknr )
{
struct address_space * mapping = root - > fs_info - > btree_inode - > i_mapping ;
int blockbits = root - > fs_info - > sb - > s_blocksize_bits ;
unsigned long index = blocknr > > ( PAGE_CACHE_SHIFT - blockbits ) ;
struct page * page ;
struct buffer_head * bh ;
struct buffer_head * head ;
struct buffer_head * ret = NULL ;
2007-04-02 18:50:19 +04:00
2007-03-28 21:57:48 +04:00
page = find_lock_page ( mapping , index ) ;
if ( ! page )
return NULL ;
if ( ! page_has_buffers ( page ) )
goto out_unlock ;
head = page_buffers ( page ) ;
bh = head ;
do {
2007-04-11 23:53:25 +04:00
if ( buffer_mapped ( bh ) & & bh_blocknr ( bh ) = = blocknr ) {
2007-03-28 21:57:48 +04:00
ret = bh ;
get_bh ( bh ) ;
goto out_unlock ;
}
bh = bh - > b_this_page ;
} while ( bh ! = head ) ;
out_unlock :
unlock_page ( page ) ;
2007-03-30 22:27:56 +04:00
if ( ret ) {
2007-03-30 16:47:31 +04:00
touch_buffer ( ret ) ;
2007-03-30 22:27:56 +04:00
}
2007-03-28 21:57:48 +04:00
page_cache_release ( page ) ;
return ret ;
}
2007-04-12 18:43:05 +04:00
int btrfs_map_bh_to_logical ( struct btrfs_root * root , struct buffer_head * bh ,
2007-04-11 23:53:25 +04:00
u64 logical )
{
struct dev_lookup * lookup [ 2 ] ;
int ret ;
2007-04-19 21:37:44 +04:00
if ( logical = = 0 ) {
bh - > b_bdev = NULL ;
bh - > b_blocknr = 0 ;
set_buffer_mapped ( bh ) ;
return 0 ;
}
2007-04-11 23:53:25 +04:00
root = root - > fs_info - > dev_root ;
ret = radix_tree_gang_lookup ( & root - > fs_info - > dev_radix ,
( void * * ) lookup ,
( unsigned long ) logical ,
ARRAY_SIZE ( lookup ) ) ;
if ( ret = = 0 | | lookup [ 0 ] - > block_start > logical | |
lookup [ 0 ] - > block_start + lookup [ 0 ] - > num_blocks < = logical ) {
ret = - ENOENT ;
goto out ;
}
bh - > b_bdev = lookup [ 0 ] - > bdev ;
bh - > b_blocknr = logical - lookup [ 0 ] - > block_start ;
set_buffer_mapped ( bh ) ;
ret = 0 ;
out :
return ret ;
}
2007-03-28 21:57:48 +04:00
struct buffer_head * btrfs_find_create_tree_block ( struct btrfs_root * root ,
u64 blocknr )
{
struct address_space * mapping = root - > fs_info - > btree_inode - > i_mapping ;
int blockbits = root - > fs_info - > sb - > s_blocksize_bits ;
unsigned long index = blocknr > > ( PAGE_CACHE_SHIFT - blockbits ) ;
struct page * page ;
struct buffer_head * bh ;
struct buffer_head * head ;
struct buffer_head * ret = NULL ;
2007-04-11 23:53:25 +04:00
int err ;
2007-03-28 21:57:48 +04:00
u64 first_block = index < < ( PAGE_CACHE_SHIFT - blockbits ) ;
2007-03-30 16:47:31 +04:00
2007-03-28 21:57:48 +04:00
page = grab_cache_page ( mapping , index ) ;
if ( ! page )
return NULL ;
if ( ! page_has_buffers ( page ) )
create_empty_buffers ( page , root - > fs_info - > sb - > s_blocksize , 0 ) ;
head = page_buffers ( page ) ;
bh = head ;
do {
if ( ! buffer_mapped ( bh ) ) {
2007-04-12 18:43:05 +04:00
err = btrfs_map_bh_to_logical ( root , bh , first_block ) ;
2007-04-11 23:53:25 +04:00
BUG_ON ( err ) ;
2007-03-28 21:57:48 +04:00
}
2007-04-11 23:53:25 +04:00
if ( bh_blocknr ( bh ) = = blocknr ) {
2007-03-28 21:57:48 +04:00
ret = bh ;
get_bh ( bh ) ;
goto out_unlock ;
}
bh = bh - > b_this_page ;
first_block + + ;
} while ( bh ! = head ) ;
out_unlock :
unlock_page ( page ) ;
2007-03-30 16:47:31 +04:00
if ( ret )
touch_buffer ( ret ) ;
2007-03-28 21:57:48 +04:00
page_cache_release ( page ) ;
return ret ;
}
static int btree_get_block ( struct inode * inode , sector_t iblock ,
struct buffer_head * bh , int create )
{
2007-04-11 23:53:25 +04:00
int err ;
struct btrfs_root * root = BTRFS_I ( bh - > b_page - > mapping - > host ) - > root ;
2007-04-12 18:43:05 +04:00
err = btrfs_map_bh_to_logical ( root , bh , iblock ) ;
2007-04-11 23:53:25 +04:00
return err ;
2007-03-28 21:57:48 +04:00
}
2007-03-29 23:15:27 +04:00
int btrfs_csum_data ( struct btrfs_root * root , char * data , size_t len ,
char * result )
2007-03-29 03:44:27 +04:00
{
struct scatterlist sg ;
struct crypto_hash * tfm = root - > fs_info - > hash_tfm ;
struct hash_desc desc ;
int ret ;
desc . tfm = tfm ;
desc . flags = 0 ;
2007-03-29 23:15:27 +04:00
sg_init_one ( & sg , data , len ) ;
2007-03-29 03:44:27 +04:00
spin_lock ( & root - > fs_info - > hash_lock ) ;
2007-03-30 16:47:31 +04:00
ret = crypto_hash_digest ( & desc , & sg , 1 , result ) ;
2007-03-29 03:44:27 +04:00
spin_unlock ( & root - > fs_info - > hash_lock ) ;
if ( ret ) {
printk ( " sha256 digest failed \n " ) ;
}
2007-03-29 23:15:27 +04:00
return ret ;
}
static int csum_tree_block ( struct btrfs_root * root , struct buffer_head * bh ,
int verify )
{
char result [ BTRFS_CSUM_SIZE ] ;
int ret ;
struct btrfs_node * node ;
ret = btrfs_csum_data ( root , bh - > b_data + BTRFS_CSUM_SIZE ,
bh - > b_size - BTRFS_CSUM_SIZE , result ) ;
if ( ret )
return ret ;
2007-03-29 03:44:27 +04:00
if ( verify ) {
2007-03-29 23:15:27 +04:00
if ( memcmp ( bh - > b_data , result , BTRFS_CSUM_SIZE ) ) {
2007-04-11 23:53:25 +04:00
printk ( " checksum verify failed on %Lu \n " ,
bh_blocknr ( bh ) ) ;
2007-03-29 23:15:27 +04:00
return 1 ;
}
} else {
node = btrfs_buffer_node ( bh ) ;
2007-03-30 16:47:31 +04:00
memcpy ( node - > header . csum , result , BTRFS_CSUM_SIZE ) ;
2007-03-29 23:15:27 +04:00
}
2007-03-29 03:44:27 +04:00
return 0 ;
}
2007-03-28 21:57:48 +04:00
static int btree_writepage ( struct page * page , struct writeback_control * wbc )
2007-03-02 02:59:40 +03:00
{
2007-03-29 03:44:27 +04:00
struct buffer_head * bh ;
2007-04-09 18:42:37 +04:00
struct btrfs_root * root = BTRFS_I ( page - > mapping - > host ) - > root ;
2007-03-29 03:44:27 +04:00
struct buffer_head * head ;
if ( ! page_has_buffers ( page ) ) {
create_empty_buffers ( page , root - > fs_info - > sb - > s_blocksize ,
( 1 < < BH_Dirty ) | ( 1 < < BH_Uptodate ) ) ;
}
head = page_buffers ( page ) ;
bh = head ;
do {
if ( buffer_dirty ( bh ) )
csum_tree_block ( root , bh , 0 ) ;
bh = bh - > b_this_page ;
} while ( bh ! = head ) ;
2007-03-28 21:57:48 +04:00
return block_write_full_page ( page , btree_get_block , wbc ) ;
2007-03-02 02:59:40 +03:00
}
2007-03-28 21:57:48 +04:00
static int btree_readpage ( struct file * file , struct page * page )
2007-02-02 17:18:22 +03:00
{
2007-03-28 21:57:48 +04:00
return block_read_full_page ( page , btree_get_block ) ;
2007-02-02 17:18:22 +03:00
}
2007-03-28 21:57:48 +04:00
static struct address_space_operations btree_aops = {
. readpage = btree_readpage ,
. writepage = btree_writepage ,
. sync_page = block_sync_page ,
} ;
2007-03-22 19:13:20 +03:00
struct buffer_head * read_tree_block ( struct btrfs_root * root , u64 blocknr )
2007-02-02 17:18:22 +03:00
{
2007-03-28 21:57:48 +04:00
struct buffer_head * bh = NULL ;
2007-02-02 17:18:22 +03:00
2007-03-28 21:57:48 +04:00
bh = btrfs_find_create_tree_block ( root , blocknr ) ;
if ( ! bh )
return bh ;
2007-04-03 19:43:19 +04:00
if ( buffer_uptodate ( bh ) )
goto uptodate ;
2007-03-28 21:57:48 +04:00
lock_buffer ( bh ) ;
if ( ! buffer_uptodate ( bh ) ) {
get_bh ( bh ) ;
bh - > b_end_io = end_buffer_read_sync ;
submit_bh ( READ , bh ) ;
wait_on_buffer ( bh ) ;
if ( ! buffer_uptodate ( bh ) )
goto fail ;
2007-03-29 03:44:27 +04:00
csum_tree_block ( root , bh , 1 ) ;
2007-03-28 21:57:48 +04:00
} else {
unlock_buffer ( bh ) ;
}
2007-04-03 19:43:19 +04:00
uptodate :
2007-03-28 21:57:48 +04:00
if ( check_tree_block ( root , bh ) )
2007-02-22 01:04:57 +03:00
BUG ( ) ;
2007-03-28 21:57:48 +04:00
return bh ;
fail :
brelse ( bh ) ;
return NULL ;
2007-02-02 17:18:22 +03:00
}
2007-03-16 23:20:31 +03:00
int dirty_tree_block ( struct btrfs_trans_handle * trans , struct btrfs_root * root ,
2007-03-22 19:13:20 +03:00
struct buffer_head * buf )
2007-03-02 02:59:40 +03:00
{
2007-03-30 22:27:56 +04:00
WARN_ON ( atomic_read ( & buf - > b_count ) = = 0 ) ;
2007-03-22 19:13:20 +03:00
mark_buffer_dirty ( buf ) ;
2007-03-02 02:59:40 +03:00
return 0 ;
}
2007-03-16 23:20:31 +03:00
int clean_tree_block ( struct btrfs_trans_handle * trans , struct btrfs_root * root ,
2007-03-22 19:13:20 +03:00
struct buffer_head * buf )
2007-03-02 02:59:40 +03:00
{
2007-03-30 22:27:56 +04:00
WARN_ON ( atomic_read ( & buf - > b_count ) = = 0 ) ;
2007-03-22 19:13:20 +03:00
clear_buffer_dirty ( buf ) ;
2007-03-02 02:59:40 +03:00
return 0 ;
}
2007-04-02 18:50:19 +04:00
static int __setup_root ( int blocksize ,
2007-03-20 21:38:32 +03:00
struct btrfs_root * root ,
struct btrfs_fs_info * fs_info ,
2007-03-22 19:13:20 +03:00
u64 objectid )
2007-02-21 00:40:44 +03:00
{
2007-02-22 01:04:57 +03:00
root - > node = NULL ;
2007-04-09 18:42:37 +04:00
root - > inode = NULL ;
2007-03-07 04:08:01 +03:00
root - > commit_root = NULL ;
2007-04-02 18:50:19 +04:00
root - > blocksize = blocksize ;
2007-03-14 21:14:43 +03:00
root - > ref_cows = 0 ;
2007-03-20 21:38:32 +03:00
root - > fs_info = fs_info ;
2007-04-09 18:42:37 +04:00
root - > objectid = objectid ;
root - > last_trans = 0 ;
2007-04-10 20:13:09 +04:00
root - > highest_inode = 0 ;
root - > last_inode_alloc = 0 ;
2007-03-13 23:47:54 +03:00
memset ( & root - > root_key , 0 , sizeof ( root - > root_key ) ) ;
memset ( & root - > root_item , 0 , sizeof ( root - > root_item ) ) ;
return 0 ;
}
2007-04-02 18:50:19 +04:00
static int find_and_setup_root ( int blocksize ,
2007-03-20 21:38:32 +03:00
struct btrfs_root * tree_root ,
struct btrfs_fs_info * fs_info ,
u64 objectid ,
2007-03-22 19:13:20 +03:00
struct btrfs_root * root )
2007-03-13 23:47:54 +03:00
{
int ret ;
2007-04-02 18:50:19 +04:00
__setup_root ( blocksize , root , fs_info , objectid ) ;
2007-03-13 23:47:54 +03:00
ret = btrfs_find_last_root ( tree_root , objectid ,
& root - > root_item , & root - > root_key ) ;
BUG_ON ( ret ) ;
root - > node = read_tree_block ( root ,
btrfs_root_blocknr ( & root - > root_item ) ) ;
BUG_ON ( ! root - > node ) ;
2007-02-21 00:40:44 +03:00
return 0 ;
}
2007-04-09 18:42:37 +04:00
struct btrfs_root * btrfs_read_fs_root ( struct btrfs_fs_info * fs_info ,
struct btrfs_key * location )
{
struct btrfs_root * root ;
struct btrfs_root * tree_root = fs_info - > tree_root ;
struct btrfs_path * path ;
struct btrfs_leaf * l ;
2007-04-10 20:13:09 +04:00
u64 highest_inode ;
2007-04-09 18:42:37 +04:00
int ret = 0 ;
printk ( " read_fs_root looking for %Lu %Lu %u \n " , location - > objectid , location - > offset , location - > flags ) ;
2007-04-11 00:58:11 +04:00
root = radix_tree_lookup ( & fs_info - > fs_roots_radix ,
( unsigned long ) location - > objectid ) ;
if ( root ) {
printk ( " found %p in cache \n " , root ) ;
return root ;
}
2007-04-09 18:42:37 +04:00
root = kmalloc ( sizeof ( * root ) , GFP_NOFS ) ;
if ( ! root ) {
printk ( " failed1 \n " ) ;
return ERR_PTR ( - ENOMEM ) ;
}
if ( location - > offset = = ( u64 ) - 1 ) {
ret = find_and_setup_root ( fs_info - > sb - > s_blocksize ,
fs_info - > tree_root , fs_info ,
location - > objectid , root ) ;
if ( ret ) {
printk ( " failed2 \n " ) ;
kfree ( root ) ;
return ERR_PTR ( ret ) ;
}
goto insert ;
}
__setup_root ( fs_info - > sb - > s_blocksize , root , fs_info ,
location - > objectid ) ;
path = btrfs_alloc_path ( ) ;
BUG_ON ( ! path ) ;
ret = btrfs_search_slot ( NULL , tree_root , location , path , 0 , 0 ) ;
if ( ret ! = 0 ) {
printk ( " internal search_slot gives us %d \n " , ret ) ;
if ( ret > 0 )
ret = - ENOENT ;
goto out ;
}
l = btrfs_buffer_leaf ( path - > nodes [ 0 ] ) ;
memcpy ( & root - > root_item ,
btrfs_item_ptr ( l , path - > slots [ 0 ] , struct btrfs_root_item ) ,
sizeof ( root - > root_item ) ) ;
memcpy ( & root - > root_key , location , sizeof ( * location ) ) ;
ret = 0 ;
out :
btrfs_release_path ( root , path ) ;
btrfs_free_path ( path ) ;
if ( ret ) {
kfree ( root ) ;
return ERR_PTR ( ret ) ;
}
root - > node = read_tree_block ( root ,
btrfs_root_blocknr ( & root - > root_item ) ) ;
BUG_ON ( ! root - > node ) ;
insert :
printk ( " inserting %p \n " , root ) ;
root - > ref_cows = 1 ;
2007-04-11 00:58:11 +04:00
ret = radix_tree_insert ( & fs_info - > fs_roots_radix ,
( unsigned long ) root - > root_key . objectid ,
2007-04-09 18:42:37 +04:00
root ) ;
if ( ret ) {
printk ( " radix_tree_insert gives us %d \n " , ret ) ;
brelse ( root - > node ) ;
kfree ( root ) ;
return ERR_PTR ( ret ) ;
}
2007-04-10 20:13:09 +04:00
ret = btrfs_find_highest_inode ( root , & highest_inode ) ;
if ( ret = = 0 ) {
root - > highest_inode = highest_inode ;
root - > last_inode_alloc = highest_inode ;
printk ( " highest inode is %Lu \n " , highest_inode ) ;
}
2007-04-09 18:42:37 +04:00
printk ( " all worked \n " ) ;
return root ;
}
2007-04-12 20:14:00 +04:00
static int btrfs_open_disk ( struct btrfs_root * root , u64 device_id ,
u64 block_start , u64 num_blocks ,
char * filename , int name_len )
2007-04-12 18:43:05 +04:00
{
char * null_filename ;
struct block_device * bdev ;
int ret ;
null_filename = kmalloc ( name_len + 1 , GFP_NOFS ) ;
if ( ! null_filename )
return - ENOMEM ;
memcpy ( null_filename , filename , name_len ) ;
null_filename [ name_len ] = ' \0 ' ;
bdev = open_bdev_excl ( null_filename , O_RDWR , root - > fs_info - > sb ) ;
if ( IS_ERR ( bdev ) ) {
ret = PTR_ERR ( bdev ) ;
goto out ;
}
set_blocksize ( bdev , root - > fs_info - > sb - > s_blocksize ) ;
2007-04-12 20:14:00 +04:00
ret = btrfs_insert_dev_radix ( root , bdev , device_id ,
block_start , num_blocks ) ;
2007-04-12 18:43:05 +04:00
BUG_ON ( ret ) ;
ret = 0 ;
out :
kfree ( null_filename ) ;
return ret ;
}
static int read_device_info ( struct btrfs_root * root )
{
struct btrfs_path * path ;
int ret ;
struct btrfs_key key ;
struct btrfs_leaf * leaf ;
struct btrfs_device_item * dev_item ;
int nritems ;
int slot ;
root = root - > fs_info - > dev_root ;
path = btrfs_alloc_path ( ) ;
if ( ! path )
return - ENOMEM ;
key . objectid = 0 ;
key . offset = 0 ;
key . flags = 0 ;
btrfs_set_key_type ( & key , BTRFS_DEV_ITEM_KEY ) ;
mutex_lock ( & root - > fs_info - > fs_mutex ) ;
ret = btrfs_search_slot ( NULL , root , & key , path , 0 , 0 ) ;
leaf = btrfs_buffer_leaf ( path - > nodes [ 0 ] ) ;
nritems = btrfs_header_nritems ( & leaf - > header ) ;
while ( 1 ) {
slot = path - > slots [ 0 ] ;
if ( slot > = nritems ) {
ret = btrfs_next_leaf ( root , path ) ;
if ( ret )
break ;
leaf = btrfs_buffer_leaf ( path - > nodes [ 0 ] ) ;
nritems = btrfs_header_nritems ( & leaf - > header ) ;
slot = path - > slots [ 0 ] ;
}
btrfs_disk_key_to_cpu ( & key , & leaf - > items [ slot ] . key ) ;
if ( btrfs_key_type ( & key ) ! = BTRFS_DEV_ITEM_KEY ) {
path - > slots [ 0 ] + + ;
continue ;
}
dev_item = btrfs_item_ptr ( leaf , slot , struct btrfs_device_item ) ;
printk ( " found key %Lu %Lu \n " , key . objectid , key . offset ) ;
2007-04-12 20:14:00 +04:00
if ( btrfs_device_id ( dev_item ) ! =
btrfs_super_device_id ( root - > fs_info - > disk_super ) ) {
ret = btrfs_open_disk ( root , btrfs_device_id ( dev_item ) ,
key . objectid , key . offset ,
( char * ) ( dev_item + 1 ) ,
btrfs_device_pathlen ( dev_item ) ) ;
BUG_ON ( ret ) ;
}
2007-04-12 18:43:05 +04:00
path - > slots [ 0 ] + + ;
}
btrfs_free_path ( path ) ;
mutex_unlock ( & root - > fs_info - > fs_mutex ) ;
return 0 ;
}
2007-04-02 18:50:19 +04:00
struct btrfs_root * open_ctree ( struct super_block * sb )
2007-03-21 18:12:56 +03:00
{
2007-03-22 19:13:20 +03:00
struct btrfs_root * extent_root = kmalloc ( sizeof ( struct btrfs_root ) ,
GFP_NOFS ) ;
2007-04-11 21:57:44 +04:00
struct btrfs_root * dev_root = kmalloc ( sizeof ( struct btrfs_root ) ,
GFP_NOFS ) ;
2007-03-22 19:13:20 +03:00
struct btrfs_root * tree_root = kmalloc ( sizeof ( struct btrfs_root ) ,
GFP_NOFS ) ;
struct btrfs_fs_info * fs_info = kmalloc ( sizeof ( * fs_info ) ,
GFP_NOFS ) ;
2007-02-02 17:18:22 +03:00
int ret ;
2007-04-02 18:50:19 +04:00
struct btrfs_super_block * disk_super ;
2007-04-11 23:53:25 +04:00
struct dev_lookup * dev_lookup ;
2007-02-02 17:18:22 +03:00
2007-03-26 18:15:30 +04:00
init_bit_radix ( & fs_info - > pinned_radix ) ;
init_bit_radix ( & fs_info - > pending_del_radix ) ;
2007-04-09 18:42:37 +04:00
INIT_RADIX_TREE ( & fs_info - > fs_roots_radix , GFP_NOFS ) ;
2007-04-11 23:53:25 +04:00
INIT_RADIX_TREE ( & fs_info - > dev_radix , GFP_NOFS ) ;
2007-04-20 05:01:03 +04:00
INIT_LIST_HEAD ( & fs_info - > trans_list ) ;
2007-04-02 18:50:19 +04:00
sb_set_blocksize ( sb , 4096 ) ;
2007-03-20 21:38:32 +03:00
fs_info - > running_transaction = NULL ;
fs_info - > tree_root = tree_root ;
fs_info - > extent_root = extent_root ;
2007-04-11 21:57:44 +04:00
fs_info - > dev_root = dev_root ;
2007-03-22 19:13:20 +03:00
fs_info - > sb = sb ;
2007-03-28 21:57:48 +04:00
fs_info - > btree_inode = new_inode ( sb ) ;
fs_info - > btree_inode - > i_ino = 1 ;
2007-04-02 18:50:19 +04:00
fs_info - > btree_inode - > i_nlink = 1 ;
2007-03-28 21:57:48 +04:00
fs_info - > btree_inode - > i_size = sb - > s_bdev - > bd_inode - > i_size ;
fs_info - > btree_inode - > i_mapping - > a_ops = & btree_aops ;
2007-04-09 18:42:37 +04:00
BTRFS_I ( fs_info - > btree_inode ) - > root = tree_root ;
memset ( & BTRFS_I ( fs_info - > btree_inode ) - > location , 0 ,
sizeof ( struct btrfs_key ) ) ;
2007-03-30 16:47:31 +04:00
insert_inode_hash ( fs_info - > btree_inode ) ;
2007-03-28 21:57:48 +04:00
mapping_set_gfp_mask ( fs_info - > btree_inode - > i_mapping , GFP_NOFS ) ;
2007-03-29 03:44:27 +04:00
fs_info - > hash_tfm = crypto_alloc_hash ( " sha256 " , 0 , CRYPTO_ALG_ASYNC ) ;
2007-03-29 17:59:15 +04:00
spin_lock_init ( & fs_info - > hash_lock ) ;
if ( ! fs_info - > hash_tfm | | IS_ERR ( fs_info - > hash_tfm ) ) {
2007-03-29 03:44:27 +04:00
printk ( " failed to allocate sha256 hash \n " ) ;
return NULL ;
}
2007-03-22 22:59:16 +03:00
mutex_init ( & fs_info - > trans_mutex ) ;
2007-03-24 02:47:49 +03:00
mutex_init ( & fs_info - > fs_mutex ) ;
2007-03-20 21:38:32 +03:00
memset ( & fs_info - > current_insert , 0 , sizeof ( fs_info - > current_insert ) ) ;
memset ( & fs_info - > last_insert , 0 , sizeof ( fs_info - > last_insert ) ) ;
2007-03-13 23:47:54 +03:00
2007-04-11 21:57:44 +04:00
__setup_root ( sb - > s_blocksize , dev_root ,
fs_info , BTRFS_DEV_TREE_OBJECTID ) ;
2007-04-02 18:50:19 +04:00
__setup_root ( sb - > s_blocksize , tree_root ,
fs_info , BTRFS_ROOT_TREE_OBJECTID ) ;
2007-04-11 23:53:25 +04:00
dev_lookup = kmalloc ( sizeof ( * dev_lookup ) , GFP_NOFS ) ;
dev_lookup - > block_start = 0 ;
dev_lookup - > num_blocks = ( u32 ) - 2 ;
dev_lookup - > bdev = sb - > s_bdev ;
2007-04-12 20:14:00 +04:00
dev_lookup - > device_id = 0 ;
2007-04-11 23:53:25 +04:00
ret = radix_tree_insert ( & fs_info - > dev_radix , ( u32 ) - 2 , dev_lookup ) ;
BUG_ON ( ret ) ;
2007-04-02 18:50:19 +04:00
fs_info - > sb_buffer = read_tree_block ( tree_root ,
BTRFS_SUPER_INFO_OFFSET /
sb - > s_blocksize ) ;
2007-03-28 21:57:48 +04:00
2007-04-09 18:42:37 +04:00
if ( ! fs_info - > sb_buffer )
2007-03-28 21:57:48 +04:00
return NULL ;
disk_super = ( struct btrfs_super_block * ) fs_info - > sb_buffer - > b_data ;
2007-04-09 18:42:37 +04:00
if ( ! btrfs_super_root ( disk_super ) )
2007-04-02 18:50:19 +04:00
return NULL ;
2007-04-09 18:42:37 +04:00
2007-04-12 18:43:05 +04:00
i_size_write ( fs_info - > btree_inode ,
btrfs_super_total_blocks ( disk_super ) < <
fs_info - > btree_inode - > i_blkbits ) ;
2007-04-11 23:53:25 +04:00
radix_tree_delete ( & fs_info - > dev_radix , ( u32 ) - 2 ) ;
dev_lookup - > block_start = btrfs_super_device_block_start ( disk_super ) ;
dev_lookup - > num_blocks = btrfs_super_device_num_blocks ( disk_super ) ;
2007-04-12 20:14:00 +04:00
dev_lookup - > device_id = btrfs_super_device_id ( disk_super ) ;
2007-04-11 23:53:25 +04:00
ret = radix_tree_insert ( & fs_info - > dev_radix ,
dev_lookup - > block_start +
2007-04-12 18:43:05 +04:00
dev_lookup - > num_blocks - 1 , dev_lookup ) ;
2007-04-11 23:53:25 +04:00
BUG_ON ( ret ) ;
2007-03-28 21:57:48 +04:00
fs_info - > disk_super = disk_super ;
2007-04-12 18:43:05 +04:00
2007-04-11 21:57:44 +04:00
dev_root - > node = read_tree_block ( tree_root ,
btrfs_super_device_root ( disk_super ) ) ;
2007-04-12 18:43:05 +04:00
ret = read_device_info ( dev_root ) ;
BUG_ON ( ret ) ;
2007-03-22 19:13:20 +03:00
tree_root - > node = read_tree_block ( tree_root ,
btrfs_super_root ( disk_super ) ) ;
2007-03-13 23:47:54 +03:00
BUG_ON ( ! tree_root - > node ) ;
2007-04-02 18:50:19 +04:00
mutex_lock ( & fs_info - > fs_mutex ) ;
ret = find_and_setup_root ( sb - > s_blocksize , tree_root , fs_info ,
2007-03-22 19:13:20 +03:00
BTRFS_EXTENT_TREE_OBJECTID , extent_root ) ;
2007-03-13 23:47:54 +03:00
BUG_ON ( ret ) ;
2007-04-09 18:42:37 +04:00
fs_info - > generation = btrfs_super_generation ( disk_super ) + 1 ;
2007-04-06 23:37:36 +04:00
memset ( & fs_info - > kobj , 0 , sizeof ( fs_info - > kobj ) ) ;
kobj_set_kset_s ( fs_info , btrfs_subsys ) ;
kobject_set_name ( & fs_info - > kobj , " %s " , sb - > s_id ) ;
kobject_register ( & fs_info - > kobj ) ;
2007-04-05 21:35:25 +04:00
mutex_unlock ( & fs_info - > fs_mutex ) ;
2007-04-09 18:42:37 +04:00
return tree_root ;
2007-02-02 17:18:22 +03:00
}
2007-03-16 23:20:31 +03:00
int write_ctree_super ( struct btrfs_trans_handle * trans , struct btrfs_root
2007-03-22 22:59:16 +03:00
* root )
2007-02-02 17:18:22 +03:00
{
2007-03-23 17:01:08 +03:00
struct buffer_head * bh = root - > fs_info - > sb_buffer ;
2007-04-02 18:50:19 +04:00
2007-03-23 17:01:08 +03:00
btrfs_set_super_root ( root - > fs_info - > disk_super ,
2007-04-11 23:53:25 +04:00
bh_blocknr ( root - > fs_info - > tree_root - > node ) ) ;
2007-03-23 17:01:08 +03:00
lock_buffer ( bh ) ;
2007-04-02 18:50:19 +04:00
WARN_ON ( atomic_read ( & bh - > b_count ) < 1 ) ;
2007-03-23 17:01:08 +03:00
clear_buffer_dirty ( bh ) ;
2007-03-29 03:44:27 +04:00
csum_tree_block ( root , bh , 0 ) ;
2007-03-23 17:01:08 +03:00
bh - > b_end_io = end_buffer_write_sync ;
get_bh ( bh ) ;
submit_bh ( WRITE , bh ) ;
wait_on_buffer ( bh ) ;
if ( ! buffer_uptodate ( bh ) ) {
WARN_ON ( 1 ) ;
return - EIO ;
2007-02-22 01:04:57 +03:00
}
return 0 ;
}
2007-04-11 00:58:11 +04:00
static int free_fs_root ( struct btrfs_fs_info * fs_info , struct btrfs_root * root )
{
radix_tree_delete ( & fs_info - > fs_roots_radix ,
( unsigned long ) root - > root_key . objectid ) ;
if ( root - > inode )
iput ( root - > inode ) ;
if ( root - > node )
brelse ( root - > node ) ;
if ( root - > commit_root )
brelse ( root - > commit_root ) ;
kfree ( root ) ;
return 0 ;
}
2007-04-09 18:42:37 +04:00
int del_fs_roots ( struct btrfs_fs_info * fs_info )
{
int ret ;
struct btrfs_root * gang [ 8 ] ;
int i ;
while ( 1 ) {
ret = radix_tree_gang_lookup ( & fs_info - > fs_roots_radix ,
( void * * ) gang , 0 ,
ARRAY_SIZE ( gang ) ) ;
if ( ! ret )
break ;
2007-04-11 00:58:11 +04:00
for ( i = 0 ; i < ret ; i + + )
free_fs_root ( fs_info , gang [ i ] ) ;
2007-04-09 18:42:37 +04:00
}
return 0 ;
}
2007-04-12 20:14:00 +04:00
2007-04-11 23:53:25 +04:00
static int free_dev_radix ( struct btrfs_fs_info * fs_info )
{
struct dev_lookup * lookup [ 8 ] ;
struct block_device * super_bdev = fs_info - > sb - > s_bdev ;
int ret ;
int i ;
while ( 1 ) {
ret = radix_tree_gang_lookup ( & fs_info - > dev_radix ,
( void * * ) lookup , 0 ,
ARRAY_SIZE ( lookup ) ) ;
if ( ! ret )
break ;
for ( i = 0 ; i < ret ; i + + ) {
if ( lookup [ i ] - > bdev ! = super_bdev )
close_bdev_excl ( lookup [ i ] - > bdev ) ;
radix_tree_delete ( & fs_info - > dev_radix ,
lookup [ i ] - > block_start +
2007-04-12 18:43:05 +04:00
lookup [ i ] - > num_blocks - 1 ) ;
2007-04-11 23:53:25 +04:00
kfree ( lookup [ i ] ) ;
}
}
return 0 ;
}
2007-04-09 18:42:37 +04:00
2007-03-22 19:13:20 +03:00
int close_ctree ( struct btrfs_root * root )
2007-02-22 01:04:57 +03:00
{
2007-03-13 23:47:54 +03:00
int ret ;
2007-03-16 23:20:31 +03:00
struct btrfs_trans_handle * trans ;
2007-04-09 18:42:37 +04:00
struct btrfs_fs_info * fs_info = root - > fs_info ;
2007-03-16 23:20:31 +03:00
2007-04-09 18:42:37 +04:00
mutex_lock ( & fs_info - > fs_mutex ) ;
2007-03-22 22:59:16 +03:00
trans = btrfs_start_transaction ( root , 1 ) ;
btrfs_commit_transaction ( trans , root ) ;
/* run commit again to drop the original snapshot */
trans = btrfs_start_transaction ( root , 1 ) ;
btrfs_commit_transaction ( trans , root ) ;
ret = btrfs_write_and_wait_transaction ( NULL , root ) ;
2007-03-13 23:47:54 +03:00
BUG_ON ( ret ) ;
2007-03-22 22:59:16 +03:00
write_ctree_super ( NULL , root ) ;
2007-04-09 18:42:37 +04:00
mutex_unlock ( & fs_info - > fs_mutex ) ;
if ( fs_info - > extent_root - > node )
btrfs_block_release ( fs_info - > extent_root ,
fs_info - > extent_root - > node ) ;
2007-04-11 21:57:44 +04:00
if ( fs_info - > dev_root - > node )
btrfs_block_release ( fs_info - > dev_root ,
fs_info - > dev_root - > node ) ;
2007-04-09 18:42:37 +04:00
if ( fs_info - > tree_root - > node )
btrfs_block_release ( fs_info - > tree_root ,
fs_info - > tree_root - > node ) ;
btrfs_block_release ( root , fs_info - > sb_buffer ) ;
crypto_free_hash ( fs_info - > hash_tfm ) ;
truncate_inode_pages ( fs_info - > btree_inode - > i_mapping , 0 ) ;
iput ( fs_info - > btree_inode ) ;
2007-04-11 23:53:25 +04:00
free_dev_radix ( fs_info ) ;
2007-04-09 18:42:37 +04:00
del_fs_roots ( fs_info ) ;
kfree ( fs_info - > extent_root ) ;
kfree ( fs_info - > tree_root ) ;
kobject_unregister ( & fs_info - > kobj ) ;
2007-02-02 17:18:22 +03:00
return 0 ;
}
2007-03-22 19:13:20 +03:00
void btrfs_block_release ( struct btrfs_root * root , struct buffer_head * buf )
2007-02-02 17:18:22 +03:00
{
2007-04-02 22:53:59 +04:00
brelse ( buf ) ;
2007-02-02 17:18:22 +03:00
}