linux/fs/ocfs2/acl.c
<<
>>
Prefs
   1/* -*- mode: c; c-basic-offset: 8; -*-
   2 * vim: noexpandtab sw=8 ts=8 sts=0:
   3 *
   4 * acl.c
   5 *
   6 * Copyright (C) 2004, 2008 Oracle.  All rights reserved.
   7 *
   8 * CREDITS:
   9 * Lots of code in this file is copy from linux/fs/ext3/acl.c.
  10 * Copyright (C) 2001-2003 Andreas Gruenbacher, <agruen@suse.de>
  11 *
  12 * This program is free software; you can redistribute it and/or
  13 * modify it under the terms of the GNU General Public
  14 * License version 2 as published by the Free Software Foundation.
  15 *
  16 * This program is distributed in the hope that it will be useful,
  17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  19 * General Public License for more details.
  20 */
  21
  22#include <linux/init.h>
  23#include <linux/module.h>
  24#include <linux/slab.h>
  25#include <linux/string.h>
  26
  27#include <cluster/masklog.h>
  28
  29#include "ocfs2.h"
  30#include "alloc.h"
  31#include "dlmglue.h"
  32#include "file.h"
  33#include "inode.h"
  34#include "journal.h"
  35#include "ocfs2_fs.h"
  36
  37#include "xattr.h"
  38#include "acl.h"
  39
  40/*
  41 * Convert from xattr value to acl struct.
  42 */
  43static struct posix_acl *ocfs2_acl_from_xattr(const void *value, size_t size)
  44{
  45        int n, count;
  46        struct posix_acl *acl;
  47
  48        if (!value)
  49                return NULL;
  50        if (size < sizeof(struct posix_acl_entry))
  51                return ERR_PTR(-EINVAL);
  52
  53        count = size / sizeof(struct posix_acl_entry);
  54        if (count < 0)
  55                return ERR_PTR(-EINVAL);
  56        if (count == 0)
  57                return NULL;
  58
  59        acl = posix_acl_alloc(count, GFP_NOFS);
  60        if (!acl)
  61                return ERR_PTR(-ENOMEM);
  62        for (n = 0; n < count; n++) {
  63                struct ocfs2_acl_entry *entry =
  64                        (struct ocfs2_acl_entry *)value;
  65
  66                acl->a_entries[n].e_tag  = le16_to_cpu(entry->e_tag);
  67                acl->a_entries[n].e_perm = le16_to_cpu(entry->e_perm);
  68                acl->a_entries[n].e_id   = le32_to_cpu(entry->e_id);
  69                value += sizeof(struct posix_acl_entry);
  70
  71        }
  72        return acl;
  73}
  74
  75/*
  76 * Convert acl struct to xattr value.
  77 */
  78static void *ocfs2_acl_to_xattr(const struct posix_acl *acl, size_t *size)
  79{
  80        struct ocfs2_acl_entry *entry = NULL;
  81        char *ocfs2_acl;
  82        size_t n;
  83
  84        *size = acl->a_count * sizeof(struct posix_acl_entry);
  85
  86        ocfs2_acl = kmalloc(*size, GFP_NOFS);
  87        if (!ocfs2_acl)
  88                return ERR_PTR(-ENOMEM);
  89
  90        entry = (struct ocfs2_acl_entry *)ocfs2_acl;
  91        for (n = 0; n < acl->a_count; n++, entry++) {
  92                entry->e_tag  = cpu_to_le16(acl->a_entries[n].e_tag);
  93                entry->e_perm = cpu_to_le16(acl->a_entries[n].e_perm);
  94                entry->e_id   = cpu_to_le32(acl->a_entries[n].e_id);
  95        }
  96        return ocfs2_acl;
  97}
  98
  99static struct posix_acl *ocfs2_get_acl_nolock(struct inode *inode,
 100                                              int type,
 101                                              struct buffer_head *di_bh)
 102{
 103        int name_index;
 104        char *value = NULL;
 105        struct posix_acl *acl;
 106        int retval;
 107
 108        switch (type) {
 109        case ACL_TYPE_ACCESS:
 110                name_index = OCFS2_XATTR_INDEX_POSIX_ACL_ACCESS;
 111                break;
 112        case ACL_TYPE_DEFAULT:
 113                name_index = OCFS2_XATTR_INDEX_POSIX_ACL_DEFAULT;
 114                break;
 115        default:
 116                return ERR_PTR(-EINVAL);
 117        }
 118
 119        retval = ocfs2_xattr_get_nolock(inode, di_bh, name_index, "", NULL, 0);
 120        if (retval > 0) {
 121                value = kmalloc(retval, GFP_NOFS);
 122                if (!value)
 123                        return ERR_PTR(-ENOMEM);
 124                retval = ocfs2_xattr_get_nolock(inode, di_bh, name_index,
 125                                                "", value, retval);
 126        }
 127
 128        if (retval > 0)
 129                acl = ocfs2_acl_from_xattr(value, retval);
 130        else if (retval == -ENODATA || retval == 0)
 131                acl = NULL;
 132        else
 133                acl = ERR_PTR(retval);
 134
 135        kfree(value);
 136
 137        return acl;
 138}
 139
 140
 141/*
 142 * Get posix acl.
 143 */
 144static struct posix_acl *ocfs2_get_acl(struct inode *inode, int type)
 145{
 146        struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
 147        struct buffer_head *di_bh = NULL;
 148        struct posix_acl *acl;
 149        int ret;
 150
 151        if (!(osb->s_mount_opt & OCFS2_MOUNT_POSIX_ACL))
 152                return NULL;
 153
 154        ret = ocfs2_inode_lock(inode, &di_bh, 0);
 155        if (ret < 0) {
 156                mlog_errno(ret);
 157                acl = ERR_PTR(ret);
 158                return acl;
 159        }
 160
 161        acl = ocfs2_get_acl_nolock(inode, type, di_bh);
 162
 163        ocfs2_inode_unlock(inode, 0);
 164
 165        brelse(di_bh);
 166
 167        return acl;
 168}
 169
 170/*
 171 * Helper function to set i_mode in memory and disk. Some call paths
 172 * will not have di_bh or a journal handle to pass, in which case it
 173 * will create it's own.
 174 */
 175static int ocfs2_acl_set_mode(struct inode *inode, struct buffer_head *di_bh,
 176                              handle_t *handle, umode_t new_mode)
 177{
 178        int ret, commit_handle = 0;
 179        struct ocfs2_dinode *di;
 180
 181        if (di_bh == NULL) {
 182                ret = ocfs2_read_inode_block(inode, &di_bh);
 183                if (ret) {
 184                        mlog_errno(ret);
 185                        goto out;
 186                }
 187        } else
 188                get_bh(di_bh);
 189
 190        if (handle == NULL) {
 191                handle = ocfs2_start_trans(OCFS2_SB(inode->i_sb),
 192                                           OCFS2_INODE_UPDATE_CREDITS);
 193                if (IS_ERR(handle)) {
 194                        ret = PTR_ERR(handle);
 195                        mlog_errno(ret);
 196                        goto out_brelse;
 197                }
 198
 199                commit_handle = 1;
 200        }
 201
 202        di = (struct ocfs2_dinode *)di_bh->b_data;
 203        ret = ocfs2_journal_access_di(handle, INODE_CACHE(inode), di_bh,
 204                                      OCFS2_JOURNAL_ACCESS_WRITE);
 205        if (ret) {
 206                mlog_errno(ret);
 207                goto out_commit;
 208        }
 209
 210        inode->i_mode = new_mode;
 211        inode->i_ctime = CURRENT_TIME;
 212        di->i_mode = cpu_to_le16(inode->i_mode);
 213        di->i_ctime = cpu_to_le64(inode->i_ctime.tv_sec);
 214        di->i_ctime_nsec = cpu_to_le32(inode->i_ctime.tv_nsec);
 215
 216        ocfs2_journal_dirty(handle, di_bh);
 217
 218out_commit:
 219        if (commit_handle)
 220                ocfs2_commit_trans(OCFS2_SB(inode->i_sb), handle);
 221out_brelse:
 222        brelse(di_bh);
 223out:
 224        return ret;
 225}
 226
 227/*
 228 * Set the access or default ACL of an inode.
 229 */
 230static int ocfs2_set_acl(handle_t *handle,
 231                         struct inode *inode,
 232                         struct buffer_head *di_bh,
 233                         int type,
 234                         struct posix_acl *acl,
 235                         struct ocfs2_alloc_context *meta_ac,
 236                         struct ocfs2_alloc_context *data_ac)
 237{
 238        int name_index;
 239        void *value = NULL;
 240        size_t size = 0;
 241        int ret;
 242
 243        if (S_ISLNK(inode->i_mode))
 244                return -EOPNOTSUPP;
 245
 246        switch (type) {
 247        case ACL_TYPE_ACCESS:
 248                name_index = OCFS2_XATTR_INDEX_POSIX_ACL_ACCESS;
 249                if (acl) {
 250                        umode_t mode = inode->i_mode;
 251                        ret = posix_acl_equiv_mode(acl, &mode);
 252                        if (ret < 0)
 253                                return ret;
 254                        else {
 255                                if (ret == 0)
 256                                        acl = NULL;
 257
 258                                ret = ocfs2_acl_set_mode(inode, di_bh,
 259                                                         handle, mode);
 260                                if (ret)
 261                                        return ret;
 262
 263                        }
 264                }
 265                break;
 266        case ACL_TYPE_DEFAULT:
 267                name_index = OCFS2_XATTR_INDEX_POSIX_ACL_DEFAULT;
 268                if (!S_ISDIR(inode->i_mode))
 269                        return acl ? -EACCES : 0;
 270                break;
 271        default:
 272                return -EINVAL;
 273        }
 274
 275        if (acl) {
 276                value = ocfs2_acl_to_xattr(acl, &size);
 277                if (IS_ERR(value))
 278                        return (int)PTR_ERR(value);
 279        }
 280
 281        if (handle)
 282                ret = ocfs2_xattr_set_handle(handle, inode, di_bh, name_index,
 283                                             "", value, size, 0,
 284                                             meta_ac, data_ac);
 285        else
 286                ret = ocfs2_xattr_set(inode, name_index, "", value, size, 0);
 287
 288        kfree(value);
 289
 290        return ret;
 291}
 292
 293struct posix_acl *ocfs2_iop_get_acl(struct inode *inode, int type)
 294{
 295        struct ocfs2_super *osb;
 296        struct buffer_head *di_bh = NULL;
 297        struct posix_acl *acl;
 298        int ret = -EAGAIN;
 299
 300        osb = OCFS2_SB(inode->i_sb);
 301        if (!(osb->s_mount_opt & OCFS2_MOUNT_POSIX_ACL))
 302                return NULL;
 303
 304        ret = ocfs2_read_inode_block(inode, &di_bh);
 305        if (ret < 0)
 306                return ERR_PTR(ret);
 307
 308        acl = ocfs2_get_acl_nolock(inode, type, di_bh);
 309
 310        brelse(di_bh);
 311
 312        return acl;
 313}
 314
 315int ocfs2_acl_chmod(struct inode *inode)
 316{
 317        struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
 318        struct posix_acl *acl;
 319        int ret;
 320
 321        if (S_ISLNK(inode->i_mode))
 322                return -EOPNOTSUPP;
 323
 324        if (!(osb->s_mount_opt & OCFS2_MOUNT_POSIX_ACL))
 325                return 0;
 326
 327        acl = ocfs2_get_acl(inode, ACL_TYPE_ACCESS);
 328        if (IS_ERR(acl) || !acl)
 329                return PTR_ERR(acl);
 330        ret = posix_acl_chmod(&acl, GFP_KERNEL, inode->i_mode);
 331        if (ret)
 332                return ret;
 333        ret = ocfs2_set_acl(NULL, inode, NULL, ACL_TYPE_ACCESS,
 334                            acl, NULL, NULL);
 335        posix_acl_release(acl);
 336        return ret;
 337}
 338
 339/*
 340 * Initialize the ACLs of a new inode. If parent directory has default ACL,
 341 * then clone to new inode. Called from ocfs2_mknod.
 342 */
 343int ocfs2_init_acl(handle_t *handle,
 344                   struct inode *inode,
 345                   struct inode *dir,
 346                   struct buffer_head *di_bh,
 347                   struct buffer_head *dir_bh,
 348                   struct ocfs2_alloc_context *meta_ac,
 349                   struct ocfs2_alloc_context *data_ac)
 350{
 351        struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
 352        struct posix_acl *acl = NULL;
 353        int ret = 0, ret2;
 354        umode_t mode;
 355
 356        if (!S_ISLNK(inode->i_mode)) {
 357                if (osb->s_mount_opt & OCFS2_MOUNT_POSIX_ACL) {
 358                        acl = ocfs2_get_acl_nolock(dir, ACL_TYPE_DEFAULT,
 359                                                   dir_bh);
 360                        if (IS_ERR(acl))
 361                                return PTR_ERR(acl);
 362                }
 363                if (!acl) {
 364                        mode = inode->i_mode & ~current_umask();
 365                        ret = ocfs2_acl_set_mode(inode, di_bh, handle, mode);
 366                        if (ret) {
 367                                mlog_errno(ret);
 368                                goto cleanup;
 369                        }
 370                }
 371        }
 372        if ((osb->s_mount_opt & OCFS2_MOUNT_POSIX_ACL) && acl) {
 373                if (S_ISDIR(inode->i_mode)) {
 374                        ret = ocfs2_set_acl(handle, inode, di_bh,
 375                                            ACL_TYPE_DEFAULT, acl,
 376                                            meta_ac, data_ac);
 377                        if (ret)
 378                                goto cleanup;
 379                }
 380                mode = inode->i_mode;
 381                ret = posix_acl_create(&acl, GFP_NOFS, &mode);
 382                if (ret < 0)
 383                        return ret;
 384
 385                ret2 = ocfs2_acl_set_mode(inode, di_bh, handle, mode);
 386                if (ret2) {
 387                        mlog_errno(ret2);
 388                        ret = ret2;
 389                        goto cleanup;
 390                }
 391                if (ret > 0) {
 392                        ret = ocfs2_set_acl(handle, inode,
 393                                            di_bh, ACL_TYPE_ACCESS,
 394                                            acl, meta_ac, data_ac);
 395                }
 396        }
 397cleanup:
 398        posix_acl_release(acl);
 399        return ret;
 400}
 401
 402static size_t ocfs2_xattr_list_acl_access(struct dentry *dentry,
 403                                          char *list,
 404                                          size_t list_len,
 405                                          const char *name,
 406                                          size_t name_len,
 407                                          int type)
 408{
 409        struct ocfs2_super *osb = OCFS2_SB(dentry->d_sb);
 410        const size_t size = sizeof(POSIX_ACL_XATTR_ACCESS);
 411
 412        if (!(osb->s_mount_opt & OCFS2_MOUNT_POSIX_ACL))
 413                return 0;
 414
 415        if (list && size <= list_len)
 416                memcpy(list, POSIX_ACL_XATTR_ACCESS, size);
 417        return size;
 418}
 419
 420static size_t ocfs2_xattr_list_acl_default(struct dentry *dentry,
 421                                           char *list,
 422                                           size_t list_len,
 423                                           const char *name,
 424                                           size_t name_len,
 425                                           int type)
 426{
 427        struct ocfs2_super *osb = OCFS2_SB(dentry->d_sb);
 428        const size_t size = sizeof(POSIX_ACL_XATTR_DEFAULT);
 429
 430        if (!(osb->s_mount_opt & OCFS2_MOUNT_POSIX_ACL))
 431                return 0;
 432
 433        if (list && size <= list_len)
 434                memcpy(list, POSIX_ACL_XATTR_DEFAULT, size);
 435        return size;
 436}
 437
 438static int ocfs2_xattr_get_acl(struct dentry *dentry, const char *name,
 439                void *buffer, size_t size, int type)
 440{
 441        struct ocfs2_super *osb = OCFS2_SB(dentry->d_sb);
 442        struct posix_acl *acl;
 443        int ret;
 444
 445        if (strcmp(name, "") != 0)
 446                return -EINVAL;
 447        if (!(osb->s_mount_opt & OCFS2_MOUNT_POSIX_ACL))
 448                return -EOPNOTSUPP;
 449
 450        acl = ocfs2_get_acl(dentry->d_inode, type);
 451        if (IS_ERR(acl))
 452                return PTR_ERR(acl);
 453        if (acl == NULL)
 454                return -ENODATA;
 455        ret = posix_acl_to_xattr(acl, buffer, size);
 456        posix_acl_release(acl);
 457
 458        return ret;
 459}
 460
 461static int ocfs2_xattr_set_acl(struct dentry *dentry, const char *name,
 462                const void *value, size_t size, int flags, int type)
 463{
 464        struct inode *inode = dentry->d_inode;
 465        struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
 466        struct posix_acl *acl;
 467        int ret = 0;
 468
 469        if (strcmp(name, "") != 0)
 470                return -EINVAL;
 471        if (!(osb->s_mount_opt & OCFS2_MOUNT_POSIX_ACL))
 472                return -EOPNOTSUPP;
 473
 474        if (!inode_owner_or_capable(inode))
 475                return -EPERM;
 476
 477        if (value) {
 478                acl = posix_acl_from_xattr(value, size);
 479                if (IS_ERR(acl))
 480                        return PTR_ERR(acl);
 481                else if (acl) {
 482                        ret = posix_acl_valid(acl);
 483                        if (ret)
 484                                goto cleanup;
 485                }
 486        } else
 487                acl = NULL;
 488
 489        ret = ocfs2_set_acl(NULL, inode, NULL, type, acl, NULL, NULL);
 490
 491cleanup:
 492        posix_acl_release(acl);
 493        return ret;
 494}
 495
 496const struct xattr_handler ocfs2_xattr_acl_access_handler = {
 497        .prefix = POSIX_ACL_XATTR_ACCESS,
 498        .flags  = ACL_TYPE_ACCESS,
 499        .list   = ocfs2_xattr_list_acl_access,
 500        .get    = ocfs2_xattr_get_acl,
 501        .set    = ocfs2_xattr_set_acl,
 502};
 503
 504const struct xattr_handler ocfs2_xattr_acl_default_handler = {
 505        .prefix = POSIX_ACL_XATTR_DEFAULT,
 506        .flags  = ACL_TYPE_DEFAULT,
 507        .list   = ocfs2_xattr_list_acl_default,
 508        .get    = ocfs2_xattr_get_acl,
 509        .set    = ocfs2_xattr_set_acl,
 510};
 511