2012-11-29 08:28:09 +04:00
/*
2012-11-02 12:12:17 +04:00
* fs / f2fs / acl . c
*
* Copyright ( c ) 2012 Samsung Electronics Co . , Ltd .
* http : //www.samsung.com/
*
* Portions of this code from linux / fs / ext2 / acl . c
*
* Copyright ( C ) 2001 - 2003 Andreas Gruenbacher , < agruen @ suse . de >
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation .
*/
# include <linux/f2fs_fs.h>
# include "f2fs.h"
# include "xattr.h"
# include "acl.h"
static inline size_t f2fs_acl_size ( int count )
{
if ( count < = 4 ) {
return sizeof ( struct f2fs_acl_header ) +
count * sizeof ( struct f2fs_acl_entry_short ) ;
} else {
return sizeof ( struct f2fs_acl_header ) +
4 * sizeof ( struct f2fs_acl_entry_short ) +
( count - 4 ) * sizeof ( struct f2fs_acl_entry ) ;
}
}
static inline int f2fs_acl_count ( size_t size )
{
ssize_t s ;
size - = sizeof ( struct f2fs_acl_header ) ;
s = size - 4 * sizeof ( struct f2fs_acl_entry_short ) ;
if ( s < 0 ) {
if ( size % sizeof ( struct f2fs_acl_entry_short ) )
return - 1 ;
return size / sizeof ( struct f2fs_acl_entry_short ) ;
} else {
if ( s % sizeof ( struct f2fs_acl_entry ) )
return - 1 ;
return s / sizeof ( struct f2fs_acl_entry ) + 4 ;
}
}
static struct posix_acl * f2fs_acl_from_disk ( const char * value , size_t size )
{
int i , count ;
struct posix_acl * acl ;
struct f2fs_acl_header * hdr = ( struct f2fs_acl_header * ) value ;
struct f2fs_acl_entry * entry = ( struct f2fs_acl_entry * ) ( hdr + 1 ) ;
const char * end = value + size ;
if ( hdr - > a_version ! = cpu_to_le32 ( F2FS_ACL_VERSION ) )
return ERR_PTR ( - EINVAL ) ;
count = f2fs_acl_count ( size ) ;
if ( count < 0 )
return ERR_PTR ( - EINVAL ) ;
if ( count = = 0 )
return NULL ;
2014-12-19 06:32:36 +03:00
acl = posix_acl_alloc ( count , GFP_NOFS ) ;
2012-11-02 12:12:17 +04:00
if ( ! acl )
return ERR_PTR ( - ENOMEM ) ;
for ( i = 0 ; i < count ; i + + ) {
if ( ( char * ) entry > end )
goto fail ;
acl - > a_entries [ i ] . e_tag = le16_to_cpu ( entry - > e_tag ) ;
acl - > a_entries [ i ] . e_perm = le16_to_cpu ( entry - > e_perm ) ;
switch ( acl - > a_entries [ i ] . e_tag ) {
case ACL_USER_OBJ :
case ACL_GROUP_OBJ :
case ACL_MASK :
case ACL_OTHER :
entry = ( struct f2fs_acl_entry * ) ( ( char * ) entry +
sizeof ( struct f2fs_acl_entry_short ) ) ;
break ;
case ACL_USER :
acl - > a_entries [ i ] . e_uid =
make_kuid ( & init_user_ns ,
le32_to_cpu ( entry - > e_id ) ) ;
entry = ( struct f2fs_acl_entry * ) ( ( char * ) entry +
sizeof ( struct f2fs_acl_entry ) ) ;
break ;
case ACL_GROUP :
acl - > a_entries [ i ] . e_gid =
make_kgid ( & init_user_ns ,
le32_to_cpu ( entry - > e_id ) ) ;
entry = ( struct f2fs_acl_entry * ) ( ( char * ) entry +
sizeof ( struct f2fs_acl_entry ) ) ;
break ;
default :
goto fail ;
}
}
if ( ( char * ) entry ! = end )
goto fail ;
return acl ;
fail :
posix_acl_release ( acl ) ;
return ERR_PTR ( - EINVAL ) ;
}
static void * f2fs_acl_to_disk ( const struct posix_acl * acl , size_t * size )
{
struct f2fs_acl_header * f2fs_acl ;
struct f2fs_acl_entry * entry ;
int i ;
f2fs_acl = kmalloc ( sizeof ( struct f2fs_acl_header ) + acl - > a_count *
2014-12-19 06:32:36 +03:00
sizeof ( struct f2fs_acl_entry ) , GFP_NOFS ) ;
2012-11-02 12:12:17 +04:00
if ( ! f2fs_acl )
return ERR_PTR ( - ENOMEM ) ;
f2fs_acl - > a_version = cpu_to_le32 ( F2FS_ACL_VERSION ) ;
entry = ( struct f2fs_acl_entry * ) ( f2fs_acl + 1 ) ;
for ( i = 0 ; i < acl - > a_count ; i + + ) {
entry - > e_tag = cpu_to_le16 ( acl - > a_entries [ i ] . e_tag ) ;
entry - > e_perm = cpu_to_le16 ( acl - > a_entries [ i ] . e_perm ) ;
switch ( acl - > a_entries [ i ] . e_tag ) {
case ACL_USER :
entry - > e_id = cpu_to_le32 (
from_kuid ( & init_user_ns ,
acl - > a_entries [ i ] . e_uid ) ) ;
entry = ( struct f2fs_acl_entry * ) ( ( char * ) entry +
sizeof ( struct f2fs_acl_entry ) ) ;
break ;
case ACL_GROUP :
entry - > e_id = cpu_to_le32 (
from_kgid ( & init_user_ns ,
acl - > a_entries [ i ] . e_gid ) ) ;
entry = ( struct f2fs_acl_entry * ) ( ( char * ) entry +
sizeof ( struct f2fs_acl_entry ) ) ;
break ;
case ACL_USER_OBJ :
case ACL_GROUP_OBJ :
case ACL_MASK :
case ACL_OTHER :
entry = ( struct f2fs_acl_entry * ) ( ( char * ) entry +
sizeof ( struct f2fs_acl_entry_short ) ) ;
break ;
default :
goto fail ;
}
}
* size = f2fs_acl_size ( acl - > a_count ) ;
return ( void * ) f2fs_acl ;
fail :
kfree ( f2fs_acl ) ;
return ERR_PTR ( - EINVAL ) ;
}
2014-10-14 06:42:53 +04:00
static struct posix_acl * __f2fs_get_acl ( struct inode * inode , int type ,
struct page * dpage )
2012-11-02 12:12:17 +04:00
{
int name_index = F2FS_XATTR_INDEX_POSIX_ACL_DEFAULT ;
void * value = NULL ;
struct posix_acl * acl ;
int retval ;
if ( type = = ACL_TYPE_ACCESS )
name_index = F2FS_XATTR_INDEX_POSIX_ACL_ACCESS ;
2014-10-14 06:42:53 +04:00
retval = f2fs_getxattr ( inode , name_index , " " , NULL , 0 , dpage ) ;
2012-11-02 12:12:17 +04:00
if ( retval > 0 ) {
2014-03-20 17:21:08 +04:00
value = kmalloc ( retval , GFP_F2FS_ZERO ) ;
2012-11-02 12:12:17 +04:00
if ( ! value )
return ERR_PTR ( - ENOMEM ) ;
2014-10-14 06:42:53 +04:00
retval = f2fs_getxattr ( inode , name_index , " " , value ,
retval , dpage ) ;
2012-11-02 12:12:17 +04:00
}
2013-01-03 04:24:28 +04:00
if ( retval > 0 )
2012-11-02 12:12:17 +04:00
acl = f2fs_acl_from_disk ( value , retval ) ;
2013-01-03 04:24:28 +04:00
else if ( retval = = - ENODATA )
acl = NULL ;
else
acl = ERR_PTR ( retval ) ;
2012-11-02 12:12:17 +04:00
kfree ( value ) ;
2013-01-03 04:24:28 +04:00
2012-11-02 12:12:17 +04:00
if ( ! IS_ERR ( acl ) )
set_cached_acl ( inode , type , acl ) ;
return acl ;
}
2014-10-14 06:42:53 +04:00
struct posix_acl * f2fs_get_acl ( struct inode * inode , int type )
{
return __f2fs_get_acl ( inode , type , NULL ) ;
}
2013-12-20 17:16:45 +04:00
static int __f2fs_set_acl ( struct inode * inode , int type ,
2013-10-28 08:17:54 +04:00
struct posix_acl * acl , struct page * ipage )
2012-11-02 12:12:17 +04:00
{
struct f2fs_inode_info * fi = F2FS_I ( inode ) ;
int name_index ;
void * value = NULL ;
size_t size = 0 ;
int error ;
switch ( type ) {
case ACL_TYPE_ACCESS :
name_index = F2FS_XATTR_INDEX_POSIX_ACL_ACCESS ;
if ( acl ) {
error = posix_acl_equiv_mode ( acl , & inode - > i_mode ) ;
if ( error < 0 )
return error ;
set_acl_inode ( fi , inode - > i_mode ) ;
if ( error = = 0 )
acl = NULL ;
}
break ;
case ACL_TYPE_DEFAULT :
name_index = F2FS_XATTR_INDEX_POSIX_ACL_DEFAULT ;
if ( ! S_ISDIR ( inode - > i_mode ) )
return acl ? - EACCES : 0 ;
break ;
default :
return - EINVAL ;
}
if ( acl ) {
value = f2fs_acl_to_disk ( acl , & size ) ;
if ( IS_ERR ( value ) ) {
2014-10-20 13:45:52 +04:00
clear_inode_flag ( fi , FI_ACL_MODE ) ;
2012-11-02 12:12:17 +04:00
return ( int ) PTR_ERR ( value ) ;
}
}
2014-04-23 07:23:14 +04:00
error = f2fs_setxattr ( inode , name_index , " " , value , size , ipage , 0 ) ;
2012-11-02 12:12:17 +04:00
kfree ( value ) ;
if ( ! error )
set_cached_acl ( inode , type , acl ) ;
2014-10-20 13:45:52 +04:00
clear_inode_flag ( fi , FI_ACL_MODE ) ;
2012-11-02 12:12:17 +04:00
return error ;
}
2013-12-20 17:16:45 +04:00
int f2fs_set_acl ( struct inode * inode , struct posix_acl * acl , int type )
2012-11-02 12:12:17 +04:00
{
2013-12-20 17:16:45 +04:00
return __f2fs_set_acl ( inode , type , acl , NULL ) ;
2012-11-02 12:12:17 +04:00
}
2014-10-14 06:42:53 +04:00
/*
* Most part of f2fs_acl_clone , f2fs_acl_create_masq , f2fs_acl_create
* are copied from posix_acl . c
*/
static struct posix_acl * f2fs_acl_clone ( const struct posix_acl * acl ,
gfp_t flags )
{
struct posix_acl * clone = NULL ;
if ( acl ) {
int size = sizeof ( struct posix_acl ) + acl - > a_count *
sizeof ( struct posix_acl_entry ) ;
clone = kmemdup ( acl , size , flags ) ;
if ( clone )
atomic_set ( & clone - > a_refcount , 1 ) ;
}
return clone ;
}
static int f2fs_acl_create_masq ( struct posix_acl * acl , umode_t * mode_p )
{
struct posix_acl_entry * pa , * pe ;
struct posix_acl_entry * group_obj = NULL , * mask_obj = NULL ;
umode_t mode = * mode_p ;
int not_equiv = 0 ;
/* assert(atomic_read(acl->a_refcount) == 1); */
FOREACH_ACL_ENTRY ( pa , acl , pe ) {
switch ( pa - > e_tag ) {
case ACL_USER_OBJ :
pa - > e_perm & = ( mode > > 6 ) | ~ S_IRWXO ;
mode & = ( pa - > e_perm < < 6 ) | ~ S_IRWXU ;
break ;
case ACL_USER :
case ACL_GROUP :
not_equiv = 1 ;
break ;
case ACL_GROUP_OBJ :
group_obj = pa ;
break ;
case ACL_OTHER :
pa - > e_perm & = mode | ~ S_IRWXO ;
mode & = pa - > e_perm | ~ S_IRWXO ;
break ;
case ACL_MASK :
mask_obj = pa ;
not_equiv = 1 ;
break ;
default :
return - EIO ;
}
}
if ( mask_obj ) {
mask_obj - > e_perm & = ( mode > > 3 ) | ~ S_IRWXO ;
mode & = ( mask_obj - > e_perm < < 3 ) | ~ S_IRWXG ;
} else {
if ( ! group_obj )
return - EIO ;
group_obj - > e_perm & = ( mode > > 3 ) | ~ S_IRWXO ;
mode & = ( group_obj - > e_perm < < 3 ) | ~ S_IRWXG ;
}
* mode_p = ( * mode_p & ~ S_IRWXUGO ) | mode ;
return not_equiv ;
}
static int f2fs_acl_create ( struct inode * dir , umode_t * mode ,
struct posix_acl * * default_acl , struct posix_acl * * acl ,
struct page * dpage )
{
struct posix_acl * p ;
int ret ;
if ( S_ISLNK ( * mode ) | | ! IS_POSIXACL ( dir ) )
goto no_acl ;
p = __f2fs_get_acl ( dir , ACL_TYPE_DEFAULT , dpage ) ;
if ( IS_ERR ( p ) ) {
if ( p = = ERR_PTR ( - EOPNOTSUPP ) )
goto apply_umask ;
return PTR_ERR ( p ) ;
}
if ( ! p )
goto apply_umask ;
* acl = f2fs_acl_clone ( p , GFP_NOFS ) ;
if ( ! * acl )
return - ENOMEM ;
ret = f2fs_acl_create_masq ( * acl , mode ) ;
if ( ret < 0 ) {
posix_acl_release ( * acl ) ;
return - ENOMEM ;
}
if ( ret = = 0 ) {
posix_acl_release ( * acl ) ;
* acl = NULL ;
}
if ( ! S_ISDIR ( * mode ) ) {
posix_acl_release ( p ) ;
* default_acl = NULL ;
} else {
* default_acl = p ;
}
return 0 ;
apply_umask :
* mode & = ~ current_umask ( ) ;
no_acl :
* default_acl = NULL ;
* acl = NULL ;
return 0 ;
}
int f2fs_init_acl ( struct inode * inode , struct inode * dir , struct page * ipage ,
struct page * dpage )
2012-11-02 12:12:17 +04:00
{
2014-10-14 06:42:53 +04:00
struct posix_acl * default_acl = NULL , * acl = NULL ;
2013-12-20 17:16:45 +04:00
int error = 0 ;
2012-11-02 12:12:17 +04:00
2014-10-14 06:42:53 +04:00
error = f2fs_acl_create ( dir , & inode - > i_mode , & default_acl , & acl , dpage ) ;
2012-11-02 12:12:17 +04:00
if ( error )
return error ;
2013-10-28 08:12:09 +04:00
2013-12-20 17:16:45 +04:00
if ( default_acl ) {
error = __f2fs_set_acl ( inode , ACL_TYPE_DEFAULT , default_acl ,
ipage ) ;
posix_acl_release ( default_acl ) ;
}
if ( acl ) {
2015-01-24 12:06:25 +03:00
if ( ! error )
2013-12-20 17:16:45 +04:00
error = __f2fs_set_acl ( inode , ACL_TYPE_ACCESS , acl ,
ipage ) ;
posix_acl_release ( acl ) ;
2012-11-02 12:12:17 +04:00
}
return error ;
}