2005-04-16 15:20:36 -07:00
/*
* linux / fs / ext2 / acl . c
*
* Copyright ( C ) 2001 - 2003 Andreas Gruenbacher , < agruen @ suse . de >
*/
# include <linux/init.h>
# include <linux/sched.h>
# include <linux/slab.h>
# include <linux/fs.h>
# include "ext2.h"
# include "xattr.h"
# include "acl.h"
/*
* Convert from filesystem to in - memory representation .
*/
static struct posix_acl *
ext2_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 ( ext2_acl_header ) )
return ERR_PTR ( - EINVAL ) ;
if ( ( ( ext2_acl_header * ) value ) - > a_version ! =
cpu_to_le32 ( EXT2_ACL_VERSION ) )
return ERR_PTR ( - EINVAL ) ;
value = ( char * ) value + sizeof ( ext2_acl_header ) ;
count = ext2_acl_count ( size ) ;
if ( count < 0 )
return ERR_PTR ( - EINVAL ) ;
if ( count = = 0 )
return NULL ;
acl = posix_acl_alloc ( count , GFP_KERNEL ) ;
if ( ! acl )
return ERR_PTR ( - ENOMEM ) ;
for ( n = 0 ; n < count ; n + + ) {
ext2_acl_entry * entry =
( ext2_acl_entry * ) value ;
if ( ( char * ) value + sizeof ( ext2_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 ( ext2_acl_entry_short ) ;
break ;
case ACL_USER :
2012-09-10 20:44:54 -07:00
value = ( char * ) value + sizeof ( ext2_acl_entry ) ;
if ( ( char * ) value > end )
goto fail ;
acl - > a_entries [ n ] . e_uid =
make_kuid ( & init_user_ns ,
le32_to_cpu ( entry - > e_id ) ) ;
break ;
2005-04-16 15:20:36 -07:00
case ACL_GROUP :
value = ( char * ) value + sizeof ( ext2_acl_entry ) ;
if ( ( char * ) value > end )
goto fail ;
2012-09-10 20:44:54 -07:00
acl - > a_entries [ n ] . e_gid =
make_kgid ( & init_user_ns ,
le32_to_cpu ( entry - > e_id ) ) ;
2005-04-16 15:20:36 -07:00
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 *
ext2_acl_to_disk ( const struct posix_acl * acl , size_t * size )
{
ext2_acl_header * ext_acl ;
char * e ;
size_t n ;
* size = ext2_acl_size ( acl - > a_count ) ;
2006-09-27 01:49:39 -07:00
ext_acl = kmalloc ( sizeof ( ext2_acl_header ) + acl - > a_count *
sizeof ( ext2_acl_entry ) , GFP_KERNEL ) ;
2005-04-16 15:20:36 -07:00
if ( ! ext_acl )
return ERR_PTR ( - ENOMEM ) ;
ext_acl - > a_version = cpu_to_le32 ( EXT2_ACL_VERSION ) ;
e = ( char * ) ext_acl + sizeof ( ext2_acl_header ) ;
for ( n = 0 ; n < acl - > a_count ; n + + ) {
2012-09-10 20:44:54 -07:00
const struct posix_acl_entry * acl_e = & acl - > a_entries [ n ] ;
2005-04-16 15:20:36 -07:00
ext2_acl_entry * entry = ( ext2_acl_entry * ) e ;
2012-09-10 20:44:54 -07:00
entry - > e_tag = cpu_to_le16 ( acl_e - > e_tag ) ;
entry - > e_perm = cpu_to_le16 ( acl_e - > e_perm ) ;
switch ( acl_e - > e_tag ) {
2005-04-16 15:20:36 -07:00
case ACL_USER :
2012-09-10 20:44:54 -07:00
entry - > e_id = cpu_to_le32 (
from_kuid ( & init_user_ns , acl_e - > e_uid ) ) ;
e + = sizeof ( ext2_acl_entry ) ;
break ;
2005-04-16 15:20:36 -07:00
case ACL_GROUP :
2012-09-10 20:44:54 -07:00
entry - > e_id = cpu_to_le32 (
from_kgid ( & init_user_ns , acl_e - > e_gid ) ) ;
2005-04-16 15:20:36 -07:00
e + = sizeof ( ext2_acl_entry ) ;
break ;
case ACL_USER_OBJ :
case ACL_GROUP_OBJ :
case ACL_MASK :
case ACL_OTHER :
e + = sizeof ( ext2_acl_entry_short ) ;
break ;
default :
goto fail ;
}
}
return ( char * ) ext_acl ;
fail :
kfree ( ext_acl ) ;
return ERR_PTR ( - EINVAL ) ;
}
/*
2006-01-09 15:59:24 -08:00
* inode - > i_mutex : don ' t care
2005-04-16 15:20:36 -07:00
*/
2011-07-23 17:37:31 +02:00
struct posix_acl *
2005-04-16 15:20:36 -07:00
ext2_get_acl ( struct inode * inode , int type )
{
int name_index ;
char * value = NULL ;
struct posix_acl * acl ;
int retval ;
2009-06-09 12:11:54 -04:00
switch ( type ) {
case ACL_TYPE_ACCESS :
name_index = EXT2_XATTR_INDEX_POSIX_ACL_ACCESS ;
break ;
case ACL_TYPE_DEFAULT :
name_index = EXT2_XATTR_INDEX_POSIX_ACL_DEFAULT ;
break ;
default :
BUG ( ) ;
2005-04-16 15:20:36 -07:00
}
retval = ext2_xattr_get ( inode , name_index , " " , NULL , 0 ) ;
if ( retval > 0 ) {
value = kmalloc ( retval , GFP_KERNEL ) ;
if ( ! value )
return ERR_PTR ( - ENOMEM ) ;
retval = ext2_xattr_get ( inode , name_index , " " , value , retval ) ;
}
if ( retval > 0 )
acl = ext2_acl_from_disk ( value , retval ) ;
else if ( retval = = - ENODATA | | retval = = - ENOSYS )
acl = NULL ;
else
acl = ERR_PTR ( retval ) ;
2005-11-07 01:01:34 -08:00
kfree ( value ) ;
2005-04-16 15:20:36 -07:00
return acl ;
}
2017-06-21 14:34:15 +02:00
static int
__ext2_set_acl ( struct inode * inode , struct posix_acl * acl , int type )
2005-04-16 15:20:36 -07:00
{
int name_index ;
void * value = NULL ;
2006-02-03 03:04:13 -08:00
size_t size = 0 ;
2005-04-16 15:20:36 -07:00
int error ;
switch ( type ) {
case ACL_TYPE_ACCESS :
name_index = EXT2_XATTR_INDEX_POSIX_ACL_ACCESS ;
break ;
case ACL_TYPE_DEFAULT :
name_index = EXT2_XATTR_INDEX_POSIX_ACL_DEFAULT ;
if ( ! S_ISDIR ( inode - > i_mode ) )
return acl ? - EACCES : 0 ;
break ;
default :
return - EINVAL ;
}
if ( acl ) {
value = ext2_acl_to_disk ( acl , & size ) ;
if ( IS_ERR ( value ) )
return ( int ) PTR_ERR ( value ) ;
}
error = ext2_xattr_set ( inode , name_index , " " , value , size , 0 ) ;
2005-11-07 01:01:34 -08:00
kfree ( value ) ;
2009-06-09 12:11:54 -04:00
if ( ! error )
set_cached_acl ( inode , type , acl ) ;
2005-04-16 15:20:36 -07:00
return error ;
}
2017-06-21 14:34:15 +02:00
/*
* inode - > i_mutex : down
*/
int
ext2_set_acl ( struct inode * inode , struct posix_acl * acl , int type )
{
int error ;
2017-07-12 06:54:19 -03:00
int update_mode = 0 ;
umode_t mode = inode - > i_mode ;
2017-06-21 14:34:15 +02:00
if ( type = = ACL_TYPE_ACCESS & & acl ) {
2017-07-12 06:54:19 -03:00
error = posix_acl_update_mode ( inode , & mode , & acl ) ;
2017-06-21 14:34:15 +02:00
if ( error )
return error ;
2017-07-12 06:54:19 -03:00
update_mode = 1 ;
}
error = __ext2_set_acl ( inode , acl , type ) ;
if ( ! error & & update_mode ) {
inode - > i_mode = mode ;
2017-06-21 14:34:15 +02:00
inode - > i_ctime = current_time ( inode ) ;
mark_inode_dirty ( inode ) ;
}
2017-07-12 06:54:19 -03:00
return error ;
2017-06-21 14:34:15 +02:00
}
2005-04-16 15:20:36 -07:00
/*
* Initialize the ACLs of a new inode . Called from ext2_new_inode .
*
2006-01-09 15:59:24 -08:00
* dir - > i_mutex : down
* inode - > i_mutex : up ( access to inode is still exclusive )
2005-04-16 15:20:36 -07:00
*/
int
ext2_init_acl ( struct inode * inode , struct inode * dir )
{
2013-12-20 05:16:44 -08:00
struct posix_acl * default_acl , * acl ;
int error ;
2005-04-16 15:20:36 -07:00
2013-12-20 05:16:44 -08:00
error = posix_acl_create ( dir , & inode - > i_mode , & default_acl , & acl ) ;
2011-07-23 00:18:02 -04:00
if ( error )
return error ;
2005-04-16 15:20:36 -07:00
2013-12-20 05:16:44 -08:00
if ( default_acl ) {
2017-06-21 14:34:15 +02:00
error = __ext2_set_acl ( inode , default_acl , ACL_TYPE_DEFAULT ) ;
2013-12-20 05:16:44 -08:00
posix_acl_release ( default_acl ) ;
}
if ( acl ) {
if ( ! error )
2017-06-21 14:34:15 +02:00
error = __ext2_set_acl ( inode , acl , ACL_TYPE_ACCESS ) ;
2013-12-20 05:16:44 -08:00
posix_acl_release ( acl ) ;
}
2005-04-16 15:20:36 -07:00
return error ;
}