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
  31#ifdef CONFIG_BTRFS_FS_POSIX_ACL
  32
  33static struct posix_acl *btrfs_get_acl(struct inode *inode, int type)
  34{
  35        int size;
  36        const char *name;
  37        char *value = NULL;
  38        struct posix_acl *acl;
  39
  40        if (!IS_POSIXACL(inode))
  41                return NULL;
  42
  43        acl = get_cached_acl(inode, type);
  44        if (acl != ACL_NOT_CACHED)
  45                return acl;
  46
  47        switch (type) {
  48        case ACL_TYPE_ACCESS:
  49                name = POSIX_ACL_XATTR_ACCESS;
  50                break;
  51        case ACL_TYPE_DEFAULT:
  52                name = POSIX_ACL_XATTR_DEFAULT;
  53                break;
  54        default:
  55                BUG();
  56        }
  57
  58        size = __btrfs_getxattr(inode, name, "", 0);
  59        if (size > 0) {
  60                value = kzalloc(size, GFP_NOFS);
  61                if (!value)
  62                        return ERR_PTR(-ENOMEM);
  63                size = __btrfs_getxattr(inode, name, value, size);
  64                if (size > 0) {
  65                        acl = posix_acl_from_xattr(value, size);
  66                        if (IS_ERR(acl)) {
  67                                kfree(value);
  68                                return acl;
  69                        }
  70                        set_cached_acl(inode, type, acl);
  71                }
  72                kfree(value);
  73        } else if (size == -ENOENT || size == -ENODATA || size == 0) {
  74                /* FIXME, who returns -ENOENT?  I think nobody */
  75                acl = NULL;
  76                set_cached_acl(inode, type, acl);
  77        } else {
  78                acl = ERR_PTR(-EIO);
  79        }
  80
  81        return acl;
  82}
  83
  84static int btrfs_xattr_acl_get(struct dentry *dentry, const char *name,
  85                void *value, size_t size, int type)
  86{
  87        struct posix_acl *acl;
  88        int ret = 0;
  89
  90        if (!IS_POSIXACL(dentry->d_inode))
  91                return -EOPNOTSUPP;
  92
  93        acl = btrfs_get_acl(dentry->d_inode, type);
  94
  95        if (IS_ERR(acl))
  96                return PTR_ERR(acl);
  97        if (acl == NULL)
  98                return -ENODATA;
  99        ret = posix_acl_to_xattr(acl, value, size);
 100        posix_acl_release(acl);
 101
 102        return ret;
 103}
 104
 105/*
 106 * Needs to be called with fs_mutex held
 107 */
 108static int btrfs_set_acl(struct btrfs_trans_handle *trans,
 109                         struct inode *inode, struct posix_acl *acl, int type)
 110{
 111        int ret, size = 0;
 112        const char *name;
 113        char *value = NULL;
 114        mode_t mode;
 115
 116        if (acl) {
 117                ret = posix_acl_valid(acl);
 118                if (ret < 0)
 119                        return ret;
 120                ret = 0;
 121        }
 122
 123        switch (type) {
 124        case ACL_TYPE_ACCESS:
 125                mode = inode->i_mode;
 126                name = POSIX_ACL_XATTR_ACCESS;
 127                if (acl) {
 128                        ret = posix_acl_equiv_mode(acl, &mode);
 129                        if (ret < 0)
 130                                return ret;
 131                        inode->i_mode = mode;
 132                }
 133                ret = 0;
 134                break;
 135        case ACL_TYPE_DEFAULT:
 136                if (!S_ISDIR(inode->i_mode))
 137                        return acl ? -EINVAL : 0;
 138                name = POSIX_ACL_XATTR_DEFAULT;
 139                break;
 140        default:
 141                return -EINVAL;
 142        }
 143
 144        if (acl) {
 145                size = posix_acl_xattr_size(acl->a_count);
 146                value = kmalloc(size, GFP_NOFS);
 147                if (!value) {
 148                        ret = -ENOMEM;
 149                        goto out;
 150                }
 151
 152                ret = posix_acl_to_xattr(acl, value, size);
 153                if (ret < 0)
 154                        goto out;
 155        }
 156
 157        ret = __btrfs_setxattr(trans, inode, name, value, size, 0);
 158out:
 159        kfree(value);
 160
 161        if (!ret)
 162                set_cached_acl(inode, type, acl);
 163
 164        return ret;
 165}
 166
 167static int btrfs_xattr_acl_set(struct dentry *dentry, const char *name,
 168                const void *value, size_t size, int flags, int type)
 169{
 170        int ret;
 171        struct posix_acl *acl = NULL;
 172
 173        if (!inode_owner_or_capable(dentry->d_inode))
 174                return -EPERM;
 175
 176        if (!IS_POSIXACL(dentry->d_inode))
 177                return -EOPNOTSUPP;
 178
 179        if (value) {
 180                acl = posix_acl_from_xattr(value, size);
 181                if (IS_ERR(acl))
 182                        return PTR_ERR(acl);
 183
 184                if (acl) {
 185                        ret = posix_acl_valid(acl);
 186                        if (ret)
 187                                goto out;
 188                }
 189        }
 190
 191        ret = btrfs_set_acl(NULL, dentry->d_inode, acl, type);
 192out:
 193        posix_acl_release(acl);
 194
 195        return ret;
 196}
 197
 198int btrfs_check_acl(struct inode *inode, int mask, unsigned int flags)
 199{
 200        int error = -EAGAIN;
 201
 202        if (flags & IPERM_FLAG_RCU) {
 203                if (!negative_cached_acl(inode, ACL_TYPE_ACCESS))
 204                        error = -ECHILD;
 205
 206        } else {
 207                struct posix_acl *acl;
 208                acl = btrfs_get_acl(inode, ACL_TYPE_ACCESS);
 209                if (IS_ERR(acl))
 210                        return PTR_ERR(acl);
 211                if (acl) {
 212                        error = posix_acl_permission(inode, acl, mask);
 213                        posix_acl_release(acl);
 214                }
 215        }
 216
 217        return error;
 218}
 219
 220/*
 221 * btrfs_init_acl is already generally called under fs_mutex, so the locking
 222 * stuff has been fixed to work with that.  If the locking stuff changes, we
 223 * need to re-evaluate the acl locking stuff.
 224 */
 225int btrfs_init_acl(struct btrfs_trans_handle *trans,
 226                   struct inode *inode, struct inode *dir)
 227{
 228        struct posix_acl *acl = NULL;
 229        int ret = 0;
 230
 231        /* this happens with subvols */
 232        if (!dir)
 233                return 0;
 234
 235        if (!S_ISLNK(inode->i_mode)) {
 236                if (IS_POSIXACL(dir)) {
 237                        acl = btrfs_get_acl(dir, ACL_TYPE_DEFAULT);
 238                        if (IS_ERR(acl))
 239                                return PTR_ERR(acl);
 240                }
 241
 242                if (!acl)
 243                        inode->i_mode &= ~current_umask();
 244        }
 245
 246        if (IS_POSIXACL(dir) && acl) {
 247                struct posix_acl *clone;
 248                mode_t mode;
 249
 250                if (S_ISDIR(inode->i_mode)) {
 251                        ret = btrfs_set_acl(trans, inode, acl,
 252                                            ACL_TYPE_DEFAULT);
 253                        if (ret)
 254                                goto failed;
 255                }
 256                clone = posix_acl_clone(acl, GFP_NOFS);
 257                ret = -ENOMEM;
 258                if (!clone)
 259                        goto failed;
 260
 261                mode = inode->i_mode;
 262                ret = posix_acl_create_masq(clone, &mode);
 263                if (ret >= 0) {
 264                        inode->i_mode = mode;
 265                        if (ret > 0) {
 266                                /* we need an acl */
 267                                ret = btrfs_set_acl(trans, inode, clone,
 268                                                    ACL_TYPE_ACCESS);
 269                        }
 270                }
 271                posix_acl_release(clone);
 272        }
 273failed:
 274        posix_acl_release(acl);
 275
 276        return ret;
 277}
 278
 279int btrfs_acl_chmod(struct inode *inode)
 280{
 281        struct posix_acl *acl, *clone;
 282        int ret = 0;
 283
 284        if (S_ISLNK(inode->i_mode))
 285                return -EOPNOTSUPP;
 286
 287        if (!IS_POSIXACL(inode))
 288                return 0;
 289
 290        acl = btrfs_get_acl(inode, ACL_TYPE_ACCESS);
 291        if (IS_ERR(acl) || !acl)
 292                return PTR_ERR(acl);
 293
 294        clone = posix_acl_clone(acl, GFP_KERNEL);
 295        posix_acl_release(acl);
 296        if (!clone)
 297                return -ENOMEM;
 298
 299        ret = posix_acl_chmod_masq(clone, inode->i_mode);
 300        if (!ret)
 301                ret = btrfs_set_acl(NULL, inode, clone, ACL_TYPE_ACCESS);
 302
 303        posix_acl_release(clone);
 304
 305        return ret;
 306}
 307
 308const struct xattr_handler btrfs_xattr_acl_default_handler = {
 309        .prefix = POSIX_ACL_XATTR_DEFAULT,
 310        .flags  = ACL_TYPE_DEFAULT,
 311        .get    = btrfs_xattr_acl_get,
 312        .set    = btrfs_xattr_acl_set,
 313};
 314
 315const struct xattr_handler btrfs_xattr_acl_access_handler = {
 316        .prefix = POSIX_ACL_XATTR_ACCESS,
 317        .flags  = ACL_TYPE_ACCESS,
 318        .get    = btrfs_xattr_acl_get,
 319        .set    = btrfs_xattr_acl_set,
 320};
 321
 322#else /* CONFIG_BTRFS_FS_POSIX_ACL */
 323
 324int btrfs_acl_chmod(struct inode *inode)
 325{
 326        return 0;
 327}
 328
 329int btrfs_init_acl(struct btrfs_trans_handle *trans,
 330                   struct inode *inode, struct inode *dir)
 331{
 332        return 0;
 333}
 334
 335#endif /* CONFIG_BTRFS_FS_POSIX_ACL */
 336