2007-02-02 17:18:22 +03:00
# define _XOPEN_SOURCE 500
# include <stdio.h>
# include <stdlib.h>
# include <sys/types.h>
# include <sys/stat.h>
# include <fcntl.h>
# include <unistd.h>
# include "kerncompat.h"
# include "radix-tree.h"
# include "ctree.h"
# include "disk-io.h"
static int allocated_blocks = 0 ;
2007-03-02 02:59:40 +03:00
int cache_max = 10000 ;
2007-02-02 17:18:22 +03:00
2007-03-13 17:46:10 +03:00
static int check_tree_block ( struct btrfs_root * root , struct btrfs_buffer * buf )
2007-02-02 17:18:22 +03:00
{
2007-03-12 19:01:18 +03:00
if ( buf - > blocknr ! = btrfs_header_blocknr ( & buf - > node . header ) )
2007-02-23 16:38:36 +03:00
BUG ( ) ;
2007-03-12 19:01:18 +03:00
if ( root - > node & & btrfs_header_parentid ( & buf - > node . header ) ! =
btrfs_header_parentid ( & root - > node - > node . header ) )
2007-02-23 16:38:36 +03:00
BUG ( ) ;
return 0 ;
2007-02-02 17:18:22 +03:00
}
2007-03-13 17:46:10 +03:00
static int free_some_buffers ( struct btrfs_root * root )
2007-03-02 02:59:40 +03:00
{
struct list_head * node , * next ;
2007-03-13 17:46:10 +03:00
struct btrfs_buffer * b ;
2007-03-02 02:59:40 +03:00
if ( root - > cache_size < cache_max )
return 0 ;
list_for_each_safe ( node , next , & root - > cache ) {
2007-03-13 17:46:10 +03:00
b = list_entry ( node , struct btrfs_buffer , cache ) ;
2007-03-02 02:59:40 +03:00
if ( b - > count = = 1 ) {
BUG_ON ( ! list_empty ( & b - > dirty ) ) ;
list_del_init ( & b - > cache ) ;
2007-03-13 17:46:10 +03:00
btrfs_block_release ( root , b ) ;
2007-03-02 02:59:40 +03:00
if ( root - > cache_size < cache_max )
2007-03-02 18:06:43 +03:00
break ;
2007-03-02 02:59:40 +03:00
}
}
return 0 ;
}
2007-03-13 17:46:10 +03:00
struct btrfs_buffer * alloc_tree_block ( struct btrfs_root * root , u64 blocknr )
2007-02-02 17:18:22 +03:00
{
2007-03-13 17:46:10 +03:00
struct btrfs_buffer * buf ;
2007-02-02 17:18:22 +03:00
int ret ;
2007-03-14 21:14:43 +03:00
buf = malloc ( sizeof ( struct btrfs_buffer ) + root - > blocksize ) ;
2007-02-02 17:18:22 +03:00
if ( ! buf )
return buf ;
allocated_blocks + + ;
buf - > blocknr = blocknr ;
2007-03-02 02:59:40 +03:00
buf - > count = 2 ;
INIT_LIST_HEAD ( & buf - > dirty ) ;
free_some_buffers ( root ) ;
2007-02-02 17:18:22 +03:00
radix_tree_preload ( GFP_KERNEL ) ;
ret = radix_tree_insert ( & root - > cache_radix , blocknr , buf ) ;
radix_tree_preload_end ( ) ;
2007-03-02 02:59:40 +03:00
list_add_tail ( & buf - > cache , & root - > cache ) ;
root - > cache_size + + ;
2007-02-02 17:18:22 +03:00
if ( ret ) {
free ( buf ) ;
return NULL ;
}
return buf ;
}
2007-03-13 17:46:10 +03:00
struct btrfs_buffer * find_tree_block ( struct btrfs_root * root , u64 blocknr )
2007-02-02 17:18:22 +03:00
{
2007-03-13 17:46:10 +03:00
struct btrfs_buffer * buf ;
2007-02-23 16:38:36 +03:00
buf = radix_tree_lookup ( & root - > cache_radix , blocknr ) ;
if ( buf ) {
buf - > count + + ;
} else {
buf = alloc_tree_block ( root , blocknr ) ;
if ( ! buf ) {
BUG ( ) ;
return NULL ;
}
2007-02-02 17:18:22 +03:00
}
return buf ;
}
2007-03-13 17:46:10 +03:00
struct btrfs_buffer * read_tree_block ( struct btrfs_root * root , u64 blocknr )
2007-02-02 17:18:22 +03:00
{
2007-03-14 21:14:43 +03:00
loff_t offset = blocknr * root - > blocksize ;
2007-03-13 17:46:10 +03:00
struct btrfs_buffer * buf ;
2007-02-02 17:18:22 +03:00
int ret ;
buf = radix_tree_lookup ( & root - > cache_radix , blocknr ) ;
if ( buf ) {
buf - > count + + ;
2007-02-23 16:38:36 +03:00
} else {
buf = alloc_tree_block ( root , blocknr ) ;
if ( ! buf )
return NULL ;
2007-03-14 21:14:43 +03:00
ret = pread ( root - > fp , & buf - > node , root - > blocksize , offset ) ;
if ( ret ! = root - > blocksize ) {
2007-02-23 16:38:36 +03:00
free ( buf ) ;
return NULL ;
}
2007-02-02 17:18:22 +03:00
}
2007-02-23 16:38:36 +03:00
if ( check_tree_block ( root , buf ) )
2007-02-22 01:04:57 +03:00
BUG ( ) ;
2007-02-02 17:18:22 +03:00
return buf ;
}
2007-03-13 17:46:10 +03:00
int dirty_tree_block ( struct btrfs_root * root , struct btrfs_buffer * buf )
2007-03-02 02:59:40 +03:00
{
if ( ! list_empty ( & buf - > dirty ) )
return 0 ;
list_add_tail ( & buf - > dirty , & root - > trans ) ;
buf - > count + + ;
return 0 ;
}
2007-03-13 17:46:10 +03:00
int clean_tree_block ( struct btrfs_root * root , struct btrfs_buffer * buf )
2007-03-02 02:59:40 +03:00
{
if ( ! list_empty ( & buf - > dirty ) ) {
list_del_init ( & buf - > dirty ) ;
2007-03-13 17:46:10 +03:00
btrfs_block_release ( root , buf ) ;
2007-03-02 02:59:40 +03:00
}
return 0 ;
}
2007-03-13 17:46:10 +03:00
int write_tree_block ( struct btrfs_root * root , struct btrfs_buffer * buf )
2007-02-02 17:18:22 +03:00
{
u64 blocknr = buf - > blocknr ;
2007-03-14 21:14:43 +03:00
loff_t offset = blocknr * root - > blocksize ;
2007-02-02 17:18:22 +03:00
int ret ;
2007-03-12 19:01:18 +03:00
if ( buf - > blocknr ! = btrfs_header_blocknr ( & buf - > node . header ) )
2007-02-02 17:18:22 +03:00
BUG ( ) ;
2007-03-14 21:14:43 +03:00
ret = pwrite ( root - > fp , & buf - > node , root - > blocksize , offset ) ;
if ( ret ! = root - > blocksize )
2007-02-02 17:18:22 +03:00
return ret ;
return 0 ;
}
2007-03-13 17:46:10 +03:00
static int __commit_transaction ( struct btrfs_root * root )
2007-03-02 02:59:40 +03:00
{
2007-03-13 17:46:10 +03:00
struct btrfs_buffer * b ;
2007-03-02 02:59:40 +03:00
int ret = 0 ;
int wret ;
while ( ! list_empty ( & root - > trans ) ) {
2007-03-13 17:46:10 +03:00
b = list_entry ( root - > trans . next , struct btrfs_buffer , dirty ) ;
2007-03-02 02:59:40 +03:00
list_del_init ( & b - > dirty ) ;
wret = write_tree_block ( root , b ) ;
if ( wret )
ret = wret ;
2007-03-13 17:46:10 +03:00
btrfs_block_release ( root , b ) ;
2007-03-02 02:59:40 +03:00
}
return ret ;
}
2007-03-13 23:47:54 +03:00
static int commit_extent_and_tree_roots ( struct btrfs_root * tree_root ,
struct btrfs_root * extent_root )
{
int ret ;
u64 old_extent_block ;
while ( 1 ) {
old_extent_block = btrfs_root_blocknr ( & extent_root - > root_item ) ;
if ( old_extent_block = = extent_root - > node - > blocknr )
break ;
btrfs_set_root_blocknr ( & extent_root - > root_item ,
extent_root - > node - > blocknr ) ;
ret = btrfs_update_root ( tree_root ,
& extent_root - > root_key ,
& extent_root - > root_item ) ;
BUG_ON ( ret ) ;
}
__commit_transaction ( extent_root ) ;
__commit_transaction ( tree_root ) ;
return 0 ;
}
2007-03-13 17:46:10 +03:00
int btrfs_commit_transaction ( struct btrfs_root * root ,
struct btrfs_super_block * s )
2007-03-02 02:59:40 +03:00
{
2007-03-07 04:08:01 +03:00
int ret = 0 ;
2007-03-13 23:47:54 +03:00
struct btrfs_buffer * snap = root - > commit_root ;
struct btrfs_key snap_key ;
2007-03-07 04:08:01 +03:00
2007-03-02 02:59:40 +03:00
ret = __commit_transaction ( root ) ;
BUG_ON ( ret ) ;
2007-03-13 23:47:54 +03:00
if ( root - > commit_root = = root - > node )
return 0 ;
memcpy ( & snap_key , & root - > root_key , sizeof ( snap_key ) ) ;
root - > root_key . offset + + ;
btrfs_set_root_blocknr ( & root - > root_item , root - > node - > blocknr ) ;
ret = btrfs_insert_root ( root - > tree_root , & root - > root_key ,
& root - > root_item ) ;
BUG_ON ( ret ) ;
ret = commit_extent_and_tree_roots ( root - > tree_root , root - > extent_root ) ;
BUG_ON ( ret ) ;
2007-03-07 04:08:01 +03:00
write_ctree_super ( root , s ) ;
2007-03-13 23:47:54 +03:00
btrfs_finish_extent_commit ( root - > extent_root ) ;
btrfs_finish_extent_commit ( root - > tree_root ) ;
root - > commit_root = root - > node ;
root - > node - > count + + ;
ret = btrfs_drop_snapshot ( root , snap ) ;
BUG_ON ( ret ) ;
ret = btrfs_del_root ( root - > tree_root , & snap_key ) ;
BUG_ON ( ret ) ;
2007-03-02 02:59:40 +03:00
return ret ;
}
2007-03-14 21:14:43 +03:00
static int __setup_root ( struct btrfs_super_block * super ,
struct btrfs_root * root , u64 objectid , int fp )
2007-02-21 00:40:44 +03:00
{
2007-03-02 02:59:40 +03:00
INIT_LIST_HEAD ( & root - > trans ) ;
INIT_LIST_HEAD ( & root - > cache ) ;
2007-03-07 04:08:01 +03:00
root - > cache_size = 0 ;
2007-02-21 00:40:44 +03:00
root - > fp = fp ;
2007-02-22 01:04:57 +03:00
root - > node = NULL ;
2007-03-07 04:08:01 +03:00
root - > commit_root = NULL ;
2007-03-14 21:14:43 +03:00
root - > blocksize = btrfs_super_blocksize ( super ) ;
root - > ref_cows = 0 ;
2007-03-07 04:08:01 +03:00
memset ( & root - > current_insert , 0 , sizeof ( root - > current_insert ) ) ;
2007-03-08 00:15:30 +03:00
memset ( & root - > last_insert , 0 , sizeof ( root - > last_insert ) ) ;
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-03-14 21:14:43 +03:00
static int find_and_setup_root ( struct btrfs_super_block * super ,
struct btrfs_root * tree_root , u64 objectid ,
struct btrfs_root * root , int fp )
2007-03-13 23:47:54 +03:00
{
int ret ;
2007-03-14 21:14:43 +03:00
__setup_root ( super , root , objectid , fp ) ;
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-03-13 17:46:10 +03:00
struct btrfs_root * open_ctree ( char * filename , struct btrfs_super_block * super )
2007-02-02 17:18:22 +03:00
{
2007-03-13 17:46:10 +03:00
struct btrfs_root * root = malloc ( sizeof ( struct btrfs_root ) ) ;
struct btrfs_root * extent_root = malloc ( sizeof ( struct btrfs_root ) ) ;
2007-03-13 23:47:54 +03:00
struct btrfs_root * tree_root = malloc ( sizeof ( struct btrfs_root ) ) ;
2007-02-02 17:18:22 +03:00
int fp ;
int ret ;
2007-03-13 23:47:54 +03:00
root - > extent_root = extent_root ;
root - > tree_root = tree_root ;
extent_root - > extent_root = extent_root ;
extent_root - > tree_root = tree_root ;
tree_root - > extent_root = extent_root ;
tree_root - > tree_root = tree_root ;
2007-02-26 18:46:55 +03:00
fp = open ( filename , O_CREAT | O_RDWR , 0600 ) ;
2007-02-02 17:18:22 +03:00
if ( fp < 0 ) {
free ( root ) ;
return NULL ;
}
2007-02-23 16:38:36 +03:00
INIT_RADIX_TREE ( & root - > cache_radix , GFP_KERNEL ) ;
2007-03-07 04:08:01 +03:00
INIT_RADIX_TREE ( & root - > pinned_radix , GFP_KERNEL ) ;
INIT_RADIX_TREE ( & extent_root - > pinned_radix , GFP_KERNEL ) ;
2007-02-23 16:38:36 +03:00
INIT_RADIX_TREE ( & extent_root - > cache_radix , GFP_KERNEL ) ;
2007-03-13 23:47:54 +03:00
INIT_RADIX_TREE ( & tree_root - > pinned_radix , GFP_KERNEL ) ;
INIT_RADIX_TREE ( & tree_root - > cache_radix , GFP_KERNEL ) ;
2007-03-13 17:46:10 +03:00
ret = pread ( fp , super , sizeof ( struct btrfs_super_block ) ,
2007-03-14 21:14:43 +03:00
BTRFS_SUPER_INFO_OFFSET ) ;
2007-03-13 23:47:54 +03:00
if ( ret = = 0 | | btrfs_super_root ( super ) = = 0 ) {
2007-02-22 19:39:13 +03:00
printf ( " making new FS! \n " ) ;
2007-03-14 21:14:43 +03:00
ret = mkfs ( fp , 0 , 1024 ) ;
2007-02-21 00:40:44 +03:00
if ( ret )
return NULL ;
2007-03-13 17:46:10 +03:00
ret = pread ( fp , super , sizeof ( struct btrfs_super_block ) ,
2007-03-14 21:14:43 +03:00
BTRFS_SUPER_INFO_OFFSET ) ;
2007-03-13 17:46:10 +03:00
if ( ret ! = sizeof ( struct btrfs_super_block ) )
2007-02-21 00:40:44 +03:00
return NULL ;
}
BUG_ON ( ret < 0 ) ;
2007-03-13 23:47:54 +03:00
2007-03-14 21:14:43 +03:00
__setup_root ( super , tree_root , BTRFS_ROOT_TREE_OBJECTID , fp ) ;
2007-03-13 23:47:54 +03:00
tree_root - > node = read_tree_block ( tree_root , btrfs_super_root ( super ) ) ;
BUG_ON ( ! tree_root - > node ) ;
2007-03-14 21:14:43 +03:00
ret = find_and_setup_root ( super , tree_root , BTRFS_EXTENT_TREE_OBJECTID ,
2007-03-13 23:47:54 +03:00
extent_root , fp ) ;
BUG_ON ( ret ) ;
2007-03-14 21:14:43 +03:00
ret = find_and_setup_root ( super , tree_root , BTRFS_FS_TREE_OBJECTID ,
2007-03-13 23:47:54 +03:00
root , fp ) ;
BUG_ON ( ret ) ;
2007-03-07 04:08:01 +03:00
root - > commit_root = root - > node ;
root - > node - > count + + ;
2007-03-13 23:47:54 +03:00
root - > ref_cows = 1 ;
2007-02-02 17:18:22 +03:00
return root ;
}
2007-03-13 17:46:10 +03:00
int write_ctree_super ( struct btrfs_root * root , struct btrfs_super_block * s )
2007-02-02 17:18:22 +03:00
{
int ret ;
2007-03-13 23:47:54 +03:00
btrfs_set_super_root ( s , root - > tree_root - > node - > blocknr ) ;
2007-03-13 17:46:10 +03:00
ret = pwrite ( root - > fp , s , sizeof ( * s ) ,
2007-03-14 21:14:43 +03:00
BTRFS_SUPER_INFO_OFFSET ) ;
2007-02-22 01:04:57 +03:00
if ( ret ! = sizeof ( * s ) ) {
fprintf ( stderr , " failed to write new super block err %d \n " , ret ) ;
2007-02-02 17:18:22 +03:00
return ret ;
2007-02-22 01:04:57 +03:00
}
return 0 ;
}
2007-03-13 17:46:10 +03:00
static int drop_cache ( struct btrfs_root * root )
2007-03-02 02:59:40 +03:00
{
while ( ! list_empty ( & root - > cache ) ) {
2007-03-13 17:46:10 +03:00
struct btrfs_buffer * b = list_entry ( root - > cache . next ,
struct btrfs_buffer , cache ) ;
2007-03-02 02:59:40 +03:00
list_del_init ( & b - > cache ) ;
2007-03-13 17:46:10 +03:00
btrfs_block_release ( root , b ) ;
2007-03-02 02:59:40 +03:00
}
return 0 ;
}
2007-03-13 17:46:10 +03:00
int close_ctree ( struct btrfs_root * root , struct btrfs_super_block * s )
2007-02-22 01:04:57 +03:00
{
2007-03-13 23:47:54 +03:00
int ret ;
2007-03-13 17:46:10 +03:00
btrfs_commit_transaction ( root , s ) ;
2007-03-13 23:47:54 +03:00
ret = commit_extent_and_tree_roots ( root - > tree_root , root - > extent_root ) ;
BUG_ON ( ret ) ;
2007-03-07 04:08:01 +03:00
write_ctree_super ( root , s ) ;
2007-03-02 02:59:40 +03:00
drop_cache ( root - > extent_root ) ;
2007-03-13 23:47:54 +03:00
drop_cache ( root - > tree_root ) ;
2007-03-02 02:59:40 +03:00
drop_cache ( root ) ;
BUG_ON ( ! list_empty ( & root - > trans ) ) ;
BUG_ON ( ! list_empty ( & root - > extent_root - > trans ) ) ;
2007-03-13 23:47:54 +03:00
BUG_ON ( ! list_empty ( & root - > tree_root - > trans ) ) ;
2007-03-02 02:59:40 +03:00
2007-02-22 01:04:57 +03:00
close ( root - > fp ) ;
if ( root - > node )
2007-03-13 17:46:10 +03:00
btrfs_block_release ( root , root - > node ) ;
2007-02-22 01:04:57 +03:00
if ( root - > extent_root - > node )
2007-03-13 17:46:10 +03:00
btrfs_block_release ( root - > extent_root , root - > extent_root - > node ) ;
2007-03-13 23:47:54 +03:00
if ( root - > tree_root - > node )
btrfs_block_release ( root - > tree_root , root - > tree_root - > node ) ;
2007-03-13 17:46:10 +03:00
btrfs_block_release ( root , root - > commit_root ) ;
2007-02-22 01:04:57 +03:00
free ( root ) ;
printf ( " on close %d blocks are allocated \n " , allocated_blocks ) ;
2007-02-02 17:18:22 +03:00
return 0 ;
}
2007-03-13 17:46:10 +03:00
void btrfs_block_release ( struct btrfs_root * root , struct btrfs_buffer * buf )
2007-02-02 17:18:22 +03:00
{
buf - > count - - ;
2007-02-22 01:04:57 +03:00
if ( buf - > count < 0 )
BUG ( ) ;
2007-02-02 17:18:22 +03:00
if ( buf - > count = = 0 ) {
2007-03-03 00:08:05 +03:00
BUG_ON ( ! list_empty ( & buf - > cache ) ) ;
BUG_ON ( ! list_empty ( & buf - > dirty ) ) ;
2007-02-02 17:18:22 +03:00
if ( ! radix_tree_lookup ( & root - > cache_radix , buf - > blocknr ) )
BUG ( ) ;
radix_tree_delete ( & root - > cache_radix , buf - > blocknr ) ;
memset ( buf , 0 , sizeof ( * buf ) ) ;
free ( buf ) ;
BUG_ON ( allocated_blocks = = 0 ) ;
allocated_blocks - - ;
2007-03-02 02:59:40 +03:00
BUG_ON ( root - > cache_size = = 0 ) ;
root - > cache_size - - ;
2007-02-02 17:18:22 +03:00
}
}