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 32 static 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 43 static 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 57 void ceph_forget_all_cached_acls(struct inode *inode) 58 { 59 forget_all_cached_acls(inode); 60 } 61 62 struct 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 103 int 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; 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 dentry = d_find_alias(inode); 155 if (new_mode != old_mode) { 156 newattrs.ia_mode = new_mode; 157 newattrs.ia_valid = ATTR_MODE; 158 ret = ceph_setattr(dentry, &newattrs); 159 if (ret) 160 goto out_dput; 161 } 162 163 if (value) 164 ret = __ceph_setxattr(dentry, name, value, size, 0); 165 else 166 ret = __ceph_removexattr(dentry, name); 167 168 if (ret) { 169 if (new_mode != old_mode) { 170 newattrs.ia_mode = old_mode; 171 newattrs.ia_valid = ATTR_MODE; 172 ceph_setattr(dentry, &newattrs); 173 } 174 goto out_dput; 175 } 176 177 ceph_set_cached_acl(inode, type, acl); 178 179 out_dput: 180 dput(dentry); 181 out_free: 182 kfree(value); 183 out: 184 return ret; 185 } 186 187 int ceph_init_acl(struct dentry *dentry, struct inode *inode, struct inode *dir) 188 { 189 struct posix_acl *default_acl, *acl; 190 int error; 191 192 error = posix_acl_create(dir, &inode->i_mode, &default_acl, &acl); 193 if (error) 194 return error; 195 196 if (!default_acl && !acl) 197 cache_no_acl(inode); 198 199 if (default_acl) { 200 error = ceph_set_acl(inode, default_acl, ACL_TYPE_DEFAULT); 201 posix_acl_release(default_acl); 202 } 203 if (acl) { 204 if (!error) 205 error = ceph_set_acl(inode, acl, ACL_TYPE_ACCESS); 206 posix_acl_release(acl); 207 } 208 return error; 209 } 210