vserver 2.0 rc7
[linux-2.6.git] / fs / ext3 / acl.c
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 (IS_ERR(acl))
290                 return PTR_ERR(acl);
291         if (acl) {
292                 int error = posix_acl_permission(inode, acl, mask);
293                 posix_acl_release(acl);
294                 return error;
295         }
296
297         return -EAGAIN;
298 }
299
300 int
301 ext3_permission(struct inode *inode, int mask, struct nameidata *nd)
302 {
303         return generic_permission(inode, mask, ext3_check_acl);
304 }
305
306 /*
307  * Initialize the ACLs of a new inode. Called from ext3_new_inode.
308  *
309  * dir->i_sem: down
310  * inode->i_sem: up (access to inode is still exclusive)
311  */
312 int
313 ext3_init_acl(handle_t *handle, struct inode *inode, struct inode *dir)
314 {
315         struct posix_acl *acl = NULL;
316         int error = 0;
317
318         if (!S_ISLNK(inode->i_mode)) {
319                 if (test_opt(dir->i_sb, POSIX_ACL)) {
320                         acl = ext3_get_acl(dir, ACL_TYPE_DEFAULT);
321                         if (IS_ERR(acl))
322                                 return PTR_ERR(acl);
323                 }
324                 if (!acl)
325                         inode->i_mode &= ~current->fs->umask;
326         }
327         if (test_opt(inode->i_sb, POSIX_ACL) && acl) {
328                 struct posix_acl *clone;
329                 mode_t mode;
330
331                 if (S_ISDIR(inode->i_mode)) {
332                         error = ext3_set_acl(handle, inode,
333                                              ACL_TYPE_DEFAULT, acl);
334                         if (error)
335                                 goto cleanup;
336                 }
337                 clone = posix_acl_clone(acl, GFP_KERNEL);
338                 error = -ENOMEM;
339                 if (!clone)
340                         goto cleanup;
341
342                 mode = inode->i_mode;
343                 error = posix_acl_create_masq(clone, &mode);
344                 if (error >= 0) {
345                         inode->i_mode = mode;
346                         if (error > 0) {
347                                 /* This is an extended ACL */
348                                 error = ext3_set_acl(handle, inode,
349                                                      ACL_TYPE_ACCESS, clone);
350                         }
351                 }
352                 posix_acl_release(clone);
353         }
354 cleanup:
355         posix_acl_release(acl);
356         return error;
357 }
358
359 /*
360  * Does chmod for an inode that may have an Access Control List. The
361  * inode->i_mode field must be updated to the desired value by the caller
362  * before calling this function.
363  * Returns 0 on success, or a negative error number.
364  *
365  * We change the ACL rather than storing some ACL entries in the file
366  * mode permission bits (which would be more efficient), because that
367  * would break once additional permissions (like  ACL_APPEND, ACL_DELETE
368  * for directories) are added. There are no more bits available in the
369  * file mode.
370  *
371  * inode->i_sem: down
372  */
373 int
374 ext3_acl_chmod(struct inode *inode)
375 {
376         struct posix_acl *acl, *clone;
377         int error;
378
379         if (S_ISLNK(inode->i_mode))
380                 return -EOPNOTSUPP;
381         if (!test_opt(inode->i_sb, POSIX_ACL))
382                 return 0;
383         acl = ext3_get_acl(inode, ACL_TYPE_ACCESS);
384         if (IS_ERR(acl) || !acl)
385                 return PTR_ERR(acl);
386         clone = posix_acl_clone(acl, GFP_KERNEL);
387         posix_acl_release(acl);
388         if (!clone)
389                 return -ENOMEM;
390         error = posix_acl_chmod_masq(clone, inode->i_mode);
391         if (!error) {
392                 handle_t *handle;
393                 int retries = 0;
394
395         retry:
396                 handle = ext3_journal_start(inode, EXT3_DATA_TRANS_BLOCKS);
397                 if (IS_ERR(handle)) {
398                         error = PTR_ERR(handle);
399                         ext3_std_error(inode->i_sb, error);
400                         goto out;
401                 }
402                 error = ext3_set_acl(handle, inode, ACL_TYPE_ACCESS, clone);
403                 ext3_journal_stop(handle);
404                 if (error == -ENOSPC &&
405                     ext3_should_retry_alloc(inode->i_sb, &retries))
406                         goto retry;
407         }
408 out:
409         posix_acl_release(clone);
410         return error;
411 }
412
413 /*
414  * Extended attribute handlers
415  */
416 static size_t
417 ext3_xattr_list_acl_access(struct inode *inode, char *list, size_t list_len,
418                            const char *name, size_t name_len)
419 {
420         const size_t size = sizeof(XATTR_NAME_ACL_ACCESS);
421
422         if (!test_opt(inode->i_sb, POSIX_ACL))
423                 return 0;
424         if (list && size <= list_len)
425                 memcpy(list, XATTR_NAME_ACL_ACCESS, size);
426         return size;
427 }
428
429 static size_t
430 ext3_xattr_list_acl_default(struct inode *inode, char *list, size_t list_len,
431                             const char *name, size_t name_len)
432 {
433         const size_t size = sizeof(XATTR_NAME_ACL_DEFAULT);
434
435         if (!test_opt(inode->i_sb, POSIX_ACL))
436                 return 0;
437         if (list && size <= list_len)
438                 memcpy(list, XATTR_NAME_ACL_DEFAULT, size);
439         return size;
440 }
441
442 static int
443 ext3_xattr_get_acl(struct inode *inode, int type, void *buffer, size_t size)
444 {
445         struct posix_acl *acl;
446         int error;
447
448         if (!test_opt(inode->i_sb, POSIX_ACL))
449                 return -EOPNOTSUPP;
450
451         acl = ext3_get_acl(inode, type);
452         if (IS_ERR(acl))
453                 return PTR_ERR(acl);
454         if (acl == NULL)
455                 return -ENODATA;
456         error = posix_acl_to_xattr(acl, buffer, size);
457         posix_acl_release(acl);
458
459         return error;
460 }
461
462 static int
463 ext3_xattr_get_acl_access(struct inode *inode, const char *name,
464                           void *buffer, size_t size)
465 {
466         if (strcmp(name, "") != 0)
467                 return -EINVAL;
468         return ext3_xattr_get_acl(inode, ACL_TYPE_ACCESS, buffer, size);
469 }
470
471 static int
472 ext3_xattr_get_acl_default(struct inode *inode, const char *name,
473                            void *buffer, size_t size)
474 {
475         if (strcmp(name, "") != 0)
476                 return -EINVAL;
477         return ext3_xattr_get_acl(inode, ACL_TYPE_DEFAULT, buffer, size);
478 }
479
480 static int
481 ext3_xattr_set_acl(struct inode *inode, int type, const void *value,
482                    size_t size)
483 {
484         handle_t *handle;
485         struct posix_acl *acl;
486         int error, retries = 0;
487
488         if (!test_opt(inode->i_sb, POSIX_ACL))
489                 return -EOPNOTSUPP;
490         if ((current->fsuid != inode->i_uid) && !capable(CAP_FOWNER))
491                 return -EPERM;
492
493         if (value) {
494                 acl = posix_acl_from_xattr(value, size);
495                 if (IS_ERR(acl))
496                         return PTR_ERR(acl);
497                 else if (acl) {
498                         error = posix_acl_valid(acl);
499                         if (error)
500                                 goto release_and_out;
501                 }
502         } else
503                 acl = NULL;
504
505 retry:
506         handle = ext3_journal_start(inode, EXT3_DATA_TRANS_BLOCKS);
507         if (IS_ERR(handle))
508                 return PTR_ERR(handle);
509         error = ext3_set_acl(handle, inode, type, acl);
510         ext3_journal_stop(handle);
511         if (error == -ENOSPC && ext3_should_retry_alloc(inode->i_sb, &retries))
512                 goto retry;
513
514 release_and_out:
515         posix_acl_release(acl);
516         return error;
517 }
518
519 static int
520 ext3_xattr_set_acl_access(struct inode *inode, const char *name,
521                           const void *value, size_t size, int flags)
522 {
523         if (strcmp(name, "") != 0)
524                 return -EINVAL;
525         return ext3_xattr_set_acl(inode, ACL_TYPE_ACCESS, value, size);
526 }
527
528 static int
529 ext3_xattr_set_acl_default(struct inode *inode, const char *name,
530                            const void *value, size_t size, int flags)
531 {
532         if (strcmp(name, "") != 0)
533                 return -EINVAL;
534         return ext3_xattr_set_acl(inode, ACL_TYPE_DEFAULT, value, size);
535 }
536
537 struct xattr_handler ext3_xattr_acl_access_handler = {
538         .prefix = XATTR_NAME_ACL_ACCESS,
539         .list   = ext3_xattr_list_acl_access,
540         .get    = ext3_xattr_get_acl_access,
541         .set    = ext3_xattr_set_acl_access,
542 };
543
544 struct xattr_handler ext3_xattr_acl_default_handler = {
545         .prefix = XATTR_NAME_ACL_DEFAULT,
546         .list   = ext3_xattr_list_acl_default,
547         .get    = ext3_xattr_get_acl_default,
548         .set    = ext3_xattr_set_acl_default,
549 };