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                                acl->a_entries[n].e_id = ACL_UNDEFINED_ID;
  52                                break;
  53
  54                        case ACL_USER:
  55                        case ACL_GROUP:
  56                                value = (char *)value + sizeof(ext3_acl_entry);
  57                                if ((char *)value > end)
  58                                        goto fail;
  59                                acl->a_entries[n].e_id =
  60                                        le32_to_cpu(entry->e_id);
  61                                break;
  62
  63                        default:
  64                                goto fail;
  65                }
  66        }
  67        if (value != end)
  68                goto fail;
  69        return acl;
  70
  71fail:
  72        posix_acl_release(acl);
  73        return ERR_PTR(-EINVAL);
  74}
  75
  76/*
  77 * Convert from in-memory to filesystem representation.
  78 */
  79static void *
  80ext3_acl_to_disk(const struct posix_acl *acl, size_t *size)
  81{
  82        ext3_acl_header *ext_acl;
  83        char *e;
  84        size_t n;
  85
  86        *size = ext3_acl_size(acl->a_count);
  87        ext_acl = kmalloc(sizeof(ext3_acl_header) + acl->a_count *
  88                        sizeof(ext3_acl_entry), GFP_NOFS);
  89        if (!ext_acl)
  90                return ERR_PTR(-ENOMEM);
  91        ext_acl->a_version = cpu_to_le32(EXT3_ACL_VERSION);
  92        e = (char *)ext_acl + sizeof(ext3_acl_header);
  93        for (n=0; n < acl->a_count; n++) {
  94                ext3_acl_entry *entry = (ext3_acl_entry *)e;
  95                entry->e_tag  = cpu_to_le16(acl->a_entries[n].e_tag);
  96                entry->e_perm = cpu_to_le16(acl->a_entries[n].e_perm);
  97                switch(acl->a_entries[n].e_tag) {
  98                        case ACL_USER:
  99                        case ACL_GROUP:
 100                                entry->e_id =
 101                                        cpu_to_le32(acl->a_entries[n].e_id);
 102                                e += sizeof(ext3_acl_entry);
 103                                break;
 104
 105                        case ACL_USER_OBJ:
 106                        case ACL_GROUP_OBJ:
 107                        case ACL_MASK:
 108                        case ACL_OTHER:
 109                                e += sizeof(ext3_acl_entry_short);
 110                                break;
 111
 112                        default:
 113                                goto fail;
 114                }
 115        }
 116        return (char *)ext_acl;
 117
 118fail:
 119        kfree(ext_acl);
 120        return ERR_PTR(-EINVAL);
 121}
 122
 123/*
 124 * Inode operation get_posix_acl().
 125 *
 126 * inode->i_mutex: don't care
 127 */
 128struct posix_acl *
 129ext3_get_acl(struct inode *inode, int type)
 130{
 131        int name_index;
 132        char *value = NULL;
 133        struct posix_acl *acl;
 134        int retval;
 135
 136        if (!test_opt(inode->i_sb, POSIX_ACL))
 137                return NULL;
 138
 139        acl = get_cached_acl(inode, type);
 140        if (acl != ACL_NOT_CACHED)
 141                return acl;
 142
 143        switch (type) {
 144        case ACL_TYPE_ACCESS:
 145                name_index = EXT3_XATTR_INDEX_POSIX_ACL_ACCESS;
 146                break;
 147        case ACL_TYPE_DEFAULT:
 148                name_index = EXT3_XATTR_INDEX_POSIX_ACL_DEFAULT;
 149                break;
 150        default:
 151                BUG();
 152        }
 153
 154        retval = ext3_xattr_get(inode, name_index, "", NULL, 0);
 155        if (retval > 0) {
 156                value = kmalloc(retval, GFP_NOFS);
 157                if (!value)
 158                        return ERR_PTR(-ENOMEM);
 159                retval = ext3_xattr_get(inode, name_index, "", value, retval);
 160        }
 161        if (retval > 0)
 162                acl = ext3_acl_from_disk(value, retval);
 163        else if (retval == -ENODATA || retval == -ENOSYS)
 164                acl = NULL;
 165        else
 166                acl = ERR_PTR(retval);
 167        kfree(value);
 168
 169        if (!IS_ERR(acl))
 170                set_cached_acl(inode, type, acl);
 171
 172        return acl;
 173}
 174
 175/*
 176 * Set the access or default ACL of an inode.
 177 *
 178 * inode->i_mutex: down unless called from ext3_new_inode
 179 */
 180static int
 181ext3_set_acl(handle_t *handle, struct inode *inode, int type,
 182             struct posix_acl *acl)
 183{
 184        int name_index;
 185        void *value = NULL;
 186        size_t size = 0;
 187        int error;
 188
 189        if (S_ISLNK(inode->i_mode))
 190                return -EOPNOTSUPP;
 191
 192        switch(type) {
 193                case ACL_TYPE_ACCESS:
 194                        name_index = EXT3_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                                        ext3_mark_inode_dirty(handle, inode);
 202                                        if (error == 0)
 203                                                acl = NULL;
 204                                }
 205                        }
 206                        break;
 207
 208                case ACL_TYPE_DEFAULT:
 209                        name_index = EXT3_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 = ext3_acl_to_disk(acl, &size);
 219                if (IS_ERR(value))
 220                        return (int)PTR_ERR(value);
 221        }
 222
 223        error = ext3_xattr_set_handle(handle, inode, name_index, "",
 224                                      value, size, 0);
 225
 226        kfree(value);
 227
 228        if (!error)
 229                set_cached_acl(inode, type, acl);
 230
 231        return error;
 232}
 233
 234/*
 235 * Initialize the ACLs of a new inode. Called from ext3_new_inode.
 236 *
 237 * dir->i_mutex: down
 238 * inode->i_mutex: up (access to inode is still exclusive)
 239 */
 240int
 241ext3_init_acl(handle_t *handle, struct inode *inode, struct inode *dir)
 242{
 243        struct posix_acl *acl = NULL;
 244        int error = 0;
 245
 246        if (!S_ISLNK(inode->i_mode)) {
 247                if (test_opt(dir->i_sb, POSIX_ACL)) {
 248                        acl = ext3_get_acl(dir, ACL_TYPE_DEFAULT);
 249                        if (IS_ERR(acl))
 250                                return PTR_ERR(acl);
 251                }
 252                if (!acl)
 253                        inode->i_mode &= ~current_umask();
 254        }
 255        if (test_opt(inode->i_sb, POSIX_ACL) && acl) {
 256                if (S_ISDIR(inode->i_mode)) {
 257                        error = ext3_set_acl(handle, inode,
 258                                             ACL_TYPE_DEFAULT, acl);
 259                        if (error)
 260                                goto cleanup;
 261                }
 262                error = posix_acl_create(&acl, GFP_NOFS, &inode->i_mode);
 263                if (error < 0)
 264                        return error;
 265
 266                if (error > 0) {
 267                        /* This is an extended ACL */
 268                        error = ext3_set_acl(handle, inode, ACL_TYPE_ACCESS, acl);
 269                }
 270        }
 271cleanup:
 272        posix_acl_release(acl);
 273        return error;
 274}
 275
 276/*
 277 * Does chmod for an inode that may have an Access Control List. The
 278 * inode->i_mode field must be updated to the desired value by the caller
 279 * before calling this function.
 280 * Returns 0 on success, or a negative error number.
 281 *
 282 * We change the ACL rather than storing some ACL entries in the file
 283 * mode permission bits (which would be more efficient), because that
 284 * would break once additional permissions (like  ACL_APPEND, ACL_DELETE
 285 * for directories) are added. There are no more bits available in the
 286 * file mode.
 287 *
 288 * inode->i_mutex: down
 289 */
 290int
 291ext3_acl_chmod(struct inode *inode)
 292{
 293        struct posix_acl *acl;
 294        handle_t *handle;
 295        int retries = 0;
 296        int error;
 297
 298        if (S_ISLNK(inode->i_mode))
 299                return -EOPNOTSUPP;
 300        if (!test_opt(inode->i_sb, POSIX_ACL))
 301                return 0;
 302        acl = ext3_get_acl(inode, ACL_TYPE_ACCESS);
 303        if (IS_ERR(acl) || !acl)
 304                return PTR_ERR(acl);
 305        error = posix_acl_chmod(&acl, GFP_KERNEL, inode->i_mode);
 306        if (error)
 307                return error;
 308retry:
 309        handle = ext3_journal_start(inode,
 310                        EXT3_DATA_TRANS_BLOCKS(inode->i_sb));
 311        if (IS_ERR(handle)) {
 312                error = PTR_ERR(handle);
 313                ext3_std_error(inode->i_sb, error);
 314                goto out;
 315        }
 316        error = ext3_set_acl(handle, inode, ACL_TYPE_ACCESS, acl);
 317        ext3_journal_stop(handle);
 318        if (error == -ENOSPC &&
 319            ext3_should_retry_alloc(inode->i_sb, &retries))
 320                goto retry;
 321out:
 322        posix_acl_release(acl);
 323        return error;
 324}
 325
 326/*
 327 * Extended attribute handlers
 328 */
 329static size_t
 330ext3_xattr_list_acl_access(struct dentry *dentry, char *list, size_t list_len,
 331                           const char *name, size_t name_len, int type)
 332{
 333        const size_t size = sizeof(POSIX_ACL_XATTR_ACCESS);
 334
 335        if (!test_opt(dentry->d_sb, POSIX_ACL))
 336                return 0;
 337        if (list && size <= list_len)
 338                memcpy(list, POSIX_ACL_XATTR_ACCESS, size);
 339        return size;
 340}
 341
 342static size_t
 343ext3_xattr_list_acl_default(struct dentry *dentry, char *list, size_t list_len,
 344                            const char *name, size_t name_len, int type)
 345{
 346        const size_t size = sizeof(POSIX_ACL_XATTR_DEFAULT);
 347
 348        if (!test_opt(dentry->d_sb, POSIX_ACL))
 349                return 0;
 350        if (list && size <= list_len)
 351                memcpy(list, POSIX_ACL_XATTR_DEFAULT, size);
 352        return size;
 353}
 354
 355static int
 356ext3_xattr_get_acl(struct dentry *dentry, const char *name, void *buffer,
 357                   size_t size, int type)
 358{
 359        struct posix_acl *acl;
 360        int error;
 361
 362        if (strcmp(name, "") != 0)
 363                return -EINVAL;
 364        if (!test_opt(dentry->d_sb, POSIX_ACL))
 365                return -EOPNOTSUPP;
 366
 367        acl = ext3_get_acl(dentry->d_inode, type);
 368        if (IS_ERR(acl))
 369                return PTR_ERR(acl);
 370        if (acl == NULL)
 371                return -ENODATA;
 372        error = posix_acl_to_xattr(acl, buffer, size);
 373        posix_acl_release(acl);
 374
 375        return error;
 376}
 377
 378static int
 379ext3_xattr_set_acl(struct dentry *dentry, const char *name, const void *value,
 380                   size_t size, int flags, int type)
 381{
 382        struct inode *inode = dentry->d_inode;
 383        handle_t *handle;
 384        struct posix_acl *acl;
 385        int error, retries = 0;
 386
 387        if (strcmp(name, "") != 0)
 388                return -EINVAL;
 389        if (!test_opt(inode->i_sb, POSIX_ACL))
 390                return -EOPNOTSUPP;
 391        if (!inode_owner_or_capable(inode))
 392                return -EPERM;
 393
 394        if (value) {
 395                acl = posix_acl_from_xattr(value, size);
 396                if (IS_ERR(acl))
 397                        return PTR_ERR(acl);
 398                else if (acl) {
 399                        error = posix_acl_valid(acl);
 400                        if (error)
 401                                goto release_and_out;
 402                }
 403        } else
 404                acl = NULL;
 405
 406retry:
 407        handle = ext3_journal_start(inode, EXT3_DATA_TRANS_BLOCKS(inode->i_sb));
 408        if (IS_ERR(handle))
 409                return PTR_ERR(handle);
 410        error = ext3_set_acl(handle, inode, type, acl);
 411        ext3_journal_stop(handle);
 412        if (error == -ENOSPC && ext3_should_retry_alloc(inode->i_sb, &retries))
 413                goto retry;
 414
 415release_and_out:
 416        posix_acl_release(acl);
 417        return error;
 418}
 419
 420const struct xattr_handler ext3_xattr_acl_access_handler = {
 421        .prefix = POSIX_ACL_XATTR_ACCESS,
 422        .flags  = ACL_TYPE_ACCESS,
 423        .list   = ext3_xattr_list_acl_access,
 424        .get    = ext3_xattr_get_acl,
 425        .set    = ext3_xattr_set_acl,
 426};
 427
 428const struct xattr_handler ext3_xattr_acl_default_handler = {
 429        .prefix = POSIX_ACL_XATTR_DEFAULT,
 430        .flags  = ACL_TYPE_DEFAULT,
 431        .list   = ext3_xattr_list_acl_default,
 432        .get    = ext3_xattr_get_acl,
 433        .set    = ext3_xattr_set_acl,
 434};
 435
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.