xfs_acl.c

来自「Linux Kernel 2.6.9 for OMAP1710」· C语言 代码 · 共 975 行 · 第 1/2 页

C
975
字号
/* * Copyright (c) 2001-2002 Silicon Graphics, Inc.  All Rights Reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 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. * * Further, this software is distributed without any warranty that it is * free of the rightful claim of any third person regarding infringement * or the like.  Any license provided herein, whether implied or * otherwise, applies only to this software file.  Patent licenses, if * any, provided herein do not apply to combinations of this program with * other software, or any other product whatsoever. * * You should have received a copy of the GNU General Public License along * with this program; if not, write the Free Software Foundation, Inc., 59 * Temple Place - Suite 330, Boston MA 02111-1307, USA. * * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy, * Mountain View, CA  94043, or: * * http://www.sgi.com * * For further information regarding this notice, see: * * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/ */#include "xfs.h"#include "xfs_inum.h"#include "xfs_dir.h"#include "xfs_dir2.h"#include "xfs_alloc_btree.h"#include "xfs_bmap_btree.h"#include "xfs_ialloc_btree.h"#include "xfs_btree.h"#include "xfs_attr_sf.h"#include "xfs_dir_sf.h"#include "xfs_dir2_sf.h"#include "xfs_dinode.h"#include "xfs_inode.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. */intxfs_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. */intxfs_acl_vhasacl_default(	vnode_t		*vp){	int		error;	if (vp->v_type != VDIR)		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 intposix_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 qsort(). * Primary key is ae_tag, secondary key is ae_id. */STATIC intxfs_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 intposix_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> */	qsort(src->acl_entry, src->acl_cnt, sizeof(src->acl_entry[0]),		xfs_acl_entry_compare);	dest->a_version = cpu_to_le32(POSIX_ACL_XATTR_VERSION);	dest_entry = &dest->a_entries[0];	src_entry = &src->acl_entry[0];	for (n = 0; n < src->acl_cnt; n++, dest_entry++, src_entry++) {		dest_entry->e_perm = cpu_to_le16(src_entry->ae_perm);		if (_ACL_PERM_INVALID(src_entry->ae_perm))			return -EINVAL;		dest_entry->e_tag  = cpu_to_le16(src_entry->ae_tag);		switch (src_entry->ae_tag) {		case ACL_USER:		case ACL_GROUP:			dest_entry->e_id = cpu_to_le32(src_entry->ae_id);				break;		case ACL_USER_OBJ:		case ACL_GROUP_OBJ:		case ACL_MASK:		case ACL_OTHER:			dest_entry->e_id = cpu_to_le32(ACL_UNDEFINED_ID);			break;		default:			return -EINVAL;		}	}	return new_size;}intxfs_acl_vget(	vnode_t		*vp,	void		*acl,	size_t		size,	int		kind){	int			error;	xfs_acl_t		*xfs_acl = NULL;	posix_acl_xattr_header	*ext_acl = acl;	int			flags = 0;	VN_HOLD(vp);	if(size) {		if (!(_ACL_ALLOC(xfs_acl))) {			error = ENOMEM;			goto out;		}		memset(xfs_acl, 0, sizeof(xfs_acl_t));	} else		flags = ATTR_KERNOVAL;	xfs_acl_get_attr(vp, xfs_acl, kind, flags, &error);	if (error)		goto out;	if (!size) {		error = -posix_acl_xattr_size(XFS_ACL_MAX_ENTRIES);	} else {		if (xfs_acl_invalid(xfs_acl)) {			error = EINVAL;			goto out;		}		if (kind == _ACL_TYPE_ACCESS) {			vattr_t	va;			va.va_mask = XFS_AT_MODE;			VOP_GETATTR(vp, &va, 0, sys_cred, error);			if (error)				goto out;			xfs_acl_sync_mode(va.va_mode, xfs_acl);		}		error = -posix_acl_xfs_to_xattr(xfs_acl, ext_acl, size);	}out:	VN_RELE(vp);	if(xfs_acl)		_ACL_FREE(xfs_acl);	return -error;}intxfs_acl_vremove(	vnode_t		*vp,	int		kind){	int		error;	VN_HOLD(vp);	error = xfs_acl_allow_set(vp, kind);	if (!error) {		VOP_ATTR_REMOVE(vp, kind == _ACL_TYPE_DEFAULT?				SGI_ACL_DEFAULT: SGI_ACL_FILE,				ATTR_ROOT, sys_cred, error);		if (error == ENOATTR)			error = 0;	/* 'scool */	}	VN_RELE(vp);	return -error;}intxfs_acl_vset(	vnode_t			*vp,	void			*acl,	size_t			size,	int			kind){	posix_acl_xattr_header	*ext_acl = acl;	xfs_acl_t		*xfs_acl;	int			error;	int			basicperms = 0; /* more than std unix perms? */	if (!acl)		return -EINVAL;	if (!(_ACL_ALLOC(xfs_acl)))		return -ENOMEM;	error = posix_acl_xattr_to_xfs(ext_acl, size, xfs_acl);	if (error) {		_ACL_FREE(xfs_acl);		return -error;	}	if (!xfs_acl->acl_cnt) {		_ACL_FREE(xfs_acl);		return 0;	}	VN_HOLD(vp);	error = xfs_acl_allow_set(vp, kind);	if (error)		goto out;	/* Incoming ACL exists, set file mode based on its value */	if (kind == _ACL_TYPE_ACCESS)		xfs_acl_setmode(vp, xfs_acl, &basicperms);	/*	 * If we have more than std unix permissions, set up the actual attr.	 * Otherwise, delete any existing attr.  This prevents us from	 * having actual attrs for permissions that can be stored in the	 * standard permission bits.	 */	if (!basicperms) {		xfs_acl_set_attr(vp, xfs_acl, kind, &error);	} else {		xfs_acl_vremove(vp, _ACL_TYPE_ACCESS);	}out:	VN_RELE(vp);	_ACL_FREE(xfs_acl);	return -error;}intxfs_acl_iaccess(	xfs_inode_t	*ip,	mode_t		mode,	cred_t		*cr){	xfs_acl_t	*acl;	int		rval;	if (!(_ACL_ALLOC(acl)))		return -1;	/* If the file has no ACL return -1. */	rval = sizeof(xfs_acl_t);	if (xfs_attr_fetch(ip, SGI_ACL_FILE, SGI_ACL_FILE_SIZE,			(char *)acl, &rval, ATTR_ROOT | ATTR_KERNACCESS, cr)) {		_ACL_FREE(acl);		return -1;	}	xfs_acl_get_endian(acl);	/* If the file has an empty ACL return -1. */	if (acl->acl_cnt == XFS_ACL_NOT_PRESENT) {		_ACL_FREE(acl);		return -1;	}	/* Synchronize ACL with mode bits */	xfs_acl_sync_mode(ip->i_d.di_mode, acl);	rval = xfs_acl_access(ip->i_d.di_uid, ip->i_d.di_gid, acl, mode, cr);	_ACL_FREE(acl);	return rval;}STATIC intxfs_acl_allow_set(	vnode_t		*vp,	int		kind){	vattr_t		va;	int		error;	if (vp->v_inode.i_flags & (S_IMMUTABLE|S_APPEND))		return EPERM;	if (kind == _ACL_TYPE_DEFAULT && vp->v_type != VDIR)		return ENOTDIR;	if (vp->v_vfsp->vfs_flag & VFS_RDONLY)		return EROFS;	va.va_mask = XFS_AT_UID;	VOP_GETATTR(vp, &va, 0, NULL, error);	if (error)		return error;	if (va.va_uid != current->fsuid && !capable(CAP_FOWNER))		return EPERM;	return error;}/* * Look for any effective exec access, to allow CAP_DAC_OVERRIDE for exec. * Ignore checking for exec in USER_OBJ when there is no mask, because * in this "minimal acl" case we don't have any actual acls, and we * won't even be here. */STATIC intxfs_acl_find_any_exec(	xfs_acl_t	*fap){	int		i;	int		masked_aces = 0;	int		mask = 0;	for (i = 0; i < fap->acl_cnt; i++) {		if (fap->acl_entry[i].ae_perm & ACL_EXECUTE) {			if (fap->acl_entry[i].ae_tag & (ACL_USER_OBJ|ACL_OTHER))				return 1;			if (fap->acl_entry[i].ae_tag == ACL_MASK)				mask = fap->acl_entry[i].ae_perm;			else				masked_aces |= fap->acl_entry[i].ae_perm;			if ((mask & masked_aces) & ACL_EXECUTE)				return 1;		}	}	return 0;}/* * The access control process to determine the access permission: *	if uid == file owner id, use the file owner bits. *	if gid == file owner group id, use the file group bits. *	scan ACL for a maching user or group, and use matched entry *	permission. Use total permissions of all matching group entries, *	until all acl entries are exhausted. The final permission produced *	by matching acl entry or entries needs to be & with group permission. *	if not owner, owning group, or matching entry in ACL, use file *	other bits.  Don't allow CAP_DAC_OVERRIDE on exec access unless *	there is some effective exec access somewhere. */STATIC intxfs_acl_capability_check(	mode_t		mode,	cred_t		*cr,	xfs_acl_t	*fap){	if ((mode & ACL_READ) && !capable_cred(cr, CAP_DAC_READ_SEARCH))		return EACCES;	if ((mode & ACL_WRITE) && !capable_cred(cr, CAP_DAC_OVERRIDE))		return EACCES;	if ((mode & ACL_EXECUTE) &&	    (!capable_cred(cr, CAP_DAC_OVERRIDE) ||	     !xfs_acl_find_any_exec(fap))) {		return EACCES;	}	return 0;}/* * Note: cr is only used here for the capability check if the ACL test fails. *       It is not used to find out the credentials uid or groups etc, as was *       done in IRIX. It is assumed that the uid and groups for the current *       thread are taken from "current" instead of the cr parameter. */STATIC intxfs_acl_access(	uid_t		fuid,	gid_t		fgid,	xfs_acl_t	*fap,	mode_t		md,	cred_t		*cr){	xfs_acl_entry_t	matched;	int		i, allows;	int		maskallows = -1;	/* true, but not 1, either */	int		seen_userobj = 0;	matched.ae_tag = 0;	/* Invalid type */	md >>= 6;	/* Normalize the bits for comparison */

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?