1 /* 2 * linux/fs/ext2/acl.c 3 * 4 * Copyright (C) 2001-2003 Andreas Gruenbacher, <agruen@suse.de> 5 */ 6 7 #include <linux/init.h> 8 #include <linux/sched.h> 9 #include <linux/slab.h> 10 #include <linux/fs.h> 11 #include "ext2.h" 12 #include "xattr.h" 13 #include "acl.h" 14 15 /* 16 * Convert from filesystem to in-memory representation. 17 */ 18 static struct posix_acl * 19 ext2_acl_from_disk(const void *value, size_t size) 20 { 21 const char *end = (char *)value + size; 22 int n, count; 23 struct posix_acl *acl; 24 25 if (!value) 26 return NULL; 27 if (size < sizeof(ext2_acl_header)) 28 return ERR_PTR(-EINVAL); 29 if (((ext2_acl_header *)value)->a_version != 30 cpu_to_le32(EXT2_ACL_VERSION)) 31 return ERR_PTR(-EINVAL); 32 value = (char *)value + sizeof(ext2_acl_header); 33 count = ext2_acl_count(size); 34 if (count < 0) 35 return ERR_PTR(-EINVAL); 36 if (count == 0) 37 return NULL; 38 acl = posix_acl_alloc(count, GFP_KERNEL); 39 if (!acl) 40 return ERR_PTR(-ENOMEM); 41 for (n=0; n < count; n++) { 42 ext2_acl_entry *entry = 43 (ext2_acl_entry *)value; 44 if ((char *)value + sizeof(ext2_acl_entry_short) > end) 45 goto fail; 46 acl->a_entries[n].e_tag = le16_to_cpu(entry->e_tag); 47 acl->a_entries[n].e_perm = le16_to_cpu(entry->e_perm); 48 switch(acl->a_entries[n].e_tag) { 49 case ACL_USER_OBJ: 50 case ACL_GROUP_OBJ: 51 case ACL_MASK: 52 case ACL_OTHER: 53 value = (char *)value + 54 sizeof(ext2_acl_entry_short); 55 acl->a_entries[n].e_id = ACL_UNDEFINED_ID; 56 break; 57 58 case ACL_USER: 59 case ACL_GROUP: 60 value = (char *)value + sizeof(ext2_acl_entry); 61 if ((char *)value > end) 62 goto fail; 63 acl->a_entries[n].e_id = 64 le32_to_cpu(entry->e_id); 65 break; 66 67 default: 68 goto fail; 69 } 70 } 71 if (value != end) 72 goto fail; 73 return acl; 74 75 fail: 76 posix_acl_release(acl); 77 return ERR_PTR(-EINVAL); 78 } 79 80 /* 81 * Convert from in-memory to filesystem representation. 82 */ 83 static void * 84 ext2_acl_to_disk(const struct posix_acl *acl, size_t *size) 85 { 86 ext2_acl_header *ext_acl; 87 char *e; 88 size_t n; 89 90 *size = ext2_acl_size(acl->a_count); 91 ext_acl = (ext2_acl_header *)kmalloc(sizeof(ext2_acl_header) + 92 acl->a_count * sizeof(ext2_acl_entry), GFP_KERNEL); 93 if (!ext_acl) 94 return ERR_PTR(-ENOMEM); 95 ext_acl->a_version = cpu_to_le32(EXT2_ACL_VERSION); 96 e = (char *)ext_acl + sizeof(ext2_acl_header); 97 for (n=0; n < acl->a_count; n++) { 98 ext2_acl_entry *entry = (ext2_acl_entry *)e; 99 entry->e_tag = cpu_to_le16(acl->a_entries[n].e_tag); 100 entry->e_perm = cpu_to_le16(acl->a_entries[n].e_perm); 101 switch(acl->a_entries[n].e_tag) { 102 case ACL_USER: 103 case ACL_GROUP: 104 entry->e_id = 105 cpu_to_le32(acl->a_entries[n].e_id); 106 e += sizeof(ext2_acl_entry); 107 break; 108 109 case ACL_USER_OBJ: 110 case ACL_GROUP_OBJ: 111 case ACL_MASK: 112 case ACL_OTHER: 113 e += sizeof(ext2_acl_entry_short); 114 break; 115 116 default: 117 goto fail; 118 } 119 } 120 return (char *)ext_acl; 121 122 fail: 123 kfree(ext_acl); 124 return ERR_PTR(-EINVAL); 125 } 126 127 static inline struct posix_acl * 128 ext2_iget_acl(struct inode *inode, struct posix_acl **i_acl) 129 { 130 struct posix_acl *acl = EXT2_ACL_NOT_CACHED; 131 132 spin_lock(&inode->i_lock); 133 if (*i_acl != EXT2_ACL_NOT_CACHED) 134 acl = posix_acl_dup(*i_acl); 135 spin_unlock(&inode->i_lock); 136 137 return acl; 138 } 139 140 static inline void 141 ext2_iset_acl(struct inode *inode, struct posix_acl **i_acl, 142 struct posix_acl *acl) 143 { 144 spin_lock(&inode->i_lock); 145 if (*i_acl != EXT2_ACL_NOT_CACHED) 146 posix_acl_release(*i_acl); 147 *i_acl = posix_acl_dup(acl); 148 spin_unlock(&inode->i_lock); 149 } 150 151 /* 152 * inode->i_sem: don't care 153 */ 154 static struct posix_acl * 155 ext2_get_acl(struct inode *inode, int type) 156 { 157 struct ext2_inode_info *ei = EXT2_I(inode); 158 int name_index; 159 char *value = NULL; 160 struct posix_acl *acl; 161 int retval; 162 163 if (!test_opt(inode->i_sb, POSIX_ACL)) 164 return NULL; 165 166 switch(type) { 167 case ACL_TYPE_ACCESS: 168 acl = ext2_iget_acl(inode, &ei->i_acl); 169 if (acl != EXT2_ACL_NOT_CACHED) 170 return acl; 171 name_index = EXT2_XATTR_INDEX_POSIX_ACL_ACCESS; 172 break; 173 174 case ACL_TYPE_DEFAULT: 175 acl = ext2_iget_acl(inode, &ei->i_default_acl); 176 if (acl != EXT2_ACL_NOT_CACHED) 177 return acl; 178 name_index = EXT2_XATTR_INDEX_POSIX_ACL_DEFAULT; 179 break; 180 181 default: 182 return ERR_PTR(-EINVAL); 183 } 184 retval = ext2_xattr_get(inode, name_index, "", NULL, 0); 185 if (retval > 0) { 186 value = kmalloc(retval, GFP_KERNEL); 187 if (!value) 188 return ERR_PTR(-ENOMEM); 189 retval = ext2_xattr_get(inode, name_index, "", value, retval); 190 } 191 if (retval > 0) 192 acl = ext2_acl_from_disk(value, retval); 193 else if (retval == -ENODATA || retval == -ENOSYS) 194 acl = NULL; 195 else 196 acl = ERR_PTR(retval); 197 if (value) 198 kfree(value); 199 200 if (!IS_ERR(acl)) { 201 switch(type) { 202 case ACL_TYPE_ACCESS: 203 ext2_iset_acl(inode, &ei->i_acl, acl); 204 break; 205 206 case ACL_TYPE_DEFAULT: 207 ext2_iset_acl(inode, &ei->i_default_acl, acl); 208 break; 209 } 210 } 211 return acl; 212 } 213 214 /* 215 * inode->i_sem: down 216 */ 217 static int 218 ext2_set_acl(struct inode *inode, int type, struct posix_acl *acl) 219 { 220 struct ext2_inode_info *ei = EXT2_I(inode); 221 int name_index; 222 void *value = NULL; 223 size_t size; 224 int error; 225 226 if (S_ISLNK(inode->i_mode)) 227 return -EOPNOTSUPP; 228 if (!test_opt(inode->i_sb, POSIX_ACL)) 229 return 0; 230 231 switch(type) { 232 case ACL_TYPE_ACCESS: 233 name_index = EXT2_XATTR_INDEX_POSIX_ACL_ACCESS; 234 if (acl) { 235 mode_t mode = inode->i_mode; 236 error = posix_acl_equiv_mode(acl, &mode); 237 if (error < 0) 238 return error; 239 else { 240 inode->i_mode = mode; 241 mark_inode_dirty(inode); 242 if (error == 0) 243 acl = NULL; 244 } 245 } 246 break; 247 248 case ACL_TYPE_DEFAULT: 249 name_index = EXT2_XATTR_INDEX_POSIX_ACL_DEFAULT; 250 if (!S_ISDIR(inode->i_mode)) 251 return acl ? -EACCES : 0; 252 break; 253 254 default: 255 return -EINVAL; 256 } 257 if (acl) { 258 value = ext2_acl_to_disk(acl, &size); 259 if (IS_ERR(value)) 260 return (int)PTR_ERR(value); 261 } 262 263 error = ext2_xattr_set(inode, name_index, "", value, size, 0); 264 265 if (value) 266 kfree(value); 267 if (!error) { 268 switch(type) { 269 case ACL_TYPE_ACCESS: 270 ext2_iset_acl(inode, &ei->i_acl, acl); 271 break; 272 273 case ACL_TYPE_DEFAULT: 274 ext2_iset_acl(inode, &ei->i_default_acl, acl); 275 break; 276 } 277 } 278 return error; 279 } 280 281 static int 282 ext2_check_acl(struct inode *inode, int mask) 283 { 284 struct posix_acl *acl = ext2_get_acl(inode, ACL_TYPE_ACCESS); 285 286 if (IS_ERR(acl)) 287 return PTR_ERR(acl); 288 if (acl) { 289 int error = posix_acl_permission(inode, acl, mask); 290 posix_acl_release(acl); 291 return error; 292 } 293 294 return -EAGAIN; 295 } 296 297 int 298 ext2_permission(struct inode *inode, int mask, struct nameidata *nd) 299 { 300 return generic_permission(inode, mask, ext2_check_acl); 301 } 302 303 /* 304 * Initialize the ACLs of a new inode. Called from ext2_new_inode. 305 * 306 * dir->i_sem: down 307 * inode->i_sem: up (access to inode is still exclusive) 308 */ 309 int 310 ext2_init_acl(struct inode *inode, struct inode *dir) 311 { 312 struct posix_acl *acl = NULL; 313 int error = 0; 314 315 if (!S_ISLNK(inode->i_mode)) { 316 if (test_opt(dir->i_sb, POSIX_ACL)) { 317 acl = ext2_get_acl(dir, ACL_TYPE_DEFAULT); 318 if (IS_ERR(acl)) 319 return PTR_ERR(acl); 320 } 321 if (!acl) 322 inode->i_mode &= ~current->fs->umask; 323 } 324 if (test_opt(inode->i_sb, POSIX_ACL) && acl) { 325 struct posix_acl *clone; 326 mode_t mode; 327 328 if (S_ISDIR(inode->i_mode)) { 329 error = ext2_set_acl(inode, ACL_TYPE_DEFAULT, acl); 330 if (error) 331 goto cleanup; 332 } 333 clone = posix_acl_clone(acl, GFP_KERNEL); 334 error = -ENOMEM; 335 if (!clone) 336 goto cleanup; 337 mode = inode->i_mode; 338 error = posix_acl_create_masq(clone, &mode); 339 if (error >= 0) { 340 inode->i_mode = mode; 341 if (error > 0) { 342 /* This is an extended ACL */ 343 error = ext2_set_acl(inode, 344 ACL_TYPE_ACCESS, clone); 345 } 346 } 347 posix_acl_release(clone); 348 } 349 cleanup: 350 posix_acl_release(acl); 351 return error; 352 } 353 354 /* 355 * Does chmod for an inode that may have an Access Control List. The 356 * inode->i_mode field must be updated to the desired value by the caller 357 * before calling this function. 358 * Returns 0 on success, or a negative error number. 359 * 360 * We change the ACL rather than storing some ACL entries in the file 361 * mode permission bits (which would be more efficient), because that 362 * would break once additional permissions (like ACL_APPEND, ACL_DELETE 363 * for directories) are added. There are no more bits available in the 364 * file mode. 365 * 366 * inode->i_sem: down 367 */ 368 int 369 ext2_acl_chmod(struct inode *inode) 370 { 371 struct posix_acl *acl, *clone; 372 int error; 373 374 if (!test_opt(inode->i_sb, POSIX_ACL)) 375 return 0; 376 if (S_ISLNK(inode->i_mode)) 377 return -EOPNOTSUPP; 378 acl = ext2_get_acl(inode, ACL_TYPE_ACCESS); 379 if (IS_ERR(acl) || !acl) 380 return PTR_ERR(acl); 381 clone = posix_acl_clone(acl, GFP_KERNEL); 382 posix_acl_release(acl); 383 if (!clone) 384 return -ENOMEM; 385 error = posix_acl_chmod_masq(clone, inode->i_mode); 386 if (!error) 387 error = ext2_set_acl(inode, ACL_TYPE_ACCESS, clone); 388 posix_acl_release(clone); 389 return error; 390 } 391 392 /* 393 * Extended attribut handlers 394 */ 395 static size_t 396 ext2_xattr_list_acl_access(struct inode *inode, char *list, size_t list_size, 397 const char *name, size_t name_len) 398 { 399 const size_t size = sizeof(POSIX_ACL_XATTR_ACCESS); 400 401 if (!test_opt(inode->i_sb, POSIX_ACL)) 402 return 0; 403 if (list && size <= list_size) 404 memcpy(list, POSIX_ACL_XATTR_ACCESS, size); 405 return size; 406 } 407 408 static size_t 409 ext2_xattr_list_acl_default(struct inode *inode, char *list, size_t list_size, 410 const char *name, size_t name_len) 411 { 412 const size_t size = sizeof(POSIX_ACL_XATTR_DEFAULT); 413 414 if (!test_opt(inode->i_sb, POSIX_ACL)) 415 return 0; 416 if (list && size <= list_size) 417 memcpy(list, POSIX_ACL_XATTR_DEFAULT, size); 418 return size; 419 } 420 421 static int 422 ext2_xattr_get_acl(struct inode *inode, int type, void *buffer, size_t size) 423 { 424 struct posix_acl *acl; 425 int error; 426 427 if (!test_opt(inode->i_sb, POSIX_ACL)) 428 return -EOPNOTSUPP; 429 430 acl = ext2_get_acl(inode, type); 431 if (IS_ERR(acl)) 432 return PTR_ERR(acl); 433 if (acl == NULL) 434 return -ENODATA; 435 error = posix_acl_to_xattr(acl, buffer, size); 436 posix_acl_release(acl); 437 438 return error; 439 } 440 441 static int 442 ext2_xattr_get_acl_access(struct inode *inode, const char *name, 443 void *buffer, size_t size) 444 { 445 if (strcmp(name, "") != 0) 446 return -EINVAL; 447 return ext2_xattr_get_acl(inode, ACL_TYPE_ACCESS, buffer, size); 448 } 449 450 static int 451 ext2_xattr_get_acl_default(struct inode *inode, const char *name, 452 void *buffer, size_t size) 453 { 454 if (strcmp(name, "") != 0) 455 return -EINVAL; 456 return ext2_xattr_get_acl(inode, ACL_TYPE_DEFAULT, buffer, size); 457 } 458 459 static int 460 ext2_xattr_set_acl(struct inode *inode, int type, const void *value, 461 size_t size) 462 { 463 struct posix_acl *acl; 464 int error; 465 466 if (!test_opt(inode->i_sb, POSIX_ACL)) 467 return -EOPNOTSUPP; 468 if ((current->fsuid != inode->i_uid) && !capable(CAP_FOWNER)) 469 return -EPERM; 470 471 if (value) { 472 acl = posix_acl_from_xattr(value, size); 473 if (IS_ERR(acl)) 474 return PTR_ERR(acl); 475 else if (acl) { 476 error = posix_acl_valid(acl); 477 if (error) 478 goto release_and_out; 479 } 480 } else 481 acl = NULL; 482 483 error = ext2_set_acl(inode, type, acl); 484 485 release_and_out: 486 posix_acl_release(acl); 487 return error; 488 } 489 490 static int 491 ext2_xattr_set_acl_access(struct inode *inode, const char *name, 492 const void *value, size_t size, int flags) 493 { 494 if (strcmp(name, "") != 0) 495 return -EINVAL; 496 return ext2_xattr_set_acl(inode, ACL_TYPE_ACCESS, value, size); 497 } 498 499 static int 500 ext2_xattr_set_acl_default(struct inode *inode, const char *name, 501 const void *value, size_t size, int flags) 502 { 503 if (strcmp(name, "") != 0) 504 return -EINVAL; 505 return ext2_xattr_set_acl(inode, ACL_TYPE_DEFAULT, value, size); 506 } 507 508 struct xattr_handler ext2_xattr_acl_access_handler = { 509 .prefix = POSIX_ACL_XATTR_ACCESS, 510 .list = ext2_xattr_list_acl_access, 511 .get = ext2_xattr_get_acl_access, 512 .set = ext2_xattr_set_acl_access, 513 }; 514 515 struct xattr_handler ext2_xattr_acl_default_handler = { 516 .prefix = POSIX_ACL_XATTR_DEFAULT, 517 .list = ext2_xattr_list_acl_default, 518 .get = ext2_xattr_get_acl_default, 519 .set = ext2_xattr_set_acl_default, 520 }; 521