2016-08-29 16:46:36 +03:00
/*
* FUSE : Filesystem in Userspace
* Copyright ( C ) 2001 - 2016 Miklos Szeredi < miklos @ szeredi . hu >
*
* This program can be distributed under the terms of the GNU GPL .
* See the file COPYING .
*/
# include "fuse_i.h"
# include <linux/xattr.h>
2016-08-29 16:46:37 +03:00
# include <linux/posix_acl_xattr.h>
2016-08-29 16:46:36 +03:00
2016-08-29 16:46:37 +03:00
int fuse_setxattr ( struct inode * inode , const char * name , const void * value ,
size_t size , int flags )
2016-08-29 16:46:36 +03:00
{
struct fuse_conn * fc = get_fuse_conn ( inode ) ;
FUSE_ARGS ( args ) ;
struct fuse_setxattr_in inarg ;
int err ;
if ( fc - > no_setxattr )
return - EOPNOTSUPP ;
memset ( & inarg , 0 , sizeof ( inarg ) ) ;
inarg . size = size ;
inarg . flags = flags ;
2019-09-10 16:04:08 +03:00
args . opcode = FUSE_SETXATTR ;
args . nodeid = get_node_id ( inode ) ;
args . in_numargs = 3 ;
args . in_args [ 0 ] . size = sizeof ( inarg ) ;
args . in_args [ 0 ] . value = & inarg ;
args . in_args [ 1 ] . size = strlen ( name ) + 1 ;
args . in_args [ 1 ] . value = name ;
args . in_args [ 2 ] . size = size ;
args . in_args [ 2 ] . value = value ;
2016-08-29 16:46:36 +03:00
err = fuse_simple_request ( fc , & args ) ;
if ( err = = - ENOSYS ) {
fc - > no_setxattr = 1 ;
err = - EOPNOTSUPP ;
}
if ( ! err ) {
fuse_invalidate_attr ( inode ) ;
fuse_update_ctime ( inode ) ;
}
return err ;
}
2016-08-29 16:46:37 +03:00
ssize_t fuse_getxattr ( struct inode * inode , const char * name , void * value ,
size_t size )
2016-08-29 16:46:36 +03:00
{
struct fuse_conn * fc = get_fuse_conn ( inode ) ;
FUSE_ARGS ( args ) ;
struct fuse_getxattr_in inarg ;
struct fuse_getxattr_out outarg ;
ssize_t ret ;
if ( fc - > no_getxattr )
return - EOPNOTSUPP ;
memset ( & inarg , 0 , sizeof ( inarg ) ) ;
inarg . size = size ;
2019-09-10 16:04:08 +03:00
args . opcode = FUSE_GETXATTR ;
args . nodeid = get_node_id ( inode ) ;
args . in_numargs = 2 ;
args . in_args [ 0 ] . size = sizeof ( inarg ) ;
args . in_args [ 0 ] . value = & inarg ;
args . in_args [ 1 ] . size = strlen ( name ) + 1 ;
args . in_args [ 1 ] . value = name ;
2016-08-29 16:46:36 +03:00
/* This is really two different operations rolled into one */
2019-09-10 16:04:08 +03:00
args . out_numargs = 1 ;
2016-08-29 16:46:36 +03:00
if ( size ) {
2019-09-10 16:04:08 +03:00
args . out_argvar = true ;
2019-09-10 16:04:08 +03:00
args . out_args [ 0 ] . size = size ;
args . out_args [ 0 ] . value = value ;
2016-08-29 16:46:36 +03:00
} else {
2019-09-10 16:04:08 +03:00
args . out_args [ 0 ] . size = sizeof ( outarg ) ;
args . out_args [ 0 ] . value = & outarg ;
2016-08-29 16:46:36 +03:00
}
ret = fuse_simple_request ( fc , & args ) ;
if ( ! ret & & ! size )
2016-10-03 12:06:05 +03:00
ret = min_t ( ssize_t , outarg . size , XATTR_SIZE_MAX ) ;
2016-08-29 16:46:36 +03:00
if ( ret = = - ENOSYS ) {
fc - > no_getxattr = 1 ;
ret = - EOPNOTSUPP ;
}
return ret ;
}
static int fuse_verify_xattr_list ( char * list , size_t size )
{
size_t origsize = size ;
while ( size ) {
size_t thislen = strnlen ( list , size ) ;
if ( ! thislen | | thislen = = size )
return - EIO ;
size - = thislen + 1 ;
list + = thislen + 1 ;
}
return origsize ;
}
ssize_t fuse_listxattr ( struct dentry * entry , char * list , size_t size )
{
struct inode * inode = d_inode ( entry ) ;
struct fuse_conn * fc = get_fuse_conn ( inode ) ;
FUSE_ARGS ( args ) ;
struct fuse_getxattr_in inarg ;
struct fuse_getxattr_out outarg ;
ssize_t ret ;
if ( ! fuse_allow_current_process ( fc ) )
return - EACCES ;
if ( fc - > no_listxattr )
return - EOPNOTSUPP ;
memset ( & inarg , 0 , sizeof ( inarg ) ) ;
inarg . size = size ;
2019-09-10 16:04:08 +03:00
args . opcode = FUSE_LISTXATTR ;
args . nodeid = get_node_id ( inode ) ;
args . in_numargs = 1 ;
args . in_args [ 0 ] . size = sizeof ( inarg ) ;
args . in_args [ 0 ] . value = & inarg ;
2016-08-29 16:46:36 +03:00
/* This is really two different operations rolled into one */
2019-09-10 16:04:08 +03:00
args . out_numargs = 1 ;
2016-08-29 16:46:36 +03:00
if ( size ) {
2019-09-10 16:04:08 +03:00
args . out_argvar = true ;
2019-09-10 16:04:08 +03:00
args . out_args [ 0 ] . size = size ;
args . out_args [ 0 ] . value = list ;
2016-08-29 16:46:36 +03:00
} else {
2019-09-10 16:04:08 +03:00
args . out_args [ 0 ] . size = sizeof ( outarg ) ;
args . out_args [ 0 ] . value = & outarg ;
2016-08-29 16:46:36 +03:00
}
ret = fuse_simple_request ( fc , & args ) ;
if ( ! ret & & ! size )
2016-10-03 12:06:05 +03:00
ret = min_t ( ssize_t , outarg . size , XATTR_LIST_MAX ) ;
2016-08-29 16:46:36 +03:00
if ( ret > 0 & & size )
ret = fuse_verify_xattr_list ( list , ret ) ;
if ( ret = = - ENOSYS ) {
fc - > no_listxattr = 1 ;
ret = - EOPNOTSUPP ;
}
return ret ;
}
2016-08-29 16:46:37 +03:00
int fuse_removexattr ( struct inode * inode , const char * name )
2016-08-29 16:46:36 +03:00
{
struct fuse_conn * fc = get_fuse_conn ( inode ) ;
FUSE_ARGS ( args ) ;
int err ;
if ( fc - > no_removexattr )
return - EOPNOTSUPP ;
2019-09-10 16:04:08 +03:00
args . opcode = FUSE_REMOVEXATTR ;
args . nodeid = get_node_id ( inode ) ;
args . in_numargs = 1 ;
args . in_args [ 0 ] . size = strlen ( name ) + 1 ;
args . in_args [ 0 ] . value = name ;
2016-08-29 16:46:36 +03:00
err = fuse_simple_request ( fc , & args ) ;
if ( err = = - ENOSYS ) {
fc - > no_removexattr = 1 ;
err = - EOPNOTSUPP ;
}
if ( ! err ) {
fuse_invalidate_attr ( inode ) ;
fuse_update_ctime ( inode ) ;
}
return err ;
}
static int fuse_xattr_get ( const struct xattr_handler * handler ,
struct dentry * dentry , struct inode * inode ,
const char * name , void * value , size_t size )
{
return fuse_getxattr ( inode , name , value , size ) ;
}
static int fuse_xattr_set ( const struct xattr_handler * handler ,
struct dentry * dentry , struct inode * inode ,
const char * name , const void * value , size_t size ,
int flags )
{
if ( ! value )
return fuse_removexattr ( inode , name ) ;
return fuse_setxattr ( inode , name , value , size , flags ) ;
}
2018-05-04 19:47:28 +03:00
static bool no_xattr_list ( struct dentry * dentry )
{
return false ;
}
static int no_xattr_get ( const struct xattr_handler * handler ,
struct dentry * dentry , struct inode * inode ,
const char * name , void * value , size_t size )
{
return - EOPNOTSUPP ;
}
static int no_xattr_set ( const struct xattr_handler * handler ,
struct dentry * dentry , struct inode * nodee ,
const char * name , const void * value ,
size_t size , int flags )
{
return - EOPNOTSUPP ;
}
2016-08-29 16:46:36 +03:00
static const struct xattr_handler fuse_xattr_handler = {
. prefix = " " ,
. get = fuse_xattr_get ,
. set = fuse_xattr_set ,
} ;
const struct xattr_handler * fuse_xattr_handlers [ ] = {
& fuse_xattr_handler ,
NULL
} ;
2016-08-29 16:46:37 +03:00
const struct xattr_handler * fuse_acl_xattr_handlers [ ] = {
& posix_acl_access_xattr_handler ,
& posix_acl_default_xattr_handler ,
& fuse_xattr_handler ,
NULL
} ;
2018-05-04 19:47:28 +03:00
static const struct xattr_handler fuse_no_acl_access_xattr_handler = {
. name = XATTR_NAME_POSIX_ACL_ACCESS ,
. flags = ACL_TYPE_ACCESS ,
. list = no_xattr_list ,
. get = no_xattr_get ,
. set = no_xattr_set ,
} ;
static const struct xattr_handler fuse_no_acl_default_xattr_handler = {
. name = XATTR_NAME_POSIX_ACL_DEFAULT ,
. flags = ACL_TYPE_ACCESS ,
. list = no_xattr_list ,
. get = no_xattr_get ,
. set = no_xattr_set ,
} ;
const struct xattr_handler * fuse_no_acl_xattr_handlers [ ] = {
& fuse_no_acl_access_xattr_handler ,
& fuse_no_acl_default_xattr_handler ,
& fuse_xattr_handler ,
NULL
} ;