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
2008-09-29 23:18:18 +04:00
/*
* insert a name into a directory , doing overflow properly if there is a hash
* collision . data_size indicates how big the item inserted should be . On
* success a struct btrfs_dir_item pointer is returned , otherwise it is
* an ERR_PTR .
*
* The name is not copied into the dir item , you have to do that yourself .
*/
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 ) ;
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
}
2008-09-29 23:18:18 +04:00
/*
* xattrs work a lot like directories , this inserts an xattr item
* into the tree
*/
2007-11-16 19:45:54 +03:00
int btrfs_insert_xattr_item ( struct btrfs_trans_handle * trans ,
struct btrfs_root * root , const char * name ,
u16 name_len , const void * data , u16 data_len ,
u64 dir )
{
int ret = 0 ;
struct btrfs_path * path ;
struct btrfs_dir_item * dir_item ;
unsigned long name_ptr , data_ptr ;
struct btrfs_key key , location ;
struct btrfs_disk_key disk_key ;
struct extent_buffer * leaf ;
u32 data_size ;
key . objectid = dir ;
btrfs_set_key_type ( & key , BTRFS_XATTR_ITEM_KEY ) ;
2008-02-15 18:40:52 +03:00
key . offset = btrfs_name_hash ( name , name_len ) ;
2007-11-16 19:45:54 +03:00
path = btrfs_alloc_path ( ) ;
if ( ! path )
return - ENOMEM ;
2008-01-14 21:26:08 +03:00
if ( name_len + data_len + sizeof ( struct btrfs_dir_item ) >
BTRFS_LEAF_DATA_SIZE ( root ) - sizeof ( struct btrfs_item ) )
return - ENOSPC ;
2007-11-16 19:45:54 +03:00
data_size = sizeof ( * dir_item ) + name_len + data_len ;
dir_item = insert_with_overflow ( trans , root , path , & key , data_size ,
name , name_len ) ;
/*
* FIXME : at some point we should handle xattr ' s that are larger than
* what we can fit in our leaf . We set location to NULL b / c we arent
* pointing at anything else , that will change if we store the xattr
* data in a separate inode .
*/
BUG_ON ( IS_ERR ( dir_item ) ) ;
memset ( & location , 0 , sizeof ( location ) ) ;
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 , BTRFS_FT_XATTR ) ;
btrfs_set_dir_name_len ( leaf , dir_item , name_len ) ;
2008-09-06 00:13:11 +04:00
btrfs_set_dir_transid ( leaf , dir_item , trans - > transid ) ;
2007-11-16 19:45:54 +03:00
btrfs_set_dir_data_len ( leaf , dir_item , data_len ) ;
name_ptr = ( unsigned long ) ( dir_item + 1 ) ;
data_ptr = ( unsigned long ) ( ( char * ) name_ptr + name_len ) ;
write_extent_buffer ( leaf , name , name_ptr , name_len ) ;
write_extent_buffer ( leaf , data , data_ptr , data_len ) ;
btrfs_mark_buffer_dirty ( path - > nodes [ 0 ] ) ;
btrfs_free_path ( path ) ;
return ret ;
}
2008-09-29 23:18:18 +04:00
/*
* insert a directory item in the tree , doing all the magic for
* both indexes . ' dir ' indicates which objectid to insert it into ,
* ' location ' is the key to stuff into the directory item , ' type ' is the
* type of the inode we ' re pointing to , and ' index ' is the sequence number
* to use for the second index ( if one is created ) .
*/
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 ,
2008-07-24 20:12:38 +04:00
struct btrfs_key * location , u8 type , u64 index )
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 ) ;
2008-02-15 18:40:52 +03:00
key . offset = btrfs_name_hash ( name , name_len ) ;
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 ) ;
2007-11-16 19:45:54 +03:00
btrfs_set_dir_data_len ( leaf , dir_item , 0 ) ;
2007-10-16 00:14:19 +04:00
btrfs_set_dir_name_len ( leaf , dir_item , name_len ) ;
2008-09-06 00:13:11 +04:00
btrfs_set_dir_transid ( leaf , dir_item , trans - > transid ) ;
2007-10-16 00:14:19 +04:00
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 ) ;
2008-07-24 20:12:38 +04:00
key . offset = index ;
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 ) ;
2007-11-16 19:45:54 +03:00
btrfs_set_dir_data_len ( leaf , dir_item , 0 ) ;
2007-10-16 00:14:19 +04:00
btrfs_set_dir_name_len ( leaf , dir_item , name_len ) ;
2008-09-06 00:13:11 +04:00
btrfs_set_dir_transid ( leaf , dir_item , trans - > transid ) ;
2007-10-16 00:14:19 +04:00
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
}
2008-09-29 23:18:18 +04:00
/*
* lookup a directory item based on name . ' dir ' is the objectid
* we ' re searching in , and ' mod ' tells us if you plan on deleting the
* item ( use mod < 0 ) or changing the options ( use mod > 0 )
*/
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
2008-02-15 18:40:52 +03:00
key . offset = btrfs_name_hash ( name , name_len ) ;
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
}
2008-09-29 23:18:18 +04:00
/*
* lookup a directory item based on index . ' dir ' is the objectid
* we ' re searching in , and ' mod ' tells us if you plan on deleting the
* item ( use mod < 0 ) or changing the options ( use mod > 0 )
*
* The name is used to make sure the index really points to the name you were
* looking for .
*/
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 ) ;
}
2007-11-16 19:45:54 +03:00
struct btrfs_dir_item * btrfs_lookup_xattr ( struct btrfs_trans_handle * trans ,
struct btrfs_root * root ,
struct btrfs_path * path , u64 dir ,
const char * name , u16 name_len ,
int mod )
{
int ret ;
struct btrfs_key key ;
int ins_len = mod < 0 ? - 1 : 0 ;
int cow = mod ! = 0 ;
struct btrfs_key found_key ;
struct extent_buffer * leaf ;
key . objectid = dir ;
btrfs_set_key_type ( & key , BTRFS_XATTR_ITEM_KEY ) ;
2008-02-15 18:40:52 +03:00
key . offset = btrfs_name_hash ( name , name_len ) ;
2007-11-16 19:45:54 +03: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 ] - - ;
}
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_XATTR_ITEM_KEY | |
found_key . offset ! = key . offset )
return NULL ;
return btrfs_match_dir_item_name ( root , path , name , name_len ) ;
}
2008-09-29 23:18:18 +04:00
/*
* helper function to look at the directory item pointed to by ' path '
* this walks through all the entries in a dir item and finds one
* for a specific name .
*/
2007-04-19 23:36:27 +04:00
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 ] ) ;
2009-01-06 05:25:51 +03:00
while ( cur < total_len ) {
2007-10-16 00:14:19 +04:00
this_len = sizeof ( * dir_item ) +
2007-11-16 19:45:54 +03:00
btrfs_dir_name_len ( leaf , dir_item ) +
btrfs_dir_data_len ( leaf , dir_item ) ;
2007-10-16 00:14:19 +04:00
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
2008-09-29 23:18:18 +04:00
/*
* given a pointer into a directory item , delete it . This
* handles items that have more than one entry in them .
*/
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 ] ;
2007-11-16 19:45:54 +03:00
sub_item_len = sizeof ( * di ) + btrfs_dir_name_len ( leaf , di ) +
btrfs_dir_data_len ( leaf , di ) ;
2007-10-16 00:14:19 +04:00
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 ,
2007-11-01 18:28:41 +03:00
item_len - sub_item_len , 1 ) ;
2007-04-19 23:36:27 +04:00
}
return 0 ;
}