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>
# 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"
# include "eaops.h"
# include "eattr.h"
# 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
# define ACL_ACCESS 1
# define ACL_DEFAULT 0
int gfs2_acl_validate_set ( struct gfs2_inode * ip , int access ,
struct gfs2_ea_request * er ,
int * remove , mode_t * mode )
{
struct posix_acl * acl ;
int error ;
error = gfs2_acl_validate_remove ( ip , access ) ;
if ( error )
return error ;
if ( ! er - > er_data )
return - EINVAL ;
acl = posix_acl_from_xattr ( er - > er_data , er - > er_data_len ) ;
if ( IS_ERR ( acl ) )
return PTR_ERR ( acl ) ;
if ( ! acl ) {
* remove = 1 ;
return 0 ;
}
error = posix_acl_valid ( acl ) ;
if ( error )
goto out ;
if ( access ) {
error = posix_acl_equiv_mode ( acl , mode ) ;
if ( ! error )
* remove = 1 ;
else if ( error > 0 )
error = 0 ;
}
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_validate_remove ( struct gfs2_inode * ip , int access )
{
2006-06-14 23:32:57 +04:00
if ( ! GFS2_SB ( & ip - > i_inode ) - > sd_args . ar_posix_acl )
2006-01-16 19:50:04 +03:00
return - EOPNOTSUPP ;
2007-07-17 13:30:08 +04:00
if ( ! is_owner_or_cap ( & ip - > i_inode ) )
2006-01-16 19:50:04 +03:00
return - EPERM ;
2006-11-01 20:22:46 +03:00
if ( S_ISLNK ( ip - > i_inode . i_mode ) )
2006-01-16 19:50:04 +03:00
return - EOPNOTSUPP ;
2006-11-01 20:22:46 +03:00
if ( ! access & & ! S_ISDIR ( ip - > i_inode . i_mode ) )
2006-01-16 19:50:04 +03:00
return - EACCES ;
return 0 ;
}
static int acl_get ( struct gfs2_inode * ip , int access , struct posix_acl * * acl ,
struct gfs2_ea_location * el , char * * data , unsigned int * len )
{
struct gfs2_ea_request er ;
struct gfs2_ea_location el_this ;
int error ;
2008-11-03 17:28:42 +03:00
if ( ! ip - > i_eattr )
2006-01-16 19:50:04 +03:00
return 0 ;
memset ( & er , 0 , sizeof ( struct gfs2_ea_request ) ) ;
if ( access ) {
er . er_name = GFS2_POSIX_ACL_ACCESS ;
er . er_name_len = GFS2_POSIX_ACL_ACCESS_LEN ;
} else {
er . er_name = GFS2_POSIX_ACL_DEFAULT ;
er . er_name_len = GFS2_POSIX_ACL_DEFAULT_LEN ;
}
er . er_type = GFS2_EATYPE_SYS ;
if ( ! el )
el = & el_this ;
error = gfs2_ea_find ( ip , & er , el ) ;
if ( error )
return error ;
if ( ! el - > el_ea )
return 0 ;
if ( ! GFS2_EA_DATA_LEN ( el - > el_ea ) )
goto out ;
er . er_data_len = GFS2_EA_DATA_LEN ( el - > el_ea ) ;
2008-04-09 17:33:41 +04:00
er . er_data = kmalloc ( er . er_data_len , GFP_NOFS ) ;
2006-01-16 19:50:04 +03:00
error = - ENOMEM ;
if ( ! er . er_data )
goto out ;
error = gfs2_ea_get_copy ( ip , el , er . er_data ) ;
if ( error )
goto out_kfree ;
if ( acl ) {
* acl = posix_acl_from_xattr ( er . er_data , er . er_data_len ) ;
if ( IS_ERR ( * acl ) )
error = PTR_ERR ( * acl ) ;
}
2006-09-04 20:04:26 +04:00
out_kfree :
2006-01-16 19:50:04 +03:00
if ( error | | ! data )
kfree ( er . er_data ) ;
else {
* data = er . er_data ;
* len = er . er_data_len ;
}
2006-09-04 20:04:26 +04:00
out :
2006-01-16 19:50:04 +03:00
if ( error | | el = = & el_this )
brelse ( el - > el_bh ) ;
return error ;
}
/**
2006-11-29 18:41:49 +03:00
* gfs2_check_acl - Check an ACL to see if we ' re allowed to do something
2006-01-16 19:50:04 +03:00
* @ inode : the file we want to do something to
* @ mask : what we want to do
*
* Returns : errno
*/
2006-11-29 18:41:49 +03:00
int gfs2_check_acl ( struct inode * inode , int mask )
2006-01-16 19:50:04 +03:00
{
struct posix_acl * acl = NULL ;
int error ;
2006-06-14 23:32:57 +04:00
error = acl_get ( GFS2_I ( inode ) , ACL_ACCESS , & acl , NULL , NULL , NULL ) ;
2006-01-16 19:50:04 +03:00
if ( error )
return error ;
if ( acl ) {
error = posix_acl_permission ( inode , acl , mask ) ;
posix_acl_release ( acl ) ;
return error ;
}
return - EAGAIN ;
}
static int munge_mode ( struct gfs2_inode * ip , mode_t mode )
{
2006-06-14 23:32:57 +04:00
struct gfs2_sbd * sdp = GFS2_SB ( & ip - > i_inode ) ;
2006-01-16 19:50:04 +03:00
struct buffer_head * dibh ;
int error ;
error = gfs2_trans_begin ( sdp , RES_DINODE , 0 ) ;
if ( error )
return error ;
error = gfs2_meta_inode_buffer ( ip , & dibh ) ;
if ( ! error ) {
gfs2_assert_withdraw ( sdp ,
2006-11-01 20:22:46 +03:00
( ip - > i_inode . i_mode & S_IFMT ) = = ( mode & S_IFMT ) ) ;
ip - > i_inode . i_mode = mode ;
2006-01-18 14:19:28 +03:00
gfs2_trans_add_bh ( ip - > i_gl , dibh , 1 ) ;
2006-10-31 23:07:05 +03:00
gfs2_dinode_out ( ip , dibh - > b_data ) ;
2006-01-16 19:50:04 +03:00
brelse ( dibh ) ;
}
gfs2_trans_end ( sdp ) ;
return 0 ;
}
int gfs2_acl_create ( struct gfs2_inode * dip , struct gfs2_inode * ip )
{
2006-06-14 23:32:57 +04:00
struct gfs2_sbd * sdp = GFS2_SB ( & dip - > i_inode ) ;
2006-01-16 19:50:04 +03:00
struct posix_acl * acl = NULL , * clone ;
struct gfs2_ea_request er ;
2006-11-01 20:22:46 +03:00
mode_t mode = ip - > i_inode . i_mode ;
2006-01-16 19:50:04 +03:00
int error ;
if ( ! sdp - > sd_args . ar_posix_acl )
return 0 ;
2006-11-01 20:22:46 +03:00
if ( S_ISLNK ( ip - > i_inode . i_mode ) )
2006-01-16 19:50:04 +03:00
return 0 ;
memset ( & er , 0 , sizeof ( struct gfs2_ea_request ) ) ;
er . er_type = GFS2_EATYPE_SYS ;
error = acl_get ( dip , ACL_DEFAULT , & acl , NULL ,
& er . er_data , & er . er_data_len ) ;
if ( error )
return error ;
if ( ! acl ) {
mode & = ~ current - > fs - > umask ;
2006-11-01 20:22:46 +03:00
if ( mode ! = ip - > i_inode . i_mode )
2006-01-16 19:50:04 +03:00
error = munge_mode ( ip , mode ) ;
return error ;
}
2008-04-09 17:33:41 +04:00
clone = posix_acl_clone ( acl , GFP_NOFS ) ;
2006-01-16 19:50:04 +03:00
error = - ENOMEM ;
if ( ! clone )
goto out ;
posix_acl_release ( acl ) ;
acl = clone ;
2006-11-01 20:22:46 +03:00
if ( S_ISDIR ( ip - > i_inode . i_mode ) ) {
2006-01-16 19:50:04 +03:00
er . er_name = GFS2_POSIX_ACL_DEFAULT ;
er . er_name_len = GFS2_POSIX_ACL_DEFAULT_LEN ;
error = gfs2_system_eaops . eo_set ( ip , & er ) ;
if ( error )
goto out ;
}
error = posix_acl_create_masq ( acl , & mode ) ;
if ( error < 0 )
goto out ;
if ( error > 0 ) {
er . er_name = GFS2_POSIX_ACL_ACCESS ;
er . er_name_len = GFS2_POSIX_ACL_ACCESS_LEN ;
posix_acl_to_xattr ( acl , er . er_data , er . er_data_len ) ;
er . er_mode = mode ;
er . er_flags = GFS2_ERF_MODE ;
error = gfs2_system_eaops . eo_set ( ip , & er ) ;
if ( error )
goto out ;
} else
munge_mode ( ip , mode ) ;
2006-09-04 20:04:26 +04:00
out :
2006-01-16 19:50:04 +03:00
posix_acl_release ( acl ) ;
kfree ( er . er_data ) ;
return error ;
}
int gfs2_acl_chmod ( struct gfs2_inode * ip , struct iattr * attr )
{
struct posix_acl * acl = NULL , * clone ;
struct gfs2_ea_location el ;
char * data ;
unsigned int len ;
int error ;
error = acl_get ( ip , ACL_ACCESS , & acl , & el , & data , & len ) ;
if ( error )
return error ;
if ( ! acl )
return gfs2_setattr_simple ( ip , attr ) ;
2008-04-09 17:33:41 +04:00
clone = posix_acl_clone ( acl , GFP_NOFS ) ;
2006-01-16 19:50:04 +03:00
error = - ENOMEM ;
if ( ! clone )
goto out ;
posix_acl_release ( acl ) ;
acl = clone ;
error = posix_acl_chmod_masq ( acl , attr - > ia_mode ) ;
if ( ! error ) {
posix_acl_to_xattr ( acl , data , len ) ;
error = gfs2_ea_acl_chmod ( ip , & el , attr , data ) ;
}
2006-09-04 20:04:26 +04:00
out :
2006-01-16 19:50:04 +03:00
posix_acl_release ( acl ) ;
brelse ( el . el_bh ) ;
kfree ( data ) ;
return error ;
}