root/branches/orange-next/src/kernel/linux-2.6/acl.c @ 8994

Revision 8994, 26.9 KB (checked in by dcypher, 22 months ago)

replaced %llu->%s for (PVFS|TROVE)_handle

Line 
1/*
2 * (C) 2001 Clemson University and The University of Chicago
3 *
4 * See COPYING in top-level directory.
5 */
6
7/** \file
8 *  \ingroup pvfs2linux
9 *
10 *  Linux VFS Access Control List callbacks.
11 *  This owes quite a bit of code to the ext2 acl code
12 *  with appropriate modifications necessary for PVFS2.
13 *  Currently works only for 2.6 kernels. No reason why it should
14 *  not work for 2.4 kernels, but I am way too lazy to add that right now.
15 */
16
17#include "pvfs2-kernel.h"
18#include "pvfs2-bufmap.h"
19
20#if !defined(PVFS2_LINUX_KERNEL_2_4) && defined(HAVE_GENERIC_GETXATTR) && defined(CONFIG_FS_POSIX_ACL)
21#include "pvfs2-internal.h"
22
23#ifdef HAVE_POSIX_ACL_H
24#include <linux/posix_acl.h>
25#endif
26#ifdef HAVE_LINUX_POSIX_ACL_XATTR_H
27#include <linux/posix_acl_xattr.h>
28#endif
29#include <linux/xattr.h>
30#ifdef HAVE_LINUX_XATTR_ACL_H
31#include <linux/xattr_acl.h>
32#endif
33#include "bmi-byteswap.h"
34#include <linux/fs_struct.h>
35#include "../../common/misc/pvfs2-handle-to-str.h"
36
37/*
38 * Encoding and Decoding the extended attributes so that we can
39 * retrieve it properly on any architecture.
40 * Should these go any faster by making them as macros?
41 * Probably not in the fast-path though...
42 */
43
44/*
45 * PVFS2 ACL decode
46 */
47static struct posix_acl *
48pvfs2_acl_decode(const void *value, size_t size)
49{
50    int n, count;
51    struct posix_acl *acl;
52    const char *end = (char *)value + size;
53
54    /* badness! */
55    if (!value)
56    {
57        gossip_err("pvfs2_acl_decode: NULL buffers\n");
58        return NULL;
59    }
60    /* even more badness */
61    if (size < 0 || (size  % sizeof(pvfs2_acl_entry)) != 0)
62    {
63        gossip_err("pvfs2_acl_decode: Invalid value of size %d [should be a multiple of %d]\n",
64                (int) size, (int) sizeof(pvfs2_acl_entry));
65        return ERR_PTR(-EINVAL);
66    }
67    count = size / sizeof(pvfs2_acl_entry);
68    /* No ACLs */
69    if (count == 0)
70    {
71        gossip_debug(GOSSIP_ACL_DEBUG, "pvfs2_acl_decode: no acls!\n");
72        return NULL;
73    }
74    /* Allocate a posix acl structure */
75    acl = posix_acl_alloc(count, GFP_KERNEL);
76    if (!acl)
77    {
78        gossip_err("pvfs2_acl_decode: Could not allocate acl!\n");
79        return ERR_PTR(-ENOMEM);
80    }
81    gossip_debug(GOSSIP_ACL_DEBUG, "acl decoded %zd bytes (%d acl entries)\n",
82            size, count);
83    for (n = 0; n < count; n++)
84    {
85        pvfs2_acl_entry *entry = (pvfs2_acl_entry *)value;
86
87        if ((char *) value + sizeof(pvfs2_acl_entry) > end)
88            goto fail;
89       
90        acl->a_entries[n].e_tag = bmitoh32(entry->p_tag);
91        acl->a_entries[n].e_perm = bmitoh32(entry->p_perm);
92        gossip_debug(GOSSIP_ACL_DEBUG, "Decoded acl entry %d "
93                "(p_tag %d, p_perm %d, p_id %d)\n",
94                n, acl->a_entries[n].e_tag, acl->a_entries[n].e_perm,
95                bmitoh32(entry->p_id));
96        switch(acl->a_entries[n].e_tag)
97        {
98            case ACL_USER_OBJ:
99            case ACL_GROUP_OBJ:
100            case ACL_MASK:
101            case ACL_OTHER:
102                acl->a_entries[n].e_id = ACL_UNDEFINED_ID;
103                value += sizeof(pvfs2_acl_entry);
104                break;
105
106            case ACL_USER:
107            case ACL_GROUP:
108                acl->a_entries[n].e_id =
109                        bmitoh32(entry->p_id);
110                value += sizeof(pvfs2_acl_entry);
111                break;
112
113            default:
114                gossip_err("pvfs2_acl_decode: bogus value of e_tag obtained %d\n",
115                        acl->a_entries[n].e_tag);
116                goto fail;
117        }
118    }
119    if (value != end)
120        goto fail;
121    return acl;
122
123fail:
124    posix_acl_release(acl);
125    gossip_err("pvfs2_acl_decode: returning EINVAL\n");
126    return ERR_PTR(-EINVAL);
127}
128
129/*
130 * PVFS2 ACL encode
131 * What this does is encode the posix_acl structure
132 * into little-endian bytefirst using the htobmi* macros
133 * and stuffs it into a buffer for storage.
134 */
135static void *
136pvfs2_acl_encode(const struct posix_acl *acl, size_t *size)
137{
138    char *e, *ptr;
139    size_t n;
140
141    *size = acl->a_count * sizeof(pvfs2_acl_entry);
142    gossip_debug(GOSSIP_ACL_DEBUG, "pvfs2_acl_encode: acl encoded %ld bytes "
143            " (%d entries)\n", (long) *size, acl->a_count);
144    e = (char *)kmalloc(*size, GFP_KERNEL);
145    if (!e)
146    {
147        gossip_err("pvfs2_acl_encode: Could not allocate %d bytes "
148                "for acl encode\n", (int) *size);
149        return ERR_PTR(-ENOMEM);
150    }
151    ptr = e;
152    for (n = 0; n < acl->a_count; n++)
153    {
154        pvfs2_acl_entry *entry = (pvfs2_acl_entry *)e;
155
156        entry->p_tag  = htobmi32(acl->a_entries[n].e_tag);
157        entry->p_perm = htobmi32(acl->a_entries[n].e_perm);
158        switch (acl->a_entries[n].e_tag)
159        {
160            case ACL_USER:
161            case ACL_GROUP:
162                entry->p_id = htobmi32(acl->a_entries[n].e_id);
163                e += sizeof(pvfs2_acl_entry);
164                break;
165            case ACL_USER_OBJ:
166            case ACL_GROUP_OBJ:
167            case ACL_MASK:
168            case ACL_OTHER:
169                entry->p_id = htobmi32(ACL_UNDEFINED_ID);
170                e += sizeof(pvfs2_acl_entry);
171                break;
172
173            default:
174                gossip_err("pvfs2_acl_encode: bogus value of e_tag %d\n",
175                        acl->a_entries[n].e_tag);
176                goto fail;
177        }
178        gossip_debug(GOSSIP_ACL_DEBUG, "Encoded acl entry %zd "
179                "(p_tag %d, p_perm %d, p_id %d)\n",
180                n, acl->a_entries[n].e_tag, acl->a_entries[n].e_perm,
181                acl->a_entries[n].e_id);
182    }
183    return (char *) ptr;
184fail:
185    kfree(ptr);
186    gossip_err("pvfs2_acl_encode: returning EINVAL\n");
187    return ERR_PTR(-EINVAL);
188}
189
190/**
191 * Routines that retrieve and/or set ACLs for PVFS2 files.
192 */
193static struct posix_acl *
194pvfs2_get_acl(struct inode *inode, int type)
195{
196    struct posix_acl *acl;
197    int ret;
198    char *key = NULL, *value = NULL;
199
200    /* Won't work if you don't mount with the right set of options */
201    if (get_acl_flag(inode) == 0)
202    {
203        gossip_debug(GOSSIP_ACL_DEBUG, "pvfs2_get_acl: ACL options disabled on "
204                "this FS!\n");
205        return NULL;
206    }
207    switch (type)
208    {
209        case ACL_TYPE_ACCESS:
210            key = PVFS2_XATTR_NAME_ACL_ACCESS;
211            break;
212        case ACL_TYPE_DEFAULT:
213            key = PVFS2_XATTR_NAME_ACL_DEFAULT;
214            break;
215        default:
216            gossip_err("pvfs2_get_acl: bogus value of type %d\n", type);
217            return ERR_PTR(-EINVAL);
218    }
219    /*
220     * Rather than incurring a network call just to determine the exact length of
221     * the attribute, I just allocate a max length to save on the network call
222     * Conceivably, we could pass NULL to pvfs2_inode_getxattr() to probe the length
223     * of the value, but I don't do that for now.
224     */
225    value = (char *) kmalloc(PVFS_MAX_XATTR_VALUELEN, GFP_KERNEL);
226    if (value == NULL)
227    {
228        gossip_err("pvfs2_get_acl: Could not allocate value ptr\n");
229        return ERR_PTR(-ENOMEM);
230    }
231    gossip_debug(GOSSIP_ACL_DEBUG, "inode %s, key %s, type %d\n",
232            PVFS_handle_to_str(get_handle_from_ino(inode)), key, type);
233    ret = pvfs2_inode_getxattr(inode, "", key, value, PVFS_MAX_XATTR_VALUELEN);
234    /* if the key exists, convert it to an in-memory rep */
235    if (ret > 0)
236    {
237        acl = pvfs2_acl_decode(value, ret);
238    }
239    else if (ret == -ENODATA || ret == -ENOSYS)
240    {
241        acl = NULL;
242    }
243    else {
244        gossip_err("inode %s retrieving acl's failed with error %d\n",
245                PVFS_handle_to_str(get_handle_from_ino(inode)), ret);
246        acl = ERR_PTR(ret);
247    }
248    if (value) {
249        kfree(value);
250    }
251    return acl;
252}
253
254static int
255pvfs2_set_acl(struct inode *inode, int type, struct posix_acl *acl)
256{
257    int error = 0;
258    void *value = NULL;
259    size_t size = 0;
260    const char *name = NULL;
261    pvfs2_inode_t *pvfs2_inode = PVFS2_I(inode);
262
263    /* We dont't allow this on a symbolic link */
264    if (S_ISLNK(inode->i_mode))
265    {
266        gossip_err("pvfs2_set_acl: disallow on symbolic links\n");
267        return -EACCES;
268    }
269    /* if ACL option is not set, then we return early */
270    if (get_acl_flag(inode) == 0)
271    {
272        gossip_debug(GOSSIP_ACL_DEBUG, "pvfs2_set_acl: ACL options disabled on"
273                "this FS!\n");
274        return 0;
275    }
276    switch (type)
277    {
278        case ACL_TYPE_ACCESS:
279        {
280            name = PVFS2_XATTR_NAME_ACL_ACCESS;
281            if (acl)
282            {
283                mode_t mode = inode->i_mode;
284                /* can we represent this with the UNIXy permission bits? */
285                error = posix_acl_equiv_mode(acl, &mode);
286                /* uh oh some error.. */
287                if (error < 0)
288                {
289                    gossip_err("pvfs2_set_acl: posix_acl_equiv_mode error %d\n",
290                            error);
291                    return error;
292                }
293                else /* okay, go ahead and do just that */
294                {
295                    if (inode->i_mode != mode)
296                        SetModeFlag(pvfs2_inode);
297                    inode->i_mode = mode;
298                    mark_inode_dirty_sync(inode);
299                    if (error == 0) /* equivalent. so dont set acl! */
300                        acl = NULL;
301                }
302            }
303            break;
304        }
305        case ACL_TYPE_DEFAULT:
306        {
307            name = PVFS2_XATTR_NAME_ACL_DEFAULT;
308            /* Default ACLs cannot be set/modified for non-directory objects! */
309            if (!S_ISDIR(inode->i_mode))
310            {
311                gossip_debug(GOSSIP_ACL_DEBUG, "pvfs2_set_acl: setting default "
312                        "ACLs on non-dir object? %s\n",
313                        acl ? "disallowed" : "ok");
314                return acl ? -EACCES : 0;
315            }
316            break;
317        }
318        default:
319        {
320            gossip_err("pvfs2_set_acl: invalid type %d!\n", type);
321            return -EINVAL;
322        }
323    }
324    gossip_debug(GOSSIP_ACL_DEBUG, "pvfs2_set_acl: inode %s, key %s type %d\n",
325            PVFS_handle_to_str(get_handle_from_ino(inode)), name, type);
326    /* If we do have an access control list, then we need to encode that! */
327    if (acl)
328    {
329        value = pvfs2_acl_encode(acl, &size);
330        if (IS_ERR(value))
331        {
332            return (int) PTR_ERR(value);
333        }
334    }
335    gossip_debug(GOSSIP_ACL_DEBUG, "pvfs2_set_acl: name %s, value %p, size %zd, "
336            " acl %p\n", name, value, size, acl);
337    /* Go ahead and set the extended attribute now
338     * NOTE: Suppose acl was NULL, then value will be NULL and
339     * size will be 0 and that will xlate to a removexattr.
340     * However, we dont want removexattr complain if attributes
341     * does not exist.
342     */
343    error = pvfs2_inode_setxattr(inode, "", name, value, size, 0);
344    if (value)
345    {
346        kfree(value);
347    }
348    return error;
349}
350
351static int
352pvfs2_xattr_get_acl(struct inode *inode, int type, void *buffer, size_t size)
353{
354    struct posix_acl *acl;
355    int error;
356
357    /* if we have not been mounted with acl option, ignore this */
358    if (get_acl_flag(inode) == 0)
359    {
360        gossip_debug(GOSSIP_ACL_DEBUG, "pvfs2_xattr_get_acl: ACL options "
361                "disabled on this FS!\n");
362        return -EOPNOTSUPP;
363    }
364    acl = pvfs2_get_acl(inode, type);
365    if (IS_ERR(acl))
366    {
367        error = PTR_ERR(acl);
368        gossip_err("pvfs2_get_acl failed with error %d\n", error);
369        goto out;
370    }
371    if (acl == NULL)
372    {
373        error = -ENODATA;
374        goto out;
375    }
376    error = posix_acl_to_xattr(acl, buffer, size);
377    posix_acl_release(acl);
378    gossip_debug(GOSSIP_ACL_DEBUG, "pvfs2_xattr_get_acl: posix_acl_to_xattr "
379            "returned %d\n", error);
380out:
381    return error;
382}
383
384static int pvfs2_xattr_get_acl_access(
385#ifdef HAVE_XATTR_HANDLER_GET_FIVE_PARAM
386        struct dentry *dentry,
387#else
388        struct inode *inode,
389#endif /* HAVE_XATTR_HANDLER_GET_FIVE_PARAM */
390        const char *name,
391        void *buffer,
392        size_t size
393#ifdef HAVE_XATTR_HANDLER_GET_FIVE_PARAM
394        , int handler_flag
395#endif /* HAVE_XATTR_HANDLER_GET_FIVE_PARAM */
396        )
397{
398    gossip_debug(GOSSIP_ACL_DEBUG, "pvfs2_xattr_get_acl_access %s\n", name);
399    if (strcmp(name, "") != 0)
400    {
401        gossip_err("get_acl_access invalid name %s\n", name);
402        return -EINVAL;
403    }
404#ifdef HAVE_XATTR_HANDLER_GET_FIVE_PARAM
405    return pvfs2_xattr_get_acl(dentry->d_inode, ACL_TYPE_ACCESS, buffer, size);
406#else
407    return pvfs2_xattr_get_acl(inode, ACL_TYPE_ACCESS, buffer, size);
408#endif /* HAVE_XATTR_HANDLER_GET_FIVE_PARAM */
409}
410
411static int pvfs2_xattr_get_acl_default(
412#ifdef HAVE_XATTR_HANDLER_GET_FIVE_PARAM
413        struct dentry *dentry,
414#else
415        struct inode *inode,
416#endif /* HAVE_XATTR_HANDLER_GET_FIVE_PARAM */
417        const char *name,
418        void *buffer,
419        size_t size
420#ifdef HAVE_XATTR_HANDLER_GET_FIVE_PARAM
421        , int handler_flags
422#endif /* HAVE_XATTR_HANDLER_GET_FIVE_PARAM */
423        )
424{
425    gossip_debug(GOSSIP_ACL_DEBUG, "pvfs2_xattr_get_acl_default %s\n", name);
426    if (strcmp(name, "") != 0)
427    {
428        gossip_err("get_acl_default invalid name %s\n", name);
429        return -EINVAL;
430    }
431#ifdef HAVE_XATTR_HANDLER_GET_FIVE_PARAM
432    return pvfs2_xattr_get_acl(dentry->d_inode, ACL_TYPE_DEFAULT, buffer, size);
433#else
434    return pvfs2_xattr_get_acl(inode, ACL_TYPE_DEFAULT, buffer, size);
435#endif /* HAVE_XATTR_HANDLER_GET_FIVE_PARAM */
436}
437
438static int pvfs2_xattr_set_acl(
439struct inode *inode, int type, const void *value,
440        size_t size)
441{
442    struct posix_acl *acl;
443    int error;
444#ifdef HAVE_CURRENT_FSUID
445    int fsuid = current_fsuid();
446#else
447    int fsuid = current->fsuid;
448#endif
449
450    gossip_debug(GOSSIP_ACL_DEBUG, "pvfs2_xattr_set_acl called with size %ld\n",
451            (long)size);
452    /* if we have not been mounted with acl option, ignore this */
453    if (get_acl_flag(inode) == 0)
454    {
455        gossip_debug(GOSSIP_ACL_DEBUG, "pvfs2_xattr_set_acl: ACL options "
456                "disabled on this FS!\n");
457        return -EOPNOTSUPP;
458    }
459    /* Are we capable of setting acls on a file for which we should not be? */
460    if ((fsuid != inode->i_uid) && !capable(CAP_FOWNER))
461    {
462        gossip_err("pvfs2_xattr_set_acl: operation not permitted "
463                "(current->fsuid %d), (inode->owner %d)\n",
464                fsuid, inode->i_uid);
465        return -EPERM;
466    }
467    if (value)
468    {
469        acl = posix_acl_from_xattr(value, size);
470        if (IS_ERR(acl))
471        {
472            error = PTR_ERR(acl);
473            gossip_err("pvfs2_xattr_set_acl: posix_acl_from_xattr returned "
474                    "error %d\n", error);
475            goto err;
476        }
477        else if (acl)
478        {
479            error = posix_acl_valid(acl);
480            if (error)
481            {
482                gossip_err("pvfs2_xattr_set_acl: posix_acl_valid returned "
483                        "error %d\n", error);
484                goto out;
485            }
486        }
487    }
488    else {
489        acl = NULL;
490    }
491    error = pvfs2_set_acl(inode, type, acl);
492    gossip_debug(GOSSIP_ACL_DEBUG, "pvfs2_set_acl returned error %d\n", error);
493out:
494    posix_acl_release(acl);
495err:
496    return error;
497}
498
499static int pvfs2_xattr_set_acl_access(
500#ifdef HAVE_XATTR_HANDLER_SET_SIX_PARAM
501        struct dentry *dentry,
502#else
503        struct inode *inode,
504#endif /* HAVE_XATTR_HANDLER_SET_SIX_PARAM */
505        const char *name,
506        const void *buffer,
507        size_t size,
508        int flags
509#ifdef HAVE_XATTR_HANDLER_SET_SIX_PARAM
510        , int handler_flags
511#endif /* HAVE_XATTR_HANDLER_SET_SIX_PARAM */
512        )
513{
514    gossip_debug(GOSSIP_ACL_DEBUG, "pvfs2_xattr_set_acl_access: %s\n", name);
515    if (strcmp(name, "") != 0)
516    {
517        gossip_err("set_acl_access invalid name %s\n", name);
518        return -EINVAL;
519    }
520#ifdef HAVE_XATTR_HANDLER_SET_SIX_PARAM
521    return pvfs2_xattr_set_acl(dentry->d_inode, ACL_TYPE_ACCESS, buffer, size);
522#else
523    return pvfs2_xattr_set_acl(inode, ACL_TYPE_ACCESS, buffer, size);
524#endif /* HAVE_XATTR_HANDLER_SET_SIX_PARAM */
525}
526
527static int pvfs2_xattr_set_acl_default(
528#ifdef HAVE_XATTR_HANDLER_SET_SIX_PARAM
529        struct dentry *dentry,
530#else
531        struct inode *inode,
532#endif /* HAVE_XATTR_HANDLER_SET_SIX_PARAM */
533        const char *name,
534        const void *buffer,
535        size_t size,
536        int flags
537#ifdef HAVE_XATTR_HANDLER_SET_SIX_PARAM
538        , int handler_flags
539#endif /* HAVE_XATTR_HANDLER_SET_SIX_PARAM */
540        )
541{
542    gossip_debug(GOSSIP_ACL_DEBUG, "pvfs2_xattr_set_acl_default: %s\n", name);
543    if (strcmp(name, "") != 0)
544    {
545        gossip_err("set_acl_default invalid name %s\n", name);
546        return -EINVAL;
547    }
548#ifdef HAVE_XATTR_HANDLER_SET_SIX_PARAM
549    return pvfs2_xattr_set_acl(dentry->d_inode, ACL_TYPE_DEFAULT, buffer, size);
550#else
551    return pvfs2_xattr_set_acl(inode, ACL_TYPE_DEFAULT, buffer, size);
552#endif
553}
554
555struct xattr_handler pvfs2_xattr_acl_access_handler = {
556    .prefix = PVFS2_XATTR_NAME_ACL_ACCESS,
557    .get    = pvfs2_xattr_get_acl_access,
558    .set    = pvfs2_xattr_set_acl_access,
559};
560
561struct xattr_handler pvfs2_xattr_acl_default_handler = {
562    .prefix = PVFS2_XATTR_NAME_ACL_DEFAULT,
563    .get    = pvfs2_xattr_get_acl_default,
564    .set    = pvfs2_xattr_set_acl_default,
565};
566
567/*
568 * initialize the ACLs of a new inode.
569 * This needs to be called from pvfs2_get_custom_inode.
570 * Note that for the root of the PVFS2 file system,
571 * dir will be NULL! For all others dir will be non-NULL
572 * However, inode cannot be NULL!
573 * Returns 0 on success and -ve number on failure.
574 */
575int pvfs2_init_acl(struct inode *inode, struct inode *dir)
576{
577    struct posix_acl *acl = NULL;
578    int error = 0;
579    pvfs2_inode_t *pvfs2_inode = PVFS2_I(inode);
580
581    if (dir == NULL)
582        dir = inode;
583    ClearModeFlag(pvfs2_inode);
584    if (!S_ISLNK(inode->i_mode))
585    {
586        if (get_acl_flag(inode) == 1)
587        {
588            acl = pvfs2_get_acl(dir, ACL_TYPE_DEFAULT);
589            if (IS_ERR(acl)) {
590                error = PTR_ERR(acl);
591                gossip_err("pvfs2_get_acl (default) failed with error %d\n", error);
592                return error;
593            }
594        }
595        if (!acl && dir != inode)
596        {
597            int old_mode = inode->i_mode;
598            inode->i_mode &= ~current->fs->umask;
599            gossip_debug(GOSSIP_ACL_DEBUG, "inode->i_mode before %o and "
600                    "after %o\n", old_mode, inode->i_mode);
601            if (old_mode != inode->i_mode)
602                SetModeFlag(pvfs2_inode);
603        }
604    }
605    if (get_acl_flag(inode) == 1 && acl)
606    {
607        struct posix_acl *clone;
608        mode_t mode;
609
610        if (S_ISDIR(inode->i_mode))
611        {
612            error = pvfs2_set_acl(inode, ACL_TYPE_DEFAULT, acl);
613            if (error) {
614                gossip_err("pvfs2_set_acl (default) directory failed with "
615                        "error %d\n", error);
616                ClearModeFlag(pvfs2_inode);
617                goto cleanup;
618            }
619        }
620        clone = posix_acl_clone(acl, GFP_KERNEL);
621        error = -ENOMEM;
622        if (!clone) {
623            gossip_err("posix_acl_clone failed with ENOMEM\n");
624            ClearModeFlag(pvfs2_inode);
625            goto cleanup;
626        }
627        mode = inode->i_mode;
628        error = posix_acl_create_masq(clone, &mode);
629        if (error >= 0)
630        {
631            gossip_debug(GOSSIP_ACL_DEBUG, "posix_acl_create_masq changed mode "
632                    "from %o to %o\n", inode->i_mode, mode);
633            /*
634             * Dont do a needless ->setattr() if mode has not changed
635             */
636            if (inode->i_mode != mode)
637                SetModeFlag(pvfs2_inode);
638            inode->i_mode = mode;
639            /*
640             * if this is an ACL that cannot be captured by
641             * the mode bits, go for the server!
642             */
643            if (error > 0)
644            {
645                error = pvfs2_set_acl(inode, ACL_TYPE_ACCESS, clone);
646                gossip_debug(GOSSIP_ACL_DEBUG, "pvfs2_set_acl (access) returned %d\n", error);
647            }
648        }
649        posix_acl_release(clone);
650    }
651    /* If mode of the inode was changed, then do a forcible ->setattr */
652    if (ModeFlag(pvfs2_inode))
653        pvfs2_flush_inode(inode);
654cleanup:
655    posix_acl_release(acl);
656    return error;
657}
658
659/*
660 * Handles the case when a chmod is done for an inode that may have an
661 * access control list.
662 * The inode->i_mode field is updated to the desired value by the caller
663 * before calling this function which returns 0 on success and a -ve
664 * number on failure.
665 */
666int pvfs2_acl_chmod(struct inode *inode)
667{
668    struct posix_acl *acl, *clone;
669    int error;
670
671    if (get_acl_flag(inode) == 0)
672    {
673        gossip_debug(GOSSIP_ACL_DEBUG, "pvfs2_acl_chmod: ACL options "
674                "disabled on this FS!\n");
675        return 0;
676    }
677    if (S_ISLNK(inode->i_mode))
678    {
679        gossip_err("pvfs2_acl_chmod: operation not permitted on symlink!\n");
680        error = -EACCES;
681        goto out;
682    }
683    acl = pvfs2_get_acl(inode, ACL_TYPE_ACCESS);
684    if (IS_ERR(acl))
685    {
686        error = PTR_ERR(acl);
687        gossip_err("pvfs2_acl_chmod: get acl (access) failed with %d\n", error);
688        goto out;
689    }
690    if(!acl)
691    {
692        error = 0;
693        goto out;
694    }
695    clone = posix_acl_clone(acl, GFP_KERNEL);
696    posix_acl_release(acl);
697    if (!clone)
698    {
699        gossip_err("pvfs2_acl_chmod failed with ENOMEM\n");
700        error = -ENOMEM;
701        goto out;
702    }
703    error = posix_acl_chmod_masq(clone, inode->i_mode);
704    if (!error)
705    {
706        error = pvfs2_set_acl(inode, ACL_TYPE_ACCESS, clone);
707        gossip_debug(GOSSIP_ACL_DEBUG, "pvfs2_acl_chmod: pvfs2 set acl "
708                "(access) returned %d\n", error);
709    }
710    posix_acl_release(clone);
711out:
712    return error;
713}
714
715static int pvfs2_check_acl(struct inode *inode, int mask
716#ifdef HAVE_THREE_PARAM_ACL_CHECK
717                           , unsigned int flags
718#endif /* HAVE_THREE_PARAM_ACL_CHECK */
719                          )
720{
721    struct posix_acl *acl = NULL;
722
723    gossip_debug(GOSSIP_ACL_DEBUG, "pvfs2_check_acl: called on inode %s\n",
724            PVFS_handle_to_str(get_handle_from_ino(inode)));
725
726    acl = pvfs2_get_acl(inode, ACL_TYPE_ACCESS);
727
728    if (IS_ERR(acl)) {
729        int error = PTR_ERR(acl);
730        gossip_debug(GOSSIP_ACL_DEBUG, "pvfs2_check_acl: pvfs2_get_acl returned error %d\n",
731                error);
732        return error;
733    }
734    if (acl)
735    {
736        int error = posix_acl_permission(inode, acl, mask);
737        posix_acl_release(acl);
738        gossip_debug(GOSSIP_ACL_DEBUG, "pvfs2_check_acl: posix_acl_permission "
739                " (inode %s, acl %p, mask %x) returned %d\n",
740                 PVFS_handle_to_str(get_handle_from_ino(inode)), acl, mask, error);
741        return error;
742    }
743    gossip_debug(GOSSIP_ACL_DEBUG, "pvfs2_check_acl returning EAGAIN\n");
744    return -EAGAIN;
745}
746
747#ifdef HAVE_TWO_PARAM_PERMISSION
748int pvfs2_permission(struct inode *inode, int mask)
749#else
750int pvfs2_permission(struct inode *inode, int mask,
751#ifdef HAVE_THREE_PARAM_PERMISSION_WITH_FLAG
752unsigned int flags)
753#else
754struct nameidata *nd)
755#endif /* HAVE_THREE_PARAM_PERMISSION_WITH_FLAG */
756#endif /* HAVE_TWO_PARAM_PERMISSION */
757{
758#ifdef HAVE_CURRENT_FSUID
759    int fsuid = current_fsuid();
760#else
761    int fsuid = current->fsuid;
762#endif
763
764#ifdef HAVE_GENERIC_PERMISSION
765    int ret;
766
767    ret = generic_permission(inode, mask,
768#ifdef HAVE_FOUR_PARAM_GENERIC_PERMISSION
769                             0,
770#endif /* HAVE_FOUR_PARAM_GENERIC_PERMISSION */
771                             pvfs2_check_acl);
772    if (ret != 0)
773    {
774        gossip_debug(GOSSIP_ACL_DEBUG, "pvfs2_permission failed: inode: %s mask = %o"
775                "mode = %o current->fsuid = %d "
776                "inode->i_uid = %d, inode->i_gid = %d "
777                "in_group_p = %d "
778                "(ret = %d)\n",
779                PVFS_handle_to_str(get_handle_from_ino(inode)), mask, inode->i_mode, fsuid,
780                inode->i_uid, inode->i_gid,
781                in_group_p(inode->i_gid),
782                ret);
783        gossip_debug(GOSSIP_ACL_DEBUG, "pvfs2_permission: mode [%o] & mask [%o] "
784                " & S_IRWXO [%o] = %o == mask [%o]?\n",
785                inode->i_mode, mask, S_IRWXO,
786                (inode->i_mode & mask & S_IRWXO), mask);
787        gossip_debug(GOSSIP_ACL_DEBUG, "pvfs2_permission: did we check ACL's? (mode & S_IRWXG = %d)\n",
788                inode->i_mode & S_IRWXG);
789    }
790    else {
791        gossip_debug(GOSSIP_ACL_DEBUG, "pvfs2_permission succeeded on inode %s\n",
792                PVFS_handle_to_str(get_handle_from_ino(inode)));
793    }
794    return ret;
795#else
796    /* We sort of duplicate the code below from generic_permission. */
797    int mode = inode->i_mode;
798    int error;
799
800    gossip_debug(GOSSIP_ACL_DEBUG, "pvfs2_permission: inode: %s mask = %o"
801            "mode = %o current->fsuid = %d "
802            "inode->i_uid = %d, inode->i_gid = %d"
803            "in_group_p = %d\n",
804            PVFS_handle_to_str(get_handle_from_ino(inode)), mask, mode, fsuid,
805            inode->i_uid, inode->i_gid,
806            in_group_p(inode->i_gid));
807
808    /* No write access on a rdonly FS */
809    if ((mask & MAY_WRITE) && IS_RDONLY(inode) &&
810            (S_ISREG(mode) || S_ISDIR(mode) || S_ISLNK(mode)))
811    {
812        gossip_debug(GOSSIP_ACL_DEBUG, "pvfs2_permission: cannot write to a "
813                "read-only-file-system!\n");
814        return -EROFS;
815    }
816    /* No write access to any immutable files */
817    if ((mask & MAY_WRITE) && IS_IMMUTABLE(inode))
818    {
819        gossip_err("pvfs2_permission: cannot write to an immutable file!\n");
820        return -EACCES;
821    }
822    if (fsuid == inode->i_uid)
823    {
824        mode >>= 6;
825    }
826    else
827    {
828        if (get_acl_flag(inode) == 1)
829        {
830            /*
831             * Access ACL won't work if we don't have group permission bits
832             * set on the file!
833             */
834            if (!(mode & S_IRWXG))
835            {
836                goto check_groups;
837            }
838            error = pvfs2_check_acl(inode, mask);
839            /* ACL disallows access */
840            if (error == -EACCES)
841            {
842                gossip_debug(GOSSIP_ACL_DEBUG, "pvfs2_permission: acl disallowing "
843                        "access to file\n");
844                goto check_capabilities;
845            }
846            /* No ACLs present? */
847            else if (error == -EAGAIN)
848            {
849                goto check_groups;
850            }
851            gossip_debug(GOSSIP_ACL_DEBUG, "pvfs2_permission: returning %d\n",
852                    error);
853            /* Any other error */
854            return error;
855        }
856check_groups:
857        if (in_group_p(inode->i_gid))
858            mode >>= 3;
859    }
860    if ((mode & mask & S_IRWXO) == mask)
861    {
862        return 0;
863    }
864    gossip_debug(GOSSIP_ACL_DEBUG, "pvfs2_permission: mode (%o) & mask (%o) & S_IRWXO (%o) = %o == mask (%o)?\n",
865            mode, mask, S_IRWXO, mode & mask & S_IRWXO, mask);
866check_capabilities:
867    /* Are we allowed to override DAC */
868    if (!(mask & MAY_EXEC) || (inode->i_mode & S_IXUGO) || S_ISDIR(inode->i_mode))
869    {
870        if(capable(CAP_DAC_OVERRIDE))
871        {
872            return 0;
873        }
874    }
875
876    gossip_debug(GOSSIP_ACL_DEBUG, "pvfs2_permission: disallowing access\n");
877    return -EACCES;
878#endif
879}
880
881#endif
882/*
883 * Local variables:
884 *  c-indent-level: 4
885 *  c-basic-offset: 4
886 * End:
887 *
888 * vim: ts=8 sts=4 sw=4 expandtab
889 */
Note: See TracBrowser for help on using the browser.