2007-11-16 19:45:54 +03:00
/*
* Copyright ( C ) 2007 Red Hat . All rights reserved .
*
* This program is free software ; you can redistribute it and / or
* modify it under the terms of the GNU General Public
* License v2 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 .
*
* You should have received a copy of the GNU General Public
* License along with this program ; if not , write to the
* Free Software Foundation , Inc . , 59 Temple Place - Suite 330 ,
* Boston , MA 021110 - 1307 , USA .
*/
# include <linux/fs.h>
# include <linux/string.h>
# include <linux/xattr.h>
# include <linux/posix_acl_xattr.h>
2008-07-24 20:16:36 +04:00
# include <linux/posix_acl.h>
2008-01-22 20:46:56 +03:00
# include <linux/sched.h>
2008-07-24 20:16:36 +04:00
2007-11-16 19:45:54 +03:00
# include "ctree.h"
2008-07-24 20:16:36 +04:00
# include "btrfs_inode.h"
2007-11-16 19:45:54 +03:00
# include "xattr.h"
2008-07-24 20:16:36 +04:00
2009-10-13 21:50:18 +04:00
# ifdef CONFIG_BTRFS_FS_POSIX_ACL
2008-08-28 14:21:15 +04:00
2008-07-24 20:16:36 +04:00
static struct posix_acl * btrfs_get_acl ( struct inode * inode , int type )
{
2008-08-28 14:21:17 +04:00
int size ;
const char * name ;
2008-07-24 20:16:36 +04:00
char * value = NULL ;
2009-06-09 20:11:54 +04:00
struct posix_acl * acl ;
acl = get_cached_acl ( inode , type ) ;
if ( acl ! = ACL_NOT_CACHED )
return acl ;
2008-07-24 20:16:36 +04:00
switch ( type ) {
case ACL_TYPE_ACCESS :
2008-08-28 14:21:17 +04:00
name = POSIX_ACL_XATTR_ACCESS ;
2008-07-24 20:16:36 +04:00
break ;
case ACL_TYPE_DEFAULT :
2008-08-28 14:21:17 +04:00
name = POSIX_ACL_XATTR_DEFAULT ;
2008-07-24 20:16:36 +04:00
break ;
default :
2009-06-09 20:11:54 +04:00
BUG ( ) ;
2008-07-24 20:16:36 +04:00
}
2008-08-28 14:21:17 +04:00
size = __btrfs_getxattr ( inode , name , " " , 0 ) ;
2008-07-24 20:16:36 +04:00
if ( size > 0 ) {
value = kzalloc ( size , GFP_NOFS ) ;
if ( ! value )
return ERR_PTR ( - ENOMEM ) ;
2008-08-28 14:21:17 +04:00
size = __btrfs_getxattr ( inode , name , value , size ) ;
2008-07-24 20:16:36 +04:00
if ( size > 0 ) {
acl = posix_acl_from_xattr ( value , size ) ;
2009-06-09 20:11:54 +04:00
set_cached_acl ( inode , type , acl ) ;
2008-07-24 20:16:36 +04:00
}
kfree ( value ) ;
2009-04-27 18:49:53 +04:00
} else if ( size = = - ENOENT | | size = = - ENODATA | | size = = 0 ) {
/* FIXME, who returns -ENOENT? I think nobody */
2008-07-24 20:16:36 +04:00
acl = NULL ;
2009-06-09 20:11:54 +04:00
set_cached_acl ( inode , type , acl ) ;
2009-04-27 18:49:53 +04:00
} else {
acl = ERR_PTR ( - EIO ) ;
2008-07-24 20:16:36 +04:00
}
return acl ;
}
static int btrfs_xattr_get_acl ( struct inode * inode , int type ,
void * value , size_t size )
{
struct posix_acl * acl ;
int ret = 0 ;
acl = btrfs_get_acl ( inode , type ) ;
if ( IS_ERR ( acl ) )
return PTR_ERR ( acl ) ;
if ( acl = = NULL )
return - ENODATA ;
ret = posix_acl_to_xattr ( acl , value , size ) ;
posix_acl_release ( acl ) ;
return ret ;
}
/*
* Needs to be called with fs_mutex held
*/
2009-11-12 12:35:27 +03:00
static int btrfs_set_acl ( struct btrfs_trans_handle * trans ,
struct inode * inode , struct posix_acl * acl , int type )
2008-07-24 20:16:36 +04:00
{
2008-08-28 14:21:17 +04:00
int ret , size = 0 ;
const char * name ;
2008-07-24 20:16:36 +04:00
char * value = NULL ;
mode_t mode ;
if ( acl ) {
ret = posix_acl_valid ( acl ) ;
if ( ret < 0 )
return ret ;
ret = 0 ;
}
switch ( type ) {
case ACL_TYPE_ACCESS :
mode = inode - > i_mode ;
2008-08-28 14:21:17 +04:00
name = POSIX_ACL_XATTR_ACCESS ;
2010-01-18 04:36:18 +03:00
if ( acl ) {
ret = posix_acl_equiv_mode ( acl , & mode ) ;
if ( ret < 0 )
return ret ;
inode - > i_mode = mode ;
}
ret = 0 ;
2008-07-24 20:16:36 +04:00
break ;
case ACL_TYPE_DEFAULT :
if ( ! S_ISDIR ( inode - > i_mode ) )
return acl ? - EINVAL : 0 ;
2008-08-28 14:21:17 +04:00
name = POSIX_ACL_XATTR_DEFAULT ;
2008-07-24 20:16:36 +04:00
break ;
default :
return - EINVAL ;
}
if ( acl ) {
size = posix_acl_xattr_size ( acl - > a_count ) ;
value = kmalloc ( size , GFP_NOFS ) ;
if ( ! value ) {
ret = - ENOMEM ;
goto out ;
}
ret = posix_acl_to_xattr ( acl , value , size ) ;
if ( ret < 0 )
goto out ;
}
2009-11-12 12:35:27 +03:00
ret = __btrfs_setxattr ( trans , inode , name , value , size , 0 ) ;
2008-07-24 20:16:36 +04:00
out :
2009-01-06 05:25:51 +03:00
kfree ( value ) ;
2008-07-24 20:16:36 +04:00
if ( ! ret )
2009-06-09 20:11:54 +04:00
set_cached_acl ( inode , type , acl ) ;
2008-07-24 20:16:36 +04:00
return ret ;
}
2008-01-17 19:59:51 +03:00
2008-01-14 21:26:08 +03:00
static int btrfs_xattr_set_acl ( struct inode * inode , int type ,
const void * value , size_t size )
{
2009-11-12 12:35:27 +03:00
int ret ;
2008-07-24 20:16:36 +04:00
struct posix_acl * acl = NULL ;
2007-11-16 19:45:54 +03:00
2008-01-14 21:26:08 +03:00
if ( value ) {
acl = posix_acl_from_xattr ( value , size ) ;
if ( acl = = NULL ) {
value = NULL ;
size = 0 ;
} else if ( IS_ERR ( acl ) ) {
2008-07-24 20:16:36 +04:00
return PTR_ERR ( acl ) ;
2008-01-14 21:26:08 +03:00
}
}
2007-11-19 18:18:17 +03:00
2009-11-12 12:35:27 +03:00
ret = btrfs_set_acl ( NULL , inode , acl , type ) ;
2008-07-24 20:16:36 +04:00
posix_acl_release ( acl ) ;
return ret ;
2008-01-14 21:26:08 +03:00
}
2008-07-24 20:16:36 +04:00
2007-11-16 19:45:54 +03:00
static int btrfs_xattr_acl_access_get ( struct inode * inode , const char * name ,
void * value , size_t size )
{
2008-07-24 20:16:36 +04:00
return btrfs_xattr_get_acl ( inode , ACL_TYPE_ACCESS , value , size ) ;
2007-11-16 19:45:54 +03:00
}
2008-07-24 20:16:36 +04:00
2007-11-16 19:45:54 +03:00
static int btrfs_xattr_acl_access_set ( struct inode * inode , const char * name ,
const void * value , size_t size , int flags )
{
2008-07-24 20:16:36 +04:00
return btrfs_xattr_set_acl ( inode , ACL_TYPE_ACCESS , value , size ) ;
2007-11-16 19:45:54 +03:00
}
2008-07-24 20:16:36 +04:00
2007-11-16 19:45:54 +03:00
static int btrfs_xattr_acl_default_get ( struct inode * inode , const char * name ,
void * value , size_t size )
{
2008-07-24 20:16:36 +04:00
return btrfs_xattr_get_acl ( inode , ACL_TYPE_DEFAULT , value , size ) ;
2007-11-16 19:45:54 +03:00
}
2008-07-24 20:16:36 +04:00
2007-11-16 19:45:54 +03:00
static int btrfs_xattr_acl_default_set ( struct inode * inode , const char * name ,
2009-01-06 05:25:51 +03:00
const void * value , size_t size , int flags )
2007-11-16 19:45:54 +03:00
{
2008-07-24 20:16:36 +04:00
return btrfs_xattr_set_acl ( inode , ACL_TYPE_DEFAULT , value , size ) ;
}
int btrfs_check_acl ( struct inode * inode , int mask )
{
struct posix_acl * acl ;
int error = - EAGAIN ;
acl = btrfs_get_acl ( inode , ACL_TYPE_ACCESS ) ;
if ( IS_ERR ( acl ) )
return PTR_ERR ( acl ) ;
if ( acl ) {
error = posix_acl_permission ( inode , acl , mask ) ;
posix_acl_release ( acl ) ;
}
return error ;
}
/*
* btrfs_init_acl is already generally called under fs_mutex , so the locking
* stuff has been fixed to work with that . If the locking stuff changes , we
* need to re - evaluate the acl locking stuff .
*/
2009-11-12 12:35:27 +03:00
int btrfs_init_acl ( struct btrfs_trans_handle * trans ,
struct inode * inode , struct inode * dir )
2008-07-24 20:16:36 +04:00
{
struct posix_acl * acl = NULL ;
int ret = 0 ;
/* this happens with subvols */
if ( ! dir )
return 0 ;
if ( ! S_ISLNK ( inode - > i_mode ) ) {
if ( IS_POSIXACL ( dir ) ) {
acl = btrfs_get_acl ( dir , ACL_TYPE_DEFAULT ) ;
if ( IS_ERR ( acl ) )
return PTR_ERR ( acl ) ;
}
if ( ! acl )
2009-03-30 03:08:22 +04:00
inode - > i_mode & = ~ current_umask ( ) ;
2008-07-24 20:16:36 +04:00
}
if ( IS_POSIXACL ( dir ) & & acl ) {
struct posix_acl * clone ;
mode_t mode ;
if ( S_ISDIR ( inode - > i_mode ) ) {
2009-11-12 12:35:27 +03:00
ret = btrfs_set_acl ( trans , inode , acl ,
ACL_TYPE_DEFAULT ) ;
2008-07-24 20:16:36 +04:00
if ( ret )
goto failed ;
}
clone = posix_acl_clone ( acl , GFP_NOFS ) ;
ret = - ENOMEM ;
if ( ! clone )
goto failed ;
mode = inode - > i_mode ;
ret = posix_acl_create_masq ( clone , & mode ) ;
if ( ret > = 0 ) {
inode - > i_mode = mode ;
if ( ret > 0 ) {
/* we need an acl */
2009-11-12 12:35:27 +03:00
ret = btrfs_set_acl ( trans , inode , clone ,
2008-07-24 20:16:36 +04:00
ACL_TYPE_ACCESS ) ;
}
}
}
failed :
posix_acl_release ( acl ) ;
return ret ;
}
int btrfs_acl_chmod ( struct inode * inode )
{
struct posix_acl * acl , * clone ;
int ret = 0 ;
if ( S_ISLNK ( inode - > i_mode ) )
return - EOPNOTSUPP ;
if ( ! IS_POSIXACL ( inode ) )
return 0 ;
acl = btrfs_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 )
2009-11-12 12:35:27 +03:00
ret = btrfs_set_acl ( NULL , inode , clone , ACL_TYPE_ACCESS ) ;
2008-07-24 20:16:36 +04:00
posix_acl_release ( clone ) ;
return ret ;
2007-11-16 19:45:54 +03:00
}
2008-07-24 20:16:36 +04:00
2007-11-16 19:45:54 +03:00
struct xattr_handler btrfs_xattr_acl_default_handler = {
. prefix = POSIX_ACL_XATTR_DEFAULT ,
. get = btrfs_xattr_acl_default_get ,
. set = btrfs_xattr_acl_default_set ,
} ;
struct xattr_handler btrfs_xattr_acl_access_handler = {
. prefix = POSIX_ACL_XATTR_ACCESS ,
. get = btrfs_xattr_acl_access_get ,
. set = btrfs_xattr_acl_access_set ,
} ;
2008-08-28 14:21:15 +04:00
2009-10-13 21:50:18 +04:00
# else /* CONFIG_BTRFS_FS_POSIX_ACL */
2008-08-28 14:21:15 +04:00
int btrfs_acl_chmod ( struct inode * inode )
{
return 0 ;
}
2009-11-12 12:35:27 +03:00
int btrfs_init_acl ( struct btrfs_trans_handle * trans ,
struct inode * inode , struct inode * dir )
2008-08-28 14:21:15 +04:00
{
return 0 ;
}
2009-10-13 21:50:18 +04:00
# endif /* CONFIG_BTRFS_FS_POSIX_ACL */