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_mutex: 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 kfree(value); 198 199 if (!IS_ERR(acl)) { 200 switch(type) { 201 case ACL_TYPE_ACCESS: 202 ext2_iset_acl(inode, &ei->i_acl, acl); 203 break; 204 205 case ACL_TYPE_DEFAULT: 206 ext2_iset_acl(inode, &ei->i_default_acl, acl); 207 break; 208 } 209 } 210 return acl; 211 } 212 213 /* 214 * inode->i_mutex: down 215 */ 216 static int 217 ext2_set_acl(struct inode *inode, int type, struct posix_acl *acl) 218 { 219 struct ext2_inode_info *ei = EXT2_I(inode); 220 int name_index; 221 void *value = NULL; 222 size_t size; 223 int error; 224 225 if (S_ISLNK(inode->i_mode)) 226 return -EOPNOTSUPP; 227 if (!test_opt(inode->i_sb, POSIX_ACL)) 228 return 0; 229 230 switch(type) { 231 case ACL_TYPE_ACCESS: 232 name_index = EXT2_XATTR_INDEX_POSIX_ACL_ACCESS; 233 if (acl) { 234 mode_t mode = inode->i_mode; 235 error = posix_acl_equiv_mode(acl, &mode); 236 if (error < 0) 237 return error; 238 else { 239 inode->i_mode = mode; 240 mark_inode_dirty(inode); 241 if (error == 0) 242 acl = NULL; 243 } 244 } 245 break; 246 247 case ACL_TYPE_DEFAULT: 248 name_index = EXT2_XATTR_INDEX_POSIX_ACL_DEFAULT; 249 if (!S_ISDIR(inode->i_mode)) 250 return acl ? -EACCES : 0; 251 break; 252 253 default: 254 return -EINVAL; 255 } 256 if (acl) { 257 value = ext2_acl_to_disk(acl, &size); 258 if (IS_ERR(value)) 259 return (int)PTR_ERR(value); 260 } 261 262 error = ext2_xattr_set(inode, name_index, "", value, size, 0); 263 264 kfree(value); 265 if (!error) { 266 switch(type) { 267 case ACL_TYPE_ACCESS: 268 ext2_iset_acl(inode, &ei->i_acl, acl); 269 break; 270 271 case ACL_TYPE_DEFAULT: 272 ext2_iset_acl(inode, &ei->i_default_acl, acl); 273 break; 274 } 275 } 276 return error; 277 } 278 279 static int 280 ext2_check_acl(struct inode *inode, int mask) 281 { 282 struct posix_acl *acl = ext2_get_acl(inode, ACL_TYPE_ACCESS); 283 284 if (IS_ERR(acl)) 285 return PTR_ERR(acl); 286 if (acl) { 287 int error = posix_acl_permission(inode, acl, mask); 288 posix_acl_release(acl); 289 return error; 290 } 291 292 return -EAGAIN; 293 } 294 295 int 296 ext2_permission(struct inode *inode, int mask, struct nameidata *nd) 297 { 298 return generic_permission(inode, mask, ext2_check_acl); 299 } 300 301 /* 302 * Initialize the ACLs of a new inode. Called from ext2_new_inode. 303 * 304 * dir->i_mutex: down 305 * inode->i_mutex: up (access to inode is still exclusive) 306 */ 307 int 308 ext2_init_acl(struct inode *inode, struct inode *dir) 309 { 310 struct posix_acl *acl = NULL; 311 int error = 0; 312 313 if (!S_ISLNK(inode->i_mode)) { 314 if (test_opt(dir->i_sb, POSIX_ACL)) { 315 acl = ext2_get_acl(dir, ACL_TYPE_DEFAULT); 316 if (IS_ERR(acl)) 317 return PTR_ERR(acl); 318 } 319 if (!acl) 320 inode->i_mode &= ~current->fs->umask; 321 } 322 if (test_opt(inode->i_sb, POSIX_ACL) && acl) { 323 struct posix_acl *clone; 324 mode_t mode; 325 326 if (S_ISDIR(inode->i_mode)) { 327 error = ext2_set_acl(inode, ACL_TYPE_DEFAULT, acl); 328 if (error) 329 goto cleanup; 330 } 331 clone = posix_acl_clone(acl, GFP_KERNEL); 332 error = -ENOMEM; 333 if (!clone) 334 goto cleanup; 335 mode = inode->i_mode; 336 error = posix_acl_create_masq(clone, &mode); 337 if (error >= 0) { 338 inode->i_mode = mode; 339 if (error > 0) { 340 /* This is an extended ACL */ 341 error = ext2_set_acl(inode, 342 ACL_TYPE_ACCESS, clone); 343 } 344 } 345 posix_acl_release(clone); 346 } 347 cleanup: 348 posix_acl_release(acl); 349 return error; 350 } 351 352 /* 353 * Does chmod for an inode that may have an Access Control List. The 354 * inode->i_mode field must be updated to the desired value by the caller 355 * before calling this function. 356 * Returns 0 on success, or a negative error number. 357 * 358 * We change the ACL rather than storing some ACL entries in the file 359 * mode permission bits (which would be more efficient), because that 360 * would break once additional permissions (like ACL_APPEND, ACL_DELETE 361 * for directories) are added. There are no more bits available in the 362 * file mode. 363 * 364 * inode->i_mutex: down 365 */ 366 int 367 ext2_acl_chmod(struct inode *inode) 368 { 369 struct posix_acl *acl, *clone; 370 int error; 371 372 if (!test_opt(inode->i_sb, POSIX_ACL)) 373 return 0; 374 if (S_ISLNK(inode->i_mode)) 375 return -EOPNOTSUPP; 376 acl = ext2_get_acl(inode, ACL_TYPE_ACCESS); 377 if (IS_ERR(acl) || !acl) 378 return PTR_ERR(acl); 379 clone = posix_acl_clone(acl, GFP_KERNEL); 380 posix_acl_release(acl); 381 if (!clone) 382 return -ENOMEM; 383 error = posix_acl_chmod_masq(clone, inode->i_mode); 384 if (!error) 385 error = ext2_set_acl(inode, ACL_TYPE_ACCESS, clone); 386 posix_acl_release(clone); 387 return error; 388 } 389 390 /* 391 * Extended attribut handlers 392 */ 393 static size_t 394 ext2_xattr_list_acl_access(struct inode *inode, char *list, size_t list_size, 395 const char *name, size_t name_len) 396 { 397 const size_t size = sizeof(POSIX_ACL_XATTR_ACCESS); 398 399 if (!test_opt(inode->i_sb, POSIX_ACL)) 400 return 0; 401 if (list && size <= list_size) 402 memcpy(list, POSIX_ACL_XATTR_ACCESS, size); 403 return size; 404 } 405 406 static size_t 407 ext2_xattr_list_acl_default(struct inode *inode, char *list, size_t list_size, 408 const char *name, size_t name_len) 409 { 410 const size_t size = sizeof(POSIX_ACL_XATTR_DEFAULT); 411 412 if (!test_opt(inode->i_sb, POSIX_ACL)) 413 return 0; 414 if (list && size <= list_size) 415 memcpy(list, POSIX_ACL_XATTR_DEFAULT, size); 416 return size; 417 } 418 419 static int 420 ext2_xattr_get_acl(struct inode *inode, int type, void *buffer, size_t size) 421 { 422 struct posix_acl *acl; 423 int error; 424 425 if (!test_opt(inode->i_sb, POSIX_ACL)) 426 return -EOPNOTSUPP; 427 428 acl = ext2_get_acl(inode, type); 429 if (IS_ERR(acl)) 430 return PTR_ERR(acl); 431 if (acl == NULL) 432 return -ENODATA; 433 error = posix_acl_to_xattr(acl, buffer, size); 434 posix_acl_release(acl); 435 436 return error; 437 } 438 439 static int 440 ext2_xattr_get_acl_access(struct inode *inode, const char *name, 441 void *buffer, size_t size) 442 { 443 if (strcmp(name, "") != 0) 444 return -EINVAL; 445 return ext2_xattr_get_acl(inode, ACL_TYPE_ACCESS, buffer, size); 446 } 447 448 static int 449 ext2_xattr_get_acl_default(struct inode *inode, const char *name, 450 void *buffer, size_t size) 451 { 452 if (strcmp(name, "") != 0) 453 return -EINVAL; 454 return ext2_xattr_get_acl(inode, ACL_TYPE_DEFAULT, buffer, size); 455 } 456 457 static int 458 ext2_xattr_set_acl(struct inode *inode, int type, const void *value, 459 size_t size) 460 { 461 struct posix_acl *acl; 462 int error; 463 464 if (!test_opt(inode->i_sb, POSIX_ACL)) 465 return -EOPNOTSUPP; 466 if ((current->fsuid != inode->i_uid) && !capable(CAP_FOWNER)) 467 return -EPERM; 468 469 if (value) { 470 acl = posix_acl_from_xattr(value, size); 471 if (IS_ERR(acl)) 472 return PTR_ERR(acl); 473 else if (acl) { 474 error = posix_acl_valid(acl); 475 if (error) 476 goto release_and_out; 477 } 478 } else 479 acl = NULL; 480 481 error = ext2_set_acl(inode, type, acl); 482 483 release_and_out: 484 posix_acl_release(acl); 485 return error; 486 } 487 488 static int 489 ext2_xattr_set_acl_access(struct inode *inode, const char *name, 490 const void *value, size_t size, int flags) 491 { 492 if (strcmp(name, "") != 0) 493 return -EINVAL; 494 return ext2_xattr_set_acl(inode, ACL_TYPE_ACCESS, value, size); 495 } 496 497 static int 498 ext2_xattr_set_acl_default(struct inode *inode, const char *name, 499 const void *value, size_t size, int flags) 500 { 501 if (strcmp(name, "") != 0) 502 return -EINVAL; 503 return ext2_xattr_set_acl(inode, ACL_TYPE_DEFAULT, value, size); 504 } 505 506 struct xattr_handler ext2_xattr_acl_access_handler = { 507 .prefix = POSIX_ACL_XATTR_ACCESS, 508 .list = ext2_xattr_list_acl_access, 509 .get = ext2_xattr_get_acl_access, 510 .set = ext2_xattr_set_acl_access, 511 }; 512 513 struct xattr_handler ext2_xattr_acl_default_handler = { 514 .prefix = POSIX_ACL_XATTR_DEFAULT, 515 .list = ext2_xattr_list_acl_default, 516 .get = ext2_xattr_get_acl_default, 517 .set = ext2_xattr_set_acl_default, 518 }; 519