/*
* 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.132 2005/07/28 14:46:40 dedekind Exp $
*
*/
#include <linux/kernel.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"
void jffs2_truncate_fragtree (struct jffs2_sb_info *c, struct rb_root *list, uint32_t size)
{
struct jffs2_node_frag *frag = jffs2_lookup_node_frag(list, size);
JFFS2_DBG_FRAGTREE("truncating fragtree to 0x%08x bytes\n", size);
/* We know frag->ofs <= size. That's what lookup does for us */
if (frag && frag->ofs != size) {
if (frag->ofs+frag->size >= size) {
JFFS2_DBG_FRAGTREE2("truncating frag 0x%08x-0x%08x\n", frag->ofs, frag->ofs+frag->size);
frag->size = size - frag->ofs;
}
frag = frag_next(frag);
}
while (frag && frag->ofs >= size) {
struct jffs2_node_frag *next = frag_next(frag);
JFFS2_DBG_FRAGTREE("removing frag 0x%08x-0x%08x\n", frag->ofs, frag->ofs+frag->size);
frag_erase(frag, list);
jffs2_obsolete_node_frag(c, frag);
frag = next;
}
}
/*
* 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 = this->rb_parent;
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;
JFFS2_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,
uint32_t read,
struct jffs2_full_dirent **fdp,
int32_t *latest_mctime,
uint32_t *mctime_ver)
{
struct jffs2_full_dirent *fd;
/* The direntry nodes are checked during the flash scanning */
BUG_ON(ref_flags(ref) == REF_UNCHECKED);
/* Obsoleted. This cannot happen, surely? dwmw2 20020308 */
BUG_ON(ref_obsolete(ref));
/* 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;
}
fd = jffs2_alloc_full_dirent(rd->nsize + 1);
if (unlikely(!fd))
return -ENOMEM;
fd->raw = ref;