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 ,
2021-03-25 18:18:22 +03:00
size_t size , int flags , unsigned int extra_flags )
2016-08-29 16:46:36 +03:00
{
2020-05-06 18:44:12 +03:00
struct fuse_mount * fm = get_fuse_mount ( inode ) ;
2016-08-29 16:46:36 +03:00
FUSE_ARGS ( args ) ;
struct fuse_setxattr_in inarg ;
int err ;
2020-05-06 18:44:12 +03:00
if ( fm - > fc - > no_setxattr )
2016-08-29 16:46:36 +03:00
return - EOPNOTSUPP ;
memset ( & inarg , 0 , sizeof ( inarg ) ) ;
inarg . size = size ;
inarg . flags = flags ;
2021-03-25 18:18:22 +03:00
inarg . setxattr_flags = extra_flags ;
2019-09-10 16:04:08 +03:00
args . opcode = FUSE_SETXATTR ;
args . nodeid = get_node_id ( inode ) ;
args . in_numargs = 3 ;
2021-03-25 18:18:22 +03:00
args . in_args [ 0 ] . size = fm - > fc - > setxattr_ext ?
sizeof ( inarg ) : FUSE_COMPAT_SETXATTR_IN_SIZE ;
2019-09-10 16:04:08 +03:00
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 ;
2020-05-06 18:44:12 +03:00
err = fuse_simple_request ( fm , & args ) ;
2016-08-29 16:46:36 +03:00
if ( err = = - ENOSYS ) {
2020-05-06 18:44:12 +03:00
fm - > fc - > no_setxattr = 1 ;
2016-08-29 16:46:36 +03:00
err = - EOPNOTSUPP ;
}
2021-10-22 18:03:01 +03:00
if ( ! err )
2016-08-29 16:46:36 +03:00
fuse_update_ctime ( inode ) ;
2021-10-22 18:03:01 +03:00
2016-08-29 16:46:36 +03:00
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
{
2020-05-06 18:44:12 +03:00
struct fuse_mount * fm = get_fuse_mount ( inode ) ;
2016-08-29 16:46:36 +03:00
FUSE_ARGS ( args ) ;
struct fuse_getxattr_in inarg ;
struct fuse_getxattr_out outarg ;
ssize_t ret ;
2020-05-06 18:44:12 +03:00
if ( fm - > fc - > no_getxattr )
2016-08-29 16:46:36 +03:00
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
}
2020-05-06 18:44:12 +03:00
ret = fuse_simple_request ( fm , & args ) ;
2016-08-29 16:46:36 +03:00
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 ) {
2020-05-06 18:44:12 +03:00
fm - > fc - > no_getxattr = 1 ;
2016-08-29 16:46:36 +03:00
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 ) ;
2020-05-06 18:44:12 +03:00
struct fuse_mount * fm = get_fuse_mount ( inode ) ;
2016-08-29 16:46:36 +03:00
FUSE_ARGS ( args ) ;
struct fuse_getxattr_in inarg ;
struct fuse_getxattr_out outarg ;
ssize_t ret ;
2020-12-10 17:33:14 +03:00
if ( fuse_is_bad ( inode ) )
return - EIO ;
2020-05-06 18:44:12 +03:00
if ( ! fuse_allow_current_process ( fm - > fc ) )
2016-08-29 16:46:36 +03:00
return - EACCES ;
2020-05-06 18:44:12 +03:00
if ( fm - > fc - > no_listxattr )
2016-08-29 16:46:36 +03:00
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
}
2020-05-06 18:44:12 +03:00
ret = fuse_simple_request ( fm , & args ) ;
2016-08-29 16:46:36 +03:00
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 ) {
2020-05-06 18:44:12 +03:00
fm - > fc - > no_listxattr = 1 ;
2016-08-29 16:46:36 +03:00
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
{
2020-05-06 18:44:12 +03:00
struct fuse_mount * fm = get_fuse_mount ( inode ) ;
2016-08-29 16:46:36 +03:00
FUSE_ARGS ( args ) ;
int err ;
2020-05-06 18:44:12 +03:00
if ( fm - > fc - > no_removexattr )
2016-08-29 16:46:36 +03:00
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 ;
2020-05-06 18:44:12 +03:00
err = fuse_simple_request ( fm , & args ) ;
2016-08-29 16:46:36 +03:00
if ( err = = - ENOSYS ) {
2020-05-06 18:44:12 +03:00
fm - > fc - > no_removexattr = 1 ;
2016-08-29 16:46:36 +03:00
err = - EOPNOTSUPP ;
}
2021-10-22 18:03:01 +03:00
if ( ! err )
2016-08-29 16:46:36 +03:00
fuse_update_ctime ( inode ) ;
2021-10-22 18:03:01 +03:00
2016-08-29 16:46:36 +03:00
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 )
{
2020-12-10 17:33:14 +03:00
if ( fuse_is_bad ( inode ) )
return - EIO ;
2016-08-29 16:46:36 +03:00
return fuse_getxattr ( inode , name , value , size ) ;
}
static int fuse_xattr_set ( const struct xattr_handler * handler ,
2023-01-13 14:49:23 +03:00
struct mnt_idmap * idmap ,
2016-08-29 16:46:36 +03:00
struct dentry * dentry , struct inode * inode ,
const char * name , const void * value , size_t size ,
int flags )
{
2020-12-10 17:33:14 +03:00
if ( fuse_is_bad ( inode ) )
return - EIO ;
2016-08-29 16:46:36 +03:00
if ( ! value )
return fuse_removexattr ( inode , name ) ;
2021-03-25 18:18:22 +03:00
return fuse_setxattr ( inode , name , value , size , flags , 0 ) ;
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 ,
} ;
2023-09-30 08:00:15 +03:00
const struct xattr_handler * const fuse_xattr_handlers [ ] = {
2016-08-29 16:46:36 +03:00
& fuse_xattr_handler ,
NULL
} ;