2007-11-16 19:45:54 +03:00
/*
* Copyright ( C ) 2007 Red Hat . 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 .
*/
# include <linux/init.h>
# include <linux/fs.h>
# include <linux/slab.h>
# include <linux/rwsem.h>
# include <linux/xattr.h>
2009-02-04 17:29:13 +03:00
# include <linux/security.h>
2007-11-16 19:45:54 +03:00
# include "ctree.h"
# include "btrfs_inode.h"
# include "transaction.h"
# include "xattr.h"
# include "disk-io.h"
2008-07-24 20:16:36 +04:00
2007-11-16 19:45:54 +03:00
2008-08-28 14:21:17 +04:00
ssize_t __btrfs_getxattr ( struct inode * inode , const char * name ,
void * buffer , size_t size )
2007-11-16 19:45:54 +03:00
{
struct btrfs_dir_item * di ;
struct btrfs_root * root = BTRFS_I ( inode ) - > root ;
struct btrfs_path * path ;
struct extent_buffer * leaf ;
int ret = 0 ;
unsigned long data_ptr ;
path = btrfs_alloc_path ( ) ;
2008-08-28 14:21:17 +04:00
if ( ! path )
2007-11-16 19:45:54 +03:00
return - ENOMEM ;
/* lookup the xattr by name */
di = btrfs_lookup_xattr ( NULL , root , path , inode - > i_ino , name ,
strlen ( name ) , 0 ) ;
2009-01-21 18:49:16 +03:00
if ( ! di ) {
2007-11-16 19:45:54 +03:00
ret = - ENODATA ;
goto out ;
2009-01-21 18:49:16 +03:00
} else if ( IS_ERR ( di ) ) {
ret = PTR_ERR ( di ) ;
goto out ;
2007-11-16 19:45:54 +03:00
}
leaf = path - > nodes [ 0 ] ;
/* if size is 0, that means we want the size of the attr */
if ( ! size ) {
ret = btrfs_dir_data_len ( leaf , di ) ;
goto out ;
}
/* now get the data out of our dir_item */
if ( btrfs_dir_data_len ( leaf , di ) > size ) {
ret = - ERANGE ;
goto out ;
}
2009-01-21 18:49:16 +03:00
/*
* The way things are packed into the leaf is like this
* | struct btrfs_dir_item | name | data |
* where name is the xattr name , so security . foo , and data is the
* content of the xattr . data_ptr points to the location in memory
* where the data starts in the in memory leaf
*/
2007-11-16 19:45:54 +03:00
data_ptr = ( unsigned long ) ( ( char * ) ( di + 1 ) +
btrfs_dir_name_len ( leaf , di ) ) ;
read_extent_buffer ( leaf , buffer , data_ptr ,
2007-11-19 18:18:19 +03:00
btrfs_dir_data_len ( leaf , di ) ) ;
2007-11-16 19:45:54 +03:00
ret = btrfs_dir_data_len ( leaf , di ) ;
out :
btrfs_free_path ( path ) ;
return ret ;
}
2008-08-28 14:21:17 +04:00
int __btrfs_setxattr ( struct inode * inode , const char * name ,
const void * value , size_t size , int flags )
2007-11-16 19:45:54 +03:00
{
struct btrfs_dir_item * di ;
struct btrfs_root * root = BTRFS_I ( inode ) - > root ;
struct btrfs_trans_handle * trans ;
struct btrfs_path * path ;
int ret = 0 , mod = 0 ;
path = btrfs_alloc_path ( ) ;
2008-08-28 14:21:17 +04:00
if ( ! path )
2007-11-16 19:45:54 +03:00
return - ENOMEM ;
2009-02-04 17:18:33 +03:00
trans = btrfs_join_transaction ( root , 1 ) ;
2007-11-16 19:45:54 +03:00
btrfs_set_trans_block_group ( trans , inode ) ;
/* first lets see if we already have this xattr */
di = btrfs_lookup_xattr ( trans , root , path , inode - > i_ino , name ,
strlen ( name ) , - 1 ) ;
if ( IS_ERR ( di ) ) {
ret = PTR_ERR ( di ) ;
goto out ;
}
/* ok we already have this xattr, lets remove it */
if ( di ) {
/* if we want create only exit */
if ( flags & XATTR_CREATE ) {
ret = - EEXIST ;
goto out ;
}
ret = btrfs_delete_one_dir_name ( trans , root , path , di ) ;
if ( ret )
goto out ;
btrfs_release_path ( root , path ) ;
/* if we don't have a value then we are removing the xattr */
if ( ! value ) {
mod = 1 ;
goto out ;
}
2008-07-24 20:16:36 +04:00
} else {
btrfs_release_path ( root , path ) ;
if ( flags & XATTR_REPLACE ) {
/* we couldn't find the attr to replace */
ret = - ENODATA ;
goto out ;
}
2007-11-16 19:45:54 +03:00
}
/* ok we have to create a completely new xattr */
ret = btrfs_insert_xattr_item ( trans , root , name , strlen ( name ) ,
value , size , inode - > i_ino ) ;
if ( ret )
goto out ;
mod = 1 ;
out :
if ( mod ) {
inode - > i_ctime = CURRENT_TIME ;
ret = btrfs_update_inode ( trans , root , inode ) ;
}
btrfs_end_transaction ( trans , root ) ;
btrfs_free_path ( path ) ;
return ret ;
}
ssize_t btrfs_listxattr ( struct dentry * dentry , char * buffer , size_t size )
{
struct btrfs_key key , found_key ;
struct inode * inode = dentry - > d_inode ;
struct btrfs_root * root = BTRFS_I ( inode ) - > root ;
struct btrfs_path * path ;
struct btrfs_item * item ;
struct extent_buffer * leaf ;
struct btrfs_dir_item * di ;
int ret = 0 , slot , advance ;
2008-08-28 14:21:16 +04:00
size_t total_size = 0 , size_left = size ;
2007-11-16 19:45:54 +03:00
unsigned long name_ptr ;
2008-08-28 14:21:16 +04:00
size_t name_len ;
2007-11-16 19:45:54 +03:00
u32 nritems ;
/*
* ok we want all objects associated with this id .
* NOTE : we set key . offset = 0 ; because we want to start with the
* first xattr that we find and walk forward
*/
key . objectid = inode - > i_ino ;
btrfs_set_key_type ( & key , BTRFS_XATTR_ITEM_KEY ) ;
key . offset = 0 ;
path = btrfs_alloc_path ( ) ;
if ( ! path )
return - ENOMEM ;
2007-11-19 18:18:17 +03:00
path - > reada = 2 ;
2007-11-16 19:45:54 +03:00
/* search for our xattrs */
ret = btrfs_search_slot ( NULL , root , & key , path , 0 , 0 ) ;
if ( ret < 0 )
goto err ;
advance = 0 ;
while ( 1 ) {
leaf = path - > nodes [ 0 ] ;
nritems = btrfs_header_nritems ( leaf ) ;
slot = path - > slots [ 0 ] ;
/* this is where we start walking through the path */
if ( advance | | slot > = nritems ) {
/*
* if we ' ve reached the last slot in this leaf we need
* to go to the next leaf and reset everything
*/
if ( slot > = nritems - 1 ) {
ret = btrfs_next_leaf ( root , path ) ;
if ( ret )
break ;
leaf = path - > nodes [ 0 ] ;
nritems = btrfs_header_nritems ( leaf ) ;
slot = path - > slots [ 0 ] ;
} else {
/*
* just walking through the slots on this leaf
*/
slot + + ;
path - > slots [ 0 ] + + ;
}
}
advance = 1 ;
item = btrfs_item_nr ( leaf , slot ) ;
btrfs_item_key_to_cpu ( leaf , & found_key , slot ) ;
/* check to make sure this item is what we want */
if ( found_key . objectid ! = key . objectid )
break ;
if ( btrfs_key_type ( & found_key ) ! = BTRFS_XATTR_ITEM_KEY )
break ;
di = btrfs_item_ptr ( leaf , slot , struct btrfs_dir_item ) ;
2008-08-28 14:21:16 +04:00
name_len = btrfs_dir_name_len ( leaf , di ) ;
total_size + = name_len + 1 ;
2007-11-16 19:45:54 +03:00
/* we are just looking for how big our buffer needs to be */
if ( ! size )
continue ;
2008-08-28 14:21:16 +04:00
if ( ! buffer | | ( name_len + 1 ) > size_left ) {
2007-11-16 19:45:54 +03:00
ret = - ERANGE ;
2008-12-17 18:21:26 +03:00
goto err ;
2007-11-16 19:45:54 +03:00
}
2008-08-28 14:21:16 +04:00
name_ptr = ( unsigned long ) ( di + 1 ) ;
read_extent_buffer ( leaf , buffer , name_ptr , name_len ) ;
buffer [ name_len ] = ' \0 ' ;
size_left - = name_len + 1 ;
buffer + = name_len + 1 ;
2007-11-16 19:45:54 +03:00
}
ret = total_size ;
err :
btrfs_free_path ( path ) ;
return ret ;
}
/*
2008-08-28 14:21:17 +04:00
* List of handlers for synthetic system . * attributes . All real ondisk
* attributes are handled directly .
*/
struct xattr_handler * btrfs_xattr_handlers [ ] = {
# ifdef CONFIG_FS_POSIX_ACL
& btrfs_xattr_acl_access_handler ,
& btrfs_xattr_acl_default_handler ,
# endif
NULL ,
} ;
/*
* Check if the attribute is in a supported namespace .
*
* This applied after the check for the synthetic attributes in the system
* namespace .
2007-11-16 19:45:54 +03:00
*/
2008-08-28 14:21:17 +04:00
static bool btrfs_is_valid_xattr ( const char * name )
{
2009-01-06 05:25:51 +03:00
return ! strncmp ( name , XATTR_SECURITY_PREFIX ,
XATTR_SECURITY_PREFIX_LEN ) | |
2008-08-28 14:21:17 +04:00
! strncmp ( name , XATTR_SYSTEM_PREFIX , XATTR_SYSTEM_PREFIX_LEN ) | |
! strncmp ( name , XATTR_TRUSTED_PREFIX , XATTR_TRUSTED_PREFIX_LEN ) | |
! strncmp ( name , XATTR_USER_PREFIX , XATTR_USER_PREFIX_LEN ) ;
2008-01-14 22:33:35 +03:00
}
2008-08-28 14:21:17 +04:00
ssize_t btrfs_getxattr ( struct dentry * dentry , const char * name ,
void * buffer , size_t size )
{
/*
* If this is a request for a synthetic attribute in the system . *
* namespace use the generic infrastructure to resolve a handler
* for it via sb - > s_xattr .
*/
if ( ! strncmp ( name , XATTR_SYSTEM_PREFIX , XATTR_SYSTEM_PREFIX_LEN ) )
return generic_getxattr ( dentry , name , buffer , size ) ;
2007-11-16 19:45:54 +03:00
2008-08-28 14:21:17 +04:00
if ( ! btrfs_is_valid_xattr ( name ) )
return - EOPNOTSUPP ;
return __btrfs_getxattr ( dentry - > d_inode , name , buffer , size ) ;
}
2007-11-16 19:45:54 +03:00
2008-08-28 14:21:17 +04:00
int btrfs_setxattr ( struct dentry * dentry , const char * name , const void * value ,
size_t size , int flags )
{
/*
* If this is a request for a synthetic attribute in the system . *
* namespace use the generic infrastructure to resolve a handler
* for it via sb - > s_xattr .
*/
if ( ! strncmp ( name , XATTR_SYSTEM_PREFIX , XATTR_SYSTEM_PREFIX_LEN ) )
return generic_setxattr ( dentry , name , value , size , flags ) ;
2007-11-16 19:45:54 +03:00
2008-08-28 14:21:17 +04:00
if ( ! btrfs_is_valid_xattr ( name ) )
return - EOPNOTSUPP ;
2007-11-16 19:45:54 +03:00
2008-08-28 14:21:17 +04:00
if ( size = = 0 )
value = " " ; /* empty EA, do not remove */
return __btrfs_setxattr ( dentry - > d_inode , name , value , size , flags ) ;
}
int btrfs_removexattr ( struct dentry * dentry , const char * name )
{
/*
* If this is a request for a synthetic attribute in the system . *
* namespace use the generic infrastructure to resolve a handler
* for it via sb - > s_xattr .
*/
if ( ! strncmp ( name , XATTR_SYSTEM_PREFIX , XATTR_SYSTEM_PREFIX_LEN ) )
return generic_removexattr ( dentry , name ) ;
if ( ! btrfs_is_valid_xattr ( name ) )
return - EOPNOTSUPP ;
return __btrfs_setxattr ( dentry - > d_inode , name , NULL , 0 , XATTR_REPLACE ) ;
}
2009-02-04 17:29:13 +03:00
int btrfs_xattr_security_init ( struct inode * inode , struct inode * dir )
{
int err ;
size_t len ;
void * value ;
char * suffix ;
char * name ;
err = security_inode_init_security ( inode , dir , & suffix , & value , & len ) ;
if ( err ) {
if ( err = = - EOPNOTSUPP )
return 0 ;
return err ;
}
name = kmalloc ( XATTR_SECURITY_PREFIX_LEN + strlen ( suffix ) + 1 ,
GFP_NOFS ) ;
if ( ! name ) {
err = - ENOMEM ;
} else {
strcpy ( name , XATTR_SECURITY_PREFIX ) ;
strcpy ( name + XATTR_SECURITY_PREFIX_LEN , suffix ) ;
err = __btrfs_setxattr ( inode , name , value , len , 0 ) ;
kfree ( name ) ;
}
kfree ( suffix ) ;
kfree ( value ) ;
return err ;
}