/*
* JFFS2 -- Journalling Flash File System, Version 2.
*
* Copyright (C) 2001-2003 Red Hat, Inc.
*
* Created by David Woodhouse <dwmw2@infradead.org>
*
* For licensing information, see the file 'LICENCE' in this directory.
*
* $Id: readinode.c,v 1.143 2005/11/07 11:14:41 gleixner Exp $
*
*/
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/fs.h>
#include <linux/crc32.h>
#include <linux/pagemap.h>
#include <linux/mtd/mtd.h>
#include <linux/compiler.h>
#include "nodelist.h"
/*
* Put a new tmp_dnode_info into the temporaty RB-tree, keeping the list in
* order of increasing version.
*/
static void jffs2_add_tn_to_tree(struct jffs2_tmp_dnode_info *tn, struct rb_root *list)
{
struct rb_node **p = &list->rb_node;
struct rb_node * parent = NULL;
struct jffs2_tmp_dnode_info *this;
while (*p) {
parent = *p;
this = rb_entry(parent, struct jffs2_tmp_dnode_info, rb);
/* There may actually be a collision here, but it doesn't
actually matter. As long as the two nodes with the same
version are together, it's all fine. */
if (tn->version > this->version)
p = &(*p)->rb_left;
else
p = &(*p)->rb_right;
}
rb_link_node(&tn->rb, parent, p);
rb_insert_color(&tn->rb, list);
}
static void jffs2_free_tmp_dnode_info_list(struct rb_root *list)
{
struct rb_node *this;
struct jffs2_tmp_dnode_info *tn;
this = list->rb_node;
/* Now at bottom of tree */
while (this) {
if (this->rb_left)
this = this->rb_left;
else if (this->rb_right)
this = this->rb_right;
else {
tn = rb_entry(this, struct jffs2_tmp_dnode_info, rb);
jffs2_free_full_dnode(tn->fn);
jffs2_free_tmp_dnode_info(tn);
this = rb_parent(this);
if (!this)
break;
if (this->rb_left == &tn->rb)
this->rb_left = NULL;
else if (this->rb_right == &tn->rb)
this->rb_right = NULL;
else BUG();
}
}
list->rb_node = NULL;
}
static void jffs2_free_full_dirent_list(struct jffs2_full_dirent *fd)
{
struct jffs2_full_dirent *next;
while (fd) {
next = fd->next;
jffs2_free_full_dirent(fd);
fd = next;
}
}
/* Returns first valid node after 'ref'. May return 'ref' */
static struct jffs2_raw_node_ref *jffs2_first_valid_node(struct jffs2_raw_node_ref *ref)
{
while (ref && ref->next_in_ino) {
if (!ref_obsolete(ref))
return ref;
dbg_noderef("node at 0x%08x is obsoleted. Ignoring.\n", ref_offset(ref));
ref = ref->next_in_ino;
}
return NULL;
}
/*
* Helper function for jffs2_get_inode_nodes().
* It is called every time an directory entry node is found.
*
* Returns: 0 on succes;
* 1 if the node should be marked obsolete;
* negative error code on failure.
*/
static inline int read_direntry(struct jffs2_sb_info *c, struct jffs2_raw_node_ref *ref,
struct jffs2_raw_dirent *rd, size_t read, struct jffs2_full_dirent **fdp,
uint32_t *latest_mctime, uint32_t *mctime_ver)
{
struct jffs2_full_dirent *fd;
uint32_t crc;
/* Obsoleted. This cannot happen, surely? dwmw2 20020308 */
BUG_ON(ref_obsolete(ref));
crc = crc32(0, rd, sizeof(*rd) - 8);
if (unlikely(crc != je32_to_cpu(rd->node_crc))) {
JFFS2_NOTICE("header CRC failed on dirent node at %#08x: read %#08x, calculated %#08x\n",
ref_offset(ref), je32_to_cpu(rd->node_crc), crc);
return 1;
}
/* If we've never checked the CRCs on this node, check them now */
if (ref_flags(ref) == REF_UNCHECKED) {
struct jffs2_eraseblock *jeb;
int len;
/* Sanity check */
if (unlikely(PAD((rd->nsize + sizeof(*rd))) != PAD(je32_to_cpu(rd->totlen)))) {
JFFS2_ERROR("illegal nsize in node at %#08x: nsize %#02x, totlen %#04x\n",
ref_offset(ref), rd->nsize, je32_to_cpu(rd->totlen));
return 1;
}
jeb = &c->blocks[ref->flash_offset / c->sector_size];
len = ref_totlen(