Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
acl.c
Go to the documentation of this file.
1 /*
2  * linux/fs/ext2/acl.c
3  *
4  * Copyright (C) 2001-2003 Andreas Gruenbacher, <[email protected]>
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 !=
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++) {
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  break;
57 
58  case ACL_USER:
59  value = (char *)value + sizeof(ext2_acl_entry);
60  if ((char *)value > end)
61  goto fail;
62  acl->a_entries[n].e_uid =
64  le32_to_cpu(entry->e_id));
65  break;
66  case ACL_GROUP:
67  value = (char *)value + sizeof(ext2_acl_entry);
68  if ((char *)value > end)
69  goto fail;
70  acl->a_entries[n].e_gid =
72  le32_to_cpu(entry->e_id));
73  break;
74 
75  default:
76  goto fail;
77  }
78  }
79  if (value != end)
80  goto fail;
81  return acl;
82 
83 fail:
84  posix_acl_release(acl);
85  return ERR_PTR(-EINVAL);
86 }
87 
88 /*
89  * Convert from in-memory to filesystem representation.
90  */
91 static void *
92 ext2_acl_to_disk(const struct posix_acl *acl, size_t *size)
93 {
94  ext2_acl_header *ext_acl;
95  char *e;
96  size_t n;
97 
98  *size = ext2_acl_size(acl->a_count);
99  ext_acl = kmalloc(sizeof(ext2_acl_header) + acl->a_count *
100  sizeof(ext2_acl_entry), GFP_KERNEL);
101  if (!ext_acl)
102  return ERR_PTR(-ENOMEM);
104  e = (char *)ext_acl + sizeof(ext2_acl_header);
105  for (n=0; n < acl->a_count; n++) {
106  const struct posix_acl_entry *acl_e = &acl->a_entries[n];
107  ext2_acl_entry *entry = (ext2_acl_entry *)e;
108  entry->e_tag = cpu_to_le16(acl_e->e_tag);
109  entry->e_perm = cpu_to_le16(acl_e->e_perm);
110  switch(acl_e->e_tag) {
111  case ACL_USER:
112  entry->e_id = cpu_to_le32(
113  from_kuid(&init_user_ns, acl_e->e_uid));
114  e += sizeof(ext2_acl_entry);
115  break;
116  case ACL_GROUP:
117  entry->e_id = cpu_to_le32(
118  from_kgid(&init_user_ns, acl_e->e_gid));
119  e += sizeof(ext2_acl_entry);
120  break;
121 
122  case ACL_USER_OBJ:
123  case ACL_GROUP_OBJ:
124  case ACL_MASK:
125  case ACL_OTHER:
126  e += sizeof(ext2_acl_entry_short);
127  break;
128 
129  default:
130  goto fail;
131  }
132  }
133  return (char *)ext_acl;
134 
135 fail:
136  kfree(ext_acl);
137  return ERR_PTR(-EINVAL);
138 }
139 
140 /*
141  * inode->i_mutex: don't care
142  */
143 struct posix_acl *
145 {
146  int name_index;
147  char *value = NULL;
148  struct posix_acl *acl;
149  int retval;
150 
151  if (!test_opt(inode->i_sb, POSIX_ACL))
152  return NULL;
153 
154  acl = get_cached_acl(inode, type);
155  if (acl != ACL_NOT_CACHED)
156  return acl;
157 
158  switch (type) {
159  case ACL_TYPE_ACCESS:
161  break;
162  case ACL_TYPE_DEFAULT:
164  break;
165  default:
166  BUG();
167  }
168  retval = ext2_xattr_get(inode, name_index, "", NULL, 0);
169  if (retval > 0) {
170  value = kmalloc(retval, GFP_KERNEL);
171  if (!value)
172  return ERR_PTR(-ENOMEM);
173  retval = ext2_xattr_get(inode, name_index, "", value, retval);
174  }
175  if (retval > 0)
176  acl = ext2_acl_from_disk(value, retval);
177  else if (retval == -ENODATA || retval == -ENOSYS)
178  acl = NULL;
179  else
180  acl = ERR_PTR(retval);
181  kfree(value);
182 
183  if (!IS_ERR(acl))
184  set_cached_acl(inode, type, acl);
185 
186  return acl;
187 }
188 
189 /*
190  * inode->i_mutex: down
191  */
192 static int
193 ext2_set_acl(struct inode *inode, int type, struct posix_acl *acl)
194 {
195  int name_index;
196  void *value = NULL;
197  size_t size = 0;
198  int error;
199 
200  if (S_ISLNK(inode->i_mode))
201  return -EOPNOTSUPP;
202  if (!test_opt(inode->i_sb, POSIX_ACL))
203  return 0;
204 
205  switch(type) {
206  case ACL_TYPE_ACCESS:
208  if (acl) {
209  error = posix_acl_equiv_mode(acl, &inode->i_mode);
210  if (error < 0)
211  return error;
212  else {
213  inode->i_ctime = CURRENT_TIME_SEC;
214  mark_inode_dirty(inode);
215  if (error == 0)
216  acl = NULL;
217  }
218  }
219  break;
220 
221  case ACL_TYPE_DEFAULT:
223  if (!S_ISDIR(inode->i_mode))
224  return acl ? -EACCES : 0;
225  break;
226 
227  default:
228  return -EINVAL;
229  }
230  if (acl) {
231  value = ext2_acl_to_disk(acl, &size);
232  if (IS_ERR(value))
233  return (int)PTR_ERR(value);
234  }
235 
236  error = ext2_xattr_set(inode, name_index, "", value, size, 0);
237 
238  kfree(value);
239  if (!error)
240  set_cached_acl(inode, type, acl);
241  return error;
242 }
243 
244 /*
245  * Initialize the ACLs of a new inode. Called from ext2_new_inode.
246  *
247  * dir->i_mutex: down
248  * inode->i_mutex: up (access to inode is still exclusive)
249  */
250 int
251 ext2_init_acl(struct inode *inode, struct inode *dir)
252 {
253  struct posix_acl *acl = NULL;
254  int error = 0;
255 
256  if (!S_ISLNK(inode->i_mode)) {
257  if (test_opt(dir->i_sb, POSIX_ACL)) {
258  acl = ext2_get_acl(dir, ACL_TYPE_DEFAULT);
259  if (IS_ERR(acl))
260  return PTR_ERR(acl);
261  }
262  if (!acl)
263  inode->i_mode &= ~current_umask();
264  }
265  if (test_opt(inode->i_sb, POSIX_ACL) && acl) {
266  if (S_ISDIR(inode->i_mode)) {
267  error = ext2_set_acl(inode, ACL_TYPE_DEFAULT, acl);
268  if (error)
269  goto cleanup;
270  }
271  error = posix_acl_create(&acl, GFP_KERNEL, &inode->i_mode);
272  if (error < 0)
273  return error;
274  if (error > 0) {
275  /* This is an extended ACL */
276  error = ext2_set_acl(inode, ACL_TYPE_ACCESS, acl);
277  }
278  }
279 cleanup:
280  posix_acl_release(acl);
281  return error;
282 }
283 
284 /*
285  * Does chmod for an inode that may have an Access Control List. The
286  * inode->i_mode field must be updated to the desired value by the caller
287  * before calling this function.
288  * Returns 0 on success, or a negative error number.
289  *
290  * We change the ACL rather than storing some ACL entries in the file
291  * mode permission bits (which would be more efficient), because that
292  * would break once additional permissions (like ACL_APPEND, ACL_DELETE
293  * for directories) are added. There are no more bits available in the
294  * file mode.
295  *
296  * inode->i_mutex: down
297  */
298 int
299 ext2_acl_chmod(struct inode *inode)
300 {
301  struct posix_acl *acl;
302  int error;
303 
304  if (!test_opt(inode->i_sb, POSIX_ACL))
305  return 0;
306  if (S_ISLNK(inode->i_mode))
307  return -EOPNOTSUPP;
308  acl = ext2_get_acl(inode, ACL_TYPE_ACCESS);
309  if (IS_ERR(acl) || !acl)
310  return PTR_ERR(acl);
311  error = posix_acl_chmod(&acl, GFP_KERNEL, inode->i_mode);
312  if (error)
313  return error;
314  error = ext2_set_acl(inode, ACL_TYPE_ACCESS, acl);
315  posix_acl_release(acl);
316  return error;
317 }
318 
319 /*
320  * Extended attribut handlers
321  */
322 static size_t
323 ext2_xattr_list_acl_access(struct dentry *dentry, char *list, size_t list_size,
324  const char *name, size_t name_len, int type)
325 {
326  const size_t size = sizeof(POSIX_ACL_XATTR_ACCESS);
327 
328  if (!test_opt(dentry->d_sb, POSIX_ACL))
329  return 0;
330  if (list && size <= list_size)
331  memcpy(list, POSIX_ACL_XATTR_ACCESS, size);
332  return size;
333 }
334 
335 static size_t
336 ext2_xattr_list_acl_default(struct dentry *dentry, char *list, size_t list_size,
337  const char *name, size_t name_len, int type)
338 {
339  const size_t size = sizeof(POSIX_ACL_XATTR_DEFAULT);
340 
341  if (!test_opt(dentry->d_sb, POSIX_ACL))
342  return 0;
343  if (list && size <= list_size)
344  memcpy(list, POSIX_ACL_XATTR_DEFAULT, size);
345  return size;
346 }
347 
348 static int
349 ext2_xattr_get_acl(struct dentry *dentry, const char *name, void *buffer,
350  size_t size, int type)
351 {
352  struct posix_acl *acl;
353  int error;
354 
355  if (strcmp(name, "") != 0)
356  return -EINVAL;
357  if (!test_opt(dentry->d_sb, POSIX_ACL))
358  return -EOPNOTSUPP;
359 
360  acl = ext2_get_acl(dentry->d_inode, type);
361  if (IS_ERR(acl))
362  return PTR_ERR(acl);
363  if (acl == NULL)
364  return -ENODATA;
365  error = posix_acl_to_xattr(&init_user_ns, acl, buffer, size);
366  posix_acl_release(acl);
367 
368  return error;
369 }
370 
371 static int
372 ext2_xattr_set_acl(struct dentry *dentry, const char *name, const void *value,
373  size_t size, int flags, int type)
374 {
375  struct posix_acl *acl;
376  int error;
377 
378  if (strcmp(name, "") != 0)
379  return -EINVAL;
380  if (!test_opt(dentry->d_sb, POSIX_ACL))
381  return -EOPNOTSUPP;
382  if (!inode_owner_or_capable(dentry->d_inode))
383  return -EPERM;
384 
385  if (value) {
386  acl = posix_acl_from_xattr(&init_user_ns, value, size);
387  if (IS_ERR(acl))
388  return PTR_ERR(acl);
389  else if (acl) {
390  error = posix_acl_valid(acl);
391  if (error)
392  goto release_and_out;
393  }
394  } else
395  acl = NULL;
396 
397  error = ext2_set_acl(dentry->d_inode, type, acl);
398 
399 release_and_out:
400  posix_acl_release(acl);
401  return error;
402 }
403 
405  .prefix = POSIX_ACL_XATTR_ACCESS,
406  .flags = ACL_TYPE_ACCESS,
407  .list = ext2_xattr_list_acl_access,
408  .get = ext2_xattr_get_acl,
409  .set = ext2_xattr_set_acl,
410 };
411 
413  .prefix = POSIX_ACL_XATTR_DEFAULT,
414  .flags = ACL_TYPE_DEFAULT,
415  .list = ext2_xattr_list_acl_default,
416  .get = ext2_xattr_get_acl,
417  .set = ext2_xattr_set_acl,
418 };