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