📄 xfs_acl.c
字号:
/* * 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_dir2.h"#include "xfs_bmap_btree.h"#include "xfs_alloc_btree.h"#include "xfs_ialloc_btree.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_attr.h"#include "xfs_vnodeops.h"#include <linux/capability.h>#include <linux/posix_acl_xattr.h>STATIC int xfs_acl_setmode(bhv_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(bhv_vnode_t *, xfs_acl_t *, int, int, int *);STATIC void xfs_acl_set_attr(bhv_vnode_t *, xfs_acl_t *, int, int *);STATIC int xfs_acl_allow_set(bhv_vnode_t *, int);kmem_zone_t *xfs_acl_zone;/* * Test for existence of access ACL attribute as efficiently as possible. */intxfs_acl_vhasacl_access( bhv_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( bhv_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 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 xfs_sort(). * 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> */ xfs_sort(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( bhv_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) { bhv_vattr_t va; va.va_mask = XFS_AT_MODE; error = xfs_getattr(xfs_vtoi(vp), &va, 0); 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( bhv_vnode_t *vp, int kind){ int error; VN_HOLD(vp); error = xfs_acl_allow_set(vp, kind); if (!error) { error = xfs_attr_remove(xfs_vtoi(vp), kind == _ACL_TYPE_DEFAULT? SGI_ACL_DEFAULT: SGI_ACL_FILE, ATTR_ROOT); if (error == ENOATTR) error = 0; /* 'scool */ } VN_RELE(vp); return -error;}intxfs_acl_vset( bhv_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( bhv_vnode_t *vp, int kind){ xfs_inode_t *ip = xfs_vtoi(vp); bhv_vattr_t va; int error; if (vp->i_flags & (S_IMMUTABLE|S_APPEND)) return EPERM; if (kind == _ACL_TYPE_DEFAULT && !VN_ISDIR(vp)) return ENOTDIR; if (vp->i_sb->s_flags & MS_RDONLY) return EROFS; va.va_mask = XFS_AT_UID; error = xfs_getattr(ip, &va, 0); if (error) return error; if (va.va_uid != current->fsuid && !capable(CAP_FOWNER)) return EPERM; return error;}/* * 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 matching 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. */STATIC intxfs_acl_capability_check( mode_t mode, cred_t *cr){ 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)) 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 */ matched.ae_perm = 0; md >>= 6; /* Normalize the bits for comparison */ for (i = 0; i < fap->acl_cnt; i++) { /* * Break out if we've got a user_obj entry or * a user entry and the mask (and have processed USER_OBJ) */ if (matched.ae_tag == ACL_USER_OBJ) break; if (matched.ae_tag == ACL_USER) { if (maskallows != -1 && seen_userobj) break; if (fap->acl_entry[i].ae_tag != ACL_MASK && fap->acl_entry[i].ae_tag != ACL_USER_OBJ) continue; } /* True if this entry allows the requested access */ allows = ((fap->acl_entry[i].ae_perm & md) == md); switch (fap->acl_entry[i].ae_tag) { case ACL_USER_OBJ: seen_userobj = 1; if (fuid != current->fsuid) continue; matched.ae_tag = ACL_USER_OBJ;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -