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/gfp.h>
  26#include <linux/sunrpc/xdr.h>
  27#include <linux/nfsacl.h>
  28#include <linux/nfs3.h>
  29#include <linux/sort.h>
  30
  31MODULE_LICENSE("GPL");
  32
  33EXPORT_SYMBOL_GPL(nfsacl_encode);
  34EXPORT_SYMBOL_GPL(nfsacl_decode);
  35
  36struct nfsacl_encode_desc {
  37        struct xdr_array2_desc desc;
  38        unsigned int count;
  39        struct posix_acl *acl;
  40        int typeflag;
  41        uid_t uid;
  42        gid_t gid;
  43};
  44
  45struct nfsacl_simple_acl {
  46        struct posix_acl acl;
  47        struct posix_acl_entry ace[4];
  48};
  49
  50static int
  51xdr_nfsace_encode(struct xdr_array2_desc *desc, void *elem)
  52{
  53        struct nfsacl_encode_desc *nfsacl_desc =
  54                (struct nfsacl_encode_desc *) desc;
  55        __be32 *p = elem;
  56
  57        struct posix_acl_entry *entry =
  58                &nfsacl_desc->acl->a_entries[nfsacl_desc->count++];
  59
  60        *p++ = htonl(entry->e_tag | nfsacl_desc->typeflag);
  61        switch(entry->e_tag) {
  62                case ACL_USER_OBJ:
  63                        *p++ = htonl(nfsacl_desc->uid);
  64                        break;
  65                case ACL_GROUP_OBJ:
  66                        *p++ = htonl(nfsacl_desc->gid);
  67                        break;
  68                case ACL_USER:
  69                case ACL_GROUP:
  70                        *p++ = htonl(entry->e_id);
  71                        break;
  72                default:  /* Solaris depends on that! */
  73                        *p++ = 0;
  74                        break;
  75        }
  76        *p++ = htonl(entry->e_perm & S_IRWXO);
  77        return 0;
  78}
  79
  80/**
  81 * nfsacl_encode - Encode an NFSv3 ACL
  82 *
  83 * @buf: destination xdr_buf to contain XDR encoded ACL
  84 * @base: byte offset in xdr_buf where XDR'd ACL begins
  85 * @inode: inode of file whose ACL this is
  86 * @acl: posix_acl to encode
  87 * @encode_entries: whether to encode ACEs as well
  88 * @typeflag: ACL type: NFS_ACL_DEFAULT or zero
  89 *
  90 * Returns size of encoded ACL in bytes or a negative errno value.
  91 */
  92int nfsacl_encode(struct xdr_buf *buf, unsigned int base, struct inode *inode,
  93                  struct posix_acl *acl, int encode_entries, int typeflag)
  94{
  95        int entries = (acl && acl->a_count) ? max_t(int, acl->a_count, 4) : 0;
  96        struct nfsacl_encode_desc nfsacl_desc = {
  97                .desc = {
  98                        .elem_size = 12,
  99                        .array_len = encode_entries ? entries : 0,
 100                        .xcode = xdr_nfsace_encode,
 101                },
 102                .acl = acl,
 103                .typeflag = typeflag,
 104                .uid = inode->i_uid,
 105                .gid = inode->i_gid,
 106        };
 107        struct nfsacl_simple_acl aclbuf;
 108        int err;
 109
 110        if (entries > NFS_ACL_MAX_ENTRIES ||
 111            xdr_encode_word(buf, base, entries))
 112                return -EINVAL;
 113        if (encode_entries && acl && acl->a_count == 3) {
 114                struct posix_acl *acl2 = &aclbuf.acl;
 115
 116                /* Avoid the use of posix_acl_alloc().  nfsacl_encode() is
 117                 * invoked in contexts where a memory allocation failure is
 118                 * fatal.  Fortunately this fake ACL is small enough to
 119                 * construct on the stack. */
 120                posix_acl_init(acl2, 4);
 121
 122                /* Insert entries in canonical order: other orders seem
 123                 to confuse Solaris VxFS. */
 124                acl2->a_entries[0] = acl->a_entries[0];  /* ACL_USER_OBJ */
 125                acl2->a_entries[1] = acl->a_entries[1];  /* ACL_GROUP_OBJ */
 126                acl2->a_entries[2] = acl->a_entries[1];  /* ACL_MASK */
 127                acl2->a_entries[2].e_tag = ACL_MASK;
 128                acl2->a_entries[3] = acl->a_entries[2];  /* ACL_OTHER */
 129                nfsacl_desc.acl = acl2;
 130        }
 131        err = xdr_encode_array2(buf, base + 4, &nfsacl_desc.desc);
 132        if (!err)
 133                err = 8 + nfsacl_desc.desc.elem_size *
 134                          nfsacl_desc.desc.array_len;
 135        return err;
 136}
 137
 138struct nfsacl_decode_desc {
 139        struct xdr_array2_desc desc;
 140        unsigned int count;
 141        struct posix_acl *acl;
 142};
 143
 144static int
 145xdr_nfsace_decode(struct xdr_array2_desc *desc, void *elem)
 146{
 147        struct nfsacl_decode_desc *nfsacl_desc =
 148                (struct nfsacl_decode_desc *) desc;
 149        __be32 *p = elem;
 150        struct posix_acl_entry *entry;
 151
 152        if (!nfsacl_desc->acl) {
 153                if (desc->array_len > NFS_ACL_MAX_ENTRIES)
 154                        return -EINVAL;
 155                nfsacl_desc->acl = posix_acl_alloc(desc->array_len, GFP_KERNEL);
 156                if (!nfsacl_desc->acl)
 157                        return -ENOMEM;
 158                nfsacl_desc->count = 0;
 159        }
 160
 161        entry = &nfsacl_desc->acl->a_entries[nfsacl_desc->count++];
 162        entry->e_tag = ntohl(*p++) & ~NFS_ACL_DEFAULT;
 163        entry->e_id = ntohl(*p++);
 164        entry->e_perm = ntohl(*p++);
 165
 166        switch(entry->e_tag) {
 167                case ACL_USER_OBJ:
 168                case ACL_USER:
 169                case ACL_GROUP_OBJ:
 170                case ACL_GROUP:
 171                case ACL_OTHER:
 172                        if (entry->e_perm & ~S_IRWXO)
 173                                return -EINVAL;
 174                        break;
 175                case ACL_MASK:
 176                        /* Solaris sometimes sets additional bits in the mask */
 177                        entry->e_perm &= S_IRWXO;
 178                        break;
 179                default:
 180                        return -EINVAL;
 181        }
 182
 183        return 0;
 184}
 185
 186static int
 187cmp_acl_entry(const void *x, const void *y)
 188{
 189        const struct posix_acl_entry *a = x, *b = y;
 190
 191        if (a->e_tag != b->e_tag)
 192                return a->e_tag - b->e_tag;
 193        else if (a->e_id > b->e_id)
 194                return 1;
 195        else if (a->e_id < b->e_id)
 196                return -1;
 197        else
 198                return 0;
 199}
 200
 201/*
 202 * Convert from a Solaris ACL to a POSIX 1003.1e draft 17 ACL.
 203 */
 204static int
 205posix_acl_from_nfsacl(struct posix_acl *acl)
 206{
 207        struct posix_acl_entry *pa, *pe,
 208               *group_obj = NULL, *mask = NULL;
 209
 210        if (!acl)
 211                return 0;
 212
 213        sort(acl->a_entries, acl->a_count, sizeof(struct posix_acl_entry),
 214             cmp_acl_entry, NULL);
 215
 216        /* Clear undefined identifier fields and find the ACL_GROUP_OBJ
 217           and ACL_MASK entries. */
 218        FOREACH_ACL_ENTRY(pa, acl, pe) {
 219                switch(pa->e_tag) {
 220                        case ACL_USER_OBJ:
 221                                pa->e_id = ACL_UNDEFINED_ID;
 222                                break;
 223                        case ACL_GROUP_OBJ:
 224                                pa->e_id = ACL_UNDEFINED_ID;
 225                                group_obj = pa;
 226                                break;
 227                        case ACL_MASK:
 228                                mask = pa;
 229                                /* fall through */
 230                        case ACL_OTHER:
 231                                pa->e_id = ACL_UNDEFINED_ID;
 232                                break;
 233                }
 234        }
 235        if (acl->a_count == 4 && group_obj && mask &&
 236            mask->e_perm == group_obj->e_perm) {
 237                /* remove bogus ACL_MASK entry */
 238                memmove(mask, mask+1, (3 - (mask - acl->a_entries)) *
 239                                      sizeof(struct posix_acl_entry));
 240                acl->a_count = 3;
 241        }
 242        return 0;
 243}
 244
 245/**
 246 * nfsacl_decode - Decode an NFSv3 ACL
 247 *
 248 * @buf: xdr_buf containing XDR'd ACL data to decode
 249 * @base: byte offset in xdr_buf where XDR'd ACL begins
 250 * @aclcnt: count of ACEs in decoded posix_acl
 251 * @pacl: buffer in which to place decoded posix_acl
 252 *
 253 * Returns the length of the decoded ACL in bytes, or a negative errno value.
 254 */
 255int nfsacl_decode(struct xdr_buf *buf, unsigned int base, unsigned int *aclcnt,
 256                  struct posix_acl **pacl)
 257{
 258        struct nfsacl_decode_desc nfsacl_desc = {
 259                .desc = {
 260                        .elem_size = 12,
 261                        .xcode = pacl ? xdr_nfsace_decode : NULL,
 262                },
 263        };
 264        u32 entries;
 265        int err;
 266
 267        if (xdr_decode_word(buf, base, &entries) ||
 268            entries > NFS_ACL_MAX_ENTRIES)
 269                return -EINVAL;
 270        nfsacl_desc.desc.array_maxlen = entries;
 271        err = xdr_decode_array2(buf, base + 4, &nfsacl_desc.desc);
 272        if (err)
 273                return err;
 274        if (pacl) {
 275                if (entries != nfsacl_desc.desc.array_len ||
 276                    posix_acl_from_nfsacl(nfsacl_desc.acl) != 0) {
 277                        posix_acl_release(nfsacl_desc.acl);
 278                        return -EINVAL;
 279                }
 280                *pacl = nfsacl_desc.acl;
 281        }
 282        if (aclcnt)
 283                *aclcnt = entries;
 284        return 8 + nfsacl_desc.desc.elem_size *
 285                   nfsacl_desc.desc.array_len;
 286}
 287