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-15 19:56:47 +03:00
# include "ctree.h"
# include "disk-io.h"
# include "hash.h"
2007-03-16 23:20:31 +03:00
# include "transaction.h"
2007-03-15 19:56:47 +03:00
2007-05-02 23:53:43 +04:00
static struct btrfs_dir_item * insert_with_overflow ( struct btrfs_trans_handle
* trans ,
struct btrfs_root * root ,
struct btrfs_path * path ,
struct btrfs_key * cpu_key ,
2007-05-23 23:44:28 +04:00
u32 data_size ,
const char * name ,
int name_len )
2007-04-05 20:13:21 +04:00
{
int ret ;
2007-04-19 23:36:27 +04:00
char * ptr ;
struct btrfs_item * item ;
2007-10-16 00:14:19 +04:00
struct extent_buffer * leaf ;
2007-04-05 20:13:21 +04:00
ret = btrfs_insert_empty_item ( trans , root , path , cpu_key , data_size ) ;
2007-04-19 23:36:27 +04:00
if ( ret = = - EEXIST ) {
2007-05-23 23:44:28 +04:00
struct btrfs_dir_item * di ;
di = btrfs_match_dir_item_name ( root , path , name , name_len ) ;
if ( di )
return ERR_PTR ( - EEXIST ) ;
2007-04-19 23:36:27 +04:00
ret = btrfs_extend_item ( trans , root , path , data_size ) ;
WARN_ON ( ret > 0 ) ;
if ( ret )
return ERR_PTR ( ret ) ;
2007-04-05 20:13:21 +04:00
}
2007-06-22 22:16:25 +04:00
if ( ret < 0 )
return ERR_PTR ( ret ) ;
2007-04-19 23:36:27 +04:00
WARN_ON ( ret > 0 ) ;
2007-10-16 00:14:19 +04:00
leaf = path - > nodes [ 0 ] ;
item = btrfs_item_nr ( leaf , path - > slots [ 0 ] ) ;
2007-04-19 23:36:27 +04:00
ptr = btrfs_item_ptr ( leaf , path - > slots [ 0 ] , char ) ;
2007-10-16 00:14:19 +04:00
BUG_ON ( data_size > btrfs_item_size ( leaf , item ) ) ;
ptr + = btrfs_item_size ( leaf , item ) - data_size ;
2007-04-19 23:36:27 +04:00
return ( struct btrfs_dir_item * ) ptr ;
2007-04-05 20:13:21 +04:00
}
2007-03-16 23:20:31 +03:00
int btrfs_insert_dir_item ( struct btrfs_trans_handle * trans , struct btrfs_root
2007-04-06 23:37:36 +04:00
* root , const char * name , int name_len , u64 dir ,
struct btrfs_key * location , u8 type )
2007-03-15 19:56:47 +03:00
{
int ret = 0 ;
2007-05-23 23:44:28 +04:00
int ret2 = 0 ;
2007-04-02 19:20:42 +04:00
struct btrfs_path * path ;
2007-03-15 19:56:47 +03:00
struct btrfs_dir_item * dir_item ;
2007-10-16 00:14:19 +04:00
struct extent_buffer * leaf ;
unsigned long name_ptr ;
2007-03-15 19:56:47 +03:00
struct btrfs_key key ;
2007-10-16 00:14:19 +04:00
struct btrfs_disk_key disk_key ;
2007-03-15 19:56:47 +03:00
u32 data_size ;
key . objectid = dir ;
2007-03-15 22:18:43 +03:00
btrfs_set_key_type ( & key , BTRFS_DIR_ITEM_KEY ) ;
2007-03-22 19:13:20 +03:00
ret = btrfs_name_hash ( name , name_len , & key . offset ) ;
2007-03-15 19:56:47 +03:00
BUG_ON ( ret ) ;
2007-04-02 19:20:42 +04:00
path = btrfs_alloc_path ( ) ;
2007-03-15 19:56:47 +03:00
data_size = sizeof ( * dir_item ) + name_len ;
2007-05-23 23:44:28 +04:00
dir_item = insert_with_overflow ( trans , root , path , & key , data_size ,
name , name_len ) ;
2007-04-19 23:36:27 +04:00
if ( IS_ERR ( dir_item ) ) {
ret = PTR_ERR ( dir_item ) ;
2007-05-23 23:44:28 +04:00
if ( ret = = - EEXIST )
goto second_insert ;
2007-03-15 19:56:47 +03:00
goto out ;
2007-04-19 23:36:27 +04:00
}
2007-03-15 19:56:47 +03:00
2007-10-16 00:14:19 +04:00
leaf = path - > nodes [ 0 ] ;
btrfs_cpu_key_to_disk ( & disk_key , location ) ;
btrfs_set_dir_item_key ( leaf , dir_item , & disk_key ) ;
btrfs_set_dir_type ( leaf , dir_item , type ) ;
btrfs_set_dir_flags ( leaf , dir_item , 0 ) ;
btrfs_set_dir_name_len ( leaf , dir_item , name_len ) ;
name_ptr = ( unsigned long ) ( dir_item + 1 ) ;
2007-04-10 17:27:04 +04:00
2007-10-16 00:14:19 +04:00
write_extent_buffer ( leaf , name , name_ptr , name_len ) ;
btrfs_mark_buffer_dirty ( leaf ) ;
2007-04-19 23:36:27 +04:00
2007-05-23 23:44:28 +04:00
second_insert :
2007-04-19 23:36:27 +04:00
/* FIXME, use some real flag for selecting the extra index */
if ( root = = root - > fs_info - > tree_root ) {
ret = 0 ;
goto out ;
}
2007-04-02 19:20:42 +04:00
btrfs_release_path ( root , path ) ;
2007-04-19 23:36:27 +04:00
btrfs_set_key_type ( & key , BTRFS_DIR_INDEX_KEY ) ;
key . offset = location - > objectid ;
2007-05-23 23:44:28 +04:00
dir_item = insert_with_overflow ( trans , root , path , & key , data_size ,
name , name_len ) ;
2007-04-19 23:36:27 +04:00
if ( IS_ERR ( dir_item ) ) {
2007-05-23 23:44:28 +04:00
ret2 = PTR_ERR ( dir_item ) ;
2007-04-19 23:36:27 +04:00
goto out ;
}
2007-10-16 00:14:19 +04:00
leaf = path - > nodes [ 0 ] ;
btrfs_cpu_key_to_disk ( & disk_key , location ) ;
btrfs_set_dir_item_key ( leaf , dir_item , & disk_key ) ;
btrfs_set_dir_type ( leaf , dir_item , type ) ;
btrfs_set_dir_flags ( leaf , dir_item , 0 ) ;
btrfs_set_dir_name_len ( leaf , dir_item , name_len ) ;
name_ptr = ( unsigned long ) ( dir_item + 1 ) ;
write_extent_buffer ( leaf , name , name_ptr , name_len ) ;
btrfs_mark_buffer_dirty ( leaf ) ;
2007-04-19 23:36:27 +04:00
out :
2007-04-02 19:20:42 +04:00
btrfs_free_path ( path ) ;
2007-05-23 23:44:28 +04:00
if ( ret )
return ret ;
if ( ret2 )
return ret2 ;
return 0 ;
2007-03-15 19:56:47 +03:00
}
2007-04-19 23:36:27 +04:00
struct btrfs_dir_item * btrfs_lookup_dir_item ( struct btrfs_trans_handle * trans ,
struct btrfs_root * root ,
struct btrfs_path * path , u64 dir ,
const char * name , int name_len ,
int mod )
2007-03-15 19:56:47 +03:00
{
2007-03-15 22:18:43 +03:00
int ret ;
2007-03-15 19:56:47 +03:00
struct btrfs_key key ;
2007-03-15 22:18:43 +03:00
int ins_len = mod < 0 ? - 1 : 0 ;
int cow = mod ! = 0 ;
2007-10-16 00:14:19 +04:00
struct btrfs_key found_key ;
struct extent_buffer * leaf ;
2007-03-15 19:56:47 +03:00
key . objectid = dir ;
2007-03-15 22:18:43 +03:00
btrfs_set_key_type ( & key , BTRFS_DIR_ITEM_KEY ) ;
2007-10-16 00:14:19 +04:00
2007-03-15 19:56:47 +03:00
ret = btrfs_name_hash ( name , name_len , & key . offset ) ;
BUG_ON ( ret ) ;
2007-10-16 00:14:19 +04:00
2007-04-19 23:36:27 +04:00
ret = btrfs_search_slot ( trans , root , & key , path , ins_len , cow ) ;
if ( ret < 0 )
return ERR_PTR ( ret ) ;
if ( ret > 0 ) {
if ( path - > slots [ 0 ] = = 0 )
return NULL ;
path - > slots [ 0 ] - - ;
2007-04-05 20:13:21 +04:00
}
2007-04-19 23:36:27 +04:00
2007-10-16 00:14:19 +04:00
leaf = path - > nodes [ 0 ] ;
btrfs_item_key_to_cpu ( leaf , & found_key , path - > slots [ 0 ] ) ;
if ( found_key . objectid ! = dir | |
btrfs_key_type ( & found_key ) ! = BTRFS_DIR_ITEM_KEY | |
found_key . offset ! = key . offset )
2007-04-19 23:36:27 +04:00
return NULL ;
return btrfs_match_dir_item_name ( root , path , name , name_len ) ;
2007-03-15 19:56:47 +03:00
}
2007-04-19 23:36:27 +04:00
struct btrfs_dir_item *
btrfs_lookup_dir_index_item ( struct btrfs_trans_handle * trans ,
struct btrfs_root * root ,
struct btrfs_path * path , u64 dir ,
u64 objectid , const char * name , int name_len ,
int mod )
{
int ret ;
struct btrfs_key key ;
int ins_len = mod < 0 ? - 1 : 0 ;
int cow = mod ! = 0 ;
key . objectid = dir ;
btrfs_set_key_type ( & key , BTRFS_DIR_INDEX_KEY ) ;
key . offset = objectid ;
ret = btrfs_search_slot ( trans , root , & key , path , ins_len , cow ) ;
if ( ret < 0 )
return ERR_PTR ( ret ) ;
if ( ret > 0 )
return ERR_PTR ( - ENOENT ) ;
return btrfs_match_dir_item_name ( root , path , name , name_len ) ;
}
struct btrfs_dir_item * btrfs_match_dir_item_name ( struct btrfs_root * root ,
2007-03-23 22:56:19 +03:00
struct btrfs_path * path ,
const char * name , int name_len )
2007-03-15 19:56:47 +03:00
{
struct btrfs_dir_item * dir_item ;
2007-10-16 00:14:19 +04:00
unsigned long name_ptr ;
2007-04-19 23:36:27 +04:00
u32 total_len ;
u32 cur = 0 ;
u32 this_len ;
2007-10-16 00:14:19 +04:00
struct extent_buffer * leaf ;
2007-03-16 15:46:49 +03:00
2007-10-16 00:14:19 +04:00
leaf = path - > nodes [ 0 ] ;
2007-04-19 23:36:27 +04:00
dir_item = btrfs_item_ptr ( leaf , path - > slots [ 0 ] , struct btrfs_dir_item ) ;
2007-10-16 00:14:19 +04:00
total_len = btrfs_item_size_nr ( leaf , path - > slots [ 0 ] ) ;
2007-04-19 23:36:27 +04:00
while ( cur < total_len ) {
2007-10-16 00:14:19 +04:00
this_len = sizeof ( * dir_item ) +
btrfs_dir_name_len ( leaf , dir_item ) ;
name_ptr = ( unsigned long ) ( dir_item + 1 ) ;
2007-04-19 23:36:27 +04:00
2007-10-16 00:14:19 +04:00
if ( btrfs_dir_name_len ( leaf , dir_item ) = = name_len & &
memcmp_extent_buffer ( leaf , name , name_ptr , name_len ) = = 0 )
2007-04-19 23:36:27 +04:00
return dir_item ;
cur + = this_len ;
dir_item = ( struct btrfs_dir_item * ) ( ( char * ) dir_item +
this_len ) ;
}
return NULL ;
2007-03-15 19:56:47 +03:00
}
2007-04-19 23:36:27 +04:00
int btrfs_delete_one_dir_name ( struct btrfs_trans_handle * trans ,
struct btrfs_root * root ,
struct btrfs_path * path ,
struct btrfs_dir_item * di )
{
2007-10-16 00:14:19 +04:00
struct extent_buffer * leaf ;
2007-04-19 23:36:27 +04:00
u32 sub_item_len ;
u32 item_len ;
2007-06-22 22:16:25 +04:00
int ret = 0 ;
2007-04-19 23:36:27 +04:00
2007-10-16 00:14:19 +04:00
leaf = path - > nodes [ 0 ] ;
sub_item_len = sizeof ( * di ) + btrfs_dir_name_len ( leaf , di ) ;
item_len = btrfs_item_size_nr ( leaf , path - > slots [ 0 ] ) ;
if ( sub_item_len = = item_len ) {
2007-04-19 23:36:27 +04:00
ret = btrfs_del_item ( trans , root , path ) ;
} else {
2007-10-16 00:14:19 +04:00
/* MARKER */
unsigned long ptr = ( unsigned long ) di ;
unsigned long start ;
start = btrfs_item_ptr_offset ( leaf , path - > slots [ 0 ] ) ;
memmove_extent_buffer ( leaf , ptr , ptr + sub_item_len ,
2007-04-19 23:36:27 +04:00
item_len - ( ptr + sub_item_len - start ) ) ;
ret = btrfs_truncate_item ( trans , root , path ,
item_len - sub_item_len ) ;
}
return 0 ;
}