linux/fs/f2fs/acl.c
<<
>>
Prefs
   1/*
   2 * fs/f2fs/acl.c
   3 *
   4 * Copyright (c) 2012 Samsung Electronics Co., Ltd.
   5 *             http://www.samsung.com/
   6 *
   7 * Portions of this code from linux/fs/ext2/acl.c
   8 *
   9 * Copyright (C) 2001-2003 Andreas Gruenbacher, <agruen@suse.de>
  10 *
  11 * This program is free software; you can redistribute it and/or modify
  12 * it under the terms of the GNU General Public License version 2 as
  13 * published by the Free Software Foundation.
  14 */
  15#include <linux/f2fs_fs.h>
  16#include "f2fs.h"
  17#include "xattr.h"
  18#include "acl.h"
  19
  20static inline size_t f2fs_acl_size(int count)
  21{
  22        if (count <= 4) {
  23                return sizeof(struct f2fs_acl_header) +
  24                        count * sizeof(struct f2fs_acl_entry_short);
  25        } else {
  26                return sizeof(struct f2fs_acl_header) +
  27                        4 * sizeof(struct f2fs_acl_entry_short) +
  28                        (count - 4) * sizeof(struct f2fs_acl_entry);
  29        }
  30}
  31
  32static inline int f2fs_acl_count(size_t size)
  33{
  34        ssize_t s;
  35        size -= sizeof(struct f2fs_acl_header);
  36        s = size - 4 * sizeof(struct f2fs_acl_entry_short);
  37        if (s < 0) {
  38                if (size % sizeof(struct f2fs_acl_entry_short))
  39                        return -1;
  40                return size / sizeof(struct f2fs_acl_entry_short);
  41        } else {
  42                if (s % sizeof(struct f2fs_acl_entry))
  43                        return -1;
  44                return s / sizeof(struct f2fs_acl_entry) + 4;
  45        }
  46}
  47
  48static struct posix_acl *f2fs_acl_from_disk(const char *value, size_t size)
  49{
  50        int i, count;
  51        struct posix_acl *acl;
  52        struct f2fs_acl_header *hdr = (struct f2fs_acl_header *)value;
  53        struct f2fs_acl_entry *entry = (struct f2fs_acl_entry *)(hdr + 1);
  54        const char *end = value + size;
  55
  56        if (hdr->a_version != cpu_to_le32(F2FS_ACL_VERSION))
  57                return ERR_PTR(-EINVAL);
  58
  59        count = f2fs_acl_count(size);
  60        if (count < 0)
  61                return ERR_PTR(-EINVAL);
  62        if (count == 0)
  63                return NULL;
  64
  65        acl = posix_acl_alloc(count, GFP_NOFS);
  66        if (!acl)
  67                return ERR_PTR(-ENOMEM);
  68
  69        for (i = 0; i < count; i++) {
  70
  71                if ((char *)entry > end)
  72                        goto fail;
  73
  74                acl->a_entries[i].e_tag  = le16_to_cpu(entry->e_tag);
  75                acl->a_entries[i].e_perm = le16_to_cpu(entry->e_perm);
  76
  77                switch (acl->a_entries[i].e_tag) {
  78                case ACL_USER_OBJ:
  79                case ACL_GROUP_OBJ:
  80                case ACL_MASK:
  81                case ACL_OTHER:
  82                        entry = (struct f2fs_acl_entry *)((char *)entry +
  83                                        sizeof(struct f2fs_acl_entry_short));
  84                        break;
  85
  86                case ACL_USER:
  87                        acl->a_entries[i].e_uid =
  88                                make_kuid(&init_user_ns,
  89                                                le32_to_cpu(entry->e_id));
  90                        entry = (struct f2fs_acl_entry *)((char *)entry +
  91                                        sizeof(struct f2fs_acl_entry));
  92                        break;
  93                case ACL_GROUP:
  94                        acl->a_entries[i].e_gid =
  95                                make_kgid(&init_user_ns,
  96                                                le32_to_cpu(entry->e_id));
  97                        entry = (struct f2fs_acl_entry *)((char *)entry +
  98                                        sizeof(struct f2fs_acl_entry));
  99                        break;
 100                default:
 101                        goto fail;
 102                }
 103        }
 104        if ((char *)entry != end)
 105                goto fail;
 106        return acl;
 107fail:
 108        posix_acl_release(acl);
 109        return ERR_PTR(-EINVAL);
 110}
 111
 112static void *f2fs_acl_to_disk(const struct posix_acl *acl, size_t *size)
 113{
 114        struct f2fs_acl_header *f2fs_acl;
 115        struct f2fs_acl_entry *entry;
 116        int i;
 117
 118        f2fs_acl = kmalloc(sizeof(struct f2fs_acl_header) + acl->a_count *
 119                        sizeof(struct f2fs_acl_entry), GFP_NOFS);
 120        if (!f2fs_acl)
 121                return ERR_PTR(-ENOMEM);
 122
 123        f2fs_acl->a_version = cpu_to_le32(F2FS_ACL_VERSION);
 124        entry = (struct f2fs_acl_entry *)(f2fs_acl + 1);
 125
 126        for (i = 0; i < acl->a_count; i++) {
 127
 128                entry->e_tag  = cpu_to_le16(acl->a_entries[i].e_tag);
 129                entry->e_perm = cpu_to_le16(acl->a_entries[i].e_perm);
 130
 131                switch (acl->a_entries[i].e_tag) {
 132                case ACL_USER:
 133                        entry->e_id = cpu_to_le32(
 134                                        from_kuid(&init_user_ns,
 135                                                acl->a_entries[i].e_uid));
 136                        entry = (struct f2fs_acl_entry *)((char *)entry +
 137                                        sizeof(struct f2fs_acl_entry));
 138                        break;
 139                case ACL_GROUP:
 140                        entry->e_id = cpu_to_le32(
 141                                        from_kgid(&init_user_ns,
 142                                                acl->a_entries[i].e_gid));
 143                        entry = (struct f2fs_acl_entry *)((char *)entry +
 144                                        sizeof(struct f2fs_acl_entry));
 145                        break;
 146                case ACL_USER_OBJ:
 147                case ACL_GROUP_OBJ:
 148                case ACL_MASK:
 149                case ACL_OTHER:
 150                        entry = (struct f2fs_acl_entry *)((char *)entry +
 151                                        sizeof(struct f2fs_acl_entry_short));
 152                        break;
 153                default:
 154                        goto fail;
 155                }
 156        }
 157        *size = f2fs_acl_size(acl->a_count);
 158        return (void *)f2fs_acl;
 159
 160fail:
 161        kfree(f2fs_acl);
 162        return ERR_PTR(-EINVAL);
 163}
 164
 165static struct posix_acl *__f2fs_get_acl(struct inode *inode, int type,
 166                                                struct page *dpage)
 167{
 168        int name_index = F2FS_XATTR_INDEX_POSIX_ACL_DEFAULT;
 169        void *value = NULL;
 170        struct posix_acl *acl;
 171        int retval;
 172
 173        if (type == ACL_TYPE_ACCESS)
 174                name_index = F2FS_XATTR_INDEX_POSIX_ACL_ACCESS;
 175
 176        retval = f2fs_getxattr(inode, name_index, "", NULL, 0, dpage);
 177        if (retval > 0) {
 178                value = kmalloc(retval, GFP_F2FS_ZERO);
 179                if (!value)
 180                        return ERR_PTR(-ENOMEM);
 181                retval = f2fs_getxattr(inode, name_index, "", value,
 182                                                        retval, dpage);
 183        }
 184
 185        if (retval > 0)
 186                acl = f2fs_acl_from_disk(value, retval);
 187        else if (retval == -ENODATA)
 188                acl = NULL;
 189        else
 190                acl = ERR_PTR(retval);
 191        kfree(value);
 192
 193        if (!IS_ERR(acl))
 194                set_cached_acl(inode, type, acl);
 195
 196        return acl;
 197}
 198
 199struct posix_acl *f2fs_get_acl(struct inode *inode, int type)
 200{
 201        return __f2fs_get_acl(inode, type, NULL);
 202}
 203
 204static int __f2fs_set_acl(struct inode *inode, int type,
 205                        struct posix_acl *acl, struct page *ipage)
 206{
 207        struct f2fs_inode_info *fi = F2FS_I(inode);
 208        int name_index;
 209        void *value = NULL;
 210        size_t size = 0;
 211        int error;
 212
 213        switch (type) {
 214        case ACL_TYPE_ACCESS:
 215                name_index = F2FS_XATTR_INDEX_POSIX_ACL_ACCESS;
 216                if (acl) {
 217                        error = posix_acl_equiv_mode(acl, &inode->i_mode);
 218                        if (error < 0)
 219                                return error;
 220                        set_acl_inode(fi, inode->i_mode);
 221                        if (error == 0)
 222                                acl = NULL;
 223                }
 224                break;
 225
 226        case ACL_TYPE_DEFAULT:
 227                name_index = F2FS_XATTR_INDEX_POSIX_ACL_DEFAULT;
 228                if (!S_ISDIR(inode->i_mode))
 229                        return acl ? -EACCES : 0;
 230                break;
 231
 232        default:
 233                return -EINVAL;
 234        }
 235
 236        if (acl) {
 237                value = f2fs_acl_to_disk(acl, &size);
 238                if (IS_ERR(value)) {
 239                        clear_inode_flag(fi, FI_ACL_MODE);
 240                        return (int)PTR_ERR(value);
 241                }
 242        }
 243
 244        error = f2fs_setxattr(inode, name_index, "", value, size, ipage, 0);
 245
 246        kfree(value);
 247        if (!error)
 248                set_cached_acl(inode, type, acl);
 249
 250        clear_inode_flag(fi, FI_ACL_MODE);
 251        return error;
 252}
 253
 254int f2fs_set_acl(struct inode *inode, struct posix_acl *acl, int type)
 255{
 256        return __f2fs_set_acl(inode, type, acl, NULL);
 257}
 258
 259/*
 260 * Most part of f2fs_acl_clone, f2fs_acl_create_masq, f2fs_acl_create
 261 * are copied from posix_acl.c
 262 */
 263static struct posix_acl *f2fs_acl_clone(const struct posix_acl *acl,
 264                                                        gfp_t flags)
 265{
 266        struct posix_acl *clone = NULL;
 267
 268        if (acl) {
 269                int size = sizeof(struct posix_acl) + acl->a_count *
 270                                sizeof(struct posix_acl_entry);
 271                clone = kmemdup(acl, size, flags);
 272                if (clone)
 273                        atomic_set(&clone->a_refcount, 1);
 274        }
 275        return clone;
 276}
 277
 278static int f2fs_acl_create_masq(struct posix_acl *acl, umode_t *mode_p)
 279{
 280        struct posix_acl_entry *pa, *pe;
 281        struct posix_acl_entry *group_obj = NULL, *mask_obj = NULL;
 282        umode_t mode = *mode_p;
 283        int not_equiv = 0;
 284
 285        /* assert(atomic_read(acl->a_refcount) == 1); */
 286
 287        FOREACH_ACL_ENTRY(pa, acl, pe) {
 288                switch(pa->e_tag) {
 289                case ACL_USER_OBJ:
 290                        pa->e_perm &= (mode >> 6) | ~S_IRWXO;
 291                        mode &= (pa->e_perm << 6) | ~S_IRWXU;
 292                        break;
 293
 294                case ACL_USER:
 295                case ACL_GROUP:
 296                        not_equiv = 1;
 297                        break;
 298
 299                case ACL_GROUP_OBJ:
 300                        group_obj = pa;
 301                        break;
 302
 303                case ACL_OTHER:
 304                        pa->e_perm &= mode | ~S_IRWXO;
 305                        mode &= pa->e_perm | ~S_IRWXO;
 306                        break;
 307
 308                case ACL_MASK:
 309                        mask_obj = pa;
 310                        not_equiv = 1;
 311                        break;
 312
 313                default:
 314                        return -EIO;
 315                }
 316        }
 317
 318        if (mask_obj) {
 319                mask_obj->e_perm &= (mode >> 3) | ~S_IRWXO;
 320                mode &= (mask_obj->e_perm << 3) | ~S_IRWXG;
 321        } else {
 322                if (!group_obj)
 323                        return -EIO;
 324                group_obj->e_perm &= (mode >> 3) | ~S_IRWXO;
 325                mode &= (group_obj->e_perm << 3) | ~S_IRWXG;
 326        }
 327
 328        *mode_p = (*mode_p & ~S_IRWXUGO) | mode;
 329        return not_equiv;
 330}
 331
 332static int f2fs_acl_create(struct inode *dir, umode_t *mode,
 333                struct posix_acl **default_acl, struct posix_acl **acl,
 334                struct page *dpage)
 335{
 336        struct posix_acl *p;
 337        struct posix_acl *clone;
 338        int ret;
 339
 340        *acl = NULL;
 341        *default_acl = NULL;
 342
 343        if (S_ISLNK(*mode) || !IS_POSIXACL(dir))
 344                return 0;
 345
 346        p = __f2fs_get_acl(dir, ACL_TYPE_DEFAULT, dpage);
 347        if (!p || p == ERR_PTR(-EOPNOTSUPP)) {
 348                *mode &= ~current_umask();
 349                return 0;
 350        }
 351        if (IS_ERR(p))
 352                return PTR_ERR(p);
 353
 354        clone = f2fs_acl_clone(p, GFP_NOFS);
 355        if (!clone)
 356                goto no_mem;
 357
 358        ret = f2fs_acl_create_masq(clone, mode);
 359        if (ret < 0)
 360                goto no_mem_clone;
 361
 362        if (ret == 0)
 363                posix_acl_release(clone);
 364        else
 365                *acl = clone;
 366
 367        if (!S_ISDIR(*mode))
 368                posix_acl_release(p);
 369        else
 370                *default_acl = p;
 371
 372        return 0;
 373
 374no_mem_clone:
 375        posix_acl_release(clone);
 376no_mem:
 377        posix_acl_release(p);
 378        return -ENOMEM;
 379}
 380
 381int f2fs_init_acl(struct inode *inode, struct inode *dir, struct page *ipage,
 382                                                        struct page *dpage)
 383{
 384        struct posix_acl *default_acl = NULL, *acl = NULL;
 385        int error = 0;
 386
 387        error = f2fs_acl_create(dir, &inode->i_mode, &default_acl, &acl, dpage);
 388        if (error)
 389                return error;
 390
 391        if (default_acl) {
 392                error = __f2fs_set_acl(inode, ACL_TYPE_DEFAULT, default_acl,
 393                                       ipage);
 394                posix_acl_release(default_acl);
 395        }
 396        if (acl) {
 397                if (!error)
 398                        error = __f2fs_set_acl(inode, ACL_TYPE_ACCESS, acl,
 399                                               ipage);
 400                posix_acl_release(acl);
 401        }
 402
 403        return error;
 404}
 405