aboutsummaryrefslogtreecommitdiffstats
path: root/fs/xfs/linux-2.6/xfs_acl.c
diff options
context:
space:
mode:
authorChristoph Hellwig <hch@lst.de>2009-06-10 11:07:47 -0400
committerChristoph Hellwig <hch@brick.lst.de>2009-06-10 11:07:47 -0400
commitef14f0c1578dce4b688726eb2603e50b62d6665a (patch)
tree5a221081850fab8f96455745e90f4a0e2127bce0 /fs/xfs/linux-2.6/xfs_acl.c
parent8b5403a6d772d340541cfb30a668fde119c40ac1 (diff)
xfs: use generic Posix ACL code
This patch rips out the XFS ACL handling code and uses the generic fs/posix_acl.c code instead. The ondisk format is of course left unchanged. This also introduces the same ACL caching all other Linux filesystems do by adding pointers to the acl and default acl in struct xfs_inode. Signed-off-by: Christoph Hellwig <hch@lst.de> Reviewed-by: Eric Sandeen <sandeen@sandeen.net>
Diffstat (limited to 'fs/xfs/linux-2.6/xfs_acl.c')
-rw-r--r--fs/xfs/linux-2.6/xfs_acl.c523
1 files changed, 523 insertions, 0 deletions
diff --git a/fs/xfs/linux-2.6/xfs_acl.c b/fs/xfs/linux-2.6/xfs_acl.c
new file mode 100644
index 000000000000..1e9d1246eebc
--- /dev/null
+++ b/fs/xfs/linux-2.6/xfs_acl.c
@@ -0,0 +1,523 @@
1/*
2 * Copyright (c) 2008, Christoph Hellwig
3 * All Rights Reserved.
4 *
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License as
7 * published by the Free Software Foundation.
8 *
9 * This program is distributed in the hope that it would be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write the Free Software Foundation,
16 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
17 */
18#include "xfs.h"
19#include "xfs_acl.h"
20#include "xfs_attr.h"
21#include "xfs_bmap_btree.h"
22#include "xfs_inode.h"
23#include "xfs_vnodeops.h"
24#include <linux/xattr.h>
25#include <linux/posix_acl_xattr.h>
26
27
28#define XFS_ACL_NOT_CACHED ((void *)-1)
29
30/*
31 * Locking scheme:
32 * - all ACL updates are protected by inode->i_mutex, which is taken before
33 * calling into this file.
34 * - access and updates to the ip->i_acl and ip->i_default_acl pointers are
35 * protected by inode->i_lock.
36 */
37
38STATIC struct posix_acl *
39xfs_acl_from_disk(struct xfs_acl *aclp)
40{
41 struct posix_acl_entry *acl_e;
42 struct posix_acl *acl;
43 struct xfs_acl_entry *ace;
44 int count, i;
45
46 count = be32_to_cpu(aclp->acl_cnt);
47
48 acl = posix_acl_alloc(count, GFP_KERNEL);
49 if (!acl)
50 return ERR_PTR(-ENOMEM);
51
52 for (i = 0; i < count; i++) {
53 acl_e = &acl->a_entries[i];
54 ace = &aclp->acl_entry[i];
55
56 /*
57 * The tag is 32 bits on disk and 16 bits in core.
58 *
59 * Because every access to it goes through the core
60 * format first this is not a problem.
61 */
62 acl_e->e_tag = be32_to_cpu(ace->ae_tag);
63 acl_e->e_perm = be16_to_cpu(ace->ae_perm);
64
65 switch (acl_e->e_tag) {
66 case ACL_USER:
67 case ACL_GROUP:
68 acl_e->e_id = be32_to_cpu(ace->ae_id);
69 break;
70 case ACL_USER_OBJ:
71 case ACL_GROUP_OBJ:
72 case ACL_MASK:
73 case ACL_OTHER:
74 acl_e->e_id = ACL_UNDEFINED_ID;
75 break;
76 default:
77 goto fail;
78 }
79 }
80 return acl;
81
82fail:
83 posix_acl_release(acl);
84 return ERR_PTR(-EINVAL);
85}
86
87STATIC void
88xfs_acl_to_disk(struct xfs_acl *aclp, const struct posix_acl *acl)
89{
90 const struct posix_acl_entry *acl_e;
91 struct xfs_acl_entry *ace;
92 int i;
93
94 aclp->acl_cnt = cpu_to_be32(acl->a_count);
95 for (i = 0; i < acl->a_count; i++) {
96 ace = &aclp->acl_entry[i];
97 acl_e = &acl->a_entries[i];
98
99 ace->ae_tag = cpu_to_be32(acl_e->e_tag);
100 ace->ae_id = cpu_to_be32(acl_e->e_id);
101 ace->ae_perm = cpu_to_be16(acl_e->e_perm);
102 }
103}
104
105/*
106 * Update the cached ACL pointer in the inode.
107 *
108 * Because we don't hold any locks while reading/writing the attribute
109 * from/to disk another thread could have raced and updated the cached
110 * ACL value before us. In that case we release the previous cached value
111 * and update it with our new value.
112 */
113STATIC void
114xfs_update_cached_acl(struct inode *inode, struct posix_acl **p_acl,
115 struct posix_acl *acl)
116{
117 spin_lock(&inode->i_lock);
118 if (*p_acl && *p_acl != XFS_ACL_NOT_CACHED)
119 posix_acl_release(*p_acl);
120 *p_acl = posix_acl_dup(acl);
121 spin_unlock(&inode->i_lock);
122}
123
124struct posix_acl *
125xfs_get_acl(struct inode *inode, int type)
126{
127 struct xfs_inode *ip = XFS_I(inode);
128 struct posix_acl *acl = NULL, **p_acl;
129 struct xfs_acl *xfs_acl;
130 int len = sizeof(struct xfs_acl);
131 char *ea_name;
132 int error;
133
134 switch (type) {
135 case ACL_TYPE_ACCESS:
136 ea_name = SGI_ACL_FILE;
137 p_acl = &ip->i_acl;
138 break;
139 case ACL_TYPE_DEFAULT:
140 ea_name = SGI_ACL_DEFAULT;
141 p_acl = &ip->i_default_acl;
142 break;
143 default:
144 return ERR_PTR(-EINVAL);
145 }
146
147 spin_lock(&inode->i_lock);
148 if (*p_acl != XFS_ACL_NOT_CACHED)
149 acl = posix_acl_dup(*p_acl);
150 spin_unlock(&inode->i_lock);
151
152 /*
153 * If we have a cached ACLs value just return it, not need to
154 * go out to the disk.
155 */
156 if (acl)
157 return acl;
158
159 xfs_acl = kzalloc(sizeof(struct xfs_acl), GFP_KERNEL);
160 if (!xfs_acl)
161 return ERR_PTR(-ENOMEM);
162
163 error = -xfs_attr_get(ip, ea_name, (char *)xfs_acl, &len, ATTR_ROOT);
164 if (error) {
165 /*
166 * If the attribute doesn't exist make sure we have a negative
167 * cache entry, for any other error assume it is transient and
168 * leave the cache entry as XFS_ACL_NOT_CACHED.
169 */
170 if (error == -ENOATTR) {
171 acl = NULL;
172 goto out_update_cache;
173 }
174 goto out;
175 }
176
177 acl = xfs_acl_from_disk(xfs_acl);
178 if (IS_ERR(acl))
179 goto out;
180
181 out_update_cache:
182 xfs_update_cached_acl(inode, p_acl, acl);
183 out:
184 kfree(xfs_acl);
185 return acl;
186}
187
188STATIC int
189xfs_set_acl(struct inode *inode, int type, struct posix_acl *acl)
190{
191 struct xfs_inode *ip = XFS_I(inode);
192 struct posix_acl **p_acl;
193 char *ea_name;
194 int error;
195
196 if (S_ISLNK(inode->i_mode))
197 return -EOPNOTSUPP;
198
199 switch (type) {
200 case ACL_TYPE_ACCESS:
201 ea_name = SGI_ACL_FILE;
202 p_acl = &ip->i_acl;
203 break;
204 case ACL_TYPE_DEFAULT:
205 if (!S_ISDIR(inode->i_mode))
206 return acl ? -EACCES : 0;
207 ea_name = SGI_ACL_DEFAULT;
208 p_acl = &ip->i_default_acl;
209 break;
210 default:
211 return -EINVAL;
212 }
213
214 if (acl) {
215 struct xfs_acl *xfs_acl;
216 int len;
217
218 xfs_acl = kzalloc(sizeof(struct xfs_acl), GFP_KERNEL);
219 if (!xfs_acl)
220 return -ENOMEM;
221
222 xfs_acl_to_disk(xfs_acl, acl);
223 len = sizeof(struct xfs_acl) -
224 (sizeof(struct xfs_acl_entry) *
225 (XFS_ACL_MAX_ENTRIES - acl->a_count));
226
227 error = -xfs_attr_set(ip, ea_name, (char *)xfs_acl,
228 len, ATTR_ROOT);
229
230 kfree(xfs_acl);
231 } else {
232 /*
233 * A NULL ACL argument means we want to remove the ACL.
234 */
235 error = -xfs_attr_remove(ip, ea_name, ATTR_ROOT);
236
237 /*
238 * If the attribute didn't exist to start with that's fine.
239 */
240 if (error == -ENOATTR)
241 error = 0;
242 }
243
244 if (!error)
245 xfs_update_cached_acl(inode, p_acl, acl);
246 return error;
247}
248
249int
250xfs_check_acl(struct inode *inode, int mask)
251{
252 struct xfs_inode *ip = XFS_I(inode);
253 struct posix_acl *acl;
254 int error = -EAGAIN;
255
256 xfs_itrace_entry(ip);
257
258 /*
259 * If there is no attribute fork no ACL exists on this inode and
260 * we can skip the whole exercise.
261 */
262 if (!XFS_IFORK_Q(ip))
263 return -EAGAIN;
264
265 acl = xfs_get_acl(inode, ACL_TYPE_ACCESS);
266 if (IS_ERR(acl))
267 return PTR_ERR(acl);
268 if (acl) {
269 error = posix_acl_permission(inode, acl, mask);
270 posix_acl_release(acl);
271 }
272
273 return error;
274}
275
276static int
277xfs_set_mode(struct inode *inode, mode_t mode)
278{
279 int error = 0;
280
281 if (mode != inode->i_mode) {
282 struct iattr iattr;
283
284 iattr.ia_valid = ATTR_MODE;
285 iattr.ia_mode = mode;
286
287 error = -xfs_setattr(XFS_I(inode), &iattr, XFS_ATTR_NOACL);
288 }
289
290 return error;
291}
292
293static int
294xfs_acl_exists(struct inode *inode, char *name)
295{
296 int len = sizeof(struct xfs_acl);
297
298 return (xfs_attr_get(XFS_I(inode), name, NULL, &len,
299 ATTR_ROOT|ATTR_KERNOVAL) == 0);
300}
301
302int
303posix_acl_access_exists(struct inode *inode)
304{
305 return xfs_acl_exists(inode, SGI_ACL_FILE);
306}
307
308int
309posix_acl_default_exists(struct inode *inode)
310{
311 if (!S_ISDIR(inode->i_mode))
312 return 0;
313 return xfs_acl_exists(inode, SGI_ACL_DEFAULT);
314}
315
316/*
317 * No need for i_mutex because the inode is not yet exposed to the VFS.
318 */
319int
320xfs_inherit_acl(struct inode *inode, struct posix_acl *default_acl)
321{
322 struct posix_acl *clone;
323 mode_t mode;
324 int error = 0, inherit = 0;
325
326 if (S_ISDIR(inode->i_mode)) {
327 error = xfs_set_acl(inode, ACL_TYPE_DEFAULT, default_acl);
328 if (error)
329 return error;
330 }
331
332 clone = posix_acl_clone(default_acl, GFP_KERNEL);
333 if (!clone)
334 return -ENOMEM;
335
336 mode = inode->i_mode;
337 error = posix_acl_create_masq(clone, &mode);
338 if (error < 0)
339 goto out_release_clone;
340
341 /*
342 * If posix_acl_create_masq returns a positive value we need to
343 * inherit a permission that can't be represented using the Unix
344 * mode bits and we actually need to set an ACL.
345 */
346 if (error > 0)
347 inherit = 1;
348
349 error = xfs_set_mode(inode, mode);
350 if (error)
351 goto out_release_clone;
352
353 if (inherit)
354 error = xfs_set_acl(inode, ACL_TYPE_ACCESS, clone);
355
356 out_release_clone:
357 posix_acl_release(clone);
358 return error;
359}
360
361int
362xfs_acl_chmod(struct inode *inode)
363{
364 struct posix_acl *acl, *clone;
365 int error;
366
367 if (S_ISLNK(inode->i_mode))
368 return -EOPNOTSUPP;
369
370 acl = xfs_get_acl(inode, ACL_TYPE_ACCESS);
371 if (IS_ERR(acl) || !acl)
372 return PTR_ERR(acl);
373
374 clone = posix_acl_clone(acl, GFP_KERNEL);
375 posix_acl_release(acl);
376 if (!clone)
377 return -ENOMEM;
378
379 error = posix_acl_chmod_masq(clone, inode->i_mode);
380 if (!error)
381 error = xfs_set_acl(inode, ACL_TYPE_ACCESS, clone);
382
383 posix_acl_release(clone);
384 return error;
385}
386
387void
388xfs_inode_init_acls(struct xfs_inode *ip)
389{
390 /*
391 * No need for locking, inode is not live yet.
392 */
393 ip->i_acl = XFS_ACL_NOT_CACHED;
394 ip->i_default_acl = XFS_ACL_NOT_CACHED;
395}
396
397void
398xfs_inode_clear_acls(struct xfs_inode *ip)
399{
400 /*
401 * No need for locking here, the inode is not live anymore
402 * and just about to be freed.
403 */
404 if (ip->i_acl != XFS_ACL_NOT_CACHED)
405 posix_acl_release(ip->i_acl);
406 if (ip->i_default_acl != XFS_ACL_NOT_CACHED)
407 posix_acl_release(ip->i_default_acl);
408}
409
410
411/*
412 * System xattr handlers.
413 *
414 * Currently Posix ACLs are the only system namespace extended attribute
415 * handlers supported by XFS, so we just implement the handlers here.
416 * If we ever support other system extended attributes this will need
417 * some refactoring.
418 */
419
420static int
421xfs_decode_acl(const char *name)
422{
423 if (strcmp(name, "posix_acl_access") == 0)
424 return ACL_TYPE_ACCESS;
425 else if (strcmp(name, "posix_acl_default") == 0)
426 return ACL_TYPE_DEFAULT;
427 return -EINVAL;
428}
429
430static int
431xfs_xattr_system_get(struct inode *inode, const char *name,
432 void *value, size_t size)
433{
434 struct posix_acl *acl;
435 int type, error;
436
437 type = xfs_decode_acl(name);
438 if (type < 0)
439 return type;
440
441 acl = xfs_get_acl(inode, type);
442 if (IS_ERR(acl))
443 return PTR_ERR(acl);
444 if (acl == NULL)
445 return -ENODATA;
446
447 error = posix_acl_to_xattr(acl, value, size);
448 posix_acl_release(acl);
449
450 return error;
451}
452
453static int
454xfs_xattr_system_set(struct inode *inode, const char *name,
455 const void *value, size_t size, int flags)
456{
457 struct posix_acl *acl = NULL;
458 int error = 0, type;
459
460 type = xfs_decode_acl(name);
461 if (type < 0)
462 return type;
463 if (flags & XATTR_CREATE)
464 return -EINVAL;
465 if (type == ACL_TYPE_DEFAULT && !S_ISDIR(inode->i_mode))
466 return value ? -EACCES : 0;
467 if ((current_fsuid() != inode->i_uid) && !capable(CAP_FOWNER))
468 return -EPERM;
469
470 if (!value)
471 goto set_acl;
472
473 acl = posix_acl_from_xattr(value, size);
474 if (!acl) {
475 /*
476 * acl_set_file(3) may request that we set default ACLs with
477 * zero length -- defend (gracefully) against that here.
478 */
479 goto out;
480 }
481 if (IS_ERR(acl)) {
482 error = PTR_ERR(acl);
483 goto out;
484 }
485
486 error = posix_acl_valid(acl);
487 if (error)
488 goto out_release;
489
490 error = -EINVAL;
491 if (acl->a_count > XFS_ACL_MAX_ENTRIES)
492 goto out_release;
493
494 if (type == ACL_TYPE_ACCESS) {
495 mode_t mode = inode->i_mode;
496 error = posix_acl_equiv_mode(acl, &mode);
497
498 if (error <= 0) {
499 posix_acl_release(acl);
500 acl = NULL;
501
502 if (error < 0)
503 return error;
504 }
505
506 error = xfs_set_mode(inode, mode);
507 if (error)
508 goto out_release;
509 }
510
511 set_acl:
512 error = xfs_set_acl(inode, type, acl);
513 out_release:
514 posix_acl_release(acl);
515 out:
516 return error;
517}
518
519struct xattr_handler xfs_xattr_system_handler = {
520 .prefix = XATTR_SYSTEM_PREFIX,
521 .get = xfs_xattr_system_get,
522 .set = xfs_xattr_system_set,
523};