linux/fs/generic_acl.c
<<
>>
Prefs
   1/*
   2 * (C) 2005 Andreas Gruenbacher <agruen@suse.de>
   3 *
   4 * This file is released under the GPL.
   5 *
   6 * Generic ACL support for in-memory filesystems.
   7 */
   8
   9#include <linux/sched.h>
  10#include <linux/gfp.h>
  11#include <linux/fs.h>
  12#include <linux/generic_acl.h>
  13#include <linux/posix_acl.h>
  14#include <linux/posix_acl_xattr.h>
  15
  16
  17static size_t
  18generic_acl_list(struct dentry *dentry, char *list, size_t list_size,
  19                const char *name, size_t name_len, int type)
  20{
  21        struct posix_acl *acl;
  22        const char *xname;
  23        size_t size;
  24
  25        acl = get_cached_acl(dentry->d_inode, type);
  26        if (!acl)
  27                return 0;
  28        posix_acl_release(acl);
  29
  30        switch (type) {
  31        case ACL_TYPE_ACCESS:
  32                xname = POSIX_ACL_XATTR_ACCESS;
  33                break;
  34        case ACL_TYPE_DEFAULT:
  35                xname = POSIX_ACL_XATTR_DEFAULT;
  36                break;
  37        default:
  38                return 0;
  39        }
  40        size = strlen(xname) + 1;
  41        if (list && size <= list_size)
  42                memcpy(list, xname, size);
  43        return size;
  44}
  45
  46static int
  47generic_acl_get(struct dentry *dentry, const char *name, void *buffer,
  48                     size_t size, int type)
  49{
  50        struct posix_acl *acl;
  51        int error;
  52
  53        if (strcmp(name, "") != 0)
  54                return -EINVAL;
  55
  56        acl = get_cached_acl(dentry->d_inode, type);
  57        if (!acl)
  58                return -ENODATA;
  59        error = posix_acl_to_xattr(acl, buffer, size);
  60        posix_acl_release(acl);
  61
  62        return error;
  63}
  64
  65static int
  66generic_acl_set(struct dentry *dentry, const char *name, const void *value,
  67                     size_t size, int flags, int type)
  68{
  69        struct inode *inode = dentry->d_inode;
  70        struct posix_acl *acl = NULL;
  71        int error;
  72
  73        if (strcmp(name, "") != 0)
  74                return -EINVAL;
  75        if (S_ISLNK(inode->i_mode))
  76                return -EOPNOTSUPP;
  77        if (!is_owner_or_cap(inode))
  78                return -EPERM;
  79        if (value) {
  80                acl = posix_acl_from_xattr(value, size);
  81                if (IS_ERR(acl))
  82                        return PTR_ERR(acl);
  83        }
  84        if (acl) {
  85                mode_t mode;
  86
  87                error = posix_acl_valid(acl);
  88                if (error)
  89                        goto failed;
  90                switch (type) {
  91                case ACL_TYPE_ACCESS:
  92                        mode = inode->i_mode;
  93                        error = posix_acl_equiv_mode(acl, &mode);
  94                        if (error < 0)
  95                                goto failed;
  96                        inode->i_mode = mode;
  97                        inode->i_ctime = CURRENT_TIME;
  98                        if (error == 0) {
  99                                posix_acl_release(acl);
 100                                acl = NULL;
 101                        }
 102                        break;
 103                case ACL_TYPE_DEFAULT:
 104                        if (!S_ISDIR(inode->i_mode)) {
 105                                error = -EINVAL;
 106                                goto failed;
 107                        }
 108                        break;
 109                }
 110        }
 111        set_cached_acl(inode, type, acl);
 112        error = 0;
 113failed:
 114        posix_acl_release(acl);
 115        return error;
 116}
 117
 118/**
 119 * generic_acl_init  -  Take care of acl inheritance at @inode create time
 120 *
 121 * Files created inside a directory with a default ACL inherit the
 122 * directory's default ACL.
 123 */
 124int
 125generic_acl_init(struct inode *inode, struct inode *dir)
 126{
 127        struct posix_acl *acl = NULL;
 128        mode_t mode = inode->i_mode;
 129        int error;
 130
 131        inode->i_mode = mode & ~current_umask();
 132        if (!S_ISLNK(inode->i_mode))
 133                acl = get_cached_acl(dir, ACL_TYPE_DEFAULT);
 134        if (acl) {
 135                struct posix_acl *clone;
 136
 137                if (S_ISDIR(inode->i_mode)) {
 138                        clone = posix_acl_clone(acl, GFP_KERNEL);
 139                        error = -ENOMEM;
 140                        if (!clone)
 141                                goto cleanup;
 142                        set_cached_acl(inode, ACL_TYPE_DEFAULT, clone);
 143                        posix_acl_release(clone);
 144                }
 145                clone = posix_acl_clone(acl, GFP_KERNEL);
 146                error = -ENOMEM;
 147                if (!clone)
 148                        goto cleanup;
 149                error = posix_acl_create_masq(clone, &mode);
 150                if (error >= 0) {
 151                        inode->i_mode = mode;
 152                        if (error > 0)
 153                                set_cached_acl(inode, ACL_TYPE_ACCESS, clone);
 154                }
 155                posix_acl_release(clone);
 156        }
 157        error = 0;
 158
 159cleanup:
 160        posix_acl_release(acl);
 161        return error;
 162}
 163
 164/**
 165 * generic_acl_chmod  -  change the access acl of @inode upon chmod()
 166 *
 167 * A chmod also changes the permissions of the owner, group/mask, and
 168 * other ACL entries.
 169 */
 170int
 171generic_acl_chmod(struct inode *inode)
 172{
 173        struct posix_acl *acl, *clone;
 174        int error = 0;
 175
 176        if (S_ISLNK(inode->i_mode))
 177                return -EOPNOTSUPP;
 178        acl = get_cached_acl(inode, ACL_TYPE_ACCESS);
 179        if (acl) {
 180                clone = posix_acl_clone(acl, GFP_KERNEL);
 181                posix_acl_release(acl);
 182                if (!clone)
 183                        return -ENOMEM;
 184                error = posix_acl_chmod_masq(clone, inode->i_mode);
 185                if (!error)
 186                        set_cached_acl(inode, ACL_TYPE_ACCESS, clone);
 187                posix_acl_release(clone);
 188        }
 189        return error;
 190}
 191
 192int
 193generic_check_acl(struct inode *inode, int mask, unsigned int flags)
 194{
 195        if (flags & IPERM_FLAG_RCU) {
 196                if (!negative_cached_acl(inode, ACL_TYPE_ACCESS))
 197                        return -ECHILD;
 198        } else {
 199                struct posix_acl *acl;
 200
 201                acl = get_cached_acl(inode, ACL_TYPE_ACCESS);
 202                if (acl) {
 203                        int error = posix_acl_permission(inode, acl, mask);
 204                        posix_acl_release(acl);
 205                        return error;
 206                }
 207        }
 208        return -EAGAIN;
 209}
 210
 211const struct xattr_handler generic_acl_access_handler = {
 212        .prefix = POSIX_ACL_XATTR_ACCESS,
 213        .flags  = ACL_TYPE_ACCESS,
 214        .list   = generic_acl_list,
 215        .get    = generic_acl_get,
 216        .set    = generic_acl_set,
 217};
 218
 219const struct xattr_handler generic_acl_default_handler = {
 220        .prefix = POSIX_ACL_XATTR_DEFAULT,
 221        .flags  = ACL_TYPE_DEFAULT,
 222        .list   = generic_acl_list,
 223        .get    = generic_acl_get,
 224        .set    = generic_acl_set,
 225};
 226