linux/fs/gfs2/acl.c
<<
>>
Prefs
   1/*
   2 * Copyright (C) Sistina Software, Inc.  1997-2003 All rights reserved.
   3 * Copyright (C) 2004-2006 Red Hat, Inc.  All rights reserved.
   4 *
   5 * This copyrighted material is made available to anyone wishing to use,
   6 * modify, copy, or redistribute it subject to the terms and conditions
   7 * of the GNU General Public License version 2.
   8 */
   9
  10#include <linux/sched.h>
  11#include <linux/slab.h>
  12#include <linux/spinlock.h>
  13#include <linux/completion.h>
  14#include <linux/buffer_head.h>
  15#include <linux/posix_acl.h>
  16#include <linux/posix_acl_xattr.h>
  17#include <linux/gfs2_ondisk.h>
  18#include <linux/lm_interface.h>
  19
  20#include "gfs2.h"
  21#include "incore.h"
  22#include "acl.h"
  23#include "eaops.h"
  24#include "eattr.h"
  25#include "glock.h"
  26#include "inode.h"
  27#include "meta_io.h"
  28#include "trans.h"
  29#include "util.h"
  30
  31#define ACL_ACCESS 1
  32#define ACL_DEFAULT 0
  33
  34int gfs2_acl_validate_set(struct gfs2_inode *ip, int access,
  35                      struct gfs2_ea_request *er,
  36                      int *remove, mode_t *mode)
  37{
  38        struct posix_acl *acl;
  39        int error;
  40
  41        error = gfs2_acl_validate_remove(ip, access);
  42        if (error)
  43                return error;
  44
  45        if (!er->er_data)
  46                return -EINVAL;
  47
  48        acl = posix_acl_from_xattr(er->er_data, er->er_data_len);
  49        if (IS_ERR(acl))
  50                return PTR_ERR(acl);
  51        if (!acl) {
  52                *remove = 1;
  53                return 0;
  54        }
  55
  56        error = posix_acl_valid(acl);
  57        if (error)
  58                goto out;
  59
  60        if (access) {
  61                error = posix_acl_equiv_mode(acl, mode);
  62                if (!error)
  63                        *remove = 1;
  64                else if (error > 0)
  65                        error = 0;
  66        }
  67
  68out:
  69        posix_acl_release(acl);
  70        return error;
  71}
  72
  73int gfs2_acl_validate_remove(struct gfs2_inode *ip, int access)
  74{
  75        if (!GFS2_SB(&ip->i_inode)->sd_args.ar_posix_acl)
  76                return -EOPNOTSUPP;
  77        if (!is_owner_or_cap(&ip->i_inode))
  78                return -EPERM;
  79        if (S_ISLNK(ip->i_inode.i_mode))
  80                return -EOPNOTSUPP;
  81        if (!access && !S_ISDIR(ip->i_inode.i_mode))
  82                return -EACCES;
  83
  84        return 0;
  85}
  86
  87static int acl_get(struct gfs2_inode *ip, int access, struct posix_acl **acl,
  88                   struct gfs2_ea_location *el, char **data, unsigned int *len)
  89{
  90        struct gfs2_ea_request er;
  91        struct gfs2_ea_location el_this;
  92        int error;
  93
  94        if (!ip->i_di.di_eattr)
  95                return 0;
  96
  97        memset(&er, 0, sizeof(struct gfs2_ea_request));
  98        if (access) {
  99                er.er_name = GFS2_POSIX_ACL_ACCESS;
 100                er.er_name_len = GFS2_POSIX_ACL_ACCESS_LEN;
 101        } else {
 102                er.er_name = GFS2_POSIX_ACL_DEFAULT;
 103                er.er_name_len = GFS2_POSIX_ACL_DEFAULT_LEN;
 104        }
 105        er.er_type = GFS2_EATYPE_SYS;
 106
 107        if (!el)
 108                el = &el_this;
 109
 110        error = gfs2_ea_find(ip, &er, el);
 111        if (error)
 112                return error;
 113        if (!el->el_ea)
 114                return 0;
 115        if (!GFS2_EA_DATA_LEN(el->el_ea))
 116                goto out;
 117
 118        er.er_data_len = GFS2_EA_DATA_LEN(el->el_ea);
 119        er.er_data = kmalloc(er.er_data_len, GFP_NOFS);
 120        error = -ENOMEM;
 121        if (!er.er_data)
 122                goto out;
 123
 124        error = gfs2_ea_get_copy(ip, el, er.er_data);
 125        if (error)
 126                goto out_kfree;
 127
 128        if (acl) {
 129                *acl = posix_acl_from_xattr(er.er_data, er.er_data_len);
 130                if (IS_ERR(*acl))
 131                        error = PTR_ERR(*acl);
 132        }
 133
 134out_kfree:
 135        if (error || !data)
 136                kfree(er.er_data);
 137        else {
 138                *data = er.er_data;
 139                *len = er.er_data_len;
 140        }
 141out:
 142        if (error || el == &el_this)
 143                brelse(el->el_bh);
 144        return error;
 145}
 146
 147/**
 148 * gfs2_check_acl - Check an ACL to see if we're allowed to do something
 149 * @inode: the file we want to do something to
 150 * @mask: what we want to do
 151 *
 152 * Returns: errno
 153 */
 154
 155int gfs2_check_acl(struct inode *inode, int mask)
 156{
 157        struct posix_acl *acl = NULL;
 158        int error;
 159
 160        error = acl_get(GFS2_I(inode), ACL_ACCESS, &acl, NULL, NULL, NULL);
 161        if (error)
 162                return error;
 163
 164        if (acl) {
 165                error = posix_acl_permission(inode, acl, mask);
 166                posix_acl_release(acl);
 167                return error;
 168        }
 169
 170        return -EAGAIN;
 171}
 172
 173static int munge_mode(struct gfs2_inode *ip, mode_t mode)
 174{
 175        struct gfs2_sbd *sdp = GFS2_SB(&ip->i_inode);
 176        struct buffer_head *dibh;
 177        int error;
 178
 179        error = gfs2_trans_begin(sdp, RES_DINODE, 0);
 180        if (error)
 181                return error;
 182
 183        error = gfs2_meta_inode_buffer(ip, &dibh);
 184        if (!error) {
 185                gfs2_assert_withdraw(sdp,
 186                                (ip->i_inode.i_mode & S_IFMT) == (mode & S_IFMT));
 187                ip->i_inode.i_mode = mode;
 188                gfs2_trans_add_bh(ip->i_gl, dibh, 1);
 189                gfs2_dinode_out(ip, dibh->b_data);
 190                brelse(dibh);
 191        }
 192
 193        gfs2_trans_end(sdp);
 194
 195        return 0;
 196}
 197
 198int gfs2_acl_create(struct gfs2_inode *dip, struct gfs2_inode *ip)
 199{
 200        struct gfs2_sbd *sdp = GFS2_SB(&dip->i_inode);
 201        struct posix_acl *acl = NULL, *clone;
 202        struct gfs2_ea_request er;
 203        mode_t mode = ip->i_inode.i_mode;
 204        int error;
 205
 206        if (!sdp->sd_args.ar_posix_acl)
 207                return 0;
 208        if (S_ISLNK(ip->i_inode.i_mode))
 209                return 0;
 210
 211        memset(&er, 0, sizeof(struct gfs2_ea_request));
 212        er.er_type = GFS2_EATYPE_SYS;
 213
 214        error = acl_get(dip, ACL_DEFAULT, &acl, NULL,
 215                        &er.er_data, &er.er_data_len);
 216        if (error)
 217                return error;
 218        if (!acl) {
 219                mode &= ~current->fs->umask;
 220                if (mode != ip->i_inode.i_mode)
 221                        error = munge_mode(ip, mode);
 222                return error;
 223        }
 224
 225        clone = posix_acl_clone(acl, GFP_NOFS);
 226        error = -ENOMEM;
 227        if (!clone)
 228                goto out;
 229        posix_acl_release(acl);
 230        acl = clone;
 231
 232        if (S_ISDIR(ip->i_inode.i_mode)) {
 233                er.er_name = GFS2_POSIX_ACL_DEFAULT;
 234                er.er_name_len = GFS2_POSIX_ACL_DEFAULT_LEN;
 235                error = gfs2_system_eaops.eo_set(ip, &er);
 236                if (error)
 237                        goto out;
 238        }
 239
 240        error = posix_acl_create_masq(acl, &mode);
 241        if (error < 0)
 242                goto out;
 243        if (error > 0) {
 244                er.er_name = GFS2_POSIX_ACL_ACCESS;
 245                er.er_name_len = GFS2_POSIX_ACL_ACCESS_LEN;
 246                posix_acl_to_xattr(acl, er.er_data, er.er_data_len);
 247                er.er_mode = mode;
 248                er.er_flags = GFS2_ERF_MODE;
 249                error = gfs2_system_eaops.eo_set(ip, &er);
 250                if (error)
 251                        goto out;
 252        } else
 253                munge_mode(ip, mode);
 254
 255out:
 256        posix_acl_release(acl);
 257        kfree(er.er_data);
 258        return error;
 259}
 260
 261int gfs2_acl_chmod(struct gfs2_inode *ip, struct iattr *attr)
 262{
 263        struct posix_acl *acl = NULL, *clone;
 264        struct gfs2_ea_location el;
 265        char *data;
 266        unsigned int len;
 267        int error;
 268
 269        error = acl_get(ip, ACL_ACCESS, &acl, &el, &data, &len);
 270        if (error)
 271                return error;
 272        if (!acl)
 273                return gfs2_setattr_simple(ip, attr);
 274
 275        clone = posix_acl_clone(acl, GFP_NOFS);
 276        error = -ENOMEM;
 277        if (!clone)
 278                goto out;
 279        posix_acl_release(acl);
 280        acl = clone;
 281
 282        error = posix_acl_chmod_masq(acl, attr->ia_mode);
 283        if (!error) {
 284                posix_acl_to_xattr(acl, data, len);
 285                error = gfs2_ea_acl_chmod(ip, &el, attr, data);
 286        }
 287
 288out:
 289        posix_acl_release(acl);
 290        brelse(el.el_bh);
 291        kfree(data);
 292        return error;
 293}
 294
 295
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.