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 | if (count <= 4) { |
29 | return sizeof(struct jffs2_acl_header) |
30 | + count * sizeof(struct jffs2_acl_entry_short); |
31 | } else { |
32 | return sizeof(struct jffs2_acl_header) |
33 | + 4 * sizeof(struct jffs2_acl_entry_short) |
34 | + (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 | size -= sizeof(struct jffs2_acl_header); |
43 | if (size < 4 * sizeof(struct jffs2_acl_entry_short)) { |
44 | if (size % sizeof(struct jffs2_acl_entry_short)) |
45 | return -1; |
46 | return size / sizeof(struct jffs2_acl_entry_short); |
47 | } else { |
48 | s = size - 4 * sizeof(struct jffs2_acl_entry_short); |
49 | if (s % sizeof(struct jffs2_acl_entry)) |
50 | return -1; |
51 | return s / sizeof(struct jffs2_acl_entry) + 4; |
52 | } |
53 | } |
54 | |
55 | static struct posix_acl *jffs2_acl_from_medium(void *value, size_t size) |
56 | { |
57 | void *end = value + size; |
58 | struct jffs2_acl_header * = value; |
59 | struct jffs2_acl_entry *entry; |
60 | struct posix_acl *acl; |
61 | uint32_t ver; |
62 | int i, count; |
63 | |
64 | if (!value) |
65 | return NULL; |
66 | if (size < sizeof(struct jffs2_acl_header)) |
67 | return ERR_PTR(error: -EINVAL); |
68 | ver = je32_to_cpu(header->a_version); |
69 | if (ver != JFFS2_ACL_VERSION) { |
70 | JFFS2_WARNING("Invalid ACL version. (=%u)\n" , ver); |
71 | return ERR_PTR(error: -EINVAL); |
72 | } |
73 | |
74 | value += sizeof(struct jffs2_acl_header); |
75 | count = jffs2_acl_count(size); |
76 | if (count < 0) |
77 | return ERR_PTR(error: -EINVAL); |
78 | if (count == 0) |
79 | return NULL; |
80 | |
81 | acl = posix_acl_alloc(count, GFP_KERNEL); |
82 | if (!acl) |
83 | return ERR_PTR(error: -ENOMEM); |
84 | |
85 | for (i=0; i < count; i++) { |
86 | entry = value; |
87 | if (value + sizeof(struct jffs2_acl_entry_short) > end) |
88 | goto fail; |
89 | acl->a_entries[i].e_tag = je16_to_cpu(entry->e_tag); |
90 | acl->a_entries[i].e_perm = je16_to_cpu(entry->e_perm); |
91 | 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 | value += sizeof(struct jffs2_acl_entry); |
101 | if (value > end) |
102 | goto fail; |
103 | acl->a_entries[i].e_uid = |
104 | make_kuid(from: &init_user_ns, |
105 | je32_to_cpu(entry->e_id)); |
106 | break; |
107 | case ACL_GROUP: |
108 | value += sizeof(struct jffs2_acl_entry); |
109 | if (value > end) |
110 | goto fail; |
111 | acl->a_entries[i].e_gid = |
112 | make_kgid(from: &init_user_ns, |
113 | je32_to_cpu(entry->e_id)); |
114 | break; |
115 | |
116 | default: |
117 | goto fail; |
118 | } |
119 | } |
120 | if (value != end) |
121 | goto fail; |
122 | return acl; |
123 | fail: |
124 | posix_acl_release(acl); |
125 | return ERR_PTR(error: -EINVAL); |
126 | } |
127 | |
128 | static void *jffs2_acl_to_medium(const struct posix_acl *acl, size_t *size) |
129 | { |
130 | struct jffs2_acl_header *; |
131 | struct jffs2_acl_entry *entry; |
132 | void *e; |
133 | size_t i; |
134 | |
135 | *size = jffs2_acl_size(count: acl->a_count); |
136 | header = kmalloc(struct_size(header, a_entries, acl->a_count), |
137 | GFP_KERNEL); |
138 | if (!header) |
139 | return ERR_PTR(error: -ENOMEM); |
140 | header->a_version = cpu_to_je32(JFFS2_ACL_VERSION); |
141 | e = header + 1; |
142 | for (i=0; i < acl->a_count; i++) { |
143 | const struct posix_acl_entry *acl_e = &acl->a_entries[i]; |
144 | entry = e; |
145 | entry->e_tag = cpu_to_je16(acl_e->e_tag); |
146 | entry->e_perm = cpu_to_je16(acl_e->e_perm); |
147 | switch(acl_e->e_tag) { |
148 | case ACL_USER: |
149 | entry->e_id = cpu_to_je32( |
150 | from_kuid(&init_user_ns, acl_e->e_uid)); |
151 | e += sizeof(struct jffs2_acl_entry); |
152 | break; |
153 | case ACL_GROUP: |
154 | entry->e_id = cpu_to_je32( |
155 | from_kgid(&init_user_ns, acl_e->e_gid)); |
156 | e += sizeof(struct jffs2_acl_entry); |
157 | break; |
158 | |
159 | case ACL_USER_OBJ: |
160 | case ACL_GROUP_OBJ: |
161 | case ACL_MASK: |
162 | case ACL_OTHER: |
163 | e += sizeof(struct jffs2_acl_entry_short); |
164 | break; |
165 | |
166 | default: |
167 | goto fail; |
168 | } |
169 | } |
170 | return header; |
171 | fail: |
172 | kfree(objp: header); |
173 | return ERR_PTR(error: -EINVAL); |
174 | } |
175 | |
176 | struct posix_acl *jffs2_get_acl(struct inode *inode, int type, bool rcu) |
177 | { |
178 | struct posix_acl *acl; |
179 | char *value = NULL; |
180 | int rc, xprefix; |
181 | |
182 | if (rcu) |
183 | return ERR_PTR(error: -ECHILD); |
184 | |
185 | 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 | break; |
192 | default: |
193 | BUG(); |
194 | } |
195 | rc = do_jffs2_getxattr(inode, xprefix, xname: "" , NULL, size: 0); |
196 | if (rc > 0) { |
197 | value = kmalloc(size: rc, GFP_KERNEL); |
198 | if (!value) |
199 | return ERR_PTR(error: -ENOMEM); |
200 | rc = do_jffs2_getxattr(inode, xprefix, xname: "" , buffer: value, size: rc); |
201 | } |
202 | if (rc > 0) { |
203 | acl = jffs2_acl_from_medium(value, size: rc); |
204 | } else if (rc == -ENODATA || rc == -ENOSYS) { |
205 | acl = NULL; |
206 | } else { |
207 | acl = ERR_PTR(error: rc); |
208 | } |
209 | kfree(objp: value); |
210 | return acl; |
211 | } |
212 | |
213 | static int __jffs2_set_acl(struct inode *inode, int xprefix, struct posix_acl *acl) |
214 | { |
215 | char *value = NULL; |
216 | size_t size = 0; |
217 | int rc; |
218 | |
219 | if (acl) { |
220 | value = jffs2_acl_to_medium(acl, size: &size); |
221 | if (IS_ERR(ptr: value)) |
222 | return PTR_ERR(ptr: value); |
223 | } |
224 | rc = do_jffs2_setxattr(inode, xprefix, xname: "" , buffer: value, size, flags: 0); |
225 | if (!value && rc == -ENODATA) |
226 | rc = 0; |
227 | kfree(objp: value); |
228 | |
229 | return rc; |
230 | } |
231 | |
232 | int jffs2_set_acl(struct mnt_idmap *idmap, struct dentry *dentry, |
233 | struct posix_acl *acl, int type) |
234 | { |
235 | int rc, xprefix; |
236 | struct inode *inode = d_inode(dentry); |
237 | |
238 | switch (type) { |
239 | case ACL_TYPE_ACCESS: |
240 | xprefix = JFFS2_XPREFIX_ACL_ACCESS; |
241 | if (acl) { |
242 | umode_t mode; |
243 | |
244 | rc = posix_acl_update_mode(&nop_mnt_idmap, inode, &mode, |
245 | &acl); |
246 | if (rc) |
247 | return rc; |
248 | if (inode->i_mode != mode) { |
249 | struct iattr attr; |
250 | |
251 | attr.ia_valid = ATTR_MODE | ATTR_CTIME; |
252 | attr.ia_mode = mode; |
253 | attr.ia_ctime = current_time(inode); |
254 | rc = jffs2_do_setattr(inode, &attr); |
255 | if (rc < 0) |
256 | return rc; |
257 | } |
258 | } |
259 | break; |
260 | case ACL_TYPE_DEFAULT: |
261 | xprefix = JFFS2_XPREFIX_ACL_DEFAULT; |
262 | if (!S_ISDIR(inode->i_mode)) |
263 | return acl ? -EACCES : 0; |
264 | break; |
265 | default: |
266 | return -EINVAL; |
267 | } |
268 | rc = __jffs2_set_acl(inode, xprefix, acl); |
269 | if (!rc) |
270 | set_cached_acl(inode, type, acl); |
271 | return rc; |
272 | } |
273 | |
274 | int jffs2_init_acl_pre(struct inode *dir_i, struct inode *inode, umode_t *i_mode) |
275 | { |
276 | struct posix_acl *default_acl, *acl; |
277 | int rc; |
278 | |
279 | cache_no_acl(inode); |
280 | |
281 | rc = posix_acl_create(dir_i, i_mode, &default_acl, &acl); |
282 | if (rc) |
283 | return rc; |
284 | |
285 | if (default_acl) { |
286 | set_cached_acl(inode, ACL_TYPE_DEFAULT, acl: default_acl); |
287 | posix_acl_release(acl: default_acl); |
288 | } |
289 | if (acl) { |
290 | set_cached_acl(inode, ACL_TYPE_ACCESS, acl); |
291 | posix_acl_release(acl); |
292 | } |
293 | return 0; |
294 | } |
295 | |
296 | int jffs2_init_acl_post(struct inode *inode) |
297 | { |
298 | int rc; |
299 | |
300 | if (inode->i_default_acl) { |
301 | rc = __jffs2_set_acl(inode, JFFS2_XPREFIX_ACL_DEFAULT, acl: inode->i_default_acl); |
302 | if (rc) |
303 | return rc; |
304 | } |
305 | |
306 | if (inode->i_acl) { |
307 | rc = __jffs2_set_acl(inode, JFFS2_XPREFIX_ACL_ACCESS, acl: inode->i_acl); |
308 | if (rc) |
309 | return rc; |
310 | } |
311 | |
312 | return 0; |
313 | } |
314 | |