root/branches/Orange-Branch/src/kernel/linux-2.6/acl.c @ 8686

Revision 8686, 26.3 KB (checked in by mtmoore, 2 years ago)

xattr_handler member functions changed to the first argument being a struct dentry * instead of struct inode *. Each function also added an additional handler_flags function. maint/config/kernel.m4 updated with appropriate #defines. This version of kernel.m4 also include changes to detect a change in the ctl_table struct with other patches to follow

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