2005-04-17 02:20:36 +04:00
/*
2013-12-20 17:16:37 +04:00
* Copyright ( C ) 2002 , 2003 by Andreas Gruenbacher < a . gruenbacher @ computer . org >
2005-04-17 02:20:36 +04:00
*
2013-12-20 17:16:37 +04:00
* Fixes from William Schumacher incorporated on 15 March 2001.
* ( Reported by Charles Bertsch , < CBertsch @ microtest . com > ) .
2005-04-17 02:20:36 +04:00
*/
/*
* This file contains generic functions for manipulating
* POSIX 1003.1 e draft standard 17 ACLs .
*/
# include <linux/kernel.h>
# include <linux/slab.h>
2011-07-27 03:09:06 +04:00
# include <linux/atomic.h>
2005-04-17 02:20:36 +04:00
# include <linux/fs.h>
# include <linux/sched.h>
# include <linux/posix_acl.h>
2013-12-20 17:16:37 +04:00
# include <linux/posix_acl_xattr.h>
2013-12-20 17:16:40 +04:00
# include <linux/xattr.h>
2011-11-17 08:57:37 +04:00
# include <linux/export.h>
2013-12-20 17:16:37 +04:00
# include <linux/user_namespace.h>
2005-04-17 02:20:36 +04:00
2016-03-24 16:38:38 +03:00
static struct posix_acl * * acl_by_type ( struct inode * inode , int type )
2014-01-22 03:48:42 +04:00
{
switch ( type ) {
case ACL_TYPE_ACCESS :
return & inode - > i_acl ;
case ACL_TYPE_DEFAULT :
return & inode - > i_default_acl ;
default :
BUG ( ) ;
}
}
struct posix_acl * get_cached_acl ( struct inode * inode , int type )
{
struct posix_acl * * p = acl_by_type ( inode , type ) ;
2016-03-24 16:38:37 +03:00
struct posix_acl * acl ;
for ( ; ; ) {
rcu_read_lock ( ) ;
acl = rcu_dereference ( * p ) ;
if ( ! acl | | is_uncached_acl ( acl ) | |
atomic_inc_not_zero ( & acl - > a_refcount ) )
break ;
rcu_read_unlock ( ) ;
cpu_relax ( ) ;
2014-01-22 03:48:42 +04:00
}
2016-03-24 16:38:37 +03:00
rcu_read_unlock ( ) ;
2014-01-22 03:48:42 +04:00
return acl ;
}
EXPORT_SYMBOL ( get_cached_acl ) ;
struct posix_acl * get_cached_acl_rcu ( struct inode * inode , int type )
{
return rcu_dereference ( * acl_by_type ( inode , type ) ) ;
}
EXPORT_SYMBOL ( get_cached_acl_rcu ) ;
void set_cached_acl ( struct inode * inode , int type , struct posix_acl * acl )
{
struct posix_acl * * p = acl_by_type ( inode , type ) ;
struct posix_acl * old ;
2016-03-24 16:38:37 +03:00
old = xchg ( p , posix_acl_dup ( acl ) ) ;
if ( ! is_uncached_acl ( old ) )
2014-01-22 03:48:42 +04:00
posix_acl_release ( old ) ;
}
EXPORT_SYMBOL ( set_cached_acl ) ;
2016-03-24 16:38:37 +03:00
static void __forget_cached_acl ( struct posix_acl * * p )
2014-01-22 03:48:42 +04:00
{
struct posix_acl * old ;
2016-03-24 16:38:37 +03:00
old = xchg ( p , ACL_NOT_CACHED ) ;
if ( ! is_uncached_acl ( old ) )
2014-01-22 03:48:42 +04:00
posix_acl_release ( old ) ;
}
2016-03-24 16:38:37 +03:00
void forget_cached_acl ( struct inode * inode , int type )
{
__forget_cached_acl ( acl_by_type ( inode , type ) ) ;
}
2014-01-22 03:48:42 +04:00
EXPORT_SYMBOL ( forget_cached_acl ) ;
void forget_all_cached_acls ( struct inode * inode )
{
2016-03-24 16:38:37 +03:00
__forget_cached_acl ( & inode - > i_acl ) ;
__forget_cached_acl ( & inode - > i_default_acl ) ;
2014-01-22 03:48:42 +04:00
}
EXPORT_SYMBOL ( forget_all_cached_acls ) ;
2005-04-17 02:20:36 +04:00
2013-12-20 17:16:38 +04:00
struct posix_acl * get_acl ( struct inode * inode , int type )
{
2016-03-24 16:38:37 +03:00
void * sentinel ;
struct posix_acl * * p ;
2013-12-20 17:16:38 +04:00
struct posix_acl * acl ;
2016-03-24 16:38:37 +03:00
/*
* The sentinel is used to detect when another operation like
* set_cached_acl ( ) or forget_cached_acl ( ) races with get_acl ( ) .
* It is guaranteed that is_uncached_acl ( sentinel ) is true .
*/
2013-12-20 17:16:38 +04:00
acl = get_cached_acl ( inode , type ) ;
2016-03-24 16:38:37 +03:00
if ( ! is_uncached_acl ( acl ) )
2013-12-20 17:16:38 +04:00
return acl ;
if ( ! IS_POSIXACL ( inode ) )
return NULL ;
2016-03-24 16:38:37 +03:00
sentinel = uncached_acl_sentinel ( current ) ;
p = acl_by_type ( inode , type ) ;
/*
* If the ACL isn ' t being read yet , set our sentinel . Otherwise , the
* current value of the ACL will not be ACL_NOT_CACHED and so our own
* sentinel will not be set ; another task will update the cache . We
* could wait for that other task to complete its job , but it ' s easier
* to just call - > get_acl to fetch the ACL ourself . ( This is going to
* be an unlikely race . )
*/
if ( cmpxchg ( p , ACL_NOT_CACHED , sentinel ) ! = ACL_NOT_CACHED )
/* fall through */ ;
2013-12-20 17:16:38 +04:00
/*
2016-03-24 16:38:37 +03:00
* Normally , the ACL returned by - > get_acl will be cached .
* A filesystem can prevent that by calling
* forget_cached_acl ( inode , type ) in - > get_acl .
2013-12-20 17:16:38 +04:00
*
* If the filesystem doesn ' t have a get_acl ( ) function at all , we ' ll
* just create the negative cache entry .
*/
if ( ! inode - > i_op - > get_acl ) {
set_cached_acl ( inode , type , NULL ) ;
return NULL ;
}
2016-03-24 16:38:37 +03:00
acl = inode - > i_op - > get_acl ( inode , type ) ;
if ( IS_ERR ( acl ) ) {
/*
* Remove our sentinel so that we don ' t block future attempts
* to cache the ACL .
*/
cmpxchg ( p , sentinel , ACL_NOT_CACHED ) ;
return acl ;
}
/*
* Cache the result , but only if our sentinel is still in place .
*/
posix_acl_dup ( acl ) ;
if ( unlikely ( cmpxchg ( p , sentinel , acl ) ! = sentinel ) )
posix_acl_release ( acl ) ;
return acl ;
2013-12-20 17:16:38 +04:00
}
EXPORT_SYMBOL ( get_acl ) ;
2011-01-21 06:05:38 +03:00
/*
* Init a fresh posix_acl
*/
void
posix_acl_init ( struct posix_acl * acl , int count )
{
atomic_set ( & acl - > a_refcount , 1 ) ;
acl - > a_count = count ;
}
2014-01-22 03:48:42 +04:00
EXPORT_SYMBOL ( posix_acl_init ) ;
2011-01-21 06:05:38 +03:00
2005-04-17 02:20:36 +04:00
/*
* Allocate a new ACL with the specified number of entries .
*/
struct posix_acl *
2005-10-07 10:46:04 +04:00
posix_acl_alloc ( int count , gfp_t flags )
2005-04-17 02:20:36 +04:00
{
const size_t size = sizeof ( struct posix_acl ) +
count * sizeof ( struct posix_acl_entry ) ;
struct posix_acl * acl = kmalloc ( size , flags ) ;
2011-01-21 06:05:38 +03:00
if ( acl )
posix_acl_init ( acl , count ) ;
2005-04-17 02:20:36 +04:00
return acl ;
}
2014-01-22 03:48:42 +04:00
EXPORT_SYMBOL ( posix_acl_alloc ) ;
2005-04-17 02:20:36 +04:00
/*
* Clone an ACL .
*/
2011-07-23 11:27:37 +04:00
static struct posix_acl *
2005-10-07 10:46:04 +04:00
posix_acl_clone ( const struct posix_acl * acl , gfp_t flags )
2005-04-17 02:20:36 +04:00
{
struct posix_acl * clone = NULL ;
if ( acl ) {
int size = sizeof ( struct posix_acl ) + acl - > a_count *
sizeof ( struct posix_acl_entry ) ;
2006-10-01 10:27:21 +04:00
clone = kmemdup ( acl , size , flags ) ;
if ( clone )
2005-04-17 02:20:36 +04:00
atomic_set ( & clone - > a_refcount , 1 ) ;
}
return clone ;
}
/*
* Check if an acl is valid . Returns 0 if it is , or - E . . . otherwise .
*/
int
2016-06-28 00:04:06 +03:00
posix_acl_valid ( struct user_namespace * user_ns , const struct posix_acl * acl )
2005-04-17 02:20:36 +04:00
{
const struct posix_acl_entry * pa , * pe ;
int state = ACL_USER_OBJ ;
int needs_mask = 0 ;
FOREACH_ACL_ENTRY ( pa , acl , pe ) {
if ( pa - > e_perm & ~ ( ACL_READ | ACL_WRITE | ACL_EXECUTE ) )
return - EINVAL ;
switch ( pa - > e_tag ) {
case ACL_USER_OBJ :
if ( state = = ACL_USER_OBJ ) {
state = ACL_USER ;
break ;
}
return - EINVAL ;
case ACL_USER :
if ( state ! = ACL_USER )
return - EINVAL ;
2016-06-28 00:04:06 +03:00
if ( ! kuid_has_mapping ( user_ns , pa - > e_uid ) )
2005-04-17 02:20:36 +04:00
return - EINVAL ;
needs_mask = 1 ;
break ;
case ACL_GROUP_OBJ :
if ( state = = ACL_USER ) {
state = ACL_GROUP ;
break ;
}
return - EINVAL ;
case ACL_GROUP :
if ( state ! = ACL_GROUP )
return - EINVAL ;
2016-06-28 00:04:06 +03:00
if ( ! kgid_has_mapping ( user_ns , pa - > e_gid ) )
2012-02-08 06:52:57 +04:00
return - EINVAL ;
2005-04-17 02:20:36 +04:00
needs_mask = 1 ;
break ;
case ACL_MASK :
if ( state ! = ACL_GROUP )
return - EINVAL ;
state = ACL_OTHER ;
break ;
case ACL_OTHER :
if ( state = = ACL_OTHER | |
( state = = ACL_GROUP & & ! needs_mask ) ) {
state = 0 ;
break ;
}
return - EINVAL ;
default :
return - EINVAL ;
}
}
if ( state = = 0 )
return 0 ;
return - EINVAL ;
}
2014-01-22 03:48:42 +04:00
EXPORT_SYMBOL ( posix_acl_valid ) ;
2005-04-17 02:20:36 +04:00
/*
* Returns 0 if the acl can be exactly represented in the traditional
* file mode permission bits , or else 1. Returns - E . . . on error .
*/
int
2011-07-24 02:56:36 +04:00
posix_acl_equiv_mode ( const struct posix_acl * acl , umode_t * mode_p )
2005-04-17 02:20:36 +04:00
{
const struct posix_acl_entry * pa , * pe ;
2011-07-24 02:56:36 +04:00
umode_t mode = 0 ;
2005-04-17 02:20:36 +04:00
int not_equiv = 0 ;
2014-05-04 15:03:32 +04:00
/*
* A null ACL can always be presented as mode bits .
*/
if ( ! acl )
return 0 ;
2005-04-17 02:20:36 +04:00
FOREACH_ACL_ENTRY ( pa , acl , pe ) {
switch ( pa - > e_tag ) {
case ACL_USER_OBJ :
mode | = ( pa - > e_perm & S_IRWXO ) < < 6 ;
break ;
case ACL_GROUP_OBJ :
mode | = ( pa - > e_perm & S_IRWXO ) < < 3 ;
break ;
case ACL_OTHER :
mode | = pa - > e_perm & S_IRWXO ;
break ;
case ACL_MASK :
mode = ( mode & ~ S_IRWXG ) |
( ( pa - > e_perm & S_IRWXO ) < < 3 ) ;
not_equiv = 1 ;
break ;
case ACL_USER :
case ACL_GROUP :
not_equiv = 1 ;
break ;
default :
return - EINVAL ;
}
}
if ( mode_p )
* mode_p = ( * mode_p & ~ S_IRWXUGO ) | mode ;
return not_equiv ;
}
2014-01-22 03:48:42 +04:00
EXPORT_SYMBOL ( posix_acl_equiv_mode ) ;
2005-04-17 02:20:36 +04:00
/*
* Create an ACL representing the file mode permission bits of an inode .
*/
struct posix_acl *
2011-07-24 03:01:48 +04:00
posix_acl_from_mode ( umode_t mode , gfp_t flags )
2005-04-17 02:20:36 +04:00
{
struct posix_acl * acl = posix_acl_alloc ( 3 , flags ) ;
if ( ! acl )
return ERR_PTR ( - ENOMEM ) ;
acl - > a_entries [ 0 ] . e_tag = ACL_USER_OBJ ;
acl - > a_entries [ 0 ] . e_perm = ( mode & S_IRWXU ) > > 6 ;
acl - > a_entries [ 1 ] . e_tag = ACL_GROUP_OBJ ;
acl - > a_entries [ 1 ] . e_perm = ( mode & S_IRWXG ) > > 3 ;
acl - > a_entries [ 2 ] . e_tag = ACL_OTHER ;
acl - > a_entries [ 2 ] . e_perm = ( mode & S_IRWXO ) ;
return acl ;
}
2014-01-22 03:48:42 +04:00
EXPORT_SYMBOL ( posix_acl_from_mode ) ;
2005-04-17 02:20:36 +04:00
/*
* Return 0 if current is granted want access to the inode
* by the acl . Returns - E . . . otherwise .
*/
int
posix_acl_permission ( struct inode * inode , const struct posix_acl * acl , int want )
{
const struct posix_acl_entry * pa , * pe , * mask_obj ;
int found = 0 ;
2011-10-23 21:43:32 +04:00
want & = MAY_READ | MAY_WRITE | MAY_EXEC | MAY_NOT_BLOCK ;
2005-04-17 02:20:36 +04:00
FOREACH_ACL_ENTRY ( pa , acl , pe ) {
switch ( pa - > e_tag ) {
case ACL_USER_OBJ :
/* (May have been checked already) */
2012-02-08 06:52:57 +04:00
if ( uid_eq ( inode - > i_uid , current_fsuid ( ) ) )
2005-04-17 02:20:36 +04:00
goto check_perm ;
break ;
case ACL_USER :
2012-02-08 06:52:57 +04:00
if ( uid_eq ( pa - > e_uid , current_fsuid ( ) ) )
2005-04-17 02:20:36 +04:00
goto mask ;
break ;
case ACL_GROUP_OBJ :
if ( in_group_p ( inode - > i_gid ) ) {
found = 1 ;
if ( ( pa - > e_perm & want ) = = want )
goto mask ;
}
break ;
case ACL_GROUP :
2012-02-08 06:52:57 +04:00
if ( in_group_p ( pa - > e_gid ) ) {
2005-04-17 02:20:36 +04:00
found = 1 ;
if ( ( pa - > e_perm & want ) = = want )
goto mask ;
}
break ;
case ACL_MASK :
break ;
case ACL_OTHER :
if ( found )
return - EACCES ;
else
goto check_perm ;
default :
return - EIO ;
}
}
return - EIO ;
mask :
for ( mask_obj = pa + 1 ; mask_obj ! = pe ; mask_obj + + ) {
if ( mask_obj - > e_tag = = ACL_MASK ) {
if ( ( pa - > e_perm & mask_obj - > e_perm & want ) = = want )
return 0 ;
return - EACCES ;
}
}
check_perm :
if ( ( pa - > e_perm & want ) = = want )
return 0 ;
return - EACCES ;
}
/*
* Modify acl when creating a new inode . The caller must ensure the acl is
* only referenced once .
*
* mode_p initially must contain the mode parameter to the open ( ) / creat ( )
* system calls . All permissions that are not granted by the acl are removed .
* The permissions in the acl are changed to reflect the mode_p parameter .
*/
2011-07-24 02:37:50 +04:00
static int posix_acl_create_masq ( struct posix_acl * acl , umode_t * mode_p )
2005-04-17 02:20:36 +04:00
{
struct posix_acl_entry * pa , * pe ;
struct posix_acl_entry * group_obj = NULL , * mask_obj = NULL ;
2011-07-24 02:37:50 +04:00
umode_t mode = * mode_p ;
2005-04-17 02:20:36 +04:00
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 ;
}
/*
* Modify the ACL for the chmod syscall .
*/
2013-12-20 17:16:41 +04:00
static int __posix_acl_chmod_masq ( struct posix_acl * acl , umode_t mode )
2005-04-17 02:20:36 +04:00
{
struct posix_acl_entry * group_obj = NULL , * mask_obj = NULL ;
struct posix_acl_entry * pa , * pe ;
/* 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 & S_IRWXU ) > > 6 ;
break ;
case ACL_USER :
case ACL_GROUP :
break ;
case ACL_GROUP_OBJ :
group_obj = pa ;
break ;
case ACL_MASK :
mask_obj = pa ;
break ;
case ACL_OTHER :
pa - > e_perm = ( mode & S_IRWXO ) ;
break ;
default :
return - EIO ;
}
}
if ( mask_obj ) {
mask_obj - > e_perm = ( mode & S_IRWXG ) > > 3 ;
} else {
if ( ! group_obj )
return - EIO ;
group_obj - > e_perm = ( mode & S_IRWXG ) > > 3 ;
}
return 0 ;
}
2011-07-23 08:18:02 +04:00
2011-07-23 11:10:32 +04:00
int
2013-12-20 17:16:42 +04:00
__posix_acl_create ( struct posix_acl * * acl , gfp_t gfp , umode_t * mode_p )
2011-07-23 11:10:32 +04:00
{
struct posix_acl * clone = posix_acl_clone ( * acl , gfp ) ;
int err = - ENOMEM ;
if ( clone ) {
err = posix_acl_create_masq ( clone , mode_p ) ;
if ( err < 0 ) {
posix_acl_release ( clone ) ;
clone = NULL ;
}
}
posix_acl_release ( * acl ) ;
* acl = clone ;
return err ;
}
2013-12-20 17:16:42 +04:00
EXPORT_SYMBOL ( __posix_acl_create ) ;
2011-07-23 11:10:32 +04:00
2011-07-23 08:18:02 +04:00
int
2013-12-20 17:16:41 +04:00
__posix_acl_chmod ( struct posix_acl * * acl , gfp_t gfp , umode_t mode )
2011-07-23 08:18:02 +04:00
{
struct posix_acl * clone = posix_acl_clone ( * acl , gfp ) ;
int err = - ENOMEM ;
if ( clone ) {
2013-12-20 17:16:41 +04:00
err = __posix_acl_chmod_masq ( clone , mode ) ;
2011-07-23 08:18:02 +04:00
if ( err ) {
posix_acl_release ( clone ) ;
clone = NULL ;
}
}
posix_acl_release ( * acl ) ;
* acl = clone ;
return err ;
}
2013-12-20 17:16:41 +04:00
EXPORT_SYMBOL ( __posix_acl_chmod ) ;
int
2013-12-20 17:16:42 +04:00
posix_acl_chmod ( struct inode * inode , umode_t mode )
2013-12-20 17:16:41 +04:00
{
struct posix_acl * acl ;
int ret = 0 ;
if ( ! IS_POSIXACL ( inode ) )
return 0 ;
if ( ! inode - > i_op - > set_acl )
return - EOPNOTSUPP ;
acl = get_acl ( inode , ACL_TYPE_ACCESS ) ;
2014-01-31 23:25:19 +04:00
if ( IS_ERR_OR_NULL ( acl ) ) {
if ( acl = = ERR_PTR ( - EOPNOTSUPP ) )
return 0 ;
2013-12-20 17:16:41 +04:00
return PTR_ERR ( acl ) ;
2014-01-31 23:25:19 +04:00
}
2013-12-20 17:16:41 +04:00
2013-12-20 17:16:42 +04:00
ret = __posix_acl_chmod ( & acl , GFP_KERNEL , mode ) ;
2013-12-20 17:16:41 +04:00
if ( ret )
return ret ;
ret = inode - > i_op - > set_acl ( inode , acl , ACL_TYPE_ACCESS ) ;
posix_acl_release ( acl ) ;
return ret ;
}
2011-07-23 08:18:02 +04:00
EXPORT_SYMBOL ( posix_acl_chmod ) ;
2013-12-20 17:16:37 +04:00
2013-12-20 17:16:42 +04:00
int
posix_acl_create ( struct inode * dir , umode_t * mode ,
struct posix_acl * * default_acl , struct posix_acl * * acl )
{
struct posix_acl * p ;
2015-06-19 02:00:55 +03:00
struct posix_acl * clone ;
2013-12-20 17:16:42 +04:00
int ret ;
2015-06-19 02:00:55 +03:00
* acl = NULL ;
* default_acl = NULL ;
2013-12-20 17:16:42 +04:00
if ( S_ISLNK ( * mode ) | | ! IS_POSIXACL ( dir ) )
2015-06-19 02:00:55 +03:00
return 0 ;
2013-12-20 17:16:42 +04:00
p = get_acl ( dir , ACL_TYPE_DEFAULT ) ;
2015-06-19 02:00:55 +03:00
if ( ! p | | p = = ERR_PTR ( - EOPNOTSUPP ) ) {
* mode & = ~ current_umask ( ) ;
return 0 ;
2013-12-20 17:16:42 +04:00
}
2015-06-19 02:00:55 +03:00
if ( IS_ERR ( p ) )
return PTR_ERR ( p ) ;
2013-12-20 17:16:42 +04:00
2016-09-16 13:44:21 +03:00
ret = - ENOMEM ;
2015-06-19 02:00:55 +03:00
clone = posix_acl_clone ( p , GFP_NOFS ) ;
if ( ! clone )
2016-09-16 13:44:21 +03:00
goto err_release ;
2013-12-20 17:16:42 +04:00
2015-06-19 02:00:55 +03:00
ret = posix_acl_create_masq ( clone , mode ) ;
2015-02-09 08:45:25 +03:00
if ( ret < 0 )
2016-09-16 13:44:21 +03:00
goto err_release_clone ;
2013-12-20 17:16:42 +04:00
2015-06-19 02:00:55 +03:00
if ( ret = = 0 )
posix_acl_release ( clone ) ;
else
* acl = clone ;
2013-12-20 17:16:42 +04:00
2015-06-19 02:00:55 +03:00
if ( ! S_ISDIR ( * mode ) )
2013-12-20 17:16:42 +04:00
posix_acl_release ( p ) ;
2015-06-19 02:00:55 +03:00
else
2013-12-20 17:16:42 +04:00
* default_acl = p ;
return 0 ;
2015-02-09 08:45:25 +03:00
2016-09-16 13:44:21 +03:00
err_release_clone :
2015-06-19 02:00:55 +03:00
posix_acl_release ( clone ) ;
2016-09-16 13:44:21 +03:00
err_release :
2015-02-09 08:45:25 +03:00
posix_acl_release ( p ) ;
2016-09-16 13:44:21 +03:00
return ret ;
2013-12-20 17:16:42 +04:00
}
EXPORT_SYMBOL_GPL ( posix_acl_create ) ;
2016-09-19 18:39:09 +03:00
/**
* posix_acl_update_mode - update mode in set_acl
*
* Update the file mode when setting an ACL : compute the new file permission
* bits based on the ACL . In addition , if the ACL is equivalent to the new
* file mode , set * acl to NULL to indicate that no ACL should be set .
*
* As with chmod , clear the setgit bit if the caller is not in the owning group
* or capable of CAP_FSETID ( see inode_change_ok ) .
*
* Called from set_acl inode operations .
*/
int posix_acl_update_mode ( struct inode * inode , umode_t * mode_p ,
struct posix_acl * * acl )
{
umode_t mode = inode - > i_mode ;
int error ;
error = posix_acl_equiv_mode ( * acl , & mode ) ;
if ( error < 0 )
return error ;
if ( error = = 0 )
* acl = NULL ;
if ( ! in_group_p ( inode - > i_gid ) & &
! capable_wrt_inode_uidgid ( inode , CAP_FSETID ) )
mode & = ~ S_ISGID ;
* mode_p = mode ;
return 0 ;
}
EXPORT_SYMBOL ( posix_acl_update_mode ) ;
2013-12-20 17:16:37 +04:00
/*
* Fix up the uids and gids in posix acl extended attributes in place .
*/
static void posix_acl_fix_xattr_userns (
struct user_namespace * to , struct user_namespace * from ,
void * value , size_t size )
{
2016-09-27 14:03:22 +03:00
struct posix_acl_xattr_header * header = value ;
struct posix_acl_xattr_entry * entry = ( void * ) ( header + 1 ) , * end ;
2013-12-20 17:16:37 +04:00
int count ;
kuid_t uid ;
kgid_t gid ;
if ( ! value )
return ;
2016-09-27 14:03:22 +03:00
if ( size < sizeof ( struct posix_acl_xattr_header ) )
2013-12-20 17:16:37 +04:00
return ;
if ( header - > a_version ! = cpu_to_le32 ( POSIX_ACL_XATTR_VERSION ) )
return ;
count = posix_acl_xattr_count ( size ) ;
if ( count < 0 )
return ;
if ( count = = 0 )
return ;
for ( end = entry + count ; entry ! = end ; entry + + ) {
switch ( le16_to_cpu ( entry - > e_tag ) ) {
case ACL_USER :
uid = make_kuid ( from , le32_to_cpu ( entry - > e_id ) ) ;
entry - > e_id = cpu_to_le32 ( from_kuid ( to , uid ) ) ;
break ;
case ACL_GROUP :
gid = make_kgid ( from , le32_to_cpu ( entry - > e_id ) ) ;
entry - > e_id = cpu_to_le32 ( from_kgid ( to , gid ) ) ;
break ;
default :
break ;
}
}
}
void posix_acl_fix_xattr_from_user ( void * value , size_t size )
{
struct user_namespace * user_ns = current_user_ns ( ) ;
if ( user_ns = = & init_user_ns )
return ;
posix_acl_fix_xattr_userns ( & init_user_ns , user_ns , value , size ) ;
}
void posix_acl_fix_xattr_to_user ( void * value , size_t size )
{
struct user_namespace * user_ns = current_user_ns ( ) ;
if ( user_ns = = & init_user_ns )
return ;
posix_acl_fix_xattr_userns ( user_ns , & init_user_ns , value , size ) ;
}
/*
* Convert from extended attribute to in - memory representation .
*/
struct posix_acl *
posix_acl_from_xattr ( struct user_namespace * user_ns ,
const void * value , size_t size )
{
2016-09-27 14:03:22 +03:00
const struct posix_acl_xattr_header * header = value ;
const struct posix_acl_xattr_entry * entry = ( const void * ) ( header + 1 ) , * end ;
2013-12-20 17:16:37 +04:00
int count ;
struct posix_acl * acl ;
struct posix_acl_entry * acl_e ;
if ( ! value )
return NULL ;
2016-09-27 14:03:22 +03:00
if ( size < sizeof ( struct posix_acl_xattr_header ) )
2013-12-20 17:16:37 +04:00
return ERR_PTR ( - EINVAL ) ;
if ( header - > a_version ! = cpu_to_le32 ( POSIX_ACL_XATTR_VERSION ) )
return ERR_PTR ( - EOPNOTSUPP ) ;
count = posix_acl_xattr_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 ) ;
acl_e = acl - > a_entries ;
for ( end = entry + count ; entry ! = end ; acl_e + + , entry + + ) {
acl_e - > e_tag = le16_to_cpu ( entry - > e_tag ) ;
acl_e - > e_perm = le16_to_cpu ( entry - > e_perm ) ;
switch ( acl_e - > e_tag ) {
case ACL_USER_OBJ :
case ACL_GROUP_OBJ :
case ACL_MASK :
case ACL_OTHER :
break ;
case ACL_USER :
acl_e - > e_uid =
make_kuid ( user_ns ,
le32_to_cpu ( entry - > e_id ) ) ;
if ( ! uid_valid ( acl_e - > e_uid ) )
goto fail ;
break ;
case ACL_GROUP :
acl_e - > e_gid =
make_kgid ( user_ns ,
le32_to_cpu ( entry - > e_id ) ) ;
if ( ! gid_valid ( acl_e - > e_gid ) )
goto fail ;
break ;
default :
goto fail ;
}
}
return acl ;
fail :
posix_acl_release ( acl ) ;
return ERR_PTR ( - EINVAL ) ;
}
EXPORT_SYMBOL ( posix_acl_from_xattr ) ;
/*
* Convert from in - memory to extended attribute representation .
*/
int
posix_acl_to_xattr ( struct user_namespace * user_ns , const struct posix_acl * acl ,
void * buffer , size_t size )
{
2016-09-27 14:03:22 +03:00
struct posix_acl_xattr_header * ext_acl = buffer ;
struct posix_acl_xattr_entry * ext_entry ;
2013-12-20 17:16:37 +04:00
int real_size , n ;
real_size = posix_acl_xattr_size ( acl - > a_count ) ;
if ( ! buffer )
return real_size ;
if ( real_size > size )
return - ERANGE ;
2014-02-14 13:05:49 +04:00
2016-09-27 14:03:22 +03:00
ext_entry = ( void * ) ( ext_acl + 1 ) ;
2013-12-20 17:16:37 +04:00
ext_acl - > a_version = cpu_to_le32 ( POSIX_ACL_XATTR_VERSION ) ;
for ( n = 0 ; n < acl - > a_count ; n + + , ext_entry + + ) {
const struct posix_acl_entry * acl_e = & acl - > a_entries [ n ] ;
ext_entry - > e_tag = cpu_to_le16 ( acl_e - > e_tag ) ;
ext_entry - > e_perm = cpu_to_le16 ( acl_e - > e_perm ) ;
switch ( acl_e - > e_tag ) {
case ACL_USER :
ext_entry - > e_id =
cpu_to_le32 ( from_kuid ( user_ns , acl_e - > e_uid ) ) ;
break ;
case ACL_GROUP :
ext_entry - > e_id =
cpu_to_le32 ( from_kgid ( user_ns , acl_e - > e_gid ) ) ;
break ;
default :
ext_entry - > e_id = cpu_to_le32 ( ACL_UNDEFINED_ID ) ;
break ;
}
}
return real_size ;
}
EXPORT_SYMBOL ( posix_acl_to_xattr ) ;
2013-12-20 17:16:40 +04:00
static int
2015-10-04 20:18:51 +03:00
posix_acl_xattr_get ( const struct xattr_handler * handler ,
2016-04-11 03:48:24 +03:00
struct dentry * unused , struct inode * inode ,
const char * name , void * value , size_t size )
2013-12-20 17:16:40 +04:00
{
struct posix_acl * acl ;
int error ;
2016-04-11 03:48:24 +03:00
if ( ! IS_POSIXACL ( inode ) )
2013-12-20 17:16:40 +04:00
return - EOPNOTSUPP ;
2016-04-11 03:48:24 +03:00
if ( S_ISLNK ( inode - > i_mode ) )
2013-12-20 17:16:40 +04:00
return - EOPNOTSUPP ;
2016-04-11 03:48:24 +03:00
acl = get_acl ( inode , handler - > flags ) ;
2013-12-20 17:16:40 +04:00
if ( IS_ERR ( acl ) )
return PTR_ERR ( acl ) ;
if ( acl = = NULL )
return - ENODATA ;
error = posix_acl_to_xattr ( & init_user_ns , acl , value , size ) ;
posix_acl_release ( acl ) ;
return error ;
}
2016-06-23 00:57:25 +03:00
int
set_posix_acl ( struct inode * inode , int type , struct posix_acl * acl )
2013-12-20 17:16:40 +04:00
{
if ( ! IS_POSIXACL ( inode ) )
return - EOPNOTSUPP ;
if ( ! inode - > i_op - > set_acl )
return - EOPNOTSUPP ;
2016-06-23 00:57:25 +03:00
if ( type = = ACL_TYPE_DEFAULT & & ! S_ISDIR ( inode - > i_mode ) )
return acl ? - EACCES : 0 ;
2013-12-20 17:16:40 +04:00
if ( ! inode_owner_or_capable ( inode ) )
return - EPERM ;
2016-06-23 00:57:25 +03:00
if ( acl ) {
2016-07-30 01:54:19 +03:00
int ret = posix_acl_valid ( inode - > i_sb - > s_user_ns , acl ) ;
2016-06-23 00:57:25 +03:00
if ( ret )
return ret ;
}
return inode - > i_op - > set_acl ( inode , acl , type ) ;
}
EXPORT_SYMBOL ( set_posix_acl ) ;
static int
posix_acl_xattr_set ( const struct xattr_handler * handler ,
struct dentry * unused , struct inode * inode ,
const char * name , const void * value ,
size_t size , int flags )
{
struct posix_acl * acl = NULL ;
int ret ;
2013-12-20 17:16:40 +04:00
if ( value ) {
acl = posix_acl_from_xattr ( & init_user_ns , value , size ) ;
if ( IS_ERR ( acl ) )
return PTR_ERR ( acl ) ;
}
2016-06-23 00:57:25 +03:00
ret = set_posix_acl ( inode , handler - > flags , acl ) ;
2013-12-20 17:16:40 +04:00
posix_acl_release ( acl ) ;
return ret ;
}
2015-12-02 16:44:43 +03:00
static bool
posix_acl_xattr_list ( struct dentry * dentry )
2013-12-20 17:16:40 +04:00
{
2015-12-02 16:44:43 +03:00
return IS_POSIXACL ( d_backing_inode ( dentry ) ) ;
2013-12-20 17:16:40 +04:00
}
const struct xattr_handler posix_acl_access_xattr_handler = {
2015-12-02 16:44:36 +03:00
. name = XATTR_NAME_POSIX_ACL_ACCESS ,
2013-12-20 17:16:40 +04:00
. flags = ACL_TYPE_ACCESS ,
. list = posix_acl_xattr_list ,
. get = posix_acl_xattr_get ,
. set = posix_acl_xattr_set ,
} ;
EXPORT_SYMBOL_GPL ( posix_acl_access_xattr_handler ) ;
const struct xattr_handler posix_acl_default_xattr_handler = {
2015-12-02 16:44:36 +03:00
. name = XATTR_NAME_POSIX_ACL_DEFAULT ,
2013-12-20 17:16:40 +04:00
. flags = ACL_TYPE_DEFAULT ,
. list = posix_acl_xattr_list ,
. get = posix_acl_xattr_get ,
. set = posix_acl_xattr_set ,
} ;
EXPORT_SYMBOL_GPL ( posix_acl_default_xattr_handler ) ;
2013-12-20 17:16:54 +04:00
int simple_set_acl ( struct inode * inode , struct posix_acl * acl , int type )
{
int error ;
if ( type = = ACL_TYPE_ACCESS ) {
2017-01-09 04:34:48 +03:00
error = posix_acl_update_mode ( inode ,
& inode - > i_mode , & acl ) ;
if ( error )
return error ;
2013-12-20 17:16:54 +04:00
}
2016-09-14 17:48:04 +03:00
inode - > i_ctime = current_time ( inode ) ;
2013-12-20 17:16:54 +04:00
set_cached_acl ( inode , type , acl ) ;
return 0 ;
}
int simple_acl_create ( struct inode * dir , struct inode * inode )
{
struct posix_acl * default_acl , * acl ;
int error ;
error = posix_acl_create ( dir , & inode - > i_mode , & default_acl , & acl ) ;
if ( error )
return error ;
set_cached_acl ( inode , ACL_TYPE_DEFAULT , default_acl ) ;
set_cached_acl ( inode , ACL_TYPE_ACCESS , acl ) ;
if ( default_acl )
posix_acl_release ( default_acl ) ;
if ( acl )
posix_acl_release ( acl ) ;
return 0 ;
}