2005-04-17 02:20:36 +04:00
# include <linux/fs.h>
# include <linux/posix_acl.h>
# include <linux/reiserfs_fs.h>
# include <linux/errno.h>
# include <linux/pagemap.h>
# include <linux/xattr.h>
2005-06-23 11:10:19 +04:00
# include <linux/posix_acl_xattr.h>
2005-04-17 02:20:36 +04:00
# include <linux/reiserfs_xattr.h>
# include <linux/reiserfs_acl.h>
# include <asm/uaccess.h>
static int reiserfs_set_acl ( struct inode * inode , int type , struct posix_acl * acl ) ;
static int
xattr_set_acl ( struct inode * inode , int type , const void * value , size_t size )
{
struct posix_acl * acl ;
int error ;
if ( ! reiserfs_posixacl ( inode - > i_sb ) )
return - EOPNOTSUPP ;
if ( ( current - > fsuid ! = inode - > i_uid ) & & ! capable ( CAP_FOWNER ) )
return - EPERM ;
if ( value ) {
acl = posix_acl_from_xattr ( value , size ) ;
if ( IS_ERR ( acl ) ) {
return PTR_ERR ( acl ) ;
} else if ( acl ) {
error = posix_acl_valid ( acl ) ;
if ( error )
goto release_and_out ;
}
} else
acl = NULL ;
error = reiserfs_set_acl ( inode , type , acl ) ;
release_and_out :
posix_acl_release ( acl ) ;
return error ;
}
static int
xattr_get_acl ( struct inode * inode , int type , void * buffer , size_t size )
{
struct posix_acl * acl ;
int error ;
if ( ! reiserfs_posixacl ( inode - > i_sb ) )
return - EOPNOTSUPP ;
acl = reiserfs_get_acl ( inode , type ) ;
if ( IS_ERR ( acl ) )
return PTR_ERR ( acl ) ;
if ( acl = = NULL )
return - ENODATA ;
error = posix_acl_to_xattr ( acl , buffer , size ) ;
posix_acl_release ( acl ) ;
return error ;
}
/*
* Convert from filesystem to in - memory representation .
*/
static struct posix_acl *
posix_acl_from_disk ( const void * value , size_t size )
{
const char * end = ( char * ) value + size ;
int n , count ;
struct posix_acl * acl ;
if ( ! value )
return NULL ;
if ( size < sizeof ( reiserfs_acl_header ) )
return ERR_PTR ( - EINVAL ) ;
if ( ( ( reiserfs_acl_header * ) value ) - > a_version ! =
cpu_to_le32 ( REISERFS_ACL_VERSION ) )
return ERR_PTR ( - EINVAL ) ;
value = ( char * ) value + sizeof ( reiserfs_acl_header ) ;
count = reiserfs_acl_count ( size ) ;
if ( count < 0 )
return ERR_PTR ( - EINVAL ) ;
if ( count = = 0 )
return NULL ;
acl = posix_acl_alloc ( count , GFP_NOFS ) ;
if ( ! acl )
return ERR_PTR ( - ENOMEM ) ;
for ( n = 0 ; n < count ; n + + ) {
reiserfs_acl_entry * entry =
( reiserfs_acl_entry * ) value ;
if ( ( char * ) value + sizeof ( reiserfs_acl_entry_short ) > end )
goto fail ;
acl - > a_entries [ n ] . e_tag = le16_to_cpu ( entry - > e_tag ) ;
acl - > a_entries [ n ] . e_perm = le16_to_cpu ( entry - > e_perm ) ;
switch ( acl - > a_entries [ n ] . e_tag ) {
case ACL_USER_OBJ :
case ACL_GROUP_OBJ :
case ACL_MASK :
case ACL_OTHER :
value = ( char * ) value +
sizeof ( reiserfs_acl_entry_short ) ;
acl - > a_entries [ n ] . e_id = ACL_UNDEFINED_ID ;
break ;
case ACL_USER :
case ACL_GROUP :
value = ( char * ) value + sizeof ( reiserfs_acl_entry ) ;
if ( ( char * ) value > end )
goto fail ;
acl - > a_entries [ n ] . e_id =
le32_to_cpu ( entry - > e_id ) ;
break ;
default :
goto fail ;
}
}
if ( value ! = end )
goto fail ;
return acl ;
fail :
posix_acl_release ( acl ) ;
return ERR_PTR ( - EINVAL ) ;
}
/*
* Convert from in - memory to filesystem representation .
*/
static void *
posix_acl_to_disk ( const struct posix_acl * acl , size_t * size )
{
reiserfs_acl_header * ext_acl ;
char * e ;
int n ;
* size = reiserfs_acl_size ( acl - > a_count ) ;
ext_acl = ( reiserfs_acl_header * ) kmalloc ( sizeof ( reiserfs_acl_header ) +
acl - > a_count * sizeof ( reiserfs_acl_entry ) , GFP_NOFS ) ;
if ( ! ext_acl )
return ERR_PTR ( - ENOMEM ) ;
ext_acl - > a_version = cpu_to_le32 ( REISERFS_ACL_VERSION ) ;
e = ( char * ) ext_acl + sizeof ( reiserfs_acl_header ) ;
for ( n = 0 ; n < acl - > a_count ; n + + ) {
reiserfs_acl_entry * entry = ( reiserfs_acl_entry * ) e ;
entry - > e_tag = cpu_to_le16 ( acl - > a_entries [ n ] . e_tag ) ;
entry - > e_perm = cpu_to_le16 ( acl - > a_entries [ n ] . e_perm ) ;
switch ( acl - > a_entries [ n ] . e_tag ) {
case ACL_USER :
case ACL_GROUP :
entry - > e_id =
cpu_to_le32 ( acl - > a_entries [ n ] . e_id ) ;
e + = sizeof ( reiserfs_acl_entry ) ;
break ;
case ACL_USER_OBJ :
case ACL_GROUP_OBJ :
case ACL_MASK :
case ACL_OTHER :
e + = sizeof ( reiserfs_acl_entry_short ) ;
break ;
default :
goto fail ;
}
}
return ( char * ) ext_acl ;
fail :
kfree ( ext_acl ) ;
return ERR_PTR ( - EINVAL ) ;
}
/*
* Inode operation get_posix_acl ( ) .
*
* inode - > i_sem : down
* BKL held [ before 2.5 . x ]
*/
struct posix_acl *
reiserfs_get_acl ( struct inode * inode , int type )
{
char * name , * value ;
struct posix_acl * acl , * * p_acl ;
size_t size ;
int retval ;
struct reiserfs_inode_info * reiserfs_i = REISERFS_I ( inode ) ;
switch ( type ) {
case ACL_TYPE_ACCESS :
2005-06-23 11:10:19 +04:00
name = POSIX_ACL_XATTR_ACCESS ;
2005-04-17 02:20:36 +04:00
p_acl = & reiserfs_i - > i_acl_access ;
break ;
case ACL_TYPE_DEFAULT :
2005-06-23 11:10:19 +04:00
name = POSIX_ACL_XATTR_DEFAULT ;
2005-04-17 02:20:36 +04:00
p_acl = & reiserfs_i - > i_acl_default ;
break ;
default :
return ERR_PTR ( - EINVAL ) ;
}
if ( IS_ERR ( * p_acl ) ) {
if ( PTR_ERR ( * p_acl ) = = - ENODATA )
return NULL ;
} else if ( * p_acl ! = NULL )
return posix_acl_dup ( * p_acl ) ;
size = reiserfs_xattr_get ( inode , name , NULL , 0 ) ;
if ( ( int ) size < 0 ) {
if ( size = = - ENODATA | | size = = - ENOSYS ) {
* p_acl = ERR_PTR ( - ENODATA ) ;
return NULL ;
}
return ERR_PTR ( size ) ;
}
value = kmalloc ( size , GFP_NOFS ) ;
if ( ! value )
return ERR_PTR ( - ENOMEM ) ;
retval = reiserfs_xattr_get ( inode , name , value , size ) ;
if ( retval = = - ENODATA | | retval = = - ENOSYS ) {
/* This shouldn't actually happen as it should have
been caught above . . but just in case */
acl = NULL ;
* p_acl = ERR_PTR ( - ENODATA ) ;
} else if ( retval < 0 ) {
acl = ERR_PTR ( retval ) ;
} else {
acl = posix_acl_from_disk ( value , retval ) ;
* p_acl = posix_acl_dup ( acl ) ;
}
kfree ( value ) ;
return acl ;
}
/*
* Inode operation set_posix_acl ( ) .
*
* inode - > i_sem : down
* BKL held [ before 2.5 . x ]
*/
static int
reiserfs_set_acl ( struct inode * inode , int type , struct posix_acl * acl )
{
char * name ;
void * value = NULL ;
struct posix_acl * * p_acl ;
size_t size ;
int error ;
struct reiserfs_inode_info * reiserfs_i = REISERFS_I ( inode ) ;
if ( S_ISLNK ( inode - > i_mode ) )
return - EOPNOTSUPP ;
switch ( type ) {
case ACL_TYPE_ACCESS :
2005-06-23 11:10:19 +04:00
name = POSIX_ACL_XATTR_ACCESS ;
2005-04-17 02:20:36 +04:00
p_acl = & reiserfs_i - > i_acl_access ;
if ( acl ) {
mode_t mode = inode - > i_mode ;
error = posix_acl_equiv_mode ( acl , & mode ) ;
if ( error < 0 )
return error ;
else {
inode - > i_mode = mode ;
if ( error = = 0 )
acl = NULL ;
}
}
break ;
case ACL_TYPE_DEFAULT :
2005-06-23 11:10:19 +04:00
name = POSIX_ACL_XATTR_DEFAULT ;
2005-04-17 02:20:36 +04:00
p_acl = & reiserfs_i - > i_acl_default ;
if ( ! S_ISDIR ( inode - > i_mode ) )
return acl ? - EACCES : 0 ;
break ;
default :
return - EINVAL ;
}
if ( acl ) {
value = posix_acl_to_disk ( acl , & size ) ;
if ( IS_ERR ( value ) )
return ( int ) PTR_ERR ( value ) ;
error = reiserfs_xattr_set ( inode , name , value , size , 0 ) ;
} else {
error = reiserfs_xattr_del ( inode , name ) ;
if ( error = = - ENODATA ) {
/* This may seem odd here, but it means that the ACL was set
* with a value representable with mode bits . If there was
* an ACL before , reiserfs_xattr_del already dirtied the inode .
*/
mark_inode_dirty ( inode ) ;
error = 0 ;
}
}
if ( value )
kfree ( value ) ;
if ( ! error ) {
/* Release the old one */
if ( ! IS_ERR ( * p_acl ) & & * p_acl )
posix_acl_release ( * p_acl ) ;
if ( acl = = NULL )
* p_acl = ERR_PTR ( - ENODATA ) ;
else
* p_acl = posix_acl_dup ( acl ) ;
}
return error ;
}
/* dir->i_sem: down,
* inode is new and not released into the wild yet */
int
reiserfs_inherit_default_acl ( struct inode * dir , struct dentry * dentry , struct inode * inode )
{
struct posix_acl * acl ;
int err = 0 ;
/* ACLs only get applied to files and directories */
if ( S_ISLNK ( inode - > i_mode ) )
return 0 ;
/* ACLs can only be used on "new" objects, so if it's an old object
* there is nothing to inherit from */
if ( get_inode_sd_version ( dir ) = = STAT_DATA_V1 )
goto apply_umask ;
/* Don't apply ACLs to objects in the .reiserfs_priv tree.. This
* would be useless since permissions are ignored , and a pain because
* it introduces locking cycles */
if ( is_reiserfs_priv_object ( dir ) ) {
reiserfs_mark_inode_private ( inode ) ;
goto apply_umask ;
}
acl = reiserfs_get_acl ( dir , ACL_TYPE_DEFAULT ) ;
if ( IS_ERR ( acl ) ) {
if ( PTR_ERR ( acl ) = = - ENODATA )
goto apply_umask ;
return PTR_ERR ( acl ) ;
}
if ( acl ) {
struct posix_acl * acl_copy ;
mode_t mode = inode - > i_mode ;
int need_acl ;
/* Copy the default ACL to the default ACL of a new directory */
if ( S_ISDIR ( inode - > i_mode ) ) {
err = reiserfs_set_acl ( inode , ACL_TYPE_DEFAULT , acl ) ;
if ( err )
goto cleanup ;
}
/* Now we reconcile the new ACL and the mode,
potentially modifying both */
acl_copy = posix_acl_clone ( acl , GFP_NOFS ) ;
if ( ! acl_copy ) {
err = - ENOMEM ;
goto cleanup ;
}
need_acl = posix_acl_create_masq ( acl_copy , & mode ) ;
if ( need_acl > = 0 ) {
if ( mode ! = inode - > i_mode ) {
inode - > i_mode = mode ;
}
/* If we need an ACL.. */
if ( need_acl > 0 ) {
err = reiserfs_set_acl ( inode , ACL_TYPE_ACCESS , acl_copy ) ;
if ( err )
goto cleanup_copy ;
}
}
cleanup_copy :
posix_acl_release ( acl_copy ) ;
cleanup :
posix_acl_release ( acl ) ;
} else {
apply_umask :
/* no ACL, apply umask */
inode - > i_mode & = ~ current - > fs - > umask ;
}
return err ;
}
/* Looks up and caches the result of the default ACL.
* We do this so that we don ' t need to carry the xattr_sem into
* reiserfs_new_inode if we don ' t need to */
int
reiserfs_cache_default_acl ( struct inode * inode )
{
int ret = 0 ;
if ( reiserfs_posixacl ( inode - > i_sb ) & &
! is_reiserfs_priv_object ( inode ) ) {
struct posix_acl * acl ;
reiserfs_read_lock_xattr_i ( inode ) ;
reiserfs_read_lock_xattrs ( inode - > i_sb ) ;
acl = reiserfs_get_acl ( inode , ACL_TYPE_DEFAULT ) ;
reiserfs_read_unlock_xattrs ( inode - > i_sb ) ;
reiserfs_read_unlock_xattr_i ( inode ) ;
ret = acl ? 1 : 0 ;
posix_acl_release ( acl ) ;
}
return ret ;
}
int
reiserfs_acl_chmod ( struct inode * inode )
{
struct posix_acl * acl , * clone ;
int error ;
if ( S_ISLNK ( inode - > i_mode ) )
return - EOPNOTSUPP ;
if ( get_inode_sd_version ( inode ) = = STAT_DATA_V1 | |
! reiserfs_posixacl ( inode - > i_sb ) )
{
return 0 ;
}
reiserfs_read_lock_xattrs ( inode - > i_sb ) ;
acl = reiserfs_get_acl ( inode , ACL_TYPE_ACCESS ) ;
reiserfs_read_unlock_xattrs ( inode - > i_sb ) ;
if ( ! acl )
return 0 ;
if ( IS_ERR ( acl ) )
return PTR_ERR ( acl ) ;
clone = posix_acl_clone ( acl , GFP_NOFS ) ;
posix_acl_release ( acl ) ;
if ( ! clone )
return - ENOMEM ;
error = posix_acl_chmod_masq ( clone , inode - > i_mode ) ;
if ( ! error ) {
int lock = ! has_xattr_dir ( inode ) ;
reiserfs_write_lock_xattr_i ( inode ) ;
if ( lock )
reiserfs_write_lock_xattrs ( inode - > i_sb ) ;
else
reiserfs_read_lock_xattrs ( inode - > i_sb ) ;
error = reiserfs_set_acl ( inode , ACL_TYPE_ACCESS , clone ) ;
if ( lock )
reiserfs_write_unlock_xattrs ( inode - > i_sb ) ;
else
reiserfs_read_unlock_xattrs ( inode - > i_sb ) ;
reiserfs_write_unlock_xattr_i ( inode ) ;
}
posix_acl_release ( clone ) ;
return error ;
}
static int
posix_acl_access_get ( struct inode * inode , const char * name ,
void * buffer , size_t size )
{
2005-06-23 11:10:19 +04:00
if ( strlen ( name ) ! = sizeof ( POSIX_ACL_XATTR_ACCESS ) - 1 )
2005-04-17 02:20:36 +04:00
return - EINVAL ;
return xattr_get_acl ( inode , ACL_TYPE_ACCESS , buffer , size ) ;
}
static int
posix_acl_access_set ( struct inode * inode , const char * name ,
const void * value , size_t size , int flags )
{
2005-06-23 11:10:19 +04:00
if ( strlen ( name ) ! = sizeof ( POSIX_ACL_XATTR_ACCESS ) - 1 )
2005-04-17 02:20:36 +04:00
return - EINVAL ;
return xattr_set_acl ( inode , ACL_TYPE_ACCESS , value , size ) ;
}
static int
posix_acl_access_del ( struct inode * inode , const char * name )
{
struct reiserfs_inode_info * reiserfs_i = REISERFS_I ( inode ) ;
struct posix_acl * * acl = & reiserfs_i - > i_acl_access ;
2005-06-23 11:10:19 +04:00
if ( strlen ( name ) ! = sizeof ( POSIX_ACL_XATTR_ACCESS ) - 1 )
2005-04-17 02:20:36 +04:00
return - EINVAL ;
if ( ! IS_ERR ( * acl ) & & * acl ) {
posix_acl_release ( * acl ) ;
* acl = ERR_PTR ( - ENODATA ) ;
}
return 0 ;
}
static int
posix_acl_access_list ( struct inode * inode , const char * name , int namelen , char * out )
{
int len = namelen ;
if ( ! reiserfs_posixacl ( inode - > i_sb ) )
return 0 ;
if ( out )
memcpy ( out , name , len ) ;
return len ;
}
struct reiserfs_xattr_handler posix_acl_access_handler = {
2005-06-23 11:10:19 +04:00
. prefix = POSIX_ACL_XATTR_ACCESS ,
2005-04-17 02:20:36 +04:00
. get = posix_acl_access_get ,
. set = posix_acl_access_set ,
. del = posix_acl_access_del ,
. list = posix_acl_access_list ,
} ;
static int
posix_acl_default_get ( struct inode * inode , const char * name ,
void * buffer , size_t size )
{
2005-06-23 11:10:19 +04:00
if ( strlen ( name ) ! = sizeof ( POSIX_ACL_XATTR_DEFAULT ) - 1 )
2005-04-17 02:20:36 +04:00
return - EINVAL ;
return xattr_get_acl ( inode , ACL_TYPE_DEFAULT , buffer , size ) ;
}
static int
posix_acl_default_set ( struct inode * inode , const char * name ,
const void * value , size_t size , int flags )
{
2005-06-23 11:10:19 +04:00
if ( strlen ( name ) ! = sizeof ( POSIX_ACL_XATTR_DEFAULT ) - 1 )
2005-04-17 02:20:36 +04:00
return - EINVAL ;
return xattr_set_acl ( inode , ACL_TYPE_DEFAULT , value , size ) ;
}
static int
posix_acl_default_del ( struct inode * inode , const char * name )
{
struct reiserfs_inode_info * reiserfs_i = REISERFS_I ( inode ) ;
struct posix_acl * * acl = & reiserfs_i - > i_acl_default ;
2005-06-23 11:10:19 +04:00
if ( strlen ( name ) ! = sizeof ( POSIX_ACL_XATTR_DEFAULT ) - 1 )
2005-04-17 02:20:36 +04:00
return - EINVAL ;
if ( ! IS_ERR ( * acl ) & & * acl ) {
posix_acl_release ( * acl ) ;
* acl = ERR_PTR ( - ENODATA ) ;
}
return 0 ;
}
static int
posix_acl_default_list ( struct inode * inode , const char * name , int namelen , char * out )
{
int len = namelen ;
if ( ! reiserfs_posixacl ( inode - > i_sb ) )
return 0 ;
if ( out )
memcpy ( out , name , len ) ;
return len ;
}
struct reiserfs_xattr_handler posix_acl_default_handler = {
2005-06-23 11:10:19 +04:00
. prefix = POSIX_ACL_XATTR_DEFAULT ,
2005-04-17 02:20:36 +04:00
. get = posix_acl_default_get ,
. set = posix_acl_default_set ,
. del = posix_acl_default_del ,
. list = posix_acl_default_list ,
} ;