2005-04-17 02:20:36 +04:00
/*
File : fs / xattr . c
Extended attribute handling .
Copyright ( C ) 2001 by Andreas Gruenbacher < a . gruenbacher @ computer . org >
Copyright ( C ) 2001 SGI - Silicon Graphics , Inc < linux - xfs @ oss . sgi . com >
Copyright ( c ) 2004 Red Hat , Inc . , James Morris < jmorris @ redhat . com >
*/
# include <linux/fs.h>
# include <linux/slab.h>
# include <linux/file.h>
# include <linux/xattr.h>
2008-02-16 01:37:38 +03:00
# include <linux/mount.h>
2005-04-17 02:20:36 +04:00
# include <linux/namei.h>
# include <linux/security.h>
2011-03-09 22:39:18 +03:00
# include <linux/evm.h>
2005-04-17 02:20:36 +04:00
# include <linux/syscalls.h>
2011-11-17 08:57:37 +04:00
# include <linux/export.h>
[PATCH] inotify
inotify is intended to correct the deficiencies of dnotify, particularly
its inability to scale and its terrible user interface:
* dnotify requires the opening of one fd per each directory
that you intend to watch. This quickly results in too many
open files and pins removable media, preventing unmount.
* dnotify is directory-based. You only learn about changes to
directories. Sure, a change to a file in a directory affects
the directory, but you are then forced to keep a cache of
stat structures.
* dnotify's interface to user-space is awful. Signals?
inotify provides a more usable, simple, powerful solution to file change
notification:
* inotify's interface is a system call that returns a fd, not SIGIO.
You get a single fd, which is select()-able.
* inotify has an event that says "the filesystem that the item
you were watching is on was unmounted."
* inotify can watch directories or files.
Inotify is currently used by Beagle (a desktop search infrastructure),
Gamin (a FAM replacement), and other projects.
See Documentation/filesystems/inotify.txt.
Signed-off-by: Robert Love <rml@novell.com>
Cc: John McCutchan <ttb@tentacle.dhs.org>
Cc: Christoph Hellwig <hch@lst.de>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2005-07-13 01:06:03 +04:00
# include <linux/fsnotify.h>
2005-11-03 19:00:25 +03:00
# include <linux/audit.h>
2012-04-06 01:25:07 +04:00
# include <linux/vmalloc.h>
2012-02-08 06:52:57 +04:00
# include <linux/posix_acl_xattr.h>
2005-04-17 02:20:36 +04:00
2012-04-06 01:25:07 +04:00
# include <asm/uaccess.h>
2006-01-10 07:51:55 +03:00
2006-01-10 07:51:56 +03:00
/*
* Check permissions for extended attribute access . This is a bit complicated
* because different namespaces have very different rules .
*/
static int
xattr_permission ( struct inode * inode , const char * name , int mask )
{
/*
* We can never set or remove an extended attribute on a read - only
* filesystem or on an immutable / append - only inode .
*/
if ( mask & MAY_WRITE ) {
if ( IS_IMMUTABLE ( inode ) | | IS_APPEND ( inode ) )
return - EPERM ;
}
/*
* No restriction for security . * and system . * from the VFS . Decision
* on these is left to the underlying filesystem / security module .
*/
if ( ! strncmp ( name , XATTR_SECURITY_PREFIX , XATTR_SECURITY_PREFIX_LEN ) | |
! strncmp ( name , XATTR_SYSTEM_PREFIX , XATTR_SYSTEM_PREFIX_LEN ) )
return 0 ;
/*
2011-05-27 16:50:36 +04:00
* The trusted . * namespace can only be accessed by privileged users .
2006-01-10 07:51:56 +03:00
*/
2011-05-27 16:50:36 +04:00
if ( ! strncmp ( name , XATTR_TRUSTED_PREFIX , XATTR_TRUSTED_PREFIX_LEN ) ) {
if ( ! capable ( CAP_SYS_ADMIN ) )
return ( mask & MAY_WRITE ) ? - EPERM : - ENODATA ;
return 0 ;
}
2006-01-10 07:51:56 +03:00
2011-05-27 16:50:36 +04:00
/*
* In the user . * namespace , only regular files and directories can have
2006-11-03 09:07:29 +03:00
* extended attributes . For sticky directories , only the owner and
2011-05-27 16:50:36 +04:00
* privileged users can write attributes .
2006-11-03 09:07:29 +03:00
*/
2006-01-10 07:51:56 +03:00
if ( ! strncmp ( name , XATTR_USER_PREFIX , XATTR_USER_PREFIX_LEN ) ) {
2006-11-03 09:07:29 +03:00
if ( ! S_ISREG ( inode - > i_mode ) & & ! S_ISDIR ( inode - > i_mode ) )
2011-05-27 16:50:36 +04:00
return ( mask & MAY_WRITE ) ? - EPERM : - ENODATA ;
2006-11-03 09:07:29 +03:00
if ( S_ISDIR ( inode - > i_mode ) & & ( inode - > i_mode & S_ISVTX ) & &
2011-03-24 02:43:26 +03:00
( mask & MAY_WRITE ) & & ! inode_owner_or_capable ( inode ) )
2006-01-10 07:51:56 +03:00
return - EPERM ;
}
2008-07-22 08:07:17 +04:00
return inode_permission ( inode , mask ) ;
2006-01-10 07:51:56 +03:00
}
2009-09-03 22:25:56 +04:00
/**
* __vfs_setxattr_noperm - perform setxattr operation without performing
* permission checks .
*
* @ dentry - object to perform setxattr on
* @ name - xattr name to set
* @ value - value to set @ name to
* @ size - size of @ value
* @ flags - flags to pass into filesystem operations
*
* returns the result of the internal setxattr or setsecurity operations .
*
* This function requires the caller to lock the inode ' s i_mutex before it
* is executed . It also assumes that the caller will make the appropriate
* permission checks .
*/
int __vfs_setxattr_noperm ( struct dentry * dentry , const char * name ,
const void * value , size_t size , int flags )
2006-01-10 07:51:55 +03:00
{
struct inode * inode = dentry - > d_inode ;
2009-09-03 22:25:56 +04:00
int error = - EOPNOTSUPP ;
2011-05-28 19:25:51 +04:00
int issec = ! strncmp ( name , XATTR_SECURITY_PREFIX ,
XATTR_SECURITY_PREFIX_LEN ) ;
2006-01-10 07:51:56 +03:00
2011-05-28 19:25:51 +04:00
if ( issec )
inode - > i_flags & = ~ S_NOSEC ;
2006-01-10 07:51:55 +03:00
if ( inode - > i_op - > setxattr ) {
error = inode - > i_op - > setxattr ( dentry , name , value , size , flags ) ;
if ( ! error ) {
fsnotify_xattr ( dentry ) ;
security_inode_post_setxattr ( dentry , name , value ,
size , flags ) ;
}
2011-05-28 19:25:51 +04:00
} else if ( issec ) {
2006-01-10 07:51:56 +03:00
const char * suffix = name + XATTR_SECURITY_PREFIX_LEN ;
2006-01-10 07:51:55 +03:00
error = security_inode_setsecurity ( inode , suffix , value ,
size , flags ) ;
if ( ! error )
fsnotify_xattr ( dentry ) ;
}
2009-09-03 22:25:56 +04:00
return error ;
}
int
vfs_setxattr ( struct dentry * dentry , const char * name , const void * value ,
size_t size , int flags )
{
struct inode * inode = dentry - > d_inode ;
int error ;
error = xattr_permission ( inode , name , MAY_WRITE ) ;
if ( error )
return error ;
mutex_lock ( & inode - > i_mutex ) ;
error = security_inode_setxattr ( dentry , name , value , size , flags ) ;
if ( error )
goto out ;
error = __vfs_setxattr_noperm ( dentry , name , value , size , flags ) ;
2006-01-10 07:51:55 +03:00
out :
mutex_unlock ( & inode - > i_mutex ) ;
return error ;
}
EXPORT_SYMBOL_GPL ( vfs_setxattr ) ;
2008-02-05 09:29:39 +03:00
ssize_t
xattr_getsecurity ( struct inode * inode , const char * name , void * value ,
size_t size )
{
void * buffer = NULL ;
ssize_t len ;
if ( ! value | | ! size ) {
len = security_inode_getsecurity ( inode , name , & buffer , false ) ;
goto out_noalloc ;
}
len = security_inode_getsecurity ( inode , name , & buffer , true ) ;
if ( len < 0 )
return len ;
if ( size < len ) {
len = - ERANGE ;
goto out ;
}
memcpy ( value , buffer , len ) ;
out :
security_release_secctx ( buffer , len ) ;
out_noalloc :
return len ;
}
EXPORT_SYMBOL_GPL ( xattr_getsecurity ) ;
2011-03-09 22:23:34 +03:00
/*
* vfs_getxattr_alloc - allocate memory , if necessary , before calling getxattr
*
* Allocate memory , if not already allocated , or re - allocate correct size ,
* before retrieving the extended attribute .
*
* Returns the result of alloc , if failed , or the getxattr operation .
*/
ssize_t
vfs_getxattr_alloc ( struct dentry * dentry , const char * name , char * * xattr_value ,
size_t xattr_size , gfp_t flags )
{
struct inode * inode = dentry - > d_inode ;
char * value = * xattr_value ;
int error ;
error = xattr_permission ( inode , name , MAY_READ ) ;
if ( error )
return error ;
if ( ! inode - > i_op - > getxattr )
return - EOPNOTSUPP ;
error = inode - > i_op - > getxattr ( dentry , name , NULL , 0 ) ;
if ( error < 0 )
return error ;
if ( ! value | | ( error > xattr_size ) ) {
value = krealloc ( * xattr_value , error + 1 , flags ) ;
if ( ! value )
return - ENOMEM ;
memset ( value , 0 , error + 1 ) ;
}
error = inode - > i_op - > getxattr ( dentry , name , value , error ) ;
* xattr_value = value ;
return error ;
}
/* Compare an extended attribute value with the given value */
int vfs_xattr_cmp ( struct dentry * dentry , const char * xattr_name ,
const char * value , size_t size , gfp_t flags )
{
char * xattr_value = NULL ;
int rc ;
rc = vfs_getxattr_alloc ( dentry , xattr_name , & xattr_value , 0 , flags ) ;
if ( rc < 0 )
return rc ;
if ( ( rc ! = size ) | | ( memcmp ( xattr_value , value , rc ) ! = 0 ) )
rc = - EINVAL ;
else
rc = 0 ;
kfree ( xattr_value ) ;
return rc ;
}
2006-01-10 07:51:55 +03:00
ssize_t
2008-04-29 11:59:41 +04:00
vfs_getxattr ( struct dentry * dentry , const char * name , void * value , size_t size )
2006-01-10 07:51:55 +03:00
{
struct inode * inode = dentry - > d_inode ;
int error ;
2006-01-10 07:51:56 +03:00
error = xattr_permission ( inode , name , MAY_READ ) ;
if ( error )
return error ;
2006-01-10 07:51:55 +03:00
error = security_inode_getxattr ( dentry , name ) ;
if ( error )
return error ;
if ( ! strncmp ( name , XATTR_SECURITY_PREFIX ,
2006-01-10 07:51:56 +03:00
XATTR_SECURITY_PREFIX_LEN ) ) {
const char * suffix = name + XATTR_SECURITY_PREFIX_LEN ;
2008-02-05 09:29:39 +03:00
int ret = xattr_getsecurity ( inode , suffix , value , size ) ;
2006-01-10 07:51:55 +03:00
/*
* Only overwrite the return value if a security module
* is actually active .
*/
2008-02-05 09:29:40 +03:00
if ( ret = = - EOPNOTSUPP )
goto nolsm ;
return ret ;
2006-01-10 07:51:55 +03:00
}
2008-02-05 09:29:40 +03:00
nolsm :
if ( inode - > i_op - > getxattr )
error = inode - > i_op - > getxattr ( dentry , name , value , size ) ;
else
error = - EOPNOTSUPP ;
2006-01-10 07:51:55 +03:00
return error ;
}
EXPORT_SYMBOL_GPL ( vfs_getxattr ) ;
2006-10-10 00:10:48 +04:00
ssize_t
vfs_listxattr ( struct dentry * d , char * list , size_t size )
{
ssize_t error ;
error = security_inode_listxattr ( d ) ;
if ( error )
return error ;
error = - EOPNOTSUPP ;
2008-12-04 18:06:33 +03:00
if ( d - > d_inode - > i_op - > listxattr ) {
2006-10-10 00:10:48 +04:00
error = d - > d_inode - > i_op - > listxattr ( d , list , size ) ;
} else {
error = security_inode_listsecurity ( d - > d_inode , list , size ) ;
if ( size & & error > size )
error = - ERANGE ;
}
return error ;
}
EXPORT_SYMBOL_GPL ( vfs_listxattr ) ;
2006-01-10 07:51:55 +03:00
int
2008-04-29 11:59:41 +04:00
vfs_removexattr ( struct dentry * dentry , const char * name )
2006-01-10 07:51:55 +03:00
{
struct inode * inode = dentry - > d_inode ;
int error ;
if ( ! inode - > i_op - > removexattr )
return - EOPNOTSUPP ;
2006-01-10 07:51:56 +03:00
error = xattr_permission ( inode , name , MAY_WRITE ) ;
if ( error )
return error ;
2011-03-24 00:23:06 +03:00
mutex_lock ( & inode - > i_mutex ) ;
2006-01-10 07:51:55 +03:00
error = security_inode_removexattr ( dentry , name ) ;
2011-03-24 00:23:06 +03:00
if ( error ) {
mutex_unlock ( & inode - > i_mutex ) ;
2006-01-10 07:51:55 +03:00
return error ;
2011-03-24 00:23:06 +03:00
}
2006-01-10 07:51:55 +03:00
error = inode - > i_op - > removexattr ( dentry , name ) ;
mutex_unlock ( & inode - > i_mutex ) ;
2011-03-09 22:39:18 +03:00
if ( ! error ) {
2006-01-10 07:51:55 +03:00
fsnotify_xattr ( dentry ) ;
2011-03-09 22:39:18 +03:00
evm_inode_post_removexattr ( dentry , name ) ;
}
2006-01-10 07:51:55 +03:00
return error ;
}
EXPORT_SYMBOL_GPL ( vfs_removexattr ) ;
2005-04-17 02:20:36 +04:00
/*
* Extended attribute SET operations
*/
static long
2008-04-29 11:59:41 +04:00
setxattr ( struct dentry * d , const char __user * name , const void __user * value ,
2005-04-17 02:20:36 +04:00
size_t size , int flags )
{
int error ;
void * kvalue = NULL ;
2012-04-06 01:25:07 +04:00
void * vvalue = NULL ; /* If non-NULL, we used vmalloc() */
2005-04-17 02:20:36 +04:00
char kname [ XATTR_NAME_MAX + 1 ] ;
if ( flags & ~ ( XATTR_CREATE | XATTR_REPLACE ) )
return - EINVAL ;
error = strncpy_from_user ( kname , name , sizeof ( kname ) ) ;
if ( error = = 0 | | error = = sizeof ( kname ) )
error = - ERANGE ;
if ( error < 0 )
return error ;
if ( size ) {
if ( size > XATTR_SIZE_MAX )
return - E2BIG ;
2012-04-06 01:25:07 +04:00
kvalue = kmalloc ( size , GFP_KERNEL | __GFP_NOWARN ) ;
if ( ! kvalue ) {
vvalue = vmalloc ( size ) ;
if ( ! vvalue )
return - ENOMEM ;
kvalue = vvalue ;
}
if ( copy_from_user ( kvalue , value , size ) ) {
error = - EFAULT ;
goto out ;
}
2012-02-08 06:52:57 +04:00
if ( ( strcmp ( kname , XATTR_NAME_POSIX_ACL_ACCESS ) = = 0 ) | |
( strcmp ( kname , XATTR_NAME_POSIX_ACL_DEFAULT ) = = 0 ) )
posix_acl_fix_xattr_from_user ( kvalue , size ) ;
2005-04-17 02:20:36 +04:00
}
2006-01-10 07:51:55 +03:00
error = vfs_setxattr ( d , kname , kvalue , size , flags ) ;
2012-04-06 01:25:07 +04:00
out :
if ( vvalue )
vfree ( vvalue ) ;
else
kfree ( kvalue ) ;
2005-04-17 02:20:36 +04:00
return error ;
}
2009-01-14 16:14:14 +03:00
SYSCALL_DEFINE5 ( setxattr , const char __user * , pathname ,
const char __user * , name , const void __user * , value ,
size_t , size , int , flags )
2005-04-17 02:20:36 +04:00
{
2008-07-22 17:59:21 +04:00
struct path path ;
2005-04-17 02:20:36 +04:00
int error ;
2008-07-22 17:59:21 +04:00
error = user_path ( pathname , & path ) ;
2005-04-17 02:20:36 +04:00
if ( error )
return error ;
2008-07-22 17:59:21 +04:00
error = mnt_want_write ( path . mnt ) ;
2008-02-16 01:37:38 +03:00
if ( ! error ) {
2008-07-22 17:59:21 +04:00
error = setxattr ( path . dentry , name , value , size , flags ) ;
mnt_drop_write ( path . mnt ) ;
2008-02-16 01:37:38 +03:00
}
2008-07-22 17:59:21 +04:00
path_put ( & path ) ;
2005-04-17 02:20:36 +04:00
return error ;
}
2009-01-14 16:14:14 +03:00
SYSCALL_DEFINE5 ( lsetxattr , const char __user * , pathname ,
const char __user * , name , const void __user * , value ,
size_t , size , int , flags )
2005-04-17 02:20:36 +04:00
{
2008-07-22 17:59:21 +04:00
struct path path ;
2005-04-17 02:20:36 +04:00
int error ;
2008-07-22 17:59:21 +04:00
error = user_lpath ( pathname , & path ) ;
2005-04-17 02:20:36 +04:00
if ( error )
return error ;
2008-07-22 17:59:21 +04:00
error = mnt_want_write ( path . mnt ) ;
2008-02-16 01:37:38 +03:00
if ( ! error ) {
2008-07-22 17:59:21 +04:00
error = setxattr ( path . dentry , name , value , size , flags ) ;
mnt_drop_write ( path . mnt ) ;
2008-02-16 01:37:38 +03:00
}
2008-07-22 17:59:21 +04:00
path_put ( & path ) ;
2005-04-17 02:20:36 +04:00
return error ;
}
2009-01-14 16:14:14 +03:00
SYSCALL_DEFINE5 ( fsetxattr , int , fd , const char __user * , name ,
const void __user * , value , size_t , size , int , flags )
2005-04-17 02:20:36 +04:00
{
2012-08-28 20:52:22 +04:00
struct fd f = fdget ( fd ) ;
2005-11-03 19:00:25 +03:00
struct dentry * dentry ;
2005-04-17 02:20:36 +04:00
int error = - EBADF ;
2012-08-28 20:52:22 +04:00
if ( ! f . file )
2005-04-17 02:20:36 +04:00
return error ;
2012-08-28 20:52:22 +04:00
dentry = f . file - > f_path . dentry ;
2012-10-10 23:25:23 +04:00
audit_inode ( NULL , dentry , 0 ) ;
2012-08-28 20:52:22 +04:00
error = mnt_want_write_file ( f . file ) ;
2008-02-16 01:37:38 +03:00
if ( ! error ) {
error = setxattr ( dentry , name , value , size , flags ) ;
2012-08-28 20:52:22 +04:00
mnt_drop_write_file ( f . file ) ;
2008-02-16 01:37:38 +03:00
}
2012-08-28 20:52:22 +04:00
fdput ( f ) ;
2005-04-17 02:20:36 +04:00
return error ;
}
/*
* Extended attribute GET operations
*/
static ssize_t
2008-04-29 11:59:41 +04:00
getxattr ( struct dentry * d , const char __user * name , void __user * value ,
size_t size )
2005-04-17 02:20:36 +04:00
{
ssize_t error ;
void * kvalue = NULL ;
2012-07-31 01:39:13 +04:00
void * vvalue = NULL ;
2005-04-17 02:20:36 +04:00
char kname [ XATTR_NAME_MAX + 1 ] ;
error = strncpy_from_user ( kname , name , sizeof ( kname ) ) ;
if ( error = = 0 | | error = = sizeof ( kname ) )
error = - ERANGE ;
if ( error < 0 )
return error ;
if ( size ) {
if ( size > XATTR_SIZE_MAX )
size = XATTR_SIZE_MAX ;
2012-07-31 01:39:13 +04:00
kvalue = kzalloc ( size , GFP_KERNEL | __GFP_NOWARN ) ;
if ( ! kvalue ) {
vvalue = vmalloc ( size ) ;
if ( ! vvalue )
return - ENOMEM ;
kvalue = vvalue ;
}
2005-04-17 02:20:36 +04:00
}
2006-01-10 07:51:55 +03:00
error = vfs_getxattr ( d , kname , kvalue , size ) ;
2005-09-04 02:55:18 +04:00
if ( error > 0 ) {
2012-02-08 06:52:57 +04:00
if ( ( strcmp ( kname , XATTR_NAME_POSIX_ACL_ACCESS ) = = 0 ) | |
( strcmp ( kname , XATTR_NAME_POSIX_ACL_DEFAULT ) = = 0 ) )
posix_acl_fix_xattr_to_user ( kvalue , size ) ;
2005-09-04 02:55:18 +04:00
if ( size & & copy_to_user ( value , kvalue , error ) )
error = - EFAULT ;
} else if ( error = = - ERANGE & & size > = XATTR_SIZE_MAX ) {
/* The file system tried to returned a value bigger
than XATTR_SIZE_MAX bytes . Not possible . */
error = - E2BIG ;
2005-04-17 02:20:36 +04:00
}
2012-07-31 01:39:13 +04:00
if ( vvalue )
vfree ( vvalue ) ;
else
kfree ( kvalue ) ;
2005-04-17 02:20:36 +04:00
return error ;
}
2009-01-14 16:14:14 +03:00
SYSCALL_DEFINE4 ( getxattr , const char __user * , pathname ,
const char __user * , name , void __user * , value , size_t , size )
2005-04-17 02:20:36 +04:00
{
2008-07-22 17:59:21 +04:00
struct path path ;
2005-04-17 02:20:36 +04:00
ssize_t error ;
2008-07-22 17:59:21 +04:00
error = user_path ( pathname , & path ) ;
2005-04-17 02:20:36 +04:00
if ( error )
return error ;
2008-07-22 17:59:21 +04:00
error = getxattr ( path . dentry , name , value , size ) ;
path_put ( & path ) ;
2005-04-17 02:20:36 +04:00
return error ;
}
2009-01-14 16:14:14 +03:00
SYSCALL_DEFINE4 ( lgetxattr , const char __user * , pathname ,
const char __user * , name , void __user * , value , size_t , size )
2005-04-17 02:20:36 +04:00
{
2008-07-22 17:59:21 +04:00
struct path path ;
2005-04-17 02:20:36 +04:00
ssize_t error ;
2008-07-22 17:59:21 +04:00
error = user_lpath ( pathname , & path ) ;
2005-04-17 02:20:36 +04:00
if ( error )
return error ;
2008-07-22 17:59:21 +04:00
error = getxattr ( path . dentry , name , value , size ) ;
path_put ( & path ) ;
2005-04-17 02:20:36 +04:00
return error ;
}
2009-01-14 16:14:14 +03:00
SYSCALL_DEFINE4 ( fgetxattr , int , fd , const char __user * , name ,
void __user * , value , size_t , size )
2005-04-17 02:20:36 +04:00
{
2012-08-28 20:52:22 +04:00
struct fd f = fdget ( fd ) ;
2005-04-17 02:20:36 +04:00
ssize_t error = - EBADF ;
2012-08-28 20:52:22 +04:00
if ( ! f . file )
2005-04-17 02:20:36 +04:00
return error ;
2012-10-10 23:25:23 +04:00
audit_inode ( NULL , f . file - > f_path . dentry , 0 ) ;
2012-08-28 20:52:22 +04:00
error = getxattr ( f . file - > f_path . dentry , name , value , size ) ;
fdput ( f ) ;
2005-04-17 02:20:36 +04:00
return error ;
}
/*
* Extended attribute LIST operations
*/
static ssize_t
listxattr ( struct dentry * d , char __user * list , size_t size )
{
ssize_t error ;
char * klist = NULL ;
2012-04-06 01:25:07 +04:00
char * vlist = NULL ; /* If non-NULL, we used vmalloc() */
2005-04-17 02:20:36 +04:00
if ( size ) {
if ( size > XATTR_LIST_MAX )
size = XATTR_LIST_MAX ;
2012-04-06 01:25:06 +04:00
klist = kmalloc ( size , __GFP_NOWARN | GFP_KERNEL ) ;
2012-04-06 01:25:07 +04:00
if ( ! klist ) {
vlist = vmalloc ( size ) ;
if ( ! vlist )
return - ENOMEM ;
klist = vlist ;
}
2005-04-17 02:20:36 +04:00
}
2006-10-10 00:10:48 +04:00
error = vfs_listxattr ( d , klist , size ) ;
2005-09-04 02:55:18 +04:00
if ( error > 0 ) {
if ( size & & copy_to_user ( list , klist , error ) )
error = - EFAULT ;
} else if ( error = = - ERANGE & & size > = XATTR_LIST_MAX ) {
/* The file system tried to returned a list bigger
than XATTR_LIST_MAX bytes . Not possible . */
error = - E2BIG ;
2005-04-17 02:20:36 +04:00
}
2012-04-06 01:25:07 +04:00
if ( vlist )
vfree ( vlist ) ;
else
kfree ( klist ) ;
2005-04-17 02:20:36 +04:00
return error ;
}
2009-01-14 16:14:14 +03:00
SYSCALL_DEFINE3 ( listxattr , const char __user * , pathname , char __user * , list ,
size_t , size )
2005-04-17 02:20:36 +04:00
{
2008-07-22 17:59:21 +04:00
struct path path ;
2005-04-17 02:20:36 +04:00
ssize_t error ;
2008-07-22 17:59:21 +04:00
error = user_path ( pathname , & path ) ;
2005-04-17 02:20:36 +04:00
if ( error )
return error ;
2008-07-22 17:59:21 +04:00
error = listxattr ( path . dentry , list , size ) ;
path_put ( & path ) ;
2005-04-17 02:20:36 +04:00
return error ;
}
2009-01-14 16:14:14 +03:00
SYSCALL_DEFINE3 ( llistxattr , const char __user * , pathname , char __user * , list ,
size_t , size )
2005-04-17 02:20:36 +04:00
{
2008-07-22 17:59:21 +04:00
struct path path ;
2005-04-17 02:20:36 +04:00
ssize_t error ;
2008-07-22 17:59:21 +04:00
error = user_lpath ( pathname , & path ) ;
2005-04-17 02:20:36 +04:00
if ( error )
return error ;
2008-07-22 17:59:21 +04:00
error = listxattr ( path . dentry , list , size ) ;
path_put ( & path ) ;
2005-04-17 02:20:36 +04:00
return error ;
}
2009-01-14 16:14:14 +03:00
SYSCALL_DEFINE3 ( flistxattr , int , fd , char __user * , list , size_t , size )
2005-04-17 02:20:36 +04:00
{
2012-08-28 20:52:22 +04:00
struct fd f = fdget ( fd ) ;
2005-04-17 02:20:36 +04:00
ssize_t error = - EBADF ;
2012-08-28 20:52:22 +04:00
if ( ! f . file )
2005-04-17 02:20:36 +04:00
return error ;
2012-10-10 23:25:23 +04:00
audit_inode ( NULL , f . file - > f_path . dentry , 0 ) ;
2012-08-28 20:52:22 +04:00
error = listxattr ( f . file - > f_path . dentry , list , size ) ;
fdput ( f ) ;
2005-04-17 02:20:36 +04:00
return error ;
}
/*
* Extended attribute REMOVE operations
*/
static long
2008-04-29 11:59:41 +04:00
removexattr ( struct dentry * d , const char __user * name )
2005-04-17 02:20:36 +04:00
{
int error ;
char kname [ XATTR_NAME_MAX + 1 ] ;
error = strncpy_from_user ( kname , name , sizeof ( kname ) ) ;
if ( error = = 0 | | error = = sizeof ( kname ) )
error = - ERANGE ;
if ( error < 0 )
return error ;
2006-01-10 07:51:55 +03:00
return vfs_removexattr ( d , kname ) ;
2005-04-17 02:20:36 +04:00
}
2009-01-14 16:14:14 +03:00
SYSCALL_DEFINE2 ( removexattr , const char __user * , pathname ,
const char __user * , name )
2005-04-17 02:20:36 +04:00
{
2008-07-22 17:59:21 +04:00
struct path path ;
2005-04-17 02:20:36 +04:00
int error ;
2008-07-22 17:59:21 +04:00
error = user_path ( pathname , & path ) ;
2005-04-17 02:20:36 +04:00
if ( error )
return error ;
2008-07-22 17:59:21 +04:00
error = mnt_want_write ( path . mnt ) ;
2008-02-16 01:37:38 +03:00
if ( ! error ) {
2008-07-22 17:59:21 +04:00
error = removexattr ( path . dentry , name ) ;
mnt_drop_write ( path . mnt ) ;
2008-02-16 01:37:38 +03:00
}
2008-07-22 17:59:21 +04:00
path_put ( & path ) ;
2005-04-17 02:20:36 +04:00
return error ;
}
2009-01-14 16:14:15 +03:00
SYSCALL_DEFINE2 ( lremovexattr , const char __user * , pathname ,
const char __user * , name )
2005-04-17 02:20:36 +04:00
{
2008-07-22 17:59:21 +04:00
struct path path ;
2005-04-17 02:20:36 +04:00
int error ;
2008-07-22 17:59:21 +04:00
error = user_lpath ( pathname , & path ) ;
2005-04-17 02:20:36 +04:00
if ( error )
return error ;
2008-07-22 17:59:21 +04:00
error = mnt_want_write ( path . mnt ) ;
2008-02-16 01:37:38 +03:00
if ( ! error ) {
2008-07-22 17:59:21 +04:00
error = removexattr ( path . dentry , name ) ;
mnt_drop_write ( path . mnt ) ;
2008-02-16 01:37:38 +03:00
}
2008-07-22 17:59:21 +04:00
path_put ( & path ) ;
2005-04-17 02:20:36 +04:00
return error ;
}
2009-01-14 16:14:15 +03:00
SYSCALL_DEFINE2 ( fremovexattr , int , fd , const char __user * , name )
2005-04-17 02:20:36 +04:00
{
2012-08-28 20:52:22 +04:00
struct fd f = fdget ( fd ) ;
2005-11-03 19:00:25 +03:00
struct dentry * dentry ;
2005-04-17 02:20:36 +04:00
int error = - EBADF ;
2012-08-28 20:52:22 +04:00
if ( ! f . file )
2005-04-17 02:20:36 +04:00
return error ;
2012-08-28 20:52:22 +04:00
dentry = f . file - > f_path . dentry ;
2012-10-10 23:25:23 +04:00
audit_inode ( NULL , dentry , 0 ) ;
2012-08-28 20:52:22 +04:00
error = mnt_want_write_file ( f . file ) ;
2008-02-16 01:37:38 +03:00
if ( ! error ) {
error = removexattr ( dentry , name ) ;
2012-08-28 20:52:22 +04:00
mnt_drop_write_file ( f . file ) ;
2008-02-16 01:37:38 +03:00
}
2012-08-28 20:52:22 +04:00
fdput ( f ) ;
2005-04-17 02:20:36 +04:00
return error ;
}
static const char *
strcmp_prefix ( const char * a , const char * a_prefix )
{
while ( * a_prefix & & * a = = * a_prefix ) {
a + + ;
a_prefix + + ;
}
return * a_prefix ? NULL : a ;
}
/*
* In order to implement different sets of xattr operations for each xattr
* prefix with the generic xattr API , a filesystem should create a
* null - terminated array of struct xattr_handler ( one for each prefix ) and
* hang a pointer to it off of the s_xattr field of the superblock .
*
* The generic_fooxattr ( ) functions will use this list to dispatch xattr
* operations to the correct xattr_handler .
*/
# define for_each_xattr_handler(handlers, handler) \
for ( ( handler ) = * ( handlers ) + + ; \
( handler ) ! = NULL ; \
( handler ) = * ( handlers ) + + )
/*
* Find the xattr_handler with the matching prefix .
*/
2010-05-14 04:53:14 +04:00
static const struct xattr_handler *
xattr_resolve_name ( const struct xattr_handler * * handlers , const char * * name )
2005-04-17 02:20:36 +04:00
{
2010-05-14 04:53:14 +04:00
const struct xattr_handler * handler ;
2005-04-17 02:20:36 +04:00
if ( ! * name )
return NULL ;
for_each_xattr_handler ( handlers , handler ) {
const char * n = strcmp_prefix ( * name , handler - > prefix ) ;
if ( n ) {
* name = n ;
break ;
}
}
return handler ;
}
/*
* Find the handler for the prefix and dispatch its get ( ) operation .
*/
ssize_t
generic_getxattr ( struct dentry * dentry , const char * name , void * buffer , size_t size )
{
2010-05-14 04:53:14 +04:00
const struct xattr_handler * handler ;
2005-04-17 02:20:36 +04:00
2009-11-13 12:52:56 +03:00
handler = xattr_resolve_name ( dentry - > d_sb - > s_xattr , & name ) ;
2005-04-17 02:20:36 +04:00
if ( ! handler )
return - EOPNOTSUPP ;
2009-11-13 12:52:56 +03:00
return handler - > get ( dentry , name , buffer , size , handler - > flags ) ;
2005-04-17 02:20:36 +04:00
}
/*
* Combine the results of the list ( ) operation from every xattr_handler in the
* list .
*/
ssize_t
generic_listxattr ( struct dentry * dentry , char * buffer , size_t buffer_size )
{
2010-05-14 04:53:14 +04:00
const struct xattr_handler * handler , * * handlers = dentry - > d_sb - > s_xattr ;
2005-04-17 02:20:36 +04:00
unsigned int size = 0 ;
if ( ! buffer ) {
2009-11-13 12:52:56 +03:00
for_each_xattr_handler ( handlers , handler ) {
size + = handler - > list ( dentry , NULL , 0 , NULL , 0 ,
handler - > flags ) ;
}
2005-04-17 02:20:36 +04:00
} else {
char * buf = buffer ;
for_each_xattr_handler ( handlers , handler ) {
2009-11-13 12:52:56 +03:00
size = handler - > list ( dentry , buf , buffer_size ,
NULL , 0 , handler - > flags ) ;
2005-04-17 02:20:36 +04:00
if ( size > buffer_size )
return - ERANGE ;
buf + = size ;
buffer_size - = size ;
}
size = buf - buffer ;
}
return size ;
}
/*
* Find the handler for the prefix and dispatch its set ( ) operation .
*/
int
generic_setxattr ( struct dentry * dentry , const char * name , const void * value , size_t size , int flags )
{
2010-05-14 04:53:14 +04:00
const struct xattr_handler * handler ;
2005-04-17 02:20:36 +04:00
if ( size = = 0 )
value = " " ; /* empty EA, do not remove */
2009-11-13 12:52:56 +03:00
handler = xattr_resolve_name ( dentry - > d_sb - > s_xattr , & name ) ;
2005-04-17 02:20:36 +04:00
if ( ! handler )
return - EOPNOTSUPP ;
2011-04-20 22:30:40 +04:00
return handler - > set ( dentry , name , value , size , flags , handler - > flags ) ;
2005-04-17 02:20:36 +04:00
}
/*
* Find the handler for the prefix and dispatch its set ( ) operation to remove
* any associated extended attribute .
*/
int
generic_removexattr ( struct dentry * dentry , const char * name )
{
2010-05-14 04:53:14 +04:00
const struct xattr_handler * handler ;
2005-04-17 02:20:36 +04:00
2009-11-13 12:52:56 +03:00
handler = xattr_resolve_name ( dentry - > d_sb - > s_xattr , & name ) ;
2005-04-17 02:20:36 +04:00
if ( ! handler )
return - EOPNOTSUPP ;
2009-11-13 12:52:56 +03:00
return handler - > set ( dentry , name , NULL , 0 ,
XATTR_REPLACE , handler - > flags ) ;
2005-04-17 02:20:36 +04:00
}
EXPORT_SYMBOL ( generic_getxattr ) ;
EXPORT_SYMBOL ( generic_listxattr ) ;
EXPORT_SYMBOL ( generic_setxattr ) ;
EXPORT_SYMBOL ( generic_removexattr ) ;
2012-08-24 00:53:28 +04:00
/*
* Allocate new xattr and copy in the value ; but leave the name to callers .
*/
struct simple_xattr * simple_xattr_alloc ( const void * value , size_t size )
{
struct simple_xattr * new_xattr ;
size_t len ;
/* wrap around? */
len = sizeof ( * new_xattr ) + size ;
if ( len < = sizeof ( * new_xattr ) )
return NULL ;
new_xattr = kmalloc ( len , GFP_KERNEL ) ;
if ( ! new_xattr )
return NULL ;
new_xattr - > size = size ;
memcpy ( new_xattr - > value , value , size ) ;
return new_xattr ;
}
/*
* xattr GET operation for in - memory / pseudo filesystems
*/
int simple_xattr_get ( struct simple_xattrs * xattrs , const char * name ,
void * buffer , size_t size )
{
struct simple_xattr * xattr ;
int ret = - ENODATA ;
spin_lock ( & xattrs - > lock ) ;
list_for_each_entry ( xattr , & xattrs - > head , list ) {
if ( strcmp ( name , xattr - > name ) )
continue ;
ret = xattr - > size ;
if ( buffer ) {
if ( size < xattr - > size )
ret = - ERANGE ;
else
memcpy ( buffer , xattr - > value , xattr - > size ) ;
}
break ;
}
spin_unlock ( & xattrs - > lock ) ;
return ret ;
}
static int __simple_xattr_set ( struct simple_xattrs * xattrs , const char * name ,
const void * value , size_t size , int flags )
{
struct simple_xattr * xattr ;
2012-09-12 18:31:13 +04:00
struct simple_xattr * uninitialized_var ( new_xattr ) ;
2012-08-24 00:53:28 +04:00
int err = 0 ;
/* value == NULL means remove */
if ( value ) {
new_xattr = simple_xattr_alloc ( value , size ) ;
if ( ! new_xattr )
return - ENOMEM ;
new_xattr - > name = kstrdup ( name , GFP_KERNEL ) ;
if ( ! new_xattr - > name ) {
kfree ( new_xattr ) ;
return - ENOMEM ;
}
}
spin_lock ( & xattrs - > lock ) ;
list_for_each_entry ( xattr , & xattrs - > head , list ) {
if ( ! strcmp ( name , xattr - > name ) ) {
if ( flags & XATTR_CREATE ) {
xattr = new_xattr ;
err = - EEXIST ;
} else if ( new_xattr ) {
list_replace ( & xattr - > list , & new_xattr - > list ) ;
} else {
list_del ( & xattr - > list ) ;
}
goto out ;
}
}
if ( flags & XATTR_REPLACE ) {
xattr = new_xattr ;
err = - ENODATA ;
} else {
list_add ( & new_xattr - > list , & xattrs - > head ) ;
xattr = NULL ;
}
out :
spin_unlock ( & xattrs - > lock ) ;
if ( xattr ) {
kfree ( xattr - > name ) ;
kfree ( xattr ) ;
}
return err ;
}
2012-09-12 00:28:11 +04:00
/**
* simple_xattr_set - xattr SET operation for in - memory / pseudo filesystems
* @ xattrs : target simple_xattr list
* @ name : name of the new extended attribute
* @ value : value of the new xattr . If % NULL , will remove the attribute
* @ size : size of the new xattr
* @ flags : % XATTR_ { CREATE | REPLACE }
*
* % XATTR_CREATE is set , the xattr shouldn ' t exist already ; otherwise fails
* with - EEXIST . If % XATTR_REPLACE is set , the xattr should exist ;
* otherwise , fails with - ENODATA .
*
* Returns 0 on success , - errno on failure .
2012-08-24 00:53:28 +04:00
*/
int simple_xattr_set ( struct simple_xattrs * xattrs , const char * name ,
const void * value , size_t size , int flags )
{
if ( size = = 0 )
value = " " ; /* empty EA, do not remove */
return __simple_xattr_set ( xattrs , name , value , size , flags ) ;
}
/*
* xattr REMOVE operation for in - memory / pseudo filesystems
*/
int simple_xattr_remove ( struct simple_xattrs * xattrs , const char * name )
{
return __simple_xattr_set ( xattrs , name , NULL , 0 , XATTR_REPLACE ) ;
}
static bool xattr_is_trusted ( const char * name )
{
return ! strncmp ( name , XATTR_TRUSTED_PREFIX , XATTR_TRUSTED_PREFIX_LEN ) ;
}
/*
* xattr LIST operation for in - memory / pseudo filesystems
*/
ssize_t simple_xattr_list ( struct simple_xattrs * xattrs , char * buffer ,
size_t size )
{
bool trusted = capable ( CAP_SYS_ADMIN ) ;
struct simple_xattr * xattr ;
size_t used = 0 ;
spin_lock ( & xattrs - > lock ) ;
list_for_each_entry ( xattr , & xattrs - > head , list ) {
size_t len ;
/* skip "trusted." attributes for unprivileged callers */
if ( ! trusted & & xattr_is_trusted ( xattr - > name ) )
continue ;
len = strlen ( xattr - > name ) + 1 ;
used + = len ;
if ( buffer ) {
if ( size < used ) {
used = - ERANGE ;
break ;
}
memcpy ( buffer , xattr - > name , len ) ;
buffer + = len ;
}
}
spin_unlock ( & xattrs - > lock ) ;
return used ;
}
2012-09-12 00:28:11 +04:00
/*
* Adds an extended attribute to the list
*/
2012-08-24 00:53:28 +04:00
void simple_xattr_list_add ( struct simple_xattrs * xattrs ,
struct simple_xattr * new_xattr )
{
spin_lock ( & xattrs - > lock ) ;
list_add ( & new_xattr - > list , & xattrs - > head ) ;
spin_unlock ( & xattrs - > lock ) ;
}