2015-07-17 17:38:15 +03:00
/*
* ( C ) 2001 Clemson University and The University of Chicago
*
* See COPYING in top - level directory .
*/
/*
* Linux VFS extended attribute operations .
*/
# include "protocol.h"
2015-12-04 20:56:14 +03:00
# include "orangefs-kernel.h"
# include "orangefs-bufmap.h"
2015-07-17 17:38:15 +03:00
# include <linux/posix_acl_xattr.h>
# include <linux/xattr.h>
2015-11-24 23:12:14 +03:00
# define SYSTEM_ORANGEFS_KEY "system.pvfs2."
# define SYSTEM_ORANGEFS_KEY_LEN 13
2015-07-17 17:38:15 +03:00
/*
* this function returns
* 0 if the key corresponding to name is not meant to be printed as part
* of a listxattr .
* 1 if the key corresponding to name is meant to be returned as part of
* a listxattr .
2015-11-24 23:12:14 +03:00
* The ones that start SYSTEM_ORANGEFS_KEY are the ones to avoid printing .
2015-07-17 17:38:15 +03:00
*/
static int is_reserved_key ( const char * key , size_t size )
{
2015-11-24 23:12:14 +03:00
if ( size < SYSTEM_ORANGEFS_KEY_LEN )
2015-07-17 17:38:15 +03:00
return 1 ;
2015-11-24 23:12:14 +03:00
return strncmp ( key , SYSTEM_ORANGEFS_KEY , SYSTEM_ORANGEFS_KEY_LEN ) ? 1 : 0 ;
2015-07-17 17:38:15 +03:00
}
static inline int convert_to_internal_xattr_flags ( int setxattr_flags )
{
int internal_flag = 0 ;
if ( setxattr_flags & XATTR_REPLACE ) {
/* Attribute must exist! */
2015-11-24 23:12:14 +03:00
internal_flag = ORANGEFS_XATTR_REPLACE ;
2015-07-17 17:38:15 +03:00
} else if ( setxattr_flags & XATTR_CREATE ) {
/* Attribute must not exist */
2015-11-24 23:12:14 +03:00
internal_flag = ORANGEFS_XATTR_CREATE ;
2015-07-17 17:38:15 +03:00
}
return internal_flag ;
}
/*
* Tries to get a specified key ' s attributes of a given
* file into a user - specified buffer . Note that the getxattr
* interface allows for the users to probe the size of an
* extended attribute by passing in a value of 0 to size .
* Thus our return value is always the size of the attribute
* unless the key does not exist for the file and / or if
* there were errors in fetching the attribute value .
*/
2016-06-04 12:02:33 +03:00
ssize_t orangefs_inode_getxattr ( struct inode * inode , const char * name ,
void * buffer , size_t size )
2015-07-17 17:38:15 +03:00
{
2015-11-24 23:12:14 +03:00
struct orangefs_inode_s * orangefs_inode = ORANGEFS_I ( inode ) ;
struct orangefs_kernel_op_s * new_op = NULL ;
2015-07-17 17:38:15 +03:00
ssize_t ret = - ENOMEM ;
ssize_t length = 0 ;
int fsuid ;
int fsgid ;
gossip_debug ( GOSSIP_XATTR_DEBUG ,
2016-06-04 12:02:33 +03:00
" %s: name %s, buffer_size %zd \n " ,
__func__ , name , size ) ;
2015-07-17 17:38:15 +03:00
2016-06-04 12:02:33 +03:00
if ( strlen ( name ) > = ORANGEFS_MAX_XATTR_NAMELEN ) {
2015-07-29 20:36:37 +03:00
gossip_err ( " Invalid key length (%d) \n " ,
2016-06-04 12:02:33 +03:00
( int ) strlen ( name ) ) ;
2015-07-17 17:38:15 +03:00
return - EINVAL ;
}
fsuid = from_kuid ( current_user_ns ( ) , current_fsuid ( ) ) ;
fsgid = from_kgid ( current_user_ns ( ) , current_fsgid ( ) ) ;
gossip_debug ( GOSSIP_XATTR_DEBUG ,
" getxattr on inode %pU, name %s "
" (uid %o, gid %o) \n " ,
get_khandle_from_ino ( inode ) ,
name ,
fsuid ,
fsgid ) ;
2015-11-24 23:12:14 +03:00
down_read ( & orangefs_inode - > xattr_sem ) ;
2015-07-17 17:38:15 +03:00
2015-11-24 23:12:14 +03:00
new_op = op_alloc ( ORANGEFS_VFS_OP_GETXATTR ) ;
2015-07-17 17:38:15 +03:00
if ( ! new_op )
goto out_unlock ;
2015-11-24 23:12:14 +03:00
new_op - > upcall . req . getxattr . refn = orangefs_inode - > refn ;
2016-06-04 12:02:33 +03:00
strcpy ( new_op - > upcall . req . getxattr . key , name ) ;
2015-07-17 17:38:15 +03:00
/*
* NOTE : Although keys are meant to be NULL terminated textual
* strings , I am going to explicitly pass the length just in case
* we change this later on . . .
*/
2016-06-04 12:02:33 +03:00
new_op - > upcall . req . getxattr . key_sz = strlen ( name ) + 1 ;
2015-07-17 17:38:15 +03:00
2015-11-24 23:12:14 +03:00
ret = service_operation ( new_op , " orangefs_inode_getxattr " ,
2015-07-17 17:38:15 +03:00
get_interruptible_flag ( inode ) ) ;
if ( ret ! = 0 ) {
if ( ret = = - ENOENT ) {
ret = - ENODATA ;
gossip_debug ( GOSSIP_XATTR_DEBUG ,
2015-11-24 23:12:14 +03:00
" orangefs_inode_getxattr: inode %pU key %s "
2015-07-17 17:38:15 +03:00
" does not exist! \n " ,
get_khandle_from_ino ( inode ) ,
( char * ) new_op - > upcall . req . getxattr . key ) ;
}
goto out_release_op ;
}
/*
* Length returned includes null terminator .
*/
length = new_op - > downcall . resp . getxattr . val_sz ;
/*
* Just return the length of the queried attribute .
*/
if ( size = = 0 ) {
ret = length ;
goto out_release_op ;
}
/*
* Check to see if key length is > provided buffer size .
*/
if ( length > size ) {
ret = - ERANGE ;
goto out_release_op ;
}
memcpy ( buffer , new_op - > downcall . resp . getxattr . val , length ) ;
2016-04-06 18:19:37 +03:00
memset ( buffer + length , 0 , size - length ) ;
2015-07-17 17:38:15 +03:00
gossip_debug ( GOSSIP_XATTR_DEBUG ,
2015-11-24 23:12:14 +03:00
" orangefs_inode_getxattr: inode %pU "
2015-07-17 17:38:15 +03:00
" key %s key_sz %d, val_len %d \n " ,
get_khandle_from_ino ( inode ) ,
( char * ) new_op - >
upcall . req . getxattr . key ,
( int ) new_op - >
upcall . req . getxattr . key_sz ,
( int ) ret ) ;
ret = length ;
out_release_op :
op_release ( new_op ) ;
out_unlock :
2015-11-24 23:12:14 +03:00
up_read ( & orangefs_inode - > xattr_sem ) ;
2015-07-17 17:38:15 +03:00
return ret ;
}
2016-06-04 12:02:33 +03:00
static int orangefs_inode_removexattr ( struct inode * inode , const char * name ,
int flags )
2015-07-17 17:38:15 +03:00
{
2015-11-24 23:12:14 +03:00
struct orangefs_inode_s * orangefs_inode = ORANGEFS_I ( inode ) ;
struct orangefs_kernel_op_s * new_op = NULL ;
2015-07-17 17:38:15 +03:00
int ret = - ENOMEM ;
2015-11-24 23:12:14 +03:00
down_write ( & orangefs_inode - > xattr_sem ) ;
new_op = op_alloc ( ORANGEFS_VFS_OP_REMOVEXATTR ) ;
2015-07-17 17:38:15 +03:00
if ( ! new_op )
goto out_unlock ;
2015-11-24 23:12:14 +03:00
new_op - > upcall . req . removexattr . refn = orangefs_inode - > refn ;
2015-07-17 17:38:15 +03:00
/*
* NOTE : Although keys are meant to be NULL terminated
* textual strings , I am going to explicitly pass the
* length just in case we change this later on . . .
*/
2016-06-04 12:02:33 +03:00
strcpy ( new_op - > upcall . req . removexattr . key , name ) ;
new_op - > upcall . req . removexattr . key_sz = strlen ( name ) + 1 ;
2015-07-17 17:38:15 +03:00
gossip_debug ( GOSSIP_XATTR_DEBUG ,
2015-11-24 23:12:14 +03:00
" orangefs_inode_removexattr: key %s, key_sz %d \n " ,
2015-07-17 17:38:15 +03:00
( char * ) new_op - > upcall . req . removexattr . key ,
( int ) new_op - > upcall . req . removexattr . key_sz ) ;
ret = service_operation ( new_op ,
2015-11-24 23:12:14 +03:00
" orangefs_inode_removexattr " ,
2015-07-17 17:38:15 +03:00
get_interruptible_flag ( inode ) ) ;
if ( ret = = - ENOENT ) {
/*
* Request to replace a non - existent attribute is an error .
*/
if ( flags & XATTR_REPLACE )
ret = - ENODATA ;
else
ret = 0 ;
}
gossip_debug ( GOSSIP_XATTR_DEBUG ,
2015-11-24 23:12:14 +03:00
" orangefs_inode_removexattr: returning %d \n " , ret ) ;
2015-07-17 17:38:15 +03:00
op_release ( new_op ) ;
out_unlock :
2015-11-24 23:12:14 +03:00
up_write ( & orangefs_inode - > xattr_sem ) ;
2015-07-17 17:38:15 +03:00
return ret ;
}
/*
* Tries to set an attribute for a given key on a file .
*
* Returns a - ve number on error and 0 on success . Key is text , but value
* can be binary !
*/
2016-06-04 12:02:33 +03:00
int orangefs_inode_setxattr ( struct inode * inode , const char * name ,
const void * value , size_t size , int flags )
2015-07-17 17:38:15 +03:00
{
2015-11-24 23:12:14 +03:00
struct orangefs_inode_s * orangefs_inode = ORANGEFS_I ( inode ) ;
struct orangefs_kernel_op_s * new_op ;
2015-07-17 17:38:15 +03:00
int internal_flag = 0 ;
int ret = - ENOMEM ;
gossip_debug ( GOSSIP_XATTR_DEBUG ,
2016-06-04 12:02:33 +03:00
" %s: name %s, buffer_size %zd \n " ,
__func__ , name , size ) ;
2015-07-17 17:38:15 +03:00
2016-04-06 17:52:38 +03:00
if ( size > = ORANGEFS_MAX_XATTR_VALUELEN | |
2015-07-17 17:38:15 +03:00
flags < 0 ) {
2015-11-24 23:12:14 +03:00
gossip_err ( " orangefs_inode_setxattr: bogus values of size(%d), flags(%d) \n " ,
2015-07-17 17:38:15 +03:00
( int ) size ,
flags ) ;
return - EINVAL ;
}
internal_flag = convert_to_internal_xattr_flags ( flags ) ;
2016-06-04 12:02:33 +03:00
if ( strlen ( name ) > = ORANGEFS_MAX_XATTR_NAMELEN ) {
gossip_err
( " orangefs_inode_setxattr: bogus key size (%d) \n " ,
( int ) ( strlen ( name ) ) ) ;
return - EINVAL ;
2015-07-17 17:38:15 +03:00
}
/* This is equivalent to a removexattr */
if ( size = = 0 & & value = = NULL ) {
gossip_debug ( GOSSIP_XATTR_DEBUG ,
2016-06-04 12:02:33 +03:00
" removing xattr (%s) \n " ,
2015-07-17 17:38:15 +03:00
name ) ;
2016-06-04 12:02:33 +03:00
return orangefs_inode_removexattr ( inode , name , flags ) ;
2015-07-17 17:38:15 +03:00
}
gossip_debug ( GOSSIP_XATTR_DEBUG ,
" setxattr on inode %pU, name %s \n " ,
get_khandle_from_ino ( inode ) ,
name ) ;
2015-11-24 23:12:14 +03:00
down_write ( & orangefs_inode - > xattr_sem ) ;
new_op = op_alloc ( ORANGEFS_VFS_OP_SETXATTR ) ;
2015-07-17 17:38:15 +03:00
if ( ! new_op )
goto out_unlock ;
2015-11-24 23:12:14 +03:00
new_op - > upcall . req . setxattr . refn = orangefs_inode - > refn ;
2015-07-17 17:38:15 +03:00
new_op - > upcall . req . setxattr . flags = internal_flag ;
/*
* NOTE : Although keys are meant to be NULL terminated textual
* strings , I am going to explicitly pass the length just in
* case we change this later on . . .
*/
2016-06-04 12:02:33 +03:00
strcpy ( new_op - > upcall . req . setxattr . keyval . key , name ) ;
new_op - > upcall . req . setxattr . keyval . key_sz = strlen ( name ) + 1 ;
2015-07-17 17:38:15 +03:00
memcpy ( new_op - > upcall . req . setxattr . keyval . val , value , size ) ;
new_op - > upcall . req . setxattr . keyval . val_sz = size ;
gossip_debug ( GOSSIP_XATTR_DEBUG ,
2015-11-24 23:12:14 +03:00
" orangefs_inode_setxattr: key %s, key_sz %d "
2015-07-17 17:38:15 +03:00
" value size %zd \n " ,
( char * ) new_op - > upcall . req . setxattr . keyval . key ,
( int ) new_op - > upcall . req . setxattr . keyval . key_sz ,
size ) ;
ret = service_operation ( new_op ,
2015-11-24 23:12:14 +03:00
" orangefs_inode_setxattr " ,
2015-07-17 17:38:15 +03:00
get_interruptible_flag ( inode ) ) ;
gossip_debug ( GOSSIP_XATTR_DEBUG ,
2015-11-24 23:12:14 +03:00
" orangefs_inode_setxattr: returning %d \n " ,
2015-07-17 17:38:15 +03:00
ret ) ;
/* when request is serviced properly, free req op struct */
op_release ( new_op ) ;
out_unlock :
2015-11-24 23:12:14 +03:00
up_write ( & orangefs_inode - > xattr_sem ) ;
2015-07-17 17:38:15 +03:00
return ret ;
}
/*
* Tries to get a specified object ' s keys into a user - specified buffer of a
* given size . Note that like the previous instances of xattr routines , this
* also allows you to pass in a NULL pointer and 0 size to probe the size for
* subsequent memory allocations . Thus our return value is always the size of
* all the keys unless there were errors in fetching the keys !
*/
2015-11-24 23:12:14 +03:00
ssize_t orangefs_listxattr ( struct dentry * dentry , char * buffer , size_t size )
2015-07-17 17:38:15 +03:00
{
struct inode * inode = dentry - > d_inode ;
2015-11-24 23:12:14 +03:00
struct orangefs_inode_s * orangefs_inode = ORANGEFS_I ( inode ) ;
struct orangefs_kernel_op_s * new_op ;
__u64 token = ORANGEFS_ITERATE_START ;
2015-07-17 17:38:15 +03:00
ssize_t ret = - ENOMEM ;
ssize_t total = 0 ;
int count_keys = 0 ;
int key_size ;
int i = 0 ;
2015-12-18 00:11:40 +03:00
int returned_count = 0 ;
2015-07-17 17:38:15 +03:00
if ( size > 0 & & buffer = = NULL ) {
gossip_err ( " %s: bogus NULL pointers \n " , __func__ ) ;
return - EINVAL ;
}
2015-11-24 23:12:14 +03:00
down_read ( & orangefs_inode - > xattr_sem ) ;
new_op = op_alloc ( ORANGEFS_VFS_OP_LISTXATTR ) ;
2015-07-17 17:38:15 +03:00
if ( ! new_op )
goto out_unlock ;
if ( buffer & & size > 0 )
memset ( buffer , 0 , size ) ;
try_again :
key_size = 0 ;
2015-11-24 23:12:14 +03:00
new_op - > upcall . req . listxattr . refn = orangefs_inode - > refn ;
2015-07-17 17:38:15 +03:00
new_op - > upcall . req . listxattr . token = token ;
new_op - > upcall . req . listxattr . requested_count =
2015-11-24 23:12:14 +03:00
( size = = 0 ) ? 0 : ORANGEFS_MAX_XATTR_LISTLEN ;
2015-07-17 17:38:15 +03:00
ret = service_operation ( new_op , __func__ ,
get_interruptible_flag ( inode ) ) ;
if ( ret ! = 0 )
goto done ;
if ( size = = 0 ) {
/*
* This is a bit of a big upper limit , but I did not want to
* spend too much time getting this correct , since users end
* up allocating memory rather than us . . .
*/
total = new_op - > downcall . resp . listxattr . returned_count *
2015-11-24 23:12:14 +03:00
ORANGEFS_MAX_XATTR_NAMELEN ;
2015-07-17 17:38:15 +03:00
goto done ;
}
2015-12-18 00:11:40 +03:00
returned_count = new_op - > downcall . resp . listxattr . returned_count ;
if ( returned_count < 0 | |
returned_count > = ORANGEFS_MAX_XATTR_LISTLEN ) {
gossip_err ( " %s: impossible value for returned_count:%d: \n " ,
__func__ ,
returned_count ) ;
2016-03-16 21:01:43 +03:00
ret = - EIO ;
2015-12-18 00:11:40 +03:00
goto done ;
}
2015-07-17 17:38:15 +03:00
/*
* Check to see how much can be fit in the buffer . Fit only whole keys .
*/
2015-12-18 00:11:40 +03:00
for ( i = 0 ; i < returned_count ; i + + ) {
2016-03-16 21:01:43 +03:00
if ( new_op - > downcall . resp . listxattr . lengths [ i ] < 0 | |
new_op - > downcall . resp . listxattr . lengths [ i ] >
ORANGEFS_MAX_XATTR_NAMELEN ) {
gossip_err ( " %s: impossible value for lengths[%d] \n " ,
__func__ ,
new_op - > downcall . resp . listxattr . lengths [ i ] ) ;
ret = - EIO ;
goto done ;
}
2015-07-17 17:38:15 +03:00
if ( total + new_op - > downcall . resp . listxattr . lengths [ i ] > size )
goto done ;
/*
* Since many dumb programs try to setxattr ( ) on our reserved
* xattrs this is a feeble attempt at defeating those by not
* listing them in the output of listxattr . . sigh
*/
if ( is_reserved_key ( new_op - > downcall . resp . listxattr . key +
key_size ,
new_op - > downcall . resp .
listxattr . lengths [ i ] ) ) {
gossip_debug ( GOSSIP_XATTR_DEBUG , " Copying key %d -> %s \n " ,
i , new_op - > downcall . resp . listxattr . key +
key_size ) ;
memcpy ( buffer + total ,
new_op - > downcall . resp . listxattr . key + key_size ,
new_op - > downcall . resp . listxattr . lengths [ i ] ) ;
total + = new_op - > downcall . resp . listxattr . lengths [ i ] ;
count_keys + + ;
} else {
gossip_debug ( GOSSIP_XATTR_DEBUG , " [RESERVED] key %d -> %s \n " ,
i , new_op - > downcall . resp . listxattr . key +
key_size ) ;
}
key_size + = new_op - > downcall . resp . listxattr . lengths [ i ] ;
}
/*
* Since the buffer was large enough , we might have to continue
* fetching more keys !
*/
token = new_op - > downcall . resp . listxattr . token ;
2015-11-24 23:12:14 +03:00
if ( token ! = ORANGEFS_ITERATE_END )
2015-07-17 17:38:15 +03:00
goto try_again ;
done :
gossip_debug ( GOSSIP_XATTR_DEBUG , " %s: returning %d "
" [size of buffer %ld] (filled in %d keys) \n " ,
__func__ ,
ret ? ( int ) ret : ( int ) total ,
( long ) size ,
count_keys ) ;
op_release ( new_op ) ;
if ( ret = = 0 )
ret = total ;
out_unlock :
2015-11-24 23:12:14 +03:00
up_read ( & orangefs_inode - > xattr_sem ) ;
2015-07-17 17:38:15 +03:00
return ret ;
}
2015-11-24 23:12:14 +03:00
static int orangefs_xattr_set_default ( const struct xattr_handler * handler ,
2016-05-27 17:19:30 +03:00
struct dentry * unused ,
struct inode * inode ,
2015-11-24 23:12:14 +03:00
const char * name ,
const void * buffer ,
size_t size ,
int flags )
2015-07-17 17:38:15 +03:00
{
2016-06-04 12:02:33 +03:00
return orangefs_inode_setxattr ( inode , name , buffer , size , flags ) ;
2015-07-17 17:38:15 +03:00
}
2015-11-24 23:12:14 +03:00
static int orangefs_xattr_get_default ( const struct xattr_handler * handler ,
2016-04-11 03:48:24 +03:00
struct dentry * unused ,
struct inode * inode ,
2015-11-24 23:12:14 +03:00
const char * name ,
void * buffer ,
size_t size )
2015-07-17 17:38:15 +03:00
{
2016-06-04 12:02:33 +03:00
return orangefs_inode_getxattr ( inode , name , buffer , size ) ;
2015-07-17 17:38:15 +03:00
}
2015-11-24 23:12:14 +03:00
static struct xattr_handler orangefs_xattr_default_handler = {
2016-05-30 12:25:59 +03:00
. prefix = " " , /* match any name => handlers called with full name */
2015-11-24 23:12:14 +03:00
. get = orangefs_xattr_get_default ,
. set = orangefs_xattr_set_default ,
2015-07-17 17:38:15 +03:00
} ;
2015-11-24 23:12:14 +03:00
const struct xattr_handler * orangefs_xattr_handlers [ ] = {
2015-07-17 17:38:15 +03:00
& posix_acl_access_xattr_handler ,
& posix_acl_default_xattr_handler ,
2015-11-24 23:12:14 +03:00
& orangefs_xattr_default_handler ,
2015-07-17 17:38:15 +03:00
NULL
} ;