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 + -
显示快捷键?