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"
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
# define ACL_ACCESS 1
# define ACL_DEFAULT 0
int gfs2_acl_validate_set ( struct gfs2_inode * ip , int access ,
2009-08-26 21:41:32 +04:00
struct gfs2_ea_request * er , int * remove , mode_t * mode )
2006-01-16 19:50:04 +03:00
{
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 ;
}
2009-08-26 21:41:32 +04:00
static int acl_get ( struct gfs2_inode * ip , const char * name ,
struct posix_acl * * acl , struct gfs2_ea_location * el ,
char * * datap , unsigned int * lenp )
2006-01-16 19:50:04 +03:00
{
2009-08-26 21:41:32 +04:00
char * data ;
unsigned int len ;
2006-01-16 19:50:04 +03:00
int error ;
2009-08-26 21:41:32 +04:00
el - > el_bh = NULL ;
2008-11-03 17:28:42 +03:00
if ( ! ip - > i_eattr )
2006-01-16 19:50:04 +03:00
return 0 ;
2009-08-26 21:41:32 +04:00
error = gfs2_ea_find ( ip , GFS2_EATYPE_SYS , name , el ) ;
2006-01-16 19:50:04 +03:00
if ( error )
return error ;
if ( ! el - > el_ea )
return 0 ;
if ( ! GFS2_EA_DATA_LEN ( el - > el_ea ) )
goto out ;
2009-08-26 21:41:32 +04:00
len = GFS2_EA_DATA_LEN ( el - > el_ea ) ;
data = kmalloc ( len , GFP_NOFS ) ;
2006-01-16 19:50:04 +03:00
error = - ENOMEM ;
2009-08-26 21:41:32 +04:00
if ( ! data )
2006-01-16 19:50:04 +03:00
goto out ;
2009-08-26 21:41:32 +04:00
error = gfs2_ea_get_copy ( ip , el , data , len ) ;
if ( error < 0 )
2006-01-16 19:50:04 +03:00
goto out_kfree ;
2009-08-26 21:41:32 +04:00
error = 0 ;
2006-01-16 19:50:04 +03:00
if ( acl ) {
2009-08-26 21:41:32 +04:00
* acl = posix_acl_from_xattr ( data , len ) ;
2006-01-16 19:50:04 +03:00
if ( IS_ERR ( * acl ) )
error = PTR_ERR ( * acl ) ;
}
2006-09-04 20:04:26 +04:00
out_kfree :
2009-08-26 21:41:32 +04:00
if ( error | | ! datap ) {
kfree ( data ) ;
} else {
* datap = data ;
* lenp = len ;
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
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
{
2009-08-26 21:41:32 +04:00
struct gfs2_ea_location el ;
2006-01-16 19:50:04 +03:00
struct posix_acl * acl = NULL ;
int error ;
2009-08-26 21:41:32 +04:00
error = acl_get ( GFS2_I ( inode ) , GFS2_POSIX_ACL_ACCESS , & acl , & el , NULL , NULL ) ;
brelse ( el . el_bh ) ;
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 )
{
2009-08-26 21:41:32 +04:00
struct gfs2_ea_location el ;
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 ;
2006-11-01 20:22:46 +03:00
mode_t mode = ip - > i_inode . i_mode ;
2009-08-26 21:41:32 +04:00
char * data = NULL ;
unsigned int len ;
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 ;
2009-08-26 21:41:32 +04:00
error = acl_get ( dip , GFS2_POSIX_ACL_DEFAULT , & acl , & el , & data , & len ) ;
brelse ( el . el_bh ) ;
2006-01-16 19:50:04 +03:00
if ( error )
return error ;
if ( ! acl ) {
2009-03-30 03:08:22 +04:00
mode & = ~ current_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 ) ) {
2009-08-26 21:41:32 +04:00
error = gfs2_xattr_set ( & ip - > i_inode , GFS2_EATYPE_SYS ,
GFS2_POSIX_ACL_DEFAULT , data , len , 0 ) ;
2006-01-16 19:50:04 +03:00
if ( error )
goto out ;
}
error = posix_acl_create_masq ( acl , & mode ) ;
if ( error < 0 )
goto out ;
2009-08-26 21:41:32 +04:00
if ( error = = 0 )
goto munge ;
2006-01-16 19:50:04 +03:00
2009-08-26 21:41:32 +04:00
posix_acl_to_xattr ( acl , data , len ) ;
error = gfs2_xattr_set ( & ip - > i_inode , GFS2_EATYPE_SYS ,
GFS2_POSIX_ACL_ACCESS , data , len , 0 ) ;
if ( error )
goto out ;
munge :
error = 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 ) ;
2009-08-26 21:41:32 +04:00
kfree ( data ) ;
2006-01-16 19:50:04 +03:00
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 ;
2009-08-26 21:41:32 +04:00
error = acl_get ( ip , GFS2_POSIX_ACL_ACCESS , & acl , & el , & data , & len ) ;
2006-01-16 19:50:04 +03:00
if ( error )
2009-08-26 21:41:32 +04:00
goto out_brelse ;
2006-01-16 19:50:04 +03:00
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 ) ;
kfree ( data ) ;
2009-08-26 21:41:32 +04:00
out_brelse :
brelse ( el . el_bh ) ;
2006-01-16 19:50:04 +03:00
return error ;
}