/*************************************************************************** * * Copyright (C) 2001 International Business Machines * All rights reserved. * * This file is part of the GPFS mmfslinux kernel module. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written * permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * *************************************************************************** */ /* @(#)00 1.19.1.1 src/avs/fs/mmfs/ts/kernext/gpl-linux/acl.c, mmfs, avs_rgpfs24, rgpfs24s012a 4/17/07 15:54:41 */ #if LINUX_KERNEL_VERSION > 2060000 #ifdef CONFIG_FS_POSIX_ACL #include #include #include #include #include #include #include #include #include /* Defines from posix_acl.h. We can not include that file because it conflicts with GPFS defines in cxiAclUser.h. We can rename all of GPFS defines but it will require many changes. */ #define LINUX_ACL_UNDEFINED_ID (-1) /* e_tag entry in struct posix_acl_entry */ #define LINUX_ACL_USER_OBJ (0x01) #define LINUX_ACL_USER (0x02) #define LINUX_ACL_GROUP_OBJ (0x04) #define LINUX_ACL_GROUP (0x08) #define LINUX_ACL_MASK (0x10) #define LINUX_ACL_OTHER (0x20) /* permissions in the e_perm field */ #define LINUX_ACL_READ (0x04) #define LINUX_ACL_WRITE (0x02) #define LINUX_ACL_EXECUTE (0x01) struct linux_posix_acl_entry { short e_tag; unsigned short e_perm; unsigned int e_id; }; struct linux_posix_acl { atomic_t a_refcount; unsigned int a_count; struct linux_posix_acl_entry a_entries[0]; }; struct linux_posix_acl * posix_acl_from_xattr(const void *value, size_t size); extern int posix_acl_valid(const struct linux_posix_acl *); int posix_acl_to_xattr(const struct linux_posix_acl *acl, void *buffer, size_t size); #define PERM_MASK_2_POSIX(perm) perm&(LINUX_ACL_READ|LINUX_ACL_WRITE|LINUX_ACL_EXECUTE) #define PERM_MASK_2_ACL(perm) perm&(ACL_PERM_EXECUTE|ACL_PERM_WRITE|ACL_PERM_READ) /* move data from ACL to linux_posix_acl */ int ACL_2_posix_acl(ACL *tmpaclP, const void *buf, size_t buf_size) { int i, len; int rc = 0, count = 0; acl_entry_t *aceP; struct linux_posix_acl *aclP = (struct linux_posix_acl *)buf; ENTER(0); memset(aclP, '\0', buf_size); TRACE2(TRACE_VNODE, 3, TRCID_LINUXOPS_ACL2POSIX_ENTER, "ACL_2_posix_acl: enter size %d buf_size %d\n", tmpaclP->aclLength, buf_size); /* POSIX ACLs only. Other types require additional translations. */ if (tmpaclP->aclVersion != ACL_FIRST_VERSION) { rc = -EOPNOTSUPP; goto xerror; } if (buf_size == 0) { rc = tmpaclP->aclLength; goto xerror; } /* Get the address of the first extended acl entry */ aceP = ACL_ENTRY_START(tmpaclP, tmpaclP->aclVersion); len = tmpaclP->aclLength - ACL_HEADER_SIZE_V1; buf_size -= sizeof(struct linux_posix_acl); i = 0; while ((len > 4) && (len >= aceP->aceLength)) { TRACE3(TRACE_VNODE, 3, TRCID_LINUXOPS_ACL2POSIX_2, "ACL_2_posix_acl: %d len %d buf_size %d\n", i, len, buf_size); if (buf_size < sizeof(struct linux_posix_acl_entry)) { rc = -ENOSPC; goto xerror; } switch(aceP->aceTagType) { case ACL_USER: aclP->a_entries[i].e_tag = LINUX_ACL_USER; aclP->a_entries[i].e_id = aceP->aceQualifier; break; case ACL_GROUP: aclP->a_entries[i].e_tag = LINUX_ACL_GROUP; aclP->a_entries[i].e_id = aceP->aceQualifier; break; case ACL_USER_OBJ: aclP->a_entries[i].e_tag = LINUX_ACL_USER_OBJ; aclP->a_entries[i].e_id = LINUX_ACL_UNDEFINED_ID; break; case ACL_GROUP_OBJ: aclP->a_entries[i].e_tag = LINUX_ACL_GROUP_OBJ; aclP->a_entries[i].e_id = LINUX_ACL_UNDEFINED_ID; break; case ACL_OTHER: aclP->a_entries[i].e_tag = LINUX_ACL_OTHER; aclP->a_entries[i].e_id = LINUX_ACL_UNDEFINED_ID; break; case ACL_MASK: aclP->a_entries[i].e_tag = LINUX_ACL_MASK; aclP->a_entries[i].e_id = LINUX_ACL_UNDEFINED_ID; break; } aclP->a_entries[i].e_perm = PERM_MASK_2_POSIX(aceP->acePermissions); TRACE7(TRACE_VNODE, 3, TRCID_LINUXOPS_ACL2POSIX, "ACL_2_posix_acl: %d e_tag 0x%02X aceTagType %d e_id %d aceQualifier %d e_perm 0x%02X acePermissions 0x%01X\n", i, aclP->a_entries[i].e_tag, aceP->aceTagType, aclP->a_entries[i].e_id, aceP->aceQualifier, aclP->a_entries[i].e_perm, aceP->acePermissions); /* move to the next acl entry */ len -= aceP->aceLength; aceP = (acl_entry_t *)((caddr_t)aceP + aceP->aceLength); buf_size -= sizeof(struct linux_posix_acl_entry); i++; } aclP->a_count = i; xerror: TRACE1(TRACE_VNODE, 1, TRCID_LINUXOPS_ACL2POSIX_EXIT, "ACL_2_posix_acl: exit rc %d\n", rc); EXIT(0); return rc; } /* move data from linux_posix_acl to ACL */ void posix_acl_2_ACL(ACL *tmpaclP, struct linux_posix_acl *aclP) { int i, count; acl_entry_t *aceP; count = aclP->a_count; /* Get the address of the first extended acl entry */ aceP = ACL_ENTRY_START(tmpaclP, tmpaclP->aclVersion); for (i = 0; i < count; i++) { if (aclP->a_entries[i].e_tag & LINUX_ACL_USER) { aceP->aceTagType = ACL_USER; aceP->aceQualifier = aclP->a_entries[i].e_id; } if (aclP->a_entries[i].e_tag & LINUX_ACL_GROUP) { aceP->aceTagType = ACL_GROUP; aceP->aceQualifier = aclP->a_entries[i].e_id; } if (aclP->a_entries[i].e_tag & LINUX_ACL_USER_OBJ) { aceP->aceTagType = ACL_USER_OBJ; aceP->aceQualifier = aclP->a_entries[i].e_id; } if (aclP->a_entries[i].e_tag & LINUX_ACL_GROUP_OBJ) { aceP->aceTagType = ACL_GROUP_OBJ; aceP->aceQualifier = aclP->a_entries[i].e_id; } if (aclP->a_entries[i].e_tag & LINUX_ACL_OTHER) { aceP->aceTagType = ACL_OTHER; aceP->aceQualifier = aclP->a_entries[i].e_id; } if (aclP->a_entries[i].e_tag & LINUX_ACL_MASK) { aceP->aceTagType = ACL_MASK; aceP->aceQualifier = aclP->a_entries[i].e_id; } aceP->acePermissions = PERM_MASK_2_ACL(aclP->a_entries[i].e_perm); aceP->aceLength = sizeof(acl_entry_t); TRACE6(TRACE_VNODE, 3, TRCID_LINUXOPS_POSIX2ACL, "posix_acl_2_ACL: %d e_tag 0x%X aceTagType %d aceQualifier %d e_perm 0x%X acePermissions 0x%X\n", i, aclP->a_entries[i].e_tag, aceP->aceTagType, aceP->aceQualifier, aclP->a_entries[i].e_perm, aceP->acePermissions); /* move to the next acl entry */ aceP = (acl_entry_t *)((caddr_t)aceP + sizeof(acl_entry_t)); } } int gpfs_set_posix_acl(struct dentry *dentry, int type, const void *buf, size_t buf_size) { int count, size, rc; cxiNode_t *cnP; struct gpfsVfsData_t *privVfsP; ext_cred_t eCred; ACL tmpacl, *tmpaclP = &tmpacl; int flags = 0; struct inode *iP = dentry->d_inode; struct linux_posix_acl *aclP = NULL; mm_segment_t oldfs; ENTER(0); TRACE5(TRACE_VNODE, 1, TRCID_LINUXOPS_SETACLATTR_ENTER, "gpfs_i_gpfs_set_posix_acl enter: iP 0x%lX ino %d type 0x%X buf 0x%lX size %d\n", iP, iP->i_ino, type, buf, buf_size); if (buf) { aclP = posix_acl_from_xattr(buf, buf_size); if (IS_ERR(aclP)) { rc = PTR_ERR(aclP); aclP = NULL; /* no aclP to free on exit */ goto xerror; } } if (aclP == NULL) { /* If no buf was given, or no acl was returned, there is nothing else * to do. This is in effect deleting all extended ACLs. */ memset(tmpaclP, '\0', sizeof(ACL)); tmpaclP->aclLength = ACL_HEADER_SIZE_V1; if (type == ACL_TYPE_ACCESS) tmpaclP->aclType = ACL_TYPE_ACCESS_DELETE; else if (type == ACL_TYPE_DEFAULT) tmpaclP->aclType = ACL_TYPE_DEFAULT_DELETE; else { rc = EINVAL; goto xerror; } } else { rc = posix_acl_valid(aclP); if (rc) goto xerror; count = aclP->a_count; size = ACL_HEADER_SIZE_V1 + (sizeof(acl_entry_t) * count); if (size > sizeof(ACL)) { tmpaclP = cxiMallocUnpinned(size); if (tmpaclP == NULL) { rc = ENOMEM; goto xerror; } } memset(tmpaclP, '\0', size); tmpaclP->aclLength = size; tmpaclP->aclType = type; } tmpaclP->aclVersion = ACL_FIRST_VERSION; privVfsP = VP_TO_PVP(iP); DBGASSERT(privVfsP != NULL); cnP = VP_TO_CNP(iP); /* Retrieve the ACL data */ setCred(&eCred); oldfs = get_fs(); set_fs(get_ds()); if (aclP) posix_acl_2_ACL(tmpaclP, aclP); rc = gpfs_ops.gpfsPutAcl(privVfsP, cnP, flags, tmpaclP, &eCred); set_fs(oldfs); xerror: if (aclP) kfree(aclP); if (tmpaclP && (tmpaclP != &tmpacl)) cxiFreeUnpinned(tmpaclP); TRACE2(TRACE_VNODE, 1, TRCID_LINUXOPS_SETACLATTR_EXIT, "gpfs_i_gpfs_set_posix_acl exit: iP 0x%lX rc %d\n", iP, rc); EXIT(0); return rc; } int gpfs_get_posix_acl(struct dentry *dentry, int type, const void *buf, size_t buf_size) { int rc = 0; int i, size; cxiNode_t *cnP; struct gpfsVfsData_t *privVfsP; ext_cred_t eCred; int flags = GETACL_DONOT_BLD_ACL; ACL tmpacl, *tmpaclP = &tmpacl; struct inode *iP = dentry->d_inode; mm_segment_t oldfs; ENTER(0); TRACE3(TRACE_VNODE, 1, TRCID_LINUXOPS_GETACLATTR_ENTER, "gpfs_get_posix_acl enter: iP 0x%lX type %d size %d\n", iP, type, buf_size); privVfsP = VP_TO_PVP(iP); DBGASSERT(privVfsP != NULL); cnP = VP_TO_CNP(iP); /* Retrieve the ACL data */ size = sizeof(ACL); try_again: setCred(&eCred); // ids may have been remapped before goto try_again tmpaclP->aclType = type; tmpaclP->aclVersion = ACL_FIRST_VERSION; tmpaclP->aclLength = size; oldfs = get_fs(); set_fs(get_ds()); rc = gpfs_ops.gpfsGetAcl(privVfsP, cnP, flags, tmpaclP, &eCred); set_fs(oldfs); if (rc) { if (rc == ENOSPC) { if (tmpaclP->aclLength >= size) { size = tmpaclP->aclLength; if (tmpaclP != &tmpacl) cxiFreeUnpinned(tmpaclP); tmpaclP = cxiMallocUnpinned(size); if (!tmpaclP) { rc = -ENOMEM; goto xerror; } goto try_again; } else { rc = -EINVAL; // buffer not even big enough to return length goto xerror; } } else { if (rc == ENOENT) rc = ENODATA; rc = -rc; goto xerror; } } /* move data from ACL to linux_posix_acl */ rc = ACL_2_posix_acl(tmpaclP, buf, buf_size); if (!rc) { if (size < buf_size) { if (tmpaclP != &tmpacl) cxiFreeUnpinned(tmpaclP); tmpaclP = cxiMallocUnpinned(buf_size); if (!tmpaclP) { rc = -ENOMEM; goto xerror; } } memcpy(tmpaclP, buf, buf_size); rc = posix_acl_to_xattr((struct linux_posix_acl *)tmpaclP, (void *)buf, size); } xerror: if (tmpaclP != &tmpacl) cxiFreeUnpinned(tmpaclP); TRACE2(TRACE_VNODE, 1, TRCID_LINUXOPS_GETACLATTR_EXIT, "gpfs_get_posix_acl exit: iP 0x%lX rc %d\n", iP, rc); EXIT(0); return rc; } #endif /* CONFIG_FS_POSIX_ACL */ #endif /* LINUX_KERNEL_VERSION > 2060000 */