Root/fs/jffs2/acl.c

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
261static int 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_permission(struct inode *inode, int mask)
278{
279    return generic_permission(inode, mask, jffs2_check_acl);
280}
281
282int jffs2_init_acl_pre(struct inode *dir_i, struct inode *inode, int *i_mode)
283{
284    struct posix_acl *acl, *clone;
285    int rc;
286
287    cache_no_acl(inode);
288
289    if (S_ISLNK(*i_mode))
290        return 0; /* Symlink always has no-ACL */
291
292    acl = jffs2_get_acl(dir_i, ACL_TYPE_DEFAULT);
293    if (IS_ERR(acl))
294        return PTR_ERR(acl);
295
296    if (!acl) {
297        *i_mode &= ~current_umask();
298    } else {
299        if (S_ISDIR(*i_mode))
300            set_cached_acl(inode, ACL_TYPE_DEFAULT, acl);
301
302        clone = posix_acl_clone(acl, GFP_KERNEL);
303        if (!clone)
304            return -ENOMEM;
305        rc = posix_acl_create_masq(clone, (mode_t *)i_mode);
306        if (rc < 0) {
307            posix_acl_release(clone);
308            return rc;
309        }
310        if (rc > 0)
311            set_cached_acl(inode, ACL_TYPE_ACCESS, clone);
312
313        posix_acl_release(clone);
314    }
315    return 0;
316}
317
318int jffs2_init_acl_post(struct inode *inode)
319{
320    int rc;
321
322    if (inode->i_default_acl) {
323        rc = __jffs2_set_acl(inode, JFFS2_XPREFIX_ACL_DEFAULT, inode->i_default_acl);
324        if (rc)
325            return rc;
326    }
327
328    if (inode->i_acl) {
329        rc = __jffs2_set_acl(inode, JFFS2_XPREFIX_ACL_ACCESS, inode->i_acl);
330        if (rc)
331            return rc;
332    }
333
334    return 0;
335}
336
337int jffs2_acl_chmod(struct inode *inode)
338{
339    struct posix_acl *acl, *clone;
340    int rc;
341
342    if (S_ISLNK(inode->i_mode))
343        return -EOPNOTSUPP;
344    acl = jffs2_get_acl(inode, ACL_TYPE_ACCESS);
345    if (IS_ERR(acl) || !acl)
346        return PTR_ERR(acl);
347    clone = posix_acl_clone(acl, GFP_KERNEL);
348    posix_acl_release(acl);
349    if (!clone)
350        return -ENOMEM;
351    rc = posix_acl_chmod_masq(clone, inode->i_mode);
352    if (!rc)
353        rc = jffs2_set_acl(inode, ACL_TYPE_ACCESS, clone);
354    posix_acl_release(clone);
355    return rc;
356}
357
358static size_t jffs2_acl_access_listxattr(struct inode *inode, char *list, size_t list_size,
359                     const char *name, size_t name_len)
360{
361    const int retlen = sizeof(POSIX_ACL_XATTR_ACCESS);
362
363    if (list && retlen <= list_size)
364        strcpy(list, POSIX_ACL_XATTR_ACCESS);
365    return retlen;
366}
367
368static size_t jffs2_acl_default_listxattr(struct inode *inode, char *list, size_t list_size,
369                      const char *name, size_t name_len)
370{
371    const int retlen = sizeof(POSIX_ACL_XATTR_DEFAULT);
372
373    if (list && retlen <= list_size)
374        strcpy(list, POSIX_ACL_XATTR_DEFAULT);
375    return retlen;
376}
377
378static int jffs2_acl_getxattr(struct inode *inode, int type, void *buffer, size_t size)
379{
380    struct posix_acl *acl;
381    int rc;
382
383    acl = jffs2_get_acl(inode, type);
384    if (IS_ERR(acl))
385        return PTR_ERR(acl);
386    if (!acl)
387        return -ENODATA;
388    rc = posix_acl_to_xattr(acl, buffer, size);
389    posix_acl_release(acl);
390
391    return rc;
392}
393
394static int jffs2_acl_access_getxattr(struct inode *inode, const char *name, void *buffer, size_t size)
395{
396    if (name[0] != '\0')
397        return -EINVAL;
398    return jffs2_acl_getxattr(inode, ACL_TYPE_ACCESS, buffer, size);
399}
400
401static int jffs2_acl_default_getxattr(struct inode *inode, const char *name, void *buffer, size_t size)
402{
403    if (name[0] != '\0')
404        return -EINVAL;
405    return jffs2_acl_getxattr(inode, ACL_TYPE_DEFAULT, buffer, size);
406}
407
408static int jffs2_acl_setxattr(struct inode *inode, int type, const void *value, size_t size)
409{
410    struct posix_acl *acl;
411    int rc;
412
413    if (!is_owner_or_cap(inode))
414        return -EPERM;
415
416    if (value) {
417        acl = posix_acl_from_xattr(value, size);
418        if (IS_ERR(acl))
419            return PTR_ERR(acl);
420        if (acl) {
421            rc = posix_acl_valid(acl);
422            if (rc)
423                goto out;
424        }
425    } else {
426        acl = NULL;
427    }
428    rc = jffs2_set_acl(inode, type, acl);
429 out:
430    posix_acl_release(acl);
431    return rc;
432}
433
434static int jffs2_acl_access_setxattr(struct inode *inode, const char *name,
435                     const void *buffer, size_t size, int flags)
436{
437    if (name[0] != '\0')
438        return -EINVAL;
439    return jffs2_acl_setxattr(inode, ACL_TYPE_ACCESS, buffer, size);
440}
441
442static int jffs2_acl_default_setxattr(struct inode *inode, const char *name,
443                      const void *buffer, size_t size, int flags)
444{
445    if (name[0] != '\0')
446        return -EINVAL;
447    return jffs2_acl_setxattr(inode, ACL_TYPE_DEFAULT, buffer, size);
448}
449
450struct xattr_handler jffs2_acl_access_xattr_handler = {
451    .prefix = POSIX_ACL_XATTR_ACCESS,
452    .list = jffs2_acl_access_listxattr,
453    .get = jffs2_acl_access_getxattr,
454    .set = jffs2_acl_access_setxattr,
455};
456
457struct xattr_handler jffs2_acl_default_xattr_handler = {
458    .prefix = POSIX_ACL_XATTR_DEFAULT,
459    .list = jffs2_acl_default_listxattr,
460    .get = jffs2_acl_default_getxattr,
461    .set = jffs2_acl_default_setxattr,
462};
463

Archive Download this file



interactive