2013-11-11 11:18:03 +04:00
/*
* linux / fs / ceph / acl . c
*
* Copyright ( C ) 2013 Guangliang Zhao , < lucienchao @ gmail . com >
*
* This program is free software ; you can redistribute it and / or
* modify it under the terms of the GNU General Public
* License v2 as published by the Free Software Foundation .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the GNU
* General Public License for more details .
*
* You should have received a copy of the GNU General Public
* License along with this program ; if not , write to the
* Free Software Foundation , Inc . , 59 Temple Place - Suite 330 ,
* Boston , MA 021110 - 1307 , USA .
*/
# include <linux/ceph/ceph_debug.h>
# include <linux/fs.h>
# include <linux/string.h>
# include <linux/xattr.h>
# include <linux/posix_acl_xattr.h>
# include <linux/posix_acl.h>
# include <linux/sched.h>
# include <linux/slab.h>
# include "super.h"
static inline void ceph_set_cached_acl ( struct inode * inode ,
int type , struct posix_acl * acl )
{
struct ceph_inode_info * ci = ceph_inode ( inode ) ;
spin_lock ( & ci - > i_ceph_lock ) ;
if ( __ceph_caps_issued_mask ( ci , CEPH_CAP_XATTR_SHARED , 0 ) )
set_cached_acl ( inode , type , acl ) ;
2016-03-24 16:38:37 +03:00
else
forget_cached_acl ( inode , type ) ;
2013-11-11 11:18:03 +04:00
spin_unlock ( & ci - > i_ceph_lock ) ;
}
struct posix_acl * ceph_get_acl ( struct inode * inode , int type )
{
int size ;
const char * name ;
char * value = NULL ;
struct posix_acl * acl ;
switch ( type ) {
case ACL_TYPE_ACCESS :
2015-12-02 16:44:35 +03:00
name = XATTR_NAME_POSIX_ACL_ACCESS ;
2013-11-11 11:18:03 +04:00
break ;
case ACL_TYPE_DEFAULT :
2015-12-02 16:44:35 +03:00
name = XATTR_NAME_POSIX_ACL_DEFAULT ;
2013-11-11 11:18:03 +04:00
break ;
default :
BUG ( ) ;
}
size = __ceph_getxattr ( inode , name , " " , 0 ) ;
if ( size > 0 ) {
value = kzalloc ( size , GFP_NOFS ) ;
if ( ! value )
return ERR_PTR ( - ENOMEM ) ;
size = __ceph_getxattr ( inode , name , value , size ) ;
}
if ( size > 0 )
acl = posix_acl_from_xattr ( & init_user_ns , value , size ) ;
else if ( size = = - ERANGE | | size = = - ENODATA | | size = = 0 )
acl = NULL ;
else
acl = ERR_PTR ( - EIO ) ;
kfree ( value ) ;
if ( ! IS_ERR ( acl ) )
ceph_set_cached_acl ( inode , type , acl ) ;
return acl ;
}
2014-01-29 18:22:25 +04:00
int ceph_set_acl ( struct inode * inode , struct posix_acl * acl , int type )
2013-11-11 11:18:03 +04:00
{
int ret = 0 , size = 0 ;
const char * name = NULL ;
char * value = NULL ;
struct iattr newattrs ;
umode_t new_mode = inode - > i_mode , old_mode = inode - > i_mode ;
switch ( type ) {
case ACL_TYPE_ACCESS :
2015-12-02 16:44:35 +03:00
name = XATTR_NAME_POSIX_ACL_ACCESS ;
2013-11-11 11:18:03 +04:00
if ( acl ) {
2016-09-19 18:39:09 +03:00
ret = posix_acl_update_mode ( inode , & new_mode , & acl ) ;
if ( ret )
2013-11-11 11:18:03 +04:00
goto out ;
}
break ;
case ACL_TYPE_DEFAULT :
if ( ! S_ISDIR ( inode - > i_mode ) ) {
ret = acl ? - EINVAL : 0 ;
goto out ;
}
2015-12-02 16:44:35 +03:00
name = XATTR_NAME_POSIX_ACL_DEFAULT ;
2013-11-11 11:18:03 +04:00
break ;
default :
ret = - EINVAL ;
goto out ;
}
if ( acl ) {
size = posix_acl_xattr_size ( acl - > a_count ) ;
value = kmalloc ( size , GFP_NOFS ) ;
if ( ! value ) {
ret = - ENOMEM ;
goto out ;
}
ret = posix_acl_to_xattr ( & init_user_ns , acl , value , size ) ;
if ( ret < 0 )
goto out_free ;
}
if ( new_mode ! = old_mode ) {
newattrs . ia_mode = new_mode ;
newattrs . ia_valid = ATTR_MODE ;
2016-04-14 01:30:16 +03:00
ret = __ceph_setattr ( inode , & newattrs ) ;
2013-11-11 11:18:03 +04:00
if ( ret )
2016-04-14 01:30:16 +03:00
goto out_free ;
2013-11-11 11:18:03 +04:00
}
2016-04-14 01:30:16 +03:00
ret = __ceph_setxattr ( inode , name , value , size , 0 ) ;
2013-11-11 11:18:03 +04:00
if ( ret ) {
if ( new_mode ! = old_mode ) {
newattrs . ia_mode = old_mode ;
newattrs . ia_valid = ATTR_MODE ;
2016-04-14 01:30:16 +03:00
__ceph_setattr ( inode , & newattrs ) ;
2013-11-11 11:18:03 +04:00
}
2016-04-14 01:30:16 +03:00
goto out_free ;
2013-11-11 11:18:03 +04:00
}
ceph_set_cached_acl ( inode , type , acl ) ;
out_free :
kfree ( value ) ;
out :
return ret ;
}
2014-09-16 16:35:17 +04:00
int ceph_pre_init_acls ( struct inode * dir , umode_t * mode ,
struct ceph_acls_info * info )
2013-11-11 11:18:03 +04:00
{
2014-09-16 16:35:17 +04:00
struct posix_acl * acl , * default_acl ;
size_t val_size1 = 0 , val_size2 = 0 ;
struct ceph_pagelist * pagelist = NULL ;
void * tmp_buf = NULL ;
int err ;
err = posix_acl_create ( dir , mode , & default_acl , & acl ) ;
if ( err )
return err ;
if ( acl ) {
int ret = posix_acl_equiv_mode ( acl , mode ) ;
if ( ret < 0 )
goto out_err ;
if ( ret = = 0 ) {
posix_acl_release ( acl ) ;
acl = NULL ;
2014-07-04 09:59:43 +04:00
}
}
2013-11-11 11:18:03 +04:00
2014-09-16 16:35:17 +04:00
if ( ! default_acl & & ! acl )
return 0 ;
if ( acl )
val_size1 = posix_acl_xattr_size ( acl - > a_count ) ;
if ( default_acl )
val_size2 = posix_acl_xattr_size ( default_acl - > a_count ) ;
err = - ENOMEM ;
2015-06-13 12:27:05 +03:00
tmp_buf = kmalloc ( max ( val_size1 , val_size2 ) , GFP_KERNEL ) ;
2014-09-16 16:35:17 +04:00
if ( ! tmp_buf )
goto out_err ;
2015-06-13 12:27:05 +03:00
pagelist = kmalloc ( sizeof ( struct ceph_pagelist ) , GFP_KERNEL ) ;
2014-09-16 16:35:17 +04:00
if ( ! pagelist )
goto out_err ;
ceph_pagelist_init ( pagelist ) ;
err = ceph_pagelist_reserve ( pagelist , PAGE_SIZE ) ;
if ( err )
goto out_err ;
ceph_pagelist_encode_32 ( pagelist , acl & & default_acl ? 2 : 1 ) ;
2014-01-30 20:56:36 +04:00
if ( acl ) {
2015-12-02 16:44:35 +03:00
size_t len = strlen ( XATTR_NAME_POSIX_ACL_ACCESS ) ;
2014-09-16 16:35:17 +04:00
err = ceph_pagelist_reserve ( pagelist , len + val_size1 + 8 ) ;
if ( err )
goto out_err ;
2015-12-02 16:44:35 +03:00
ceph_pagelist_encode_string ( pagelist , XATTR_NAME_POSIX_ACL_ACCESS ,
2014-09-16 16:35:17 +04:00
len ) ;
err = posix_acl_to_xattr ( & init_user_ns , acl ,
tmp_buf , val_size1 ) ;
if ( err < 0 )
goto out_err ;
ceph_pagelist_encode_32 ( pagelist , val_size1 ) ;
ceph_pagelist_append ( pagelist , tmp_buf , val_size1 ) ;
2014-01-30 20:56:36 +04:00
}
2014-09-16 16:35:17 +04:00
if ( default_acl ) {
2015-12-02 16:44:35 +03:00
size_t len = strlen ( XATTR_NAME_POSIX_ACL_DEFAULT ) ;
2014-09-16 16:35:17 +04:00
err = ceph_pagelist_reserve ( pagelist , len + val_size2 + 8 ) ;
if ( err )
goto out_err ;
err = ceph_pagelist_encode_string ( pagelist ,
2015-12-02 16:44:35 +03:00
XATTR_NAME_POSIX_ACL_DEFAULT , len ) ;
2014-09-16 16:35:17 +04:00
err = posix_acl_to_xattr ( & init_user_ns , default_acl ,
tmp_buf , val_size2 ) ;
if ( err < 0 )
goto out_err ;
ceph_pagelist_encode_32 ( pagelist , val_size2 ) ;
ceph_pagelist_append ( pagelist , tmp_buf , val_size2 ) ;
}
kfree ( tmp_buf ) ;
info - > acl = acl ;
info - > default_acl = default_acl ;
info - > pagelist = pagelist ;
return 0 ;
out_err :
posix_acl_release ( acl ) ;
posix_acl_release ( default_acl ) ;
kfree ( tmp_buf ) ;
if ( pagelist )
ceph_pagelist_release ( pagelist ) ;
return err ;
}
void ceph_init_inode_acls ( struct inode * inode , struct ceph_acls_info * info )
{
if ( ! inode )
return ;
ceph_set_cached_acl ( inode , ACL_TYPE_ACCESS , info - > acl ) ;
ceph_set_cached_acl ( inode , ACL_TYPE_DEFAULT , info - > default_acl ) ;
}
void ceph_release_acls_info ( struct ceph_acls_info * info )
{
posix_acl_release ( info - > acl ) ;
posix_acl_release ( info - > default_acl ) ;
if ( info - > pagelist )
ceph_pagelist_release ( info - > pagelist ) ;
2013-11-11 11:18:03 +04:00
}