linux/fs/ext4/acl.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * linux/fs/ext4/acl.c
   4 *
   5 * Copyright (C) 2001-2003 Andreas Gruenbacher, <agruen@suse.de>
   6 */
   7
   8#include <linux/quotaops.h>
   9#include "ext4_jbd2.h"
  10#include "ext4.h"
  11#include "xattr.h"
  12#include "acl.h"
  13
  14/*
  15 * Convert from filesystem to in-memory representation.
  16 */
  17static struct posix_acl *
  18ext4_acl_from_disk(const void *value, size_t size)
  19{
  20        const char *end = (char *)value + size;
  21        int n, count;
  22        struct posix_acl *acl;
  23
  24        if (!value)
  25                return NULL;
  26        if (size < sizeof(ext4_acl_header))
  27                 return ERR_PTR(-EINVAL);
  28        if (((ext4_acl_header *)value)->a_version !=
  29            cpu_to_le32(EXT4_ACL_VERSION))
  30                return ERR_PTR(-EINVAL);
  31        value = (char *)value + sizeof(ext4_acl_header);
  32        count = ext4_acl_count(size);
  33        if (count < 0)
  34                return ERR_PTR(-EINVAL);
  35        if (count == 0)
  36                return NULL;
  37        acl = posix_acl_alloc(count, GFP_NOFS);
  38        if (!acl)
  39                return ERR_PTR(-ENOMEM);
  40        for (n = 0; n < count; n++) {
  41                ext4_acl_entry *entry =
  42                        (ext4_acl_entry *)value;
  43                if ((char *)value + sizeof(ext4_acl_entry_short) > end)
  44                        goto fail;
  45                acl->a_entries[n].e_tag  = le16_to_cpu(entry->e_tag);
  46                acl->a_entries[n].e_perm = le16_to_cpu(entry->e_perm);
  47
  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(ext4_acl_entry_short);
  55                        break;
  56
  57                case ACL_USER:
  58                        value = (char *)value + sizeof(ext4_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(ext4_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 *
  91ext4_acl_to_disk(const struct posix_acl *acl, size_t *size)
  92{
  93        ext4_acl_header *ext_acl;
  94        char *e;
  95        size_t n;
  96
  97        *size = ext4_acl_size(acl->a_count);
  98        ext_acl = kmalloc(sizeof(ext4_acl_header) + acl->a_count *
  99                        sizeof(ext4_acl_entry), GFP_NOFS);
 100        if (!ext_acl)
 101                return ERR_PTR(-ENOMEM);
 102        ext_acl->a_version = cpu_to_le32(EXT4_ACL_VERSION);
 103        e = (char *)ext_acl + sizeof(ext4_acl_header);
 104        for (n = 0; n < acl->a_count; n++) {
 105                const struct posix_acl_entry *acl_e = &acl->a_entries[n];
 106                ext4_acl_entry *entry = (ext4_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(ext4_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(ext4_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(ext4_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 operation get_posix_acl().
 141 *
 142 * inode->i_mutex: don't care
 143 */
 144struct posix_acl *
 145ext4_get_acl(struct inode *inode, int type)
 146{
 147        int name_index;
 148        char *value = NULL;
 149        struct posix_acl *acl;
 150        int retval;
 151
 152        switch (type) {
 153        case ACL_TYPE_ACCESS:
 154                name_index = EXT4_XATTR_INDEX_POSIX_ACL_ACCESS;
 155                break;
 156        case ACL_TYPE_DEFAULT:
 157                name_index = EXT4_XATTR_INDEX_POSIX_ACL_DEFAULT;
 158                break;
 159        default:
 160                BUG();
 161        }
 162        retval = ext4_xattr_get(inode, name_index, "", NULL, 0);
 163        if (retval > 0) {
 164                value = kmalloc(retval, GFP_NOFS);
 165                if (!value)
 166                        return ERR_PTR(-ENOMEM);
 167                retval = ext4_xattr_get(inode, name_index, "", value, retval);
 168        }
 169        if (retval > 0)
 170                acl = ext4_acl_from_disk(value, retval);
 171        else if (retval == -ENODATA || retval == -ENOSYS)
 172                acl = NULL;
 173        else
 174                acl = ERR_PTR(retval);
 175        kfree(value);
 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 ext4_new_inode
 184 */
 185static int
 186__ext4_set_acl(handle_t *handle, struct inode *inode, int type,
 187             struct posix_acl *acl, int xattr_flags)
 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 = EXT4_XATTR_INDEX_POSIX_ACL_ACCESS;
 197                break;
 198
 199        case ACL_TYPE_DEFAULT:
 200                name_index = EXT4_XATTR_INDEX_POSIX_ACL_DEFAULT;
 201                if (!S_ISDIR(inode->i_mode))
 202                        return acl ? -EACCES : 0;
 203                break;
 204
 205        default:
 206                return -EINVAL;
 207        }
 208        if (acl) {
 209                value = ext4_acl_to_disk(acl, &size);
 210                if (IS_ERR(value))
 211                        return (int)PTR_ERR(value);
 212        }
 213
 214        error = ext4_xattr_set_handle(handle, inode, name_index, "",
 215                                      value, size, xattr_flags);
 216
 217        kfree(value);
 218        if (!error)
 219                set_cached_acl(inode, type, acl);
 220
 221        return error;
 222}
 223
 224int
 225ext4_set_acl(struct user_namespace *mnt_userns, struct inode *inode,
 226             struct posix_acl *acl, int type)
 227{
 228        handle_t *handle;
 229        int error, credits, retries = 0;
 230        size_t acl_size = acl ? ext4_acl_size(acl->a_count) : 0;
 231        umode_t mode = inode->i_mode;
 232        int update_mode = 0;
 233
 234        error = dquot_initialize(inode);
 235        if (error)
 236                return error;
 237retry:
 238        error = ext4_xattr_set_credits(inode, acl_size, false /* is_create */,
 239                                       &credits);
 240        if (error)
 241                return error;
 242
 243        handle = ext4_journal_start(inode, EXT4_HT_XATTR, credits);
 244        if (IS_ERR(handle))
 245                return PTR_ERR(handle);
 246        ext4_fc_start_update(inode);
 247
 248        if ((type == ACL_TYPE_ACCESS) && acl) {
 249                error = posix_acl_update_mode(mnt_userns, inode, &mode, &acl);
 250                if (error)
 251                        goto out_stop;
 252                if (mode != inode->i_mode)
 253                        update_mode = 1;
 254        }
 255
 256        error = __ext4_set_acl(handle, inode, type, acl, 0 /* xattr_flags */);
 257        if (!error && update_mode) {
 258                inode->i_mode = mode;
 259                inode->i_ctime = current_time(inode);
 260                error = ext4_mark_inode_dirty(handle, inode);
 261        }
 262out_stop:
 263        ext4_journal_stop(handle);
 264        ext4_fc_stop_update(inode);
 265        if (error == -ENOSPC && ext4_should_retry_alloc(inode->i_sb, &retries))
 266                goto retry;
 267        return error;
 268}
 269
 270/*
 271 * Initialize the ACLs of a new inode. Called from ext4_new_inode.
 272 *
 273 * dir->i_mutex: down
 274 * inode->i_mutex: up (access to inode is still exclusive)
 275 */
 276int
 277ext4_init_acl(handle_t *handle, struct inode *inode, struct inode *dir)
 278{
 279        struct posix_acl *default_acl, *acl;
 280        int error;
 281
 282        error = posix_acl_create(dir, &inode->i_mode, &default_acl, &acl);
 283        if (error)
 284                return error;
 285
 286        if (default_acl) {
 287                error = __ext4_set_acl(handle, inode, ACL_TYPE_DEFAULT,
 288                                       default_acl, XATTR_CREATE);
 289                posix_acl_release(default_acl);
 290        } else {
 291                inode->i_default_acl = NULL;
 292        }
 293        if (acl) {
 294                if (!error)
 295                        error = __ext4_set_acl(handle, inode, ACL_TYPE_ACCESS,
 296                                               acl, XATTR_CREATE);
 297                posix_acl_release(acl);
 298        } else {
 299                inode->i_acl = NULL;
 300        }
 301        return error;
 302}
 303