linux/fs/nfs_common/nfsacl.c
<<
>>
Prefs
   1/*
   2 * fs/nfs_common/nfsacl.c
   3 *
   4 *  Copyright (C) 2002-2003 Andreas Gruenbacher <agruen@suse.de>
   5 */
   6
   7/*
   8 * The Solaris nfsacl protocol represents some ACLs slightly differently
   9 * than POSIX 1003.1e draft 17 does (and we do):
  10 *
  11 *  - Minimal ACLs always have an ACL_MASK entry, so they have
  12 *    four instead of three entries.
  13 *  - The ACL_MASK entry in such minimal ACLs always has the same
  14 *    permissions as the ACL_GROUP_OBJ entry. (In extended ACLs
  15 *    the ACL_MASK and ACL_GROUP_OBJ entries may differ.)
  16 *  - The identifier fields of the ACL_USER_OBJ and ACL_GROUP_OBJ
  17 *    entries contain the identifiers of the owner and owning group.
  18 *    (In POSIX ACLs we always set them to ACL_UNDEFINED_ID).
  19 *  - ACL entries in the kernel are kept sorted in ascending order
  20 *    of (e_tag, e_id). Solaris ACLs are unsorted.
  21 */
  22
  23#include <linux/module.h>
  24#include <linux/fs.h>
  25#include <linux/sunrpc/xdr.h>
  26#include <linux/nfsacl.h>
  27#include <linux/nfs3.h>
  28#include <linux/sort.h>
  29
  30MODULE_LICENSE("GPL");
  31
  32EXPORT_SYMBOL(nfsacl_encode);
  33EXPORT_SYMBOL(nfsacl_decode);
  34
  35struct nfsacl_encode_desc {
  36        struct xdr_array2_desc desc;
  37        unsigned int count;
  38        struct posix_acl *acl;
  39        int typeflag;
  40        uid_t uid;
  41        gid_t gid;
  42};
  43
  44static int
  45xdr_nfsace_encode(struct xdr_array2_desc *desc, void *elem)
  46{
  47        struct nfsacl_encode_desc *nfsacl_desc =
  48                (struct nfsacl_encode_desc *) desc;
  49        __be32 *p = elem;
  50
  51        struct posix_acl_entry *entry =
  52                &nfsacl_desc->acl->a_entries[nfsacl_desc->count++];
  53
  54        *p++ = htonl(entry->e_tag | nfsacl_desc->typeflag);
  55        switch(entry->e_tag) {
  56                case ACL_USER_OBJ:
  57                        *p++ = htonl(nfsacl_desc->uid);
  58                        break;
  59                case ACL_GROUP_OBJ:
  60                        *p++ = htonl(nfsacl_desc->gid);
  61                        break;
  62                case ACL_USER:
  63                case ACL_GROUP:
  64                        *p++ = htonl(entry->e_id);
  65                        break;
  66                default:  /* Solaris depends on that! */
  67                        *p++ = 0;
  68                        break;
  69        }
  70        *p++ = htonl(entry->e_perm & S_IRWXO);
  71        return 0;
  72}
  73
  74unsigned int
  75nfsacl_encode(struct xdr_buf *buf, unsigned int base, struct inode *inode,
  76              struct posix_acl *acl, int encode_entries, int typeflag)
  77{
  78        int entries = (acl && acl->a_count) ? max_t(int, acl->a_count, 4) : 0;
  79        struct nfsacl_encode_desc nfsacl_desc = {
  80                .desc = {
  81                        .elem_size = 12,
  82                        .array_len = encode_entries ? entries : 0,
  83                        .xcode = xdr_nfsace_encode,
  84                },
  85                .acl = acl,
  86                .typeflag = typeflag,
  87                .uid = inode->i_uid,
  88                .gid = inode->i_gid,
  89        };
  90        int err;
  91        struct posix_acl *acl2 = NULL;
  92
  93        if (entries > NFS_ACL_MAX_ENTRIES ||
  94            xdr_encode_word(buf, base, entries))
  95                return -EINVAL;
  96        if (encode_entries && acl && acl->a_count == 3) {
  97                /* Fake up an ACL_MASK entry. */
  98                acl2 = posix_acl_alloc(4, GFP_KERNEL);
  99                if (!acl2)
 100                        return -ENOMEM;
 101                /* Insert entries in canonical order: other orders seem
 102                 to confuse Solaris VxFS. */
 103                acl2->a_entries[0] = acl->a_entries[0];  /* ACL_USER_OBJ */
 104                acl2->a_entries[1] = acl->a_entries[1];  /* ACL_GROUP_OBJ */
 105                acl2->a_entries[2] = acl->a_entries[1];  /* ACL_MASK */
 106                acl2->a_entries[2].e_tag = ACL_MASK;
 107                acl2->a_entries[3] = acl->a_entries[2];  /* ACL_OTHER */
 108                nfsacl_desc.acl = acl2;
 109        }
 110        err = xdr_encode_array2(buf, base + 4, &nfsacl_desc.desc);
 111        if (acl2)
 112                posix_acl_release(acl2);
 113        if (!err)
 114                err = 8 + nfsacl_desc.desc.elem_size *
 115                          nfsacl_desc.desc.array_len;
 116        return err;
 117}
 118
 119struct nfsacl_decode_desc {
 120        struct xdr_array2_desc desc;
 121        unsigned int count;
 122        struct posix_acl *acl;
 123};
 124
 125static int
 126xdr_nfsace_decode(struct xdr_array2_desc *desc, void *elem)
 127{
 128        struct nfsacl_decode_desc *nfsacl_desc =
 129                (struct nfsacl_decode_desc *) desc;
 130        __be32 *p = elem;
 131        struct posix_acl_entry *entry;
 132
 133        if (!nfsacl_desc->acl) {
 134                if (desc->array_len > NFS_ACL_MAX_ENTRIES)
 135                        return -EINVAL;
 136                nfsacl_desc->acl = posix_acl_alloc(desc->array_len, GFP_KERNEL);
 137                if (!nfsacl_desc->acl)
 138                        return -ENOMEM;
 139                nfsacl_desc->count = 0;
 140        }
 141
 142        entry = &nfsacl_desc->acl->a_entries[nfsacl_desc->count++];
 143        entry->e_tag = ntohl(*p++) & ~NFS_ACL_DEFAULT;
 144        entry->e_id = ntohl(*p++);
 145        entry->e_perm = ntohl(*p++);
 146
 147        switch(entry->e_tag) {
 148                case ACL_USER_OBJ:
 149                case ACL_USER:
 150                case ACL_GROUP_OBJ:
 151                case ACL_GROUP:
 152                case ACL_OTHER:
 153                        if (entry->e_perm & ~S_IRWXO)
 154                                return -EINVAL;
 155                        break;
 156                case ACL_MASK:
 157                        /* Solaris sometimes sets additonal bits in the mask */
 158                        entry->e_perm &= S_IRWXO;
 159                        break;
 160                default:
 161                        return -EINVAL;
 162        }
 163
 164        return 0;
 165}
 166
 167static int
 168cmp_acl_entry(const void *x, const void *y)
 169{
 170        const struct posix_acl_entry *a = x, *b = y;
 171
 172        if (a->e_tag != b->e_tag)
 173                return a->e_tag - b->e_tag;
 174        else if (a->e_id > b->e_id)
 175                return 1;
 176        else if (a->e_id < b->e_id)
 177                return -1;
 178        else
 179                return 0;
 180}
 181
 182/*
 183 * Convert from a Solaris ACL to a POSIX 1003.1e draft 17 ACL.
 184 */
 185static int
 186posix_acl_from_nfsacl(struct posix_acl *acl)
 187{
 188        struct posix_acl_entry *pa, *pe,
 189               *group_obj = NULL, *mask = NULL;
 190
 191        if (!acl)
 192                return 0;
 193
 194        sort(acl->a_entries, acl->a_count, sizeof(struct posix_acl_entry),
 195             cmp_acl_entry, NULL);
 196
 197        /* Clear undefined identifier fields and find the ACL_GROUP_OBJ
 198           and ACL_MASK entries. */
 199        FOREACH_ACL_ENTRY(pa, acl, pe) {
 200                switch(pa->e_tag) {
 201                        case ACL_USER_OBJ:
 202                                pa->e_id = ACL_UNDEFINED_ID;
 203                                break;
 204                        case ACL_GROUP_OBJ:
 205                                pa->e_id = ACL_UNDEFINED_ID;
 206                                group_obj = pa;
 207                                break;
 208                        case ACL_MASK:
 209                                mask = pa;
 210                                /* fall through */
 211                        case ACL_OTHER:
 212                                pa->e_id = ACL_UNDEFINED_ID;
 213                                break;
 214                }
 215        }
 216        if (acl->a_count == 4 && group_obj && mask &&
 217            mask->e_perm == group_obj->e_perm) {
 218                /* remove bogus ACL_MASK entry */
 219                memmove(mask, mask+1, (3 - (mask - acl->a_entries)) *
 220                                      sizeof(struct posix_acl_entry));
 221                acl->a_count = 3;
 222        }
 223        return 0;
 224}
 225
 226unsigned int
 227nfsacl_decode(struct xdr_buf *buf, unsigned int base, unsigned int *aclcnt,
 228              struct posix_acl **pacl)
 229{
 230        struct nfsacl_decode_desc nfsacl_desc = {
 231                .desc = {
 232                        .elem_size = 12,
 233                        .xcode = pacl ? xdr_nfsace_decode : NULL,
 234                },
 235        };
 236        u32 entries;
 237        int err;
 238
 239        if (xdr_decode_word(buf, base, &entries) ||
 240            entries > NFS_ACL_MAX_ENTRIES)
 241                return -EINVAL;
 242        nfsacl_desc.desc.array_maxlen = entries;
 243        err = xdr_decode_array2(buf, base + 4, &nfsacl_desc.desc);
 244        if (err)
 245                return err;
 246        if (pacl) {
 247                if (entries != nfsacl_desc.desc.array_len ||
 248                    posix_acl_from_nfsacl(nfsacl_desc.acl) != 0) {
 249                        posix_acl_release(nfsacl_desc.acl);
 250                        return -EINVAL;
 251                }
 252                *pacl = nfsacl_desc.acl;
 253        }
 254        if (aclcnt)
 255                *aclcnt = entries;
 256        return 8 + nfsacl_desc.desc.elem_size *
 257                   nfsacl_desc.desc.array_len;
 258}
 259