diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /fs/ext3/acl.c |
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'fs/ext3/acl.c')
-rw-r--r-- | fs/ext3/acl.c | 547 |
1 files changed, 547 insertions, 0 deletions
diff --git a/fs/ext3/acl.c b/fs/ext3/acl.c new file mode 100644 index 000000000000..328592c3a956 --- /dev/null +++ b/fs/ext3/acl.c | |||
@@ -0,0 +1,547 @@ | |||
1 | /* | ||
2 | * linux/fs/ext3/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 <linux/ext3_jbd.h> | ||
12 | #include <linux/ext3_fs.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 | ext3_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(ext3_acl_header)) | ||
29 | return ERR_PTR(-EINVAL); | ||
30 | if (((ext3_acl_header *)value)->a_version != | ||
31 | cpu_to_le32(EXT3_ACL_VERSION)) | ||
32 | return ERR_PTR(-EINVAL); | ||
33 | value = (char *)value + sizeof(ext3_acl_header); | ||
34 | count = ext3_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 | ext3_acl_entry *entry = | ||
44 | (ext3_acl_entry *)value; | ||
45 | if ((char *)value + sizeof(ext3_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(ext3_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(ext3_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 | ext3_acl_to_disk(const struct posix_acl *acl, size_t *size) | ||
86 | { | ||
87 | ext3_acl_header *ext_acl; | ||
88 | char *e; | ||
89 | size_t n; | ||
90 | |||
91 | *size = ext3_acl_size(acl->a_count); | ||
92 | ext_acl = (ext3_acl_header *)kmalloc(sizeof(ext3_acl_header) + | ||
93 | acl->a_count * sizeof(ext3_acl_entry), GFP_KERNEL); | ||
94 | if (!ext_acl) | ||
95 | return ERR_PTR(-ENOMEM); | ||
96 | ext_acl->a_version = cpu_to_le32(EXT3_ACL_VERSION); | ||
97 | e = (char *)ext_acl + sizeof(ext3_acl_header); | ||
98 | for (n=0; n < acl->a_count; n++) { | ||
99 | ext3_acl_entry *entry = (ext3_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(ext3_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(ext3_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 | static inline struct posix_acl * | ||
129 | ext3_iget_acl(struct inode *inode, struct posix_acl **i_acl) | ||
130 | { | ||
131 | struct posix_acl *acl = EXT3_ACL_NOT_CACHED; | ||
132 | |||
133 | spin_lock(&inode->i_lock); | ||
134 | if (*i_acl != EXT3_ACL_NOT_CACHED) | ||
135 | acl = posix_acl_dup(*i_acl); | ||
136 | spin_unlock(&inode->i_lock); | ||
137 | |||
138 | return acl; | ||
139 | } | ||
140 | |||
141 | static inline void | ||
142 | ext3_iset_acl(struct inode *inode, struct posix_acl **i_acl, | ||
143 | struct posix_acl *acl) | ||
144 | { | ||
145 | spin_lock(&inode->i_lock); | ||
146 | if (*i_acl != EXT3_ACL_NOT_CACHED) | ||
147 | posix_acl_release(*i_acl); | ||
148 | *i_acl = posix_acl_dup(acl); | ||
149 | spin_unlock(&inode->i_lock); | ||
150 | } | ||
151 | |||
152 | /* | ||
153 | * Inode operation get_posix_acl(). | ||
154 | * | ||
155 | * inode->i_sem: don't care | ||
156 | */ | ||
157 | static struct posix_acl * | ||
158 | ext3_get_acl(struct inode *inode, int type) | ||
159 | { | ||
160 | struct ext3_inode_info *ei = EXT3_I(inode); | ||
161 | int name_index; | ||
162 | char *value = NULL; | ||
163 | struct posix_acl *acl; | ||
164 | int retval; | ||
165 | |||
166 | if (!test_opt(inode->i_sb, POSIX_ACL)) | ||
167 | return NULL; | ||
168 | |||
169 | switch(type) { | ||
170 | case ACL_TYPE_ACCESS: | ||
171 | acl = ext3_iget_acl(inode, &ei->i_acl); | ||
172 | if (acl != EXT3_ACL_NOT_CACHED) | ||
173 | return acl; | ||
174 | name_index = EXT3_XATTR_INDEX_POSIX_ACL_ACCESS; | ||
175 | break; | ||
176 | |||
177 | case ACL_TYPE_DEFAULT: | ||
178 | acl = ext3_iget_acl(inode, &ei->i_default_acl); | ||
179 | if (acl != EXT3_ACL_NOT_CACHED) | ||
180 | return acl; | ||
181 | name_index = EXT3_XATTR_INDEX_POSIX_ACL_DEFAULT; | ||
182 | break; | ||
183 | |||
184 | default: | ||
185 | return ERR_PTR(-EINVAL); | ||
186 | } | ||
187 | retval = ext3_xattr_get(inode, name_index, "", NULL, 0); | ||
188 | if (retval > 0) { | ||
189 | value = kmalloc(retval, GFP_KERNEL); | ||
190 | if (!value) | ||
191 | return ERR_PTR(-ENOMEM); | ||
192 | retval = ext3_xattr_get(inode, name_index, "", value, retval); | ||
193 | } | ||
194 | if (retval > 0) | ||
195 | acl = ext3_acl_from_disk(value, retval); | ||
196 | else if (retval == -ENODATA || retval == -ENOSYS) | ||
197 | acl = NULL; | ||
198 | else | ||
199 | acl = ERR_PTR(retval); | ||
200 | kfree(value); | ||
201 | |||
202 | if (!IS_ERR(acl)) { | ||
203 | switch(type) { | ||
204 | case ACL_TYPE_ACCESS: | ||
205 | ext3_iset_acl(inode, &ei->i_acl, acl); | ||
206 | break; | ||
207 | |||
208 | case ACL_TYPE_DEFAULT: | ||
209 | ext3_iset_acl(inode, &ei->i_default_acl, acl); | ||
210 | break; | ||
211 | } | ||
212 | } | ||
213 | return acl; | ||
214 | } | ||
215 | |||
216 | /* | ||
217 | * Set the access or default ACL of an inode. | ||
218 | * | ||
219 | * inode->i_sem: down unless called from ext3_new_inode | ||
220 | */ | ||
221 | static int | ||
222 | ext3_set_acl(handle_t *handle, struct inode *inode, int type, | ||
223 | struct posix_acl *acl) | ||
224 | { | ||
225 | struct ext3_inode_info *ei = EXT3_I(inode); | ||
226 | int name_index; | ||
227 | void *value = NULL; | ||
228 | size_t size; | ||
229 | int error; | ||
230 | |||
231 | if (S_ISLNK(inode->i_mode)) | ||
232 | return -EOPNOTSUPP; | ||
233 | |||
234 | switch(type) { | ||
235 | case ACL_TYPE_ACCESS: | ||
236 | name_index = EXT3_XATTR_INDEX_POSIX_ACL_ACCESS; | ||
237 | if (acl) { | ||
238 | mode_t mode = inode->i_mode; | ||
239 | error = posix_acl_equiv_mode(acl, &mode); | ||
240 | if (error < 0) | ||
241 | return error; | ||
242 | else { | ||
243 | inode->i_mode = mode; | ||
244 | ext3_mark_inode_dirty(handle, inode); | ||
245 | if (error == 0) | ||
246 | acl = NULL; | ||
247 | } | ||
248 | } | ||
249 | break; | ||
250 | |||
251 | case ACL_TYPE_DEFAULT: | ||
252 | name_index = EXT3_XATTR_INDEX_POSIX_ACL_DEFAULT; | ||
253 | if (!S_ISDIR(inode->i_mode)) | ||
254 | return acl ? -EACCES : 0; | ||
255 | break; | ||
256 | |||
257 | default: | ||
258 | return -EINVAL; | ||
259 | } | ||
260 | if (acl) { | ||
261 | value = ext3_acl_to_disk(acl, &size); | ||
262 | if (IS_ERR(value)) | ||
263 | return (int)PTR_ERR(value); | ||
264 | } | ||
265 | |||
266 | error = ext3_xattr_set_handle(handle, inode, name_index, "", | ||
267 | value, size, 0); | ||
268 | |||
269 | kfree(value); | ||
270 | if (!error) { | ||
271 | switch(type) { | ||
272 | case ACL_TYPE_ACCESS: | ||
273 | ext3_iset_acl(inode, &ei->i_acl, acl); | ||
274 | break; | ||
275 | |||
276 | case ACL_TYPE_DEFAULT: | ||
277 | ext3_iset_acl(inode, &ei->i_default_acl, acl); | ||
278 | break; | ||
279 | } | ||
280 | } | ||
281 | return error; | ||
282 | } | ||
283 | |||
284 | static int | ||
285 | ext3_check_acl(struct inode *inode, int mask) | ||
286 | { | ||
287 | struct posix_acl *acl = ext3_get_acl(inode, ACL_TYPE_ACCESS); | ||
288 | |||
289 | if (acl) { | ||
290 | int error = posix_acl_permission(inode, acl, mask); | ||
291 | posix_acl_release(acl); | ||
292 | return error; | ||
293 | } | ||
294 | |||
295 | return -EAGAIN; | ||
296 | } | ||
297 | |||
298 | int | ||
299 | ext3_permission(struct inode *inode, int mask, struct nameidata *nd) | ||
300 | { | ||
301 | return generic_permission(inode, mask, ext3_check_acl); | ||
302 | } | ||
303 | |||
304 | /* | ||
305 | * Initialize the ACLs of a new inode. Called from ext3_new_inode. | ||
306 | * | ||
307 | * dir->i_sem: down | ||
308 | * inode->i_sem: up (access to inode is still exclusive) | ||
309 | */ | ||
310 | int | ||
311 | ext3_init_acl(handle_t *handle, struct inode *inode, struct inode *dir) | ||
312 | { | ||
313 | struct posix_acl *acl = NULL; | ||
314 | int error = 0; | ||
315 | |||
316 | if (!S_ISLNK(inode->i_mode)) { | ||
317 | if (test_opt(dir->i_sb, POSIX_ACL)) { | ||
318 | acl = ext3_get_acl(dir, ACL_TYPE_DEFAULT); | ||
319 | if (IS_ERR(acl)) | ||
320 | return PTR_ERR(acl); | ||
321 | } | ||
322 | if (!acl) | ||
323 | inode->i_mode &= ~current->fs->umask; | ||
324 | } | ||
325 | if (test_opt(inode->i_sb, POSIX_ACL) && acl) { | ||
326 | struct posix_acl *clone; | ||
327 | mode_t mode; | ||
328 | |||
329 | if (S_ISDIR(inode->i_mode)) { | ||
330 | error = ext3_set_acl(handle, inode, | ||
331 | ACL_TYPE_DEFAULT, acl); | ||
332 | if (error) | ||
333 | goto cleanup; | ||
334 | } | ||
335 | clone = posix_acl_clone(acl, GFP_KERNEL); | ||
336 | error = -ENOMEM; | ||
337 | if (!clone) | ||
338 | goto cleanup; | ||
339 | |||
340 | mode = inode->i_mode; | ||
341 | error = posix_acl_create_masq(clone, &mode); | ||
342 | if (error >= 0) { | ||
343 | inode->i_mode = mode; | ||
344 | if (error > 0) { | ||
345 | /* This is an extended ACL */ | ||
346 | error = ext3_set_acl(handle, inode, | ||
347 | ACL_TYPE_ACCESS, clone); | ||
348 | } | ||
349 | } | ||
350 | posix_acl_release(clone); | ||
351 | } | ||
352 | cleanup: | ||
353 | posix_acl_release(acl); | ||
354 | return error; | ||
355 | } | ||
356 | |||
357 | /* | ||
358 | * Does chmod for an inode that may have an Access Control List. The | ||
359 | * inode->i_mode field must be updated to the desired value by the caller | ||
360 | * before calling this function. | ||
361 | * Returns 0 on success, or a negative error number. | ||
362 | * | ||
363 | * We change the ACL rather than storing some ACL entries in the file | ||
364 | * mode permission bits (which would be more efficient), because that | ||
365 | * would break once additional permissions (like ACL_APPEND, ACL_DELETE | ||
366 | * for directories) are added. There are no more bits available in the | ||
367 | * file mode. | ||
368 | * | ||
369 | * inode->i_sem: down | ||
370 | */ | ||
371 | int | ||
372 | ext3_acl_chmod(struct inode *inode) | ||
373 | { | ||
374 | struct posix_acl *acl, *clone; | ||
375 | int error; | ||
376 | |||
377 | if (S_ISLNK(inode->i_mode)) | ||
378 | return -EOPNOTSUPP; | ||
379 | if (!test_opt(inode->i_sb, POSIX_ACL)) | ||
380 | return 0; | ||
381 | acl = ext3_get_acl(inode, ACL_TYPE_ACCESS); | ||
382 | if (IS_ERR(acl) || !acl) | ||
383 | return PTR_ERR(acl); | ||
384 | clone = posix_acl_clone(acl, GFP_KERNEL); | ||
385 | posix_acl_release(acl); | ||
386 | if (!clone) | ||
387 | return -ENOMEM; | ||
388 | error = posix_acl_chmod_masq(clone, inode->i_mode); | ||
389 | if (!error) { | ||
390 | handle_t *handle; | ||
391 | int retries = 0; | ||
392 | |||
393 | retry: | ||
394 | handle = ext3_journal_start(inode, EXT3_DATA_TRANS_BLOCKS); | ||
395 | if (IS_ERR(handle)) { | ||
396 | error = PTR_ERR(handle); | ||
397 | ext3_std_error(inode->i_sb, error); | ||
398 | goto out; | ||
399 | } | ||
400 | error = ext3_set_acl(handle, inode, ACL_TYPE_ACCESS, clone); | ||
401 | ext3_journal_stop(handle); | ||
402 | if (error == -ENOSPC && | ||
403 | ext3_should_retry_alloc(inode->i_sb, &retries)) | ||
404 | goto retry; | ||
405 | } | ||
406 | out: | ||
407 | posix_acl_release(clone); | ||
408 | return error; | ||
409 | } | ||
410 | |||
411 | /* | ||
412 | * Extended attribute handlers | ||
413 | */ | ||
414 | static size_t | ||
415 | ext3_xattr_list_acl_access(struct inode *inode, char *list, size_t list_len, | ||
416 | const char *name, size_t name_len) | ||
417 | { | ||
418 | const size_t size = sizeof(XATTR_NAME_ACL_ACCESS); | ||
419 | |||
420 | if (!test_opt(inode->i_sb, POSIX_ACL)) | ||
421 | return 0; | ||
422 | if (list && size <= list_len) | ||
423 | memcpy(list, XATTR_NAME_ACL_ACCESS, size); | ||
424 | return size; | ||
425 | } | ||
426 | |||
427 | static size_t | ||
428 | ext3_xattr_list_acl_default(struct inode *inode, char *list, size_t list_len, | ||
429 | const char *name, size_t name_len) | ||
430 | { | ||
431 | const size_t size = sizeof(XATTR_NAME_ACL_DEFAULT); | ||
432 | |||
433 | if (!test_opt(inode->i_sb, POSIX_ACL)) | ||
434 | return 0; | ||
435 | if (list && size <= list_len) | ||
436 | memcpy(list, XATTR_NAME_ACL_DEFAULT, size); | ||
437 | return size; | ||
438 | } | ||
439 | |||
440 | static int | ||
441 | ext3_xattr_get_acl(struct inode *inode, int type, void *buffer, size_t size) | ||
442 | { | ||
443 | struct posix_acl *acl; | ||
444 | int error; | ||
445 | |||
446 | if (!test_opt(inode->i_sb, POSIX_ACL)) | ||
447 | return -EOPNOTSUPP; | ||
448 | |||
449 | acl = ext3_get_acl(inode, type); | ||
450 | if (IS_ERR(acl)) | ||
451 | return PTR_ERR(acl); | ||
452 | if (acl == NULL) | ||
453 | return -ENODATA; | ||
454 | error = posix_acl_to_xattr(acl, buffer, size); | ||
455 | posix_acl_release(acl); | ||
456 | |||
457 | return error; | ||
458 | } | ||
459 | |||
460 | static int | ||
461 | ext3_xattr_get_acl_access(struct inode *inode, const char *name, | ||
462 | void *buffer, size_t size) | ||
463 | { | ||
464 | if (strcmp(name, "") != 0) | ||
465 | return -EINVAL; | ||
466 | return ext3_xattr_get_acl(inode, ACL_TYPE_ACCESS, buffer, size); | ||
467 | } | ||
468 | |||
469 | static int | ||
470 | ext3_xattr_get_acl_default(struct inode *inode, const char *name, | ||
471 | void *buffer, size_t size) | ||
472 | { | ||
473 | if (strcmp(name, "") != 0) | ||
474 | return -EINVAL; | ||
475 | return ext3_xattr_get_acl(inode, ACL_TYPE_DEFAULT, buffer, size); | ||
476 | } | ||
477 | |||
478 | static int | ||
479 | ext3_xattr_set_acl(struct inode *inode, int type, const void *value, | ||
480 | size_t size) | ||
481 | { | ||
482 | handle_t *handle; | ||
483 | struct posix_acl *acl; | ||
484 | int error, retries = 0; | ||
485 | |||
486 | if (!test_opt(inode->i_sb, POSIX_ACL)) | ||
487 | return -EOPNOTSUPP; | ||
488 | if ((current->fsuid != inode->i_uid) && !capable(CAP_FOWNER)) | ||
489 | return -EPERM; | ||
490 | |||
491 | if (value) { | ||
492 | acl = posix_acl_from_xattr(value, size); | ||
493 | if (IS_ERR(acl)) | ||
494 | return PTR_ERR(acl); | ||
495 | else if (acl) { | ||
496 | error = posix_acl_valid(acl); | ||
497 | if (error) | ||
498 | goto release_and_out; | ||
499 | } | ||
500 | } else | ||
501 | acl = NULL; | ||
502 | |||
503 | retry: | ||
504 | handle = ext3_journal_start(inode, EXT3_DATA_TRANS_BLOCKS); | ||
505 | if (IS_ERR(handle)) | ||
506 | return PTR_ERR(handle); | ||
507 | error = ext3_set_acl(handle, inode, type, acl); | ||
508 | ext3_journal_stop(handle); | ||
509 | if (error == -ENOSPC && ext3_should_retry_alloc(inode->i_sb, &retries)) | ||
510 | goto retry; | ||
511 | |||
512 | release_and_out: | ||
513 | posix_acl_release(acl); | ||
514 | return error; | ||
515 | } | ||
516 | |||
517 | static int | ||
518 | ext3_xattr_set_acl_access(struct inode *inode, const char *name, | ||
519 | const void *value, size_t size, int flags) | ||
520 | { | ||
521 | if (strcmp(name, "") != 0) | ||
522 | return -EINVAL; | ||
523 | return ext3_xattr_set_acl(inode, ACL_TYPE_ACCESS, value, size); | ||
524 | } | ||
525 | |||
526 | static int | ||
527 | ext3_xattr_set_acl_default(struct inode *inode, const char *name, | ||
528 | const void *value, size_t size, int flags) | ||
529 | { | ||
530 | if (strcmp(name, "") != 0) | ||
531 | return -EINVAL; | ||
532 | return ext3_xattr_set_acl(inode, ACL_TYPE_DEFAULT, value, size); | ||
533 | } | ||
534 | |||
535 | struct xattr_handler ext3_xattr_acl_access_handler = { | ||
536 | .prefix = XATTR_NAME_ACL_ACCESS, | ||
537 | .list = ext3_xattr_list_acl_access, | ||
538 | .get = ext3_xattr_get_acl_access, | ||
539 | .set = ext3_xattr_set_acl_access, | ||
540 | }; | ||
541 | |||
542 | struct xattr_handler ext3_xattr_acl_default_handler = { | ||
543 | .prefix = XATTR_NAME_ACL_DEFAULT, | ||
544 | .list = ext3_xattr_list_acl_default, | ||
545 | .get = ext3_xattr_get_acl_default, | ||
546 | .set = ext3_xattr_set_acl_default, | ||
547 | }; | ||