2010-09-27 22:57:39 +04:00
/*
* Copyright IBM Corporation , 2010
* Author Aneesh Kumar K . V < aneesh . kumar @ linux . vnet . ibm . com >
*
* This program is free software ; you can redistribute it and / or modify it
* under the terms of version 2.1 of the GNU Lesser General Public License
* as published by the Free Software Foundation .
*
* This program is distributed in the hope that it would be useful , but
* WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE .
*
*/
# 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
static struct posix_acl * __v9fs_get_acl ( struct p9_fid * fid , char * name )
{
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 ) ;
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-11 07:17:44 +04:00
acl = posix_acl_from_xattr ( & init_user_ns , value , size ) ;
2010-09-27 22:57:39 +04:00
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-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 ;
}
2011-07-23 19:37:31 +04:00
struct posix_acl * v9fs_iop_get_acl ( struct inode * inode , int type )
2010-09-27 22:57:39 +04:00
{
2010-09-27 22:57:41 +04:00
struct v9fs_session_info * v9ses ;
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
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 ;
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 ;
}
2015-10-04 20:18:51 +03:00
static int v9fs_xattr_get_acl ( const struct xattr_handler * handler ,
2016-04-11 03:48:24 +03:00
struct dentry * dentry , struct inode * inode ,
const char * name , void * buffer , size_t size )
2010-09-27 22:57:39 +04:00
{
2010-09-27 22:57:41 +04:00
struct v9fs_session_info * v9ses ;
2010-09-27 22:57:39 +04:00
struct posix_acl * acl ;
int error ;
2011-03-08 14:09:50 +03:00
v9ses = v9fs_dentry2v9ses ( dentry ) ;
2010-09-27 22:57:41 +04:00
/*
* 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 16:44:36 +03:00
return v9fs_xattr_get ( dentry , handler - > name , buffer , size ) ;
2010-09-27 22:57:41 +04:00
2016-04-11 03:48:24 +03:00
acl = v9fs_get_cached_acl ( inode , handler - > flags ) ;
2010-09-27 22:57:39 +04:00
if ( IS_ERR ( acl ) )
return PTR_ERR ( acl ) ;
if ( acl = = NULL )
return - ENODATA ;
2012-09-11 07:17:44 +04:00
error = posix_acl_to_xattr ( & init_user_ns , acl , buffer , size ) ;
2010-09-27 22:57:39 +04:00
posix_acl_release ( acl ) ;
return error ;
}
2015-10-04 20:18:51 +03:00
static int v9fs_xattr_set_acl ( const struct xattr_handler * handler ,
2021-01-21 16:19:27 +03:00
struct user_namespace * mnt_userns ,
2016-05-27 17:19:30 +03:00
struct dentry * dentry , struct inode * inode ,
const char * name , const void * value ,
size_t size , int flags )
2010-09-27 22:57:39 +04:00
{
2010-09-27 22:57:40 +04:00
int retval ;
struct posix_acl * acl ;
2010-09-27 22:57:41 +04:00
struct v9fs_session_info * v9ses ;
2010-09-27 22:57:40 +04:00
2011-03-08 14:09:50 +03:00
v9ses = v9fs_dentry2v9ses ( dentry ) ;
2010-09-27 22:57:41 +04:00
/*
* 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 16:44:36 +03:00
return v9fs_xattr_set ( dentry , handler - > name , value , size ,
2015-10-04 20:18:52 +03:00
flags ) ;
2010-09-27 22:57:41 +04:00
2010-09-27 22:57:40 +04:00
if ( S_ISLNK ( inode - > i_mode ) )
return - EOPNOTSUPP ;
2021-01-21 16:19:25 +03:00
if ( ! inode_owner_or_capable ( & init_user_ns , inode ) )
2010-09-27 22:57:40 +04:00
return - EPERM ;
if ( value ) {
/* update the cached acl value */
2012-09-11 07:17:44 +04:00
acl = posix_acl_from_xattr ( & init_user_ns , value , size ) ;
2010-09-27 22:57:40 +04:00
if ( IS_ERR ( acl ) )
return PTR_ERR ( acl ) ;
else if ( acl ) {
2016-06-28 00:04:06 +03:00
retval = posix_acl_valid ( inode - > i_sb - > s_user_ns , acl ) ;
2010-09-27 22:57:40 +04:00
if ( retval )
goto err_out ;
}
} else
acl = NULL ;
2015-10-04 20:18:51 +03:00
switch ( handler - > flags ) {
2010-09-27 22:57:40 +04:00
case ACL_TYPE_ACCESS :
if ( acl ) {
2018-09-07 18:10:57 +03:00
struct iattr iattr = { 0 } ;
2017-02-23 02:40:53 +03:00
struct posix_acl * old_acl = acl ;
2016-09-19 18:39:09 +03:00
2021-01-21 16:19:43 +03:00
retval = posix_acl_update_mode ( & init_user_ns , inode ,
2021-01-21 16:19:27 +03:00
& iattr . ia_mode , & acl ) ;
2016-09-19 18:39:09 +03:00
if ( retval )
2010-09-27 22:57:40 +04:00
goto err_out ;
2016-09-19 18:39:09 +03:00
if ( ! acl ) {
/*
* ACL can be represented
* by the mode bits . So don ' t
* update ACL .
2010-09-27 22:57:40 +04:00
*/
2017-02-23 02:40:53 +03:00
posix_acl_release ( old_acl ) ;
2016-09-19 18:39:09 +03:00
value = NULL ;
size = 0 ;
2010-09-27 22:57:40 +04:00
}
2016-09-19 18:39:09 +03:00
iattr . ia_valid = ATTR_MODE ;
/* FIXME should we update ctime ?
* What is the following setxattr update the
* mode ?
*/
2021-01-21 16:19:43 +03:00
v9fs_vfs_setattr_dotl ( & init_user_ns , dentry , & iattr ) ;
2010-09-27 22:57:40 +04:00
}
break ;
case ACL_TYPE_DEFAULT :
if ( ! S_ISDIR ( inode - > i_mode ) ) {
2010-12-10 09:49:31 +03:00
retval = acl ? - EINVAL : 0 ;
2010-09-27 22:57:40 +04:00
goto err_out ;
}
break ;
default :
BUG ( ) ;
}
2015-12-02 16:44:36 +03:00
retval = v9fs_xattr_set ( dentry , handler - > name , value , size , flags ) ;
2010-09-27 22:57:40 +04:00
if ( ! retval )
2015-10-04 20:18:51 +03:00
set_cached_acl ( inode , handler - > flags , acl ) ;
2010-09-27 22:57:40 +04:00
err_out :
posix_acl_release ( acl ) ;
return retval ;
2010-09-27 22:57:39 +04:00
}
const struct xattr_handler v9fs_xattr_acl_access_handler = {
2015-12-02 16:44:36 +03:00
. name = XATTR_NAME_POSIX_ACL_ACCESS ,
2010-09-27 22:57:39 +04:00
. 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 16:44:36 +03:00
. name = XATTR_NAME_POSIX_ACL_DEFAULT ,
2010-09-27 22:57:39 +04:00
. flags = ACL_TYPE_DEFAULT ,
. get = v9fs_xattr_get_acl ,
. set = v9fs_xattr_set_acl ,
} ;