/*
* Copyright (c) 2001-2002,2005 Silicon Graphics, Inc.
* 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 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it would 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 the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "xfs.h"
#include "xfs_fs.h"
#include "xfs_types.h"
#include "xfs_bit.h"
#include "xfs_inum.h"
#include "xfs_ag.h"
#include "xfs_dir.h"
#include "xfs_dir2.h"
#include "xfs_bmap_btree.h"
#include "xfs_alloc_btree.h"
#include "xfs_ialloc_btree.h"
#include "xfs_dir_sf.h"
#include "xfs_dir2_sf.h"
#include "xfs_attr_sf.h"
#include "xfs_dinode.h"
#include "xfs_inode.h"
#include "xfs_btree.h"
#include "xfs_acl.h"
#include "xfs_mac.h"
#include "xfs_attr.h"
#include <linux/posix_acl_xattr.h>
STATIC int xfs_acl_setmode(vnode_t *, xfs_acl_t *, int *);
STATIC void xfs_acl_filter_mode(mode_t, xfs_acl_t *);
STATIC void xfs_acl_get_endian(xfs_acl_t *);
STATIC int xfs_acl_access(uid_t, gid_t, xfs_acl_t *, mode_t, cred_t *);
STATIC int xfs_acl_invalid(xfs_acl_t *);
STATIC void xfs_acl_sync_mode(mode_t, xfs_acl_t *);
STATIC void xfs_acl_get_attr(vnode_t *, xfs_acl_t *, int, int, int *);
STATIC void xfs_acl_set_attr(vnode_t *, xfs_acl_t *, int, int *);
STATIC int xfs_acl_allow_set(vnode_t *, int);
kmem_zone_t *xfs_acl_zone;
/*
* Test for existence of access ACL attribute as efficiently as possible.
*/
int
xfs_acl_vhasacl_access(
vnode_t *vp)
{
int error;
xfs_acl_get_attr(vp, NULL, _ACL_TYPE_ACCESS, ATTR_KERNOVAL, &error);
return (error == 0);
}
/*
* Test for existence of default ACL attribute as efficiently as possible.
*/
int
xfs_acl_vhasacl_default(
vnode_t *vp)
{
int error;
if (!VN_ISDIR(vp))
return 0;
xfs_acl_get_attr(vp, NULL, _ACL_TYPE_DEFAULT, ATTR_KERNOVAL, &error);
return (error == 0);
}
/*
* Convert from extended attribute representation to in-memory for XFS.
*/
STATIC int
posix_acl_xattr_to_xfs(
posix_acl_xattr_header *src,
size_t size,
xfs_acl_t *dest)
{
posix_acl_xattr_entry *src_entry;
xfs_acl_entry_t *dest_entry;
int n;
if (!src || !dest)
return EINVAL;
if (size < sizeof(posix_acl_xattr_header))
return EINVAL;
if (src->a_version != cpu_to_le32(POSIX_ACL_XATTR_VERSION))
return EOPNOTSUPP;
memset(dest, 0, sizeof(xfs_acl_t));
dest->acl_cnt = posix_acl_xattr_count(size);
if (dest->acl_cnt < 0 || dest->acl_cnt > XFS_ACL_MAX_ENTRIES)
return EINVAL;
/*
* acl_set_file(3) may request that we set default ACLs with
* zero length -- defend (gracefully) against that here.
*/
if (!dest->acl_cnt)
return 0;
src_entry = (posix_acl_xattr_entry *)((char *)src + sizeof(*src));
dest_entry = &dest->acl_entry[0];
for (n = 0; n < dest->acl_cnt; n++, src_entry++, dest_entry++) {
dest_entry->ae_perm = le16_to_cpu(src_entry->e_perm);
if (_ACL_PERM_INVALID(dest_entry->ae_perm))
return EINVAL;
dest_entry->ae_tag = le16_to_cpu(src_entry->e_tag);
switch(dest_entry->ae_tag) {
case ACL_USER:
case ACL_GROUP:
dest_entry->ae_id = le32_to_cpu(src_entry->e_id);
break;
case ACL_USER_OBJ:
case ACL_GROUP_OBJ:
case ACL_MASK:
case ACL_OTHER:
dest_entry->ae_id = ACL_UNDEFINED_ID;
break;
default:
return EINVAL;
}
}
if (xfs_acl_invalid(dest))
return EINVAL;
return 0;
}
/*
* Comparison function called from xfs_sort().
* Primary key is ae_tag, secondary key is ae_id.
*/
STATIC int
xfs_acl_entry_compare(
const void *va,
const void *vb)
{
xfs_acl_entry_t *a = (xfs_acl_entry_t *)va,
*b = (xfs_acl_entry_t *)vb;
if (a->ae_tag == b->ae_tag)
return (a->ae_id - b->ae_id);
return (a->ae_tag - b->ae_tag);
}
/*
* Convert from in-memory XFS to extended attribute representation.
*/
STATIC int
posix_acl_xfs_to_xattr(
xfs_acl_t *src,
posix_acl_xattr_header *dest,
size_t size)
{
int n;
size_t new_size = posix_acl_xattr_size(src->acl_cnt);
posix_acl_xattr_entry *dest_entry;
xfs_acl_entry_t *src_entry;
if (size < new_size)
return -ERANGE;
/* Need to sort src XFS ACL by <ae_tag,ae_id> */
xfs_sort(src->acl_entry, src->acl_cnt, sizeof