linux/fs/jffs2/acl.c
<<
>>
Prefs
   1/*
   2 * JFFS2 -- Journalling Flash File System, Version 2.
   3 *
   4 * Copyright © 2006  NEC Corporation
   5 *
   6 * Created by KaiGai Kohei <kaigai@ak.jp.nec.com>
   7 *
   8 * For licensing information, see the file 'LICENCE' in this directory.
   9 *
  10 */
  11
  12#include <linux/kernel.h>
  13#include <linux/slab.h>
  14#include <linux/fs.h>
  15#include <linux/sched.h>
  16#include <linux/time.h>
  17#include <linux/crc32.h>
  18#include <linux/jffs2.h>
  19#include <linux/xattr.h>
  20#include <linux/posix_acl_xattr.h>
  21#include <linux/mtd/mtd.h>
  22#include "nodelist.h"
  23
  24static size_t jffs2_acl_size(int count)
  25{
  26        if (count <= 4) {
  27                return sizeof(struct jffs2_acl_header)
  28                       + count * sizeof(struct jffs2_acl_entry_short);
  29        } else {
  30                return sizeof(struct jffs2_acl_header)
  31                       + 4 * sizeof(struct jffs2_acl_entry_short)
  32                       + (count - 4) * sizeof(struct jffs2_acl_entry);
  33        }
  34}
  35
  36static int jffs2_acl_count(size_t size)
  37{
  38        size_t s;
  39
  40        size -= sizeof(struct jffs2_acl_header);
  41        if (size < 4 * sizeof(struct jffs2_acl_entry_short)) {
  42                if (size % sizeof(struct jffs2_acl_entry_short))
  43                        return -1;
  44                return size / sizeof(struct jffs2_acl_entry_short);
  45        } else {
  46                s = size - 4 * sizeof(struct jffs2_acl_entry_short);
  47                if (s % sizeof(struct jffs2_acl_entry))
  48                        return -1;
  49                return s / sizeof(struct jffs2_acl_entry) + 4;
  50        }
  51}
  52
  53static struct posix_acl *jffs2_acl_from_medium(void *value, size_t size)
  54{
  55        void *end = value + size;
  56        struct jffs2_acl_header *header = value;
  57        struct jffs2_acl_entry *entry;
  58        struct posix_acl *acl;
  59        uint32_t ver;
  60        int i, count;
  61
  62        if (!value)
  63                return NULL;
  64        if (size < sizeof(struct jffs2_acl_header))
  65                return ERR_PTR(-EINVAL);
  66        ver = je32_to_cpu(header->a_version);
  67        if (ver != JFFS2_ACL_VERSION) {
  68                JFFS2_WARNING("Invalid ACL version. (=%u)\n", ver);
  69                return ERR_PTR(-EINVAL);
  70        }
  71
  72        value += sizeof(struct jffs2_acl_header);
  73        count = jffs2_acl_count(size);
  74        if (count < 0)
  75                return ERR_PTR(-EINVAL);
  76        if (count == 0)
  77                return NULL;
  78
  79        acl = posix_acl_alloc(count, GFP_KERNEL);
  80        if (!acl)
  81                return ERR_PTR(-ENOMEM);
  82
  83        for (i=0; i < count; i++) {
  84                entry = value;
  85                if (value + sizeof(struct jffs2_acl_entry_short) > end)
  86                        goto fail;
  87                acl->a_entries[i].e_tag = je16_to_cpu(entry->e_tag);
  88                acl->a_entries[i].e_perm = je16_to_cpu(entry->e_perm);
  89                switch (acl->a_entries[i].e_tag) {
  90                        case ACL_USER_OBJ:
  91                        case ACL_GROUP_OBJ:
  92                        case ACL_MASK:
  93                        case ACL_OTHER:
  94                                value += sizeof(struct jffs2_acl_entry_short);
  95                                acl->a_entries[i].e_id = ACL_UNDEFINED_ID;
  96                                break;
  97
  98                        case ACL_USER:
  99                        case ACL_GROUP:
 100                                value += sizeof(struct jffs2_acl_entry);
 101                                if (value > end)
 102                                        goto fail;
 103                                acl->a_entries[i].e_id = je32_to_cpu(entry->e_id);
 104                                break;
 105
 106                        default:
 107                                goto fail;
 108                }
 109        }
 110        if (value != end)
 111                goto fail;
 112        return acl;
 113 fail:
 114        posix_acl_release(acl);
 115        return ERR_PTR(-EINVAL);
 116}
 117
 118static void *jffs2_acl_to_medium(const struct posix_acl *acl, size_t *size)
 119{
 120        struct jffs2_acl_header *header;
 121        struct jffs2_acl_entry *entry;
 122        void *e;
 123        size_t i;
 124
 125        *size = jffs2_acl_size(acl->a_count);
 126        header = kmalloc(sizeof(*header) + acl->a_count * sizeof(*entry), GFP_KERNEL);
 127        if (!header)
 128                return ERR_PTR(-ENOMEM);
 129        header->a_version = cpu_to_je32(JFFS2_ACL_VERSION);
 130        e = header + 1;
 131        for (i=0; i < acl->a_count; i++) {
 132                entry = e;
 133                entry->e_tag = cpu_to_je16(acl->a_entries[i].e_tag);
 134                entry->e_perm = cpu_to_je16(acl->a_entries[i].e_perm);
 135                switch(acl->a_entries[i].e_tag) {
 136                        case ACL_USER:
 137                        case ACL_GROUP:
 138                                entry->e_id = cpu_to_je32(acl->a_entries[i].e_id);
 139                                e += sizeof(struct jffs2_acl_entry);
 140                                break;
 141
 142                        case ACL_USER_OBJ:
 143                        case ACL_GROUP_OBJ:
 144                        case ACL_MASK:
 145                        case ACL_OTHER:
 146                                e += sizeof(struct jffs2_acl_entry_short);
 147                                break;
 148
 149                        default:
 150                                goto fail;
 151                }
 152        }
 153        return header;
 154 fail:
 155        kfree(header);
 156        return ERR_PTR(-EINVAL);
 157}
 158
 159static struct posix_acl *jffs2_get_acl(struct inode *inode, int type)
 160{
 161        struct posix_acl *acl;
 162        char *value = NULL;
 163        int rc, xprefix;
 164
 165        acl = get_cached_acl(inode, type);
 166        if (acl != ACL_NOT_CACHED)
 167                return acl;
 168
 169        switch (type) {
 170        case ACL_TYPE_ACCESS:
 171                xprefix = JFFS2_XPREFIX_ACL_ACCESS;
 172                break;
 173        case ACL_TYPE_DEFAULT:
 174                xprefix = JFFS2_XPREFIX_ACL_DEFAULT;
 175                break;
 176        default:
 177                BUG();
 178        }
 179        rc = do_jffs2_getxattr(inode, xprefix, "", NULL, 0);
 180        if (rc > 0) {
 181                value = kmalloc(rc, GFP_KERNEL);
 182                if (!value)
 183                        return ERR_PTR(-ENOMEM);
 184                rc = do_jffs2_getxattr(inode, xprefix, "", value, rc);
 185        }
 186        if (rc > 0) {
 187                acl = jffs2_acl_from_medium(value, rc);
 188        } else if (rc == -ENODATA || rc == -ENOSYS) {
 189                acl = NULL;
 190        } else {
 191                acl = ERR_PTR(rc);
 192        }
 193        if (value)
 194                kfree(value);
 195        if (!IS_ERR(acl))
 196                set_cached_acl(inode, type, acl);
 197        return acl;
 198}
 199
 200static int __jffs2_set_acl(struct inode *inode, int xprefix, struct posix_acl *acl)
 201{
 202        char *value = NULL;
 203        size_t size = 0;
 204        int rc;
 205
 206        if (acl) {
 207                value = jffs2_acl_to_medium(acl, &size);
 208                if (IS_ERR(value))
 209                        return PTR_ERR(value);
 210        }
 211        rc = do_jffs2_setxattr(inode, xprefix, "", value, size, 0);
 212        if (!value && rc == -ENODATA)
 213                rc = 0;
 214        kfree(value);
 215
 216        return rc;
 217}
 218
 219static int jffs2_set_acl(struct inode *inode, int type, struct posix_acl *acl)
 220{
 221        int rc, xprefix;
 222
 223        if (S_ISLNK(inode->i_mode))
 224                return -EOPNOTSUPP;
 225
 226        switch (type) {
 227        case ACL_TYPE_ACCESS:
 228                xprefix = JFFS2_XPREFIX_ACL_ACCESS;
 229                if (acl) {
 230                        mode_t mode = inode->i_mode;
 231                        rc = posix_acl_equiv_mode(acl, &mode);
 232                        if (rc < 0)
 233                                return rc;
 234                        if (inode->i_mode != mode) {
 235                                struct iattr attr;
 236
 237                                attr.ia_valid = ATTR_MODE;
 238                                attr.ia_mode = mode;
 239                                rc = jffs2_do_setattr(inode, &attr);
 240                                if (rc < 0)
 241                                        return rc;
 242                        }
 243                        if (rc == 0)
 244                                acl = NULL;
 245                }
 246                break;
 247        case ACL_TYPE_DEFAULT:
 248                xprefix = JFFS2_XPREFIX_ACL_DEFAULT;
 249                if (!S_ISDIR(inode->i_mode))
 250                        return acl ? -EACCES : 0;
 251                break;
 252        default:
 253                return -EINVAL;
 254        }
 255        rc = __jffs2_set_acl(inode, xprefix, acl);
 256        if (!rc)
 257                set_cached_acl(inode, type, acl);
 258        return rc;
 259}
 260
 261int jffs2_check_acl(struct inode *inode, int mask)
 262{
 263        struct posix_acl *acl;
 264        int rc;
 265
 266        acl = jffs2_get_acl(inode, ACL_TYPE_ACCESS);
 267        if (IS_ERR(acl))
 268                return PTR_ERR(acl);
 269        if (acl) {
 270                rc = posix_acl_permission(inode, acl, mask);
 271                posix_acl_release(acl);
 272                return rc;
 273        }
 274        return -EAGAIN;
 275}
 276
 277int jffs2_init_acl_pre(struct inode *dir_i, struct inode *inode, int *i_mode)
 278{
 279        struct posix_acl *acl, *clone;
 280        int rc;
 281
 282        cache_no_acl(inode);
 283
 284        if (S_ISLNK(*i_mode))
 285                return 0;       /* Symlink always has no-ACL */
 286
 287        acl = jffs2_get_acl(dir_i, ACL_TYPE_DEFAULT);
 288        if (IS_ERR(acl))
 289                return PTR_ERR(acl);
 290
 291        if (!acl) {
 292                *i_mode &= ~current_umask();
 293        } else {
 294                if (S_ISDIR(*i_mode))
 295                        set_cached_acl(inode, ACL_TYPE_DEFAULT, acl);
 296
 297                clone = posix_acl_clone(acl, GFP_KERNEL);
 298                if (!clone)
 299                        return -ENOMEM;
 300                rc = posix_acl_create_masq(clone, (mode_t *)i_mode);
 301                if (rc < 0) {
 302                        posix_acl_release(clone);
 303                        return rc;
 304                }
 305                if (rc > 0)
 306                        set_cached_acl(inode, ACL_TYPE_ACCESS, clone);
 307
 308                posix_acl_release(clone);
 309        }
 310        return 0;
 311}
 312
 313int jffs2_init_acl_post(struct inode *inode)
 314{
 315        int rc;
 316
 317        if (inode->i_default_acl) {
 318                rc = __jffs2_set_acl(inode, JFFS2_XPREFIX_ACL_DEFAULT, inode->i_default_acl);
 319                if (rc)
 320                        return rc;
 321        }
 322
 323        if (inode->i_acl) {
 324                rc = __jffs2_set_acl(inode, JFFS2_XPREFIX_ACL_ACCESS, inode->i_acl);
 325                if (rc)
 326                        return rc;
 327        }
 328
 329        return 0;
 330}
 331
 332int jffs2_acl_chmod(struct inode *inode)
 333{
 334        struct posix_acl *acl, *clone;
 335        int rc;
 336
 337        if (S_ISLNK(inode->i_mode))
 338                return -EOPNOTSUPP;
 339        acl = jffs2_get_acl(inode, ACL_TYPE_ACCESS);
 340        if (IS_ERR(acl) || !acl)
 341                return PTR_ERR(acl);
 342        clone = posix_acl_clone(acl, GFP_KERNEL);
 343        posix_acl_release(acl);
 344        if (!clone)
 345                return -ENOMEM;
 346        rc = posix_acl_chmod_masq(clone, inode->i_mode);
 347        if (!rc)
 348                rc = jffs2_set_acl(inode, ACL_TYPE_ACCESS, clone);
 349        posix_acl_release(clone);
 350        return rc;
 351}
 352
 353static size_t jffs2_acl_access_listxattr(struct inode *inode, char *list, size_t list_size,
 354                                         const char *name, size_t name_len)
 355{
 356        const int retlen = sizeof(POSIX_ACL_XATTR_ACCESS);
 357
 358        if (list && retlen <= list_size)
 359                strcpy(list, POSIX_ACL_XATTR_ACCESS);
 360        return retlen;
 361}
 362
 363static size_t jffs2_acl_default_listxattr(struct inode *inode, char *list, size_t list_size,
 364                                          const char *name, size_t name_len)
 365{
 366        const int retlen = sizeof(POSIX_ACL_XATTR_DEFAULT);
 367
 368        if (list && retlen <= list_size)
 369                strcpy(list, POSIX_ACL_XATTR_DEFAULT);
 370        return retlen;
 371}
 372
 373static int jffs2_acl_getxattr(struct inode *inode, int type, void *buffer, size_t size)
 374{
 375        struct posix_acl *acl;
 376        int rc;
 377
 378        acl = jffs2_get_acl(inode, type);
 379        if (IS_ERR(acl))
 380                return PTR_ERR(acl);
 381        if (!acl)
 382                return -ENODATA;
 383        rc = posix_acl_to_xattr(acl, buffer, size);
 384        posix_acl_release(acl);
 385
 386        return rc;
 387}
 388
 389static int jffs2_acl_access_getxattr(struct inode *inode, const char *name, void *buffer, size_t size)
 390{
 391        if (name[0] != '\0')
 392                return -EINVAL;
 393        return jffs2_acl_getxattr(inode, ACL_TYPE_ACCESS, buffer, size);
 394}
 395
 396static int jffs2_acl_default_getxattr(struct inode *inode, const char *name, void *buffer, size_t size)
 397{
 398        if (name[0] != '\0')
 399                return -EINVAL;
 400        return jffs2_acl_getxattr(inode, ACL_TYPE_DEFAULT, buffer, size);
 401}
 402
 403static int jffs2_acl_setxattr(struct inode *inode, int type, const void *value, size_t size)
 404{
 405        struct posix_acl *acl;
 406        int rc;
 407
 408        if (!is_owner_or_cap(inode))
 409                return -EPERM;
 410
 411        if (value) {
 412                acl = posix_acl_from_xattr(value, size);
 413                if (IS_ERR(acl))
 414                        return PTR_ERR(acl);
 415                if (acl) {
 416                        rc = posix_acl_valid(acl);
 417                        if (rc)
 418                                goto out;
 419                }
 420        } else {
 421                acl = NULL;
 422        }
 423        rc = jffs2_set_acl(inode, type, acl);
 424 out:
 425        posix_acl_release(acl);
 426        return rc;
 427}
 428
 429static int jffs2_acl_access_setxattr(struct inode *inode, const char *name,
 430                                     const void *buffer, size_t size, int flags)
 431{
 432        if (name[0] != '\0')
 433                return -EINVAL;
 434        return jffs2_acl_setxattr(inode, ACL_TYPE_ACCESS, buffer, size);
 435}
 436
 437static int jffs2_acl_default_setxattr(struct inode *inode, const char *name,
 438                                      const void *buffer, size_t size, int flags)
 439{
 440        if (name[0] != '\0')
 441                return -EINVAL;
 442        return jffs2_acl_setxattr(inode, ACL_TYPE_DEFAULT, buffer, size);
 443}
 444
 445struct xattr_handler jffs2_acl_access_xattr_handler = {
 446        .prefix = POSIX_ACL_XATTR_ACCESS,
 447        .list   = jffs2_acl_access_listxattr,
 448        .get    = jffs2_acl_access_getxattr,
 449        .set    = jffs2_acl_access_setxattr,
 450};
 451
 452struct xattr_handler jffs2_acl_default_xattr_handler = {
 453        .prefix = POSIX_ACL_XATTR_DEFAULT,
 454        .list   = jffs2_acl_default_listxattr,
 455        .get    = jffs2_acl_default_getxattr,
 456        .set    = jffs2_acl_default_setxattr,
 457};
 458