linux/fs/f2fs/acl.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * fs/f2fs/acl.c
   4 *
   5 * Copyright (c) 2012 Samsung Electronics Co., Ltd.
   6 *             http://www.samsung.com/
   7 *
   8 * Portions of this code from linux/fs/ext2/acl.c
   9 *
  10 * Copyright (C) 2001-2003 Andreas Gruenbacher, <agruen@suse.de>
  11 */
  12#include <linux/f2fs_fs.h>
  13#include "f2fs.h"
  14#include "xattr.h"
  15#include "acl.h"
  16
  17static inline size_t f2fs_acl_size(int count)
  18{
  19        if (count <= 4) {
  20                return sizeof(struct f2fs_acl_header) +
  21                        count * sizeof(struct f2fs_acl_entry_short);
  22        } else {
  23                return sizeof(struct f2fs_acl_header) +
  24                        4 * sizeof(struct f2fs_acl_entry_short) +
  25                        (count - 4) * sizeof(struct f2fs_acl_entry);
  26        }
  27}
  28
  29static inline int f2fs_acl_count(size_t size)
  30{
  31        ssize_t s;
  32        size -= sizeof(struct f2fs_acl_header);
  33        s = size - 4 * sizeof(struct f2fs_acl_entry_short);
  34        if (s < 0) {
  35                if (size % sizeof(struct f2fs_acl_entry_short))
  36                        return -1;
  37                return size / sizeof(struct f2fs_acl_entry_short);
  38        } else {
  39                if (s % sizeof(struct f2fs_acl_entry))
  40                        return -1;
  41                return s / sizeof(struct f2fs_acl_entry) + 4;
  42        }
  43}
  44
  45static struct posix_acl *f2fs_acl_from_disk(const char *value, size_t size)
  46{
  47        int i, count;
  48        struct posix_acl *acl;
  49        struct f2fs_acl_header *hdr = (struct f2fs_acl_header *)value;
  50        struct f2fs_acl_entry *entry = (struct f2fs_acl_entry *)(hdr + 1);
  51        const char *end = value + size;
  52
  53        if (size < sizeof(struct f2fs_acl_header))
  54                return ERR_PTR(-EINVAL);
  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(struct f2fs_sb_info *sbi,
 113                                const struct posix_acl *acl, size_t *size)
 114{
 115        struct f2fs_acl_header *f2fs_acl;
 116        struct f2fs_acl_entry *entry;
 117        int i;
 118
 119        f2fs_acl = f2fs_kmalloc(sbi, sizeof(struct f2fs_acl_header) +
 120                        acl->a_count * sizeof(struct f2fs_acl_entry),
 121                        GFP_NOFS);
 122        if (!f2fs_acl)
 123                return ERR_PTR(-ENOMEM);
 124
 125        f2fs_acl->a_version = cpu_to_le32(F2FS_ACL_VERSION);
 126        entry = (struct f2fs_acl_entry *)(f2fs_acl + 1);
 127
 128        for (i = 0; i < acl->a_count; i++) {
 129
 130                entry->e_tag  = cpu_to_le16(acl->a_entries[i].e_tag);
 131                entry->e_perm = cpu_to_le16(acl->a_entries[i].e_perm);
 132
 133                switch (acl->a_entries[i].e_tag) {
 134                case ACL_USER:
 135                        entry->e_id = cpu_to_le32(
 136                                        from_kuid(&init_user_ns,
 137                                                acl->a_entries[i].e_uid));
 138                        entry = (struct f2fs_acl_entry *)((char *)entry +
 139                                        sizeof(struct f2fs_acl_entry));
 140                        break;
 141                case ACL_GROUP:
 142                        entry->e_id = cpu_to_le32(
 143                                        from_kgid(&init_user_ns,
 144                                                acl->a_entries[i].e_gid));
 145                        entry = (struct f2fs_acl_entry *)((char *)entry +
 146                                        sizeof(struct f2fs_acl_entry));
 147                        break;
 148                case ACL_USER_OBJ:
 149                case ACL_GROUP_OBJ:
 150                case ACL_MASK:
 151                case ACL_OTHER:
 152                        entry = (struct f2fs_acl_entry *)((char *)entry +
 153                                        sizeof(struct f2fs_acl_entry_short));
 154                        break;
 155                default:
 156                        goto fail;
 157                }
 158        }
 159        *size = f2fs_acl_size(acl->a_count);
 160        return (void *)f2fs_acl;
 161
 162fail:
 163        kvfree(f2fs_acl);
 164        return ERR_PTR(-EINVAL);
 165}
 166
 167static struct posix_acl *__f2fs_get_acl(struct inode *inode, int type,
 168                                                struct page *dpage)
 169{
 170        int name_index = F2FS_XATTR_INDEX_POSIX_ACL_DEFAULT;
 171        void *value = NULL;
 172        struct posix_acl *acl;
 173        int retval;
 174
 175        if (type == ACL_TYPE_ACCESS)
 176                name_index = F2FS_XATTR_INDEX_POSIX_ACL_ACCESS;
 177
 178        retval = f2fs_getxattr(inode, name_index, "", NULL, 0, dpage);
 179        if (retval > 0) {
 180                value = f2fs_kmalloc(F2FS_I_SB(inode), retval, GFP_F2FS_ZERO);
 181                if (!value)
 182                        return ERR_PTR(-ENOMEM);
 183                retval = f2fs_getxattr(inode, name_index, "", value,
 184                                                        retval, dpage);
 185        }
 186
 187        if (retval > 0)
 188                acl = f2fs_acl_from_disk(value, retval);
 189        else if (retval == -ENODATA)
 190                acl = NULL;
 191        else
 192                acl = ERR_PTR(retval);
 193        kvfree(value);
 194
 195        return acl;
 196}
 197
 198struct posix_acl *f2fs_get_acl(struct inode *inode, int type)
 199{
 200        return __f2fs_get_acl(inode, type, NULL);
 201}
 202
 203static int __f2fs_set_acl(struct inode *inode, int type,
 204                        struct posix_acl *acl, struct page *ipage)
 205{
 206        int name_index;
 207        void *value = NULL;
 208        size_t size = 0;
 209        int error;
 210        umode_t mode = inode->i_mode;
 211
 212        switch (type) {
 213        case ACL_TYPE_ACCESS:
 214                name_index = F2FS_XATTR_INDEX_POSIX_ACL_ACCESS;
 215                if (acl && !ipage) {
 216                        error = posix_acl_update_mode(inode, &mode, &acl);
 217                        if (error)
 218                                return error;
 219                        set_acl_inode(inode, mode);
 220                }
 221                break;
 222
 223        case ACL_TYPE_DEFAULT:
 224                name_index = F2FS_XATTR_INDEX_POSIX_ACL_DEFAULT;
 225                if (!S_ISDIR(inode->i_mode))
 226                        return acl ? -EACCES : 0;
 227                break;
 228
 229        default:
 230                return -EINVAL;
 231        }
 232
 233        if (acl) {
 234                value = f2fs_acl_to_disk(F2FS_I_SB(inode), acl, &size);
 235                if (IS_ERR(value)) {
 236                        clear_inode_flag(inode, FI_ACL_MODE);
 237                        return PTR_ERR(value);
 238                }
 239        }
 240
 241        error = f2fs_setxattr(inode, name_index, "", value, size, ipage, 0);
 242
 243        kvfree(value);
 244        if (!error)
 245                set_cached_acl(inode, type, acl);
 246
 247        clear_inode_flag(inode, FI_ACL_MODE);
 248        return error;
 249}
 250
 251int f2fs_set_acl(struct inode *inode, struct posix_acl *acl, int type)
 252{
 253        if (unlikely(f2fs_cp_error(F2FS_I_SB(inode))))
 254                return -EIO;
 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                        refcount_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                ret = -ENOMEM;
 357                goto release_acl;
 358        }
 359
 360        ret = f2fs_acl_create_masq(clone, mode);
 361        if (ret < 0)
 362                goto release_clone;
 363
 364        if (ret == 0)
 365                posix_acl_release(clone);
 366        else
 367                *acl = clone;
 368
 369        if (!S_ISDIR(*mode))
 370                posix_acl_release(p);
 371        else
 372                *default_acl = p;
 373
 374        return 0;
 375
 376release_clone:
 377        posix_acl_release(clone);
 378release_acl:
 379        posix_acl_release(p);
 380        return ret;
 381}
 382
 383int f2fs_init_acl(struct inode *inode, struct inode *dir, struct page *ipage,
 384                                                        struct page *dpage)
 385{
 386        struct posix_acl *default_acl = NULL, *acl = NULL;
 387        int error = 0;
 388
 389        error = f2fs_acl_create(dir, &inode->i_mode, &default_acl, &acl, dpage);
 390        if (error)
 391                return error;
 392
 393        f2fs_mark_inode_dirty_sync(inode, true);
 394
 395        if (default_acl) {
 396                error = __f2fs_set_acl(inode, ACL_TYPE_DEFAULT, default_acl,
 397                                       ipage);
 398                posix_acl_release(default_acl);
 399        } else {
 400                inode->i_default_acl = NULL;
 401        }
 402        if (acl) {
 403                if (!error)
 404                        error = __f2fs_set_acl(inode, ACL_TYPE_ACCESS, acl,
 405                                               ipage);
 406                posix_acl_release(acl);
 407        } else {
 408                inode->i_acl = NULL;
 409        }
 410
 411        return error;
 412}
 413