linux/fs/ext2/acl.c
<<
>>
Prefs
   1/*
   2 * linux/fs/ext2/acl.c
   3 *
   4 * Copyright (C) 2001-2003 Andreas Gruenbacher, <agruen@suse.de>
   5 */
   6
   7#include <linux/init.h>
   8#include <linux/sched.h>
   9#include <linux/slab.h>
  10#include <linux/fs.h>
  11#include "ext2.h"
  12#include "xattr.h"
  13#include "acl.h"
  14
  15/*
  16 * Convert from filesystem to in-memory representation.
  17 */
  18static struct posix_acl *
  19ext2_acl_from_disk(const void *value, size_t size)
  20{
  21        const char *end = (char *)value + size;
  22        int n, count;
  23        struct posix_acl *acl;
  24
  25        if (!value)
  26                return NULL;
  27        if (size < sizeof(ext2_acl_header))
  28                 return ERR_PTR(-EINVAL);
  29        if (((ext2_acl_header *)value)->a_version !=
  30            cpu_to_le32(EXT2_ACL_VERSION))
  31                return ERR_PTR(-EINVAL);
  32        value = (char *)value + sizeof(ext2_acl_header);
  33        count = ext2_acl_count(size);
  34        if (count < 0)
  35                return ERR_PTR(-EINVAL);
  36        if (count == 0)
  37                return NULL;
  38        acl = posix_acl_alloc(count, GFP_KERNEL);
  39        if (!acl)
  40                return ERR_PTR(-ENOMEM);
  41        for (n=0; n < count; n++) {
  42                ext2_acl_entry *entry =
  43                        (ext2_acl_entry *)value;
  44                if ((char *)value + sizeof(ext2_acl_entry_short) > end)
  45                        goto fail;
  46                acl->a_entries[n].e_tag  = le16_to_cpu(entry->e_tag);
  47                acl->a_entries[n].e_perm = le16_to_cpu(entry->e_perm);
  48                switch(acl->a_entries[n].e_tag) {
  49                        case ACL_USER_OBJ:
  50                        case ACL_GROUP_OBJ:
  51                        case ACL_MASK:
  52                        case ACL_OTHER:
  53                                value = (char *)value +
  54                                        sizeof(ext2_acl_entry_short);
  55                                break;
  56
  57                        case ACL_USER:
  58                                value = (char *)value + sizeof(ext2_acl_entry);
  59                                if ((char *)value > end)
  60                                        goto fail;
  61                                acl->a_entries[n].e_uid =
  62                                        make_kuid(&init_user_ns,
  63                                                  le32_to_cpu(entry->e_id));
  64                                break;
  65                        case ACL_GROUP:
  66                                value = (char *)value + sizeof(ext2_acl_entry);
  67                                if ((char *)value > end)
  68                                        goto fail;
  69                                acl->a_entries[n].e_gid =
  70                                        make_kgid(&init_user_ns,
  71                                                  le32_to_cpu(entry->e_id));
  72                                break;
  73
  74                        default:
  75                                goto fail;
  76                }
  77        }
  78        if (value != end)
  79                goto fail;
  80        return acl;
  81
  82fail:
  83        posix_acl_release(acl);
  84        return ERR_PTR(-EINVAL);
  85}
  86
  87/*
  88 * Convert from in-memory to filesystem representation.
  89 */
  90static void *
  91ext2_acl_to_disk(const struct posix_acl *acl, size_t *size)
  92{
  93        ext2_acl_header *ext_acl;
  94        char *e;
  95        size_t n;
  96
  97        *size = ext2_acl_size(acl->a_count);
  98        ext_acl = kmalloc(sizeof(ext2_acl_header) + acl->a_count *
  99                        sizeof(ext2_acl_entry), GFP_KERNEL);
 100        if (!ext_acl)
 101                return ERR_PTR(-ENOMEM);
 102        ext_acl->a_version = cpu_to_le32(EXT2_ACL_VERSION);
 103        e = (char *)ext_acl + sizeof(ext2_acl_header);
 104        for (n=0; n < acl->a_count; n++) {
 105                const struct posix_acl_entry *acl_e = &acl->a_entries[n];
 106                ext2_acl_entry *entry = (ext2_acl_entry *)e;
 107                entry->e_tag  = cpu_to_le16(acl_e->e_tag);
 108                entry->e_perm = cpu_to_le16(acl_e->e_perm);
 109                switch(acl_e->e_tag) {
 110                        case ACL_USER:
 111                                entry->e_id = cpu_to_le32(
 112                                        from_kuid(&init_user_ns, acl_e->e_uid));
 113                                e += sizeof(ext2_acl_entry);
 114                                break;
 115                        case ACL_GROUP:
 116                                entry->e_id = cpu_to_le32(
 117                                        from_kgid(&init_user_ns, acl_e->e_gid));
 118                                e += sizeof(ext2_acl_entry);
 119                                break;
 120
 121                        case ACL_USER_OBJ:
 122                        case ACL_GROUP_OBJ:
 123                        case ACL_MASK:
 124                        case ACL_OTHER:
 125                                e += sizeof(ext2_acl_entry_short);
 126                                break;
 127
 128                        default:
 129                                goto fail;
 130                }
 131        }
 132        return (char *)ext_acl;
 133
 134fail:
 135        kfree(ext_acl);
 136        return ERR_PTR(-EINVAL);
 137}
 138
 139/*
 140 * inode->i_mutex: don't care
 141 */
 142struct posix_acl *
 143ext2_get_acl(struct inode *inode, int type)
 144{
 145        int name_index;
 146        char *value = NULL;
 147        struct posix_acl *acl;
 148        int retval;
 149
 150        switch (type) {
 151        case ACL_TYPE_ACCESS:
 152                name_index = EXT2_XATTR_INDEX_POSIX_ACL_ACCESS;
 153                break;
 154        case ACL_TYPE_DEFAULT:
 155                name_index = EXT2_XATTR_INDEX_POSIX_ACL_DEFAULT;
 156                break;
 157        default:
 158                BUG();
 159        }
 160        retval = ext2_xattr_get(inode, name_index, "", NULL, 0);
 161        if (retval > 0) {
 162                value = kmalloc(retval, GFP_KERNEL);
 163                if (!value)
 164                        return ERR_PTR(-ENOMEM);
 165                retval = ext2_xattr_get(inode, name_index, "", value, retval);
 166        }
 167        if (retval > 0)
 168                acl = ext2_acl_from_disk(value, retval);
 169        else if (retval == -ENODATA || retval == -ENOSYS)
 170                acl = NULL;
 171        else
 172                acl = ERR_PTR(retval);
 173        kfree(value);
 174
 175        if (!IS_ERR(acl))
 176                set_cached_acl(inode, type, acl);
 177
 178        return acl;
 179}
 180
 181/*
 182 * inode->i_mutex: down
 183 */
 184int
 185ext2_set_acl(struct inode *inode, struct posix_acl *acl, int type)
 186{
 187        int name_index;
 188        void *value = NULL;
 189        size_t size = 0;
 190        int error;
 191
 192        switch(type) {
 193                case ACL_TYPE_ACCESS:
 194                        name_index = EXT2_XATTR_INDEX_POSIX_ACL_ACCESS;
 195                        if (acl) {
 196                                error = posix_acl_equiv_mode(acl, &inode->i_mode);
 197                                if (error < 0)
 198                                        return error;
 199                                else {
 200                                        inode->i_ctime = CURRENT_TIME_SEC;
 201                                        mark_inode_dirty(inode);
 202                                        if (error == 0)
 203                                                acl = NULL;
 204                                }
 205                        }
 206                        break;
 207
 208                case ACL_TYPE_DEFAULT:
 209                        name_index = EXT2_XATTR_INDEX_POSIX_ACL_DEFAULT;
 210                        if (!S_ISDIR(inode->i_mode))
 211                                return acl ? -EACCES : 0;
 212                        break;
 213
 214                default:
 215                        return -EINVAL;
 216        }
 217        if (acl) {
 218                value = ext2_acl_to_disk(acl, &size);
 219                if (IS_ERR(value))
 220                        return (int)PTR_ERR(value);
 221        }
 222
 223        error = ext2_xattr_set(inode, name_index, "", value, size, 0);
 224
 225        kfree(value);
 226        if (!error)
 227                set_cached_acl(inode, type, acl);
 228        return error;
 229}
 230
 231/*
 232 * Initialize the ACLs of a new inode. Called from ext2_new_inode.
 233 *
 234 * dir->i_mutex: down
 235 * inode->i_mutex: up (access to inode is still exclusive)
 236 */
 237int
 238ext2_init_acl(struct inode *inode, struct inode *dir)
 239{
 240        struct posix_acl *default_acl, *acl;
 241        int error;
 242
 243        error = posix_acl_create(dir, &inode->i_mode, &default_acl, &acl);
 244        if (error)
 245                return error;
 246
 247        if (default_acl) {
 248                error = ext2_set_acl(inode, default_acl, ACL_TYPE_DEFAULT);
 249                posix_acl_release(default_acl);
 250        }
 251        if (acl) {
 252                if (!error)
 253                        error = ext2_set_acl(inode, acl, ACL_TYPE_ACCESS);
 254                posix_acl_release(acl);
 255        }
 256        return error;
 257}
 258