linux/fs/btrfs/acl.c
<<
>>
Prefs
   1/*
   2 * Copyright (C) 2007 Red Hat.  All rights reserved.
   3 *
   4 * This program is free software; you can redistribute it and/or
   5 * modify it under the terms of the GNU General Public
   6 * License v2 as published by the Free Software Foundation.
   7 *
   8 * This program is distributed in the hope that it will be useful,
   9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  11 * General Public License for more details.
  12 *
  13 * You should have received a copy of the GNU General Public
  14 * License along with this program; if not, write to the
  15 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
  16 * Boston, MA 021110-1307, USA.
  17 */
  18
  19#include <linux/fs.h>
  20#include <linux/string.h>
  21#include <linux/xattr.h>
  22#include <linux/posix_acl_xattr.h>
  23#include <linux/posix_acl.h>
  24#include <linux/sched.h>
  25#include <linux/slab.h>
  26
  27#include "ctree.h"
  28#include "btrfs_inode.h"
  29#include "xattr.h"
  30
  31struct posix_acl *btrfs_get_acl(struct inode *inode, int type)
  32{
  33        int size;
  34        const char *name;
  35        char *value = NULL;
  36        struct posix_acl *acl;
  37
  38        if (!IS_POSIXACL(inode))
  39                return NULL;
  40
  41        acl = get_cached_acl(inode, type);
  42        if (acl != ACL_NOT_CACHED)
  43                return acl;
  44
  45        switch (type) {
  46        case ACL_TYPE_ACCESS:
  47                name = POSIX_ACL_XATTR_ACCESS;
  48                break;
  49        case ACL_TYPE_DEFAULT:
  50                name = POSIX_ACL_XATTR_DEFAULT;
  51                break;
  52        default:
  53                BUG();
  54        }
  55
  56        size = __btrfs_getxattr(inode, name, "", 0);
  57        if (size > 0) {
  58                value = kzalloc(size, GFP_NOFS);
  59                if (!value)
  60                        return ERR_PTR(-ENOMEM);
  61                size = __btrfs_getxattr(inode, name, value, size);
  62        }
  63        if (size > 0) {
  64                acl = posix_acl_from_xattr(&init_user_ns, value, size);
  65        } else if (size == -ENOENT || size == -ENODATA || size == 0) {
  66                /* FIXME, who returns -ENOENT?  I think nobody */
  67                acl = NULL;
  68        } else {
  69                acl = ERR_PTR(-EIO);
  70        }
  71        kfree(value);
  72
  73        if (!IS_ERR(acl))
  74                set_cached_acl(inode, type, acl);
  75
  76        return acl;
  77}
  78
  79static int btrfs_xattr_acl_get(struct dentry *dentry, const char *name,
  80                void *value, size_t size, int type)
  81{
  82        struct posix_acl *acl;
  83        int ret = 0;
  84
  85        if (!IS_POSIXACL(dentry->d_inode))
  86                return -EOPNOTSUPP;
  87
  88        acl = btrfs_get_acl(dentry->d_inode, type);
  89
  90        if (IS_ERR(acl))
  91                return PTR_ERR(acl);
  92        if (acl == NULL)
  93                return -ENODATA;
  94        ret = posix_acl_to_xattr(&init_user_ns, acl, value, size);
  95        posix_acl_release(acl);
  96
  97        return ret;
  98}
  99
 100/*
 101 * Needs to be called with fs_mutex held
 102 */
 103static int btrfs_set_acl(struct btrfs_trans_handle *trans,
 104                         struct inode *inode, struct posix_acl *acl, int type)
 105{
 106        int ret, size = 0;
 107        const char *name;
 108        char *value = NULL;
 109
 110        if (acl) {
 111                ret = posix_acl_valid(acl);
 112                if (ret < 0)
 113                        return ret;
 114                ret = 0;
 115        }
 116
 117        switch (type) {
 118        case ACL_TYPE_ACCESS:
 119                name = POSIX_ACL_XATTR_ACCESS;
 120                if (acl) {
 121                        ret = posix_acl_equiv_mode(acl, &inode->i_mode);
 122                        if (ret < 0)
 123                                return ret;
 124                        if (ret == 0)
 125                                acl = NULL;
 126                }
 127                ret = 0;
 128                break;
 129        case ACL_TYPE_DEFAULT:
 130                if (!S_ISDIR(inode->i_mode))
 131                        return acl ? -EINVAL : 0;
 132                name = POSIX_ACL_XATTR_DEFAULT;
 133                break;
 134        default:
 135                return -EINVAL;
 136        }
 137
 138        if (acl) {
 139                size = posix_acl_xattr_size(acl->a_count);
 140                value = kmalloc(size, GFP_NOFS);
 141                if (!value) {
 142                        ret = -ENOMEM;
 143                        goto out;
 144                }
 145
 146                ret = posix_acl_to_xattr(&init_user_ns, acl, value, size);
 147                if (ret < 0)
 148                        goto out;
 149        }
 150
 151        ret = __btrfs_setxattr(trans, inode, name, value, size, 0);
 152out:
 153        kfree(value);
 154
 155        if (!ret)
 156                set_cached_acl(inode, type, acl);
 157
 158        return ret;
 159}
 160
 161static int btrfs_xattr_acl_set(struct dentry *dentry, const char *name,
 162                const void *value, size_t size, int flags, int type)
 163{
 164        int ret;
 165        struct posix_acl *acl = NULL;
 166
 167        if (!inode_owner_or_capable(dentry->d_inode))
 168                return -EPERM;
 169
 170        if (!IS_POSIXACL(dentry->d_inode))
 171                return -EOPNOTSUPP;
 172
 173        if (value) {
 174                acl = posix_acl_from_xattr(&init_user_ns, value, size);
 175                if (IS_ERR(acl))
 176                        return PTR_ERR(acl);
 177
 178                if (acl) {
 179                        ret = posix_acl_valid(acl);
 180                        if (ret)
 181                                goto out;
 182                }
 183        }
 184
 185        ret = btrfs_set_acl(NULL, dentry->d_inode, acl, type);
 186out:
 187        posix_acl_release(acl);
 188
 189        return ret;
 190}
 191
 192/*
 193 * btrfs_init_acl is already generally called under fs_mutex, so the locking
 194 * stuff has been fixed to work with that.  If the locking stuff changes, we
 195 * need to re-evaluate the acl locking stuff.
 196 */
 197int btrfs_init_acl(struct btrfs_trans_handle *trans,
 198                   struct inode *inode, struct inode *dir)
 199{
 200        struct posix_acl *acl = NULL;
 201        int ret = 0;
 202
 203        /* this happens with subvols */
 204        if (!dir)
 205                return 0;
 206
 207        if (!S_ISLNK(inode->i_mode)) {
 208                if (IS_POSIXACL(dir)) {
 209                        acl = btrfs_get_acl(dir, ACL_TYPE_DEFAULT);
 210                        if (IS_ERR(acl))
 211                                return PTR_ERR(acl);
 212                }
 213
 214                if (!acl)
 215                        inode->i_mode &= ~current_umask();
 216        }
 217
 218        if (IS_POSIXACL(dir) && acl) {
 219                if (S_ISDIR(inode->i_mode)) {
 220                        ret = btrfs_set_acl(trans, inode, acl,
 221                                            ACL_TYPE_DEFAULT);
 222                        if (ret)
 223                                goto failed;
 224                }
 225                ret = posix_acl_create(&acl, GFP_NOFS, &inode->i_mode);
 226                if (ret < 0)
 227                        return ret;
 228
 229                if (ret > 0) {
 230                        /* we need an acl */
 231                        ret = btrfs_set_acl(trans, inode, acl, ACL_TYPE_ACCESS);
 232                } else {
 233                        cache_no_acl(inode);
 234                }
 235        } else {
 236                cache_no_acl(inode);
 237        }
 238failed:
 239        posix_acl_release(acl);
 240
 241        return ret;
 242}
 243
 244int btrfs_acl_chmod(struct inode *inode)
 245{
 246        struct posix_acl *acl;
 247        int ret = 0;
 248
 249        if (S_ISLNK(inode->i_mode))
 250                return -EOPNOTSUPP;
 251
 252        if (!IS_POSIXACL(inode))
 253                return 0;
 254
 255        acl = btrfs_get_acl(inode, ACL_TYPE_ACCESS);
 256        if (IS_ERR_OR_NULL(acl))
 257                return PTR_ERR(acl);
 258
 259        ret = posix_acl_chmod(&acl, GFP_KERNEL, inode->i_mode);
 260        if (ret)
 261                return ret;
 262        ret = btrfs_set_acl(NULL, inode, acl, ACL_TYPE_ACCESS);
 263        posix_acl_release(acl);
 264        return ret;
 265}
 266
 267const struct xattr_handler btrfs_xattr_acl_default_handler = {
 268        .prefix = POSIX_ACL_XATTR_DEFAULT,
 269        .flags  = ACL_TYPE_DEFAULT,
 270        .get    = btrfs_xattr_acl_get,
 271        .set    = btrfs_xattr_acl_set,
 272};
 273
 274const struct xattr_handler btrfs_xattr_acl_access_handler = {
 275        .prefix = POSIX_ACL_XATTR_ACCESS,
 276        .flags  = ACL_TYPE_ACCESS,
 277        .get    = btrfs_xattr_acl_get,
 278        .set    = btrfs_xattr_acl_set,
 279};
 280