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
2012-02-16 03:56:45 +04:00
# define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
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 ) ;
2009-03-04 23:01:41 +03:00
if ( size < 4 * sizeof ( struct jffs2_acl_entry_short ) ) {
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 {
2009-03-04 23:01:41 +03:00
s = size - 4 * sizeof ( struct jffs2_acl_entry_short ) ;
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
break ;
case ACL_USER :
2012-02-08 04:28:39 +04:00
value + = sizeof ( struct jffs2_acl_entry ) ;
if ( value > end )
goto fail ;
acl - > a_entries [ i ] . e_uid =
make_kuid ( & init_user_ns ,
je32_to_cpu ( entry - > e_id ) ) ;
break ;
2006-05-13 10:09:47 +04:00
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 ;
2012-02-08 04:28:39 +04:00
acl - > a_entries [ i ] . e_gid =
make_kgid ( & init_user_ns ,
je32_to_cpu ( entry - > e_id ) ) ;
2006-05-13 10:09:47 +04:00
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 + + ) {
2012-02-08 04:28:39 +04:00
const struct posix_acl_entry * acl_e = & acl - > a_entries [ i ] ;
2006-05-13 10:20:24 +04:00
entry = e ;
2012-02-08 04:28:39 +04:00
entry - > e_tag = cpu_to_je16 ( acl_e - > e_tag ) ;
entry - > e_perm = cpu_to_je16 ( acl_e - > e_perm ) ;
switch ( acl_e - > e_tag ) {
2006-05-13 10:09:47 +04:00
case ACL_USER :
2012-02-08 04:28:39 +04:00
entry - > e_id = cpu_to_je32 (
from_kuid ( & init_user_ns , acl_e - > e_uid ) ) ;
e + = sizeof ( struct jffs2_acl_entry ) ;
break ;
2006-05-13 10:09:47 +04:00
case ACL_GROUP :
2012-02-08 04:28:39 +04:00
entry - > e_id = cpu_to_je32 (
from_kgid ( & init_user_ns , acl_e - > e_gid ) ) ;
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 ) ;
}
2011-07-23 19:37:31 +04:00
struct posix_acl * jffs2_get_acl ( struct inode * inode , int type )
2006-05-13 10:09:47 +04:00
{
struct posix_acl * acl ;
char * value = NULL ;
int rc , xprefix ;
switch ( type ) {
case ACL_TYPE_ACCESS :
xprefix = JFFS2_XPREFIX_ACL_ACCESS ;
break ;
case ACL_TYPE_DEFAULT :
xprefix = JFFS2_XPREFIX_ACL_DEFAULT ;
break ;
default :
2009-06-09 20:11:54 +04:00
BUG ( ) ;
2006-05-13 10:09:47 +04:00
}
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 ) ;
}
2014-06-16 21:58:07 +04:00
kfree ( value ) ;
2006-05-13 10:09:47 +04:00
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 ;
}
2013-12-20 17:16:47 +04:00
int jffs2_set_acl ( struct inode * inode , struct posix_acl * acl , int type )
2006-05-13 10:09:47 +04:00
{
int rc , xprefix ;
switch ( type ) {
case ACL_TYPE_ACCESS :
xprefix = JFFS2_XPREFIX_ACL_ACCESS ;
if ( acl ) {
2011-07-24 02:56:36 +04:00
umode_t mode = inode - > i_mode ;
2006-05-13 10:09:47 +04:00
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 ;
2010-06-04 19:07:55 +04:00
attr . ia_valid = ATTR_MODE | ATTR_CTIME ;
2007-08-22 15:39:19 +04:00
attr . ia_mode = mode ;
2010-06-04 19:07:55 +04:00
attr . ia_ctime = CURRENT_TIME_SEC ;
2007-08-22 15:39:19 +04:00
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 ) ;
2009-06-09 20:11:54 +04:00
if ( ! rc )
set_cached_acl ( inode , type , acl ) ;
2006-05-13 10:09:47 +04:00
return rc ;
}
2011-07-24 02:37:50 +04:00
int jffs2_init_acl_pre ( struct inode * dir_i , struct inode * inode , umode_t * i_mode )
2006-05-13 10:09:47 +04:00
{
2013-12-20 17:16:47 +04:00
struct posix_acl * default_acl , * acl ;
2007-09-14 10:16:35 +04:00
int rc ;
2006-05-13 10:09:47 +04:00
2009-06-25 00:58:48 +04:00
cache_no_acl ( inode ) ;
2007-09-14 10:16:35 +04:00
2013-12-20 17:16:47 +04:00
rc = posix_acl_create ( dir_i , i_mode , & default_acl , & acl ) ;
if ( rc )
return rc ;
2007-09-14 10:16:35 +04:00
2013-12-20 17:16:47 +04:00
if ( default_acl ) {
set_cached_acl ( inode , ACL_TYPE_DEFAULT , default_acl ) ;
posix_acl_release ( default_acl ) ;
}
if ( acl ) {
set_cached_acl ( inode , ACL_TYPE_ACCESS , acl ) ;
2011-07-23 11:10:32 +04:00
posix_acl_release ( acl ) ;
2006-05-13 10:09:47 +04:00
}
2007-09-14 10:16:35 +04:00
return 0 ;
}
int jffs2_init_acl_post ( struct inode * inode )
{
int rc ;
2009-06-09 03:55:12 +04:00
if ( inode - > i_default_acl ) {
rc = __jffs2_set_acl ( inode , JFFS2_XPREFIX_ACL_DEFAULT , inode - > i_default_acl ) ;
2007-09-14 10:16:35 +04:00
if ( rc )
return rc ;
}
2009-06-09 03:55:12 +04:00
if ( inode - > i_acl ) {
rc = __jffs2_set_acl ( inode , JFFS2_XPREFIX_ACL_ACCESS , inode - > i_acl ) ;
2007-09-14 10:16:35 +04:00
if ( rc )
return rc ;
}
2007-10-27 18:36:44 +04:00
return 0 ;
2006-05-13 10:09:47 +04:00
}