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 */
2011-04-20 06:31:50 +04:00
di = btrfs_lookup_xattr ( NULL , root , path , btrfs_ino ( inode ) , name ,
2007-11-16 19:45:54 +03:00
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 ;
}
2009-11-12 12:35:27 +03:00
static int do_setxattr ( struct btrfs_trans_handle * trans ,
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_path * path ;
2009-11-12 12:35:27 +03:00
size_t name_len = strlen ( name ) ;
int ret = 0 ;
if ( name_len + size > BTRFS_MAX_XATTR_SIZE ( root ) )
return - ENOSPC ;
2007-11-16 19:45:54 +03:00
path = btrfs_alloc_path ( ) ;
2008-08-28 14:21:17 +04:00
if ( ! path )
2007-11-16 19:45:54 +03:00
return - ENOMEM ;
2011-05-27 20:06:11 +04:00
if ( flags & XATTR_REPLACE ) {
di = btrfs_lookup_xattr ( trans , root , path , btrfs_ino ( inode ) , name ,
name_len , - 1 ) ;
if ( IS_ERR ( di ) ) {
ret = PTR_ERR ( di ) ;
goto out ;
} else if ( ! di ) {
ret = - ENODATA ;
2007-11-16 19:45:54 +03:00
goto out ;
}
ret = btrfs_delete_one_dir_name ( trans , root , path , di ) ;
2011-05-27 20:06:11 +04:00
if ( ret )
goto out ;
2011-04-21 03:20:15 +04:00
btrfs_release_path ( path ) ;
2011-09-11 18:52:25 +04:00
/*
* remove the attribute
*/
if ( ! value )
goto out ;
2012-11-28 14:43:12 +04:00
} else {
di = btrfs_lookup_xattr ( NULL , root , path , btrfs_ino ( inode ) ,
name , name_len , 0 ) ;
if ( IS_ERR ( di ) ) {
ret = PTR_ERR ( di ) ;
goto out ;
}
if ( ! di & & ! value )
goto out ;
btrfs_release_path ( path ) ;
2011-05-27 20:06:11 +04:00
}
2007-11-16 19:45:54 +03:00
2011-05-27 20:06:11 +04:00
again :
ret = btrfs_insert_xattr_item ( trans , root , path , btrfs_ino ( inode ) ,
name , name_len , value , size ) ;
2011-10-13 21:09:22 +04:00
/*
* If we ' re setting an xattr to a new value but the new value is say
* exactly BTRFS_MAX_XATTR_SIZE , we could end up with EOVERFLOW getting
* back from split_leaf . This is because it thinks we ' ll be extending
* the existing item size , but we ' re asking for enough space to add the
* item itself . So if we get EOVERFLOW just set ret to EEXIST and let
* the rest of the function figure it out .
*/
if ( ret = = - EOVERFLOW )
ret = - EEXIST ;
2011-05-27 20:06:11 +04:00
if ( ret = = - EEXIST ) {
if ( flags & XATTR_CREATE )
2007-11-16 19:45:54 +03:00
goto out ;
2011-05-27 20:06:11 +04:00
/*
* We can ' t use the path we already have since we won ' t have the
* proper locking for a delete , so release the path and
* re - lookup to delete the thing .
*/
2011-04-21 03:20:15 +04:00
btrfs_release_path ( path ) ;
2011-05-27 20:06:11 +04:00
di = btrfs_lookup_xattr ( trans , root , path , btrfs_ino ( inode ) ,
name , name_len , - 1 ) ;
if ( IS_ERR ( di ) ) {
ret = PTR_ERR ( di ) ;
goto out ;
} else if ( ! di ) {
/* Shouldn't happen but just in case... */
btrfs_release_path ( path ) ;
goto again ;
}
2008-07-24 20:16:36 +04:00
2011-05-27 20:06:11 +04:00
ret = btrfs_delete_one_dir_name ( trans , root , path , di ) ;
if ( ret )
2008-07-24 20:16:36 +04:00
goto out ;
2011-05-27 20:06:11 +04:00
/*
* We have a value to set , so go back and try to insert it now .
*/
if ( value ) {
btrfs_release_path ( path ) ;
goto again ;
2008-07-24 20:16:36 +04:00
}
2007-11-16 19:45:54 +03:00
}
2009-11-12 12:35:27 +03:00
out :
btrfs_free_path ( path ) ;
return ret ;
}
2011-09-11 18:52:25 +04:00
/*
* @ value : " " makes the attribute to empty , NULL removes it
*/
2009-11-12 12:35:27 +03:00
int __btrfs_setxattr ( struct btrfs_trans_handle * trans ,
struct inode * inode , const char * name ,
const void * value , size_t size , int flags )
{
struct btrfs_root * root = BTRFS_I ( inode ) - > root ;
int ret ;
if ( trans )
return do_setxattr ( trans , inode , name , value , size , flags ) ;
2010-05-16 18:48:46 +04:00
trans = btrfs_start_transaction ( root , 2 ) ;
if ( IS_ERR ( trans ) )
return PTR_ERR ( trans ) ;
2007-11-16 19:45:54 +03:00
2009-11-12 12:35:27 +03:00
ret = do_setxattr ( trans , inode , name , value , size , flags ) ;
if ( ret )
goto out ;
2012-04-05 23:03:02 +04:00
inode_inc_iversion ( inode ) ;
2009-11-12 12:35:27 +03:00
inode - > i_ctime = CURRENT_TIME ;
2012-10-11 23:53:56 +04:00
set_bit ( BTRFS_INODE_COPY_EVERYTHING , & BTRFS_I ( inode ) - > runtime_flags ) ;
2009-11-12 12:35:27 +03:00
ret = btrfs_update_inode ( trans , root , inode ) ;
BUG_ON ( ret ) ;
out :
2012-01-13 04:10:12 +04:00
btrfs_end_transaction ( trans , root ) ;
2007-11-16 19:45:54 +03:00
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 extent_buffer * leaf ;
struct btrfs_dir_item * di ;
2011-03-17 10:17:59 +03:00
int ret = 0 , slot ;
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
/*
* 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
*/
2011-04-20 06:31:50 +04:00
key . objectid = btrfs_ino ( inode ) ;
2007-11-16 19:45:54 +03:00
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 ;
2011-03-17 10:17:59 +03:00
2007-11-16 19:45:54 +03:00
while ( 1 ) {
leaf = path - > nodes [ 0 ] ;
slot = path - > slots [ 0 ] ;
/* this is where we start walking through the path */
2011-03-17 10:17:59 +03:00
if ( slot > = btrfs_header_nritems ( leaf ) ) {
2007-11-16 19:45:54 +03:00
/*
* if we ' ve reached the last slot in this leaf we need
* to go to the next leaf and reset everything
*/
2011-03-17 10:17:59 +03:00
ret = btrfs_next_leaf ( root , path ) ;
if ( ret < 0 )
goto err ;
else if ( ret > 0 )
break ;
continue ;
2007-11-16 19:45:54 +03:00
}
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 ) ;
2011-03-16 23:47:17 +03:00
if ( verify_dir_item ( root , leaf , di ) )
2012-11-26 06:58:36 +04:00
goto next ;
2007-11-16 19:45:54 +03:00
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 )
2011-03-17 10:17:59 +03:00
goto next ;
2007-11-16 19:45:54 +03:00
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 ;
2011-03-17 10:17:59 +03:00
next :
path - > slots [ 0 ] + + ;
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 .
*/
2010-05-14 04:53:15 +04:00
const struct xattr_handler * btrfs_xattr_handlers [ ] = {
2009-10-13 21:50:18 +04:00
# ifdef CONFIG_BTRFS_FS_POSIX_ACL
2008-08-28 14:21:17 +04:00
& 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 )
{
2010-12-20 11:04:08 +03:00
struct btrfs_root * root = BTRFS_I ( dentry - > d_inode ) - > root ;
/*
* The permission on security . * and system . * is not checked
* in permission ( ) .
*/
if ( btrfs_root_readonly ( root ) )
return - EROFS ;
2008-08-28 14:21:17 +04:00
/*
* 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 */
2009-11-12 12:35:27 +03:00
return __btrfs_setxattr ( NULL , dentry - > d_inode , name , value , size ,
flags ) ;
2008-08-28 14:21:17 +04:00
}
int btrfs_removexattr ( struct dentry * dentry , const char * name )
{
2010-12-20 11:04:08 +03:00
struct btrfs_root * root = BTRFS_I ( dentry - > d_inode ) - > root ;
/*
* The permission on security . * and system . * is not checked
* in permission ( ) .
*/
if ( btrfs_root_readonly ( root ) )
return - EROFS ;
2008-08-28 14:21:17 +04:00
/*
* 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 ;
2009-11-12 12:35:27 +03:00
return __btrfs_setxattr ( NULL , dentry - > d_inode , name , NULL , 0 ,
XATTR_REPLACE ) ;
2008-08-28 14:21:17 +04:00
}
2009-02-04 17:29:13 +03:00
2013-04-26 00:41:01 +04:00
static int btrfs_initxattrs ( struct inode * inode ,
const struct xattr * xattr_array , void * fs_info )
2009-02-04 17:29:13 +03:00
{
2011-06-06 23:29:25 +04:00
const struct xattr * xattr ;
struct btrfs_trans_handle * trans = fs_info ;
2009-02-04 17:29:13 +03:00
char * name ;
2011-06-06 23:29:25 +04:00
int err = 0 ;
2009-02-04 17:29:13 +03:00
2011-06-06 23:29:25 +04:00
for ( xattr = xattr_array ; xattr - > name ! = NULL ; xattr + + ) {
name = kmalloc ( XATTR_SECURITY_PREFIX_LEN +
strlen ( xattr - > name ) + 1 , GFP_NOFS ) ;
if ( ! name ) {
err = - ENOMEM ;
break ;
}
2009-02-04 17:29:13 +03:00
strcpy ( name , XATTR_SECURITY_PREFIX ) ;
2011-06-06 23:29:25 +04:00
strcpy ( name + XATTR_SECURITY_PREFIX_LEN , xattr - > name ) ;
err = __btrfs_setxattr ( trans , inode , name ,
xattr - > value , xattr - > value_len , 0 ) ;
2009-02-04 17:29:13 +03:00
kfree ( name ) ;
2011-06-06 23:29:25 +04:00
if ( err < 0 )
break ;
2009-02-04 17:29:13 +03:00
}
return err ;
}
2011-06-06 23:29:25 +04:00
int btrfs_xattr_security_init ( struct btrfs_trans_handle * trans ,
struct inode * inode , struct inode * dir ,
const struct qstr * qstr )
{
return security_inode_init_security ( inode , dir , qstr ,
& btrfs_initxattrs , trans ) ;
}