1 /* 2 * Copyright (C) 2007 Red Hat. All rights reserved. 3 * 4 * This program is free software; you can redistribute it and/or 5 * modify it under the terms of the GNU General Public 6 * License v2 as published by the Free Software Foundation. 7 * 8 * This program is distributed in the hope that it will be useful, 9 * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 11 * General Public License for more details. 12 * 13 * You should have received a copy of the GNU General Public 14 * License along with this program; if not, write to the 15 * Free Software Foundation, Inc., 59 Temple Place - Suite 330, 16 * Boston, MA 021110-1307, USA. 17 */ 18 19 #include <linux/fs.h> 20 #include <linux/string.h> 21 #include <linux/xattr.h> 22 #include <linux/posix_acl_xattr.h> 23 #include <linux/posix_acl.h> 24 #include <linux/sched.h> 25 26 #include "ctree.h" 27 #include "btrfs_inode.h" 28 #include "xattr.h" 29 30 #ifdef CONFIG_BTRFS_FS_POSIX_ACL 31 32 static struct posix_acl *btrfs_get_acl(struct inode *inode, int type) 33 { 34 int size; 35 const char *name; 36 char *value = NULL; 37 struct posix_acl *acl; 38 39 acl = get_cached_acl(inode, type); 40 if (acl != ACL_NOT_CACHED) 41 return acl; 42 43 switch (type) { 44 case ACL_TYPE_ACCESS: 45 name = POSIX_ACL_XATTR_ACCESS; 46 break; 47 case ACL_TYPE_DEFAULT: 48 name = POSIX_ACL_XATTR_DEFAULT; 49 break; 50 default: 51 BUG(); 52 } 53 54 size = __btrfs_getxattr(inode, name, "", 0); 55 if (size > 0) { 56 value = kzalloc(size, GFP_NOFS); 57 if (!value) 58 return ERR_PTR(-ENOMEM); 59 size = __btrfs_getxattr(inode, name, value, size); 60 if (size > 0) { 61 acl = posix_acl_from_xattr(value, size); 62 set_cached_acl(inode, type, acl); 63 } 64 kfree(value); 65 } else if (size == -ENOENT || size == -ENODATA || size == 0) { 66 /* FIXME, who returns -ENOENT? I think nobody */ 67 acl = NULL; 68 set_cached_acl(inode, type, acl); 69 } else { 70 acl = ERR_PTR(-EIO); 71 } 72 73 return acl; 74 } 75 76 static int btrfs_xattr_acl_get(struct dentry *dentry, const char *name, 77 void *value, size_t size, int type) 78 { 79 struct posix_acl *acl; 80 int ret = 0; 81 82 acl = btrfs_get_acl(dentry->d_inode, type); 83 84 if (IS_ERR(acl)) 85 return PTR_ERR(acl); 86 if (acl == NULL) 87 return -ENODATA; 88 ret = posix_acl_to_xattr(acl, value, size); 89 posix_acl_release(acl); 90 91 return ret; 92 } 93 94 /* 95 * Needs to be called with fs_mutex held 96 */ 97 static int btrfs_set_acl(struct btrfs_trans_handle *trans, 98 struct inode *inode, struct posix_acl *acl, int type) 99 { 100 int ret, size = 0; 101 const char *name; 102 char *value = NULL; 103 mode_t mode; 104 105 if (acl) { 106 ret = posix_acl_valid(acl); 107 if (ret < 0) 108 return ret; 109 ret = 0; 110 } 111 112 switch (type) { 113 case ACL_TYPE_ACCESS: 114 mode = inode->i_mode; 115 ret = posix_acl_equiv_mode(acl, &mode); 116 if (ret < 0) 117 return ret; 118 ret = 0; 119 inode->i_mode = mode; 120 name = POSIX_ACL_XATTR_ACCESS; 121 break; 122 case ACL_TYPE_DEFAULT: 123 if (!S_ISDIR(inode->i_mode)) 124 return acl ? -EINVAL : 0; 125 name = POSIX_ACL_XATTR_DEFAULT; 126 break; 127 default: 128 return -EINVAL; 129 } 130 131 if (acl) { 132 size = posix_acl_xattr_size(acl->a_count); 133 value = kmalloc(size, GFP_NOFS); 134 if (!value) { 135 ret = -ENOMEM; 136 goto out; 137 } 138 139 ret = posix_acl_to_xattr(acl, value, size); 140 if (ret < 0) 141 goto out; 142 } 143 144 ret = __btrfs_setxattr(trans, inode, name, value, size, 0); 145 out: 146 kfree(value); 147 148 if (!ret) 149 set_cached_acl(inode, type, acl); 150 151 return ret; 152 } 153 154 static int btrfs_xattr_acl_set(struct dentry *dentry, const char *name, 155 const void *value, size_t size, int flags, int type) 156 { 157 int ret; 158 struct posix_acl *acl = NULL; 159 160 if (value) { 161 acl = posix_acl_from_xattr(value, size); 162 if (acl == NULL) { 163 value = NULL; 164 size = 0; 165 } else if (IS_ERR(acl)) { 166 return PTR_ERR(acl); 167 } 168 } 169 170 ret = btrfs_set_acl(NULL, dentry->d_inode, acl, type); 171 172 posix_acl_release(acl); 173 174 return ret; 175 } 176 177 int btrfs_check_acl(struct inode *inode, int mask) 178 { 179 struct posix_acl *acl; 180 int error = -EAGAIN; 181 182 acl = btrfs_get_acl(inode, ACL_TYPE_ACCESS); 183 184 if (IS_ERR(acl)) 185 return PTR_ERR(acl); 186 if (acl) { 187 error = posix_acl_permission(inode, acl, mask); 188 posix_acl_release(acl); 189 } 190 191 return error; 192 } 193 194 /* 195 * btrfs_init_acl is already generally called under fs_mutex, so the locking 196 * stuff has been fixed to work with that. If the locking stuff changes, we 197 * need to re-evaluate the acl locking stuff. 198 */ 199 int btrfs_init_acl(struct btrfs_trans_handle *trans, 200 struct inode *inode, struct inode *dir) 201 { 202 struct posix_acl *acl = NULL; 203 int ret = 0; 204 205 /* this happens with subvols */ 206 if (!dir) 207 return 0; 208 209 if (!S_ISLNK(inode->i_mode)) { 210 if (IS_POSIXACL(dir)) { 211 acl = btrfs_get_acl(dir, ACL_TYPE_DEFAULT); 212 if (IS_ERR(acl)) 213 return PTR_ERR(acl); 214 } 215 216 if (!acl) 217 inode->i_mode &= ~current_umask(); 218 } 219 220 if (IS_POSIXACL(dir) && acl) { 221 struct posix_acl *clone; 222 mode_t mode; 223 224 if (S_ISDIR(inode->i_mode)) { 225 ret = btrfs_set_acl(trans, inode, acl, 226 ACL_TYPE_DEFAULT); 227 if (ret) 228 goto failed; 229 } 230 clone = posix_acl_clone(acl, GFP_NOFS); 231 ret = -ENOMEM; 232 if (!clone) 233 goto failed; 234 235 mode = inode->i_mode; 236 ret = posix_acl_create_masq(clone, &mode); 237 if (ret >= 0) { 238 inode->i_mode = mode; 239 if (ret > 0) { 240 /* we need an acl */ 241 ret = btrfs_set_acl(trans, inode, clone, 242 ACL_TYPE_ACCESS); 243 } 244 } 245 } 246 failed: 247 posix_acl_release(acl); 248 249 return ret; 250 } 251 252 int btrfs_acl_chmod(struct inode *inode) 253 { 254 struct posix_acl *acl, *clone; 255 int ret = 0; 256 257 if (S_ISLNK(inode->i_mode)) 258 return -EOPNOTSUPP; 259 260 if (!IS_POSIXACL(inode)) 261 return 0; 262 263 acl = btrfs_get_acl(inode, ACL_TYPE_ACCESS); 264 if (IS_ERR(acl) || !acl) 265 return PTR_ERR(acl); 266 267 clone = posix_acl_clone(acl, GFP_KERNEL); 268 posix_acl_release(acl); 269 if (!clone) 270 return -ENOMEM; 271 272 ret = posix_acl_chmod_masq(clone, inode->i_mode); 273 if (!ret) 274 ret = btrfs_set_acl(NULL, inode, clone, ACL_TYPE_ACCESS); 275 276 posix_acl_release(clone); 277 278 return ret; 279 } 280 281 struct xattr_handler btrfs_xattr_acl_default_handler = { 282 .prefix = POSIX_ACL_XATTR_DEFAULT, 283 .flags = ACL_TYPE_DEFAULT, 284 .get = btrfs_xattr_acl_get, 285 .set = btrfs_xattr_acl_set, 286 }; 287 288 struct xattr_handler btrfs_xattr_acl_access_handler = { 289 .prefix = POSIX_ACL_XATTR_ACCESS, 290 .flags = ACL_TYPE_ACCESS, 291 .get = btrfs_xattr_acl_get, 292 .set = btrfs_xattr_acl_set, 293 }; 294 295 #else /* CONFIG_BTRFS_FS_POSIX_ACL */ 296 297 int btrfs_acl_chmod(struct inode *inode) 298 { 299 return 0; 300 } 301 302 int btrfs_init_acl(struct btrfs_trans_handle *trans, 303 struct inode *inode, struct inode *dir) 304 { 305 return 0; 306 } 307 308 #endif /* CONFIG_BTRFS_FS_POSIX_ACL */ 309