2019-05-27 09:55:20 +03:00
// SPDX-License-Identifier: GPL-2.0-only
2013-11-11 11:18:03 +04:00
/*
* linux / fs / ceph / acl . c
*
* Copyright ( C ) 2013 Guangliang Zhao , < lucienchao @ gmail . com >
*/
# 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 ;
2018-06-20 10:42:55 +03:00
unsigned int retry_cnt = 0 ;
2013-11-11 11:18:03 +04:00
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 ( ) ;
}
2018-06-20 10:42:55 +03:00
retry :
2013-11-11 11:18:03 +04:00
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 ) ;
}
2018-06-20 10:42:55 +03:00
if ( size = = - ERANGE & & retry_cnt < 10 ) {
retry_cnt + + ;
kfree ( value ) ;
value = NULL ;
goto retry ;
}
if ( size > 0 ) {
2013-11-11 11:18:03 +04:00
acl = posix_acl_from_xattr ( & init_user_ns , value , size ) ;
2018-06-20 10:42:55 +03:00
} else if ( size = = - ENODATA | | size = = 0 ) {
2013-11-11 11:18:03 +04:00
acl = NULL ;
2018-06-20 10:42:55 +03:00
} else {
pr_err_ratelimited ( " get acl %llx.%llx failed, err=%d \n " ,
ceph_vinop ( inode ) , size ) ;
2013-11-11 11:18:03 +04:00
acl = ERR_PTR ( - EIO ) ;
2018-06-20 10:42:55 +03:00
}
2013-11-11 11:18:03 +04:00
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 ;
2018-06-22 10:01:13 +03:00
struct timespec64 old_ctime = inode - > i_ctime ;
2013-11-11 11:18:03 +04:00
umode_t new_mode = inode - > i_mode , old_mode = inode - > i_mode ;
2018-09-02 18:21:09 +03:00
if ( ceph_snap ( inode ) ! = CEPH_NOSNAP ) {
ret = - EROFS ;
goto out ;
}
2013-11-11 11:18:03 +04:00
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 ) {
2017-06-01 12:08:00 +03:00
newattrs . ia_ctime = current_time ( inode ) ;
2013-11-11 11:18:03 +04:00
newattrs . ia_mode = new_mode ;
2018-06-22 10:01:13 +03:00
newattrs . ia_valid = ATTR_MODE | ATTR_CTIME ;
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 ) {
2018-06-22 10:01:13 +03:00
newattrs . ia_ctime = old_ctime ;
2013-11-11 11:18:03 +04:00
newattrs . ia_mode = old_mode ;
2018-06-22 10:01:13 +03:00
newattrs . ia_valid = ATTR_MODE | ATTR_CTIME ;
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 ,
2019-05-26 10:35:39 +03:00
struct ceph_acl_sec_ctx * as_ctx )
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 ) {
2018-07-09 17:48:07 +03:00
err = posix_acl_equiv_mode ( acl , mode ) ;
if ( err < 0 )
2014-09-16 16:35:17 +04:00
goto out_err ;
2018-07-09 17:48:07 +03:00
if ( err = = 0 ) {
2014-09-16 16:35:17 +04:00
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 ;
2018-09-28 16:38:34 +03:00
pagelist = ceph_pagelist_alloc ( GFP_KERNEL ) ;
2014-09-16 16:35:17 +04:00
if ( ! pagelist )
goto out_err ;
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 ;
2018-08-17 17:05:31 +03:00
ceph_pagelist_encode_string ( pagelist ,
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 ) ;
2019-05-26 10:35:39 +03:00
as_ctx - > acl = acl ;
as_ctx - > default_acl = default_acl ;
as_ctx - > pagelist = pagelist ;
2014-09-16 16:35:17 +04:00
return 0 ;
out_err :
posix_acl_release ( acl ) ;
posix_acl_release ( default_acl ) ;
kfree ( tmp_buf ) ;
if ( pagelist )
ceph_pagelist_release ( pagelist ) ;
return err ;
}
2019-05-26 10:35:39 +03:00
void ceph_init_inode_acls ( struct inode * inode , struct ceph_acl_sec_ctx * as_ctx )
2014-09-16 16:35:17 +04:00
{
if ( ! inode )
return ;
2019-05-26 10:35:39 +03:00
ceph_set_cached_acl ( inode , ACL_TYPE_ACCESS , as_ctx - > acl ) ;
ceph_set_cached_acl ( inode , ACL_TYPE_DEFAULT , as_ctx - > default_acl ) ;
2013-11-11 11:18:03 +04:00
}