2017-03-17 09:18:50 +03:00
// SPDX-License-Identifier: GPL-2.0
# include "bcachefs.h"
2023-09-09 01:14:08 +03:00
# include "acl.h"
# include "xattr.h"
2017-03-17 09:18:50 +03:00
# include <linux/posix_acl.h>
2023-09-09 01:14:08 +03:00
static const char * const acl_types [ ] = {
[ ACL_USER_OBJ ] = " user_obj " ,
[ ACL_USER ] = " user " ,
[ ACL_GROUP_OBJ ] = " group_obj " ,
[ ACL_GROUP ] = " group " ,
[ ACL_MASK ] = " mask " ,
[ ACL_OTHER ] = " other " ,
NULL ,
} ;
void bch2_acl_to_text ( struct printbuf * out , const void * value , size_t size )
{
const void * p , * end = value + size ;
if ( ! value | |
size < sizeof ( bch_acl_header ) | |
( ( bch_acl_header * ) value ) - > a_version ! = cpu_to_le32 ( BCH_ACL_VERSION ) )
return ;
p = value + sizeof ( bch_acl_header ) ;
while ( p < end ) {
const bch_acl_entry * in = p ;
unsigned tag = le16_to_cpu ( in - > e_tag ) ;
prt_str ( out , acl_types [ tag ] ) ;
switch ( tag ) {
case ACL_USER_OBJ :
case ACL_GROUP_OBJ :
case ACL_MASK :
case ACL_OTHER :
p + = sizeof ( bch_acl_entry_short ) ;
break ;
case ACL_USER :
prt_printf ( out , " uid %u " , le32_to_cpu ( in - > e_id ) ) ;
p + = sizeof ( bch_acl_entry ) ;
break ;
case ACL_GROUP :
prt_printf ( out , " gid %u " , le32_to_cpu ( in - > e_id ) ) ;
p + = sizeof ( bch_acl_entry ) ;
break ;
}
prt_printf ( out , " %o " , le16_to_cpu ( in - > e_perm ) ) ;
if ( p ! = end )
prt_char ( out , ' ' ) ;
}
}
# ifdef CONFIG_BCACHEFS_POSIX_ACL
# include "fs.h"
# include <linux/fs.h>
2017-03-17 09:18:50 +03:00
# include <linux/posix_acl_xattr.h>
# include <linux/sched.h>
# include <linux/slab.h>
static inline size_t bch2_acl_size ( unsigned nr_short , unsigned nr_long )
{
return sizeof ( bch_acl_header ) +
sizeof ( bch_acl_entry_short ) * nr_short +
sizeof ( bch_acl_entry ) * nr_long ;
}
static inline int acl_to_xattr_type ( int type )
{
switch ( type ) {
case ACL_TYPE_ACCESS :
2018-11-01 22:10:01 +03:00
return KEY_TYPE_XATTR_INDEX_POSIX_ACL_ACCESS ;
2017-03-17 09:18:50 +03:00
case ACL_TYPE_DEFAULT :
2018-11-01 22:10:01 +03:00
return KEY_TYPE_XATTR_INDEX_POSIX_ACL_DEFAULT ;
2017-03-17 09:18:50 +03:00
default :
BUG ( ) ;
}
}
/*
* Convert from filesystem to in - memory representation .
*/
2023-05-28 10:44:38 +03:00
static struct posix_acl * bch2_acl_from_disk ( struct btree_trans * trans ,
const void * value , size_t size )
2017-03-17 09:18:50 +03:00
{
const void * p , * end = value + size ;
struct posix_acl * acl ;
struct posix_acl_entry * out ;
unsigned count = 0 ;
2023-05-28 10:44:38 +03:00
int ret ;
2017-03-17 09:18:50 +03:00
if ( ! value )
return NULL ;
if ( size < sizeof ( bch_acl_header ) )
goto invalid ;
if ( ( ( bch_acl_header * ) value ) - > a_version ! =
cpu_to_le32 ( BCH_ACL_VERSION ) )
goto invalid ;
p = value + sizeof ( bch_acl_header ) ;
while ( p < end ) {
const bch_acl_entry * entry = p ;
if ( p + sizeof ( bch_acl_entry_short ) > end )
goto invalid ;
switch ( le16_to_cpu ( entry - > e_tag ) ) {
case ACL_USER_OBJ :
case ACL_GROUP_OBJ :
case ACL_MASK :
case ACL_OTHER :
p + = sizeof ( bch_acl_entry_short ) ;
break ;
case ACL_USER :
case ACL_GROUP :
p + = sizeof ( bch_acl_entry ) ;
break ;
default :
goto invalid ;
}
count + + ;
}
if ( p > end )
goto invalid ;
if ( ! count )
return NULL ;
2023-05-28 10:44:38 +03:00
acl = allocate_dropping_locks ( trans , ret ,
posix_acl_alloc ( count , _gfp ) ) ;
2017-03-17 09:18:50 +03:00
if ( ! acl )
return ERR_PTR ( - ENOMEM ) ;
2023-05-28 10:44:38 +03:00
if ( ret ) {
kfree ( acl ) ;
return ERR_PTR ( ret ) ;
}
2017-03-17 09:18:50 +03:00
out = acl - > a_entries ;
p = value + sizeof ( bch_acl_header ) ;
while ( p < end ) {
const bch_acl_entry * in = p ;
out - > e_tag = le16_to_cpu ( in - > e_tag ) ;
out - > e_perm = le16_to_cpu ( in - > e_perm ) ;
switch ( out - > e_tag ) {
case ACL_USER_OBJ :
case ACL_GROUP_OBJ :
case ACL_MASK :
case ACL_OTHER :
p + = sizeof ( bch_acl_entry_short ) ;
break ;
case ACL_USER :
out - > e_uid = make_kuid ( & init_user_ns ,
le32_to_cpu ( in - > e_id ) ) ;
p + = sizeof ( bch_acl_entry ) ;
break ;
case ACL_GROUP :
out - > e_gid = make_kgid ( & init_user_ns ,
le32_to_cpu ( in - > e_id ) ) ;
p + = sizeof ( bch_acl_entry ) ;
break ;
}
out + + ;
}
BUG_ON ( out ! = acl - > a_entries + acl - > a_count ) ;
return acl ;
invalid :
pr_err ( " invalid acl entry " ) ;
return ERR_PTR ( - EINVAL ) ;
}
# define acl_for_each_entry(acl, acl_e) \
for ( acl_e = acl - > a_entries ; \
acl_e < acl - > a_entries + acl - > a_count ; \
acl_e + + )
/*
* Convert from in - memory to filesystem representation .
*/
static struct bkey_i_xattr *
bch2_acl_to_xattr ( struct btree_trans * trans ,
const struct posix_acl * acl ,
int type )
{
struct bkey_i_xattr * xattr ;
bch_acl_header * acl_header ;
const struct posix_acl_entry * acl_e ;
void * outptr ;
unsigned nr_short = 0 , nr_long = 0 , acl_len , u64s ;
acl_for_each_entry ( acl , acl_e ) {
switch ( acl_e - > e_tag ) {
case ACL_USER :
case ACL_GROUP :
nr_long + + ;
break ;
case ACL_USER_OBJ :
case ACL_GROUP_OBJ :
case ACL_MASK :
case ACL_OTHER :
nr_short + + ;
break ;
default :
return ERR_PTR ( - EINVAL ) ;
}
}
acl_len = bch2_acl_size ( nr_short , nr_long ) ;
u64s = BKEY_U64s + xattr_val_u64s ( 0 , acl_len ) ;
if ( u64s > U8_MAX )
return ERR_PTR ( - E2BIG ) ;
xattr = bch2_trans_kmalloc ( trans , u64s * sizeof ( u64 ) ) ;
if ( IS_ERR ( xattr ) )
return xattr ;
bkey_xattr_init ( & xattr - > k_i ) ;
xattr - > k . u64s = u64s ;
xattr - > v . x_type = acl_to_xattr_type ( type ) ;
2022-10-20 01:31:33 +03:00
xattr - > v . x_name_len = 0 ;
2017-03-17 09:18:50 +03:00
xattr - > v . x_val_len = cpu_to_le16 ( acl_len ) ;
acl_header = xattr_val ( & xattr - > v ) ;
acl_header - > a_version = cpu_to_le32 ( BCH_ACL_VERSION ) ;
outptr = ( void * ) acl_header + sizeof ( * acl_header ) ;
acl_for_each_entry ( acl , acl_e ) {
bch_acl_entry * entry = outptr ;
entry - > e_tag = cpu_to_le16 ( acl_e - > e_tag ) ;
entry - > e_perm = cpu_to_le16 ( acl_e - > e_perm ) ;
switch ( acl_e - > e_tag ) {
case ACL_USER :
entry - > e_id = cpu_to_le32 (
from_kuid ( & init_user_ns , acl_e - > e_uid ) ) ;
outptr + = sizeof ( bch_acl_entry ) ;
break ;
case ACL_GROUP :
entry - > e_id = cpu_to_le32 (
from_kgid ( & init_user_ns , acl_e - > e_gid ) ) ;
outptr + = sizeof ( bch_acl_entry ) ;
break ;
case ACL_USER_OBJ :
case ACL_GROUP_OBJ :
case ACL_MASK :
case ACL_OTHER :
outptr + = sizeof ( bch_acl_entry_short ) ;
break ;
}
}
BUG_ON ( outptr ! = xattr_val ( & xattr - > v ) + acl_len ) ;
return xattr ;
}
struct posix_acl * bch2_get_acl ( struct mnt_idmap * idmap ,
struct dentry * dentry , int type )
{
struct bch_inode_info * inode = to_bch_ei ( dentry - > d_inode ) ;
struct bch_fs * c = inode - > v . i_sb - > s_fs_info ;
2021-03-03 02:35:30 +03:00
struct bch_hash_info hash = bch2_hash_info_init ( c , & inode - > ei_inode ) ;
2023-07-07 05:47:42 +03:00
struct xattr_search_key search = X_SEARCH ( acl_to_xattr_type ( type ) , " " , 0 ) ;
2023-09-13 00:16:02 +03:00
struct btree_trans * trans = bch2_trans_get ( c ) ;
2021-08-30 22:18:31 +03:00
struct btree_iter iter = { NULL } ;
2017-03-17 09:18:50 +03:00
struct bkey_s_c_xattr xattr ;
struct posix_acl * acl = NULL ;
2021-07-25 02:50:40 +03:00
struct bkey_s_c k ;
int ret ;
2017-03-17 09:18:50 +03:00
retry :
2023-09-13 00:16:02 +03:00
bch2_trans_begin ( trans ) ;
2017-03-17 09:18:50 +03:00
2023-09-13 00:16:02 +03:00
ret = bch2_hash_lookup ( trans , & iter , bch2_xattr_hash_desc ,
2023-07-07 05:47:42 +03:00
& hash , inode_inum ( inode ) , & search , 0 ) ;
2021-08-30 22:18:31 +03:00
if ( ret ) {
2023-05-28 02:59:59 +03:00
if ( ! bch2_err_matches ( ret , ENOENT ) )
2021-08-30 22:18:31 +03:00
acl = ERR_PTR ( ret ) ;
2017-03-17 09:18:50 +03:00
goto out ;
}
2021-08-30 22:18:31 +03:00
k = bch2_btree_iter_peek_slot ( & iter ) ;
2021-07-25 02:50:40 +03:00
ret = bkey_err ( k ) ;
if ( ret ) {
acl = ERR_PTR ( ret ) ;
goto out ;
}
xattr = bkey_s_c_to_xattr ( k ) ;
2023-09-13 00:16:02 +03:00
acl = bch2_acl_from_disk ( trans , xattr_val ( xattr . v ) ,
2017-03-17 09:18:50 +03:00
le16_to_cpu ( xattr . v - > x_val_len ) ) ;
if ( ! IS_ERR ( acl ) )
set_cached_acl ( & inode - > v , type , acl ) ;
out :
2023-05-28 10:44:38 +03:00
if ( bch2_err_matches ( PTR_ERR_OR_ZERO ( acl ) , BCH_ERR_transaction_restart ) )
goto retry ;
2023-09-13 00:16:02 +03:00
bch2_trans_iter_exit ( trans , & iter ) ;
bch2_trans_put ( trans ) ;
2017-03-17 09:18:50 +03:00
return acl ;
}
2021-03-16 07:28:17 +03:00
int bch2_set_acl_trans ( struct btree_trans * trans , subvol_inum inum ,
2017-03-17 09:18:50 +03:00
struct bch_inode_unpacked * inode_u ,
struct posix_acl * acl , int type )
{
2021-03-16 07:28:17 +03:00
struct bch_hash_info hash_info = bch2_hash_info_init ( trans - > c , inode_u ) ;
2017-03-17 09:18:50 +03:00
int ret ;
if ( type = = ACL_TYPE_DEFAULT & &
! S_ISDIR ( inode_u - > bi_mode ) )
return acl ? - EACCES : 0 ;
if ( acl ) {
struct bkey_i_xattr * xattr =
bch2_acl_to_xattr ( trans , acl , type ) ;
if ( IS_ERR ( xattr ) )
return PTR_ERR ( xattr ) ;
2021-03-16 07:28:17 +03:00
ret = bch2_hash_set ( trans , bch2_xattr_hash_desc , & hash_info ,
inum , & xattr - > k_i , 0 ) ;
2017-03-17 09:18:50 +03:00
} else {
struct xattr_search_key search =
X_SEARCH ( acl_to_xattr_type ( type ) , " " , 0 ) ;
2021-03-16 07:28:17 +03:00
ret = bch2_hash_delete ( trans , bch2_xattr_hash_desc , & hash_info ,
inum , & search ) ;
2017-03-17 09:18:50 +03:00
}
2023-05-28 02:59:59 +03:00
return bch2_err_matches ( ret , ENOENT ) ? 0 : ret ;
2017-03-17 09:18:50 +03:00
}
int bch2_set_acl ( struct mnt_idmap * idmap ,
struct dentry * dentry ,
2019-10-01 23:51:57 +03:00
struct posix_acl * _acl , int type )
2017-03-17 09:18:50 +03:00
{
struct bch_inode_info * inode = to_bch_ei ( dentry - > d_inode ) ;
struct bch_fs * c = inode - > v . i_sb - > s_fs_info ;
2023-09-13 00:16:02 +03:00
struct btree_trans * trans = bch2_trans_get ( c ) ;
2021-08-30 22:18:31 +03:00
struct btree_iter inode_iter = { NULL } ;
2017-03-17 09:18:50 +03:00
struct bch_inode_unpacked inode_u ;
2019-10-01 23:51:57 +03:00
struct posix_acl * acl ;
umode_t mode ;
2017-03-17 09:18:50 +03:00
int ret ;
2018-07-21 05:23:42 +03:00
mutex_lock ( & inode - > ei_update_lock ) ;
2019-10-01 23:51:57 +03:00
retry :
2023-09-13 00:16:02 +03:00
bch2_trans_begin ( trans ) ;
2019-10-01 23:51:57 +03:00
acl = _acl ;
2023-09-13 00:16:02 +03:00
ret = bch2_inode_peek ( trans , & inode_iter , & inode_u , inode_inum ( inode ) ,
2021-08-30 22:18:31 +03:00
BTREE_ITER_INTENT ) ;
2019-10-01 23:51:57 +03:00
if ( ret )
goto btree_err ;
2018-07-21 05:23:42 +03:00
2019-10-01 23:51:57 +03:00
mode = inode_u . bi_mode ;
if ( type = = ACL_TYPE_ACCESS ) {
2017-03-17 09:18:50 +03:00
ret = posix_acl_update_mode ( idmap , & inode - > v , & mode , & acl ) ;
if ( ret )
2021-03-20 03:29:11 +03:00
goto btree_err ;
2017-03-17 09:18:50 +03:00
}
2023-09-13 00:16:02 +03:00
ret = bch2_set_acl_trans ( trans , inode_inum ( inode ) , & inode_u , acl , type ) ;
2019-10-01 23:51:57 +03:00
if ( ret )
goto btree_err ;
inode_u . bi_ctime = bch2_current_time ( c ) ;
inode_u . bi_mode = mode ;
2023-09-13 00:16:02 +03:00
ret = bch2_inode_write ( trans , & inode_iter , & inode_u ) ? :
bch2_trans_commit ( trans , NULL , NULL , 0 ) ;
2019-10-01 23:51:57 +03:00
btree_err :
2023-09-13 00:16:02 +03:00
bch2_trans_iter_exit ( trans , & inode_iter ) ;
2021-03-20 03:29:11 +03:00
2022-07-18 06:06:38 +03:00
if ( bch2_err_matches ( ret , BCH_ERR_transaction_restart ) )
2017-03-17 09:18:50 +03:00
goto retry ;
if ( unlikely ( ret ) )
goto err ;
2023-09-13 00:16:02 +03:00
bch2_inode_update_after_write ( trans , inode , & inode_u ,
2017-03-17 09:18:50 +03:00
ATTR_CTIME | ATTR_MODE ) ;
set_cached_acl ( & inode - > v , type , acl ) ;
err :
2018-07-21 05:23:42 +03:00
mutex_unlock ( & inode - > ei_update_lock ) ;
2023-09-13 00:16:02 +03:00
bch2_trans_put ( trans ) ;
2017-03-17 09:18:50 +03:00
return ret ;
}
2021-03-16 07:28:17 +03:00
int bch2_acl_chmod ( struct btree_trans * trans , subvol_inum inum ,
2021-03-03 02:35:30 +03:00
struct bch_inode_unpacked * inode ,
2017-03-17 09:18:50 +03:00
umode_t mode ,
struct posix_acl * * new_acl )
{
2021-03-03 02:35:30 +03:00
struct bch_hash_info hash_info = bch2_hash_info_init ( trans - > c , inode ) ;
2023-07-07 05:47:42 +03:00
struct xattr_search_key search = X_SEARCH ( KEY_TYPE_XATTR_INDEX_POSIX_ACL_ACCESS , " " , 0 ) ;
2021-08-30 22:18:31 +03:00
struct btree_iter iter ;
2017-03-17 09:18:50 +03:00
struct bkey_s_c_xattr xattr ;
struct bkey_i_xattr * new ;
2023-09-15 15:55:40 +03:00
struct posix_acl * acl = NULL ;
2021-07-25 02:50:40 +03:00
struct bkey_s_c k ;
2021-03-20 03:29:11 +03:00
int ret ;
2017-03-17 09:18:50 +03:00
2021-08-30 22:18:31 +03:00
ret = bch2_hash_lookup ( trans , & iter , bch2_xattr_hash_desc ,
2023-07-07 05:47:42 +03:00
& hash_info , inum , & search , BTREE_ITER_INTENT ) ;
2021-03-20 03:29:11 +03:00
if ( ret )
2023-05-28 02:59:59 +03:00
return bch2_err_matches ( ret , ENOENT ) ? 0 : ret ;
2017-03-17 09:18:50 +03:00
2021-08-30 22:18:31 +03:00
k = bch2_btree_iter_peek_slot ( & iter ) ;
2021-07-25 02:50:40 +03:00
xattr = bkey_s_c_to_xattr ( k ) ;
if ( ret )
goto err ;
2023-05-28 10:44:38 +03:00
acl = bch2_acl_from_disk ( trans , xattr_val ( xattr . v ) ,
2017-03-17 09:18:50 +03:00
le16_to_cpu ( xattr . v - > x_val_len ) ) ;
2021-03-20 03:29:11 +03:00
ret = PTR_ERR_OR_ZERO ( acl ) ;
2021-06-24 04:52:41 +03:00
if ( IS_ERR_OR_NULL ( acl ) )
2021-03-20 03:29:11 +03:00
goto err ;
2017-03-17 09:18:50 +03:00
2023-05-28 10:44:38 +03:00
ret = allocate_dropping_locks_errcode ( trans ,
__posix_acl_chmod ( & acl , _gfp , mode ) ) ;
2017-03-17 09:18:50 +03:00
if ( ret )
goto err ;
new = bch2_acl_to_xattr ( trans , acl , ACL_TYPE_ACCESS ) ;
if ( IS_ERR ( new ) ) {
ret = PTR_ERR ( new ) ;
goto err ;
}
2021-08-30 22:18:31 +03:00
new - > k . p = iter . pos ;
ret = bch2_trans_update ( trans , & iter , & new - > k_i , 0 ) ;
2017-03-17 09:18:50 +03:00
* new_acl = acl ;
acl = NULL ;
err :
2021-08-30 22:18:31 +03:00
bch2_trans_iter_exit ( trans , & iter ) ;
2021-06-24 04:52:41 +03:00
if ( ! IS_ERR_OR_NULL ( acl ) )
kfree ( acl ) ;
2017-03-17 09:18:50 +03:00
return ret ;
}
# endif /* CONFIG_BCACHEFS_POSIX_ACL */