linux/fs/reiserfs/xattr_acl.c
<<
>>
Prefs
   1#include <linux/capability.h>
   2#include <linux/fs.h>
   3#include <linux/posix_acl.h>
   4#include "reiserfs.h"
   5#include <linux/errno.h>
   6#include <linux/pagemap.h>
   7#include <linux/xattr.h>
   8#include <linux/slab.h>
   9#include <linux/posix_acl_xattr.h>
  10#include "xattr.h"
  11#include "acl.h"
  12#include <asm/uaccess.h>
  13
  14static int reiserfs_set_acl(struct reiserfs_transaction_handle *th,
  15                            struct inode *inode, int type,
  16                            struct posix_acl *acl);
  17
  18static int
  19posix_acl_set(struct dentry *dentry, const char *name, const void *value,
  20                size_t size, int flags, int type)
  21{
  22        struct inode *inode = dentry->d_inode;
  23        struct posix_acl *acl;
  24        int error, error2;
  25        struct reiserfs_transaction_handle th;
  26        size_t jcreate_blocks;
  27        if (!reiserfs_posixacl(inode->i_sb))
  28                return -EOPNOTSUPP;
  29        if (!inode_owner_or_capable(inode))
  30                return -EPERM;
  31
  32        if (value) {
  33                acl = posix_acl_from_xattr(&init_user_ns, value, size);
  34                if (IS_ERR(acl)) {
  35                        return PTR_ERR(acl);
  36                } else if (acl) {
  37                        error = posix_acl_valid(acl);
  38                        if (error)
  39                                goto release_and_out;
  40                }
  41        } else
  42                acl = NULL;
  43
  44        /* Pessimism: We can't assume that anything from the xattr root up
  45         * has been created. */
  46
  47        jcreate_blocks = reiserfs_xattr_jcreate_nblocks(inode) +
  48                         reiserfs_xattr_nblocks(inode, size) * 2;
  49
  50        reiserfs_write_lock(inode->i_sb);
  51        error = journal_begin(&th, inode->i_sb, jcreate_blocks);
  52        if (error == 0) {
  53                error = reiserfs_set_acl(&th, inode, type, acl);
  54                error2 = journal_end(&th, inode->i_sb, jcreate_blocks);
  55                if (error2)
  56                        error = error2;
  57        }
  58        reiserfs_write_unlock(inode->i_sb);
  59
  60      release_and_out:
  61        posix_acl_release(acl);
  62        return error;
  63}
  64
  65static int
  66posix_acl_get(struct dentry *dentry, const char *name, void *buffer,
  67                size_t size, int type)
  68{
  69        struct posix_acl *acl;
  70        int error;
  71
  72        if (!reiserfs_posixacl(dentry->d_sb))
  73                return -EOPNOTSUPP;
  74
  75        acl = reiserfs_get_acl(dentry->d_inode, type);
  76        if (IS_ERR(acl))
  77                return PTR_ERR(acl);
  78        if (acl == NULL)
  79                return -ENODATA;
  80        error = posix_acl_to_xattr(&init_user_ns, acl, buffer, size);
  81        posix_acl_release(acl);
  82
  83        return error;
  84}
  85
  86/*
  87 * Convert from filesystem to in-memory representation.
  88 */
  89static struct posix_acl *posix_acl_from_disk(const void *value, size_t size)
  90{
  91        const char *end = (char *)value + size;
  92        int n, count;
  93        struct posix_acl *acl;
  94
  95        if (!value)
  96                return NULL;
  97        if (size < sizeof(reiserfs_acl_header))
  98                return ERR_PTR(-EINVAL);
  99        if (((reiserfs_acl_header *) value)->a_version !=
 100            cpu_to_le32(REISERFS_ACL_VERSION))
 101                return ERR_PTR(-EINVAL);
 102        value = (char *)value + sizeof(reiserfs_acl_header);
 103        count = reiserfs_acl_count(size);
 104        if (count < 0)
 105                return ERR_PTR(-EINVAL);
 106        if (count == 0)
 107                return NULL;
 108        acl = posix_acl_alloc(count, GFP_NOFS);
 109        if (!acl)
 110                return ERR_PTR(-ENOMEM);
 111        for (n = 0; n < count; n++) {
 112                reiserfs_acl_entry *entry = (reiserfs_acl_entry *) value;
 113                if ((char *)value + sizeof(reiserfs_acl_entry_short) > end)
 114                        goto fail;
 115                acl->a_entries[n].e_tag = le16_to_cpu(entry->e_tag);
 116                acl->a_entries[n].e_perm = le16_to_cpu(entry->e_perm);
 117                switch (acl->a_entries[n].e_tag) {
 118                case ACL_USER_OBJ:
 119                case ACL_GROUP_OBJ:
 120                case ACL_MASK:
 121                case ACL_OTHER:
 122                        value = (char *)value +
 123                            sizeof(reiserfs_acl_entry_short);
 124                        break;
 125
 126                case ACL_USER:
 127                        value = (char *)value + sizeof(reiserfs_acl_entry);
 128                        if ((char *)value > end)
 129                                goto fail;
 130                        acl->a_entries[n].e_uid = 
 131                                make_kuid(&init_user_ns,
 132                                          le32_to_cpu(entry->e_id));
 133                        break;
 134                case ACL_GROUP:
 135                        value = (char *)value + sizeof(reiserfs_acl_entry);
 136                        if ((char *)value > end)
 137                                goto fail;
 138                        acl->a_entries[n].e_gid =
 139                                make_kgid(&init_user_ns,
 140                                          le32_to_cpu(entry->e_id));
 141                        break;
 142
 143                default:
 144                        goto fail;
 145                }
 146        }
 147        if (value != end)
 148                goto fail;
 149        return acl;
 150
 151      fail:
 152        posix_acl_release(acl);
 153        return ERR_PTR(-EINVAL);
 154}
 155
 156/*
 157 * Convert from in-memory to filesystem representation.
 158 */
 159static void *posix_acl_to_disk(const struct posix_acl *acl, size_t * size)
 160{
 161        reiserfs_acl_header *ext_acl;
 162        char *e;
 163        int n;
 164
 165        *size = reiserfs_acl_size(acl->a_count);
 166        ext_acl = kmalloc(sizeof(reiserfs_acl_header) +
 167                                                  acl->a_count *
 168                                                  sizeof(reiserfs_acl_entry),
 169                                                  GFP_NOFS);
 170        if (!ext_acl)
 171                return ERR_PTR(-ENOMEM);
 172        ext_acl->a_version = cpu_to_le32(REISERFS_ACL_VERSION);
 173        e = (char *)ext_acl + sizeof(reiserfs_acl_header);
 174        for (n = 0; n < acl->a_count; n++) {
 175                const struct posix_acl_entry *acl_e = &acl->a_entries[n];
 176                reiserfs_acl_entry *entry = (reiserfs_acl_entry *) e;
 177                entry->e_tag = cpu_to_le16(acl->a_entries[n].e_tag);
 178                entry->e_perm = cpu_to_le16(acl->a_entries[n].e_perm);
 179                switch (acl->a_entries[n].e_tag) {
 180                case ACL_USER:
 181                        entry->e_id = cpu_to_le32(
 182                                from_kuid(&init_user_ns, acl_e->e_uid));
 183                        e += sizeof(reiserfs_acl_entry);
 184                        break;
 185                case ACL_GROUP:
 186                        entry->e_id = cpu_to_le32(
 187                                from_kgid(&init_user_ns, acl_e->e_gid));
 188                        e += sizeof(reiserfs_acl_entry);
 189                        break;
 190
 191                case ACL_USER_OBJ:
 192                case ACL_GROUP_OBJ:
 193                case ACL_MASK:
 194                case ACL_OTHER:
 195                        e += sizeof(reiserfs_acl_entry_short);
 196                        break;
 197
 198                default:
 199                        goto fail;
 200                }
 201        }
 202        return (char *)ext_acl;
 203
 204      fail:
 205        kfree(ext_acl);
 206        return ERR_PTR(-EINVAL);
 207}
 208
 209/*
 210 * Inode operation get_posix_acl().
 211 *
 212 * inode->i_mutex: down
 213 * BKL held [before 2.5.x]
 214 */
 215struct posix_acl *reiserfs_get_acl(struct inode *inode, int type)
 216{
 217        char *name, *value;
 218        struct posix_acl *acl;
 219        int size;
 220        int retval;
 221
 222        acl = get_cached_acl(inode, type);
 223        if (acl != ACL_NOT_CACHED)
 224                return acl;
 225
 226        switch (type) {
 227        case ACL_TYPE_ACCESS:
 228                name = POSIX_ACL_XATTR_ACCESS;
 229                break;
 230        case ACL_TYPE_DEFAULT:
 231                name = POSIX_ACL_XATTR_DEFAULT;
 232                break;
 233        default:
 234                BUG();
 235        }
 236
 237        size = reiserfs_xattr_get(inode, name, NULL, 0);
 238        if (size < 0) {
 239                if (size == -ENODATA || size == -ENOSYS) {
 240                        set_cached_acl(inode, type, NULL);
 241                        return NULL;
 242                }
 243                return ERR_PTR(size);
 244        }
 245
 246        value = kmalloc(size, GFP_NOFS);
 247        if (!value)
 248                return ERR_PTR(-ENOMEM);
 249
 250        retval = reiserfs_xattr_get(inode, name, value, size);
 251        if (retval == -ENODATA || retval == -ENOSYS) {
 252                /* This shouldn't actually happen as it should have
 253                   been caught above.. but just in case */
 254                acl = NULL;
 255        } else if (retval < 0) {
 256                acl = ERR_PTR(retval);
 257        } else {
 258                acl = posix_acl_from_disk(value, retval);
 259        }
 260        if (!IS_ERR(acl))
 261                set_cached_acl(inode, type, acl);
 262
 263        kfree(value);
 264        return acl;
 265}
 266
 267/*
 268 * Inode operation set_posix_acl().
 269 *
 270 * inode->i_mutex: down
 271 * BKL held [before 2.5.x]
 272 */
 273static int
 274reiserfs_set_acl(struct reiserfs_transaction_handle *th, struct inode *inode,
 275                 int type, struct posix_acl *acl)
 276{
 277        char *name;
 278        void *value = NULL;
 279        size_t size = 0;
 280        int error;
 281
 282        if (S_ISLNK(inode->i_mode))
 283                return -EOPNOTSUPP;
 284
 285        switch (type) {
 286        case ACL_TYPE_ACCESS:
 287                name = POSIX_ACL_XATTR_ACCESS;
 288                if (acl) {
 289                        error = posix_acl_equiv_mode(acl, &inode->i_mode);
 290                        if (error < 0)
 291                                return error;
 292                        else {
 293                                if (error == 0)
 294                                        acl = NULL;
 295                        }
 296                }
 297                break;
 298        case ACL_TYPE_DEFAULT:
 299                name = POSIX_ACL_XATTR_DEFAULT;
 300                if (!S_ISDIR(inode->i_mode))
 301                        return acl ? -EACCES : 0;
 302                break;
 303        default:
 304                return -EINVAL;
 305        }
 306
 307        if (acl) {
 308                value = posix_acl_to_disk(acl, &size);
 309                if (IS_ERR(value))
 310                        return (int)PTR_ERR(value);
 311        }
 312
 313        error = reiserfs_xattr_set_handle(th, inode, name, value, size, 0);
 314
 315        /*
 316         * Ensure that the inode gets dirtied if we're only using
 317         * the mode bits and an old ACL didn't exist. We don't need
 318         * to check if the inode is hashed here since we won't get
 319         * called by reiserfs_inherit_default_acl().
 320         */
 321        if (error == -ENODATA) {
 322                error = 0;
 323                if (type == ACL_TYPE_ACCESS) {
 324                        inode->i_ctime = CURRENT_TIME_SEC;
 325                        mark_inode_dirty(inode);
 326                }
 327        }
 328
 329        kfree(value);
 330
 331        if (!error)
 332                set_cached_acl(inode, type, acl);
 333
 334        return error;
 335}
 336
 337/* dir->i_mutex: locked,
 338 * inode is new and not released into the wild yet */
 339int
 340reiserfs_inherit_default_acl(struct reiserfs_transaction_handle *th,
 341                             struct inode *dir, struct dentry *dentry,
 342                             struct inode *inode)
 343{
 344        struct posix_acl *acl;
 345        int err = 0;
 346
 347        /* ACLs only get applied to files and directories */
 348        if (S_ISLNK(inode->i_mode))
 349                return 0;
 350
 351        /* ACLs can only be used on "new" objects, so if it's an old object
 352         * there is nothing to inherit from */
 353        if (get_inode_sd_version(dir) == STAT_DATA_V1)
 354                goto apply_umask;
 355
 356        /* Don't apply ACLs to objects in the .reiserfs_priv tree.. This
 357         * would be useless since permissions are ignored, and a pain because
 358         * it introduces locking cycles */
 359        if (IS_PRIVATE(dir)) {
 360                inode->i_flags |= S_PRIVATE;
 361                goto apply_umask;
 362        }
 363
 364        acl = reiserfs_get_acl(dir, ACL_TYPE_DEFAULT);
 365        if (IS_ERR(acl))
 366                return PTR_ERR(acl);
 367
 368        if (acl) {
 369                /* Copy the default ACL to the default ACL of a new directory */
 370                if (S_ISDIR(inode->i_mode)) {
 371                        err = reiserfs_set_acl(th, inode, ACL_TYPE_DEFAULT,
 372                                               acl);
 373                        if (err)
 374                                goto cleanup;
 375                }
 376
 377                /* Now we reconcile the new ACL and the mode,
 378                   potentially modifying both */
 379                err = posix_acl_create(&acl, GFP_NOFS, &inode->i_mode);
 380                if (err < 0)
 381                        return err;
 382
 383                /* If we need an ACL.. */
 384                if (err > 0)
 385                        err = reiserfs_set_acl(th, inode, ACL_TYPE_ACCESS, acl);
 386              cleanup:
 387                posix_acl_release(acl);
 388        } else {
 389              apply_umask:
 390                /* no ACL, apply umask */
 391                inode->i_mode &= ~current_umask();
 392        }
 393
 394        return err;
 395}
 396
 397/* This is used to cache the default acl before a new object is created.
 398 * The biggest reason for this is to get an idea of how many blocks will
 399 * actually be required for the create operation if we must inherit an ACL.
 400 * An ACL write can add up to 3 object creations and an additional file write
 401 * so we'd prefer not to reserve that many blocks in the journal if we can.
 402 * It also has the advantage of not loading the ACL with a transaction open,
 403 * this may seem silly, but if the owner of the directory is doing the
 404 * creation, the ACL may not be loaded since the permissions wouldn't require
 405 * it.
 406 * We return the number of blocks required for the transaction.
 407 */
 408int reiserfs_cache_default_acl(struct inode *inode)
 409{
 410        struct posix_acl *acl;
 411        int nblocks = 0;
 412
 413        if (IS_PRIVATE(inode))
 414                return 0;
 415
 416        acl = reiserfs_get_acl(inode, ACL_TYPE_DEFAULT);
 417
 418        if (acl && !IS_ERR(acl)) {
 419                int size = reiserfs_acl_size(acl->a_count);
 420
 421                /* Other xattrs can be created during inode creation. We don't
 422                 * want to claim too many blocks, so we check to see if we
 423                 * we need to create the tree to the xattrs, and then we
 424                 * just want two files. */
 425                nblocks = reiserfs_xattr_jcreate_nblocks(inode);
 426                nblocks += JOURNAL_BLOCKS_PER_OBJECT(inode->i_sb);
 427
 428                REISERFS_I(inode)->i_flags |= i_has_xattr_dir;
 429
 430                /* We need to account for writes + bitmaps for two files */
 431                nblocks += reiserfs_xattr_nblocks(inode, size) * 4;
 432                posix_acl_release(acl);
 433        }
 434
 435        return nblocks;
 436}
 437
 438int reiserfs_acl_chmod(struct inode *inode)
 439{
 440        struct reiserfs_transaction_handle th;
 441        struct posix_acl *acl;
 442        size_t size;
 443        int depth;
 444        int error;
 445
 446        if (S_ISLNK(inode->i_mode))
 447                return -EOPNOTSUPP;
 448
 449        if (get_inode_sd_version(inode) == STAT_DATA_V1 ||
 450            !reiserfs_posixacl(inode->i_sb)) {
 451                return 0;
 452        }
 453
 454        reiserfs_write_unlock(inode->i_sb);
 455        acl = reiserfs_get_acl(inode, ACL_TYPE_ACCESS);
 456        reiserfs_write_lock(inode->i_sb);
 457        if (!acl)
 458                return 0;
 459        if (IS_ERR(acl))
 460                return PTR_ERR(acl);
 461        error = posix_acl_chmod(&acl, GFP_NOFS, inode->i_mode);
 462        if (error)
 463                return error;
 464
 465        size = reiserfs_xattr_nblocks(inode, reiserfs_acl_size(acl->a_count));
 466        depth = reiserfs_write_lock_once(inode->i_sb);
 467        error = journal_begin(&th, inode->i_sb, size * 2);
 468        if (!error) {
 469                int error2;
 470                error = reiserfs_set_acl(&th, inode, ACL_TYPE_ACCESS, acl);
 471                error2 = journal_end(&th, inode->i_sb, size * 2);
 472                if (error2)
 473                        error = error2;
 474        }
 475        reiserfs_write_unlock_once(inode->i_sb, depth);
 476        posix_acl_release(acl);
 477        return error;
 478}
 479
 480static size_t posix_acl_access_list(struct dentry *dentry, char *list,
 481                                    size_t list_size, const char *name,
 482                                    size_t name_len, int type)
 483{
 484        const size_t size = sizeof(POSIX_ACL_XATTR_ACCESS);
 485        if (!reiserfs_posixacl(dentry->d_sb))
 486                return 0;
 487        if (list && size <= list_size)
 488                memcpy(list, POSIX_ACL_XATTR_ACCESS, size);
 489        return size;
 490}
 491
 492const struct xattr_handler reiserfs_posix_acl_access_handler = {
 493        .prefix = POSIX_ACL_XATTR_ACCESS,
 494        .flags = ACL_TYPE_ACCESS,
 495        .get = posix_acl_get,
 496        .set = posix_acl_set,
 497        .list = posix_acl_access_list,
 498};
 499
 500static size_t posix_acl_default_list(struct dentry *dentry, char *list,
 501                                     size_t list_size, const char *name,
 502                                     size_t name_len, int type)
 503{
 504        const size_t size = sizeof(POSIX_ACL_XATTR_DEFAULT);
 505        if (!reiserfs_posixacl(dentry->d_sb))
 506                return 0;
 507        if (list && size <= list_size)
 508                memcpy(list, POSIX_ACL_XATTR_DEFAULT, size);
 509        return size;
 510}
 511
 512const struct xattr_handler reiserfs_posix_acl_default_handler = {
 513        .prefix = POSIX_ACL_XATTR_DEFAULT,
 514        .flags = ACL_TYPE_DEFAULT,
 515        .get = posix_acl_get,
 516        .set = posix_acl_set,
 517        .list = posix_acl_default_list,
 518};
 519
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.