forked from Minki/linux
19646447ad
clang static analysis reports this problem
fs/jffs2/summary.c:794:31: warning: Use of memory after it is freed
c->summary->sum_list_head = temp->u.next;
^~~~~~~~~~~~
In jffs2_sum_write_data(), in a loop summary data is handles a node at
a time. When it has written out the node it is removed the summary list,
and the node is deleted. In the corner case when a
JFFS2_FEATURE_RWCOMPAT_COPY is seen, a call is made to
jffs2_sum_disable_collecting(). jffs2_sum_disable_collecting() deletes
the whole list which conflicts with the loop's deleting the list by parts.
To preserve the old behavior of stopping the write midway, bail out of
the loop after disabling summary collection.
Fixes: 6171586a7a
("[JFFS2] Correct handling of JFFS2_FEATURE_RWCOMPAT_COPY nodes.")
Signed-off-by: Tom Rix <trix@redhat.com>
Reviewed-by: Nathan Chancellor <natechancellor@gmail.com>
Signed-off-by: Richard Weinberger <richard@nod.at>
878 lines
24 KiB
C
878 lines
24 KiB
C
/*
|
|
* 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(sizeof(struct jffs2_summary), GFP_KERNEL);
|
|
|
|
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(sizeof(struct jffs2_sum_inode_mem), GFP_KERNEL);
|
|
|
|
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(sizeof(struct jffs2_sum_xattr_mem), GFP_KERNEL);
|
|
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(sizeof(struct jffs2_sum_xref_mem), GFP_KERNEL);
|
|
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(sizeof(struct jffs2_sum_inode_mem), GFP_KERNEL);
|
|
|
|
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(sizeof(struct jffs2_sum_xattr_mem), GFP_KERNEL);
|
|
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(sizeof(struct jffs2_sum_xref_mem), GFP_KERNEL);
|
|
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;
|
|
jffs2_prealloc_raw_node_refs(c, jeb, 1);
|
|
|
|
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);
|
|
spin_lock(&c->erase_completion_lock);
|
|
return ret;
|
|
}
|