linux/fs/xfs/xfs_acl.c
<<
>>
Prefs
   1/*
   2 * Copyright (c) 2008, Christoph Hellwig
   3 * All Rights Reserved.
   4 *
   5 * This program is free software; you can redistribute it and/or
   6 * modify it under the terms of the GNU General Public License as
   7 * published by the Free Software Foundation.
   8 *
   9 * This program is distributed in the hope that it would be useful,
  10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12 * GNU General Public License for more details.
  13 *
  14 * You should have received a copy of the GNU General Public License
  15 * along with this program; if not, write the Free Software Foundation,
  16 * Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
  17 */
  18#include "xfs.h"
  19#include "xfs_acl.h"
  20#include "xfs_attr.h"
  21#include "xfs_bmap_btree.h"
  22#include "xfs_inode.h"
  23#include "xfs_vnodeops.h"
  24#include "xfs_trace.h"
  25#include <linux/slab.h>
  26#include <linux/xattr.h>
  27#include <linux/posix_acl_xattr.h>
  28
  29
  30/*
  31 * Locking scheme:
  32 *  - all ACL updates are protected by inode->i_mutex, which is taken before
  33 *    calling into this file.
  34 */
  35
  36STATIC struct posix_acl *
  37xfs_acl_from_disk(struct xfs_acl *aclp)
  38{
  39        struct posix_acl_entry *acl_e;
  40        struct posix_acl *acl;
  41        struct xfs_acl_entry *ace;
  42        unsigned int count, i;
  43
  44        count = be32_to_cpu(aclp->acl_cnt);
  45        if (count > XFS_ACL_MAX_ENTRIES)
  46                return ERR_PTR(-EFSCORRUPTED);
  47
  48        acl = posix_acl_alloc(count, GFP_KERNEL);
  49        if (!acl)
  50                return ERR_PTR(-ENOMEM);
  51
  52        for (i = 0; i < count; i++) {
  53                acl_e = &acl->a_entries[i];
  54                ace = &aclp->acl_entry[i];
  55
  56                /*
  57                 * The tag is 32 bits on disk and 16 bits in core.
  58                 *
  59                 * Because every access to it goes through the core
  60                 * format first this is not a problem.
  61                 */
  62                acl_e->e_tag = be32_to_cpu(ace->ae_tag);
  63                acl_e->e_perm = be16_to_cpu(ace->ae_perm);
  64
  65                switch (acl_e->e_tag) {
  66                case ACL_USER:
  67                case ACL_GROUP:
  68                        acl_e->e_id = be32_to_cpu(ace->ae_id);
  69                        break;
  70                case ACL_USER_OBJ:
  71                case ACL_GROUP_OBJ:
  72                case ACL_MASK:
  73                case ACL_OTHER:
  74                        acl_e->e_id = ACL_UNDEFINED_ID;
  75                        break;
  76                default:
  77                        goto fail;
  78                }
  79        }
  80        return acl;
  81
  82fail:
  83        posix_acl_release(acl);
  84        return ERR_PTR(-EINVAL);
  85}
  86
  87STATIC void
  88xfs_acl_to_disk(struct xfs_acl *aclp, const struct posix_acl *acl)
  89{
  90        const struct posix_acl_entry *acl_e;
  91        struct xfs_acl_entry *ace;
  92        int i;
  93
  94        aclp->acl_cnt = cpu_to_be32(acl->a_count);
  95        for (i = 0; i < acl->a_count; i++) {
  96                ace = &aclp->acl_entry[i];
  97                acl_e = &acl->a_entries[i];
  98
  99                ace->ae_tag = cpu_to_be32(acl_e->e_tag);
 100                ace->ae_id = cpu_to_be32(acl_e->e_id);
 101                ace->ae_perm = cpu_to_be16(acl_e->e_perm);
 102        }
 103}
 104
 105struct posix_acl *
 106xfs_get_acl(struct inode *inode, int type)
 107{
 108        struct xfs_inode *ip = XFS_I(inode);
 109        struct posix_acl *acl;
 110        struct xfs_acl *xfs_acl;
 111        int len = sizeof(struct xfs_acl);
 112        unsigned char *ea_name;
 113        int error;
 114
 115        acl = get_cached_acl(inode, type);
 116        if (acl != ACL_NOT_CACHED)
 117                return acl;
 118
 119        trace_xfs_get_acl(ip);
 120
 121        switch (type) {
 122        case ACL_TYPE_ACCESS:
 123                ea_name = SGI_ACL_FILE;
 124                break;
 125        case ACL_TYPE_DEFAULT:
 126                ea_name = SGI_ACL_DEFAULT;
 127                break;
 128        default:
 129                BUG();
 130        }
 131
 132        /*
 133         * If we have a cached ACLs value just return it, not need to
 134         * go out to the disk.
 135         */
 136
 137        xfs_acl = kzalloc(sizeof(struct xfs_acl), GFP_KERNEL);
 138        if (!xfs_acl)
 139                return ERR_PTR(-ENOMEM);
 140
 141        error = -xfs_attr_get(ip, ea_name, (unsigned char *)xfs_acl,
 142                                                        &len, ATTR_ROOT);
 143        if (error) {
 144                /*
 145                 * If the attribute doesn't exist make sure we have a negative
 146                 * cache entry, for any other error assume it is transient and
 147                 * leave the cache entry as ACL_NOT_CACHED.
 148                 */
 149                if (error == -ENOATTR) {
 150                        acl = NULL;
 151                        goto out_update_cache;
 152                }
 153                goto out;
 154        }
 155
 156        acl = xfs_acl_from_disk(xfs_acl);
 157        if (IS_ERR(acl))
 158                goto out;
 159
 160 out_update_cache:
 161        set_cached_acl(inode, type, acl);
 162 out:
 163        kfree(xfs_acl);
 164        return acl;
 165}
 166
 167STATIC int
 168xfs_set_acl(struct inode *inode, int type, struct posix_acl *acl)
 169{
 170        struct xfs_inode *ip = XFS_I(inode);
 171        unsigned char *ea_name;
 172        int error;
 173
 174        if (S_ISLNK(inode->i_mode))
 175                return -EOPNOTSUPP;
 176
 177        switch (type) {
 178        case ACL_TYPE_ACCESS:
 179                ea_name = SGI_ACL_FILE;
 180                break;
 181        case ACL_TYPE_DEFAULT:
 182                if (!S_ISDIR(inode->i_mode))
 183                        return acl ? -EACCES : 0;
 184                ea_name = SGI_ACL_DEFAULT;
 185                break;
 186        default:
 187                return -EINVAL;
 188        }
 189
 190        if (acl) {
 191                struct xfs_acl *xfs_acl;
 192                int len;
 193
 194                xfs_acl = kzalloc(sizeof(struct xfs_acl), GFP_KERNEL);
 195                if (!xfs_acl)
 196                        return -ENOMEM;
 197
 198                xfs_acl_to_disk(xfs_acl, acl);
 199                len = sizeof(struct xfs_acl) -
 200                        (sizeof(struct xfs_acl_entry) *
 201                         (XFS_ACL_MAX_ENTRIES - acl->a_count));
 202
 203                error = -xfs_attr_set(ip, ea_name, (unsigned char *)xfs_acl,
 204                                len, ATTR_ROOT);
 205
 206                kfree(xfs_acl);
 207        } else {
 208                /*
 209                 * A NULL ACL argument means we want to remove the ACL.
 210                 */
 211                error = -xfs_attr_remove(ip, ea_name, ATTR_ROOT);
 212
 213                /*
 214                 * If the attribute didn't exist to start with that's fine.
 215                 */
 216                if (error == -ENOATTR)
 217                        error = 0;
 218        }
 219
 220        if (!error)
 221                set_cached_acl(inode, type, acl);
 222        return error;
 223}
 224
 225static int
 226xfs_set_mode(struct inode *inode, umode_t mode)
 227{
 228        int error = 0;
 229
 230        if (mode != inode->i_mode) {
 231                struct iattr iattr;
 232
 233                iattr.ia_valid = ATTR_MODE | ATTR_CTIME;
 234                iattr.ia_mode = mode;
 235                iattr.ia_ctime = current_fs_time(inode->i_sb);
 236
 237                error = -xfs_setattr_nonsize(XFS_I(inode), &iattr, XFS_ATTR_NOACL);
 238        }
 239
 240        return error;
 241}
 242
 243static int
 244xfs_acl_exists(struct inode *inode, unsigned char *name)
 245{
 246        int len = sizeof(struct xfs_acl);
 247
 248        return (xfs_attr_get(XFS_I(inode), name, NULL, &len,
 249                            ATTR_ROOT|ATTR_KERNOVAL) == 0);
 250}
 251
 252int
 253posix_acl_access_exists(struct inode *inode)
 254{
 255        return xfs_acl_exists(inode, SGI_ACL_FILE);
 256}
 257
 258int
 259posix_acl_default_exists(struct inode *inode)
 260{
 261        if (!S_ISDIR(inode->i_mode))
 262                return 0;
 263        return xfs_acl_exists(inode, SGI_ACL_DEFAULT);
 264}
 265
 266/*
 267 * No need for i_mutex because the inode is not yet exposed to the VFS.
 268 */
 269int
 270xfs_inherit_acl(struct inode *inode, struct posix_acl *acl)
 271{
 272        umode_t mode = inode->i_mode;
 273        int error = 0, inherit = 0;
 274
 275        if (S_ISDIR(inode->i_mode)) {
 276                error = xfs_set_acl(inode, ACL_TYPE_DEFAULT, acl);
 277                if (error)
 278                        goto out;
 279        }
 280
 281        error = posix_acl_create(&acl, GFP_KERNEL, &mode);
 282        if (error < 0)
 283                return error;
 284
 285        /*
 286         * If posix_acl_create returns a positive value we need to
 287         * inherit a permission that can't be represented using the Unix
 288         * mode bits and we actually need to set an ACL.
 289         */
 290        if (error > 0)
 291                inherit = 1;
 292
 293        error = xfs_set_mode(inode, mode);
 294        if (error)
 295                goto out;
 296
 297        if (inherit)
 298                error = xfs_set_acl(inode, ACL_TYPE_ACCESS, acl);
 299
 300out:
 301        posix_acl_release(acl);
 302        return error;
 303}
 304
 305int
 306xfs_acl_chmod(struct inode *inode)
 307{
 308        struct posix_acl *acl;
 309        int error;
 310
 311        if (S_ISLNK(inode->i_mode))
 312                return -EOPNOTSUPP;
 313
 314        acl = xfs_get_acl(inode, ACL_TYPE_ACCESS);
 315        if (IS_ERR(acl) || !acl)
 316                return PTR_ERR(acl);
 317
 318        error = posix_acl_chmod(&acl, GFP_KERNEL, inode->i_mode);
 319        if (error)
 320                return error;
 321
 322        error = xfs_set_acl(inode, ACL_TYPE_ACCESS, acl);
 323        posix_acl_release(acl);
 324        return error;
 325}
 326
 327static int
 328xfs_xattr_acl_get(struct dentry *dentry, const char *name,
 329                void *value, size_t size, int type)
 330{
 331        struct posix_acl *acl;
 332        int error;
 333
 334        acl = xfs_get_acl(dentry->d_inode, type);
 335        if (IS_ERR(acl))
 336                return PTR_ERR(acl);
 337        if (acl == NULL)
 338                return -ENODATA;
 339
 340        error = posix_acl_to_xattr(acl, value, size);
 341        posix_acl_release(acl);
 342
 343        return error;
 344}
 345
 346static int
 347xfs_xattr_acl_set(struct dentry *dentry, const char *name,
 348                const void *value, size_t size, int flags, int type)
 349{
 350        struct inode *inode = dentry->d_inode;
 351        struct posix_acl *acl = NULL;
 352        int error = 0;
 353
 354        if (flags & XATTR_CREATE)
 355                return -EINVAL;
 356        if (type == ACL_TYPE_DEFAULT && !S_ISDIR(inode->i_mode))
 357                return value ? -EACCES : 0;
 358        if ((current_fsuid() != inode->i_uid) && !capable(CAP_FOWNER))
 359                return -EPERM;
 360
 361        if (!value)
 362                goto set_acl;
 363
 364        acl = posix_acl_from_xattr(value, size);
 365        if (!acl) {
 366                /*
 367                 * acl_set_file(3) may request that we set default ACLs with
 368                 * zero length -- defend (gracefully) against that here.
 369                 */
 370                goto out;
 371        }
 372        if (IS_ERR(acl)) {
 373                error = PTR_ERR(acl);
 374                goto out;
 375        }
 376
 377        error = posix_acl_valid(acl);
 378        if (error)
 379                goto out_release;
 380
 381        error = -EINVAL;
 382        if (acl->a_count > XFS_ACL_MAX_ENTRIES)
 383                goto out_release;
 384
 385        if (type == ACL_TYPE_ACCESS) {
 386                umode_t mode = inode->i_mode;
 387                error = posix_acl_equiv_mode(acl, &mode);
 388
 389                if (error <= 0) {
 390                        posix_acl_release(acl);
 391                        acl = NULL;
 392
 393                        if (error < 0)
 394                                return error;
 395                }
 396
 397                error = xfs_set_mode(inode, mode);
 398                if (error)
 399                        goto out_release;
 400        }
 401
 402 set_acl:
 403        error = xfs_set_acl(inode, type, acl);
 404 out_release:
 405        posix_acl_release(acl);
 406 out:
 407        return error;
 408}
 409
 410const struct xattr_handler xfs_xattr_acl_access_handler = {
 411        .prefix = POSIX_ACL_XATTR_ACCESS,
 412        .flags  = ACL_TYPE_ACCESS,
 413        .get    = xfs_xattr_acl_get,
 414        .set    = xfs_xattr_acl_set,
 415};
 416
 417const struct xattr_handler xfs_xattr_acl_default_handler = {
 418        .prefix = POSIX_ACL_XATTR_DEFAULT,
 419        .flags  = ACL_TYPE_DEFAULT,
 420        .get    = xfs_xattr_acl_get,
 421        .set    = xfs_xattr_acl_set,
 422};
 423
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.