/src/barebox/fs/jffs2/fs.c
Line | Count | Source |
1 | | // SPDX-License-Identifier: GPL-2.0-only |
2 | | /* |
3 | | * JFFS2 -- Journalling Flash File System, Version 2. |
4 | | * |
5 | | * Copyright © 2001-2007 Red Hat, Inc. |
6 | | * Copyright © 2004-2010 David Woodhouse <dwmw2@infradead.org> |
7 | | * |
8 | | * Created by David Woodhouse <dwmw2@infradead.org> |
9 | | */ |
10 | | #define pr_fmt(fmt) "jffs2: " fmt |
11 | | #include <common.h> |
12 | | #include <crc.h> |
13 | | #include <driver.h> |
14 | | #include <init.h> |
15 | | #include <fs.h> |
16 | | #include <malloc.h> |
17 | | #include <linux/kernel.h> |
18 | | #include <linux/sched.h> |
19 | | #include <linux/list.h> |
20 | | #include <linux/mtd/mtd.h> |
21 | | #include <linux/pagemap.h> |
22 | | #include <linux/stat.h> |
23 | | #include "compr.h" |
24 | | #include "nodelist.h" |
25 | | #include "os-linux.h" |
26 | | |
27 | | static int jffs2_flash_setup(struct jffs2_sb_info *c); |
28 | | |
29 | | /* from include/linux/fs.h */ |
30 | | static inline void i_uid_write(struct inode *inode, uid_t uid) |
31 | 0 | { |
32 | 0 | inode->i_uid = uid; |
33 | 0 | } |
34 | | |
35 | | static inline void i_gid_write(struct inode *inode, gid_t gid) |
36 | 0 | { |
37 | 0 | inode->i_gid = gid; |
38 | 0 | } |
39 | | |
40 | | const struct inode_operations jffs2_file_inode_operations; |
41 | | |
42 | | static int jffs2_open(struct inode *inode, struct file *file) |
43 | 0 | { |
44 | 0 | struct jffs2_file *jf; |
45 | |
|
46 | 0 | jf = xzalloc(sizeof(*jf)); |
47 | |
|
48 | 0 | jf->inode = inode; |
49 | 0 | jf->buf = xmalloc(JFFS2_BLOCK_SIZE); |
50 | 0 | jf->offset = -1; |
51 | |
|
52 | 0 | file->private_data = jf; |
53 | |
|
54 | 0 | return 0; |
55 | 0 | } |
56 | | |
57 | | static int jffs2_close(struct inode *inode, struct file *f) |
58 | 0 | { |
59 | 0 | struct jffs2_file *jf = f->private_data; |
60 | |
|
61 | 0 | free(jf->buf); |
62 | 0 | free(jf); |
63 | |
|
64 | 0 | return 0; |
65 | 0 | } |
66 | | |
67 | | static int jffs2_get_block(struct jffs2_file *jf, unsigned int pos) |
68 | 0 | { |
69 | 0 | struct jffs2_sb_info *c = JFFS2_SB_INFO(jf->inode->i_sb); |
70 | 0 | struct jffs2_inode_info *f = JFFS2_INODE_INFO(jf->inode); |
71 | 0 | int ret; |
72 | | |
73 | | /* pos always has to be 4096 bytes aligned here */ |
74 | 0 | WARN_ON(pos % JFFS2_BLOCK_SIZE != 0); |
75 | |
|
76 | 0 | if (pos != jf->offset) { |
77 | 0 | ret = jffs2_read_inode_range(c, f, jf->buf, pos, |
78 | 0 | JFFS2_BLOCK_SIZE); |
79 | 0 | if (ret && ret != -ENOENT) |
80 | 0 | return ret; |
81 | 0 | jf->offset = pos; |
82 | 0 | } |
83 | | |
84 | 0 | return 0; |
85 | 0 | } |
86 | | |
87 | | static int jffs2_read(struct file *f, void *buf, size_t insize) |
88 | 0 | { |
89 | 0 | struct jffs2_file *jf = f->private_data; |
90 | 0 | unsigned int pos = f->f_pos; |
91 | 0 | unsigned int ofs; |
92 | 0 | unsigned int now; |
93 | 0 | unsigned int size = insize; |
94 | 0 | int ret; |
95 | | |
96 | | /* Read till end of current block */ |
97 | 0 | ofs = f->f_pos % JFFS2_BLOCK_SIZE; |
98 | 0 | if (ofs) { |
99 | 0 | ret = jffs2_get_block(jf, f->f_pos - ofs); /* Align down block */ |
100 | 0 | if (ret) |
101 | 0 | return ret; |
102 | | |
103 | 0 | now = min(size, JFFS2_BLOCK_SIZE - ofs); |
104 | | |
105 | | /* Complete block has been read, re-apply ofset now */ |
106 | 0 | memcpy(buf, jf->buf + ofs, now); |
107 | 0 | size -= now; |
108 | 0 | pos += now; |
109 | 0 | buf += now; |
110 | 0 | } |
111 | | |
112 | | /* Do full blocks */ |
113 | 0 | while (size >= JFFS2_BLOCK_SIZE) { |
114 | 0 | ret = jffs2_get_block(jf, pos); |
115 | 0 | if (ret) |
116 | 0 | return ret; |
117 | | |
118 | 0 | memcpy(buf, jf->buf, JFFS2_BLOCK_SIZE); |
119 | 0 | size -= JFFS2_BLOCK_SIZE; |
120 | 0 | pos += JFFS2_BLOCK_SIZE; |
121 | 0 | buf += JFFS2_BLOCK_SIZE; |
122 | 0 | } |
123 | | |
124 | | /* And the rest */ |
125 | 0 | if (size) { |
126 | 0 | ret = jffs2_get_block(jf, pos); |
127 | 0 | if (ret) |
128 | 0 | return ret; |
129 | 0 | memcpy(buf, jf->buf, size); |
130 | 0 | } |
131 | | |
132 | 0 | return insize; |
133 | |
|
134 | 0 | } |
135 | | |
136 | | const struct file_operations jffs2_file_operations = { |
137 | | .open = jffs2_open, |
138 | | .release = jffs2_close, |
139 | | .read = jffs2_read, |
140 | | }; |
141 | | |
142 | | struct inode *jffs2_iget(struct super_block *sb, unsigned long ino) |
143 | 0 | { |
144 | 0 | struct jffs2_inode_info *f; |
145 | 0 | struct jffs2_sb_info *c; |
146 | 0 | struct jffs2_raw_inode latest_node; |
147 | 0 | struct inode *inode; |
148 | 0 | int ret; |
149 | |
|
150 | 0 | jffs2_dbg(1, "%s(): ino == %lu\n", __func__, ino); |
151 | |
|
152 | 0 | inode = iget_locked(sb, ino); |
153 | 0 | if (!inode) |
154 | 0 | return ERR_PTR(-ENOMEM); |
155 | 0 | if (!(inode->i_state & I_NEW)) |
156 | 0 | return inode; |
157 | | |
158 | 0 | f = JFFS2_INODE_INFO(inode); |
159 | 0 | c = JFFS2_SB_INFO(inode->i_sb); |
160 | |
|
161 | 0 | jffs2_init_inode_info(f); |
162 | 0 | mutex_lock(&f->sem); |
163 | |
|
164 | 0 | ret = jffs2_do_read_inode(c, f, inode->i_ino, &latest_node); |
165 | 0 | if (ret) |
166 | 0 | goto error; |
167 | | |
168 | 0 | inode->i_mode = jemode_to_cpu(latest_node.mode); |
169 | 0 | i_uid_write(inode, je16_to_cpu(latest_node.uid)); |
170 | 0 | i_gid_write(inode, je16_to_cpu(latest_node.gid)); |
171 | 0 | inode->i_size = je32_to_cpu(latest_node.isize); |
172 | 0 | inode->i_atime = ITIME(je32_to_cpu(latest_node.atime)); |
173 | 0 | inode->i_mtime = ITIME(je32_to_cpu(latest_node.mtime)); |
174 | 0 | inode->i_ctime = ITIME(je32_to_cpu(latest_node.ctime)); |
175 | |
|
176 | 0 | set_nlink(inode, f->inocache->pino_nlink); |
177 | |
|
178 | 0 | inode->i_blocks = (inode->i_size + 511) >> 9; |
179 | |
|
180 | 0 | switch (inode->i_mode & S_IFMT) { |
181 | | |
182 | 0 | case S_IFLNK: |
183 | 0 | inode->i_op = &simple_symlink_inode_operations; |
184 | 0 | inode->i_link = f->target; |
185 | 0 | break; |
186 | | |
187 | 0 | case S_IFDIR: |
188 | 0 | { |
189 | 0 | struct jffs2_full_dirent *fd; |
190 | 0 | set_nlink(inode, 2); /* parent and '.' */ |
191 | |
|
192 | 0 | for (fd=f->dents; fd; fd = fd->next) { |
193 | 0 | if (fd->type == DT_DIR && fd->ino) |
194 | 0 | inc_nlink(inode); |
195 | 0 | } |
196 | | /* Root dir gets i_nlink 3 for some reason */ |
197 | 0 | if (inode->i_ino == 1) |
198 | 0 | inc_nlink(inode); |
199 | |
|
200 | 0 | inode->i_op = &jffs2_dir_inode_operations; |
201 | 0 | inode->i_fop = &jffs2_dir_operations; |
202 | 0 | break; |
203 | 0 | } |
204 | 0 | case S_IFREG: |
205 | 0 | inode->i_op = &jffs2_file_inode_operations; |
206 | 0 | inode->i_fop = &jffs2_file_operations; |
207 | 0 | break; |
208 | | |
209 | 0 | case S_IFBLK: |
210 | 0 | case S_IFCHR: |
211 | 0 | case S_IFSOCK: |
212 | 0 | case S_IFIFO: |
213 | 0 | break; |
214 | | |
215 | 0 | default: |
216 | 0 | pr_warn("%s(): Bogus i_mode %o for ino %lu\n", |
217 | 0 | __func__, inode->i_mode, (unsigned long)inode->i_ino); |
218 | 0 | } |
219 | | |
220 | 0 | mutex_unlock(&f->sem); |
221 | |
|
222 | 0 | jffs2_dbg(1, "jffs2_read_inode() returning\n"); |
223 | 0 | return inode; |
224 | | |
225 | 0 | error: |
226 | 0 | mutex_unlock(&f->sem); |
227 | 0 | iget_failed(inode); |
228 | 0 | return ERR_PTR(ret); |
229 | 0 | } |
230 | | |
231 | | static int calculate_inocache_hashsize(uint32_t flash_size) |
232 | 0 | { |
233 | | /* |
234 | | * Pick a inocache hash size based on the size of the medium. |
235 | | * Count how many megabytes we're dealing with, apply a hashsize twice |
236 | | * that size, but rounding down to the usual big powers of 2. And keep |
237 | | * to sensible bounds. |
238 | | */ |
239 | |
|
240 | 0 | int size_mb = flash_size / 1024 / 1024; |
241 | 0 | int hashsize = (size_mb * 2) & ~0x3f; |
242 | |
|
243 | 0 | if (hashsize < INOCACHE_HASHSIZE_MIN) |
244 | 0 | return INOCACHE_HASHSIZE_MIN; |
245 | 0 | if (hashsize > INOCACHE_HASHSIZE_MAX) |
246 | 0 | return INOCACHE_HASHSIZE_MAX; |
247 | | |
248 | 0 | return hashsize; |
249 | 0 | } |
250 | | |
251 | | static void jffs2_put_super(struct super_block *sb) |
252 | 0 | { |
253 | 0 | if (sb->s_fs_info) { |
254 | 0 | struct jffs2_sb_info *ctx = sb->s_fs_info; |
255 | |
|
256 | 0 | jffs2_free_ino_caches(ctx); |
257 | 0 | jffs2_free_raw_node_refs(ctx); |
258 | 0 | kfree(ctx->blocks); |
259 | 0 | kfree(ctx->inocache_list); |
260 | 0 | jffs2_flash_cleanup(ctx); |
261 | |
|
262 | 0 | kfree(sb->s_fs_info); |
263 | 0 | } |
264 | 0 | } |
265 | | |
266 | | int jffs2_do_fill_super(struct super_block *sb, int silent) |
267 | 0 | { |
268 | 0 | struct jffs2_sb_info *c; |
269 | 0 | struct inode *root_i; |
270 | 0 | int ret; |
271 | 0 | size_t blocks; |
272 | |
|
273 | 0 | c = JFFS2_SB_INFO(sb); |
274 | |
|
275 | 0 | c->flash_size = c->mtd->size; |
276 | 0 | c->sector_size = c->mtd->erasesize; |
277 | 0 | blocks = c->flash_size / c->sector_size; |
278 | | |
279 | | /* |
280 | | * Size alignment check |
281 | | */ |
282 | 0 | if ((c->sector_size * blocks) != c->flash_size) { |
283 | 0 | c->flash_size = c->sector_size * blocks; |
284 | 0 | pr_info("Flash size not aligned to erasesize, reducing to %dKiB", |
285 | 0 | c->flash_size / 1024); |
286 | 0 | } |
287 | |
|
288 | 0 | if (c->flash_size < 5*c->sector_size) { |
289 | 0 | pr_err("Too few erase blocks (%d)", |
290 | 0 | c->flash_size / c->sector_size); |
291 | 0 | return -EINVAL; |
292 | 0 | } |
293 | | |
294 | 0 | c->cleanmarker_size = sizeof(struct jffs2_unknown_node); |
295 | | |
296 | | /* NAND (or other bizarre) flash... do setup accordingly */ |
297 | 0 | ret = jffs2_flash_setup(c); |
298 | 0 | if (ret) |
299 | 0 | return ret; |
300 | | |
301 | 0 | c->inocache_hashsize = calculate_inocache_hashsize(c->flash_size); |
302 | 0 | c->inocache_list = kcalloc(c->inocache_hashsize, sizeof(struct jffs2_inode_cache *), GFP_KERNEL); |
303 | 0 | if (!c->inocache_list) { |
304 | 0 | ret = -ENOMEM; |
305 | 0 | goto out_wbuf; |
306 | 0 | } |
307 | | |
308 | 0 | jffs2_init_xattr_subsystem(c); |
309 | |
|
310 | 0 | if ((ret = jffs2_do_mount_fs(c))) |
311 | 0 | goto out_inohash; |
312 | | |
313 | 0 | jffs2_dbg(1, "%s(): Getting root inode\n", __func__); |
314 | 0 | root_i = jffs2_iget(sb, 1); |
315 | 0 | if (IS_ERR(root_i)) { |
316 | 0 | jffs2_dbg(1, "get root inode failed\n"); |
317 | 0 | ret = PTR_ERR(root_i); |
318 | 0 | goto out_root; |
319 | 0 | } |
320 | | |
321 | 0 | ret = -ENOMEM; |
322 | |
|
323 | 0 | jffs2_dbg(1, "%s(): d_make_root()\n", __func__); |
324 | 0 | sb->s_root = d_make_root(root_i); |
325 | 0 | if (!sb->s_root) |
326 | 0 | goto out_root; |
327 | | |
328 | 0 | sb->s_maxbytes = 0xFFFFFFFF; |
329 | 0 | sb->s_blocksize = PAGE_SIZE; |
330 | 0 | sb->s_blocksize_bits = PAGE_SHIFT; |
331 | 0 | sb->s_magic = JFFS2_SUPER_MAGIC; |
332 | |
|
333 | 0 | return 0; |
334 | | |
335 | 0 | out_root: |
336 | 0 | jffs2_free_ino_caches(c); |
337 | 0 | jffs2_free_raw_node_refs(c); |
338 | 0 | out_inohash: |
339 | 0 | kfree(c->inocache_list); |
340 | 0 | out_wbuf: |
341 | 0 | jffs2_flash_cleanup(c); |
342 | 0 | return ret; |
343 | 0 | } |
344 | | |
345 | 0 | static int jffs2_flash_setup(struct jffs2_sb_info *c) { |
346 | 0 | int ret = 0; |
347 | |
|
348 | 0 | if (jffs2_cleanmarker_oob(c)) { |
349 | | /* NAND flash... do setup accordingly */ |
350 | 0 | ret = jffs2_nand_flash_setup(c); |
351 | 0 | if (ret) |
352 | 0 | return ret; |
353 | 0 | } |
354 | | |
355 | | /* and Dataflash */ |
356 | 0 | if (jffs2_dataflash(c)) { |
357 | 0 | ret = jffs2_dataflash_setup(c); |
358 | 0 | if (ret) |
359 | 0 | return ret; |
360 | 0 | } |
361 | | |
362 | | /* and an UBI volume */ |
363 | 0 | if (jffs2_ubivol(c)) { |
364 | 0 | ret = jffs2_ubivol_setup(c); |
365 | 0 | if (ret) |
366 | 0 | return ret; |
367 | 0 | } |
368 | | |
369 | 0 | return ret; |
370 | 0 | } |
371 | | |
372 | 0 | void jffs2_flash_cleanup(struct jffs2_sb_info *c) { |
373 | |
|
374 | 0 | if (jffs2_cleanmarker_oob(c)) { |
375 | 0 | jffs2_nand_flash_cleanup(c); |
376 | 0 | } |
377 | | |
378 | | /* and DataFlash */ |
379 | 0 | if (jffs2_dataflash(c)) { |
380 | 0 | jffs2_dataflash_cleanup(c); |
381 | 0 | } |
382 | | |
383 | | /* and Intel "Sibley" flash */ |
384 | 0 | if (jffs2_nor_wbuf_flash(c)) { |
385 | 0 | jffs2_nor_wbuf_flash_cleanup(c); |
386 | 0 | } |
387 | | |
388 | | /* and an UBI volume */ |
389 | 0 | if (jffs2_ubivol(c)) { |
390 | 0 | jffs2_ubivol_cleanup(c); |
391 | 0 | } |
392 | 0 | } |
393 | | |
394 | | static int jffs2_probe_cnt; |
395 | | |
396 | | static int jffs2_probe(struct device *dev) |
397 | 0 | { |
398 | 0 | struct fs_device *fsdev; |
399 | 0 | struct super_block *sb; |
400 | 0 | struct jffs2_sb_info *ctx; |
401 | 0 | int ret; |
402 | |
|
403 | 0 | fsdev = dev_to_fs_device(dev); |
404 | 0 | sb = &fsdev->sb; |
405 | |
|
406 | 0 | ret = fsdev_open_cdev(fsdev); |
407 | 0 | if (ret) |
408 | 0 | goto err_out; |
409 | | |
410 | 0 | ctx = kzalloc(sizeof(struct jffs2_sb_info), GFP_KERNEL); |
411 | 0 | if (!ctx) |
412 | 0 | return -ENOMEM; |
413 | | |
414 | 0 | ctx->mtd = fsdev->cdev->mtd; |
415 | |
|
416 | 0 | sb->s_fs_info = ctx; |
417 | |
|
418 | 0 | if (!jffs2_probe_cnt) { |
419 | 0 | ret = jffs2_compressors_init(); |
420 | 0 | if (ret) { |
421 | 0 | pr_err("error: Failed to initialise compressors\n"); |
422 | 0 | goto err_out; |
423 | 0 | } |
424 | 0 | } |
425 | | |
426 | 0 | if (jffs2_fill_super(fsdev, 0)) { |
427 | 0 | dev_err(dev, "no valid jffs2 found\n"); |
428 | 0 | ret = -EINVAL; |
429 | 0 | goto err_compressors; |
430 | 0 | } |
431 | | |
432 | 0 | jffs2_probe_cnt++; |
433 | |
|
434 | 0 | return 0; |
435 | | |
436 | 0 | err_compressors: |
437 | 0 | jffs2_compressors_exit(); |
438 | 0 | err_out: |
439 | |
|
440 | 0 | return ret; |
441 | 0 | } |
442 | | |
443 | | static void jffs2_remove(struct device *dev) |
444 | 0 | { |
445 | 0 | struct fs_device *fsdev; |
446 | 0 | struct super_block *sb; |
447 | |
|
448 | 0 | fsdev = dev_to_fs_device(dev); |
449 | 0 | sb = &fsdev->sb; |
450 | |
|
451 | 0 | jffs2_probe_cnt--; |
452 | |
|
453 | 0 | if (!jffs2_probe_cnt) { |
454 | 0 | jffs2_compressors_exit(); |
455 | 0 | } |
456 | |
|
457 | 0 | jffs2_put_super(sb); |
458 | 0 | } |
459 | | |
460 | | |
461 | | static struct fs_driver jffs2_driver = { |
462 | | .type = filetype_jffs2, |
463 | | .drv = { |
464 | | .probe = jffs2_probe, |
465 | | .remove = jffs2_remove, |
466 | | .name = "jffs2", |
467 | | } |
468 | | }; |
469 | | |
470 | | static int jffs2_init(void) |
471 | 26 | { |
472 | 26 | return register_fs_driver(&jffs2_driver); |
473 | 26 | } |
474 | | coredevice_initcall(jffs2_init); |