2006-01-16 19:50:04 +03:00
/*
* Copyright ( C ) Sistina Software , Inc . 1997 - 2003 All rights reserved .
2006-05-18 23:09:15 +04:00
* Copyright ( C ) 2004 - 2006 Red Hat , Inc . All rights reserved .
2006-01-16 19:50:04 +03:00
*
* This copyrighted material is made available to anyone wishing to use ,
* modify , copy , or redistribute it subject to the terms and conditions
2006-09-01 19:05:15 +04:00
* of the GNU General Public License version 2.
2006-01-16 19:50:04 +03:00
*/
# include <linux/sched.h>
# include <linux/slab.h>
# include <linux/spinlock.h>
# include <linux/completion.h>
# include <linux/buffer_head.h>
2009-10-02 14:50:54 +04:00
# include <linux/xattr.h>
2006-01-16 19:50:04 +03:00
# include <linux/posix_acl.h>
# include <linux/posix_acl_xattr.h>
2006-02-28 01:23:27 +03:00
# include <linux/gfs2_ondisk.h>
2006-01-16 19:50:04 +03:00
# include "gfs2.h"
2006-02-28 01:23:27 +03:00
# include "incore.h"
2006-01-16 19:50:04 +03:00
# include "acl.h"
2009-08-26 21:51:04 +04:00
# include "xattr.h"
2006-01-16 19:50:04 +03:00
# include "glock.h"
# include "inode.h"
# include "meta_io.h"
# include "trans.h"
2006-02-28 01:23:27 +03:00
# include "util.h"
2006-01-16 19:50:04 +03:00
2009-10-02 15:00:00 +04:00
static const char * gfs2_acl_name ( int type )
2006-01-16 19:50:04 +03:00
{
2009-10-02 15:00:00 +04:00
switch ( type ) {
case ACL_TYPE_ACCESS :
return GFS2_POSIX_ACL_ACCESS ;
case ACL_TYPE_DEFAULT :
return GFS2_POSIX_ACL_DEFAULT ;
}
return NULL ;
}
2006-01-16 19:50:04 +03:00
2009-10-02 15:00:00 +04:00
static struct posix_acl * gfs2_acl_get ( struct gfs2_inode * ip , int type )
{
struct posix_acl * acl ;
const char * name ;
char * data ;
int len ;
2009-08-26 21:41:32 +04:00
2008-11-03 17:28:42 +03:00
if ( ! ip - > i_eattr )
2009-10-02 15:00:00 +04:00
return NULL ;
2006-01-16 19:50:04 +03:00
2009-09-29 19:26:23 +04:00
acl = get_cached_acl ( & ip - > i_inode , type ) ;
if ( acl ! = ACL_NOT_CACHED )
return acl ;
2009-10-02 15:00:00 +04:00
name = gfs2_acl_name ( type ) ;
if ( name = = NULL )
return ERR_PTR ( - EINVAL ) ;
2006-01-16 19:50:04 +03:00
2009-10-02 15:00:00 +04:00
len = gfs2_xattr_acl_get ( ip , name , & data ) ;
if ( len < 0 )
return ERR_PTR ( len ) ;
if ( len = = 0 )
return NULL ;
2006-01-16 19:50:04 +03:00
2009-10-02 15:00:00 +04:00
acl = posix_acl_from_xattr ( data , len ) ;
kfree ( data ) ;
return acl ;
2006-01-16 19:50:04 +03:00
}
2011-07-23 19:37:31 +04:00
struct posix_acl * gfs2_get_acl ( struct inode * inode , int type )
2006-01-16 19:50:04 +03:00
{
2011-07-23 19:37:31 +04:00
return gfs2_acl_get ( GFS2_I ( inode ) , type ) ;
2006-01-16 19:50:04 +03:00
}
2011-07-24 02:37:50 +04:00
static int gfs2_set_mode ( struct inode * inode , umode_t mode )
2006-01-16 19:50:04 +03:00
{
2009-09-29 15:40:19 +04:00
int error = 0 ;
2006-01-16 19:50:04 +03:00
2009-09-29 15:40:19 +04:00
if ( mode ! = inode - > i_mode ) {
struct iattr iattr ;
2006-01-16 19:50:04 +03:00
2009-09-29 15:40:19 +04:00
iattr . ia_valid = ATTR_MODE ;
iattr . ia_mode = mode ;
2006-01-16 19:50:04 +03:00
2009-09-29 15:40:19 +04:00
error = gfs2_setattr_simple ( GFS2_I ( inode ) , & iattr ) ;
}
2006-01-16 19:50:04 +03:00
2009-09-29 15:40:19 +04:00
return error ;
2006-01-16 19:50:04 +03:00
}
2009-10-02 15:00:00 +04:00
static int gfs2_acl_set ( struct inode * inode , int type , struct posix_acl * acl )
2006-01-16 19:50:04 +03:00
{
int error ;
2009-10-02 15:00:00 +04:00
int len ;
char * data ;
const char * name = gfs2_acl_name ( type ) ;
BUG_ON ( name = = NULL ) ;
len = posix_acl_to_xattr ( acl , NULL , 0 ) ;
if ( len = = 0 )
return 0 ;
data = kmalloc ( len , GFP_NOFS ) ;
if ( data = = NULL )
return - ENOMEM ;
error = posix_acl_to_xattr ( acl , data , len ) ;
if ( error < 0 )
goto out ;
2009-11-13 12:52:56 +03:00
error = __gfs2_xattr_set ( inode , name , data , len , 0 , GFS2_EATYPE_SYS ) ;
2009-09-29 19:26:23 +04:00
if ( ! error )
set_cached_acl ( inode , type , acl ) ;
2009-10-02 15:00:00 +04:00
out :
kfree ( data ) ;
return error ;
}
int gfs2_acl_create ( struct gfs2_inode * dip , struct inode * inode )
{
struct gfs2_sbd * sdp = GFS2_SB ( & dip - > i_inode ) ;
2011-07-23 11:10:32 +04:00
struct posix_acl * acl ;
2011-07-24 02:37:50 +04:00
umode_t mode = inode - > i_mode ;
2009-10-02 15:00:00 +04:00
int error = 0 ;
2006-01-16 19:50:04 +03:00
if ( ! sdp - > sd_args . ar_posix_acl )
return 0 ;
2009-10-02 15:00:00 +04:00
if ( S_ISLNK ( inode - > i_mode ) )
2006-01-16 19:50:04 +03:00
return 0 ;
2009-10-02 15:00:00 +04:00
acl = gfs2_acl_get ( dip , ACL_TYPE_DEFAULT ) ;
if ( IS_ERR ( acl ) )
return PTR_ERR ( acl ) ;
2006-01-16 19:50:04 +03:00
if ( ! acl ) {
2009-03-30 03:08:22 +04:00
mode & = ~ current_umask ( ) ;
2009-10-02 15:00:00 +04:00
if ( mode ! = inode - > i_mode )
error = gfs2_set_mode ( inode , mode ) ;
2006-01-16 19:50:04 +03:00
return error ;
}
2009-10-02 15:00:00 +04:00
if ( S_ISDIR ( inode - > i_mode ) ) {
error = gfs2_acl_set ( inode , ACL_TYPE_DEFAULT , acl ) ;
if ( error )
goto out ;
}
2011-07-23 11:10:32 +04:00
error = posix_acl_create ( & acl , GFP_NOFS , & mode ) ;
2006-01-16 19:50:04 +03:00
if ( error < 0 )
2011-07-23 11:10:32 +04:00
return error ;
2009-08-26 21:41:32 +04:00
if ( error = = 0 )
goto munge ;
2006-01-16 19:50:04 +03:00
2009-10-02 15:00:00 +04:00
error = gfs2_acl_set ( inode , ACL_TYPE_ACCESS , acl ) ;
2009-08-26 21:41:32 +04:00
if ( error )
goto out ;
munge :
2009-10-02 15:00:00 +04:00
error = gfs2_set_mode ( inode , mode ) ;
2006-09-04 20:04:26 +04:00
out :
2006-01-16 19:50:04 +03:00
posix_acl_release ( acl ) ;
return error ;
}
int gfs2_acl_chmod ( struct gfs2_inode * ip , struct iattr * attr )
{
2011-07-23 08:18:02 +04:00
struct posix_acl * acl ;
2006-01-16 19:50:04 +03:00
char * data ;
unsigned int len ;
int error ;
2009-10-02 15:00:00 +04:00
acl = gfs2_acl_get ( ip , ACL_TYPE_ACCESS ) ;
if ( IS_ERR ( acl ) )
return PTR_ERR ( acl ) ;
2006-01-16 19:50:04 +03:00
if ( ! acl )
return gfs2_setattr_simple ( ip , attr ) ;
2011-07-23 08:18:02 +04:00
error = posix_acl_chmod ( & acl , GFP_NOFS , attr - > ia_mode ) ;
if ( error )
return error ;
len = posix_acl_to_xattr ( acl , NULL , 0 ) ;
data = kmalloc ( len , GFP_NOFS ) ;
2006-01-16 19:50:04 +03:00
error = - ENOMEM ;
2011-07-23 08:18:02 +04:00
if ( data = = NULL )
2006-01-16 19:50:04 +03:00
goto out ;
2011-07-23 08:18:02 +04:00
posix_acl_to_xattr ( acl , data , len ) ;
error = gfs2_xattr_acl_chmod ( ip , attr , data ) ;
kfree ( data ) ;
set_cached_acl ( & ip - > i_inode , ACL_TYPE_ACCESS , acl ) ;
2006-01-16 19:50:04 +03:00
2006-09-04 20:04:26 +04:00
out :
2006-01-16 19:50:04 +03:00
posix_acl_release ( acl ) ;
return error ;
}
2009-10-02 14:50:54 +04:00
static int gfs2_acl_type ( const char * name )
{
if ( strcmp ( name , GFS2_POSIX_ACL_ACCESS ) = = 0 )
return ACL_TYPE_ACCESS ;
if ( strcmp ( name , GFS2_POSIX_ACL_DEFAULT ) = = 0 )
return ACL_TYPE_DEFAULT ;
return - EINVAL ;
}
2009-11-13 12:52:56 +03:00
static int gfs2_xattr_system_get ( struct dentry * dentry , const char * name ,
void * buffer , size_t size , int xtype )
2009-10-02 14:50:54 +04:00
{
2009-11-13 12:52:56 +03:00
struct inode * inode = dentry - > d_inode ;
2010-05-21 19:12:27 +04:00
struct gfs2_sbd * sdp = GFS2_SB ( inode ) ;
2009-09-29 19:26:23 +04:00
struct posix_acl * acl ;
2009-10-02 14:50:54 +04:00
int type ;
2009-09-29 19:26:23 +04:00
int error ;
2009-10-02 14:50:54 +04:00
2010-05-21 19:12:27 +04:00
if ( ! sdp - > sd_args . ar_posix_acl )
return - EOPNOTSUPP ;
2009-10-02 14:50:54 +04:00
type = gfs2_acl_type ( name ) ;
if ( type < 0 )
return type ;
2009-09-29 19:26:23 +04:00
acl = gfs2_acl_get ( GFS2_I ( inode ) , type ) ;
if ( IS_ERR ( acl ) )
return PTR_ERR ( acl ) ;
if ( acl = = NULL )
return - ENODATA ;
2009-10-02 14:50:54 +04:00
2009-09-29 19:26:23 +04:00
error = posix_acl_to_xattr ( acl , buffer , size ) ;
posix_acl_release ( acl ) ;
return error ;
}
2009-10-02 14:50:54 +04:00
2009-11-13 12:52:56 +03:00
static int gfs2_xattr_system_set ( struct dentry * dentry , const char * name ,
const void * value , size_t size , int flags ,
int xtype )
2009-10-02 14:50:54 +04:00
{
2009-11-13 12:52:56 +03:00
struct inode * inode = dentry - > d_inode ;
2009-10-02 14:50:54 +04:00
struct gfs2_sbd * sdp = GFS2_SB ( inode ) ;
struct posix_acl * acl = NULL ;
int error = 0 , type ;
if ( ! sdp - > sd_args . ar_posix_acl )
return - EOPNOTSUPP ;
type = gfs2_acl_type ( name ) ;
if ( type < 0 )
return type ;
if ( flags & XATTR_CREATE )
return - EINVAL ;
if ( type = = ACL_TYPE_DEFAULT & & ! S_ISDIR ( inode - > i_mode ) )
return value ? - EACCES : 0 ;
if ( ( current_fsuid ( ) ! = inode - > i_uid ) & & ! capable ( CAP_FOWNER ) )
return - EPERM ;
if ( S_ISLNK ( inode - > i_mode ) )
return - EOPNOTSUPP ;
if ( ! value )
goto set_acl ;
acl = posix_acl_from_xattr ( value , size ) ;
if ( ! acl ) {
/*
* acl_set_file ( 3 ) may request that we set default ACLs with
* zero length - - defend ( gracefully ) against that here .
*/
goto out ;
}
if ( IS_ERR ( acl ) ) {
error = PTR_ERR ( acl ) ;
goto out ;
}
error = posix_acl_valid ( acl ) ;
if ( error )
goto out_release ;
error = - EINVAL ;
if ( acl - > a_count > GFS2_ACL_MAX_ENTRIES )
goto out_release ;
if ( type = = ACL_TYPE_ACCESS ) {
2011-07-24 02:56:36 +04:00
umode_t mode = inode - > i_mode ;
2009-10-02 14:50:54 +04:00
error = posix_acl_equiv_mode ( acl , & mode ) ;
if ( error < = 0 ) {
posix_acl_release ( acl ) ;
acl = NULL ;
if ( error < 0 )
return error ;
}
error = gfs2_set_mode ( inode , mode ) ;
if ( error )
goto out_release ;
}
set_acl :
2009-11-13 12:52:56 +03:00
error = __gfs2_xattr_set ( inode , name , value , size , 0 , GFS2_EATYPE_SYS ) ;
2009-09-29 19:26:23 +04:00
if ( ! error ) {
if ( acl )
set_cached_acl ( inode , type , acl ) ;
else
forget_cached_acl ( inode , type ) ;
}
2009-10-02 14:50:54 +04:00
out_release :
posix_acl_release ( acl ) ;
out :
return error ;
}
2010-05-14 04:53:23 +04:00
const struct xattr_handler gfs2_xattr_system_handler = {
2009-10-02 14:50:54 +04:00
. prefix = XATTR_SYSTEM_PREFIX ,
2009-11-13 12:52:56 +03:00
. flags = GFS2_EATYPE_SYS ,
2009-10-02 14:50:54 +04:00
. get = gfs2_xattr_system_get ,
. set = gfs2_xattr_system_set ,
} ;