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