Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
fork

Configure Feed

Select the types of activity you want to include in your feed.

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