aboutsummaryrefslogtreecommitdiffstats
path: root/fs/jffs2/acl.c
diff options
context:
space:
mode:
authorKaiGai Kohei <kaigai@ak.jp.nec.com>2006-05-13 02:09:47 -0400
committerKaiGai Kohei <kaigai@ak.jp.nec.com>2006-05-13 02:09:47 -0400
commitaa98d7cf59b5b0764d3502662053489585faf2fe (patch)
treee98e83f3e69ebe3a1112394a19d440419e899749 /fs/jffs2/acl.c
parent4992a9e88886b0c5ebc3d27eb74d0344c873eeea (diff)
[JFFS2][XATTR] XATTR support on JFFS2 (version. 5)
This attached patches provide xattr support including POSIX-ACL and SELinux support on JFFS2 (version.5). There are some significant differences from previous version posted at last December. The biggest change is addition of EBS(Erase Block Summary) support. Currently, both kernel and usermode utility (sumtool) can recognize xattr nodes which have JFFS2_NODETYPE_XATTR/_XREF nodetype. In addition, some bugs are fixed. - A potential race condition was fixed. - Unexpected fail when updating a xattr by same name/value pair was fixed. - A bug when removing xattr name/value pair was fixed. The fundamental structures (such as using two new nodetypes and exclusion mechanism by rwsem) are unchanged. But most of implementation were reviewed and updated if necessary. Espacially, we had to change several internal implementations related to load_xattr_datum() to avoid a potential race condition. [1/2] xattr_on_jffs2.kernel.version-5.patch [2/2] xattr_on_jffs2.utils.version-5.patch Signed-off-by: KaiGai Kohei <kaigai@ak.jp.nec.com> Signed-off-by: David Woodhouse <dwmw2@infradead.org>
Diffstat (limited to 'fs/jffs2/acl.c')
-rw-r--r--fs/jffs2/acl.c483
1 files changed, 483 insertions, 0 deletions
diff --git a/fs/jffs2/acl.c b/fs/jffs2/acl.c
new file mode 100644
index 000000000000..080bb51e4b65
--- /dev/null
+++ b/fs/jffs2/acl.c
@@ -0,0 +1,483 @@
1/*-------------------------------------------------------------------------*
2 * File: fs/jffs2/acl.c
3 * POSIX ACL support on JFFS2 FileSystem
4 *
5 * Implemented by KaiGai Kohei <kaigai@ak.jp.nec.com>
6 * Copyright (C) 2006 NEC Corporation
7 *
8 * For licensing information, see the file 'LICENCE' in the jffs2 directory.
9 *-------------------------------------------------------------------------*/
10#include <linux/kernel.h>
11#include <linux/slab.h>
12#include <linux/fs.h>
13#include <linux/time.h>
14#include <linux/crc32.h>
15#include <linux/jffs2.h>
16#include <linux/xattr.h>
17#include <linux/posix_acl_xattr.h>
18#include <linux/mtd/mtd.h>
19#include "nodelist.h"
20
21static size_t jffs2_acl_size(int count)
22{
23 if (count <= 4) {
24 return sizeof(jffs2_acl_header)
25 + count * sizeof(jffs2_acl_entry_short);
26 } else {
27 return sizeof(jffs2_acl_header)
28 + 4 * sizeof(jffs2_acl_entry_short)
29 + (count - 4) * sizeof(jffs2_acl_entry);
30 }
31}
32
33static int jffs2_acl_count(size_t size)
34{
35 size_t s;
36
37 size -= sizeof(jffs2_acl_header);
38 s = size - 4 * sizeof(jffs2_acl_entry_short);
39 if (s < 0) {
40 if (size % sizeof(jffs2_acl_entry_short))
41 return -1;
42 return size / sizeof(jffs2_acl_entry_short);
43 } else {
44 if (s % sizeof(jffs2_acl_entry))
45 return -1;
46 return s / sizeof(jffs2_acl_entry) + 4;
47 }
48}
49
50static struct posix_acl *jffs2_acl_from_medium(const void *value, size_t size)
51{
52 const char *end = (char *)value + size;
53 struct posix_acl *acl;
54 uint32_t ver;
55 int i, count;
56
57 if (!value)
58 return NULL;
59 if (size < sizeof(jffs2_acl_header))
60 return ERR_PTR(-EINVAL);
61 ver = je32_to_cpu(((jffs2_acl_header *)value)->a_version);
62 if (ver != JFFS2_ACL_VERSION) {
63 JFFS2_WARNING("Invalid ACL version. (=%u)\n", ver);
64 return ERR_PTR(-EINVAL);
65 }
66
67 value = (char *)value + sizeof(jffs2_acl_header);
68 count = jffs2_acl_count(size);
69 if (count < 0)
70 return ERR_PTR(-EINVAL);
71 if (count == 0)
72 return NULL;
73
74 acl = posix_acl_alloc(count, GFP_KERNEL);
75 if (!acl)
76 return ERR_PTR(-ENOMEM);
77
78 for (i=0; i < count; i++) {
79 jffs2_acl_entry *entry = (jffs2_acl_entry *)value;
80 if ((char *)value + sizeof(jffs2_acl_entry_short) > end)
81 goto fail;
82 acl->a_entries[i].e_tag = je16_to_cpu(entry->e_tag);
83 acl->a_entries[i].e_perm = je16_to_cpu(entry->e_perm);
84 switch (acl->a_entries[i].e_tag) {
85 case ACL_USER_OBJ:
86 case ACL_GROUP_OBJ:
87 case ACL_MASK:
88 case ACL_OTHER:
89 value = (char *)value + sizeof(jffs2_acl_entry_short);
90 acl->a_entries[i].e_id = ACL_UNDEFINED_ID;
91 break;
92
93 case ACL_USER:
94 case ACL_GROUP:
95 value = (char *)value + sizeof(jffs2_acl_entry);
96 if ((char *)value > end)
97 goto fail;
98 acl->a_entries[i].e_id = je32_to_cpu(entry->e_id);
99 break;
100
101 default:
102 goto fail;
103 }
104 }
105 if (value != end)
106 goto fail;
107 return acl;
108 fail:
109 posix_acl_release(acl);
110 return ERR_PTR(-EINVAL);
111}
112
113static void *jffs2_acl_to_medium(const struct posix_acl *acl, size_t *size)
114{
115 jffs2_acl_header *jffs2_acl;
116 char *e;
117 size_t i;
118
119 *size = jffs2_acl_size(acl->a_count);
120 jffs2_acl = (jffs2_acl_header *)kmalloc(sizeof(jffs2_acl_header)
121 + acl->a_count * sizeof(jffs2_acl_entry),
122 GFP_KERNEL);
123 if (!jffs2_acl)
124 return ERR_PTR(-ENOMEM);
125 jffs2_acl->a_version = cpu_to_je32(JFFS2_ACL_VERSION);
126 e = (char *)jffs2_acl + sizeof(jffs2_acl_header);
127 for (i=0; i < acl->a_count; i++) {
128 jffs2_acl_entry *entry = (jffs2_acl_entry *)e;
129 entry->e_tag = cpu_to_je16(acl->a_entries[i].e_tag);
130 entry->e_perm = cpu_to_je16(acl->a_entries[i].e_perm);
131 switch(acl->a_entries[i].e_tag) {
132 case ACL_USER:
133 case ACL_GROUP:
134 entry->e_id = cpu_to_je32(acl->a_entries[i].e_id);
135 e += sizeof(jffs2_acl_entry);
136 break;
137
138 case ACL_USER_OBJ:
139 case ACL_GROUP_OBJ:
140 case ACL_MASK:
141 case ACL_OTHER:
142 e += sizeof(jffs2_acl_entry_short);
143 break;
144
145 default:
146 goto fail;
147 }
148 }
149 return (char *)jffs2_acl;
150 fail:
151 kfree(jffs2_acl);
152 return ERR_PTR(-EINVAL);
153}
154
155static struct posix_acl *jffs2_iget_acl(struct inode *inode, struct posix_acl **i_acl)
156{
157 struct posix_acl *acl = JFFS2_ACL_NOT_CACHED;
158
159 spin_lock(&inode->i_lock);
160 if (*i_acl != JFFS2_ACL_NOT_CACHED)
161 acl = posix_acl_dup(*i_acl);
162 spin_unlock(&inode->i_lock);
163 return acl;
164}
165
166static void jffs2_iset_acl(struct inode *inode, struct posix_acl **i_acl, struct posix_acl *acl)
167{
168 spin_lock(&inode->i_lock);
169 if (*i_acl != JFFS2_ACL_NOT_CACHED)
170 posix_acl_release(*i_acl);
171 *i_acl = posix_acl_dup(acl);
172 spin_unlock(&inode->i_lock);
173}
174
175static struct posix_acl *jffs2_get_acl(struct inode *inode, int type)
176{
177 struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode);
178 struct posix_acl *acl;
179 char *value = NULL;
180 int rc, xprefix;
181
182 switch (type) {
183 case ACL_TYPE_ACCESS:
184 acl = jffs2_iget_acl(inode, &f->i_acl_access);
185 if (acl != JFFS2_ACL_NOT_CACHED)
186 return acl;
187 xprefix = JFFS2_XPREFIX_ACL_ACCESS;
188 break;
189 case ACL_TYPE_DEFAULT:
190 acl = jffs2_iget_acl(inode, &f->i_acl_default);
191 if (acl != JFFS2_ACL_NOT_CACHED)
192 return acl;
193 xprefix = JFFS2_XPREFIX_ACL_DEFAULT;
194 break;
195 default:
196 return ERR_PTR(-EINVAL);
197 }
198 rc = do_jffs2_getxattr(inode, xprefix, "", NULL, 0);
199 if (rc > 0) {
200 value = kmalloc(rc, GFP_KERNEL);
201 if (!value)
202 return ERR_PTR(-ENOMEM);
203 rc = do_jffs2_getxattr(inode, xprefix, "", value, rc);
204 }
205 if (rc > 0) {
206 acl = jffs2_acl_from_medium(value, rc);
207 } else if (rc == -ENODATA || rc == -ENOSYS) {
208 acl = NULL;
209 } else {
210 acl = ERR_PTR(rc);
211 }
212 if (value)
213 kfree(value);
214 if (!IS_ERR(acl)) {
215 switch (type) {
216 case ACL_TYPE_ACCESS:
217 jffs2_iset_acl(inode, &f->i_acl_access, acl);
218 break;
219 case ACL_TYPE_DEFAULT:
220 jffs2_iset_acl(inode, &f->i_acl_default, acl);
221 break;
222 }
223 }
224 return acl;
225}
226
227static int jffs2_set_acl(struct inode *inode, int type, struct posix_acl *acl)
228{
229 struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode);
230 size_t size = 0;
231 char *value = NULL;
232 int rc, xprefix;
233
234 if (S_ISLNK(inode->i_mode))
235 return -EOPNOTSUPP;
236
237 switch (type) {
238 case ACL_TYPE_ACCESS:
239 xprefix = JFFS2_XPREFIX_ACL_ACCESS;
240 if (acl) {
241 mode_t mode = inode->i_mode;
242 rc = posix_acl_equiv_mode(acl, &mode);
243 if (rc < 0)
244 return rc;
245 if (inode->i_mode != mode) {
246 inode->i_mode = mode;
247 jffs2_dirty_inode(inode);
248 }
249 if (rc == 0)
250 acl = NULL;
251 }
252 break;
253 case ACL_TYPE_DEFAULT:
254 xprefix = JFFS2_XPREFIX_ACL_DEFAULT;
255 if (!S_ISDIR(inode->i_mode))
256 return acl ? -EACCES : 0;
257 break;
258 default:
259 return -EINVAL;
260 }
261 if (acl) {
262 value = jffs2_acl_to_medium(acl, &size);
263 if (IS_ERR(value))
264 return PTR_ERR(value);
265 }
266
267 rc = do_jffs2_setxattr(inode, xprefix, "", value, size, 0);
268 if (value)
269 kfree(value);
270 if (!rc) {
271 switch(type) {
272 case ACL_TYPE_ACCESS:
273 jffs2_iset_acl(inode, &f->i_acl_access, acl);
274 break;
275 case ACL_TYPE_DEFAULT:
276 jffs2_iset_acl(inode, &f->i_acl_default, acl);
277 break;
278 }
279 }
280 return rc;
281}
282
283static int jffs2_check_acl(struct inode *inode, int mask)
284{
285 struct posix_acl *acl;
286 int rc;
287
288 acl = jffs2_get_acl(inode, ACL_TYPE_ACCESS);
289 if (IS_ERR(acl))
290 return PTR_ERR(acl);
291 if (acl) {
292 rc = posix_acl_permission(inode, acl, mask);
293 posix_acl_release(acl);
294 return rc;
295 }
296 return -EAGAIN;
297}
298
299int jffs2_permission(struct inode *inode, int mask, struct nameidata *nd)
300{
301 return generic_permission(inode, mask, jffs2_check_acl);
302}
303
304int jffs2_init_acl(struct inode *inode, struct inode *dir)
305{
306 struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode);
307 struct posix_acl *acl = NULL, *clone;
308 mode_t mode;
309 int rc = 0;
310
311 f->i_acl_access = JFFS2_ACL_NOT_CACHED;
312 f->i_acl_default = JFFS2_ACL_NOT_CACHED;
313 if (!S_ISLNK(inode->i_mode)) {
314 acl = jffs2_get_acl(dir, ACL_TYPE_DEFAULT);
315 if (IS_ERR(acl))
316 return PTR_ERR(acl);
317 if (!acl)
318 inode->i_mode &= ~current->fs->umask;
319 }
320 if (acl) {
321 if (S_ISDIR(inode->i_mode)) {
322 rc = jffs2_set_acl(inode, ACL_TYPE_DEFAULT, acl);
323 if (rc)
324 goto cleanup;
325 }
326 clone = posix_acl_clone(acl, GFP_KERNEL);
327 rc = -ENOMEM;
328 if (!clone)
329 goto cleanup;
330 mode = inode->i_mode;
331 rc = posix_acl_create_masq(clone, &mode);
332 if (rc >= 0) {
333 inode->i_mode = mode;
334 if (rc > 0)
335 rc = jffs2_set_acl(inode, ACL_TYPE_ACCESS, clone);
336 }
337 posix_acl_release(clone);
338 }
339 cleanup:
340 posix_acl_release(acl);
341 return rc;
342}
343
344void jffs2_clear_acl(struct inode *inode)
345{
346 struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode);
347
348 if (f->i_acl_access && f->i_acl_access != JFFS2_ACL_NOT_CACHED) {
349 posix_acl_release(f->i_acl_access);
350 f->i_acl_access = JFFS2_ACL_NOT_CACHED;
351 }
352 if (f->i_acl_default && f->i_acl_default != JFFS2_ACL_NOT_CACHED) {
353 posix_acl_release(f->i_acl_default);
354 f->i_acl_default = JFFS2_ACL_NOT_CACHED;
355 }
356}
357
358int jffs2_acl_chmod(struct inode *inode)
359{
360 struct posix_acl *acl, *clone;
361 int rc;
362
363 if (S_ISLNK(inode->i_mode))
364 return -EOPNOTSUPP;
365 acl = jffs2_get_acl(inode, ACL_TYPE_ACCESS);
366 if (IS_ERR(acl) || !acl)
367 return PTR_ERR(acl);
368 clone = posix_acl_clone(acl, GFP_KERNEL);
369 posix_acl_release(acl);
370 if (!clone)
371 return -ENOMEM;
372 rc = posix_acl_chmod_masq(clone, inode->i_mode);
373 if (!rc)
374 rc = jffs2_set_acl(inode, ACL_TYPE_ACCESS, clone);
375 posix_acl_release(clone);
376 return rc;
377}
378
379static size_t jffs2_acl_access_listxattr(struct inode *inode, char *list, size_t list_size,
380 const char *name, size_t name_len)
381{
382 const int retlen = sizeof(POSIX_ACL_XATTR_ACCESS);
383
384 if (list && retlen <= list_size)
385 strcpy(list, POSIX_ACL_XATTR_ACCESS);
386 return retlen;
387}
388
389static size_t jffs2_acl_default_listxattr(struct inode *inode, char *list, size_t list_size,
390 const char *name, size_t name_len)
391{
392 const int retlen = sizeof(POSIX_ACL_XATTR_DEFAULT);
393
394 if (list && retlen <= list_size)
395 strcpy(list, POSIX_ACL_XATTR_DEFAULT);
396 return retlen;
397}
398
399static int jffs2_acl_getxattr(struct inode *inode, int type, void *buffer, size_t size)
400{
401 struct posix_acl *acl;
402 int rc;
403
404 acl = jffs2_get_acl(inode, type);
405 if (IS_ERR(acl))
406 return PTR_ERR(acl);
407 if (!acl)
408 return -ENODATA;
409 rc = posix_acl_to_xattr(acl, buffer, size);
410 posix_acl_release(acl);
411
412 return rc;
413}
414
415static int jffs2_acl_access_getxattr(struct inode *inode, const char *name, void *buffer, size_t size)
416{
417 if (name[0] != '\0')
418 return -EINVAL;
419 return jffs2_acl_getxattr(inode, ACL_TYPE_ACCESS, buffer, size);
420}
421
422static int jffs2_acl_default_getxattr(struct inode *inode, const char *name, void *buffer, size_t size)
423{
424 if (name[0] != '\0')
425 return -EINVAL;
426 return jffs2_acl_getxattr(inode, ACL_TYPE_DEFAULT, buffer, size);
427}
428
429static int jffs2_acl_setxattr(struct inode *inode, int type, const void *value, size_t size)
430{
431 struct posix_acl *acl;
432 int rc;
433
434 if ((current->fsuid != inode->i_uid) && !capable(CAP_FOWNER))
435 return -EPERM;
436
437 if (value) {
438 acl = posix_acl_from_xattr(value, size);
439 if (IS_ERR(acl))
440 return PTR_ERR(acl);
441 if (acl) {
442 rc = posix_acl_valid(acl);
443 if (rc)
444 goto out;
445 }
446 } else {
447 acl = NULL;
448 }
449 rc = jffs2_set_acl(inode, type, acl);
450 out:
451 posix_acl_release(acl);
452 return rc;
453}
454
455static int jffs2_acl_access_setxattr(struct inode *inode, const char *name,
456 const void *buffer, size_t size, int flags)
457{
458 if (name[0] != '\0')
459 return -EINVAL;
460 return jffs2_acl_setxattr(inode, ACL_TYPE_ACCESS, buffer, size);
461}
462
463static int jffs2_acl_default_setxattr(struct inode *inode, const char *name,
464 const void *buffer, size_t size, int flags)
465{
466 if (name[0] != '\0')
467 return -EINVAL;
468 return jffs2_acl_setxattr(inode, ACL_TYPE_DEFAULT, buffer, size);
469}
470
471struct xattr_handler jffs2_acl_access_xattr_handler = {
472 .prefix = POSIX_ACL_XATTR_ACCESS,
473 .list = jffs2_acl_access_listxattr,
474 .get = jffs2_acl_access_getxattr,
475 .set = jffs2_acl_access_setxattr,
476};
477
478struct xattr_handler jffs2_acl_default_xattr_handler = {
479 .prefix = POSIX_ACL_XATTR_DEFAULT,
480 .list = jffs2_acl_default_listxattr,
481 .get = jffs2_acl_default_getxattr,
482 .set = jffs2_acl_default_setxattr,
483};