2005-06-22 21:16:27 +04:00
# include <linux/fs.h>
# include <linux/nfs.h>
# include <linux/nfs3.h>
# include <linux/nfs_fs.h>
2005-06-29 07:44:58 +04:00
# include <linux/posix_acl_xattr.h>
2005-06-22 21:16:27 +04:00
# include <linux/nfsacl.h>
# define NFSDBG_FACILITY NFSDBG_PROC
ssize_t nfs3_listxattr ( struct dentry * dentry , char * buffer , size_t size )
{
struct inode * inode = dentry - > d_inode ;
struct posix_acl * acl ;
int pos = 0 , len = 0 ;
# define output(s) do { \
if ( pos + sizeof ( s ) < = size ) { \
memcpy ( buffer + pos , s , sizeof ( s ) ) ; \
pos + = sizeof ( s ) ; \
} \
len + = sizeof ( s ) ; \
} while ( 0 )
acl = nfs3_proc_getacl ( inode , ACL_TYPE_ACCESS ) ;
if ( IS_ERR ( acl ) )
return PTR_ERR ( acl ) ;
if ( acl ) {
output ( " system.posix_acl_access " ) ;
posix_acl_release ( acl ) ;
}
if ( S_ISDIR ( inode - > i_mode ) ) {
acl = nfs3_proc_getacl ( inode , ACL_TYPE_DEFAULT ) ;
if ( IS_ERR ( acl ) )
return PTR_ERR ( acl ) ;
if ( acl ) {
output ( " system.posix_acl_default " ) ;
posix_acl_release ( acl ) ;
}
}
# undef output
if ( ! buffer | | len < = size )
return len ;
return - ERANGE ;
}
ssize_t nfs3_getxattr ( struct dentry * dentry , const char * name ,
void * buffer , size_t size )
{
struct inode * inode = dentry - > d_inode ;
struct posix_acl * acl ;
int type , error = 0 ;
2005-06-29 07:44:58 +04:00
if ( strcmp ( name , POSIX_ACL_XATTR_ACCESS ) = = 0 )
2005-06-22 21:16:27 +04:00
type = ACL_TYPE_ACCESS ;
2005-06-29 07:44:58 +04:00
else if ( strcmp ( name , POSIX_ACL_XATTR_DEFAULT ) = = 0 )
2005-06-22 21:16:27 +04:00
type = ACL_TYPE_DEFAULT ;
else
return - EOPNOTSUPP ;
acl = nfs3_proc_getacl ( inode , type ) ;
if ( IS_ERR ( acl ) )
return PTR_ERR ( acl ) ;
else if ( acl ) {
if ( type = = ACL_TYPE_ACCESS & & acl - > a_count = = 0 )
error = - ENODATA ;
else
error = posix_acl_to_xattr ( acl , buffer , size ) ;
posix_acl_release ( acl ) ;
} else
error = - ENODATA ;
return error ;
}
int nfs3_setxattr ( struct dentry * dentry , const char * name ,
const void * value , size_t size , int flags )
{
struct inode * inode = dentry - > d_inode ;
struct posix_acl * acl ;
int type , error ;
2005-06-29 07:44:58 +04:00
if ( strcmp ( name , POSIX_ACL_XATTR_ACCESS ) = = 0 )
2005-06-22 21:16:27 +04:00
type = ACL_TYPE_ACCESS ;
2005-06-29 07:44:58 +04:00
else if ( strcmp ( name , POSIX_ACL_XATTR_DEFAULT ) = = 0 )
2005-06-22 21:16:27 +04:00
type = ACL_TYPE_DEFAULT ;
else
return - EOPNOTSUPP ;
acl = posix_acl_from_xattr ( value , size ) ;
if ( IS_ERR ( acl ) )
return PTR_ERR ( acl ) ;
error = nfs3_proc_setacl ( inode , type , acl ) ;
posix_acl_release ( acl ) ;
return error ;
}
int nfs3_removexattr ( struct dentry * dentry , const char * name )
{
struct inode * inode = dentry - > d_inode ;
int type ;
2005-06-29 07:44:58 +04:00
if ( strcmp ( name , POSIX_ACL_XATTR_ACCESS ) = = 0 )
2005-06-22 21:16:27 +04:00
type = ACL_TYPE_ACCESS ;
2005-06-29 07:44:58 +04:00
else if ( strcmp ( name , POSIX_ACL_XATTR_DEFAULT ) = = 0 )
2005-06-22 21:16:27 +04:00
type = ACL_TYPE_DEFAULT ;
else
return - EOPNOTSUPP ;
return nfs3_proc_setacl ( inode , type , NULL ) ;
}
2005-06-22 21:16:27 +04:00
static void __nfs3_forget_cached_acls ( struct nfs_inode * nfsi )
{
2005-06-22 21:16:27 +04:00
if ( ! IS_ERR ( nfsi - > acl_access ) ) {
2005-06-22 21:16:27 +04:00
posix_acl_release ( nfsi - > acl_access ) ;
nfsi - > acl_access = ERR_PTR ( - EAGAIN ) ;
}
2005-06-22 21:16:27 +04:00
if ( ! IS_ERR ( nfsi - > acl_default ) ) {
2005-06-22 21:16:27 +04:00
posix_acl_release ( nfsi - > acl_default ) ;
nfsi - > acl_default = ERR_PTR ( - EAGAIN ) ;
}
}
void nfs3_forget_cached_acls ( struct inode * inode )
{
dprintk ( " NFS: nfs3_forget_cached_acls(%s/%ld) \n " , inode - > i_sb - > s_id ,
inode - > i_ino ) ;
spin_lock ( & inode - > i_lock ) ;
__nfs3_forget_cached_acls ( NFS_I ( inode ) ) ;
spin_unlock ( & inode - > i_lock ) ;
}
static struct posix_acl * nfs3_get_cached_acl ( struct inode * inode , int type )
{
struct nfs_inode * nfsi = NFS_I ( inode ) ;
2005-06-22 21:16:27 +04:00
struct posix_acl * acl = ERR_PTR ( - EINVAL ) ;
2005-06-22 21:16:27 +04:00
spin_lock ( & inode - > i_lock ) ;
switch ( type ) {
case ACL_TYPE_ACCESS :
acl = nfsi - > acl_access ;
break ;
case ACL_TYPE_DEFAULT :
acl = nfsi - > acl_default ;
break ;
default :
2005-06-22 21:16:27 +04:00
goto out ;
2005-06-22 21:16:27 +04:00
}
2005-06-22 21:16:27 +04:00
if ( IS_ERR ( acl ) )
2005-06-22 21:16:27 +04:00
acl = ERR_PTR ( - EAGAIN ) ;
else
acl = posix_acl_dup ( acl ) ;
2005-06-22 21:16:27 +04:00
out :
2005-06-22 21:16:27 +04:00
spin_unlock ( & inode - > i_lock ) ;
dprintk ( " NFS: nfs3_get_cached_acl(%s/%ld, %d) = %p \n " , inode - > i_sb - > s_id ,
inode - > i_ino , type , acl ) ;
return acl ;
}
static void nfs3_cache_acls ( struct inode * inode , struct posix_acl * acl ,
struct posix_acl * dfacl )
{
struct nfs_inode * nfsi = NFS_I ( inode ) ;
dprintk ( " nfs3_cache_acls(%s/%ld, %p, %p) \n " , inode - > i_sb - > s_id ,
inode - > i_ino , acl , dfacl ) ;
spin_lock ( & inode - > i_lock ) ;
__nfs3_forget_cached_acls ( NFS_I ( inode ) ) ;
2006-05-25 09:41:03 +04:00
if ( ! IS_ERR ( acl ) )
nfsi - > acl_access = posix_acl_dup ( acl ) ;
if ( ! IS_ERR ( dfacl ) )
nfsi - > acl_default = posix_acl_dup ( dfacl ) ;
2005-06-22 21:16:27 +04:00
spin_unlock ( & inode - > i_lock ) ;
}
2005-06-22 21:16:27 +04:00
struct posix_acl * nfs3_proc_getacl ( struct inode * inode , int type )
{
struct nfs_server * server = NFS_SERVER ( inode ) ;
struct nfs_fattr fattr ;
struct page * pages [ NFSACL_MAXPAGES ] = { } ;
struct nfs3_getaclargs args = {
. fh = NFS_FH ( inode ) ,
/* The xdr layer may allocate pages here. */
. pages = pages ,
} ;
struct nfs3_getaclres res = {
. fattr = & fattr ,
} ;
2006-03-20 21:44:23 +03:00
struct rpc_message msg = {
. rpc_argp = & args ,
. rpc_resp = & res ,
} ;
2005-06-22 21:16:27 +04:00
struct posix_acl * acl ;
2005-06-22 21:16:27 +04:00
int status , count ;
if ( ! nfs_server_capable ( inode , NFS_CAP_ACLS ) )
return ERR_PTR ( - EOPNOTSUPP ) ;
2005-06-22 21:16:27 +04:00
status = nfs_revalidate_inode ( server , inode ) ;
if ( status < 0 )
return ERR_PTR ( status ) ;
acl = nfs3_get_cached_acl ( inode , type ) ;
if ( acl ! = ERR_PTR ( - EAGAIN ) )
return acl ;
acl = NULL ;
2005-06-22 21:16:27 +04:00
2005-06-22 21:16:27 +04:00
/*
* Only get the access acl when explicitly requested : We don ' t
* need it for access decisions , and only some applications use
* it . Applications which request the access acl first are not
* penalized from this optimization .
*/
if ( type = = ACL_TYPE_ACCESS )
args . mask | = NFS_ACLCNT | NFS_ACL ;
if ( S_ISDIR ( inode - > i_mode ) )
args . mask | = NFS_DFACLCNT | NFS_DFACL ;
if ( args . mask = = 0 )
return NULL ;
2005-06-22 21:16:27 +04:00
dprintk ( " NFS call getacl \n " ) ;
2006-03-20 21:44:23 +03:00
msg . rpc_proc = & server - > client_acl - > cl_procinfo [ ACLPROC3_GETACL ] ;
status = rpc_call_sync ( server - > client_acl , & msg , 0 ) ;
2005-06-22 21:16:27 +04:00
dprintk ( " NFS reply getacl: %d \n " , status ) ;
/* pages may have been allocated at the xdr layer. */
for ( count = 0 ; count < NFSACL_MAXPAGES & & args . pages [ count ] ; count + + )
__free_page ( args . pages [ count ] ) ;
switch ( status ) {
case 0 :
status = nfs_refresh_inode ( inode , & fattr ) ;
break ;
case - EPFNOSUPPORT :
case - EPROTONOSUPPORT :
dprintk ( " NFS_V3_ACL extension not supported; disabling \n " ) ;
server - > caps & = ~ NFS_CAP_ACLS ;
case - ENOTSUPP :
status = - EOPNOTSUPP ;
default :
goto getout ;
}
if ( ( args . mask & res . mask ) ! = args . mask ) {
status = - EIO ;
goto getout ;
}
if ( res . acl_access ! = NULL ) {
if ( posix_acl_equiv_mode ( res . acl_access , NULL ) = = 0 ) {
posix_acl_release ( res . acl_access ) ;
res . acl_access = NULL ;
}
}
2006-05-25 09:41:03 +04:00
nfs3_cache_acls ( inode ,
( res . mask & NFS_ACL ) ? res . acl_access : ERR_PTR ( - EINVAL ) ,
( res . mask & NFS_DFACL ) ? res . acl_default : ERR_PTR ( - EINVAL ) ) ;
2005-06-22 21:16:27 +04:00
switch ( type ) {
case ACL_TYPE_ACCESS :
acl = res . acl_access ;
res . acl_access = NULL ;
break ;
case ACL_TYPE_DEFAULT :
acl = res . acl_default ;
res . acl_default = NULL ;
}
getout :
posix_acl_release ( res . acl_access ) ;
posix_acl_release ( res . acl_default ) ;
if ( status ! = 0 ) {
posix_acl_release ( acl ) ;
acl = ERR_PTR ( status ) ;
}
return acl ;
}
static int nfs3_proc_setacls ( struct inode * inode , struct posix_acl * acl ,
struct posix_acl * dfacl )
{
struct nfs_server * server = NFS_SERVER ( inode ) ;
struct nfs_fattr fattr ;
struct page * pages [ NFSACL_MAXPAGES ] = { } ;
struct nfs3_setaclargs args = {
. inode = inode ,
. mask = NFS_ACL ,
. acl_access = acl ,
. pages = pages ,
} ;
2006-03-20 21:44:23 +03:00
struct rpc_message msg = {
. rpc_argp = & args ,
. rpc_resp = & fattr ,
} ;
2005-06-22 21:16:27 +04:00
int status , count ;
status = - EOPNOTSUPP ;
if ( ! nfs_server_capable ( inode , NFS_CAP_ACLS ) )
goto out ;
/* We are doing this here, because XDR marshalling can only
return - ENOMEM . */
status = - ENOSPC ;
if ( acl ! = NULL & & acl - > a_count > NFS_ACL_MAX_ENTRIES )
goto out ;
if ( dfacl ! = NULL & & dfacl - > a_count > NFS_ACL_MAX_ENTRIES )
goto out ;
if ( S_ISDIR ( inode - > i_mode ) ) {
args . mask | = NFS_DFACL ;
args . acl_default = dfacl ;
}
dprintk ( " NFS call setacl \n " ) ;
2006-03-20 21:44:23 +03:00
msg . rpc_proc = & server - > client_acl - > cl_procinfo [ ACLPROC3_SETACL ] ;
status = rpc_call_sync ( server - > client_acl , & msg , 0 ) ;
2005-08-18 22:24:12 +04:00
spin_lock ( & inode - > i_lock ) ;
2005-08-18 22:24:09 +04:00
NFS_I ( inode ) - > cache_validity | = NFS_INO_INVALID_ACCESS ;
2005-08-18 22:24:12 +04:00
spin_unlock ( & inode - > i_lock ) ;
2005-06-22 21:16:27 +04:00
dprintk ( " NFS reply setacl: %d \n " , status ) ;
/* pages may have been allocated at the xdr layer. */
for ( count = 0 ; count < NFSACL_MAXPAGES & & args . pages [ count ] ; count + + )
__free_page ( args . pages [ count ] ) ;
switch ( status ) {
case 0 :
status = nfs_refresh_inode ( inode , & fattr ) ;
2006-05-25 09:41:03 +04:00
nfs3_cache_acls ( inode , acl , dfacl ) ;
2005-06-22 21:16:27 +04:00
break ;
case - EPFNOSUPPORT :
case - EPROTONOSUPPORT :
dprintk ( " NFS_V3_ACL SETACL RPC not supported "
" (will not retry) \n " ) ;
server - > caps & = ~ NFS_CAP_ACLS ;
case - ENOTSUPP :
status = - EOPNOTSUPP ;
}
out :
return status ;
}
int nfs3_proc_setacl ( struct inode * inode , int type , struct posix_acl * acl )
{
struct posix_acl * alloc = NULL , * dfacl = NULL ;
int status ;
if ( S_ISDIR ( inode - > i_mode ) ) {
switch ( type ) {
case ACL_TYPE_ACCESS :
alloc = dfacl = nfs3_proc_getacl ( inode ,
ACL_TYPE_DEFAULT ) ;
if ( IS_ERR ( alloc ) )
goto fail ;
break ;
case ACL_TYPE_DEFAULT :
dfacl = acl ;
alloc = acl = nfs3_proc_getacl ( inode ,
ACL_TYPE_ACCESS ) ;
if ( IS_ERR ( alloc ) )
goto fail ;
break ;
default :
return - EINVAL ;
}
} else if ( type ! = ACL_TYPE_ACCESS )
return - EINVAL ;
if ( acl = = NULL ) {
alloc = acl = posix_acl_from_mode ( inode - > i_mode , GFP_KERNEL ) ;
if ( IS_ERR ( alloc ) )
goto fail ;
}
status = nfs3_proc_setacls ( inode , acl , dfacl ) ;
posix_acl_release ( alloc ) ;
return status ;
fail :
return PTR_ERR ( alloc ) ;
}
2005-06-22 21:16:27 +04:00
int nfs3_proc_set_default_acl ( struct inode * dir , struct inode * inode ,
mode_t mode )
{
struct posix_acl * dfacl , * acl ;
int error = 0 ;
dfacl = nfs3_proc_getacl ( dir , ACL_TYPE_DEFAULT ) ;
if ( IS_ERR ( dfacl ) ) {
error = PTR_ERR ( dfacl ) ;
return ( error = = - EOPNOTSUPP ) ? 0 : error ;
}
if ( ! dfacl )
return 0 ;
acl = posix_acl_clone ( dfacl , GFP_KERNEL ) ;
error = - ENOMEM ;
if ( ! acl )
goto out_release_dfacl ;
error = posix_acl_create_masq ( acl , & mode ) ;
if ( error < 0 )
goto out_release_acl ;
error = nfs3_proc_setacls ( inode , acl , S_ISDIR ( inode - > i_mode ) ?
dfacl : NULL ) ;
out_release_acl :
posix_acl_release ( acl ) ;
out_release_dfacl :
posix_acl_release ( dfacl ) ;
return error ;
}