summaryrefslogtreecommitdiffstats
path: root/fs/jffs2/summary.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2026-08-30 09:22:00 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2026-08-30 09:22:00 -0700
commit034dd340b08be1f2f0477ad16131d609f9dbd53c (patch)
tree2536f4b2d7893ccd916b5abdf3c454ad6edc03c2 /fs/jffs2/summary.c
downloadlinux-stable-034dd340b08be1f2f0477ad16131d609f9dbd53c.tar.gz
linux-stable-034dd340b08be1f2f0477ad16131d609f9dbd53c.zip
Merge tag 'trace-v7.3-2' of git://git.kernel.org/pub/scm/linux/kernel/git/trace/linux-tracegrafted
Pull tracing fixes from Steven Rostedt: - Fix error output of boot instance creation failure Currently if a boot instance creation fails, instead of printing out the name of the instance that failed, it prints "(null)". That is because it prints "cur_str" that had already been processed by strsep(). Print the saved name instead. While at it, print the error code of the failure. - Fix use-after-free for same named historgrams Histograms can be named so that they can be used in multiple events. But if the named histogram has a variable attached, the second event that uses the named histogram which duplicates it and needs to free the original after duplication leaves the old variable in place and still visible. If another histogram uses than variable, it will use the stale one which will try to reference the freed duplicate histogram and crash the kernel. Free the duplicate variables along with the duplicated histogram data. - Check return value of kthread_run() in event self test The events self tests uses a kthread for testing but does not check if it succeeded in creating a kthread. If the kthread creation were to fail, the code will still try to call kthread_stop() on the error returned. - Fix race between reading trace_pipe and updating subbuffer size If a user is reading the trace_pipe file at the same time they update the ring buffer sub-buffer size, can cause the trace_pipe read to read stale data. Add trace_access_lock() around updating the ring buffer sub-buffer size. - Fix eventfs_inode on failure path in creation of the events directory In the creation of the "events" directory, if after allocating the eventfs_inode a failure is detected, it calls cleanup_ei() which calls free_ei(). The free_ei() will test if eventfs_inode being freed has no children. It is a bug if it does. But on the failure case of the creation of the "events" directory, the children lists have not yet been initialized and the free will trigger a warning because list_empty() on an uninitialized list returns false. Move the initialization into init_ei() where it makes more sense and makes sure that a created eventfs_inode has its lists initialized upon creation. - Check return value of kthread_run() in ftrace direct sample code The sample code that shows how to use the ftrace direct calls does not test the return of kthread_run() to see if it succeeds. Return a failure if the kthread_run() doesn't succeed. - Clear user events state on fork in case of alloc failure On fork, the child gets a pointer to the parent's user events state. It makes a copy of it then updates the child's pointer to it. But if the allocation fails, the duplication function leaves the child with a pointer to its parent's descriptor. When the child cleans up its data, it will free the parent's descriptor while the parent is still using it. In the duplication function, set the child's user_event_mm to NULL before testing if the allocation succeeded, and when it exits it will not free the parent's descriptor. - Fix retry exhaustion in simple ring buffer reader swap simple_ring_buffer_swap_reader_page() starts with retry set to 8 and post-decrements it only after a failed link replacement. On the final attempt, a successful replacement leaves retry at zero, while a failed replacement leaves it at -1. But the check for success expects the retry value to be non-zero and exits with an error on zero. This is the opposite result. Fix it. - Fail nicely when the remote swap_reader_page() returns an error Currently, if the swap_reader_page() of a remote buffer fails, it triggers a WARN_ON_ONCE() and continues normally. Instead, have it exit with an error and a pr_warn() print instead of a full WARNING. * tag 'trace-v7.3-2' of git://git.kernel.org/pub/scm/linux/kernel/git/trace/linux-trace: ring-buffer: Stop remote reader update when page swap fails tracing: Fix retry exhaustion in simple ring buffer reader swap tracing/user_events: Clear copied tracing state before fork duplication samples/ftrace: Fix kthread_stop() on ERR_PTR in ftrace-direct-multi-modify samples/ftrace: Fix kthread_stop() on ERR_PTR in ftrace-direct-modify eventfs: Initialize ei->children and ei->list in init_ei() tracing: Fix use-after-free in trace_pipe read on sub-buffer order change tracing: Fix crash passing ERR_PTR to kthread_stop() tracing: Fix use-after-free with same-name named triggers tracing: Fix logged instance name on creation failure
Diffstat (limited to 'fs/jffs2/summary.c')
-rw-r--r--fs/jffs2/summary.c882
1 files changed, 882 insertions, 0 deletions
diff --git a/fs/jffs2/summary.c b/fs/jffs2/summary.c
new file mode 100644
index 000000000..4521a7723
--- /dev/null
+++ b/fs/jffs2/summary.c
@@ -0,0 +1,882 @@
+/*
+ * JFFS2 -- Journalling Flash File System, Version 2.
+ *
+ * Copyright © 2004 Ferenc Havasi <havasi@inf.u-szeged.hu>,
+ * Zoltan Sogor <weth@inf.u-szeged.hu>,
+ * Patrik Kluba <pajko@halom.u-szeged.hu>,
+ * University of Szeged, Hungary
+ * 2006 KaiGai Kohei <kaigai@ak.jp.nec.com>
+ *
+ * For licensing information, see the file 'LICENCE' in this directory.
+ *
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/mtd/mtd.h>
+#include <linux/pagemap.h>
+#include <linux/crc32.h>
+#include <linux/compiler.h>
+#include <linux/vmalloc.h>
+#include "nodelist.h"
+#include "debug.h"
+
+int jffs2_sum_init(struct jffs2_sb_info *c)
+{
+ uint32_t sum_size = min_t(uint32_t, c->sector_size, MAX_SUMMARY_SIZE);
+
+ c->summary = kzalloc_obj(struct jffs2_summary);
+
+ if (!c->summary) {
+ JFFS2_WARNING("Can't allocate memory for summary information!\n");
+ return -ENOMEM;
+ }
+
+ c->summary->sum_buf = kmalloc(sum_size, GFP_KERNEL);
+
+ if (!c->summary->sum_buf) {
+ JFFS2_WARNING("Can't allocate buffer for writing out summary information!\n");
+ kfree(c->summary);
+ return -ENOMEM;
+ }
+
+ dbg_summary("returned successfully\n");
+
+ return 0;
+}
+
+void jffs2_sum_exit(struct jffs2_sb_info *c)
+{
+ dbg_summary("called\n");
+
+ jffs2_sum_disable_collecting(c->summary);
+
+ kfree(c->summary->sum_buf);
+ c->summary->sum_buf = NULL;
+
+ kfree(c->summary);
+ c->summary = NULL;
+}
+
+static int jffs2_sum_add_mem(struct jffs2_summary *s, union jffs2_sum_mem *item)
+{
+ if (!s->sum_list_head)
+ s->sum_list_head = (union jffs2_sum_mem *) item;
+ if (s->sum_list_tail)
+ s->sum_list_tail->u.next = (union jffs2_sum_mem *) item;
+ s->sum_list_tail = (union jffs2_sum_mem *) item;
+
+ switch (je16_to_cpu(item->u.nodetype)) {
+ case JFFS2_NODETYPE_INODE:
+ s->sum_size += JFFS2_SUMMARY_INODE_SIZE;
+ s->sum_num++;
+ dbg_summary("inode (%u) added to summary\n",
+ je32_to_cpu(item->i.inode));
+ break;
+ case JFFS2_NODETYPE_DIRENT:
+ s->sum_size += JFFS2_SUMMARY_DIRENT_SIZE(item->d.nsize);
+ s->sum_num++;
+ dbg_summary("dirent (%u) added to summary\n",
+ je32_to_cpu(item->d.ino));
+ break;
+#ifdef CONFIG_JFFS2_FS_XATTR
+ case JFFS2_NODETYPE_XATTR:
+ s->sum_size += JFFS2_SUMMARY_XATTR_SIZE;
+ s->sum_num++;
+ dbg_summary("xattr (xid=%u, version=%u) added to summary\n",
+ je32_to_cpu(item->x.xid), je32_to_cpu(item->x.version));
+ break;
+ case JFFS2_NODETYPE_XREF:
+ s->sum_size += JFFS2_SUMMARY_XREF_SIZE;
+ s->sum_num++;
+ dbg_summary("xref added to summary\n");
+ break;
+#endif
+ default:
+ JFFS2_WARNING("UNKNOWN node type %u\n",
+ je16_to_cpu(item->u.nodetype));
+ return 1;
+ }
+ return 0;
+}
+
+
+/* The following 3 functions are called from scan.c to collect summary info for not closed jeb */
+
+int jffs2_sum_add_padding_mem(struct jffs2_summary *s, uint32_t size)
+{
+ dbg_summary("called with %u\n", size);
+ s->sum_padded += size;
+ return 0;
+}
+
+int jffs2_sum_add_inode_mem(struct jffs2_summary *s, struct jffs2_raw_inode *ri,
+ uint32_t ofs)
+{
+ struct jffs2_sum_inode_mem *temp = kmalloc_obj(struct jffs2_sum_inode_mem);
+
+ if (!temp)
+ return -ENOMEM;
+
+ temp->nodetype = ri->nodetype;
+ temp->inode = ri->ino;
+ temp->version = ri->version;
+ temp->offset = cpu_to_je32(ofs); /* relative offset from the beginning of the jeb */
+ temp->totlen = ri->totlen;
+ temp->next = NULL;
+
+ return jffs2_sum_add_mem(s, (union jffs2_sum_mem *)temp);
+}
+
+int jffs2_sum_add_dirent_mem(struct jffs2_summary *s, struct jffs2_raw_dirent *rd,
+ uint32_t ofs)
+{
+ struct jffs2_sum_dirent_mem *temp =
+ kmalloc(sizeof(struct jffs2_sum_dirent_mem) + rd->nsize, GFP_KERNEL);
+
+ if (!temp)
+ return -ENOMEM;
+
+ temp->nodetype = rd->nodetype;
+ temp->totlen = rd->totlen;
+ temp->offset = cpu_to_je32(ofs); /* relative from the beginning of the jeb */
+ temp->pino = rd->pino;
+ temp->version = rd->version;
+ temp->ino = rd->ino;
+ temp->nsize = rd->nsize;
+ temp->type = rd->type;
+ temp->next = NULL;
+
+ memcpy(temp->name, rd->name, rd->nsize);
+
+ return jffs2_sum_add_mem(s, (union jffs2_sum_mem *)temp);
+}
+
+#ifdef CONFIG_JFFS2_FS_XATTR
+int jffs2_sum_add_xattr_mem(struct jffs2_summary *s, struct jffs2_raw_xattr *rx, uint32_t ofs)
+{
+ struct jffs2_sum_xattr_mem *temp;
+
+ temp = kmalloc_obj(struct jffs2_sum_xattr_mem);
+ if (!temp)
+ return -ENOMEM;
+
+ temp->nodetype = rx->nodetype;
+ temp->xid = rx->xid;
+ temp->version = rx->version;
+ temp->offset = cpu_to_je32(ofs);
+ temp->totlen = rx->totlen;
+ temp->next = NULL;
+
+ return jffs2_sum_add_mem(s, (union jffs2_sum_mem *)temp);
+}
+
+int jffs2_sum_add_xref_mem(struct jffs2_summary *s, struct jffs2_raw_xref *rr, uint32_t ofs)
+{
+ struct jffs2_sum_xref_mem *temp;
+
+ temp = kmalloc_obj(struct jffs2_sum_xref_mem);
+ if (!temp)
+ return -ENOMEM;
+
+ temp->nodetype = rr->nodetype;
+ temp->offset = cpu_to_je32(ofs);
+ temp->next = NULL;
+
+ return jffs2_sum_add_mem(s, (union jffs2_sum_mem *)temp);
+}
+#endif
+/* Cleanup every collected summary information */
+
+static void jffs2_sum_clean_collected(struct jffs2_summary *s)
+{
+ union jffs2_sum_mem *temp;
+
+ if (!s->sum_list_head) {
+ dbg_summary("already empty\n");
+ }
+ while (s->sum_list_head) {
+ temp = s->sum_list_head;
+ s->sum_list_head = s->sum_list_head->u.next;
+ kfree(temp);
+ }
+ s->sum_list_tail = NULL;
+ s->sum_padded = 0;
+ s->sum_num = 0;
+}
+
+void jffs2_sum_reset_collected(struct jffs2_summary *s)
+{
+ dbg_summary("called\n");
+ jffs2_sum_clean_collected(s);
+ s->sum_size = 0;
+}
+
+void jffs2_sum_disable_collecting(struct jffs2_summary *s)
+{
+ dbg_summary("called\n");
+ jffs2_sum_clean_collected(s);
+ s->sum_size = JFFS2_SUMMARY_NOSUM_SIZE;
+}
+
+int jffs2_sum_is_disabled(struct jffs2_summary *s)
+{
+ return (s->sum_size == JFFS2_SUMMARY_NOSUM_SIZE);
+}
+
+/* Move the collected summary information into sb (called from scan.c) */
+
+void jffs2_sum_move_collected(struct jffs2_sb_info *c, struct jffs2_summary *s)
+{
+ dbg_summary("oldsize=0x%x oldnum=%u => newsize=0x%x newnum=%u\n",
+ c->summary->sum_size, c->summary->sum_num,
+ s->sum_size, s->sum_num);
+
+ c->summary->sum_size = s->sum_size;
+ c->summary->sum_num = s->sum_num;
+ c->summary->sum_padded = s->sum_padded;
+ c->summary->sum_list_head = s->sum_list_head;
+ c->summary->sum_list_tail = s->sum_list_tail;
+
+ s->sum_list_head = s->sum_list_tail = NULL;
+}
+
+/* Called from wbuf.c to collect writed node info */
+
+int jffs2_sum_add_kvec(struct jffs2_sb_info *c, const struct kvec *invecs,
+ unsigned long count, uint32_t ofs)
+{
+ union jffs2_node_union *node;
+ struct jffs2_eraseblock *jeb;
+
+ if (c->summary->sum_size == JFFS2_SUMMARY_NOSUM_SIZE) {
+ dbg_summary("Summary is disabled for this jeb! Skipping summary info!\n");
+ return 0;
+ }
+
+ node = invecs[0].iov_base;
+ jeb = &c->blocks[ofs / c->sector_size];
+ ofs -= jeb->offset;
+
+ switch (je16_to_cpu(node->u.nodetype)) {
+ case JFFS2_NODETYPE_INODE: {
+ struct jffs2_sum_inode_mem *temp =
+ kmalloc_obj(struct jffs2_sum_inode_mem);
+
+ if (!temp)
+ goto no_mem;
+
+ temp->nodetype = node->i.nodetype;
+ temp->inode = node->i.ino;
+ temp->version = node->i.version;
+ temp->offset = cpu_to_je32(ofs);
+ temp->totlen = node->i.totlen;
+ temp->next = NULL;
+
+ return jffs2_sum_add_mem(c->summary, (union jffs2_sum_mem *)temp);
+ }
+
+ case JFFS2_NODETYPE_DIRENT: {
+ struct jffs2_sum_dirent_mem *temp =
+ kmalloc(sizeof(struct jffs2_sum_dirent_mem) + node->d.nsize, GFP_KERNEL);
+
+ if (!temp)
+ goto no_mem;
+
+ temp->nodetype = node->d.nodetype;
+ temp->totlen = node->d.totlen;
+ temp->offset = cpu_to_je32(ofs);
+ temp->pino = node->d.pino;
+ temp->version = node->d.version;
+ temp->ino = node->d.ino;
+ temp->nsize = node->d.nsize;
+ temp->type = node->d.type;
+ temp->next = NULL;
+
+ switch (count) {
+ case 1:
+ memcpy(temp->name,node->d.name,node->d.nsize);
+ break;
+
+ case 2:
+ memcpy(temp->name,invecs[1].iov_base,node->d.nsize);
+ break;
+
+ default:
+ BUG(); /* impossible count value */
+ break;
+ }
+
+ return jffs2_sum_add_mem(c->summary, (union jffs2_sum_mem *)temp);
+ }
+#ifdef CONFIG_JFFS2_FS_XATTR
+ case JFFS2_NODETYPE_XATTR: {
+ struct jffs2_sum_xattr_mem *temp;
+ temp = kmalloc_obj(struct jffs2_sum_xattr_mem);
+ if (!temp)
+ goto no_mem;
+
+ temp->nodetype = node->x.nodetype;
+ temp->xid = node->x.xid;
+ temp->version = node->x.version;
+ temp->totlen = node->x.totlen;
+ temp->offset = cpu_to_je32(ofs);
+ temp->next = NULL;
+
+ return jffs2_sum_add_mem(c->summary, (union jffs2_sum_mem *)temp);
+ }
+ case JFFS2_NODETYPE_XREF: {
+ struct jffs2_sum_xref_mem *temp;
+ temp = kmalloc_obj(struct jffs2_sum_xref_mem);
+ if (!temp)
+ goto no_mem;
+ temp->nodetype = node->r.nodetype;
+ temp->offset = cpu_to_je32(ofs);
+ temp->next = NULL;
+
+ return jffs2_sum_add_mem(c->summary, (union jffs2_sum_mem *)temp);
+ }
+#endif
+ case JFFS2_NODETYPE_PADDING:
+ dbg_summary("node PADDING\n");
+ c->summary->sum_padded += je32_to_cpu(node->u.totlen);
+ break;
+
+ case JFFS2_NODETYPE_CLEANMARKER:
+ dbg_summary("node CLEANMARKER\n");
+ break;
+
+ case JFFS2_NODETYPE_SUMMARY:
+ dbg_summary("node SUMMARY\n");
+ break;
+
+ default:
+ /* If you implement a new node type you should also implement
+ summary support for it or disable summary.
+ */
+ BUG();
+ break;
+ }
+
+ return 0;
+
+no_mem:
+ JFFS2_WARNING("MEMORY ALLOCATION ERROR!");
+ return -ENOMEM;
+}
+
+static struct jffs2_raw_node_ref *sum_link_node_ref(struct jffs2_sb_info *c,
+ struct jffs2_eraseblock *jeb,
+ uint32_t ofs, uint32_t len,
+ struct jffs2_inode_cache *ic)
+{
+ /* If there was a gap, mark it dirty */
+ if ((ofs & ~3) > c->sector_size - jeb->free_size) {
+ /* Ew. Summary doesn't actually tell us explicitly about dirty space */
+ jffs2_scan_dirty_space(c, jeb, (ofs & ~3) - (c->sector_size - jeb->free_size));
+ }
+
+ return jffs2_link_node_ref(c, jeb, jeb->offset + ofs, len, ic);
+}
+
+/* Process the stored summary information - helper function for jffs2_sum_scan_sumnode() */
+
+static int jffs2_sum_process_sum_data(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb,
+ struct jffs2_raw_summary *summary, uint32_t *pseudo_random)
+{
+ struct jffs2_inode_cache *ic;
+ struct jffs2_full_dirent *fd;
+ void *sp;
+ int i, ino;
+ int err;
+
+ sp = summary->sum;
+
+ for (i=0; i<je32_to_cpu(summary->sum_num); i++) {
+ dbg_summary("processing summary index %d\n", i);
+
+ cond_resched();
+
+ /* Make sure there's a spare ref for dirty space */
+ err = jffs2_prealloc_raw_node_refs(c, jeb, 2);
+ if (err)
+ return err;
+
+ switch (je16_to_cpu(((struct jffs2_sum_unknown_flash *)sp)->nodetype)) {
+ case JFFS2_NODETYPE_INODE: {
+ struct jffs2_sum_inode_flash *spi;
+ spi = sp;
+
+ ino = je32_to_cpu(spi->inode);
+
+ dbg_summary("Inode at 0x%08x-0x%08x\n",
+ jeb->offset + je32_to_cpu(spi->offset),
+ jeb->offset + je32_to_cpu(spi->offset) + je32_to_cpu(spi->totlen));
+
+ ic = jffs2_scan_make_ino_cache(c, ino);
+ if (!ic) {
+ JFFS2_NOTICE("scan_make_ino_cache failed\n");
+ return -ENOMEM;
+ }
+
+ sum_link_node_ref(c, jeb, je32_to_cpu(spi->offset) | REF_UNCHECKED,
+ PAD(je32_to_cpu(spi->totlen)), ic);
+
+ *pseudo_random += je32_to_cpu(spi->version);
+
+ sp += JFFS2_SUMMARY_INODE_SIZE;
+
+ break;
+ }
+
+ case JFFS2_NODETYPE_DIRENT: {
+ struct jffs2_sum_dirent_flash *spd;
+ int checkedlen;
+ spd = sp;
+
+ dbg_summary("Dirent at 0x%08x-0x%08x\n",
+ jeb->offset + je32_to_cpu(spd->offset),
+ jeb->offset + je32_to_cpu(spd->offset) + je32_to_cpu(spd->totlen));
+
+
+ /* This should never happen, but https://dev.laptop.org/ticket/4184 */
+ checkedlen = strnlen(spd->name, spd->nsize);
+ if (!checkedlen) {
+ pr_err("Dirent at %08x has zero at start of name. Aborting mount.\n",
+ jeb->offset +
+ je32_to_cpu(spd->offset));
+ return -EIO;
+ }
+ if (checkedlen < spd->nsize) {
+ pr_err("Dirent at %08x has zeroes in name. Truncating to %d chars\n",
+ jeb->offset +
+ je32_to_cpu(spd->offset),
+ checkedlen);
+ }
+
+
+ fd = jffs2_alloc_full_dirent(checkedlen+1);
+ if (!fd)
+ return -ENOMEM;
+
+ memcpy(&fd->name, spd->name, checkedlen);
+ fd->name[checkedlen] = 0;
+
+ ic = jffs2_scan_make_ino_cache(c, je32_to_cpu(spd->pino));
+ if (!ic) {
+ jffs2_free_full_dirent(fd);
+ return -ENOMEM;
+ }
+
+ fd->raw = sum_link_node_ref(c, jeb, je32_to_cpu(spd->offset) | REF_UNCHECKED,
+ PAD(je32_to_cpu(spd->totlen)), ic);
+
+ fd->next = NULL;
+ fd->version = je32_to_cpu(spd->version);
+ fd->ino = je32_to_cpu(spd->ino);
+ fd->nhash = full_name_hash(NULL, fd->name, checkedlen);
+ fd->type = spd->type;
+
+ jffs2_add_fd_to_list(c, fd, &ic->scan_dents);
+
+ *pseudo_random += je32_to_cpu(spd->version);
+
+ sp += JFFS2_SUMMARY_DIRENT_SIZE(spd->nsize);
+
+ break;
+ }
+#ifdef CONFIG_JFFS2_FS_XATTR
+ case JFFS2_NODETYPE_XATTR: {
+ struct jffs2_xattr_datum *xd;
+ struct jffs2_sum_xattr_flash *spx;
+
+ spx = (struct jffs2_sum_xattr_flash *)sp;
+ dbg_summary("xattr at %#08x-%#08x (xid=%u, version=%u)\n",
+ jeb->offset + je32_to_cpu(spx->offset),
+ jeb->offset + je32_to_cpu(spx->offset) + je32_to_cpu(spx->totlen),
+ je32_to_cpu(spx->xid), je32_to_cpu(spx->version));
+
+ xd = jffs2_setup_xattr_datum(c, je32_to_cpu(spx->xid),
+ je32_to_cpu(spx->version));
+ if (IS_ERR(xd))
+ return PTR_ERR(xd);
+ if (xd->version > je32_to_cpu(spx->version)) {
+ /* node is not the newest one */
+ struct jffs2_raw_node_ref *raw
+ = sum_link_node_ref(c, jeb, je32_to_cpu(spx->offset) | REF_UNCHECKED,
+ PAD(je32_to_cpu(spx->totlen)), NULL);
+ raw->next_in_ino = xd->node->next_in_ino;
+ xd->node->next_in_ino = raw;
+ } else {
+ xd->version = je32_to_cpu(spx->version);
+ sum_link_node_ref(c, jeb, je32_to_cpu(spx->offset) | REF_UNCHECKED,
+ PAD(je32_to_cpu(spx->totlen)), (void *)xd);
+ }
+ *pseudo_random += je32_to_cpu(spx->xid);
+ sp += JFFS2_SUMMARY_XATTR_SIZE;
+
+ break;
+ }
+ case JFFS2_NODETYPE_XREF: {
+ struct jffs2_xattr_ref *ref;
+ struct jffs2_sum_xref_flash *spr;
+
+ spr = (struct jffs2_sum_xref_flash *)sp;
+ dbg_summary("xref at %#08x-%#08x\n",
+ jeb->offset + je32_to_cpu(spr->offset),
+ jeb->offset + je32_to_cpu(spr->offset) +
+ (uint32_t)PAD(sizeof(struct jffs2_raw_xref)));
+
+ ref = jffs2_alloc_xattr_ref();
+ if (!ref) {
+ JFFS2_NOTICE("allocation of xattr_datum failed\n");
+ return -ENOMEM;
+ }
+ ref->next = c->xref_temp;
+ c->xref_temp = ref;
+
+ sum_link_node_ref(c, jeb, je32_to_cpu(spr->offset) | REF_UNCHECKED,
+ PAD(sizeof(struct jffs2_raw_xref)), (void *)ref);
+
+ *pseudo_random += ref->node->flash_offset;
+ sp += JFFS2_SUMMARY_XREF_SIZE;
+
+ break;
+ }
+#endif
+ default : {
+ uint16_t nodetype = je16_to_cpu(((struct jffs2_sum_unknown_flash *)sp)->nodetype);
+ JFFS2_WARNING("Unsupported node type %x found in summary! Exiting...\n", nodetype);
+ if ((nodetype & JFFS2_COMPAT_MASK) == JFFS2_FEATURE_INCOMPAT)
+ return -EIO;
+
+ /* For compatible node types, just fall back to the full scan */
+ c->wasted_size -= jeb->wasted_size;
+ c->free_size += c->sector_size - jeb->free_size;
+ c->used_size -= jeb->used_size;
+ c->dirty_size -= jeb->dirty_size;
+ jeb->wasted_size = jeb->used_size = jeb->dirty_size = 0;
+ jeb->free_size = c->sector_size;
+
+ jffs2_free_jeb_node_refs(c, jeb);
+ return -ENOTRECOVERABLE;
+ }
+ }
+ }
+ return 0;
+}
+
+/* Process the summary node - called from jffs2_scan_eraseblock() */
+int jffs2_sum_scan_sumnode(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb,
+ struct jffs2_raw_summary *summary, uint32_t sumsize,
+ uint32_t *pseudo_random)
+{
+ struct jffs2_unknown_node crcnode;
+ int ret, ofs;
+ uint32_t crc;
+
+ ofs = c->sector_size - sumsize;
+
+ dbg_summary("summary found for 0x%08x at 0x%08x (0x%x bytes)\n",
+ jeb->offset, jeb->offset + ofs, sumsize);
+
+ /* OK, now check for node validity and CRC */
+ crcnode.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK);
+ crcnode.nodetype = cpu_to_je16(JFFS2_NODETYPE_SUMMARY);
+ crcnode.totlen = summary->totlen;
+ crc = crc32(0, &crcnode, sizeof(crcnode)-4);
+
+ if (je32_to_cpu(summary->hdr_crc) != crc) {
+ dbg_summary("Summary node header is corrupt (bad CRC or "
+ "no summary at all)\n");
+ goto crc_err;
+ }
+
+ if (je32_to_cpu(summary->totlen) != sumsize) {
+ dbg_summary("Summary node is corrupt (wrong erasesize?)\n");
+ goto crc_err;
+ }
+
+ crc = crc32(0, summary, sizeof(struct jffs2_raw_summary)-8);
+
+ if (je32_to_cpu(summary->node_crc) != crc) {
+ dbg_summary("Summary node is corrupt (bad CRC)\n");
+ goto crc_err;
+ }
+
+ crc = crc32(0, summary->sum, sumsize - sizeof(struct jffs2_raw_summary));
+
+ if (je32_to_cpu(summary->sum_crc) != crc) {
+ dbg_summary("Summary node data is corrupt (bad CRC)\n");
+ goto crc_err;
+ }
+
+ if ( je32_to_cpu(summary->cln_mkr) ) {
+
+ dbg_summary("Summary : CLEANMARKER node \n");
+
+ ret = jffs2_prealloc_raw_node_refs(c, jeb, 1);
+ if (ret)
+ return ret;
+
+ if (je32_to_cpu(summary->cln_mkr) != c->cleanmarker_size) {
+ dbg_summary("CLEANMARKER node has totlen 0x%x != normal 0x%x\n",
+ je32_to_cpu(summary->cln_mkr), c->cleanmarker_size);
+ if ((ret = jffs2_scan_dirty_space(c, jeb, PAD(je32_to_cpu(summary->cln_mkr)))))
+ return ret;
+ } else if (jeb->first_node) {
+ dbg_summary("CLEANMARKER node not first node in block "
+ "(0x%08x)\n", jeb->offset);
+ if ((ret = jffs2_scan_dirty_space(c, jeb, PAD(je32_to_cpu(summary->cln_mkr)))))
+ return ret;
+ } else {
+ jffs2_link_node_ref(c, jeb, jeb->offset | REF_NORMAL,
+ je32_to_cpu(summary->cln_mkr), NULL);
+ }
+ }
+
+ ret = jffs2_sum_process_sum_data(c, jeb, summary, pseudo_random);
+ /* -ENOTRECOVERABLE isn't a fatal error -- it means we should do a full
+ scan of this eraseblock. So return zero */
+ if (ret == -ENOTRECOVERABLE)
+ return 0;
+ if (ret)
+ return ret; /* real error */
+
+ /* for PARANOIA_CHECK */
+ ret = jffs2_prealloc_raw_node_refs(c, jeb, 2);
+ if (ret)
+ return ret;
+
+ sum_link_node_ref(c, jeb, ofs | REF_NORMAL, sumsize, NULL);
+
+ if (unlikely(jeb->free_size)) {
+ JFFS2_WARNING("Free size 0x%x bytes in eraseblock @0x%08x with summary?\n",
+ jeb->free_size, jeb->offset);
+ jeb->wasted_size += jeb->free_size;
+ c->wasted_size += jeb->free_size;
+ c->free_size -= jeb->free_size;
+ jeb->free_size = 0;
+ }
+
+ return jffs2_scan_classify_jeb(c, jeb);
+
+crc_err:
+ JFFS2_WARNING("Summary node crc error, skipping summary information.\n");
+
+ return 0;
+}
+
+/* Write summary data to flash - helper function for jffs2_sum_write_sumnode() */
+
+static int jffs2_sum_write_data(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb,
+ uint32_t infosize, uint32_t datasize, int padsize)
+{
+ struct jffs2_raw_summary isum;
+ union jffs2_sum_mem *temp;
+ struct jffs2_sum_marker *sm;
+ struct kvec vecs[2];
+ uint32_t sum_ofs;
+ void *wpage;
+ int ret;
+ size_t retlen;
+
+ if (padsize + datasize > MAX_SUMMARY_SIZE) {
+ /* It won't fit in the buffer. Abort summary for this jeb */
+ jffs2_sum_disable_collecting(c->summary);
+
+ JFFS2_WARNING("Summary too big (%d data, %d pad) in eraseblock at %08x\n",
+ datasize, padsize, jeb->offset);
+ /* Non-fatal */
+ return 0;
+ }
+ /* Is there enough space for summary? */
+ if (padsize < 0) {
+ /* don't try to write out summary for this jeb */
+ jffs2_sum_disable_collecting(c->summary);
+
+ JFFS2_WARNING("Not enough space for summary, padsize = %d\n",
+ padsize);
+ /* Non-fatal */
+ return 0;
+ }
+
+ memset(c->summary->sum_buf, 0xff, datasize);
+ memset(&isum, 0, sizeof(isum));
+
+ isum.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK);
+ isum.nodetype = cpu_to_je16(JFFS2_NODETYPE_SUMMARY);
+ isum.totlen = cpu_to_je32(infosize);
+ isum.hdr_crc = cpu_to_je32(crc32(0, &isum, sizeof(struct jffs2_unknown_node) - 4));
+ isum.padded = cpu_to_je32(c->summary->sum_padded);
+ isum.cln_mkr = cpu_to_je32(c->cleanmarker_size);
+ isum.sum_num = cpu_to_je32(c->summary->sum_num);
+ wpage = c->summary->sum_buf;
+
+ while (c->summary->sum_num) {
+ temp = c->summary->sum_list_head;
+
+ switch (je16_to_cpu(temp->u.nodetype)) {
+ case JFFS2_NODETYPE_INODE: {
+ struct jffs2_sum_inode_flash *sino_ptr = wpage;
+
+ sino_ptr->nodetype = temp->i.nodetype;
+ sino_ptr->inode = temp->i.inode;
+ sino_ptr->version = temp->i.version;
+ sino_ptr->offset = temp->i.offset;
+ sino_ptr->totlen = temp->i.totlen;
+
+ wpage += JFFS2_SUMMARY_INODE_SIZE;
+
+ break;
+ }
+
+ case JFFS2_NODETYPE_DIRENT: {
+ struct jffs2_sum_dirent_flash *sdrnt_ptr = wpage;
+
+ sdrnt_ptr->nodetype = temp->d.nodetype;
+ sdrnt_ptr->totlen = temp->d.totlen;
+ sdrnt_ptr->offset = temp->d.offset;
+ sdrnt_ptr->pino = temp->d.pino;
+ sdrnt_ptr->version = temp->d.version;
+ sdrnt_ptr->ino = temp->d.ino;
+ sdrnt_ptr->nsize = temp->d.nsize;
+ sdrnt_ptr->type = temp->d.type;
+
+ memcpy(sdrnt_ptr->name, temp->d.name,
+ temp->d.nsize);
+
+ wpage += JFFS2_SUMMARY_DIRENT_SIZE(temp->d.nsize);
+
+ break;
+ }
+#ifdef CONFIG_JFFS2_FS_XATTR
+ case JFFS2_NODETYPE_XATTR: {
+ struct jffs2_sum_xattr_flash *sxattr_ptr = wpage;
+
+ temp = c->summary->sum_list_head;
+ sxattr_ptr->nodetype = temp->x.nodetype;
+ sxattr_ptr->xid = temp->x.xid;
+ sxattr_ptr->version = temp->x.version;
+ sxattr_ptr->offset = temp->x.offset;
+ sxattr_ptr->totlen = temp->x.totlen;
+
+ wpage += JFFS2_SUMMARY_XATTR_SIZE;
+ break;
+ }
+ case JFFS2_NODETYPE_XREF: {
+ struct jffs2_sum_xref_flash *sxref_ptr = wpage;
+
+ temp = c->summary->sum_list_head;
+ sxref_ptr->nodetype = temp->r.nodetype;
+ sxref_ptr->offset = temp->r.offset;
+
+ wpage += JFFS2_SUMMARY_XREF_SIZE;
+ break;
+ }
+#endif
+ default : {
+ if ((je16_to_cpu(temp->u.nodetype) & JFFS2_COMPAT_MASK)
+ == JFFS2_FEATURE_RWCOMPAT_COPY) {
+ dbg_summary("Writing unknown RWCOMPAT_COPY node type %x\n",
+ je16_to_cpu(temp->u.nodetype));
+ jffs2_sum_disable_collecting(c->summary);
+ /* The above call removes the list, nothing more to do */
+ goto bail_rwcompat;
+ } else {
+ BUG(); /* unknown node in summary information */
+ }
+ }
+ }
+
+ c->summary->sum_list_head = temp->u.next;
+ kfree(temp);
+
+ c->summary->sum_num--;
+ }
+ bail_rwcompat:
+
+ jffs2_sum_reset_collected(c->summary);
+
+ wpage += padsize;
+
+ sm = wpage;
+ sm->offset = cpu_to_je32(c->sector_size - jeb->free_size);
+ sm->magic = cpu_to_je32(JFFS2_SUM_MAGIC);
+
+ isum.sum_crc = cpu_to_je32(crc32(0, c->summary->sum_buf, datasize));
+ isum.node_crc = cpu_to_je32(crc32(0, &isum, sizeof(isum) - 8));
+
+ vecs[0].iov_base = &isum;
+ vecs[0].iov_len = sizeof(isum);
+ vecs[1].iov_base = c->summary->sum_buf;
+ vecs[1].iov_len = datasize;
+
+ sum_ofs = jeb->offset + c->sector_size - jeb->free_size;
+
+ dbg_summary("writing out data to flash to pos : 0x%08x\n", sum_ofs);
+
+ ret = jffs2_flash_writev(c, vecs, 2, sum_ofs, &retlen, 0);
+
+ if (ret || (retlen != infosize)) {
+
+ JFFS2_WARNING("Write of %u bytes at 0x%08x failed. returned %d, retlen %zd\n",
+ infosize, sum_ofs, ret, retlen);
+
+ if (retlen) {
+ /* Waste remaining space */
+ spin_lock(&c->erase_completion_lock);
+ jffs2_link_node_ref(c, jeb, sum_ofs | REF_OBSOLETE, infosize, NULL);
+ spin_unlock(&c->erase_completion_lock);
+ }
+
+ c->summary->sum_size = JFFS2_SUMMARY_NOSUM_SIZE;
+
+ return 0;
+ }
+
+ spin_lock(&c->erase_completion_lock);
+ jffs2_link_node_ref(c, jeb, sum_ofs | REF_NORMAL, infosize, NULL);
+ spin_unlock(&c->erase_completion_lock);
+
+ return 0;
+}
+
+/* Write out summary information - called from jffs2_do_reserve_space */
+
+int jffs2_sum_write_sumnode(struct jffs2_sb_info *c)
+ __must_hold(&c->erase_completion_block)
+{
+ int datasize, infosize, padsize;
+ struct jffs2_eraseblock *jeb;
+ int ret = 0;
+
+ dbg_summary("called\n");
+
+ spin_unlock(&c->erase_completion_lock);
+
+ jeb = c->nextblock;
+ ret = jffs2_prealloc_raw_node_refs(c, jeb, 1);
+
+ if (ret)
+ goto out;
+
+ if (!c->summary->sum_num || !c->summary->sum_list_head) {
+ JFFS2_WARNING("Empty summary info!!!\n");
+ BUG();
+ }
+
+ datasize = c->summary->sum_size + sizeof(struct jffs2_sum_marker);
+ infosize = sizeof(struct jffs2_raw_summary) + datasize;
+ padsize = jeb->free_size - infosize;
+ infosize += padsize;
+ datasize += padsize;
+
+ ret = jffs2_sum_write_data(c, jeb, infosize, datasize, padsize);
+
+out:
+ spin_lock(&c->erase_completion_lock);
+ return ret;
+}