2006-05-13 10:18:27 +04:00
/*
* JFFS2 - - Journalling Flash File System , Version 2.
2006-05-13 10:09:47 +04:00
*
2007-04-25 17:16:47 +04:00
* Copyright © 2006 NEC Corporation
2006-05-13 10:09:47 +04:00
*
2006-05-13 10:18:27 +04:00
* Created by KaiGai Kohei < kaigai @ ak . jp . nec . com >
*
* For licensing information , see the file ' LICENCE ' in this directory .
*
*/
2007-04-25 17:16:47 +04:00
2006-05-13 10:09:47 +04:00
# include <linux/kernel.h>
# include <linux/slab.h>
# include <linux/fs.h>
2006-10-18 21:55:46 +04:00
# include <linux/sched.h>
2006-05-13 10:09:47 +04:00
# include <linux/time.h>
# include <linux/crc32.h>
# include <linux/jffs2.h>
# include <linux/xattr.h>
# include <linux/posix_acl_xattr.h>
# include <linux/mtd/mtd.h>
# include "nodelist.h"
static size_t jffs2_acl_size ( int count )
{
if ( count < = 4 ) {
2006-05-13 10:13:27 +04:00
return sizeof ( struct jffs2_acl_header )
+ count * sizeof ( struct jffs2_acl_entry_short ) ;
2006-05-13 10:09:47 +04:00
} else {
2006-05-13 10:13:27 +04:00
return sizeof ( struct jffs2_acl_header )
+ 4 * sizeof ( struct jffs2_acl_entry_short )
+ ( count - 4 ) * sizeof ( struct jffs2_acl_entry ) ;
2006-05-13 10:09:47 +04:00
}
}
static int jffs2_acl_count ( size_t size )
{
size_t s ;
2006-05-13 10:13:27 +04:00
size - = sizeof ( struct jffs2_acl_header ) ;
s = size - 4 * sizeof ( struct jffs2_acl_entry_short ) ;
2006-05-13 10:09:47 +04:00
if ( s < 0 ) {
2006-05-13 10:13:27 +04:00
if ( size % sizeof ( struct jffs2_acl_entry_short ) )
2006-05-13 10:09:47 +04:00
return - 1 ;
2006-05-13 10:13:27 +04:00
return size / sizeof ( struct jffs2_acl_entry_short ) ;
2006-05-13 10:09:47 +04:00
} else {
2006-05-13 10:13:27 +04:00
if ( s % sizeof ( struct jffs2_acl_entry ) )
2006-05-13 10:09:47 +04:00
return - 1 ;
2006-05-13 10:13:27 +04:00
return s / sizeof ( struct jffs2_acl_entry ) + 4 ;
2006-05-13 10:09:47 +04:00
}
}
2006-05-13 10:20:24 +04:00
static struct posix_acl * jffs2_acl_from_medium ( void * value , size_t size )
2006-05-13 10:09:47 +04:00
{
2006-05-13 10:20:24 +04:00
void * end = value + size ;
struct jffs2_acl_header * header = value ;
struct jffs2_acl_entry * entry ;
2006-05-13 10:09:47 +04:00
struct posix_acl * acl ;
uint32_t ver ;
int i , count ;
if ( ! value )
return NULL ;
2006-05-13 10:13:27 +04:00
if ( size < sizeof ( struct jffs2_acl_header ) )
2006-05-13 10:09:47 +04:00
return ERR_PTR ( - EINVAL ) ;
2006-05-13 10:20:24 +04:00
ver = je32_to_cpu ( header - > a_version ) ;
2006-05-13 10:09:47 +04:00
if ( ver ! = JFFS2_ACL_VERSION ) {
JFFS2_WARNING ( " Invalid ACL version. (=%u) \n " , ver ) ;
return ERR_PTR ( - EINVAL ) ;
}
2006-05-13 10:20:24 +04:00
value + = sizeof ( struct jffs2_acl_header ) ;
2006-05-13 10:09:47 +04:00
count = jffs2_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 ( i = 0 ; i < count ; i + + ) {
2006-05-13 10:20:24 +04:00
entry = value ;
if ( value + sizeof ( struct jffs2_acl_entry_short ) > end )
2006-05-13 10:09:47 +04:00
goto fail ;
acl - > a_entries [ i ] . e_tag = je16_to_cpu ( entry - > e_tag ) ;
acl - > a_entries [ i ] . e_perm = je16_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 :
2006-05-13 10:20:24 +04:00
value + = sizeof ( struct jffs2_acl_entry_short ) ;
2006-05-13 10:09:47 +04:00
acl - > a_entries [ i ] . e_id = ACL_UNDEFINED_ID ;
break ;
case ACL_USER :
case ACL_GROUP :
2006-05-13 10:20:24 +04:00
value + = sizeof ( struct jffs2_acl_entry ) ;
if ( value > end )
2006-05-13 10:09:47 +04:00
goto fail ;
acl - > a_entries [ i ] . e_id = je32_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 ) ;
}
static void * jffs2_acl_to_medium ( const struct posix_acl * acl , size_t * size )
{
2006-05-13 10:20:24 +04:00
struct jffs2_acl_header * header ;
struct jffs2_acl_entry * entry ;
void * e ;
2006-05-13 10:09:47 +04:00
size_t i ;
* size = jffs2_acl_size ( acl - > a_count ) ;
2006-05-13 10:20:24 +04:00
header = kmalloc ( sizeof ( * header ) + acl - > a_count * sizeof ( * entry ) , GFP_KERNEL ) ;
if ( ! header )
2006-05-13 10:09:47 +04:00
return ERR_PTR ( - ENOMEM ) ;
2006-05-13 10:20:24 +04:00
header - > a_version = cpu_to_je32 ( JFFS2_ACL_VERSION ) ;
e = header + 1 ;
2006-05-13 10:09:47 +04:00
for ( i = 0 ; i < acl - > a_count ; i + + ) {
2006-05-13 10:20:24 +04:00
entry = e ;
2006-05-13 10:09:47 +04:00
entry - > e_tag = cpu_to_je16 ( acl - > a_entries [ i ] . e_tag ) ;
entry - > e_perm = cpu_to_je16 ( acl - > a_entries [ i ] . e_perm ) ;
switch ( acl - > a_entries [ i ] . e_tag ) {
case ACL_USER :
case ACL_GROUP :
entry - > e_id = cpu_to_je32 ( acl - > a_entries [ i ] . e_id ) ;
2006-05-13 10:13:27 +04:00
e + = sizeof ( struct jffs2_acl_entry ) ;
2006-05-13 10:09:47 +04:00
break ;
case ACL_USER_OBJ :
case ACL_GROUP_OBJ :
case ACL_MASK :
case ACL_OTHER :
2006-05-13 10:13:27 +04:00
e + = sizeof ( struct jffs2_acl_entry_short ) ;
2006-05-13 10:09:47 +04:00
break ;
default :
goto fail ;
}
}
2006-05-13 10:20:24 +04:00
return header ;
2006-05-13 10:09:47 +04:00
fail :
2006-05-13 10:20:24 +04:00
kfree ( header ) ;
2006-05-13 10:09:47 +04:00
return ERR_PTR ( - EINVAL ) ;
}
static struct posix_acl * jffs2_iget_acl ( struct inode * inode , struct posix_acl * * i_acl )
{
struct posix_acl * acl = JFFS2_ACL_NOT_CACHED ;
spin_lock ( & inode - > i_lock ) ;
if ( * i_acl ! = JFFS2_ACL_NOT_CACHED )
acl = posix_acl_dup ( * i_acl ) ;
spin_unlock ( & inode - > i_lock ) ;
return acl ;
}
static void jffs2_iset_acl ( struct inode * inode , struct posix_acl * * i_acl , struct posix_acl * acl )
{
spin_lock ( & inode - > i_lock ) ;
if ( * i_acl ! = JFFS2_ACL_NOT_CACHED )
posix_acl_release ( * i_acl ) ;
* i_acl = posix_acl_dup ( acl ) ;
spin_unlock ( & inode - > i_lock ) ;
}
2007-11-06 11:36:49 +03:00
static struct posix_acl * jffs2_get_acl ( struct inode * inode , int type )
2006-05-13 10:09:47 +04:00
{
struct jffs2_inode_info * f = JFFS2_INODE_INFO ( inode ) ;
struct posix_acl * acl ;
char * value = NULL ;
int rc , xprefix ;
switch ( type ) {
case ACL_TYPE_ACCESS :
acl = jffs2_iget_acl ( inode , & f - > i_acl_access ) ;
if ( acl ! = JFFS2_ACL_NOT_CACHED )
return acl ;
xprefix = JFFS2_XPREFIX_ACL_ACCESS ;
break ;
case ACL_TYPE_DEFAULT :
acl = jffs2_iget_acl ( inode , & f - > i_acl_default ) ;
if ( acl ! = JFFS2_ACL_NOT_CACHED )
return acl ;
xprefix = JFFS2_XPREFIX_ACL_DEFAULT ;
break ;
default :
return ERR_PTR ( - EINVAL ) ;
}
rc = do_jffs2_getxattr ( inode , xprefix , " " , NULL , 0 ) ;
if ( rc > 0 ) {
value = kmalloc ( rc , GFP_KERNEL ) ;
if ( ! value )
return ERR_PTR ( - ENOMEM ) ;
rc = do_jffs2_getxattr ( inode , xprefix , " " , value , rc ) ;
}
if ( rc > 0 ) {
acl = jffs2_acl_from_medium ( value , rc ) ;
} else if ( rc = = - ENODATA | | rc = = - ENOSYS ) {
acl = NULL ;
} else {
acl = ERR_PTR ( rc ) ;
}
if ( value )
kfree ( value ) ;
if ( ! IS_ERR ( acl ) ) {
switch ( type ) {
case ACL_TYPE_ACCESS :
jffs2_iset_acl ( inode , & f - > i_acl_access , acl ) ;
break ;
case ACL_TYPE_DEFAULT :
jffs2_iset_acl ( inode , & f - > i_acl_default , acl ) ;
break ;
}
}
return acl ;
}
2007-09-14 10:16:35 +04:00
static int __jffs2_set_acl ( struct inode * inode , int xprefix , struct posix_acl * acl )
{
char * value = NULL ;
size_t size = 0 ;
int rc ;
if ( acl ) {
value = jffs2_acl_to_medium ( acl , & size ) ;
if ( IS_ERR ( value ) )
return PTR_ERR ( value ) ;
}
rc = do_jffs2_setxattr ( inode , xprefix , " " , value , size , 0 ) ;
if ( ! value & & rc = = - ENODATA )
rc = 0 ;
kfree ( value ) ;
return rc ;
}
2006-05-13 10:09:47 +04:00
static int jffs2_set_acl ( struct inode * inode , int type , struct posix_acl * acl )
{
struct jffs2_inode_info * f = JFFS2_INODE_INFO ( inode ) ;
int rc , xprefix ;
if ( S_ISLNK ( inode - > i_mode ) )
return - EOPNOTSUPP ;
switch ( type ) {
case ACL_TYPE_ACCESS :
xprefix = JFFS2_XPREFIX_ACL_ACCESS ;
if ( acl ) {
mode_t mode = inode - > i_mode ;
rc = posix_acl_equiv_mode ( acl , & mode ) ;
if ( rc < 0 )
return rc ;
if ( inode - > i_mode ! = mode ) {
2007-08-22 15:39:19 +04:00
struct iattr attr ;
attr . ia_valid = ATTR_MODE ;
attr . ia_mode = mode ;
rc = jffs2_do_setattr ( inode , & attr ) ;
if ( rc < 0 )
return rc ;
2006-05-13 10:09:47 +04:00
}
if ( rc = = 0 )
acl = NULL ;
}
break ;
case ACL_TYPE_DEFAULT :
xprefix = JFFS2_XPREFIX_ACL_DEFAULT ;
if ( ! S_ISDIR ( inode - > i_mode ) )
return acl ? - EACCES : 0 ;
break ;
default :
return - EINVAL ;
}
2007-09-14 10:16:35 +04:00
rc = __jffs2_set_acl ( inode , xprefix , acl ) ;
2006-05-13 10:09:47 +04:00
if ( ! rc ) {
switch ( type ) {
case ACL_TYPE_ACCESS :
jffs2_iset_acl ( inode , & f - > i_acl_access , acl ) ;
break ;
case ACL_TYPE_DEFAULT :
jffs2_iset_acl ( inode , & f - > i_acl_default , acl ) ;
break ;
}
}
return rc ;
}
static int jffs2_check_acl ( struct inode * inode , int mask )
{
struct posix_acl * acl ;
int rc ;
acl = jffs2_get_acl ( inode , ACL_TYPE_ACCESS ) ;
if ( IS_ERR ( acl ) )
return PTR_ERR ( acl ) ;
if ( acl ) {
rc = posix_acl_permission ( inode , acl , mask ) ;
posix_acl_release ( acl ) ;
return rc ;
}
return - EAGAIN ;
}
2008-07-16 05:03:57 +04:00
int jffs2_permission ( struct inode * inode , int mask )
2006-05-13 10:09:47 +04:00
{
return generic_permission ( inode , mask , jffs2_check_acl ) ;
}
2007-09-14 10:16:35 +04:00
int jffs2_init_acl_pre ( struct inode * dir_i , struct inode * inode , int * i_mode )
2006-05-13 10:09:47 +04:00
{
struct jffs2_inode_info * f = JFFS2_INODE_INFO ( inode ) ;
2007-09-14 10:16:35 +04:00
struct posix_acl * acl , * clone ;
int rc ;
2006-05-13 10:09:47 +04:00
2007-09-14 10:16:35 +04:00
f - > i_acl_default = NULL ;
f - > i_acl_access = NULL ;
if ( S_ISLNK ( * i_mode ) )
return 0 ; /* Symlink always has no-ACL */
acl = jffs2_get_acl ( dir_i , ACL_TYPE_DEFAULT ) ;
if ( IS_ERR ( acl ) )
return PTR_ERR ( acl ) ;
if ( ! acl ) {
* i_mode & = ~ current - > fs - > umask ;
} else {
if ( S_ISDIR ( * i_mode ) )
jffs2_iset_acl ( inode , & f - > i_acl_default , acl ) ;
2007-08-22 15:39:19 +04:00
2006-05-13 10:09:47 +04:00
clone = posix_acl_clone ( acl , GFP_KERNEL ) ;
if ( ! clone )
2007-09-14 10:16:35 +04:00
return - ENOMEM ;
rc = posix_acl_create_masq ( clone , ( mode_t * ) i_mode ) ;
2008-01-06 19:50:34 +03:00
if ( rc < 0 ) {
posix_acl_release ( clone ) ;
2007-09-14 10:16:35 +04:00
return rc ;
2008-01-06 19:50:34 +03:00
}
2007-09-14 10:16:35 +04:00
if ( rc > 0 )
jffs2_iset_acl ( inode , & f - > i_acl_access , clone ) ;
2006-05-13 10:09:47 +04:00
posix_acl_release ( clone ) ;
}
2007-09-14 10:16:35 +04:00
return 0 ;
}
int jffs2_init_acl_post ( struct inode * inode )
{
struct jffs2_inode_info * f = JFFS2_INODE_INFO ( inode ) ;
int rc ;
if ( f - > i_acl_default ) {
rc = __jffs2_set_acl ( inode , JFFS2_XPREFIX_ACL_DEFAULT , f - > i_acl_default ) ;
if ( rc )
return rc ;
}
if ( f - > i_acl_access ) {
rc = __jffs2_set_acl ( inode , JFFS2_XPREFIX_ACL_ACCESS , f - > i_acl_access ) ;
if ( rc )
return rc ;
}
2007-10-27 18:36:44 +04:00
return 0 ;
2006-05-13 10:09:47 +04:00
}
2006-07-02 18:13:46 +04:00
void jffs2_clear_acl ( struct jffs2_inode_info * f )
2006-05-13 10:09:47 +04:00
{
if ( f - > i_acl_access & & f - > i_acl_access ! = JFFS2_ACL_NOT_CACHED ) {
posix_acl_release ( f - > i_acl_access ) ;
f - > i_acl_access = JFFS2_ACL_NOT_CACHED ;
}
if ( f - > i_acl_default & & f - > i_acl_default ! = JFFS2_ACL_NOT_CACHED ) {
posix_acl_release ( f - > i_acl_default ) ;
f - > i_acl_default = JFFS2_ACL_NOT_CACHED ;
}
}
int jffs2_acl_chmod ( struct inode * inode )
{
struct posix_acl * acl , * clone ;
int rc ;
if ( S_ISLNK ( inode - > i_mode ) )
return - EOPNOTSUPP ;
acl = jffs2_get_acl ( inode , ACL_TYPE_ACCESS ) ;
if ( IS_ERR ( acl ) | | ! acl )
return PTR_ERR ( acl ) ;
clone = posix_acl_clone ( acl , GFP_KERNEL ) ;
posix_acl_release ( acl ) ;
if ( ! clone )
return - ENOMEM ;
rc = posix_acl_chmod_masq ( clone , inode - > i_mode ) ;
if ( ! rc )
rc = jffs2_set_acl ( inode , ACL_TYPE_ACCESS , clone ) ;
posix_acl_release ( clone ) ;
return rc ;
}
static size_t jffs2_acl_access_listxattr ( struct inode * inode , char * list , size_t list_size ,
const char * name , size_t name_len )
{
const int retlen = sizeof ( POSIX_ACL_XATTR_ACCESS ) ;
if ( list & & retlen < = list_size )
strcpy ( list , POSIX_ACL_XATTR_ACCESS ) ;
return retlen ;
}
static size_t jffs2_acl_default_listxattr ( struct inode * inode , char * list , size_t list_size ,
const char * name , size_t name_len )
{
const int retlen = sizeof ( POSIX_ACL_XATTR_DEFAULT ) ;
if ( list & & retlen < = list_size )
strcpy ( list , POSIX_ACL_XATTR_DEFAULT ) ;
return retlen ;
}
static int jffs2_acl_getxattr ( struct inode * inode , int type , void * buffer , size_t size )
{
struct posix_acl * acl ;
int rc ;
acl = jffs2_get_acl ( inode , type ) ;
if ( IS_ERR ( acl ) )
return PTR_ERR ( acl ) ;
if ( ! acl )
return - ENODATA ;
rc = posix_acl_to_xattr ( acl , buffer , size ) ;
posix_acl_release ( acl ) ;
return rc ;
}
static int jffs2_acl_access_getxattr ( struct inode * inode , const char * name , void * buffer , size_t size )
{
if ( name [ 0 ] ! = ' \0 ' )
return - EINVAL ;
return jffs2_acl_getxattr ( inode , ACL_TYPE_ACCESS , buffer , size ) ;
}
static int jffs2_acl_default_getxattr ( struct inode * inode , const char * name , void * buffer , size_t size )
{
if ( name [ 0 ] ! = ' \0 ' )
return - EINVAL ;
return jffs2_acl_getxattr ( inode , ACL_TYPE_DEFAULT , buffer , size ) ;
}
static int jffs2_acl_setxattr ( struct inode * inode , int type , const void * value , size_t size )
{
struct posix_acl * acl ;
int rc ;
2007-07-17 13:30:08 +04:00
if ( ! is_owner_or_cap ( inode ) )
2006-05-13 10:09:47 +04:00
return - EPERM ;
if ( value ) {
acl = posix_acl_from_xattr ( value , size ) ;
if ( IS_ERR ( acl ) )
return PTR_ERR ( acl ) ;
if ( acl ) {
rc = posix_acl_valid ( acl ) ;
if ( rc )
goto out ;
}
} else {
acl = NULL ;
}
rc = jffs2_set_acl ( inode , type , acl ) ;
out :
posix_acl_release ( acl ) ;
return rc ;
}
static int jffs2_acl_access_setxattr ( struct inode * inode , const char * name ,
const void * buffer , size_t size , int flags )
{
if ( name [ 0 ] ! = ' \0 ' )
return - EINVAL ;
return jffs2_acl_setxattr ( inode , ACL_TYPE_ACCESS , buffer , size ) ;
}
static int jffs2_acl_default_setxattr ( struct inode * inode , const char * name ,
const void * buffer , size_t size , int flags )
{
if ( name [ 0 ] ! = ' \0 ' )
return - EINVAL ;
return jffs2_acl_setxattr ( inode , ACL_TYPE_DEFAULT , buffer , size ) ;
}
struct xattr_handler jffs2_acl_access_xattr_handler = {
. prefix = POSIX_ACL_XATTR_ACCESS ,
. list = jffs2_acl_access_listxattr ,
. get = jffs2_acl_access_getxattr ,
. set = jffs2_acl_access_setxattr ,
} ;
struct xattr_handler jffs2_acl_default_xattr_handler = {
. prefix = POSIX_ACL_XATTR_DEFAULT ,
. list = jffs2_acl_default_listxattr ,
. get = jffs2_acl_default_getxattr ,
. set = jffs2_acl_default_setxattr ,
} ;