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 (!is_owner_or_cap(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 (acl == NULL) {
 182                        value = NULL;
 183                        size = 0;
 184                } else if (IS_ERR(acl)) {
 185                        return PTR_ERR(acl);
 186                }
 187        }
 188
 189        ret = btrfs_set_acl(NULL, dentry->d_inode, acl, type);
 190
 191        posix_acl_release(acl);
 192
 193        return ret;
 194}
 195
 196int btrfs_check_acl(struct inode *inode, int mask, unsigned int flags)
 197{
 198        int error = -EAGAIN;
 199
 200        if (flags & IPERM_FLAG_RCU) {
 201                if (!negative_cached_acl(inode, ACL_TYPE_ACCESS))
 202                        error = -ECHILD;
 203
 204        } else {
 205                struct posix_acl *acl;
 206                acl = btrfs_get_acl(inode, ACL_TYPE_ACCESS);
 207                if (IS_ERR(acl))
 208                        return PTR_ERR(acl);
 209                if (acl) {
 210                        error = posix_acl_permission(inode, acl, mask);
 211                        posix_acl_release(acl);
 212                }
 213        }
 214
 215        return error;
 216}
 217
 218/*
 219 * btrfs_init_acl is already generally called under fs_mutex, so the locking
 220 * stuff has been fixed to work with that.  If the locking stuff changes, we
 221 * need to re-evaluate the acl locking stuff.
 222 */
 223int btrfs_init_acl(struct btrfs_trans_handle *trans,
 224                   struct inode *inode, struct inode *dir)
 225{
 226        struct posix_acl *acl = NULL;
 227        int ret = 0;
 228
 229        /* this happens with subvols */
 230        if (!dir)
 231                return 0;
 232
 233        if (!S_ISLNK(inode->i_mode)) {
 234                if (IS_POSIXACL(dir)) {
 235                        acl = btrfs_get_acl(dir, ACL_TYPE_DEFAULT);
 236                        if (IS_ERR(acl))
 237                                return PTR_ERR(acl);
 238                }
 239
 240                if (!acl)
 241                        inode->i_mode &= ~current_umask();
 242        }
 243
 244        if (IS_POSIXACL(dir) && acl) {
 245                struct posix_acl *clone;
 246                mode_t mode;
 247
 248                if (S_ISDIR(inode->i_mode)) {
 249                        ret = btrfs_set_acl(trans, inode, acl,
 250                                            ACL_TYPE_DEFAULT);
 251                        if (ret)
 252                                goto failed;
 253                }
 254                clone = posix_acl_clone(acl, GFP_NOFS);
 255                ret = -ENOMEM;
 256                if (!clone)
 257                        goto failed;
 258
 259                mode = inode->i_mode;
 260                ret = posix_acl_create_masq(clone, &mode);
 261                if (ret >= 0) {
 262                        inode->i_mode = mode;
 263                        if (ret > 0) {
 264                                /* we need an acl */
 265                                ret = btrfs_set_acl(trans, inode, clone,
 266                                                    ACL_TYPE_ACCESS);
 267                        }
 268                }
 269                posix_acl_release(clone);
 270        }
 271failed:
 272        posix_acl_release(acl);
 273
 274        return ret;
 275}
 276
 277int btrfs_acl_chmod(struct inode *inode)
 278{
 279        struct posix_acl *acl, *clone;
 280        int ret = 0;
 281
 282        if (S_ISLNK(inode->i_mode))
 283                return -EOPNOTSUPP;
 284
 285        if (!IS_POSIXACL(inode))
 286                return 0;
 287
 288        acl = btrfs_get_acl(inode, ACL_TYPE_ACCESS);
 289        if (IS_ERR(acl) || !acl)
 290                return PTR_ERR(acl);
 291
 292        clone = posix_acl_clone(acl, GFP_KERNEL);
 293        posix_acl_release(acl);
 294        if (!clone)
 295                return -ENOMEM;
 296
 297        ret = posix_acl_chmod_masq(clone, inode->i_mode);
 298        if (!ret)
 299                ret = btrfs_set_acl(NULL, inode, clone, ACL_TYPE_ACCESS);
 300
 301        posix_acl_release(clone);
 302
 303        return ret;
 304}
 305
 306const struct xattr_handler btrfs_xattr_acl_default_handler = {
 307        .prefix = POSIX_ACL_XATTR_DEFAULT,
 308        .flags  = ACL_TYPE_DEFAULT,
 309        .get    = btrfs_xattr_acl_get,
 310        .set    = btrfs_xattr_acl_set,
 311};
 312
 313const struct xattr_handler btrfs_xattr_acl_access_handler = {
 314        .prefix = POSIX_ACL_XATTR_ACCESS,
 315        .flags  = ACL_TYPE_ACCESS,
 316        .get    = btrfs_xattr_acl_get,
 317        .set    = btrfs_xattr_acl_set,
 318};
 319
 320#else /* CONFIG_BTRFS_FS_POSIX_ACL */
 321
 322int btrfs_acl_chmod(struct inode *inode)
 323{
 324        return 0;
 325}
 326
 327int btrfs_init_acl(struct btrfs_trans_handle *trans,
 328                   struct inode *inode, struct inode *dir)
 329{
 330        return 0;
 331}
 332
 333#endif /* CONFIG_BTRFS_FS_POSIX_ACL */
 334