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