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