2008-11-14 06:17:04 +03:00
/* -*- mode: c; c-basic-offset: 8; -*-
* vim : noexpandtab sw = 8 ts = 8 sts = 0 :
*
* acl . c
*
* Copyright ( C ) 2004 , 2008 Oracle . All rights reserved .
*
* CREDITS :
* Lots of code in this file is copy from linux / fs / ext3 / acl . c .
* Copyright ( C ) 2001 - 2003 Andreas Gruenbacher , < agruen @ suse . de >
*
* This program is free software ; you can redistribute it and / or
* modify it under the terms of the GNU General Public
* License version 2 as published by the Free Software Foundation .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the GNU
* General Public License for more details .
*/
# include <linux/init.h>
# include <linux/module.h>
# include <linux/string.h>
# define MLOG_MASK_PREFIX ML_INODE
# include <cluster/masklog.h>
# include "ocfs2.h"
# include "alloc.h"
# include "dlmglue.h"
# include "file.h"
2010-03-16 01:39:00 +03:00
# include "inode.h"
# include "journal.h"
2008-11-14 06:17:04 +03:00
# include "ocfs2_fs.h"
# include "xattr.h"
# include "acl.h"
/*
* Convert from xattr value to acl struct .
*/
static struct posix_acl * ocfs2_acl_from_xattr ( const void * value , size_t size )
{
int n , count ;
struct posix_acl * acl ;
if ( ! value )
return NULL ;
if ( size < sizeof ( struct posix_acl_entry ) )
return ERR_PTR ( - EINVAL ) ;
count = size / sizeof ( struct posix_acl_entry ) ;
if ( count < 0 )
return ERR_PTR ( - EINVAL ) ;
if ( count = = 0 )
return NULL ;
acl = posix_acl_alloc ( count , GFP_NOFS ) ;
if ( ! acl )
return ERR_PTR ( - ENOMEM ) ;
for ( n = 0 ; n < count ; n + + ) {
struct ocfs2_acl_entry * entry =
( struct ocfs2_acl_entry * ) value ;
acl - > a_entries [ n ] . e_tag = le16_to_cpu ( entry - > e_tag ) ;
acl - > a_entries [ n ] . e_perm = le16_to_cpu ( entry - > e_perm ) ;
acl - > a_entries [ n ] . e_id = le32_to_cpu ( entry - > e_id ) ;
value + = sizeof ( struct posix_acl_entry ) ;
}
return acl ;
}
/*
* Convert acl struct to xattr value .
*/
static void * ocfs2_acl_to_xattr ( const struct posix_acl * acl , size_t * size )
{
struct ocfs2_acl_entry * entry = NULL ;
char * ocfs2_acl ;
size_t n ;
* size = acl - > a_count * sizeof ( struct posix_acl_entry ) ;
ocfs2_acl = kmalloc ( * size , GFP_NOFS ) ;
if ( ! ocfs2_acl )
return ERR_PTR ( - ENOMEM ) ;
entry = ( struct ocfs2_acl_entry * ) ocfs2_acl ;
for ( n = 0 ; n < acl - > a_count ; n + + , entry + + ) {
entry - > e_tag = cpu_to_le16 ( acl - > a_entries [ n ] . e_tag ) ;
entry - > e_perm = cpu_to_le16 ( acl - > a_entries [ n ] . e_perm ) ;
entry - > e_id = cpu_to_le32 ( acl - > a_entries [ n ] . e_id ) ;
}
return ocfs2_acl ;
}
static struct posix_acl * ocfs2_get_acl_nolock ( struct inode * inode ,
int type ,
struct buffer_head * di_bh )
{
int name_index ;
char * value = NULL ;
struct posix_acl * acl ;
int retval ;
switch ( type ) {
case ACL_TYPE_ACCESS :
name_index = OCFS2_XATTR_INDEX_POSIX_ACL_ACCESS ;
break ;
case ACL_TYPE_DEFAULT :
name_index = OCFS2_XATTR_INDEX_POSIX_ACL_DEFAULT ;
break ;
default :
return ERR_PTR ( - EINVAL ) ;
}
retval = ocfs2_xattr_get_nolock ( inode , di_bh , name_index , " " , NULL , 0 ) ;
if ( retval > 0 ) {
value = kmalloc ( retval , GFP_NOFS ) ;
if ( ! value )
return ERR_PTR ( - ENOMEM ) ;
retval = ocfs2_xattr_get_nolock ( inode , di_bh , name_index ,
" " , value , retval ) ;
}
if ( retval > 0 )
acl = ocfs2_acl_from_xattr ( value , retval ) ;
else if ( retval = = - ENODATA | | retval = = 0 )
acl = NULL ;
else
acl = ERR_PTR ( retval ) ;
kfree ( value ) ;
return acl ;
}
/*
* Get posix acl .
*/
static struct posix_acl * ocfs2_get_acl ( struct inode * inode , int type )
{
struct ocfs2_super * osb = OCFS2_SB ( inode - > i_sb ) ;
struct buffer_head * di_bh = NULL ;
struct posix_acl * acl ;
int ret ;
if ( ! ( osb - > s_mount_opt & OCFS2_MOUNT_POSIX_ACL ) )
return NULL ;
ret = ocfs2_inode_lock ( inode , & di_bh , 0 ) ;
if ( ret < 0 ) {
mlog_errno ( ret ) ;
acl = ERR_PTR ( ret ) ;
return acl ;
}
acl = ocfs2_get_acl_nolock ( inode , type , di_bh ) ;
ocfs2_inode_unlock ( inode , 0 ) ;
brelse ( di_bh ) ;
return acl ;
}
2010-03-16 01:39:00 +03:00
/*
* Helper function to set i_mode in memory and disk . Some call paths
* will not have di_bh or a journal handle to pass , in which case it
* will create it ' s own .
*/
static int ocfs2_acl_set_mode ( struct inode * inode , struct buffer_head * di_bh ,
handle_t * handle , umode_t new_mode )
{
int ret , commit_handle = 0 ;
struct ocfs2_dinode * di ;
if ( di_bh = = NULL ) {
ret = ocfs2_read_inode_block ( inode , & di_bh ) ;
if ( ret ) {
mlog_errno ( ret ) ;
goto out ;
}
} else
get_bh ( di_bh ) ;
if ( handle = = NULL ) {
handle = ocfs2_start_trans ( OCFS2_SB ( inode - > i_sb ) ,
OCFS2_INODE_UPDATE_CREDITS ) ;
if ( IS_ERR ( handle ) ) {
ret = PTR_ERR ( handle ) ;
mlog_errno ( ret ) ;
goto out_brelse ;
}
commit_handle = 1 ;
}
di = ( struct ocfs2_dinode * ) di_bh - > b_data ;
ret = ocfs2_journal_access_di ( handle , INODE_CACHE ( inode ) , di_bh ,
OCFS2_JOURNAL_ACCESS_WRITE ) ;
if ( ret ) {
mlog_errno ( ret ) ;
goto out_commit ;
}
inode - > i_mode = new_mode ;
di - > i_mode = cpu_to_le16 ( inode - > i_mode ) ;
ocfs2_journal_dirty ( handle , di_bh ) ;
out_commit :
if ( commit_handle )
ocfs2_commit_trans ( OCFS2_SB ( inode - > i_sb ) , handle ) ;
out_brelse :
brelse ( di_bh ) ;
out :
return ret ;
}
2008-11-14 06:17:04 +03:00
/*
* Set the access or default ACL of an inode .
*/
static int ocfs2_set_acl ( handle_t * handle ,
struct inode * inode ,
struct buffer_head * di_bh ,
int type ,
struct posix_acl * acl ,
struct ocfs2_alloc_context * meta_ac ,
struct ocfs2_alloc_context * data_ac )
{
int name_index ;
void * value = NULL ;
size_t size = 0 ;
int ret ;
if ( S_ISLNK ( inode - > i_mode ) )
return - EOPNOTSUPP ;
switch ( type ) {
case ACL_TYPE_ACCESS :
name_index = OCFS2_XATTR_INDEX_POSIX_ACL_ACCESS ;
if ( acl ) {
mode_t mode = inode - > i_mode ;
ret = posix_acl_equiv_mode ( acl , & mode ) ;
if ( ret < 0 )
return ret ;
else {
if ( ret = = 0 )
acl = NULL ;
2010-03-16 01:39:00 +03:00
ret = ocfs2_acl_set_mode ( inode , di_bh ,
handle , mode ) ;
if ( ret )
return ret ;
2008-11-14 06:17:04 +03:00
}
}
break ;
case ACL_TYPE_DEFAULT :
name_index = OCFS2_XATTR_INDEX_POSIX_ACL_DEFAULT ;
if ( ! S_ISDIR ( inode - > i_mode ) )
return acl ? - EACCES : 0 ;
break ;
default :
return - EINVAL ;
}
if ( acl ) {
value = ocfs2_acl_to_xattr ( acl , & size ) ;
if ( IS_ERR ( value ) )
return ( int ) PTR_ERR ( value ) ;
}
if ( handle )
ret = ocfs2_xattr_set_handle ( handle , inode , di_bh , name_index ,
" " , value , size , 0 ,
meta_ac , data_ac ) ;
else
ret = ocfs2_xattr_set ( inode , name_index , " " , value , size , 0 ) ;
kfree ( value ) ;
return ret ;
}
2008-11-14 06:17:18 +03:00
int ocfs2_check_acl ( struct inode * inode , int mask )
{
struct posix_acl * acl = ocfs2_get_acl ( inode , ACL_TYPE_ACCESS ) ;
if ( IS_ERR ( acl ) )
return PTR_ERR ( acl ) ;
if ( acl ) {
int ret = posix_acl_permission ( inode , acl , mask ) ;
posix_acl_release ( acl ) ;
return ret ;
}
return - EAGAIN ;
}
2008-11-14 06:17:29 +03:00
int ocfs2_acl_chmod ( struct inode * inode )
{
struct ocfs2_super * osb = OCFS2_SB ( inode - > i_sb ) ;
struct posix_acl * acl , * clone ;
int ret ;
if ( S_ISLNK ( inode - > i_mode ) )
return - EOPNOTSUPP ;
if ( ! ( osb - > s_mount_opt & OCFS2_MOUNT_POSIX_ACL ) )
return 0 ;
acl = ocfs2_get_acl ( inode , ACL_TYPE_ACCESS ) ;
if ( IS_ERR ( acl ) | | ! acl )
return PTR_ERR ( acl ) ;
clone = posix_acl_clone ( acl , GFP_KERNEL ) ;
posix_acl_release ( acl ) ;
if ( ! clone )
return - ENOMEM ;
ret = posix_acl_chmod_masq ( clone , inode - > i_mode ) ;
if ( ! ret )
ret = ocfs2_set_acl ( NULL , inode , NULL , ACL_TYPE_ACCESS ,
clone , NULL , NULL ) ;
posix_acl_release ( clone ) ;
return ret ;
}
2008-11-14 06:17:41 +03:00
/*
* Initialize the ACLs of a new inode . If parent directory has default ACL ,
* then clone to new inode . Called from ocfs2_mknod .
*/
int ocfs2_init_acl ( handle_t * handle ,
struct inode * inode ,
struct inode * dir ,
struct buffer_head * di_bh ,
struct buffer_head * dir_bh ,
struct ocfs2_alloc_context * meta_ac ,
struct ocfs2_alloc_context * data_ac )
{
struct ocfs2_super * osb = OCFS2_SB ( inode - > i_sb ) ;
struct posix_acl * acl = NULL ;
int ret = 0 ;
2010-03-16 01:39:00 +03:00
mode_t mode ;
2008-11-14 06:17:41 +03:00
if ( ! S_ISLNK ( inode - > i_mode ) ) {
if ( osb - > s_mount_opt & OCFS2_MOUNT_POSIX_ACL ) {
acl = ocfs2_get_acl_nolock ( dir , ACL_TYPE_DEFAULT ,
dir_bh ) ;
if ( IS_ERR ( acl ) )
return PTR_ERR ( acl ) ;
}
2010-03-16 01:39:00 +03:00
if ( ! acl ) {
mode = inode - > i_mode & ~ current_umask ( ) ;
ret = ocfs2_acl_set_mode ( inode , di_bh , handle , mode ) ;
if ( ret ) {
mlog_errno ( ret ) ;
goto cleanup ;
}
}
2008-11-14 06:17:41 +03:00
}
if ( ( osb - > s_mount_opt & OCFS2_MOUNT_POSIX_ACL ) & & acl ) {
struct posix_acl * clone ;
if ( S_ISDIR ( inode - > i_mode ) ) {
ret = ocfs2_set_acl ( handle , inode , di_bh ,
ACL_TYPE_DEFAULT , acl ,
meta_ac , data_ac ) ;
if ( ret )
goto cleanup ;
}
clone = posix_acl_clone ( acl , GFP_NOFS ) ;
ret = - ENOMEM ;
if ( ! clone )
goto cleanup ;
mode = inode - > i_mode ;
ret = posix_acl_create_masq ( clone , & mode ) ;
if ( ret > = 0 ) {
2010-03-16 01:39:00 +03:00
ret = ocfs2_acl_set_mode ( inode , di_bh , handle , mode ) ;
2008-11-14 06:17:41 +03:00
if ( ret > 0 ) {
ret = ocfs2_set_acl ( handle , inode ,
di_bh , ACL_TYPE_ACCESS ,
clone , meta_ac , data_ac ) ;
}
}
posix_acl_release ( clone ) ;
}
cleanup :
posix_acl_release ( acl ) ;
return ret ;
}
2009-11-13 12:52:56 +03:00
static size_t ocfs2_xattr_list_acl_access ( struct dentry * dentry ,
2008-11-14 06:17:04 +03:00
char * list ,
size_t list_len ,
const char * name ,
2009-11-13 12:52:56 +03:00
size_t name_len ,
int type )
2008-11-14 06:17:04 +03:00
{
2009-11-13 12:52:56 +03:00
struct ocfs2_super * osb = OCFS2_SB ( dentry - > d_sb ) ;
2008-11-14 06:17:04 +03:00
const size_t size = sizeof ( POSIX_ACL_XATTR_ACCESS ) ;
if ( ! ( osb - > s_mount_opt & OCFS2_MOUNT_POSIX_ACL ) )
return 0 ;
if ( list & & size < = list_len )
memcpy ( list , POSIX_ACL_XATTR_ACCESS , size ) ;
return size ;
}
2009-11-13 12:52:56 +03:00
static size_t ocfs2_xattr_list_acl_default ( struct dentry * dentry ,
2008-11-14 06:17:04 +03:00
char * list ,
size_t list_len ,
const char * name ,
2009-11-13 12:52:56 +03:00
size_t name_len ,
int type )
2008-11-14 06:17:04 +03:00
{
2009-11-13 12:52:56 +03:00
struct ocfs2_super * osb = OCFS2_SB ( dentry - > d_sb ) ;
2008-11-14 06:17:04 +03:00
const size_t size = sizeof ( POSIX_ACL_XATTR_DEFAULT ) ;
if ( ! ( osb - > s_mount_opt & OCFS2_MOUNT_POSIX_ACL ) )
return 0 ;
if ( list & & size < = list_len )
memcpy ( list , POSIX_ACL_XATTR_DEFAULT , size ) ;
return size ;
}
2009-11-13 12:52:56 +03:00
static int ocfs2_xattr_get_acl ( struct dentry * dentry , const char * name ,
void * buffer , size_t size , int type )
2008-11-14 06:17:04 +03:00
{
2009-11-13 12:52:56 +03:00
struct ocfs2_super * osb = OCFS2_SB ( dentry - > d_sb ) ;
2008-11-14 06:17:04 +03:00
struct posix_acl * acl ;
int ret ;
2009-11-13 12:52:56 +03:00
if ( strcmp ( name , " " ) ! = 0 )
return - EINVAL ;
2008-11-14 06:17:04 +03:00
if ( ! ( osb - > s_mount_opt & OCFS2_MOUNT_POSIX_ACL ) )
return - EOPNOTSUPP ;
2009-11-13 12:52:56 +03:00
acl = ocfs2_get_acl ( dentry - > d_inode , type ) ;
2008-11-14 06:17:04 +03:00
if ( IS_ERR ( acl ) )
return PTR_ERR ( acl ) ;
if ( acl = = NULL )
return - ENODATA ;
ret = posix_acl_to_xattr ( acl , buffer , size ) ;
posix_acl_release ( acl ) ;
return ret ;
}
2009-11-13 12:52:56 +03:00
static int ocfs2_xattr_set_acl ( struct dentry * dentry , const char * name ,
const void * value , size_t size , int flags , int type )
2008-11-14 06:17:04 +03:00
{
2009-11-13 12:52:56 +03:00
struct inode * inode = dentry - > d_inode ;
2008-11-14 06:17:04 +03:00
struct ocfs2_super * osb = OCFS2_SB ( inode - > i_sb ) ;
struct posix_acl * acl ;
int ret = 0 ;
2009-11-13 12:52:56 +03:00
if ( strcmp ( name , " " ) ! = 0 )
return - EINVAL ;
2008-11-14 06:17:04 +03:00
if ( ! ( osb - > s_mount_opt & OCFS2_MOUNT_POSIX_ACL ) )
return - EOPNOTSUPP ;
if ( ! is_owner_or_cap ( inode ) )
return - EPERM ;
if ( value ) {
acl = posix_acl_from_xattr ( value , size ) ;
if ( IS_ERR ( acl ) )
return PTR_ERR ( acl ) ;
else if ( acl ) {
ret = posix_acl_valid ( acl ) ;
if ( ret )
goto cleanup ;
}
} else
acl = NULL ;
ret = ocfs2_set_acl ( NULL , inode , NULL , type , acl , NULL , NULL ) ;
cleanup :
posix_acl_release ( acl ) ;
return ret ;
}
struct xattr_handler ocfs2_xattr_acl_access_handler = {
. prefix = POSIX_ACL_XATTR_ACCESS ,
2009-11-13 12:52:56 +03:00
. flags = ACL_TYPE_ACCESS ,
2008-11-14 06:17:04 +03:00
. list = ocfs2_xattr_list_acl_access ,
2009-11-13 12:52:56 +03:00
. get = ocfs2_xattr_get_acl ,
. set = ocfs2_xattr_set_acl ,
2008-11-14 06:17:04 +03:00
} ;
struct xattr_handler ocfs2_xattr_acl_default_handler = {
. prefix = POSIX_ACL_XATTR_DEFAULT ,
2009-11-13 12:52:56 +03:00
. flags = ACL_TYPE_DEFAULT ,
2008-11-14 06:17:04 +03:00
. list = ocfs2_xattr_list_acl_default ,
2009-11-13 12:52:56 +03:00
. get = ocfs2_xattr_get_acl ,
. set = ocfs2_xattr_set_acl ,
2008-11-14 06:17:04 +03:00
} ;