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