2021-11-02 16:12:01 +03:00
// SPDX-License-Identifier: LGPL-2.1
2010-09-27 22:57:39 +04:00
/*
* Copyright IBM Corporation , 2010
* Author Aneesh Kumar K . V < aneesh . kumar @ linux . vnet . ibm . com >
*/
# include <linux/module.h>
# include <linux/fs.h>
# include <net/9p/9p.h>
# include <net/9p/client.h>
# include <linux/slab.h>
2010-09-27 22:57:40 +04:00
# include <linux/sched.h>
2010-09-27 22:57:39 +04:00
# include <linux/posix_acl_xattr.h>
# include "xattr.h"
# include "acl.h"
2010-09-27 22:57:41 +04:00
# include "v9fs.h"
2011-02-28 14:34:01 +03:00
# include "v9fs_vfs.h"
2013-01-31 21:54:47 +04:00
# include "fid.h"
2010-09-27 22:57:39 +04:00
2022-09-22 18:17:04 +03:00
static struct posix_acl * v9fs_fid_get_acl ( struct p9_fid * fid , const char * name )
2010-09-27 22:57:39 +04:00
{
ssize_t size ;
void * value = NULL ;
2010-11-15 06:04:51 +03:00
struct posix_acl * acl = NULL ;
2010-09-27 22:57:39 +04:00
size = v9fs_fid_xattr_get ( fid , name , NULL , 0 ) ;
2022-09-22 18:17:04 +03:00
if ( size < 0 )
return ERR_PTR ( size ) ;
if ( size = = 0 )
return ERR_PTR ( - ENODATA ) ;
value = kzalloc ( size , GFP_NOFS ) ;
if ( ! value )
return ERR_PTR ( - ENOMEM ) ;
size = v9fs_fid_xattr_get ( fid , name , value , size ) ;
if ( size < 0 )
acl = ERR_PTR ( size ) ;
else if ( size = = 0 )
acl = ERR_PTR ( - ENODATA ) ;
else
acl = posix_acl_from_xattr ( & init_user_ns , value , size ) ;
2010-09-27 22:57:39 +04:00
kfree ( value ) ;
return acl ;
}
2022-09-22 18:17:04 +03:00
static struct posix_acl * v9fs_acl_get ( struct dentry * dentry , const char * name )
{
struct p9_fid * fid ;
struct posix_acl * acl = NULL ;
fid = v9fs_fid_lookup ( dentry ) ;
if ( IS_ERR ( fid ) )
return ERR_CAST ( fid ) ;
acl = v9fs_fid_get_acl ( fid , name ) ;
p9_fid_put ( fid ) ;
return acl ;
}
static struct posix_acl * __v9fs_get_acl ( struct p9_fid * fid , const char * name )
{
int retval ;
struct posix_acl * acl = NULL ;
acl = v9fs_fid_get_acl ( fid , name ) ;
if ( ! IS_ERR ( acl ) )
return acl ;
retval = PTR_ERR ( acl ) ;
if ( retval = = - ENODATA | | retval = = - ENOSYS | | retval = = - EOPNOTSUPP )
return NULL ;
/* map everything else to -EIO */
return ERR_PTR ( - EIO ) ;
}
2010-09-27 22:57:39 +04:00
int v9fs_get_acl ( struct inode * inode , struct p9_fid * fid )
{
int retval = 0 ;
struct posix_acl * pacl , * dacl ;
2010-09-27 22:57:41 +04:00
struct v9fs_session_info * v9ses ;
2010-09-27 22:57:39 +04:00
2010-09-27 22:57:41 +04:00
v9ses = v9fs_inode2v9ses ( inode ) ;
2011-01-26 02:40:54 +03:00
if ( ( ( v9ses - > flags & V9FS_ACCESS_MASK ) ! = V9FS_ACCESS_CLIENT ) | |
( ( v9ses - > flags & V9FS_ACL_MASK ) ! = V9FS_POSIX_ACL ) ) {
2010-09-27 22:57:41 +04:00
set_cached_acl ( inode , ACL_TYPE_DEFAULT , NULL ) ;
set_cached_acl ( inode , ACL_TYPE_ACCESS , NULL ) ;
return 0 ;
}
2010-09-27 22:57:39 +04:00
/* get the default/access acl values and cache them */
2015-12-02 16:44:35 +03:00
dacl = __v9fs_get_acl ( fid , XATTR_NAME_POSIX_ACL_DEFAULT ) ;
pacl = __v9fs_get_acl ( fid , XATTR_NAME_POSIX_ACL_ACCESS ) ;
2010-09-27 22:57:39 +04:00
if ( ! IS_ERR ( dacl ) & & ! IS_ERR ( pacl ) ) {
set_cached_acl ( inode , ACL_TYPE_DEFAULT , dacl ) ;
set_cached_acl ( inode , ACL_TYPE_ACCESS , pacl ) ;
} else
retval = - EIO ;
2011-01-14 02:28:39 +03:00
if ( ! IS_ERR ( dacl ) )
posix_acl_release ( dacl ) ;
if ( ! IS_ERR ( pacl ) )
posix_acl_release ( pacl ) ;
2010-09-27 22:57:39 +04:00
return retval ;
}
static struct posix_acl * v9fs_get_cached_acl ( struct inode * inode , int type )
{
struct posix_acl * acl ;
/*
* 9 p Always cache the acl value when
* instantiating the inode ( v9fs_inode_from_fid )
*/
acl = get_cached_acl ( inode , type ) ;
2016-03-24 16:38:37 +03:00
BUG_ON ( is_uncached_acl ( acl ) ) ;
2010-09-27 22:57:39 +04:00
return acl ;
}
2022-09-22 18:17:04 +03:00
struct posix_acl * v9fs_iop_get_inode_acl ( struct inode * inode , int type , bool rcu )
2010-09-27 22:57:39 +04:00
{
2010-09-27 22:57:41 +04:00
struct v9fs_session_info * v9ses ;
2021-08-18 23:08:24 +03:00
if ( rcu )
return ERR_PTR ( - ECHILD ) ;
2010-09-27 22:57:41 +04:00
v9ses = v9fs_inode2v9ses ( inode ) ;
2011-01-26 02:40:54 +03:00
if ( ( ( v9ses - > flags & V9FS_ACCESS_MASK ) ! = V9FS_ACCESS_CLIENT ) | |
( ( v9ses - > flags & V9FS_ACL_MASK ) ! = V9FS_POSIX_ACL ) ) {
2010-09-27 22:57:41 +04:00
/*
2011-01-26 02:40:54 +03:00
* On access = client and acl = on mode get the acl
2010-09-27 22:57:41 +04:00
* values from the server
*/
2011-07-23 19:37:31 +04:00
return NULL ;
2010-09-27 22:57:41 +04:00
}
2011-07-23 19:37:31 +04:00
return v9fs_get_cached_acl ( inode , type ) ;
2010-09-27 22:57:39 +04:00
}
2010-09-27 22:57:39 +04:00
2023-01-13 14:49:19 +03:00
struct posix_acl * v9fs_iop_get_acl ( struct mnt_idmap * idmap ,
2022-09-22 18:17:04 +03:00
struct dentry * dentry , int type )
{
struct v9fs_session_info * v9ses ;
v9ses = v9fs_dentry2v9ses ( dentry ) ;
/* We allow set/get/list of acl when access=client is not specified. */
if ( ( v9ses - > flags & V9FS_ACCESS_MASK ) ! = V9FS_ACCESS_CLIENT )
return v9fs_acl_get ( dentry , posix_acl_xattr_name ( type ) ) ;
return v9fs_get_cached_acl ( d_inode ( dentry ) , type ) ;
}
2023-01-13 14:49:20 +03:00
int v9fs_iop_set_acl ( struct mnt_idmap * idmap , struct dentry * dentry ,
2022-09-22 18:17:05 +03:00
struct posix_acl * acl , int type )
{
int retval ;
size_t size = 0 ;
void * value = NULL ;
const char * acl_name ;
struct v9fs_session_info * v9ses ;
struct inode * inode = d_inode ( dentry ) ;
if ( acl ) {
retval = posix_acl_valid ( inode - > i_sb - > s_user_ns , acl ) ;
if ( retval )
goto err_out ;
size = posix_acl_xattr_size ( acl - > a_count ) ;
value = kzalloc ( size , GFP_NOFS ) ;
if ( ! value ) {
retval = - ENOMEM ;
goto err_out ;
}
retval = posix_acl_to_xattr ( & init_user_ns , acl , value , size ) ;
if ( retval < 0 )
goto err_out ;
}
/*
* set the attribute on the remote . Without even looking at the
* xattr value . We leave it to the server to validate
*/
acl_name = posix_acl_xattr_name ( type ) ;
v9ses = v9fs_dentry2v9ses ( dentry ) ;
if ( ( v9ses - > flags & V9FS_ACCESS_MASK ) ! = V9FS_ACCESS_CLIENT ) {
retval = v9fs_xattr_set ( dentry , acl_name , value , size , 0 ) ;
goto err_out ;
}
if ( S_ISLNK ( inode - > i_mode ) ) {
retval = - EOPNOTSUPP ;
goto err_out ;
}
2023-01-13 14:49:26 +03:00
if ( ! inode_owner_or_capable ( & nop_mnt_idmap , inode ) ) {
2022-09-22 18:17:05 +03:00
retval = - EPERM ;
goto err_out ;
}
switch ( type ) {
case ACL_TYPE_ACCESS :
if ( acl ) {
struct iattr iattr = { } ;
struct posix_acl * acl_mode = acl ;
2023-01-13 14:49:24 +03:00
retval = posix_acl_update_mode ( & nop_mnt_idmap , inode ,
2022-09-22 18:17:05 +03:00
& iattr . ia_mode ,
& acl_mode ) ;
if ( retval )
goto err_out ;
if ( ! acl_mode ) {
/*
* ACL can be represented by the mode bits .
* So don ' t update ACL below .
*/
kfree ( value ) ;
value = NULL ;
size = 0 ;
}
iattr . ia_valid = ATTR_MODE ;
/*
* FIXME should we update ctime ?
* What is the following setxattr update the mode ?
*/
2023-01-13 14:49:11 +03:00
v9fs_vfs_setattr_dotl ( & nop_mnt_idmap , dentry , & iattr ) ;
2022-09-22 18:17:05 +03:00
}
break ;
case ACL_TYPE_DEFAULT :
if ( ! S_ISDIR ( inode - > i_mode ) ) {
retval = acl ? - EINVAL : 0 ;
goto err_out ;
}
break ;
}
retval = v9fs_xattr_set ( dentry , acl_name , value , size , 0 ) ;
if ( ! retval )
set_cached_acl ( inode , type , acl ) ;
err_out :
kfree ( value ) ;
return retval ;
}
2013-01-31 21:54:47 +04:00
static int v9fs_set_acl ( struct p9_fid * fid , int type , struct posix_acl * acl )
2010-09-27 22:57:40 +04:00
{
int retval ;
char * name ;
size_t size ;
void * buffer ;
2021-11-02 16:16:43 +03:00
2011-01-14 03:33:00 +03:00
if ( ! acl )
return 0 ;
2010-09-27 22:57:40 +04:00
/* Set a setxattr request to server */
size = posix_acl_xattr_size ( acl - > a_count ) ;
buffer = kmalloc ( size , GFP_KERNEL ) ;
if ( ! buffer )
return - ENOMEM ;
2012-09-11 07:17:44 +04:00
retval = posix_acl_to_xattr ( & init_user_ns , acl , buffer , size ) ;
2010-09-27 22:57:40 +04:00
if ( retval < 0 )
goto err_free_out ;
switch ( type ) {
case ACL_TYPE_ACCESS :
2015-12-02 16:44:35 +03:00
name = XATTR_NAME_POSIX_ACL_ACCESS ;
2010-09-27 22:57:40 +04:00
break ;
case ACL_TYPE_DEFAULT :
2015-12-02 16:44:35 +03:00
name = XATTR_NAME_POSIX_ACL_DEFAULT ;
2010-09-27 22:57:40 +04:00
break ;
default :
BUG ( ) ;
}
2013-01-31 21:54:47 +04:00
retval = v9fs_fid_xattr_set ( fid , name , buffer , size , 0 ) ;
2010-09-27 22:57:40 +04:00
err_free_out :
kfree ( buffer ) ;
return retval ;
}
2013-01-31 21:58:16 +04:00
int v9fs_acl_chmod ( struct inode * inode , struct p9_fid * fid )
2010-09-27 22:57:40 +04:00
{
int retval = 0 ;
2011-07-23 08:18:02 +04:00
struct posix_acl * acl ;
2010-09-27 22:57:40 +04:00
if ( S_ISLNK ( inode - > i_mode ) )
return - EOPNOTSUPP ;
acl = v9fs_get_cached_acl ( inode , ACL_TYPE_ACCESS ) ;
if ( acl ) {
2013-12-20 17:16:41 +04:00
retval = __posix_acl_chmod ( & acl , GFP_KERNEL , inode - > i_mode ) ;
2011-07-23 08:18:02 +04:00
if ( retval )
return retval ;
2013-01-31 21:46:55 +04:00
set_cached_acl ( inode , ACL_TYPE_ACCESS , acl ) ;
2013-01-31 21:54:47 +04:00
retval = v9fs_set_acl ( fid , ACL_TYPE_ACCESS , acl ) ;
2010-09-27 22:57:40 +04:00
posix_acl_release ( acl ) ;
}
return retval ;
}
2013-01-31 22:45:39 +04:00
int v9fs_set_create_acl ( struct inode * inode , struct p9_fid * fid ,
2013-01-31 22:31:23 +04:00
struct posix_acl * dacl , struct posix_acl * acl )
2010-09-27 22:57:40 +04:00
{
2013-01-31 22:45:39 +04:00
set_cached_acl ( inode , ACL_TYPE_DEFAULT , dacl ) ;
set_cached_acl ( inode , ACL_TYPE_ACCESS , acl ) ;
v9fs_set_acl ( fid , ACL_TYPE_DEFAULT , dacl ) ;
v9fs_set_acl ( fid , ACL_TYPE_ACCESS , acl ) ;
2010-09-27 22:57:40 +04:00
return 0 ;
}
2013-01-31 22:31:23 +04:00
void v9fs_put_acl ( struct posix_acl * dacl ,
struct posix_acl * acl )
{
posix_acl_release ( dacl ) ;
posix_acl_release ( acl ) ;
}
2011-07-24 02:37:50 +04:00
int v9fs_acl_mode ( struct inode * dir , umode_t * modep ,
2010-09-27 22:57:40 +04:00
struct posix_acl * * dpacl , struct posix_acl * * pacl )
{
int retval = 0 ;
2011-07-24 02:37:50 +04:00
umode_t mode = * modep ;
2010-09-27 22:57:40 +04:00
struct posix_acl * acl = NULL ;
if ( ! S_ISLNK ( mode ) ) {
acl = v9fs_get_cached_acl ( dir , ACL_TYPE_DEFAULT ) ;
if ( IS_ERR ( acl ) )
return PTR_ERR ( acl ) ;
if ( ! acl )
mode & = ~ current_umask ( ) ;
}
if ( acl ) {
if ( S_ISDIR ( mode ) )
2011-07-23 10:28:13 +04:00
* dpacl = posix_acl_dup ( acl ) ;
2013-12-20 17:16:42 +04:00
retval = __posix_acl_create ( & acl , GFP_NOFS , & mode ) ;
2011-07-23 11:10:32 +04:00
if ( retval < 0 )
return retval ;
2010-09-27 22:57:40 +04:00
if ( retval > 0 )
2011-07-23 11:10:32 +04:00
* pacl = acl ;
2011-07-23 10:28:13 +04:00
else
2011-07-23 11:10:32 +04:00
posix_acl_release ( acl ) ;
2010-09-27 22:57:40 +04:00
}
* modep = mode ;
return 0 ;
}