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/smp_lock.h>
# include <linux/file.h>
# include <linux/xattr.h>
# include <linux/namei.h>
# include <linux/security.h>
# include <linux/syscalls.h>
# include <linux/module.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-04-17 02:20:36 +04:00
# include <asm/uaccess.h>
/*
* Extended attribute SET operations
*/
static long
setxattr ( struct dentry * d , char __user * name , void __user * value ,
size_t size , int flags )
{
int error ;
void * kvalue = NULL ;
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 ;
kvalue = kmalloc ( size , GFP_KERNEL ) ;
if ( ! kvalue )
return - ENOMEM ;
if ( copy_from_user ( kvalue , value , size ) ) {
kfree ( kvalue ) ;
return - EFAULT ;
}
}
2005-09-04 02:55:18 +04:00
down ( & d - > d_inode - > i_sem ) ;
error = security_inode_setxattr ( d , kname , kvalue , size , flags ) ;
if ( error )
goto out ;
2005-04-17 02:20:36 +04:00
error = - EOPNOTSUPP ;
if ( d - > d_inode - > i_op & & d - > d_inode - > i_op - > setxattr ) {
2005-09-04 02:55:18 +04:00
error = d - > d_inode - > i_op - > setxattr ( d , kname , kvalue ,
size , flags ) ;
[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
if ( ! error ) {
fsnotify_xattr ( d ) ;
2005-09-04 02:55:18 +04:00
security_inode_post_setxattr ( d , kname , kvalue ,
size , flags ) ;
[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
}
2005-09-04 02:55:18 +04:00
} else if ( ! strncmp ( kname , XATTR_SECURITY_PREFIX ,
sizeof XATTR_SECURITY_PREFIX - 1 ) ) {
const char * suffix = kname + sizeof XATTR_SECURITY_PREFIX - 1 ;
error = security_inode_setsecurity ( d - > d_inode , suffix , kvalue ,
size , flags ) ;
if ( ! error )
fsnotify_xattr ( d ) ;
2005-04-17 02:20:36 +04:00
}
2005-09-04 02:55:18 +04:00
out :
up ( & d - > d_inode - > i_sem ) ;
2005-04-17 02:20:36 +04:00
if ( kvalue )
kfree ( kvalue ) ;
return error ;
}
asmlinkage long
sys_setxattr ( char __user * path , char __user * name , void __user * value ,
size_t size , int flags )
{
struct nameidata nd ;
int error ;
error = user_path_walk ( path , & nd ) ;
if ( error )
return error ;
error = setxattr ( nd . dentry , name , value , size , flags ) ;
path_release ( & nd ) ;
return error ;
}
asmlinkage long
sys_lsetxattr ( char __user * path , char __user * name , void __user * value ,
size_t size , int flags )
{
struct nameidata nd ;
int error ;
error = user_path_walk_link ( path , & nd ) ;
if ( error )
return error ;
error = setxattr ( nd . dentry , name , value , size , flags ) ;
path_release ( & nd ) ;
return error ;
}
asmlinkage long
sys_fsetxattr ( int fd , char __user * name , void __user * value ,
size_t size , int flags )
{
struct file * f ;
int error = - EBADF ;
f = fget ( fd ) ;
if ( ! f )
return error ;
error = setxattr ( f - > f_dentry , name , value , size , flags ) ;
fput ( f ) ;
return error ;
}
/*
* Extended attribute GET operations
*/
static ssize_t
getxattr ( struct dentry * d , char __user * name , void __user * value , size_t size )
{
ssize_t error ;
void * kvalue = NULL ;
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 ;
kvalue = kmalloc ( size , GFP_KERNEL ) ;
if ( ! kvalue )
return - ENOMEM ;
}
2005-09-04 02:55:18 +04:00
error = security_inode_getxattr ( d , kname ) ;
if ( error )
goto out ;
2005-04-17 02:20:36 +04:00
error = - EOPNOTSUPP ;
2005-09-04 02:55:18 +04:00
if ( d - > d_inode - > i_op & & d - > d_inode - > i_op - > getxattr )
2005-04-17 02:20:36 +04:00
error = d - > d_inode - > i_op - > getxattr ( d , kname , kvalue , size ) ;
2005-09-04 02:55:18 +04:00
else if ( ! strncmp ( kname , XATTR_SECURITY_PREFIX ,
sizeof XATTR_SECURITY_PREFIX - 1 ) ) {
const char * suffix = kname + sizeof XATTR_SECURITY_PREFIX - 1 ;
error = security_inode_getsecurity ( d - > d_inode , suffix , kvalue ,
size ) ;
}
if ( error > 0 ) {
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
}
out :
if ( kvalue )
kfree ( kvalue ) ;
return error ;
}
asmlinkage ssize_t
sys_getxattr ( char __user * path , char __user * name , void __user * value ,
size_t size )
{
struct nameidata nd ;
ssize_t error ;
error = user_path_walk ( path , & nd ) ;
if ( error )
return error ;
error = getxattr ( nd . dentry , name , value , size ) ;
path_release ( & nd ) ;
return error ;
}
asmlinkage ssize_t
sys_lgetxattr ( char __user * path , char __user * name , void __user * value ,
size_t size )
{
struct nameidata nd ;
ssize_t error ;
error = user_path_walk_link ( path , & nd ) ;
if ( error )
return error ;
error = getxattr ( nd . dentry , name , value , size ) ;
path_release ( & nd ) ;
return error ;
}
asmlinkage ssize_t
sys_fgetxattr ( int fd , char __user * name , void __user * value , size_t size )
{
struct file * f ;
ssize_t error = - EBADF ;
f = fget ( fd ) ;
if ( ! f )
return error ;
error = getxattr ( f - > f_dentry , name , value , size ) ;
fput ( f ) ;
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 ;
if ( size ) {
if ( size > XATTR_LIST_MAX )
size = XATTR_LIST_MAX ;
klist = kmalloc ( size , GFP_KERNEL ) ;
if ( ! klist )
return - ENOMEM ;
}
2005-09-04 02:55:18 +04:00
error = security_inode_listxattr ( d ) ;
if ( error )
goto out ;
2005-04-17 02:20:36 +04:00
error = - EOPNOTSUPP ;
if ( d - > d_inode - > i_op & & d - > d_inode - > i_op - > listxattr ) {
error = d - > d_inode - > i_op - > listxattr ( d , klist , size ) ;
2005-09-04 02:55:18 +04:00
} else {
error = security_inode_listsecurity ( d - > d_inode , klist , size ) ;
if ( size & & error > = size )
error = - ERANGE ;
}
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
}
out :
if ( klist )
kfree ( klist ) ;
return error ;
}
asmlinkage ssize_t
sys_listxattr ( char __user * path , char __user * list , size_t size )
{
struct nameidata nd ;
ssize_t error ;
error = user_path_walk ( path , & nd ) ;
if ( error )
return error ;
error = listxattr ( nd . dentry , list , size ) ;
path_release ( & nd ) ;
return error ;
}
asmlinkage ssize_t
sys_llistxattr ( char __user * path , char __user * list , size_t size )
{
struct nameidata nd ;
ssize_t error ;
error = user_path_walk_link ( path , & nd ) ;
if ( error )
return error ;
error = listxattr ( nd . dentry , list , size ) ;
path_release ( & nd ) ;
return error ;
}
asmlinkage ssize_t
sys_flistxattr ( int fd , char __user * list , size_t size )
{
struct file * f ;
ssize_t error = - EBADF ;
f = fget ( fd ) ;
if ( ! f )
return error ;
error = listxattr ( f - > f_dentry , list , size ) ;
fput ( f ) ;
return error ;
}
/*
* Extended attribute REMOVE operations
*/
static long
removexattr ( struct dentry * d , char __user * name )
{
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 ;
error = - EOPNOTSUPP ;
if ( d - > d_inode - > i_op & & d - > d_inode - > i_op - > removexattr ) {
error = security_inode_removexattr ( d , kname ) ;
if ( error )
goto out ;
down ( & d - > d_inode - > i_sem ) ;
error = d - > d_inode - > i_op - > removexattr ( d , kname ) ;
up ( & d - > d_inode - > i_sem ) ;
}
out :
return error ;
}
asmlinkage long
sys_removexattr ( char __user * path , char __user * name )
{
struct nameidata nd ;
int error ;
error = user_path_walk ( path , & nd ) ;
if ( error )
return error ;
error = removexattr ( nd . dentry , name ) ;
path_release ( & nd ) ;
return error ;
}
asmlinkage long
sys_lremovexattr ( char __user * path , char __user * name )
{
struct nameidata nd ;
int error ;
error = user_path_walk_link ( path , & nd ) ;
if ( error )
return error ;
error = removexattr ( nd . dentry , name ) ;
path_release ( & nd ) ;
return error ;
}
asmlinkage long
sys_fremovexattr ( int fd , char __user * name )
{
struct file * f ;
int error = - EBADF ;
f = fget ( fd ) ;
if ( ! f )
return error ;
error = removexattr ( f - > f_dentry , name ) ;
fput ( f ) ;
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 .
*/
static struct xattr_handler *
xattr_resolve_name ( struct xattr_handler * * handlers , const char * * name )
{
struct xattr_handler * handler ;
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 )
{
struct xattr_handler * handler ;
struct inode * inode = dentry - > d_inode ;
handler = xattr_resolve_name ( inode - > i_sb - > s_xattr , & name ) ;
if ( ! handler )
return - EOPNOTSUPP ;
return handler - > get ( inode , name , buffer , size ) ;
}
/*
* 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 )
{
struct inode * inode = dentry - > d_inode ;
struct xattr_handler * handler , * * handlers = inode - > i_sb - > s_xattr ;
unsigned int size = 0 ;
if ( ! buffer ) {
for_each_xattr_handler ( handlers , handler )
size + = handler - > list ( inode , NULL , 0 , NULL , 0 ) ;
} else {
char * buf = buffer ;
for_each_xattr_handler ( handlers , handler ) {
size = handler - > list ( inode , buf , buffer_size , NULL , 0 ) ;
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 )
{
struct xattr_handler * handler ;
struct inode * inode = dentry - > d_inode ;
if ( size = = 0 )
value = " " ; /* empty EA, do not remove */
handler = xattr_resolve_name ( inode - > i_sb - > s_xattr , & name ) ;
if ( ! handler )
return - EOPNOTSUPP ;
return handler - > set ( inode , name , value , size , flags ) ;
}
/*
* 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 )
{
struct xattr_handler * handler ;
struct inode * inode = dentry - > d_inode ;
handler = xattr_resolve_name ( inode - > i_sb - > s_xattr , & name ) ;
if ( ! handler )
return - EOPNOTSUPP ;
return handler - > set ( inode , name , NULL , 0 , XATTR_REPLACE ) ;
}
EXPORT_SYMBOL ( generic_getxattr ) ;
EXPORT_SYMBOL ( generic_listxattr ) ;
EXPORT_SYMBOL ( generic_setxattr ) ;
EXPORT_SYMBOL ( generic_removexattr ) ;