linux/fs/btrfs/acl.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * Copyright (C) 2007 Red Hat.  All rights reserved.
   4 */
   5
   6#include <linux/fs.h>
   7#include <linux/string.h>
   8#include <linux/xattr.h>
   9#include <linux/posix_acl_xattr.h>
  10#include <linux/posix_acl.h>
  11#include <linux/sched.h>
  12#include <linux/sched/mm.h>
  13#include <linux/slab.h>
  14
  15#include "ctree.h"
  16#include "btrfs_inode.h"
  17#include "xattr.h"
  18
  19struct posix_acl *btrfs_get_acl(struct inode *inode, int type, bool rcu)
  20{
  21        int size;
  22        const char *name;
  23        char *value = NULL;
  24        struct posix_acl *acl;
  25
  26        if (rcu)
  27                return ERR_PTR(-ECHILD);
  28
  29        switch (type) {
  30        case ACL_TYPE_ACCESS:
  31                name = XATTR_NAME_POSIX_ACL_ACCESS;
  32                break;
  33        case ACL_TYPE_DEFAULT:
  34                name = XATTR_NAME_POSIX_ACL_DEFAULT;
  35                break;
  36        default:
  37                return ERR_PTR(-EINVAL);
  38        }
  39
  40        size = btrfs_getxattr(inode, name, NULL, 0);
  41        if (size > 0) {
  42                value = kzalloc(size, GFP_KERNEL);
  43                if (!value)
  44                        return ERR_PTR(-ENOMEM);
  45                size = btrfs_getxattr(inode, name, value, size);
  46        }
  47        if (size > 0)
  48                acl = posix_acl_from_xattr(&init_user_ns, value, size);
  49        else if (size == -ENODATA || size == 0)
  50                acl = NULL;
  51        else
  52                acl = ERR_PTR(size);
  53        kfree(value);
  54
  55        return acl;
  56}
  57
  58static int __btrfs_set_acl(struct btrfs_trans_handle *trans,
  59                           struct user_namespace *mnt_userns,
  60                           struct inode *inode, struct posix_acl *acl, int type)
  61{
  62        int ret, size = 0;
  63        const char *name;
  64        char *value = NULL;
  65
  66        switch (type) {
  67        case ACL_TYPE_ACCESS:
  68                name = XATTR_NAME_POSIX_ACL_ACCESS;
  69                break;
  70        case ACL_TYPE_DEFAULT:
  71                if (!S_ISDIR(inode->i_mode))
  72                        return acl ? -EINVAL : 0;
  73                name = XATTR_NAME_POSIX_ACL_DEFAULT;
  74                break;
  75        default:
  76                return -EINVAL;
  77        }
  78
  79        if (acl) {
  80                unsigned int nofs_flag;
  81
  82                size = posix_acl_xattr_size(acl->a_count);
  83                /*
  84                 * We're holding a transaction handle, so use a NOFS memory
  85                 * allocation context to avoid deadlock if reclaim happens.
  86                 */
  87                nofs_flag = memalloc_nofs_save();
  88                value = kmalloc(size, GFP_KERNEL);
  89                memalloc_nofs_restore(nofs_flag);
  90                if (!value) {
  91                        ret = -ENOMEM;
  92                        goto out;
  93                }
  94
  95                ret = posix_acl_to_xattr(&init_user_ns, acl, value, size);
  96                if (ret < 0)
  97                        goto out;
  98        }
  99
 100        if (trans)
 101                ret = btrfs_setxattr(trans, inode, name, value, size, 0);
 102        else
 103                ret = btrfs_setxattr_trans(inode, name, value, size, 0);
 104
 105out:
 106        kfree(value);
 107
 108        if (!ret)
 109                set_cached_acl(inode, type, acl);
 110
 111        return ret;
 112}
 113
 114int btrfs_set_acl(struct user_namespace *mnt_userns, struct inode *inode,
 115                  struct posix_acl *acl, int type)
 116{
 117        int ret;
 118        umode_t old_mode = inode->i_mode;
 119
 120        if (type == ACL_TYPE_ACCESS && acl) {
 121                ret = posix_acl_update_mode(mnt_userns, inode,
 122                                            &inode->i_mode, &acl);
 123                if (ret)
 124                        return ret;
 125        }
 126        ret = __btrfs_set_acl(NULL, mnt_userns, inode, acl, type);
 127        if (ret)
 128                inode->i_mode = old_mode;
 129        return ret;
 130}
 131
 132int btrfs_init_acl(struct btrfs_trans_handle *trans,
 133                   struct inode *inode, struct inode *dir)
 134{
 135        struct posix_acl *default_acl, *acl;
 136        int ret = 0;
 137
 138        /* this happens with subvols */
 139        if (!dir)
 140                return 0;
 141
 142        ret = posix_acl_create(dir, &inode->i_mode, &default_acl, &acl);
 143        if (ret)
 144                return ret;
 145
 146        if (default_acl) {
 147                ret = __btrfs_set_acl(trans, &init_user_ns, inode, default_acl,
 148                                      ACL_TYPE_DEFAULT);
 149                posix_acl_release(default_acl);
 150        }
 151
 152        if (acl) {
 153                if (!ret)
 154                        ret = __btrfs_set_acl(trans, &init_user_ns, inode, acl,
 155                                              ACL_TYPE_ACCESS);
 156                posix_acl_release(acl);
 157        }
 158
 159        if (!default_acl && !acl)
 160                cache_no_acl(inode);
 161        return ret;
 162}
 163