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