acl.c revision 758582361976e9640a1cb9516c0af10cdf8e4753
1/* 2 * linux/fs/ceph/acl.c 3 * 4 * Copyright (C) 2013 Guangliang Zhao, <lucienchao@gmail.com> 5 * 6 * This program is free software; you can redistribute it and/or 7 * modify it under the terms of the GNU General Public 8 * License v2 as published by the Free Software Foundation. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 * General Public License for more details. 14 * 15 * You should have received a copy of the GNU General Public 16 * License along with this program; if not, write to the 17 * Free Software Foundation, Inc., 59 Temple Place - Suite 330, 18 * Boston, MA 021110-1307, USA. 19 */ 20 21#include <linux/ceph/ceph_debug.h> 22#include <linux/fs.h> 23#include <linux/string.h> 24#include <linux/xattr.h> 25#include <linux/posix_acl_xattr.h> 26#include <linux/posix_acl.h> 27#include <linux/sched.h> 28#include <linux/slab.h> 29 30#include "super.h" 31 32static inline void ceph_set_cached_acl(struct inode *inode, 33 int type, struct posix_acl *acl) 34{ 35 struct ceph_inode_info *ci = ceph_inode(inode); 36 37 spin_lock(&ci->i_ceph_lock); 38 if (__ceph_caps_issued_mask(ci, CEPH_CAP_XATTR_SHARED, 0)) 39 set_cached_acl(inode, type, acl); 40 spin_unlock(&ci->i_ceph_lock); 41} 42 43static inline struct posix_acl *ceph_get_cached_acl(struct inode *inode, 44 int type) 45{ 46 struct ceph_inode_info *ci = ceph_inode(inode); 47 struct posix_acl *acl = ACL_NOT_CACHED; 48 49 spin_lock(&ci->i_ceph_lock); 50 if (__ceph_caps_issued_mask(ci, CEPH_CAP_XATTR_SHARED, 0)) 51 acl = get_cached_acl(inode, type); 52 spin_unlock(&ci->i_ceph_lock); 53 54 return acl; 55} 56 57void ceph_forget_all_cached_acls(struct inode *inode) 58{ 59 forget_all_cached_acls(inode); 60} 61 62struct posix_acl *ceph_get_acl(struct inode *inode, int type) 63{ 64 int size; 65 const char *name; 66 char *value = NULL; 67 struct posix_acl *acl; 68 69 switch (type) { 70 case ACL_TYPE_ACCESS: 71 name = POSIX_ACL_XATTR_ACCESS; 72 break; 73 case ACL_TYPE_DEFAULT: 74 name = POSIX_ACL_XATTR_DEFAULT; 75 break; 76 default: 77 BUG(); 78 } 79 80 size = __ceph_getxattr(inode, name, "", 0); 81 if (size > 0) { 82 value = kzalloc(size, GFP_NOFS); 83 if (!value) 84 return ERR_PTR(-ENOMEM); 85 size = __ceph_getxattr(inode, name, value, size); 86 } 87 88 if (size > 0) 89 acl = posix_acl_from_xattr(&init_user_ns, value, size); 90 else if (size == -ERANGE || size == -ENODATA || size == 0) 91 acl = NULL; 92 else 93 acl = ERR_PTR(-EIO); 94 95 kfree(value); 96 97 if (!IS_ERR(acl)) 98 ceph_set_cached_acl(inode, type, acl); 99 100 return acl; 101} 102 103int ceph_set_acl(struct inode *inode, struct posix_acl *acl, int type) 104{ 105 int ret = 0, size = 0; 106 const char *name = NULL; 107 char *value = NULL; 108 struct iattr newattrs; 109 umode_t new_mode = inode->i_mode, old_mode = inode->i_mode; 110 struct dentry *dentry = d_find_alias(inode); 111 112 if (acl) { 113 ret = posix_acl_valid(acl); 114 if (ret < 0) 115 goto out; 116 } 117 118 switch (type) { 119 case ACL_TYPE_ACCESS: 120 name = POSIX_ACL_XATTR_ACCESS; 121 if (acl) { 122 ret = posix_acl_equiv_mode(acl, &new_mode); 123 if (ret < 0) 124 goto out; 125 if (ret == 0) 126 acl = NULL; 127 } 128 break; 129 case ACL_TYPE_DEFAULT: 130 if (!S_ISDIR(inode->i_mode)) { 131 ret = acl ? -EINVAL : 0; 132 goto out; 133 } 134 name = POSIX_ACL_XATTR_DEFAULT; 135 break; 136 default: 137 ret = -EINVAL; 138 goto out; 139 } 140 141 if (acl) { 142 size = posix_acl_xattr_size(acl->a_count); 143 value = kmalloc(size, GFP_NOFS); 144 if (!value) { 145 ret = -ENOMEM; 146 goto out; 147 } 148 149 ret = posix_acl_to_xattr(&init_user_ns, acl, value, size); 150 if (ret < 0) 151 goto out_free; 152 } 153 154 if (new_mode != old_mode) { 155 newattrs.ia_mode = new_mode; 156 newattrs.ia_valid = ATTR_MODE; 157 ret = ceph_setattr(dentry, &newattrs); 158 if (ret) 159 goto out_free; 160 } 161 162 if (value) 163 ret = __ceph_setxattr(dentry, name, value, size, 0); 164 else 165 ret = __ceph_removexattr(dentry, name); 166 167 if (ret) { 168 if (new_mode != old_mode) { 169 newattrs.ia_mode = old_mode; 170 newattrs.ia_valid = ATTR_MODE; 171 ceph_setattr(dentry, &newattrs); 172 } 173 goto out_free; 174 } 175 176 ceph_set_cached_acl(inode, type, acl); 177 178out_free: 179 kfree(value); 180out: 181 return ret; 182} 183 184int ceph_init_acl(struct dentry *dentry, struct inode *inode, struct inode *dir) 185{ 186 struct posix_acl *default_acl, *acl; 187 int error; 188 189 error = posix_acl_create(dir, &inode->i_mode, &default_acl, &acl); 190 if (error) 191 return error; 192 193 if (!default_acl && !acl) 194 cache_no_acl(inode); 195 196 if (default_acl) { 197 error = ceph_set_acl(inode, default_acl, ACL_TYPE_DEFAULT); 198 posix_acl_release(default_acl); 199 } 200 if (acl) { 201 if (!error) 202 error = ceph_set_acl(inode, acl, ACL_TYPE_ACCESS); 203 posix_acl_release(acl); 204 } 205 return error; 206} 207