2005-04-17 02:20:36 +04:00
/*
* 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 .
*
2005-08-17 18:57:43 +04:00
* $ Id : nodelist . c , v 1.111 2005 / 08 / 17 14 : 57 : 39 dedekind Exp $
2005-04-17 02:20:36 +04:00
*
*/
# include <linux/kernel.h>
# include <linux/sched.h>
# include <linux/fs.h>
# include <linux/mtd/mtd.h>
# include <linux/rbtree.h>
# include <linux/crc32.h>
# include <linux/slab.h>
# include <linux/pagemap.h>
# include "nodelist.h"
void jffs2_add_fd_to_list ( struct jffs2_sb_info * c , struct jffs2_full_dirent * new , struct jffs2_full_dirent * * list )
{
struct jffs2_full_dirent * * prev = list ;
2005-07-28 18:46:43 +04:00
JFFS2_DBG_DENTLIST ( " add dirent \" %s \" , ino #%u \n " , new - > name , new - > ino ) ;
2005-04-17 02:20:36 +04:00
while ( ( * prev ) & & ( * prev ) - > nhash < = new - > nhash ) {
if ( ( * prev ) - > nhash = = new - > nhash & & ! strcmp ( ( * prev ) - > name , new - > name ) ) {
/* Duplicate. Free one */
if ( new - > version < ( * prev ) - > version ) {
2005-07-28 18:46:43 +04:00
JFFS2_DBG_DENTLIST ( " Eep! Marking new dirent node is obsolete, old is \" %s \" , ino #%u \n " ,
( * prev ) - > name , ( * prev ) - > ino ) ;
2005-04-17 02:20:36 +04:00
jffs2_mark_node_obsolete ( c , new - > raw ) ;
jffs2_free_full_dirent ( new ) ;
} else {
2005-07-28 18:46:43 +04:00
JFFS2_DBG_DENTLIST ( " marking old dirent \" %s \" , ino #%u bsolete \n " ,
( * prev ) - > name , ( * prev ) - > ino ) ;
2005-04-17 02:20:36 +04:00
new - > next = ( * prev ) - > next ;
jffs2_mark_node_obsolete ( c , ( ( * prev ) - > raw ) ) ;
jffs2_free_full_dirent ( * prev ) ;
* prev = new ;
}
2005-07-28 18:46:43 +04:00
return ;
2005-04-17 02:20:36 +04:00
}
prev = & ( ( * prev ) - > next ) ;
}
new - > next = * prev ;
* prev = new ;
}
2005-07-31 12:20:48 +04:00
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 ) {
2005-08-01 16:05:22 +04:00
if ( frag - > ofs + frag - > size > size ) {
2005-07-31 12:20:48 +04:00
frag - > size = size - frag - > ofs ;
}
frag = frag_next ( frag ) ;
}
while ( frag & & frag - > ofs > = size ) {
struct jffs2_node_frag * next = frag_next ( frag ) ;
frag_erase ( frag , list ) ;
jffs2_obsolete_node_frag ( c , frag ) ;
frag = next ;
}
2005-08-01 16:05:22 +04:00
if ( size = = 0 )
return ;
/*
* If the last fragment starts at the RAM page boundary , it is
* REF_PRISTINE irrespective of its size .
*/
frag = frag_last ( list ) ;
if ( ( frag - > ofs & ( PAGE_CACHE_SIZE - 1 ) ) = = 0 ) {
JFFS2_DBG_FRAGTREE2 ( " marking the last fragment 0x%08x-0x%08x REF_PRISTINE. \n " ,
frag - > ofs , frag - > ofs + frag - > size ) ;
frag - > node - > raw - > flash_offset = ref_offset ( frag - > node - > raw ) | REF_PRISTINE ;
}
2005-07-31 12:20:48 +04:00
}
2005-07-27 18:46:14 +04:00
void jffs2_obsolete_node_frag ( struct jffs2_sb_info * c , struct jffs2_node_frag * this )
2005-04-17 02:20:36 +04:00
{
2005-07-27 18:46:14 +04:00
if ( this - > node ) {
this - > node - > frags - - ;
if ( ! this - > node - > frags ) {
/* The node has no valid frags left. It's totally obsoleted */
2005-07-28 18:46:43 +04:00
JFFS2_DBG_FRAGTREE2 ( " marking old node @0x%08x (0x%04x-0x%04x) obsolete \n " ,
ref_offset ( this - > node - > raw ) , this - > node - > ofs , this - > node - > ofs + this - > node - > size ) ;
2005-07-27 18:46:14 +04:00
jffs2_mark_node_obsolete ( c , this - > node - > raw ) ;
jffs2_free_full_dnode ( this - > node ) ;
} else {
2005-07-28 18:46:43 +04:00
JFFS2_DBG_FRAGTREE2 ( " marking old node @0x%08x (0x%04x-0x%04x) REF_NORMAL. frags is %d \n " ,
ref_offset ( this - > node - > raw ) , this - > node - > ofs , this - > node - > ofs + this - > node - > size , this - > node - > frags ) ;
2005-07-27 18:46:14 +04:00
mark_ref_normal ( this - > node - > raw ) ;
}
}
jffs2_free_node_frag ( this ) ;
2005-04-17 02:20:36 +04:00
}
2005-07-27 18:46:14 +04:00
static void jffs2_fragtree_insert ( struct jffs2_node_frag * newfrag , struct jffs2_node_frag * base )
2005-04-17 02:20:36 +04:00
{
2005-07-27 18:46:14 +04:00
struct rb_node * parent = & base - > rb ;
struct rb_node * * link = & parent ;
2005-07-06 01:03:10 +04:00
2005-07-28 18:46:43 +04:00
JFFS2_DBG_FRAGTREE2 ( " insert frag (0x%04x-0x%04x) \n " , newfrag - > ofs , newfrag - > ofs + newfrag - > size ) ;
2005-07-06 01:03:10 +04:00
2005-07-27 18:46:14 +04:00
while ( * link ) {
parent = * link ;
base = rb_entry ( parent , struct jffs2_node_frag , rb ) ;
if ( newfrag - > ofs > base - > ofs )
link = & base - > rb . rb_right ;
else if ( newfrag - > ofs < base - > ofs )
link = & base - > rb . rb_left ;
2005-07-06 01:03:10 +04:00
else {
2005-07-28 18:46:43 +04:00
JFFS2_ERROR ( " duplicate frag at %08x (%p,%p) \n " , newfrag - > ofs , newfrag , base ) ;
2005-07-27 18:46:14 +04:00
BUG ( ) ;
2005-07-06 01:03:10 +04:00
}
2005-04-17 02:20:36 +04:00
}
2005-07-27 18:46:14 +04:00
rb_link_node ( & newfrag - > rb , & base - > rb , link ) ;
2005-04-17 02:20:36 +04:00
}
2005-08-01 16:05:22 +04:00
/*
* Allocate and initializes a new fragment .
*/
static inline struct jffs2_node_frag * new_fragment ( struct jffs2_full_dnode * fn , uint32_t ofs , uint32_t size )
{
struct jffs2_node_frag * newfrag ;
newfrag = jffs2_alloc_node_frag ( ) ;
if ( likely ( newfrag ) ) {
newfrag - > ofs = ofs ;
newfrag - > size = size ;
newfrag - > node = fn ;
} else {
JFFS2_ERROR ( " cannot allocate a jffs2_node_frag object \n " ) ;
}
return newfrag ;
}
/*
* Called when there is no overlapping fragment exist . Inserts a hole before the new
* fragment and inserts the new fragment to the fragtree .
*/
static int no_overlapping_node ( struct jffs2_sb_info * c , struct rb_root * root ,
struct jffs2_node_frag * newfrag ,
struct jffs2_node_frag * this , uint32_t lastend )
{
if ( lastend < newfrag - > node - > ofs ) {
/* put a hole in before the new fragment */
struct jffs2_node_frag * holefrag ;
holefrag = new_fragment ( NULL , lastend , newfrag - > node - > ofs - lastend ) ;
if ( unlikely ( ! holefrag ) ) {
jffs2_free_node_frag ( newfrag ) ;
return - ENOMEM ;
}
if ( this ) {
/* By definition, the 'this' node has no right-hand child,
because there are no frags with offset greater than it .
So that ' s where we want to put the hole */
2005-08-03 13:26:50 +04:00
JFFS2_DBG_FRAGTREE2 ( " add hole frag %#04x-%#04x on the right of the new frag. \n " ,
2005-08-01 16:05:22 +04:00
holefrag - > ofs , holefrag - > ofs + holefrag - > size ) ;
rb_link_node ( & holefrag - > rb , & this - > rb , & this - > rb . rb_right ) ;
} else {
2005-08-03 13:26:50 +04:00
JFFS2_DBG_FRAGTREE2 ( " Add hole frag %#04x-%#04x to the root of the tree. \n " ,
2005-08-01 16:05:22 +04:00
holefrag - > ofs , holefrag - > ofs + holefrag - > size ) ;
rb_link_node ( & holefrag - > rb , NULL , & root - > rb_node ) ;
}
rb_insert_color ( & holefrag - > rb , root ) ;
this = holefrag ;
}
if ( this ) {
/* By definition, the 'this' node has no right-hand child,
because there are no frags with offset greater than it .
So that ' s where we want to put new fragment */
JFFS2_DBG_FRAGTREE2 ( " add the new node at the right \n " ) ;
rb_link_node ( & newfrag - > rb , & this - > rb , & this - > rb . rb_right ) ;
} else {
JFFS2_DBG_FRAGTREE2 ( " insert the new node at the root of the tree \n " ) ;
rb_link_node ( & newfrag - > rb , NULL , & root - > rb_node ) ;
}
rb_insert_color ( & newfrag - > rb , root ) ;
return 0 ;
}
2005-07-27 18:46:14 +04:00
/* Doesn't set inode->i_size */
2005-08-01 16:05:22 +04:00
static int jffs2_add_frag_to_fragtree ( struct jffs2_sb_info * c , struct rb_root * root , struct jffs2_node_frag * newfrag )
2005-04-17 02:20:36 +04:00
{
2005-07-27 18:46:14 +04:00
struct jffs2_node_frag * this ;
uint32_t lastend ;
2005-04-17 02:20:36 +04:00
2005-07-27 18:46:14 +04:00
/* Skip all the nodes which are completed before this one starts */
2005-08-01 16:05:22 +04:00
this = jffs2_lookup_node_frag ( root , newfrag - > node - > ofs ) ;
2005-04-17 02:20:36 +04:00
2005-07-27 18:46:14 +04:00
if ( this ) {
2005-07-28 18:46:43 +04:00
JFFS2_DBG_FRAGTREE2 ( " lookup gave frag 0x%04x-0x%04x; phys 0x%08x (*%p) \n " ,
this - > ofs , this - > ofs + this - > size , this - > node ? ( ref_offset ( this - > node - > raw ) ) : 0xffffffff , this ) ;
2005-07-27 18:46:14 +04:00
lastend = this - > ofs + this - > size ;
} else {
2005-07-28 18:46:43 +04:00
JFFS2_DBG_FRAGTREE2 ( " lookup gave no frag \n " ) ;
2005-07-27 18:46:14 +04:00
lastend = 0 ;
2005-04-17 02:20:36 +04:00
}
2005-07-27 18:46:14 +04:00
2005-08-01 16:05:22 +04:00
/* See if we ran off the end of the fragtree */
2005-07-27 18:46:14 +04:00
if ( lastend < = newfrag - > ofs ) {
/* We did */
/* Check if 'this' node was on the same page as the new node.
If so , both ' this ' and the new node get marked REF_NORMAL so
the GC can take a look .
*/
if ( lastend & & ( lastend - 1 ) > > PAGE_CACHE_SHIFT = = newfrag - > ofs > > PAGE_CACHE_SHIFT ) {
if ( this - > node )
mark_ref_normal ( this - > node - > raw ) ;
mark_ref_normal ( newfrag - > node - > raw ) ;
}
2005-04-17 02:20:36 +04:00
2005-08-01 16:05:22 +04:00
return no_overlapping_node ( c , root , newfrag , this , lastend ) ;
2005-07-15 14:13:57 +04:00
}
2005-08-01 16:05:22 +04:00
if ( this - > node )
JFFS2_DBG_FRAGTREE2 ( " dealing with frag %u-%u, phys %#08x(%d). \n " ,
this - > ofs , this - > ofs + this - > size ,
ref_offset ( this - > node - > raw ) , ref_flags ( this - > node - > raw ) ) ;
else
JFFS2_DBG_FRAGTREE2 ( " dealing with hole frag %u-%u. \n " ,
this - > ofs , this - > ofs + this - > size ) ;
2005-07-15 14:13:57 +04:00
2005-07-27 18:46:14 +04:00
/* OK. 'this' is pointing at the first frag that newfrag->ofs at least partially obsoletes,
* - i . e . newfrag - > ofs < this - > ofs + this - > size & & newfrag - > ofs > = this - > ofs
2005-07-15 14:13:57 +04:00
*/
2005-07-27 18:46:14 +04:00
if ( newfrag - > ofs > this - > ofs ) {
/* This node isn't completely obsoleted. The start of it remains valid */
/* Mark the new node and the partially covered node REF_NORMAL -- let
the GC take a look at them */
mark_ref_normal ( newfrag - > node - > raw ) ;
if ( this - > node )
mark_ref_normal ( this - > node - > raw ) ;
if ( this - > ofs + this - > size > newfrag - > ofs + newfrag - > size ) {
/* The new node splits 'this' frag into two */
2005-08-01 16:05:22 +04:00
struct jffs2_node_frag * newfrag2 ;
2005-07-27 18:46:14 +04:00
if ( this - > node )
2005-07-28 18:46:43 +04:00
JFFS2_DBG_FRAGTREE2 ( " split old frag 0x%04x-0x%04x, phys 0x%08x \n " ,
this - > ofs , this - > ofs + this - > size , ref_offset ( this - > node - > raw ) ) ;
2005-07-27 18:46:14 +04:00
else
2005-07-28 18:46:43 +04:00
JFFS2_DBG_FRAGTREE2 ( " split old hole frag 0x%04x-0x%04x \n " ,
2005-08-17 18:13:48 +04:00
this - > ofs , this - > ofs + this - > size ) ;
2005-07-27 18:46:14 +04:00
/* New second frag pointing to this's node */
2005-08-01 16:05:22 +04:00
newfrag2 = new_fragment ( this - > node , newfrag - > ofs + newfrag - > size ,
this - > ofs + this - > size - newfrag - > ofs - newfrag - > size ) ;
if ( unlikely ( ! newfrag2 ) )
return - ENOMEM ;
2005-07-27 18:46:14 +04:00
if ( this - > node )
this - > node - > frags + + ;
/* Adjust size of original 'this' */
this - > size = newfrag - > ofs - this - > ofs ;
/* Now, we know there's no node with offset
greater than this - > ofs but smaller than
newfrag2 - > ofs or newfrag - > ofs , for obvious
reasons . So we can do a tree insert from
' this ' to insert newfrag , and a tree insert
from newfrag to insert newfrag2 . */
jffs2_fragtree_insert ( newfrag , this ) ;
2005-08-01 16:05:22 +04:00
rb_insert_color ( & newfrag - > rb , root ) ;
2005-07-15 14:13:57 +04:00
2005-07-27 18:46:14 +04:00
jffs2_fragtree_insert ( newfrag2 , newfrag ) ;
2005-08-01 16:05:22 +04:00
rb_insert_color ( & newfrag2 - > rb , root ) ;
2005-07-15 14:13:57 +04:00
2005-07-27 18:46:14 +04:00
return 0 ;
2005-07-15 14:13:57 +04:00
}
2005-07-27 18:46:14 +04:00
/* New node just reduces 'this' frag in size, doesn't split it */
this - > size = newfrag - > ofs - this - > ofs ;
2005-07-15 14:13:57 +04:00
2005-07-27 18:46:14 +04:00
/* Again, we know it lives down here in the tree */
jffs2_fragtree_insert ( newfrag , this ) ;
2005-08-01 16:05:22 +04:00
rb_insert_color ( & newfrag - > rb , root ) ;
2005-07-27 18:46:14 +04:00
} else {
/* New frag starts at the same point as 'this' used to. Replace
it in the tree without doing a delete and insertion */
2005-07-28 18:46:43 +04:00
JFFS2_DBG_FRAGTREE2 ( " inserting newfrag (*%p),%d-%d in before 'this' (*%p),%d-%d \n " ,
newfrag , newfrag - > ofs , newfrag - > ofs + newfrag - > size , this , this - > ofs , this - > ofs + this - > size ) ;
2005-07-15 14:13:57 +04:00
2005-08-01 16:05:22 +04:00
rb_replace_node ( & this - > rb , & newfrag - > rb , root ) ;
2005-07-15 14:13:57 +04:00
2005-07-27 18:46:14 +04:00
if ( newfrag - > ofs + newfrag - > size > = this - > ofs + this - > size ) {
2005-07-28 18:46:43 +04:00
JFFS2_DBG_FRAGTREE2 ( " obsoleting node frag %p (%x-%x) \n " , this , this - > ofs , this - > ofs + this - > size ) ;
2005-07-27 18:46:14 +04:00
jffs2_obsolete_node_frag ( c , this ) ;
2005-07-15 14:13:57 +04:00
} else {
2005-07-27 18:46:14 +04:00
this - > ofs + = newfrag - > size ;
this - > size - = newfrag - > size ;
jffs2_fragtree_insert ( this , newfrag ) ;
2005-08-01 16:05:22 +04:00
rb_insert_color ( & this - > rb , root ) ;
2005-07-27 18:46:14 +04:00
return 0 ;
2005-07-15 14:13:57 +04:00
}
}
2005-07-27 18:46:14 +04:00
/* OK, now we have newfrag added in the correct place in the tree, but
frag_next ( newfrag ) may be a fragment which is overlapped by it
*/
while ( ( this = frag_next ( newfrag ) ) & & newfrag - > ofs + newfrag - > size > = this - > ofs + this - > size ) {
/* 'this' frag is obsoleted completely. */
2005-07-28 18:46:43 +04:00
JFFS2_DBG_FRAGTREE2 ( " obsoleting node frag %p (%x-%x) and removing from tree \n " ,
this , this - > ofs , this - > ofs + this - > size ) ;
2005-08-01 16:05:22 +04:00
rb_erase ( & this - > rb , root ) ;
2005-07-27 18:46:14 +04:00
jffs2_obsolete_node_frag ( c , this ) ;
2005-07-15 14:13:57 +04:00
}
2005-07-27 18:46:14 +04:00
/* Now we're pointing at the first frag which isn't totally obsoleted by
the new frag */
2005-07-15 14:13:57 +04:00
2005-08-01 16:05:22 +04:00
if ( ! this | | newfrag - > ofs + newfrag - > size = = this - > ofs )
2005-07-27 18:46:14 +04:00
return 0 ;
2005-08-01 16:05:22 +04:00
2005-07-27 18:46:14 +04:00
/* Still some overlap but we don't need to move it in the tree */
this - > size = ( this - > ofs + this - > size ) - ( newfrag - > ofs + newfrag - > size ) ;
this - > ofs = newfrag - > ofs + newfrag - > size ;
/* And mark them REF_NORMAL so the GC takes a look at them */
if ( this - > node )
mark_ref_normal ( this - > node - > raw ) ;
mark_ref_normal ( newfrag - > node - > raw ) ;
2005-07-15 14:13:57 +04:00
return 0 ;
}
2005-08-01 16:05:22 +04:00
/*
* Given an inode , probably with existing tree of fragments , add the new node
* to the fragment tree .
2005-07-15 14:13:57 +04:00
*/
2005-07-27 18:46:14 +04:00
int jffs2_add_full_dnode_to_inode ( struct jffs2_sb_info * c , struct jffs2_inode_info * f , struct jffs2_full_dnode * fn )
2005-07-15 14:13:57 +04:00
{
2005-07-27 18:46:14 +04:00
int ret ;
struct jffs2_node_frag * newfrag ;
2005-07-15 14:13:57 +04:00
2005-07-27 18:46:14 +04:00
if ( unlikely ( ! fn - > size ) )
return 0 ;
2005-07-15 14:13:57 +04:00
2005-08-01 16:05:22 +04:00
newfrag = new_fragment ( fn , fn - > ofs , fn - > size ) ;
2005-07-27 18:46:14 +04:00
if ( unlikely ( ! newfrag ) )
return - ENOMEM ;
2005-08-01 16:05:22 +04:00
newfrag - > node - > frags = 1 ;
2005-04-17 02:20:36 +04:00
2005-07-28 18:46:43 +04:00
JFFS2_DBG_FRAGTREE ( " adding node %#04x-%#04x @0x%08x on flash, newfrag *%p \n " ,
fn - > ofs , fn - > ofs + fn - > size , ref_offset ( fn - > raw ) , newfrag ) ;
2005-04-17 02:20:36 +04:00
2005-07-27 18:46:14 +04:00
ret = jffs2_add_frag_to_fragtree ( c , & f - > fragtree , newfrag ) ;
if ( unlikely ( ret ) )
return ret ;
/* If we now share a page with other nodes, mark either previous
or next node REF_NORMAL , as appropriate . */
if ( newfrag - > ofs & ( PAGE_CACHE_SIZE - 1 ) ) {
struct jffs2_node_frag * prev = frag_prev ( newfrag ) ;
mark_ref_normal ( fn - > raw ) ;
/* If we don't start at zero there's _always_ a previous */
if ( prev - > node )
mark_ref_normal ( prev - > node - > raw ) ;
}
2005-04-17 02:20:36 +04:00
2005-07-27 18:46:14 +04:00
if ( ( newfrag - > ofs + newfrag - > size ) & ( PAGE_CACHE_SIZE - 1 ) ) {
struct jffs2_node_frag * next = frag_next ( newfrag ) ;
if ( next ) {
mark_ref_normal ( fn - > raw ) ;
if ( next - > node )
mark_ref_normal ( next - > node - > raw ) ;
2005-04-17 02:20:36 +04:00
}
}
2005-07-27 18:46:14 +04:00
jffs2_dbg_fragtree_paranoia_check_nolock ( f ) ;
2005-08-01 16:05:22 +04:00
return 0 ;
}
/*
* Check the data CRC of the node .
*
* Returns : 0 if the data CRC is correct ;
* 1 - if incorrect ;
* error code if an error occured .
*/
static int check_node_data ( struct jffs2_sb_info * c , struct jffs2_tmp_dnode_info * tn )
{
struct jffs2_raw_node_ref * ref = tn - > fn - > raw ;
int err = 0 , pointed = 0 ;
struct jffs2_eraseblock * jeb ;
unsigned char * buffer ;
uint32_t crc , ofs , retlen , len ;
BUG_ON ( tn - > csize = = 0 ) ;
/* Calculate how many bytes were already checked */
ofs = ref_offset ( ref ) + sizeof ( struct jffs2_raw_inode ) ;
2005-08-04 15:40:02 +04:00
len = ofs & ( c - > wbuf_pagesize - 1 ) ;
2005-08-17 18:57:43 +04:00
if ( likely ( len ) )
len = c - > wbuf_pagesize - len ;
2005-08-01 16:05:22 +04:00
if ( len > = tn - > csize ) {
JFFS2_DBG_READINODE ( " no need to check node at %#08x, data length %u, data starts at %#08x - it has already been checked. \n " ,
ref_offset ( ref ) , tn - > csize , ofs ) ;
goto adj_acc ;
}
ofs + = len ;
len = tn - > csize - len ;
JFFS2_DBG_READINODE ( " check node at %#08x, data length %u, partial CRC %#08x, correct CRC %#08x, data starts at %#08x, start checking from %#08x - %u bytes. \n " ,
ref_offset ( ref ) , tn - > csize , tn - > partial_crc , tn - > data_crc , ofs - len , ofs , len ) ;
# ifndef __ECOS
/* TODO: instead, incapsulate point() stuff to jffs2_flash_read(),
* adding and jffs2_flash_read_end ( ) interface . */
if ( c - > mtd - > point ) {
err = c - > mtd - > point ( c - > mtd , ofs , len , & retlen , & buffer ) ;
if ( ! err & & retlen < tn - > csize ) {
JFFS2_WARNING ( " MTD point returned len too short: %u instead of %u. \n " , retlen , tn - > csize ) ;
c - > mtd - > unpoint ( c - > mtd , buffer , ofs , len ) ;
} else if ( err )
JFFS2_WARNING ( " MTD point failed: error code %d. \n " , err ) ;
else
pointed = 1 ; /* succefully pointed to device */
}
# endif
if ( ! pointed ) {
buffer = kmalloc ( len , GFP_KERNEL ) ;
if ( unlikely ( ! buffer ) )
return - ENOMEM ;
/* TODO: this is very frequent pattern, make it a separate
* routine */
err = jffs2_flash_read ( c , ofs , len , & retlen , buffer ) ;
if ( err ) {
JFFS2_ERROR ( " can not read %d bytes from 0x%08x, error code: %d. \n " , len , ofs , err ) ;
goto free_out ;
}
if ( retlen ! = len ) {
JFFS2_ERROR ( " short read at %#08x: %d instead of %d. \n " , ofs , retlen , len ) ;
err = - EIO ;
goto free_out ;
}
}
/* Continue calculating CRC */
crc = crc32 ( tn - > partial_crc , buffer , len ) ;
if ( ! pointed )
kfree ( buffer ) ;
# ifndef __ECOS
else
c - > mtd - > unpoint ( c - > mtd , buffer , ofs , len ) ;
# endif
if ( crc ! = tn - > data_crc ) {
2005-08-03 13:26:50 +04:00
JFFS2_NOTICE ( " wrong data CRC in data node at 0x%08x: read %#08x, calculated %#08x. \n " ,
2005-08-01 16:05:22 +04:00
ofs , tn - > data_crc , crc ) ;
return 1 ;
}
adj_acc :
jeb = & c - > blocks [ ref - > flash_offset / c - > sector_size ] ;
len = ref_totlen ( c , jeb , ref ) ;
/*
* Mark the node as having been checked and fix the
* accounting accordingly .
*/
spin_lock ( & c - > erase_completion_lock ) ;
jeb - > used_size + = len ;
jeb - > unchecked_size - = len ;
c - > used_size + = len ;
c - > unchecked_size - = len ;
spin_unlock ( & c - > erase_completion_lock ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
2005-08-01 16:05:22 +04:00
free_out :
if ( ! pointed )
kfree ( buffer ) ;
# ifndef __ECOS
else
c - > mtd - > unpoint ( c - > mtd , buffer , ofs , len ) ;
# endif
return err ;
2005-04-17 02:20:36 +04:00
}
2005-08-01 16:05:22 +04:00
/*
* Helper function for jffs2_add_older_frag_to_fragtree ( ) .
*
* Checks the node if we are in the checking stage .
*/
static inline int check_node ( struct jffs2_sb_info * c , struct jffs2_inode_info * f , struct jffs2_tmp_dnode_info * tn )
{
int ret ;
BUG_ON ( ref_obsolete ( tn - > fn - > raw ) ) ;
/* We only check the data CRC of unchecked nodes */
if ( ref_flags ( tn - > fn - > raw ) ! = REF_UNCHECKED )
return 0 ;
2005-08-03 13:26:50 +04:00
JFFS2_DBG_FRAGTREE2 ( " check node %#04x-%#04x, phys offs %#08x. \n " ,
tn - > fn - > ofs , tn - > fn - > ofs + tn - > fn - > size , ref_offset ( tn - > fn - > raw ) ) ;
2005-08-01 16:05:22 +04:00
ret = check_node_data ( c , tn ) ;
if ( unlikely ( ret < 0 ) ) {
JFFS2_ERROR ( " check_node_data() returned error: %d. \n " ,
ret ) ;
} else if ( unlikely ( ret > 0 ) ) {
JFFS2_DBG_FRAGTREE2 ( " CRC error, mark it obsolete. \n " ) ;
jffs2_mark_node_obsolete ( c , tn - > fn - > raw ) ;
}
return ret ;
}
/*
* Helper function for jffs2_add_older_frag_to_fragtree ( ) .
*
* Called when the new fragment that is being inserted
* splits a hole fragment .
*/
static int split_hole ( struct jffs2_sb_info * c , struct rb_root * root ,
struct jffs2_node_frag * newfrag , struct jffs2_node_frag * hole )
{
JFFS2_DBG_FRAGTREE2 ( " fragment %#04x-%#04x splits the hole %#04x-%#04x \n " ,
newfrag - > ofs , newfrag - > ofs + newfrag - > size , hole - > ofs , hole - > ofs + hole - > size ) ;
if ( hole - > ofs = = newfrag - > ofs ) {
/*
* Well , the new fragment actually starts at the same offset as
* the hole .
*/
if ( hole - > ofs + hole - > size > newfrag - > ofs + newfrag - > size ) {
/*
* We replace the overlapped left part of the hole by
* the new node .
*/
JFFS2_DBG_FRAGTREE2 ( " insert fragment %#04x-%#04x and cut the left part of the hole \n " ,
newfrag - > ofs , newfrag - > ofs + newfrag - > size ) ;
rb_replace_node ( & hole - > rb , & newfrag - > rb , root ) ;
hole - > ofs + = newfrag - > size ;
hole - > size - = newfrag - > size ;
/*
* We know that ' hole ' should be the right hand
* fragment .
*/
jffs2_fragtree_insert ( hole , newfrag ) ;
rb_insert_color ( & hole - > rb , root ) ;
} else {
/*
* Ah , the new fragment is of the same size as the hole .
* Relace the hole by it .
*/
JFFS2_DBG_FRAGTREE2 ( " insert fragment %#04x-%#04x and overwrite hole \n " ,
newfrag - > ofs , newfrag - > ofs + newfrag - > size ) ;
rb_replace_node ( & hole - > rb , & newfrag - > rb , root ) ;
jffs2_free_node_frag ( hole ) ;
}
} else {
/* The new fragment lefts some hole space at the left */
struct jffs2_node_frag * newfrag2 = NULL ;
if ( hole - > ofs + hole - > size > newfrag - > ofs + newfrag - > size ) {
/* The new frag also lefts some space at the right */
newfrag2 = new_fragment ( NULL , newfrag - > ofs +
newfrag - > size , hole - > ofs + hole - > size
- newfrag - > ofs - newfrag - > size ) ;
if ( unlikely ( ! newfrag2 ) ) {
jffs2_free_node_frag ( newfrag ) ;
return - ENOMEM ;
}
}
hole - > size = newfrag - > ofs - hole - > ofs ;
JFFS2_DBG_FRAGTREE2 ( " left the hole %#04x-%#04x at the left and inserd fragment %#04x-%#04x \n " ,
hole - > ofs , hole - > ofs + hole - > size , newfrag - > ofs , newfrag - > ofs + newfrag - > size ) ;
jffs2_fragtree_insert ( newfrag , hole ) ;
rb_insert_color ( & newfrag - > rb , root ) ;
if ( newfrag2 ) {
JFFS2_DBG_FRAGTREE2 ( " left the hole %#04x-%#04x at the right \n " ,
newfrag2 - > ofs , newfrag2 - > ofs + newfrag2 - > size ) ;
jffs2_fragtree_insert ( newfrag2 , newfrag ) ;
rb_insert_color ( & newfrag2 - > rb , root ) ;
}
}
return 0 ;
}
/*
* This function is used when we build inode . It expects the nodes are passed
* in the decreasing version order . The whole point of this is to improve the
* inodes checking on NAND : we check the nodes ' data CRC only when they are not
* obsoleted . Previously , add_frag_to_fragtree ( ) function was used and
* nodes were passed to it in the increasing version ordes and CRCs of all
* nodes were checked .
*
* Note : tn - > fn - > size shouldn ' t be zero .
*
* Returns 0 if the node was inserted
* 1 if it wasn ' t inserted ( since it is obsolete )
* < 0 an if error occured
*/
int jffs2_add_older_frag_to_fragtree ( struct jffs2_sb_info * c , struct jffs2_inode_info * f ,
struct jffs2_tmp_dnode_info * tn )
{
struct jffs2_node_frag * this , * newfrag ;
uint32_t lastend ;
struct jffs2_full_dnode * fn = tn - > fn ;
struct rb_root * root = & f - > fragtree ;
uint32_t fn_size = fn - > size , fn_ofs = fn - > ofs ;
int err , checked = 0 ;
int ref_flag ;
2005-08-03 13:26:50 +04:00
JFFS2_DBG_FRAGTREE ( " insert fragment %#04x-%#04x, ver %u \n " , fn_ofs , fn_ofs + fn_size , tn - > version ) ;
2005-08-01 16:05:22 +04:00
/* Skip all the nodes which are completed before this one starts */
this = jffs2_lookup_node_frag ( root , fn_ofs ) ;
if ( this )
JFFS2_DBG_FRAGTREE2 ( " 'this' found %#04x-%#04x (%s) \n " , this - > ofs , this - > ofs + this - > size , this - > node ? " data " : " hole " ) ;
if ( this )
lastend = this - > ofs + this - > size ;
else
lastend = 0 ;
/* Detect the preliminary type of node */
if ( fn - > size > = PAGE_CACHE_SIZE )
ref_flag = REF_PRISTINE ;
else
ref_flag = REF_NORMAL ;
/* See if we ran off the end of the root */
if ( lastend < = fn_ofs ) {
/* We did */
/*
* We are going to insert the new node into the
* fragment tree , so check it .
*/
err = check_node ( c , f , tn ) ;
if ( err ! = 0 )
return err ;
fn - > frags = 1 ;
newfrag = new_fragment ( fn , fn_ofs , fn_size ) ;
if ( unlikely ( ! newfrag ) )
return - ENOMEM ;
err = no_overlapping_node ( c , root , newfrag , this , lastend ) ;
if ( unlikely ( err ! = 0 ) ) {
jffs2_free_node_frag ( newfrag ) ;
return err ;
}
goto out_ok ;
}
fn - > frags = 0 ;
while ( 1 ) {
/*
* Here we have :
* fn_ofs < this - > ofs + this - > size & & fn_ofs > = this - > ofs .
*
* Remember , ' this ' has higher version , any non - hole node
* which is already in the fragtree is newer then the newly
* inserted .
*/
if ( ! this - > node ) {
/*
* ' this ' is the hole fragment , so at least the
* beginning of the new fragment is valid .
*/
/*
* We are going to insert the new node into the
* fragment tree , so check it .
*/
if ( ! checked ) {
err = check_node ( c , f , tn ) ;
if ( unlikely ( err ! = 0 ) )
return err ;
checked = 1 ;
}
if ( this - > ofs + this - > size > = fn_ofs + fn_size ) {
/* We split the hole on two parts */
fn - > frags + = 1 ;
newfrag = new_fragment ( fn , fn_ofs , fn_size ) ;
if ( unlikely ( ! newfrag ) )
return - ENOMEM ;
err = split_hole ( c , root , newfrag , this ) ;
if ( unlikely ( err ) )
return err ;
goto out_ok ;
}
/*
* The beginning of the new fragment is valid since it
* overlaps the hole node .
*/
ref_flag = REF_NORMAL ;
fn - > frags + = 1 ;
newfrag = new_fragment ( fn , fn_ofs ,
this - > ofs + this - > size - fn_ofs ) ;
if ( unlikely ( ! newfrag ) )
return - ENOMEM ;
if ( fn_ofs = = this - > ofs ) {
/*
* The new node starts at the same offset as
* the hole and supersieds the hole .
*/
JFFS2_DBG_FRAGTREE2 ( " add the new fragment instead of hole %#04x-%#04x, refcnt %d \n " ,
fn_ofs , fn_ofs + this - > ofs + this - > size - fn_ofs , fn - > frags ) ;
rb_replace_node ( & this - > rb , & newfrag - > rb , root ) ;
jffs2_free_node_frag ( this ) ;
} else {
/*
* The hole becomes shorter as its right part
* is supersieded by the new fragment .
*/
JFFS2_DBG_FRAGTREE2 ( " reduce size of hole %#04x-%#04x to %#04x-%#04x \n " ,
this - > ofs , this - > ofs + this - > size , this - > ofs , this - > ofs + this - > size - newfrag - > size ) ;
JFFS2_DBG_FRAGTREE2 ( " add new fragment %#04x-%#04x, refcnt %d \n " , fn_ofs ,
fn_ofs + this - > ofs + this - > size - fn_ofs , fn - > frags ) ;
this - > size - = newfrag - > size ;
jffs2_fragtree_insert ( newfrag , this ) ;
rb_insert_color ( & newfrag - > rb , root ) ;
}
fn_ofs + = newfrag - > size ;
fn_size - = newfrag - > size ;
this = rb_entry ( rb_next ( & newfrag - > rb ) ,
struct jffs2_node_frag , rb ) ;
JFFS2_DBG_FRAGTREE2 ( " switch to the next 'this' fragment: %#04x-%#04x %s \n " ,
this - > ofs , this - > ofs + this - > size , this - > node ? " (data) " : " (hole) " ) ;
}
/*
* ' This ' node is not the hole so it obsoletes the new fragment
* either fully or partially .
*/
if ( this - > ofs + this - > size > = fn_ofs + fn_size ) {
/* The new node is obsolete, drop it */
if ( fn - > frags = = 0 ) {
JFFS2_DBG_FRAGTREE2 ( " %#04x-%#04x is obsolete, mark it obsolete \n " , fn_ofs , fn_ofs + fn_size ) ;
ref_flag = REF_OBSOLETE ;
}
goto out_ok ;
} else {
struct jffs2_node_frag * new_this ;
/* 'This' node obsoletes the beginning of the new node */
JFFS2_DBG_FRAGTREE2 ( " the beginning %#04x-%#04x is obsolete \n " , fn_ofs , this - > ofs + this - > size ) ;
ref_flag = REF_NORMAL ;
fn_size - = this - > ofs + this - > size - fn_ofs ;
fn_ofs = this - > ofs + this - > size ;
JFFS2_DBG_FRAGTREE2 ( " now considering %#04x-%#04x \n " , fn_ofs , fn_ofs + fn_size ) ;
new_this = rb_entry ( rb_next ( & this - > rb ) , struct jffs2_node_frag , rb ) ;
if ( ! new_this ) {
/*
* There is no next fragment . Add the rest of
* the new node as the right - hand child .
*/
if ( ! checked ) {
err = check_node ( c , f , tn ) ;
if ( unlikely ( err ! = 0 ) )
return err ;
checked = 1 ;
}
fn - > frags + = 1 ;
newfrag = new_fragment ( fn , fn_ofs , fn_size ) ;
if ( unlikely ( ! newfrag ) )
return - ENOMEM ;
JFFS2_DBG_FRAGTREE2 ( " there are no more fragments, insert %#04x-%#04x \n " ,
newfrag - > ofs , newfrag - > ofs + newfrag - > size ) ;
rb_link_node ( & newfrag - > rb , & this - > rb , & this - > rb . rb_right ) ;
rb_insert_color ( & newfrag - > rb , root ) ;
goto out_ok ;
} else {
this = new_this ;
JFFS2_DBG_FRAGTREE2 ( " switch to the next 'this' fragment: %#04x-%#04x %s \n " ,
this - > ofs , this - > ofs + this - > size , this - > node ? " (data) " : " (hole) " ) ;
}
}
}
out_ok :
BUG_ON ( fn - > size < PAGE_CACHE_SIZE & & ref_flag = = REF_PRISTINE ) ;
if ( ref_flag = = REF_OBSOLETE ) {
JFFS2_DBG_FRAGTREE2 ( " the node is obsolete now \n " ) ;
/* jffs2_mark_node_obsolete() will adjust space accounting */
jffs2_mark_node_obsolete ( c , fn - > raw ) ;
return 1 ;
}
JFFS2_DBG_FRAGTREE2 ( " the node is \" %s \" now \n " , ref_flag = = REF_NORMAL ? " REF_NORMAL " : " REF_PRISTINE " ) ;
/* Space accounting was adjusted at check_node_data() */
spin_lock ( & c - > erase_completion_lock ) ;
fn - > raw - > flash_offset = ref_offset ( fn - > raw ) | ref_flag ;
spin_unlock ( & c - > erase_completion_lock ) ;
return 0 ;
}
2005-07-27 18:46:14 +04:00
2005-04-17 02:20:36 +04:00
void jffs2_set_inocache_state ( struct jffs2_sb_info * c , struct jffs2_inode_cache * ic , int state )
{
spin_lock ( & c - > inocache_lock ) ;
ic - > state = state ;
wake_up ( & c - > inocache_wq ) ;
spin_unlock ( & c - > inocache_lock ) ;
}
/* During mount, this needs no locking. During normal operation, its
callers want to do other stuff while still holding the inocache_lock .
Rather than introducing special case get_ino_cache functions or
callbacks , we just let the caller do the locking itself . */
struct jffs2_inode_cache * jffs2_get_ino_cache ( struct jffs2_sb_info * c , uint32_t ino )
{
struct jffs2_inode_cache * ret ;
ret = c - > inocache_list [ ino % INOCACHE_HASHSIZE ] ;
while ( ret & & ret - > ino < ino ) {
ret = ret - > next ;
}
if ( ret & & ret - > ino ! = ino )
ret = NULL ;
return ret ;
}
void jffs2_add_ino_cache ( struct jffs2_sb_info * c , struct jffs2_inode_cache * new )
{
struct jffs2_inode_cache * * prev ;
2005-05-22 23:47:19 +04:00
2005-04-17 02:20:36 +04:00
spin_lock ( & c - > inocache_lock ) ;
2005-05-22 23:47:19 +04:00
if ( ! new - > ino )
new - > ino = + + c - > highest_ino ;
2005-07-28 18:46:43 +04:00
JFFS2_DBG_INOCACHE ( " add %p (ino #%u) \n " , new , new - > ino ) ;
2005-05-22 23:47:19 +04:00
2005-04-17 02:20:36 +04:00
prev = & c - > inocache_list [ new - > ino % INOCACHE_HASHSIZE ] ;
while ( ( * prev ) & & ( * prev ) - > ino < new - > ino ) {
prev = & ( * prev ) - > next ;
}
new - > next = * prev ;
* prev = new ;
spin_unlock ( & c - > inocache_lock ) ;
}
void jffs2_del_ino_cache ( struct jffs2_sb_info * c , struct jffs2_inode_cache * old )
{
struct jffs2_inode_cache * * prev ;
2005-07-28 18:46:43 +04:00
JFFS2_DBG_INOCACHE ( " del %p (ino #%u) \n " , old , old - > ino ) ;
2005-04-17 02:20:36 +04:00
spin_lock ( & c - > inocache_lock ) ;
prev = & c - > inocache_list [ old - > ino % INOCACHE_HASHSIZE ] ;
while ( ( * prev ) & & ( * prev ) - > ino < old - > ino ) {
prev = & ( * prev ) - > next ;
}
if ( ( * prev ) = = old ) {
* prev = old - > next ;
}
2005-02-28 02:01:36 +03:00
/* Free it now unless it's in READING or CLEARING state, which
are the transitions upon read_inode ( ) and clear_inode ( ) . The
rest of the time we know nobody else is looking at it , and
if it ' s held by read_inode ( ) or clear_inode ( ) they ' ll free it
for themselves . */
if ( old - > state ! = INO_STATE_READING & & old - > state ! = INO_STATE_CLEARING )
jffs2_free_inode_cache ( old ) ;
2005-04-17 02:20:36 +04:00
spin_unlock ( & c - > inocache_lock ) ;
}
void jffs2_free_ino_caches ( struct jffs2_sb_info * c )
{
int i ;
struct jffs2_inode_cache * this , * next ;
for ( i = 0 ; i < INOCACHE_HASHSIZE ; i + + ) {
this = c - > inocache_list [ i ] ;
while ( this ) {
next = this - > next ;
jffs2_free_inode_cache ( this ) ;
this = next ;
}
c - > inocache_list [ i ] = NULL ;
}
}
void jffs2_free_raw_node_refs ( struct jffs2_sb_info * c )
{
int i ;
struct jffs2_raw_node_ref * this , * next ;
for ( i = 0 ; i < c - > nr_blocks ; i + + ) {
this = c - > blocks [ i ] . first_node ;
while ( this ) {
next = this - > next_phys ;
jffs2_free_raw_node_ref ( this ) ;
this = next ;
}
c - > blocks [ i ] . first_node = c - > blocks [ i ] . last_node = NULL ;
}
}
struct jffs2_node_frag * jffs2_lookup_node_frag ( struct rb_root * fragtree , uint32_t offset )
{
/* The common case in lookup is that there will be a node
which precisely matches . So we go looking for that first */
struct rb_node * next ;
struct jffs2_node_frag * prev = NULL ;
struct jffs2_node_frag * frag = NULL ;
2005-07-28 18:46:43 +04:00
JFFS2_DBG_FRAGTREE2 ( " root %p, offset %d \n " , fragtree , offset ) ;
2005-04-17 02:20:36 +04:00
next = fragtree - > rb_node ;
while ( next ) {
frag = rb_entry ( next , struct jffs2_node_frag , rb ) ;
if ( frag - > ofs + frag - > size < = offset ) {
/* Remember the closest smaller match on the way down */
if ( ! prev | | frag - > ofs > prev - > ofs )
prev = frag ;
next = frag - > rb . rb_right ;
} else if ( frag - > ofs > offset ) {
next = frag - > rb . rb_left ;
} else {
return frag ;
}
}
/* Exact match not found. Go back up looking at each parent,
and return the closest smaller one */
if ( prev )
2005-07-28 18:46:43 +04:00
JFFS2_DBG_FRAGTREE2 ( " no match. Returning frag %#04x-%#04x, closest previous \n " ,
prev - > ofs , prev - > ofs + prev - > size ) ;
2005-04-17 02:20:36 +04:00
else
2005-07-28 18:46:43 +04:00
JFFS2_DBG_FRAGTREE2 ( " returning NULL, empty fragtree \n " ) ;
2005-04-17 02:20:36 +04:00
return prev ;
}
/* Pass 'c' argument to indicate that nodes should be marked obsolete as
they ' re killed . */
void jffs2_kill_fragtree ( struct rb_root * root , struct jffs2_sb_info * c )
{
struct jffs2_node_frag * frag ;
struct jffs2_node_frag * parent ;
if ( ! root - > rb_node )
return ;
2005-07-28 18:46:43 +04:00
JFFS2_DBG_FRAGTREE ( " killing \n " ) ;
2005-04-17 02:20:36 +04:00
frag = ( rb_entry ( root - > rb_node , struct jffs2_node_frag , rb ) ) ;
while ( frag ) {
if ( frag - > rb . rb_left ) {
frag = frag_left ( frag ) ;
continue ;
}
if ( frag - > rb . rb_right ) {
frag = frag_right ( frag ) ;
continue ;
}
if ( frag - > node & & ! ( - - frag - > node - > frags ) ) {
/* Not a hole, and it's the final remaining frag
of this node . Free the node */
if ( c )
jffs2_mark_node_obsolete ( c , frag - > node - > raw ) ;
jffs2_free_full_dnode ( frag - > node ) ;
}
parent = frag_parent ( frag ) ;
if ( parent ) {
if ( frag_left ( parent ) = = frag )
parent - > rb . rb_left = NULL ;
else
parent - > rb . rb_right = NULL ;
}
jffs2_free_node_frag ( frag ) ;
frag = parent ;
cond_resched ( ) ;
}
}