2021-11-02 22:12:01 +09:00
// SPDX-License-Identifier: LGPL-2.1
2010-09-28 00:27:39 +05:30
/*
* 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-28 00:27:40 +05:30
# include <linux/sched.h>
2010-09-28 00:27:39 +05:30
# include <linux/posix_acl_xattr.h>
# include "xattr.h"
# include "acl.h"
2010-09-28 00:27:41 +05:30
# include "v9fs.h"
2011-02-28 17:04:01 +05:30
# include "v9fs_vfs.h"
2013-01-31 12:54:47 -05:00
# include "fid.h"
2010-09-28 00:27:39 +05:30
static struct posix_acl * __v9fs_get_acl ( struct p9_fid * fid , char * name )
{
ssize_t size ;
void * value = NULL ;
2010-11-15 03:04:51 +00:00
struct posix_acl * acl = NULL ;
2010-09-28 00:27:39 +05:30
size = v9fs_fid_xattr_get ( fid , name , NULL , 0 ) ;
if ( size > 0 ) {
value = kzalloc ( size , GFP_NOFS ) ;
if ( ! value )
return ERR_PTR ( - ENOMEM ) ;
size = v9fs_fid_xattr_get ( fid , name , value , size ) ;
if ( size > 0 ) {
2012-09-10 20:17:44 -07:00
acl = posix_acl_from_xattr ( & init_user_ns , value , size ) ;
2010-09-28 00:27:39 +05:30
if ( IS_ERR ( acl ) )
goto err_out ;
}
} else if ( size = = - ENODATA | | size = = 0 | |
size = = - ENOSYS | | size = = - EOPNOTSUPP ) {
acl = NULL ;
} else
acl = ERR_PTR ( - EIO ) ;
err_out :
kfree ( value ) ;
return acl ;
}
int v9fs_get_acl ( struct inode * inode , struct p9_fid * fid )
{
int retval = 0 ;
struct posix_acl * pacl , * dacl ;
2010-09-28 00:27:41 +05:30
struct v9fs_session_info * v9ses ;
2010-09-28 00:27:39 +05:30
2010-09-28 00:27:41 +05:30
v9ses = v9fs_inode2v9ses ( inode ) ;
2011-01-25 15:40:54 -08:00
if ( ( ( v9ses - > flags & V9FS_ACCESS_MASK ) ! = V9FS_ACCESS_CLIENT ) | |
( ( v9ses - > flags & V9FS_ACL_MASK ) ! = V9FS_POSIX_ACL ) ) {
2010-09-28 00:27:41 +05:30
set_cached_acl ( inode , ACL_TYPE_DEFAULT , NULL ) ;
set_cached_acl ( inode , ACL_TYPE_ACCESS , NULL ) ;
return 0 ;
}
2010-09-28 00:27:39 +05:30
/* get the default/access acl values and cache them */
2015-12-02 14:44:35 +01:00
dacl = __v9fs_get_acl ( fid , XATTR_NAME_POSIX_ACL_DEFAULT ) ;
pacl = __v9fs_get_acl ( fid , XATTR_NAME_POSIX_ACL_ACCESS ) ;
2010-09-28 00:27:39 +05:30
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-13 15:28:39 -08:00
if ( ! IS_ERR ( dacl ) )
posix_acl_release ( dacl ) ;
if ( ! IS_ERR ( pacl ) )
posix_acl_release ( pacl ) ;
2010-09-28 00:27:39 +05:30
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 14:38:37 +01:00
BUG_ON ( is_uncached_acl ( acl ) ) ;
2010-09-28 00:27:39 +05:30
return acl ;
}
2021-08-18 22:08:24 +02:00
struct posix_acl * v9fs_iop_get_acl ( struct inode * inode , int type , bool rcu )
2010-09-28 00:27:39 +05:30
{
2010-09-28 00:27:41 +05:30
struct v9fs_session_info * v9ses ;
2021-08-18 22:08:24 +02:00
if ( rcu )
return ERR_PTR ( - ECHILD ) ;
2010-09-28 00:27:41 +05:30
v9ses = v9fs_inode2v9ses ( inode ) ;
2011-01-25 15:40:54 -08:00
if ( ( ( v9ses - > flags & V9FS_ACCESS_MASK ) ! = V9FS_ACCESS_CLIENT ) | |
( ( v9ses - > flags & V9FS_ACL_MASK ) ! = V9FS_POSIX_ACL ) ) {
2010-09-28 00:27:41 +05:30
/*
2011-01-25 15:40:54 -08:00
* On access = client and acl = on mode get the acl
2010-09-28 00:27:41 +05:30
* values from the server
*/
2011-07-23 17:37:31 +02:00
return NULL ;
2010-09-28 00:27:41 +05:30
}
2011-07-23 17:37:31 +02:00
return v9fs_get_cached_acl ( inode , type ) ;
2010-09-28 00:27:39 +05:30
}
2010-09-28 00:27:39 +05:30
2013-01-31 12:54:47 -05:00
static int v9fs_set_acl ( struct p9_fid * fid , int type , struct posix_acl * acl )
2010-09-28 00:27:40 +05:30
{
int retval ;
char * name ;
size_t size ;
void * buffer ;
2021-11-02 22:16:43 +09:00
2011-01-13 16:33:00 -08:00
if ( ! acl )
return 0 ;
2010-09-28 00:27:40 +05:30
/* 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-10 20:17:44 -07:00
retval = posix_acl_to_xattr ( & init_user_ns , acl , buffer , size ) ;
2010-09-28 00:27:40 +05:30
if ( retval < 0 )
goto err_free_out ;
switch ( type ) {
case ACL_TYPE_ACCESS :
2015-12-02 14:44:35 +01:00
name = XATTR_NAME_POSIX_ACL_ACCESS ;
2010-09-28 00:27:40 +05:30
break ;
case ACL_TYPE_DEFAULT :
2015-12-02 14:44:35 +01:00
name = XATTR_NAME_POSIX_ACL_DEFAULT ;
2010-09-28 00:27:40 +05:30
break ;
default :
BUG ( ) ;
}
2013-01-31 12:54:47 -05:00
retval = v9fs_fid_xattr_set ( fid , name , buffer , size , 0 ) ;
2010-09-28 00:27:40 +05:30
err_free_out :
kfree ( buffer ) ;
return retval ;
}
2013-01-31 12:58:16 -05:00
int v9fs_acl_chmod ( struct inode * inode , struct p9_fid * fid )
2010-09-28 00:27:40 +05:30
{
int retval = 0 ;
2011-07-23 00:18:02 -04:00
struct posix_acl * acl ;
2010-09-28 00:27:40 +05:30
if ( S_ISLNK ( inode - > i_mode ) )
return - EOPNOTSUPP ;
acl = v9fs_get_cached_acl ( inode , ACL_TYPE_ACCESS ) ;
if ( acl ) {
2013-12-20 05:16:41 -08:00
retval = __posix_acl_chmod ( & acl , GFP_KERNEL , inode - > i_mode ) ;
2011-07-23 00:18:02 -04:00
if ( retval )
return retval ;
2013-01-31 12:46:55 -05:00
set_cached_acl ( inode , ACL_TYPE_ACCESS , acl ) ;
2013-01-31 12:54:47 -05:00
retval = v9fs_set_acl ( fid , ACL_TYPE_ACCESS , acl ) ;
2010-09-28 00:27:40 +05:30
posix_acl_release ( acl ) ;
}
return retval ;
}
2013-01-31 13:45:39 -05:00
int v9fs_set_create_acl ( struct inode * inode , struct p9_fid * fid ,
2013-01-31 13:31:23 -05:00
struct posix_acl * dacl , struct posix_acl * acl )
2010-09-28 00:27:40 +05:30
{
2013-01-31 13:45:39 -05: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-28 00:27:40 +05:30
return 0 ;
}
2013-01-31 13:31:23 -05:00
void v9fs_put_acl ( struct posix_acl * dacl ,
struct posix_acl * acl )
{
posix_acl_release ( dacl ) ;
posix_acl_release ( acl ) ;
}
2011-07-23 18:37:50 -04:00
int v9fs_acl_mode ( struct inode * dir , umode_t * modep ,
2010-09-28 00:27:40 +05:30
struct posix_acl * * dpacl , struct posix_acl * * pacl )
{
int retval = 0 ;
2011-07-23 18:37:50 -04:00
umode_t mode = * modep ;
2010-09-28 00:27:40 +05:30
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 02:28:13 -04:00
* dpacl = posix_acl_dup ( acl ) ;
2013-12-20 05:16:42 -08:00
retval = __posix_acl_create ( & acl , GFP_NOFS , & mode ) ;
2011-07-23 03:10:32 -04:00
if ( retval < 0 )
return retval ;
2010-09-28 00:27:40 +05:30
if ( retval > 0 )
2011-07-23 03:10:32 -04:00
* pacl = acl ;
2011-07-23 02:28:13 -04:00
else
2011-07-23 03:10:32 -04:00
posix_acl_release ( acl ) ;
2010-09-28 00:27:40 +05:30
}
* modep = mode ;
return 0 ;
}
2015-10-04 19:18:51 +02:00
static int v9fs_xattr_get_acl ( const struct xattr_handler * handler ,
2016-04-10 20:48:24 -04:00
struct dentry * dentry , struct inode * inode ,
const char * name , void * buffer , size_t size )
2010-09-28 00:27:39 +05:30
{
2010-09-28 00:27:41 +05:30
struct v9fs_session_info * v9ses ;
2010-09-28 00:27:39 +05:30
struct posix_acl * acl ;
int error ;
2011-03-08 16:39:50 +05:30
v9ses = v9fs_dentry2v9ses ( dentry ) ;
2010-09-28 00:27:41 +05:30
/*
* We allow set / get / list of acl when access = client is not specified
*/
if ( ( v9ses - > flags & V9FS_ACCESS_MASK ) ! = V9FS_ACCESS_CLIENT )
2015-12-02 14:44:36 +01:00
return v9fs_xattr_get ( dentry , handler - > name , buffer , size ) ;
2010-09-28 00:27:41 +05:30
2016-04-10 20:48:24 -04:00
acl = v9fs_get_cached_acl ( inode , handler - > flags ) ;
2010-09-28 00:27:39 +05:30
if ( IS_ERR ( acl ) )
return PTR_ERR ( acl ) ;
if ( acl = = NULL )
return - ENODATA ;
2012-09-10 20:17:44 -07:00
error = posix_acl_to_xattr ( & init_user_ns , acl , buffer , size ) ;
2010-09-28 00:27:39 +05:30
posix_acl_release ( acl ) ;
return error ;
}
2015-10-04 19:18:51 +02:00
static int v9fs_xattr_set_acl ( const struct xattr_handler * handler ,
2021-01-21 14:19:27 +01:00
struct user_namespace * mnt_userns ,
2016-05-27 10:19:30 -04:00
struct dentry * dentry , struct inode * inode ,
const char * name , const void * value ,
size_t size , int flags )
2010-09-28 00:27:39 +05:30
{
2010-09-28 00:27:40 +05:30
int retval ;
struct posix_acl * acl ;
2010-09-28 00:27:41 +05:30
struct v9fs_session_info * v9ses ;
2010-09-28 00:27:40 +05:30
2011-03-08 16:39:50 +05:30
v9ses = v9fs_dentry2v9ses ( dentry ) ;
2010-09-28 00:27:41 +05:30
/*
* set the attribute on the remote . Without even looking at the
* xattr value . We leave it to the server to validate
*/
if ( ( v9ses - > flags & V9FS_ACCESS_MASK ) ! = V9FS_ACCESS_CLIENT )
2015-12-02 14:44:36 +01:00
return v9fs_xattr_set ( dentry , handler - > name , value , size ,
2015-10-04 19:18:52 +02:00
flags ) ;
2010-09-28 00:27:41 +05:30
2010-09-28 00:27:40 +05:30
if ( S_ISLNK ( inode - > i_mode ) )
return - EOPNOTSUPP ;
2021-01-21 14:19:25 +01:00
if ( ! inode_owner_or_capable ( & init_user_ns , inode ) )
2010-09-28 00:27:40 +05:30
return - EPERM ;
if ( value ) {
/* update the cached acl value */
2012-09-10 20:17:44 -07:00
acl = posix_acl_from_xattr ( & init_user_ns , value , size ) ;
2010-09-28 00:27:40 +05:30
if ( IS_ERR ( acl ) )
return PTR_ERR ( acl ) ;
else if ( acl ) {
2016-06-27 16:04:06 -05:00
retval = posix_acl_valid ( inode - > i_sb - > s_user_ns , acl ) ;
2010-09-28 00:27:40 +05:30
if ( retval )
goto err_out ;
}
} else
acl = NULL ;
2015-10-04 19:18:51 +02:00
switch ( handler - > flags ) {
2010-09-28 00:27:40 +05:30
case ACL_TYPE_ACCESS :
if ( acl ) {
2018-09-08 00:10:57 +09:00
struct iattr iattr = { 0 } ;
2017-02-22 15:40:53 -08:00
struct posix_acl * old_acl = acl ;
2016-09-19 17:39:09 +02:00
2021-01-21 14:19:43 +01:00
retval = posix_acl_update_mode ( & init_user_ns , inode ,
2021-01-21 14:19:27 +01:00
& iattr . ia_mode , & acl ) ;
2016-09-19 17:39:09 +02:00
if ( retval )
2010-09-28 00:27:40 +05:30
goto err_out ;
2016-09-19 17:39:09 +02:00
if ( ! acl ) {
/*
* ACL can be represented
* by the mode bits . So don ' t
* update ACL .
2010-09-28 00:27:40 +05:30
*/
2017-02-22 15:40:53 -08:00
posix_acl_release ( old_acl ) ;
2016-09-19 17:39:09 +02:00
value = NULL ;
size = 0 ;
2010-09-28 00:27:40 +05:30
}
2016-09-19 17:39:09 +02:00
iattr . ia_valid = ATTR_MODE ;
/* FIXME should we update ctime ?
* What is the following setxattr update the
* mode ?
*/
2021-01-21 14:19:43 +01:00
v9fs_vfs_setattr_dotl ( & init_user_ns , dentry , & iattr ) ;
2010-09-28 00:27:40 +05:30
}
break ;
case ACL_TYPE_DEFAULT :
if ( ! S_ISDIR ( inode - > i_mode ) ) {
2010-12-10 12:19:31 +05:30
retval = acl ? - EINVAL : 0 ;
2010-09-28 00:27:40 +05:30
goto err_out ;
}
break ;
default :
BUG ( ) ;
}
2015-12-02 14:44:36 +01:00
retval = v9fs_xattr_set ( dentry , handler - > name , value , size , flags ) ;
2010-09-28 00:27:40 +05:30
if ( ! retval )
2015-10-04 19:18:51 +02:00
set_cached_acl ( inode , handler - > flags , acl ) ;
2010-09-28 00:27:40 +05:30
err_out :
posix_acl_release ( acl ) ;
return retval ;
2010-09-28 00:27:39 +05:30
}
const struct xattr_handler v9fs_xattr_acl_access_handler = {
2015-12-02 14:44:36 +01:00
. name = XATTR_NAME_POSIX_ACL_ACCESS ,
2010-09-28 00:27:39 +05:30
. flags = ACL_TYPE_ACCESS ,
. get = v9fs_xattr_get_acl ,
. set = v9fs_xattr_set_acl ,
} ;
const struct xattr_handler v9fs_xattr_acl_default_handler = {
2015-12-02 14:44:36 +01:00
. name = XATTR_NAME_POSIX_ACL_DEFAULT ,
2010-09-28 00:27:39 +05:30
. flags = ACL_TYPE_DEFAULT ,
. get = v9fs_xattr_get_acl ,
. set = v9fs_xattr_set_acl ,
} ;