Loading fs/xfs/Kconfig +1 −0 Original line number Original line Diff line number Diff line Loading @@ -39,6 +39,7 @@ config XFS_QUOTA config XFS_POSIX_ACL config XFS_POSIX_ACL bool "XFS POSIX ACL support" bool "XFS POSIX ACL support" depends on XFS_FS depends on XFS_FS select FS_POSIX_ACL help help POSIX Access Control Lists (ACLs) support permissions for users and POSIX Access Control Lists (ACLs) support permissions for users and groups beyond the owner/group/world scheme. groups beyond the owner/group/world scheme. Loading fs/xfs/Makefile +2 −3 Original line number Original line Diff line number Diff line Loading @@ -40,7 +40,7 @@ xfs-$(CONFIG_PROC_FS) += quota/xfs_qm_stats.o endif endif xfs-$(CONFIG_XFS_RT) += xfs_rtalloc.o xfs-$(CONFIG_XFS_RT) += xfs_rtalloc.o xfs-$(CONFIG_XFS_POSIX_ACL) += xfs_acl.o xfs-$(CONFIG_XFS_POSIX_ACL) += $(XFS_LINUX)/xfs_acl.o xfs-$(CONFIG_PROC_FS) += $(XFS_LINUX)/xfs_stats.o xfs-$(CONFIG_PROC_FS) += $(XFS_LINUX)/xfs_stats.o xfs-$(CONFIG_SYSCTL) += $(XFS_LINUX)/xfs_sysctl.o xfs-$(CONFIG_SYSCTL) += $(XFS_LINUX)/xfs_sysctl.o xfs-$(CONFIG_COMPAT) += $(XFS_LINUX)/xfs_ioctl32.o xfs-$(CONFIG_COMPAT) += $(XFS_LINUX)/xfs_ioctl32.o Loading Loading @@ -88,8 +88,7 @@ xfs-y += xfs_alloc.o \ xfs_utils.o \ xfs_utils.o \ xfs_vnodeops.o \ xfs_vnodeops.o \ xfs_rw.o \ xfs_rw.o \ xfs_dmops.o \ xfs_dmops.o xfs_qmops.o xfs-$(CONFIG_XFS_TRACE) += xfs_btree_trace.o \ xfs-$(CONFIG_XFS_TRACE) += xfs_btree_trace.o \ xfs_dir2_trace.o xfs_dir2_trace.o Loading fs/xfs/linux-2.6/xfs_acl.c 0 → 100644 +523 −0 Original line number Original line Diff line number Diff line /* * Copyright (c) 2008, Christoph Hellwig * 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_acl.h" #include "xfs_attr.h" #include "xfs_bmap_btree.h" #include "xfs_inode.h" #include "xfs_vnodeops.h" #include <linux/xattr.h> #include <linux/posix_acl_xattr.h> #define XFS_ACL_NOT_CACHED ((void *)-1) /* * Locking scheme: * - all ACL updates are protected by inode->i_mutex, which is taken before * calling into this file. * - access and updates to the ip->i_acl and ip->i_default_acl pointers are * protected by inode->i_lock. */ STATIC struct posix_acl * xfs_acl_from_disk(struct xfs_acl *aclp) { struct posix_acl_entry *acl_e; struct posix_acl *acl; struct xfs_acl_entry *ace; int count, i; count = be32_to_cpu(aclp->acl_cnt); acl = posix_acl_alloc(count, GFP_KERNEL); if (!acl) return ERR_PTR(-ENOMEM); for (i = 0; i < count; i++) { acl_e = &acl->a_entries[i]; ace = &aclp->acl_entry[i]; /* * The tag is 32 bits on disk and 16 bits in core. * * Because every access to it goes through the core * format first this is not a problem. */ acl_e->e_tag = be32_to_cpu(ace->ae_tag); acl_e->e_perm = be16_to_cpu(ace->ae_perm); switch (acl_e->e_tag) { case ACL_USER: case ACL_GROUP: acl_e->e_id = be32_to_cpu(ace->ae_id); break; case ACL_USER_OBJ: case ACL_GROUP_OBJ: case ACL_MASK: case ACL_OTHER: acl_e->e_id = ACL_UNDEFINED_ID; break; default: goto fail; } } return acl; fail: posix_acl_release(acl); return ERR_PTR(-EINVAL); } STATIC void xfs_acl_to_disk(struct xfs_acl *aclp, const struct posix_acl *acl) { const struct posix_acl_entry *acl_e; struct xfs_acl_entry *ace; int i; aclp->acl_cnt = cpu_to_be32(acl->a_count); for (i = 0; i < acl->a_count; i++) { ace = &aclp->acl_entry[i]; acl_e = &acl->a_entries[i]; ace->ae_tag = cpu_to_be32(acl_e->e_tag); ace->ae_id = cpu_to_be32(acl_e->e_id); ace->ae_perm = cpu_to_be16(acl_e->e_perm); } } /* * Update the cached ACL pointer in the inode. * * Because we don't hold any locks while reading/writing the attribute * from/to disk another thread could have raced and updated the cached * ACL value before us. In that case we release the previous cached value * and update it with our new value. */ STATIC void xfs_update_cached_acl(struct inode *inode, struct posix_acl **p_acl, struct posix_acl *acl) { spin_lock(&inode->i_lock); if (*p_acl && *p_acl != XFS_ACL_NOT_CACHED) posix_acl_release(*p_acl); *p_acl = posix_acl_dup(acl); spin_unlock(&inode->i_lock); } struct posix_acl * xfs_get_acl(struct inode *inode, int type) { struct xfs_inode *ip = XFS_I(inode); struct posix_acl *acl = NULL, **p_acl; struct xfs_acl *xfs_acl; int len = sizeof(struct xfs_acl); char *ea_name; int error; switch (type) { case ACL_TYPE_ACCESS: ea_name = SGI_ACL_FILE; p_acl = &ip->i_acl; break; case ACL_TYPE_DEFAULT: ea_name = SGI_ACL_DEFAULT; p_acl = &ip->i_default_acl; break; default: return ERR_PTR(-EINVAL); } spin_lock(&inode->i_lock); if (*p_acl != XFS_ACL_NOT_CACHED) acl = posix_acl_dup(*p_acl); spin_unlock(&inode->i_lock); /* * If we have a cached ACLs value just return it, not need to * go out to the disk. */ if (acl) return acl; xfs_acl = kzalloc(sizeof(struct xfs_acl), GFP_KERNEL); if (!xfs_acl) return ERR_PTR(-ENOMEM); error = -xfs_attr_get(ip, ea_name, (char *)xfs_acl, &len, ATTR_ROOT); if (error) { /* * If the attribute doesn't exist make sure we have a negative * cache entry, for any other error assume it is transient and * leave the cache entry as XFS_ACL_NOT_CACHED. */ if (error == -ENOATTR) { acl = NULL; goto out_update_cache; } goto out; } acl = xfs_acl_from_disk(xfs_acl); if (IS_ERR(acl)) goto out; out_update_cache: xfs_update_cached_acl(inode, p_acl, acl); out: kfree(xfs_acl); return acl; } STATIC int xfs_set_acl(struct inode *inode, int type, struct posix_acl *acl) { struct xfs_inode *ip = XFS_I(inode); struct posix_acl **p_acl; char *ea_name; int error; if (S_ISLNK(inode->i_mode)) return -EOPNOTSUPP; switch (type) { case ACL_TYPE_ACCESS: ea_name = SGI_ACL_FILE; p_acl = &ip->i_acl; break; case ACL_TYPE_DEFAULT: if (!S_ISDIR(inode->i_mode)) return acl ? -EACCES : 0; ea_name = SGI_ACL_DEFAULT; p_acl = &ip->i_default_acl; break; default: return -EINVAL; } if (acl) { struct xfs_acl *xfs_acl; int len; xfs_acl = kzalloc(sizeof(struct xfs_acl), GFP_KERNEL); if (!xfs_acl) return -ENOMEM; xfs_acl_to_disk(xfs_acl, acl); len = sizeof(struct xfs_acl) - (sizeof(struct xfs_acl_entry) * (XFS_ACL_MAX_ENTRIES - acl->a_count)); error = -xfs_attr_set(ip, ea_name, (char *)xfs_acl, len, ATTR_ROOT); kfree(xfs_acl); } else { /* * A NULL ACL argument means we want to remove the ACL. */ error = -xfs_attr_remove(ip, ea_name, ATTR_ROOT); /* * If the attribute didn't exist to start with that's fine. */ if (error == -ENOATTR) error = 0; } if (!error) xfs_update_cached_acl(inode, p_acl, acl); return error; } int xfs_check_acl(struct inode *inode, int mask) { struct xfs_inode *ip = XFS_I(inode); struct posix_acl *acl; int error = -EAGAIN; xfs_itrace_entry(ip); /* * If there is no attribute fork no ACL exists on this inode and * we can skip the whole exercise. */ if (!XFS_IFORK_Q(ip)) return -EAGAIN; acl = xfs_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; } static int xfs_set_mode(struct inode *inode, mode_t mode) { int error = 0; if (mode != inode->i_mode) { struct iattr iattr; iattr.ia_valid = ATTR_MODE; iattr.ia_mode = mode; error = -xfs_setattr(XFS_I(inode), &iattr, XFS_ATTR_NOACL); } return error; } static int xfs_acl_exists(struct inode *inode, char *name) { int len = sizeof(struct xfs_acl); return (xfs_attr_get(XFS_I(inode), name, NULL, &len, ATTR_ROOT|ATTR_KERNOVAL) == 0); } int posix_acl_access_exists(struct inode *inode) { return xfs_acl_exists(inode, SGI_ACL_FILE); } int posix_acl_default_exists(struct inode *inode) { if (!S_ISDIR(inode->i_mode)) return 0; return xfs_acl_exists(inode, SGI_ACL_DEFAULT); } /* * No need for i_mutex because the inode is not yet exposed to the VFS. */ int xfs_inherit_acl(struct inode *inode, struct posix_acl *default_acl) { struct posix_acl *clone; mode_t mode; int error = 0, inherit = 0; if (S_ISDIR(inode->i_mode)) { error = xfs_set_acl(inode, ACL_TYPE_DEFAULT, default_acl); if (error) return error; } clone = posix_acl_clone(default_acl, GFP_KERNEL); if (!clone) return -ENOMEM; mode = inode->i_mode; error = posix_acl_create_masq(clone, &mode); if (error < 0) goto out_release_clone; /* * If posix_acl_create_masq returns a positive value we need to * inherit a permission that can't be represented using the Unix * mode bits and we actually need to set an ACL. */ if (error > 0) inherit = 1; error = xfs_set_mode(inode, mode); if (error) goto out_release_clone; if (inherit) error = xfs_set_acl(inode, ACL_TYPE_ACCESS, clone); out_release_clone: posix_acl_release(clone); return error; } int xfs_acl_chmod(struct inode *inode) { struct posix_acl *acl, *clone; int error; if (S_ISLNK(inode->i_mode)) return -EOPNOTSUPP; acl = xfs_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; error = posix_acl_chmod_masq(clone, inode->i_mode); if (!error) error = xfs_set_acl(inode, ACL_TYPE_ACCESS, clone); posix_acl_release(clone); return error; } void xfs_inode_init_acls(struct xfs_inode *ip) { /* * No need for locking, inode is not live yet. */ ip->i_acl = XFS_ACL_NOT_CACHED; ip->i_default_acl = XFS_ACL_NOT_CACHED; } void xfs_inode_clear_acls(struct xfs_inode *ip) { /* * No need for locking here, the inode is not live anymore * and just about to be freed. */ if (ip->i_acl != XFS_ACL_NOT_CACHED) posix_acl_release(ip->i_acl); if (ip->i_default_acl != XFS_ACL_NOT_CACHED) posix_acl_release(ip->i_default_acl); } /* * System xattr handlers. * * Currently Posix ACLs are the only system namespace extended attribute * handlers supported by XFS, so we just implement the handlers here. * If we ever support other system extended attributes this will need * some refactoring. */ static int xfs_decode_acl(const char *name) { if (strcmp(name, "posix_acl_access") == 0) return ACL_TYPE_ACCESS; else if (strcmp(name, "posix_acl_default") == 0) return ACL_TYPE_DEFAULT; return -EINVAL; } static int xfs_xattr_system_get(struct inode *inode, const char *name, void *value, size_t size) { struct posix_acl *acl; int type, error; type = xfs_decode_acl(name); if (type < 0) return type; acl = xfs_get_acl(inode, type); if (IS_ERR(acl)) return PTR_ERR(acl); if (acl == NULL) return -ENODATA; error = posix_acl_to_xattr(acl, value, size); posix_acl_release(acl); return error; } static int xfs_xattr_system_set(struct inode *inode, const char *name, const void *value, size_t size, int flags) { struct posix_acl *acl = NULL; int error = 0, type; type = xfs_decode_acl(name); if (type < 0) return type; if (flags & XATTR_CREATE) return -EINVAL; if (type == ACL_TYPE_DEFAULT && !S_ISDIR(inode->i_mode)) return value ? -EACCES : 0; if ((current_fsuid() != inode->i_uid) && !capable(CAP_FOWNER)) return -EPERM; if (!value) goto set_acl; acl = posix_acl_from_xattr(value, size); if (!acl) { /* * acl_set_file(3) may request that we set default ACLs with * zero length -- defend (gracefully) against that here. */ goto out; } if (IS_ERR(acl)) { error = PTR_ERR(acl); goto out; } error = posix_acl_valid(acl); if (error) goto out_release; error = -EINVAL; if (acl->a_count > XFS_ACL_MAX_ENTRIES) goto out_release; if (type == ACL_TYPE_ACCESS) { mode_t mode = inode->i_mode; error = posix_acl_equiv_mode(acl, &mode); if (error <= 0) { posix_acl_release(acl); acl = NULL; if (error < 0) return error; } error = xfs_set_mode(inode, mode); if (error) goto out_release; } set_acl: error = xfs_set_acl(inode, type, acl); out_release: posix_acl_release(acl); out: return error; } struct xattr_handler xfs_xattr_system_handler = { .prefix = XATTR_SYSTEM_PREFIX, .get = xfs_xattr_system_get, .set = xfs_xattr_system_set, }; fs/xfs/linux-2.6/xfs_ioctl.c +13 −12 Original line number Original line Diff line number Diff line Loading @@ -41,7 +41,6 @@ #include "xfs_itable.h" #include "xfs_itable.h" #include "xfs_error.h" #include "xfs_error.h" #include "xfs_rw.h" #include "xfs_rw.h" #include "xfs_acl.h" #include "xfs_attr.h" #include "xfs_attr.h" #include "xfs_bmap.h" #include "xfs_bmap.h" #include "xfs_buf_item.h" #include "xfs_buf_item.h" Loading Loading @@ -899,7 +898,8 @@ xfs_ioctl_setattr( struct xfs_mount *mp = ip->i_mount; struct xfs_mount *mp = ip->i_mount; struct xfs_trans *tp; struct xfs_trans *tp; unsigned int lock_flags = 0; unsigned int lock_flags = 0; struct xfs_dquot *udqp = NULL, *gdqp = NULL; struct xfs_dquot *udqp = NULL; struct xfs_dquot *gdqp = NULL; struct xfs_dquot *olddquot = NULL; struct xfs_dquot *olddquot = NULL; int code; int code; Loading @@ -919,7 +919,7 @@ xfs_ioctl_setattr( * because the i_*dquot fields will get updated anyway. * because the i_*dquot fields will get updated anyway. */ */ if (XFS_IS_QUOTA_ON(mp) && (mask & FSX_PROJID)) { if (XFS_IS_QUOTA_ON(mp) && (mask & FSX_PROJID)) { code = XFS_QM_DQVOPALLOC(mp, ip, ip->i_d.di_uid, code = xfs_qm_vop_dqalloc(ip, ip->i_d.di_uid, ip->i_d.di_gid, fa->fsx_projid, ip->i_d.di_gid, fa->fsx_projid, XFS_QMOPT_PQUOTA, &udqp, &gdqp); XFS_QMOPT_PQUOTA, &udqp, &gdqp); if (code) if (code) Loading Loading @@ -954,10 +954,11 @@ xfs_ioctl_setattr( * Do a quota reservation only if projid is actually going to change. * Do a quota reservation only if projid is actually going to change. */ */ if (mask & FSX_PROJID) { if (mask & FSX_PROJID) { if (XFS_IS_PQUOTA_ON(mp) && if (XFS_IS_QUOTA_RUNNING(mp) && XFS_IS_PQUOTA_ON(mp) && ip->i_d.di_projid != fa->fsx_projid) { ip->i_d.di_projid != fa->fsx_projid) { ASSERT(tp); ASSERT(tp); code = XFS_QM_DQVOPCHOWNRESV(mp, tp, ip, udqp, gdqp, code = xfs_qm_vop_chown_reserve(tp, ip, udqp, gdqp, capable(CAP_FOWNER) ? capable(CAP_FOWNER) ? XFS_QMOPT_FORCE_RES : 0); XFS_QMOPT_FORCE_RES : 0); if (code) /* out of quota */ if (code) /* out of quota */ Loading Loading @@ -1059,8 +1060,8 @@ xfs_ioctl_setattr( * in the transaction. * in the transaction. */ */ if (ip->i_d.di_projid != fa->fsx_projid) { if (ip->i_d.di_projid != fa->fsx_projid) { if (XFS_IS_PQUOTA_ON(mp)) { if (XFS_IS_QUOTA_RUNNING(mp) && XFS_IS_PQUOTA_ON(mp)) { olddquot = XFS_QM_DQVOPCHOWN(mp, tp, ip, olddquot = xfs_qm_vop_chown(tp, ip, &ip->i_gdquot, gdqp); &ip->i_gdquot, gdqp); } } ip->i_d.di_projid = fa->fsx_projid; ip->i_d.di_projid = fa->fsx_projid; Loading Loading @@ -1106,9 +1107,9 @@ xfs_ioctl_setattr( /* /* * Release any dquot(s) the inode had kept before chown. * Release any dquot(s) the inode had kept before chown. */ */ XFS_QM_DQRELE(mp, olddquot); xfs_qm_dqrele(olddquot); XFS_QM_DQRELE(mp, udqp); xfs_qm_dqrele(udqp); XFS_QM_DQRELE(mp, gdqp); xfs_qm_dqrele(gdqp); if (code) if (code) return code; return code; Loading @@ -1122,8 +1123,8 @@ xfs_ioctl_setattr( return 0; return 0; error_return: error_return: XFS_QM_DQRELE(mp, udqp); xfs_qm_dqrele(udqp); XFS_QM_DQRELE(mp, gdqp); xfs_qm_dqrele(gdqp); xfs_trans_cancel(tp, 0); xfs_trans_cancel(tp, 0); if (lock_flags) if (lock_flags) xfs_iunlock(ip, lock_flags); xfs_iunlock(ip, lock_flags); Loading fs/xfs/linux-2.6/xfs_iops.c +12 −39 Original line number Original line Diff line number Diff line Loading @@ -17,6 +17,7 @@ */ */ #include "xfs.h" #include "xfs.h" #include "xfs_fs.h" #include "xfs_fs.h" #include "xfs_acl.h" #include "xfs_bit.h" #include "xfs_bit.h" #include "xfs_log.h" #include "xfs_log.h" #include "xfs_inum.h" #include "xfs_inum.h" Loading Loading @@ -51,6 +52,7 @@ #include <linux/capability.h> #include <linux/capability.h> #include <linux/xattr.h> #include <linux/xattr.h> #include <linux/namei.h> #include <linux/namei.h> #include <linux/posix_acl.h> #include <linux/security.h> #include <linux/security.h> #include <linux/falloc.h> #include <linux/falloc.h> #include <linux/fiemap.h> #include <linux/fiemap.h> Loading Loading @@ -202,9 +204,8 @@ xfs_vn_mknod( { { struct inode *inode; struct inode *inode; struct xfs_inode *ip = NULL; struct xfs_inode *ip = NULL; xfs_acl_t *default_acl = NULL; struct posix_acl *default_acl = NULL; struct xfs_name name; struct xfs_name name; int (*test_default_acl)(struct inode *) = _ACL_DEFAULT_EXISTS; int error; int error; /* /* Loading @@ -219,18 +220,14 @@ xfs_vn_mknod( rdev = 0; rdev = 0; } } if (test_default_acl && test_default_acl(dir)) { if (IS_POSIXACL(dir)) { if (!_ACL_ALLOC(default_acl)) { default_acl = xfs_get_acl(dir, ACL_TYPE_DEFAULT); return -ENOMEM; if (IS_ERR(default_acl)) } return -PTR_ERR(default_acl); if (!_ACL_GET_DEFAULT(dir, default_acl)) { _ACL_FREE(default_acl); default_acl = NULL; } } if (IS_POSIXACL(dir) && !default_acl) if (!default_acl) mode &= ~current_umask(); mode &= ~current_umask(); } xfs_dentry_to_name(&name, dentry); xfs_dentry_to_name(&name, dentry); error = xfs_create(XFS_I(dir), &name, mode, rdev, &ip, NULL); error = xfs_create(XFS_I(dir), &name, mode, rdev, &ip, NULL); Loading @@ -244,10 +241,10 @@ xfs_vn_mknod( goto out_cleanup_inode; goto out_cleanup_inode; if (default_acl) { if (default_acl) { error = _ACL_INHERIT(inode, mode, default_acl); error = -xfs_inherit_acl(inode, default_acl); if (unlikely(error)) if (unlikely(error)) goto out_cleanup_inode; goto out_cleanup_inode; _ACL_FREE(default_acl); posix_acl_release(default_acl); } } Loading @@ -257,8 +254,7 @@ xfs_vn_mknod( out_cleanup_inode: out_cleanup_inode: xfs_cleanup_inode(dir, inode, dentry); xfs_cleanup_inode(dir, inode, dentry); out_free_acl: out_free_acl: if (default_acl) posix_acl_release(default_acl); _ACL_FREE(default_acl); return -error; return -error; } } Loading Loading @@ -488,26 +484,6 @@ xfs_vn_put_link( kfree(s); kfree(s); } } #ifdef CONFIG_XFS_POSIX_ACL STATIC int xfs_check_acl( struct inode *inode, int mask) { struct xfs_inode *ip = XFS_I(inode); int error; xfs_itrace_entry(ip); if (XFS_IFORK_Q(ip)) { error = xfs_acl_iaccess(ip, mask, NULL); if (error != -1) return -error; } return -EAGAIN; } STATIC int STATIC int xfs_vn_permission( xfs_vn_permission( struct inode *inode, struct inode *inode, Loading @@ -515,9 +491,6 @@ xfs_vn_permission( { { return generic_permission(inode, mask, xfs_check_acl); return generic_permission(inode, mask, xfs_check_acl); } } #else #define xfs_vn_permission NULL #endif STATIC int STATIC int xfs_vn_getattr( xfs_vn_getattr( Loading Loading
fs/xfs/Kconfig +1 −0 Original line number Original line Diff line number Diff line Loading @@ -39,6 +39,7 @@ config XFS_QUOTA config XFS_POSIX_ACL config XFS_POSIX_ACL bool "XFS POSIX ACL support" bool "XFS POSIX ACL support" depends on XFS_FS depends on XFS_FS select FS_POSIX_ACL help help POSIX Access Control Lists (ACLs) support permissions for users and POSIX Access Control Lists (ACLs) support permissions for users and groups beyond the owner/group/world scheme. groups beyond the owner/group/world scheme. Loading
fs/xfs/Makefile +2 −3 Original line number Original line Diff line number Diff line Loading @@ -40,7 +40,7 @@ xfs-$(CONFIG_PROC_FS) += quota/xfs_qm_stats.o endif endif xfs-$(CONFIG_XFS_RT) += xfs_rtalloc.o xfs-$(CONFIG_XFS_RT) += xfs_rtalloc.o xfs-$(CONFIG_XFS_POSIX_ACL) += xfs_acl.o xfs-$(CONFIG_XFS_POSIX_ACL) += $(XFS_LINUX)/xfs_acl.o xfs-$(CONFIG_PROC_FS) += $(XFS_LINUX)/xfs_stats.o xfs-$(CONFIG_PROC_FS) += $(XFS_LINUX)/xfs_stats.o xfs-$(CONFIG_SYSCTL) += $(XFS_LINUX)/xfs_sysctl.o xfs-$(CONFIG_SYSCTL) += $(XFS_LINUX)/xfs_sysctl.o xfs-$(CONFIG_COMPAT) += $(XFS_LINUX)/xfs_ioctl32.o xfs-$(CONFIG_COMPAT) += $(XFS_LINUX)/xfs_ioctl32.o Loading Loading @@ -88,8 +88,7 @@ xfs-y += xfs_alloc.o \ xfs_utils.o \ xfs_utils.o \ xfs_vnodeops.o \ xfs_vnodeops.o \ xfs_rw.o \ xfs_rw.o \ xfs_dmops.o \ xfs_dmops.o xfs_qmops.o xfs-$(CONFIG_XFS_TRACE) += xfs_btree_trace.o \ xfs-$(CONFIG_XFS_TRACE) += xfs_btree_trace.o \ xfs_dir2_trace.o xfs_dir2_trace.o Loading
fs/xfs/linux-2.6/xfs_acl.c 0 → 100644 +523 −0 Original line number Original line Diff line number Diff line /* * Copyright (c) 2008, Christoph Hellwig * 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_acl.h" #include "xfs_attr.h" #include "xfs_bmap_btree.h" #include "xfs_inode.h" #include "xfs_vnodeops.h" #include <linux/xattr.h> #include <linux/posix_acl_xattr.h> #define XFS_ACL_NOT_CACHED ((void *)-1) /* * Locking scheme: * - all ACL updates are protected by inode->i_mutex, which is taken before * calling into this file. * - access and updates to the ip->i_acl and ip->i_default_acl pointers are * protected by inode->i_lock. */ STATIC struct posix_acl * xfs_acl_from_disk(struct xfs_acl *aclp) { struct posix_acl_entry *acl_e; struct posix_acl *acl; struct xfs_acl_entry *ace; int count, i; count = be32_to_cpu(aclp->acl_cnt); acl = posix_acl_alloc(count, GFP_KERNEL); if (!acl) return ERR_PTR(-ENOMEM); for (i = 0; i < count; i++) { acl_e = &acl->a_entries[i]; ace = &aclp->acl_entry[i]; /* * The tag is 32 bits on disk and 16 bits in core. * * Because every access to it goes through the core * format first this is not a problem. */ acl_e->e_tag = be32_to_cpu(ace->ae_tag); acl_e->e_perm = be16_to_cpu(ace->ae_perm); switch (acl_e->e_tag) { case ACL_USER: case ACL_GROUP: acl_e->e_id = be32_to_cpu(ace->ae_id); break; case ACL_USER_OBJ: case ACL_GROUP_OBJ: case ACL_MASK: case ACL_OTHER: acl_e->e_id = ACL_UNDEFINED_ID; break; default: goto fail; } } return acl; fail: posix_acl_release(acl); return ERR_PTR(-EINVAL); } STATIC void xfs_acl_to_disk(struct xfs_acl *aclp, const struct posix_acl *acl) { const struct posix_acl_entry *acl_e; struct xfs_acl_entry *ace; int i; aclp->acl_cnt = cpu_to_be32(acl->a_count); for (i = 0; i < acl->a_count; i++) { ace = &aclp->acl_entry[i]; acl_e = &acl->a_entries[i]; ace->ae_tag = cpu_to_be32(acl_e->e_tag); ace->ae_id = cpu_to_be32(acl_e->e_id); ace->ae_perm = cpu_to_be16(acl_e->e_perm); } } /* * Update the cached ACL pointer in the inode. * * Because we don't hold any locks while reading/writing the attribute * from/to disk another thread could have raced and updated the cached * ACL value before us. In that case we release the previous cached value * and update it with our new value. */ STATIC void xfs_update_cached_acl(struct inode *inode, struct posix_acl **p_acl, struct posix_acl *acl) { spin_lock(&inode->i_lock); if (*p_acl && *p_acl != XFS_ACL_NOT_CACHED) posix_acl_release(*p_acl); *p_acl = posix_acl_dup(acl); spin_unlock(&inode->i_lock); } struct posix_acl * xfs_get_acl(struct inode *inode, int type) { struct xfs_inode *ip = XFS_I(inode); struct posix_acl *acl = NULL, **p_acl; struct xfs_acl *xfs_acl; int len = sizeof(struct xfs_acl); char *ea_name; int error; switch (type) { case ACL_TYPE_ACCESS: ea_name = SGI_ACL_FILE; p_acl = &ip->i_acl; break; case ACL_TYPE_DEFAULT: ea_name = SGI_ACL_DEFAULT; p_acl = &ip->i_default_acl; break; default: return ERR_PTR(-EINVAL); } spin_lock(&inode->i_lock); if (*p_acl != XFS_ACL_NOT_CACHED) acl = posix_acl_dup(*p_acl); spin_unlock(&inode->i_lock); /* * If we have a cached ACLs value just return it, not need to * go out to the disk. */ if (acl) return acl; xfs_acl = kzalloc(sizeof(struct xfs_acl), GFP_KERNEL); if (!xfs_acl) return ERR_PTR(-ENOMEM); error = -xfs_attr_get(ip, ea_name, (char *)xfs_acl, &len, ATTR_ROOT); if (error) { /* * If the attribute doesn't exist make sure we have a negative * cache entry, for any other error assume it is transient and * leave the cache entry as XFS_ACL_NOT_CACHED. */ if (error == -ENOATTR) { acl = NULL; goto out_update_cache; } goto out; } acl = xfs_acl_from_disk(xfs_acl); if (IS_ERR(acl)) goto out; out_update_cache: xfs_update_cached_acl(inode, p_acl, acl); out: kfree(xfs_acl); return acl; } STATIC int xfs_set_acl(struct inode *inode, int type, struct posix_acl *acl) { struct xfs_inode *ip = XFS_I(inode); struct posix_acl **p_acl; char *ea_name; int error; if (S_ISLNK(inode->i_mode)) return -EOPNOTSUPP; switch (type) { case ACL_TYPE_ACCESS: ea_name = SGI_ACL_FILE; p_acl = &ip->i_acl; break; case ACL_TYPE_DEFAULT: if (!S_ISDIR(inode->i_mode)) return acl ? -EACCES : 0; ea_name = SGI_ACL_DEFAULT; p_acl = &ip->i_default_acl; break; default: return -EINVAL; } if (acl) { struct xfs_acl *xfs_acl; int len; xfs_acl = kzalloc(sizeof(struct xfs_acl), GFP_KERNEL); if (!xfs_acl) return -ENOMEM; xfs_acl_to_disk(xfs_acl, acl); len = sizeof(struct xfs_acl) - (sizeof(struct xfs_acl_entry) * (XFS_ACL_MAX_ENTRIES - acl->a_count)); error = -xfs_attr_set(ip, ea_name, (char *)xfs_acl, len, ATTR_ROOT); kfree(xfs_acl); } else { /* * A NULL ACL argument means we want to remove the ACL. */ error = -xfs_attr_remove(ip, ea_name, ATTR_ROOT); /* * If the attribute didn't exist to start with that's fine. */ if (error == -ENOATTR) error = 0; } if (!error) xfs_update_cached_acl(inode, p_acl, acl); return error; } int xfs_check_acl(struct inode *inode, int mask) { struct xfs_inode *ip = XFS_I(inode); struct posix_acl *acl; int error = -EAGAIN; xfs_itrace_entry(ip); /* * If there is no attribute fork no ACL exists on this inode and * we can skip the whole exercise. */ if (!XFS_IFORK_Q(ip)) return -EAGAIN; acl = xfs_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; } static int xfs_set_mode(struct inode *inode, mode_t mode) { int error = 0; if (mode != inode->i_mode) { struct iattr iattr; iattr.ia_valid = ATTR_MODE; iattr.ia_mode = mode; error = -xfs_setattr(XFS_I(inode), &iattr, XFS_ATTR_NOACL); } return error; } static int xfs_acl_exists(struct inode *inode, char *name) { int len = sizeof(struct xfs_acl); return (xfs_attr_get(XFS_I(inode), name, NULL, &len, ATTR_ROOT|ATTR_KERNOVAL) == 0); } int posix_acl_access_exists(struct inode *inode) { return xfs_acl_exists(inode, SGI_ACL_FILE); } int posix_acl_default_exists(struct inode *inode) { if (!S_ISDIR(inode->i_mode)) return 0; return xfs_acl_exists(inode, SGI_ACL_DEFAULT); } /* * No need for i_mutex because the inode is not yet exposed to the VFS. */ int xfs_inherit_acl(struct inode *inode, struct posix_acl *default_acl) { struct posix_acl *clone; mode_t mode; int error = 0, inherit = 0; if (S_ISDIR(inode->i_mode)) { error = xfs_set_acl(inode, ACL_TYPE_DEFAULT, default_acl); if (error) return error; } clone = posix_acl_clone(default_acl, GFP_KERNEL); if (!clone) return -ENOMEM; mode = inode->i_mode; error = posix_acl_create_masq(clone, &mode); if (error < 0) goto out_release_clone; /* * If posix_acl_create_masq returns a positive value we need to * inherit a permission that can't be represented using the Unix * mode bits and we actually need to set an ACL. */ if (error > 0) inherit = 1; error = xfs_set_mode(inode, mode); if (error) goto out_release_clone; if (inherit) error = xfs_set_acl(inode, ACL_TYPE_ACCESS, clone); out_release_clone: posix_acl_release(clone); return error; } int xfs_acl_chmod(struct inode *inode) { struct posix_acl *acl, *clone; int error; if (S_ISLNK(inode->i_mode)) return -EOPNOTSUPP; acl = xfs_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; error = posix_acl_chmod_masq(clone, inode->i_mode); if (!error) error = xfs_set_acl(inode, ACL_TYPE_ACCESS, clone); posix_acl_release(clone); return error; } void xfs_inode_init_acls(struct xfs_inode *ip) { /* * No need for locking, inode is not live yet. */ ip->i_acl = XFS_ACL_NOT_CACHED; ip->i_default_acl = XFS_ACL_NOT_CACHED; } void xfs_inode_clear_acls(struct xfs_inode *ip) { /* * No need for locking here, the inode is not live anymore * and just about to be freed. */ if (ip->i_acl != XFS_ACL_NOT_CACHED) posix_acl_release(ip->i_acl); if (ip->i_default_acl != XFS_ACL_NOT_CACHED) posix_acl_release(ip->i_default_acl); } /* * System xattr handlers. * * Currently Posix ACLs are the only system namespace extended attribute * handlers supported by XFS, so we just implement the handlers here. * If we ever support other system extended attributes this will need * some refactoring. */ static int xfs_decode_acl(const char *name) { if (strcmp(name, "posix_acl_access") == 0) return ACL_TYPE_ACCESS; else if (strcmp(name, "posix_acl_default") == 0) return ACL_TYPE_DEFAULT; return -EINVAL; } static int xfs_xattr_system_get(struct inode *inode, const char *name, void *value, size_t size) { struct posix_acl *acl; int type, error; type = xfs_decode_acl(name); if (type < 0) return type; acl = xfs_get_acl(inode, type); if (IS_ERR(acl)) return PTR_ERR(acl); if (acl == NULL) return -ENODATA; error = posix_acl_to_xattr(acl, value, size); posix_acl_release(acl); return error; } static int xfs_xattr_system_set(struct inode *inode, const char *name, const void *value, size_t size, int flags) { struct posix_acl *acl = NULL; int error = 0, type; type = xfs_decode_acl(name); if (type < 0) return type; if (flags & XATTR_CREATE) return -EINVAL; if (type == ACL_TYPE_DEFAULT && !S_ISDIR(inode->i_mode)) return value ? -EACCES : 0; if ((current_fsuid() != inode->i_uid) && !capable(CAP_FOWNER)) return -EPERM; if (!value) goto set_acl; acl = posix_acl_from_xattr(value, size); if (!acl) { /* * acl_set_file(3) may request that we set default ACLs with * zero length -- defend (gracefully) against that here. */ goto out; } if (IS_ERR(acl)) { error = PTR_ERR(acl); goto out; } error = posix_acl_valid(acl); if (error) goto out_release; error = -EINVAL; if (acl->a_count > XFS_ACL_MAX_ENTRIES) goto out_release; if (type == ACL_TYPE_ACCESS) { mode_t mode = inode->i_mode; error = posix_acl_equiv_mode(acl, &mode); if (error <= 0) { posix_acl_release(acl); acl = NULL; if (error < 0) return error; } error = xfs_set_mode(inode, mode); if (error) goto out_release; } set_acl: error = xfs_set_acl(inode, type, acl); out_release: posix_acl_release(acl); out: return error; } struct xattr_handler xfs_xattr_system_handler = { .prefix = XATTR_SYSTEM_PREFIX, .get = xfs_xattr_system_get, .set = xfs_xattr_system_set, };
fs/xfs/linux-2.6/xfs_ioctl.c +13 −12 Original line number Original line Diff line number Diff line Loading @@ -41,7 +41,6 @@ #include "xfs_itable.h" #include "xfs_itable.h" #include "xfs_error.h" #include "xfs_error.h" #include "xfs_rw.h" #include "xfs_rw.h" #include "xfs_acl.h" #include "xfs_attr.h" #include "xfs_attr.h" #include "xfs_bmap.h" #include "xfs_bmap.h" #include "xfs_buf_item.h" #include "xfs_buf_item.h" Loading Loading @@ -899,7 +898,8 @@ xfs_ioctl_setattr( struct xfs_mount *mp = ip->i_mount; struct xfs_mount *mp = ip->i_mount; struct xfs_trans *tp; struct xfs_trans *tp; unsigned int lock_flags = 0; unsigned int lock_flags = 0; struct xfs_dquot *udqp = NULL, *gdqp = NULL; struct xfs_dquot *udqp = NULL; struct xfs_dquot *gdqp = NULL; struct xfs_dquot *olddquot = NULL; struct xfs_dquot *olddquot = NULL; int code; int code; Loading @@ -919,7 +919,7 @@ xfs_ioctl_setattr( * because the i_*dquot fields will get updated anyway. * because the i_*dquot fields will get updated anyway. */ */ if (XFS_IS_QUOTA_ON(mp) && (mask & FSX_PROJID)) { if (XFS_IS_QUOTA_ON(mp) && (mask & FSX_PROJID)) { code = XFS_QM_DQVOPALLOC(mp, ip, ip->i_d.di_uid, code = xfs_qm_vop_dqalloc(ip, ip->i_d.di_uid, ip->i_d.di_gid, fa->fsx_projid, ip->i_d.di_gid, fa->fsx_projid, XFS_QMOPT_PQUOTA, &udqp, &gdqp); XFS_QMOPT_PQUOTA, &udqp, &gdqp); if (code) if (code) Loading Loading @@ -954,10 +954,11 @@ xfs_ioctl_setattr( * Do a quota reservation only if projid is actually going to change. * Do a quota reservation only if projid is actually going to change. */ */ if (mask & FSX_PROJID) { if (mask & FSX_PROJID) { if (XFS_IS_PQUOTA_ON(mp) && if (XFS_IS_QUOTA_RUNNING(mp) && XFS_IS_PQUOTA_ON(mp) && ip->i_d.di_projid != fa->fsx_projid) { ip->i_d.di_projid != fa->fsx_projid) { ASSERT(tp); ASSERT(tp); code = XFS_QM_DQVOPCHOWNRESV(mp, tp, ip, udqp, gdqp, code = xfs_qm_vop_chown_reserve(tp, ip, udqp, gdqp, capable(CAP_FOWNER) ? capable(CAP_FOWNER) ? XFS_QMOPT_FORCE_RES : 0); XFS_QMOPT_FORCE_RES : 0); if (code) /* out of quota */ if (code) /* out of quota */ Loading Loading @@ -1059,8 +1060,8 @@ xfs_ioctl_setattr( * in the transaction. * in the transaction. */ */ if (ip->i_d.di_projid != fa->fsx_projid) { if (ip->i_d.di_projid != fa->fsx_projid) { if (XFS_IS_PQUOTA_ON(mp)) { if (XFS_IS_QUOTA_RUNNING(mp) && XFS_IS_PQUOTA_ON(mp)) { olddquot = XFS_QM_DQVOPCHOWN(mp, tp, ip, olddquot = xfs_qm_vop_chown(tp, ip, &ip->i_gdquot, gdqp); &ip->i_gdquot, gdqp); } } ip->i_d.di_projid = fa->fsx_projid; ip->i_d.di_projid = fa->fsx_projid; Loading Loading @@ -1106,9 +1107,9 @@ xfs_ioctl_setattr( /* /* * Release any dquot(s) the inode had kept before chown. * Release any dquot(s) the inode had kept before chown. */ */ XFS_QM_DQRELE(mp, olddquot); xfs_qm_dqrele(olddquot); XFS_QM_DQRELE(mp, udqp); xfs_qm_dqrele(udqp); XFS_QM_DQRELE(mp, gdqp); xfs_qm_dqrele(gdqp); if (code) if (code) return code; return code; Loading @@ -1122,8 +1123,8 @@ xfs_ioctl_setattr( return 0; return 0; error_return: error_return: XFS_QM_DQRELE(mp, udqp); xfs_qm_dqrele(udqp); XFS_QM_DQRELE(mp, gdqp); xfs_qm_dqrele(gdqp); xfs_trans_cancel(tp, 0); xfs_trans_cancel(tp, 0); if (lock_flags) if (lock_flags) xfs_iunlock(ip, lock_flags); xfs_iunlock(ip, lock_flags); Loading
fs/xfs/linux-2.6/xfs_iops.c +12 −39 Original line number Original line Diff line number Diff line Loading @@ -17,6 +17,7 @@ */ */ #include "xfs.h" #include "xfs.h" #include "xfs_fs.h" #include "xfs_fs.h" #include "xfs_acl.h" #include "xfs_bit.h" #include "xfs_bit.h" #include "xfs_log.h" #include "xfs_log.h" #include "xfs_inum.h" #include "xfs_inum.h" Loading Loading @@ -51,6 +52,7 @@ #include <linux/capability.h> #include <linux/capability.h> #include <linux/xattr.h> #include <linux/xattr.h> #include <linux/namei.h> #include <linux/namei.h> #include <linux/posix_acl.h> #include <linux/security.h> #include <linux/security.h> #include <linux/falloc.h> #include <linux/falloc.h> #include <linux/fiemap.h> #include <linux/fiemap.h> Loading Loading @@ -202,9 +204,8 @@ xfs_vn_mknod( { { struct inode *inode; struct inode *inode; struct xfs_inode *ip = NULL; struct xfs_inode *ip = NULL; xfs_acl_t *default_acl = NULL; struct posix_acl *default_acl = NULL; struct xfs_name name; struct xfs_name name; int (*test_default_acl)(struct inode *) = _ACL_DEFAULT_EXISTS; int error; int error; /* /* Loading @@ -219,18 +220,14 @@ xfs_vn_mknod( rdev = 0; rdev = 0; } } if (test_default_acl && test_default_acl(dir)) { if (IS_POSIXACL(dir)) { if (!_ACL_ALLOC(default_acl)) { default_acl = xfs_get_acl(dir, ACL_TYPE_DEFAULT); return -ENOMEM; if (IS_ERR(default_acl)) } return -PTR_ERR(default_acl); if (!_ACL_GET_DEFAULT(dir, default_acl)) { _ACL_FREE(default_acl); default_acl = NULL; } } if (IS_POSIXACL(dir) && !default_acl) if (!default_acl) mode &= ~current_umask(); mode &= ~current_umask(); } xfs_dentry_to_name(&name, dentry); xfs_dentry_to_name(&name, dentry); error = xfs_create(XFS_I(dir), &name, mode, rdev, &ip, NULL); error = xfs_create(XFS_I(dir), &name, mode, rdev, &ip, NULL); Loading @@ -244,10 +241,10 @@ xfs_vn_mknod( goto out_cleanup_inode; goto out_cleanup_inode; if (default_acl) { if (default_acl) { error = _ACL_INHERIT(inode, mode, default_acl); error = -xfs_inherit_acl(inode, default_acl); if (unlikely(error)) if (unlikely(error)) goto out_cleanup_inode; goto out_cleanup_inode; _ACL_FREE(default_acl); posix_acl_release(default_acl); } } Loading @@ -257,8 +254,7 @@ xfs_vn_mknod( out_cleanup_inode: out_cleanup_inode: xfs_cleanup_inode(dir, inode, dentry); xfs_cleanup_inode(dir, inode, dentry); out_free_acl: out_free_acl: if (default_acl) posix_acl_release(default_acl); _ACL_FREE(default_acl); return -error; return -error; } } Loading Loading @@ -488,26 +484,6 @@ xfs_vn_put_link( kfree(s); kfree(s); } } #ifdef CONFIG_XFS_POSIX_ACL STATIC int xfs_check_acl( struct inode *inode, int mask) { struct xfs_inode *ip = XFS_I(inode); int error; xfs_itrace_entry(ip); if (XFS_IFORK_Q(ip)) { error = xfs_acl_iaccess(ip, mask, NULL); if (error != -1) return -error; } return -EAGAIN; } STATIC int STATIC int xfs_vn_permission( xfs_vn_permission( struct inode *inode, struct inode *inode, Loading @@ -515,9 +491,6 @@ xfs_vn_permission( { { return generic_permission(inode, mask, xfs_check_acl); return generic_permission(inode, mask, xfs_check_acl); } } #else #define xfs_vn_permission NULL #endif STATIC int STATIC int xfs_vn_getattr( xfs_vn_getattr( Loading