mtd/fs/jffs2 nodelist.c, 1.100, 1.101 nodelist.h, 1.134, 1.135 readinode.c, 1.130, 1.131

Artem Bityuckiy dedekind at infradead.org
Wed Jul 27 10:46:14 EDT 2005


Update of /home/cvs/mtd/fs/jffs2
In directory phoenix.infradead.org:/tmp/cvs-serv22066

Modified Files:
	nodelist.c nodelist.h readinode.c 
Log Message:
[JFFS2] re-distribute functions over files

Place everything related to how we read the inode to readinode.c.
Put everything related to how we handle the fragtree and the list of dents to
nodelist.h



Index: nodelist.c
===================================================================
RCS file: /home/cvs/mtd/fs/jffs2/nodelist.c,v
retrieving revision 1.100
retrieving revision 1.101
diff -u -r1.100 -r1.101
--- nodelist.c	22 Jul 2005 10:32:08 -0000	1.100
+++ nodelist.c	27 Jul 2005 14:46:11 -0000	1.101
@@ -55,515 +55,284 @@
 	});
 }
 
-/* 
- * 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)
+void jffs2_obsolete_node_frag(struct jffs2_sb_info *c, struct jffs2_node_frag *this)
 {
-	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();
+	if (this->node) {
+		this->node->frags--;
+		if (!this->node->frags) {
+			/* The node has no valid frags left. It's totally obsoleted */
+			D2(printk(KERN_DEBUG "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));
+			jffs2_mark_node_obsolete(c, this->node->raw);
+			jffs2_free_full_dnode(this->node);
+		} else {
+			D2(printk(KERN_DEBUG "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));
+			mark_ref_normal(this->node->raw);
 		}
+		
 	}
-	list->rb_node = NULL;
+	jffs2_free_node_frag(this);
 }
 
-static void jffs2_free_full_dirent_list(struct jffs2_full_dirent *fd)
+static void jffs2_fragtree_insert(struct jffs2_node_frag *newfrag, struct jffs2_node_frag *base)
 {
-	struct jffs2_full_dirent *next;
-
-	while (fd) {
-		next = fd->next;
-		jffs2_free_full_dirent(fd);
-		fd = next;
-	}
-}
+	struct rb_node *parent = &base->rb;
+	struct rb_node **link = &parent;
 
-/* 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;
-		D1(printk(KERN_DEBUG "node at 0x%08x is obsoleted. Ignoring.\n", ref_offset(ref)));
-		ref = ref->next_in_ino;
-	}
-	return NULL;
-}
+	D2(printk(KERN_DEBUG "jffs2_fragtree_insert(%p; %d-%d, %p)\n", newfrag, 
+		  newfrag->ofs, newfrag->ofs+newfrag->size, base));
 
-/*
- * 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)))) {
-		printk(KERN_ERR "Error! Illegal nsize in node at %#08x: nsize %#02x, totlen %#04x\n",
-		       ref_offset(ref), rd->nsize, je32_to_cpu(rd->totlen));
-		return 1;
-	}
+	while (*link) {
+		parent = *link;
+		base = rb_entry(parent, struct jffs2_node_frag, rb);
 	
-	fd = jffs2_alloc_full_dirent(rd->nsize + 1);
-	if (unlikely(!fd))
-		return -ENOMEM;
-
-	fd->raw = ref;
-	fd->version = je32_to_cpu(rd->version);
-	fd->ino = je32_to_cpu(rd->ino);
-	fd->type = rd->type;
-
-	/* Pick out the mctime of the latest dirent */
-	if(fd->version > *mctime_ver) {
-		*mctime_ver = fd->version;
-		*latest_mctime = je32_to_cpu(rd->mctime);
-	}
-
-	/* 
-	 * Copy as much of the name as possible from the raw
-	 * dirent we've already read from the flash.
-	 */
-	if (read > sizeof(*rd))
-		memcpy(&fd->name[0], &rd->name[0],
-		       min_t(uint32_t, rd->nsize, (read - sizeof(*rd)) ));
-		
-	/* Do we need to copy any more of the name directly from the flash? */
-	if (rd->nsize + sizeof(*rd) > read) {
-		/* FIXME: point() */
-		int err;
-		int already = read - sizeof(*rd);
-			
-		err = jffs2_flash_read(c, (ref_offset(ref)) + read, 
-				rd->nsize - already, &read, &fd->name[already]);
-		if (unlikely(read != rd->nsize - already) && likely(!err))
-			return -EIO;
-			
-		if (unlikely(err)) {
-			printk(KERN_WARNING "Read remainder of name: error %d\n", err);
-			jffs2_free_full_dirent(fd);
-			return -EIO;
+		D2(printk(KERN_DEBUG "fragtree_insert considering frag at 0x%x\n", base->ofs));
+		if (newfrag->ofs > base->ofs)
+			link = &base->rb.rb_right;
+		else if (newfrag->ofs < base->ofs)
+			link = &base->rb.rb_left;
+		else {
+			printk(KERN_CRIT "Duplicate frag at %08x (%p,%p)\n", newfrag->ofs, newfrag, base);
+			BUG();
 		}
 	}
-	
-	fd->nhash = full_name_hash(fd->name, rd->nsize);
-	fd->next = NULL;
-	fd->name[rd->nsize] = '\0';
-	
-	/*
-	 * Wheee. We now have a complete jffs2_full_dirent structure, with
-	 * the name in it and everything. Link it into the list 
-	 */
-	D1(printk(KERN_DEBUG "Adding fd \"%s\", ino #%u\n", fd->name, fd->ino));
 
-	jffs2_add_fd_to_list(c, fd, fdp);
-
-	return 0;
+	rb_link_node(&newfrag->rb, &base->rb, link);
 }
 
-/*
- * Helper function for jffs2_get_inode_nodes().
- * It is called every time an inode node is found.
- *
- * Returns: 0 on succes;
- * 	    1 if the node should be marked obsolete;
- * 	    negative error code on failure.
- */
-static inline int
-read_dnode(struct jffs2_sb_info *c,
-	   struct jffs2_raw_node_ref *ref,
-	   struct jffs2_raw_inode *rd,
-	   uint32_t read,
-	   struct rb_root *tnp,
-	   int32_t *latest_mctime,
-	   uint32_t *mctime_ver)
+/* Doesn't set inode->i_size */
+static int jffs2_add_frag_to_fragtree(struct jffs2_sb_info *c, struct rb_root *list, struct jffs2_node_frag *newfrag)
 {
-	struct jffs2_eraseblock *jeb;
-	struct jffs2_tmp_dnode_info *tn;
-	
-	/* Obsoleted. This cannot happen, surely? dwmw2 20020308 */
-	BUG_ON(ref_obsolete(ref));
+	struct jffs2_node_frag *this;
+	uint32_t lastend;
 
-	/* If we've never checked the CRCs on this node, check them now */
-	if (ref_flags(ref) == REF_UNCHECKED) {
-		uint32_t crc, len;
-
-		crc = crc32(0, rd, sizeof(*rd) - 8);
-		if (unlikely(crc != je32_to_cpu(rd->node_crc))) {
-			printk(KERN_WARNING "Header CRC failed on node at %#08x: read %#08x, calculated %#08x\n",
-					ref_offset(ref), je32_to_cpu(rd->node_crc), crc);
-			return 1;
+	/* Skip all the nodes which are completed before this one starts */
+	this = jffs2_lookup_node_frag(list, newfrag->node->ofs);
+
+	if (this) {
+		D2(printk(KERN_DEBUG "j_a_f_d_t_f: 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));
+		lastend = this->ofs + this->size;
+	} else {
+		D2(printk(KERN_DEBUG "j_a_f_d_t_f: Lookup gave no frag\n"));
+		lastend = 0;
+	}
+			  
+	/* See if we ran off the end of the list */
+	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);
 		}
-		
-		/* Sanity checks */
-		if (unlikely(je32_to_cpu(rd->offset) > je32_to_cpu(rd->isize)) ||
-		    unlikely(PAD(je32_to_cpu(rd->csize) + sizeof(*rd)) != PAD(je32_to_cpu(rd->totlen)))) {
-			printk(KERN_WARNING "Inode corrupted at %#08x, totlen %d, #ino  %d, version %d, "
-				"isize %d, csize %d, dsize %d \n",
-				ref_offset(ref),  je32_to_cpu(rd->totlen),  je32_to_cpu(rd->ino),
-				je32_to_cpu(rd->version),  je32_to_cpu(rd->isize), 
-				je32_to_cpu(rd->csize), je32_to_cpu(rd->dsize));
-			return 1;
-		}
-
-		if (rd->compr != JFFS2_COMPR_ZERO && je32_to_cpu(rd->csize)) {
-			unsigned char *buf = NULL;
-			uint32_t pointed = 0;
-			int err;
-#ifndef __ECOS
-			if (c->mtd->point) {
-				err = c->mtd->point (c->mtd, ref_offset(ref) + sizeof(*rd), je32_to_cpu(rd->csize),
-						     &read, &buf);
-				if (unlikely(read < je32_to_cpu(rd->csize)) && likely(!err)) {
-					D1(printk(KERN_DEBUG "MTD point returned len too short: 0x%zx\n", read));
-					c->mtd->unpoint(c->mtd, buf, ref_offset(ref) + sizeof(*rd),
-							je32_to_cpu(rd->csize));
-				} else if (unlikely(err)){
-					D1(printk(KERN_DEBUG "MTD point failed %d\n", err));
-				} else
-					pointed = 1; /* succefully pointed to device */
-			}
-#endif					
-			if(!pointed){
-				buf = kmalloc(je32_to_cpu(rd->csize), GFP_KERNEL);
-				if (!buf)
-					return -ENOMEM;
-				
-				err = jffs2_flash_read(c, ref_offset(ref) + sizeof(*rd), je32_to_cpu(rd->csize),
-							&read, buf);
-				if (unlikely(read != je32_to_cpu(rd->csize)) && likely(!err))
-					err = -EIO;
-				if (err) {
-					kfree(buf);
-					return err;
-				}
+
+		if (lastend < newfrag->node->ofs) {
+			/* ... and we need to put a hole in before the new node */
+			struct jffs2_node_frag *holefrag = jffs2_alloc_node_frag();
+			if (!holefrag) {
+				jffs2_free_node_frag(newfrag);
+				return -ENOMEM;
 			}
-			crc = crc32(0, buf, je32_to_cpu(rd->csize));
-			if(!pointed)
-				kfree(buf);
-#ifndef __ECOS
-			else
-				c->mtd->unpoint(c->mtd, buf, ref_offset(ref) + sizeof(*rd), je32_to_cpu(rd->csize));
-#endif
-
-			if (crc != je32_to_cpu(rd->data_crc)) {
-				printk(KERN_NOTICE "Data CRC failed on node at %#08x: read %#08x, calculated %#08x\n",
-				       ref_offset(ref), je32_to_cpu(rd->data_crc), crc);
-				return 1;
+			holefrag->ofs = lastend;
+			holefrag->size = newfrag->node->ofs - lastend;
+			holefrag->node = NULL;
+			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 */
+				D2(printk(KERN_DEBUG "Adding hole frag (%p) on right of node at (%p)\n", holefrag, this));
+				rb_link_node(&holefrag->rb, &this->rb, &this->rb.rb_right);
+			} else {
+				D2(printk(KERN_DEBUG "Adding hole frag (%p) at root of tree\n", holefrag));
+				rb_link_node(&holefrag->rb, NULL, &list->rb_node);
 			}
-			
+			rb_insert_color(&holefrag->rb, list);
+			this = holefrag;
 		}
-
-		/* Mark the node as having been checked and fix the accounting accordingly */
-		jeb = &c->blocks[ref->flash_offset / c->sector_size];
-		len = ref_totlen(c, jeb, ref);
-
-		spin_lock(&c->erase_completion_lock);
-		jeb->used_size += len;
-		jeb->unchecked_size -= len;
-		c->used_size += len;
-		c->unchecked_size -= len;
-
-		/* If node covers at least a whole page, or if it starts at the 
-		   beginning of a page and runs to the end of the file, or if 
-		   it's a hole node, mark it REF_PRISTINE, else REF_NORMAL. 
-
-		   If it's actually overlapped, it'll get made NORMAL (or OBSOLETE) 
-		   when the overlapping node(s) get added to the tree anyway. 
-		*/
-		if ((je32_to_cpu(rd->dsize) >= PAGE_CACHE_SIZE) ||
-		    ( ((je32_to_cpu(rd->offset) & (PAGE_CACHE_SIZE-1))==0) &&
-		      (je32_to_cpu(rd->dsize) + je32_to_cpu(rd->offset) == je32_to_cpu(rd->isize)))) {
-			D1(printk(KERN_DEBUG "Marking node at %#08x REF_PRISTINE\n", ref_offset(ref)));
-			ref->flash_offset = ref_offset(ref) | REF_PRISTINE;
+		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 */
+			D2(printk(KERN_DEBUG "Adding new frag (%p) on right of node at (%p)\n", newfrag, this));
+			rb_link_node(&newfrag->rb, &this->rb, &this->rb.rb_right);			
 		} else {
-			D1(printk(KERN_DEBUG "Marking node at %#08x REF_NORMAL\n", ref_offset(ref)));
-			ref->flash_offset = ref_offset(ref) | REF_NORMAL;
+			D2(printk(KERN_DEBUG "Adding new frag (%p) at root of tree\n", newfrag));
+			rb_link_node(&newfrag->rb, NULL, &list->rb_node);
 		}
-		spin_unlock(&c->erase_completion_lock);
+		rb_insert_color(&newfrag->rb, list);
+		return 0;
 	}
 
-	tn = jffs2_alloc_tmp_dnode_info();
-	if (!tn) {
-		D1(printk(KERN_DEBUG "alloc tn failed\n"));
-		return -ENOMEM;
-	}
+	D2(printk(KERN_DEBUG "j_a_f_d_t_f: dealing with 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));
 
-	tn->fn = jffs2_alloc_full_dnode();
-	if (!tn->fn) {
-		D1(printk(KERN_DEBUG "alloc fn failed\n"));
-		jffs2_free_tmp_dnode_info(tn);
-		return -ENOMEM;
-	}
-	
-	tn->version = je32_to_cpu(rd->version);
-	tn->fn->ofs = je32_to_cpu(rd->offset);
-	tn->fn->raw = ref;
-	
-	/* There was a bug where we wrote hole nodes out with
-	   csize/dsize swapped. Deal with it */
-	if (rd->compr == JFFS2_COMPR_ZERO && !je32_to_cpu(rd->dsize) && je32_to_cpu(rd->csize))
-		tn->fn->size = je32_to_cpu(rd->csize);
-	else // normal case...
-		tn->fn->size = je32_to_cpu(rd->dsize);
-
-	D1(printk(KERN_DEBUG "dnode @%08x: ver %u, offset %#04x, dsize %#04x\n",
-		  ref_offset(ref), je32_to_cpu(rd->version),
-		  je32_to_cpu(rd->offset), je32_to_cpu(rd->dsize)));
+	/* 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  
+	 */
+	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 */
+			struct jffs2_node_frag *newfrag2 = jffs2_alloc_node_frag();
+			if (!newfrag2) {
+				jffs2_free_node_frag(newfrag);
+				return -ENOMEM;
+			}
+			D2(printk(KERN_DEBUG "split old frag 0x%04x-0x%04x -->", this->ofs, this->ofs+this->size);
+			if (this->node)
+				printk("phys 0x%08x\n", ref_offset(this->node->raw));
+			else 
+				printk("hole\n");
+			   )
+			
+			/* New second frag pointing to this's node */
+			newfrag2->ofs = newfrag->ofs + newfrag->size;
+			newfrag2->size = (this->ofs+this->size) - newfrag2->ofs;
+			newfrag2->node = this->node;
+			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);
+			rb_insert_color(&newfrag->rb, list);
+			
+			jffs2_fragtree_insert(newfrag2, newfrag);
+			rb_insert_color(&newfrag2->rb, list);
+			
+			return 0;
+		}
+		/* New node just reduces 'this' frag in size, doesn't split it */
+		this->size = newfrag->ofs - this->ofs;
+
+		/* Again, we know it lives down here in the tree */
+		jffs2_fragtree_insert(newfrag, this);
+		rb_insert_color(&newfrag->rb, list);
+	} else {
+		/* New frag starts at the same point as 'this' used to. Replace 
+		   it in the tree without doing a delete and insertion */
+		D2(printk(KERN_DEBUG "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));
 	
-	jffs2_add_tn_to_tree(tn, tnp);
+		rb_replace_node(&this->rb, &newfrag->rb, list);
+		
+		if (newfrag->ofs + newfrag->size >= this->ofs+this->size) {
+			D2(printk(KERN_DEBUG "Obsoleting node frag %p (%x-%x)\n", this, this->ofs, this->ofs+this->size));
+			jffs2_obsolete_node_frag(c, this);
+		} else {
+			this->ofs += newfrag->size;
+			this->size -= newfrag->size;
+
+			jffs2_fragtree_insert(this, newfrag);
+			rb_insert_color(&this->rb, list);
+			return 0;
+		}
+	}
+	/* 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. */
+		D2(printk(KERN_DEBUG "Obsoleting node frag %p (%x-%x) and removing from tree\n", this, this->ofs, this->ofs+this->size));
+		rb_erase(&this->rb, list);
+		jffs2_obsolete_node_frag(c, this);
+	}
+	/* Now we're pointing at the first frag which isn't totally obsoleted by 
+	   the new frag */
+
+	if (!this || newfrag->ofs + newfrag->size == this->ofs) {
+		return 0;
+	}
+	/* 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);
 
 	return 0;
 }
 
-/*
- * Helper function for jffs2_get_inode_nodes().
- * It is called every time an unknown node is found.
- *
- * Returns: 0 on succes;
- * 	    1 if the node should be marked obsolete;
- * 	    negative error code on failure.
+/* Given an inode, probably with existing list of fragments, add the new node
+ * to the fragment list.
  */
-static inline int
-read_unknown(struct jffs2_sb_info *c,
-	     struct jffs2_raw_node_ref *ref,
-	     struct jffs2_unknown_node *un,
-	     uint32_t read)
+int jffs2_add_full_dnode_to_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, struct jffs2_full_dnode *fn)
 {
-	/* We don't mark unknown nodes as REF_UNCHECKED */
-	BUG_ON(ref_flags(ref) == REF_UNCHECKED);
-	
-	un->nodetype = cpu_to_je16(JFFS2_NODE_ACCURATE | je16_to_cpu(un->nodetype));
-
-	if (crc32(0, un, sizeof(struct jffs2_unknown_node) - 4) != je32_to_cpu(un->hdr_crc)) {
-
-		/* Hmmm. This should have been caught at scan time. */
-		printk(KERN_WARNING "Warning! Node header CRC failed at %#08x. "
-				"But it must have been OK earlier.\n", ref_offset(ref));
-		D1(printk(KERN_DEBUG "Node was: { %#04x, %#04x, %#08x, %#08x }\n", 
-			je16_to_cpu(un->magic), je16_to_cpu(un->nodetype),
-			je32_to_cpu(un->totlen), je32_to_cpu(un->hdr_crc)));
-		return 1;
-	} else {
-		switch(je16_to_cpu(un->nodetype) & JFFS2_COMPAT_MASK) {
-
-		case JFFS2_FEATURE_INCOMPAT:
-			printk(KERN_NOTICE "Unknown INCOMPAT nodetype %#04X at %#08x\n",
-					je16_to_cpu(un->nodetype), ref_offset(ref));
-			/* EEP */
-			BUG();
-			break;
-
-		case JFFS2_FEATURE_ROCOMPAT:
-			printk(KERN_NOTICE "Unknown ROCOMPAT nodetype %#04X at %#08x\n",
-					je16_to_cpu(un->nodetype), ref_offset(ref));
-			BUG_ON(!(c->flags & JFFS2_SB_FLAG_RO));
-			break;
-
-		case JFFS2_FEATURE_RWCOMPAT_COPY:
-			printk(KERN_NOTICE "Unknown RWCOMPAT_COPY nodetype %#04X at %#08x\n",
-					je16_to_cpu(un->nodetype), ref_offset(ref));
-			break;
-
-		case JFFS2_FEATURE_RWCOMPAT_DELETE:
-			printk(KERN_NOTICE "Unknown RWCOMPAT_DELETE nodetype %#04X at %#08x\n",
-					je16_to_cpu(un->nodetype), ref_offset(ref));
-			return 1;
-		}
-	}
+	int ret;
+	struct jffs2_node_frag *newfrag;
 
-	return 0;
-}
+	D1(printk(KERN_DEBUG "jffs2_add_full_dnode_to_inode(ino #%u, f %p, fn %p)\n", f->inocache->ino, f, fn));
 
-/* Get tmp_dnode_info and full_dirent for all non-obsolete nodes associated
-   with this ino, returning the former in order of version */
+	if (unlikely(!fn->size))
+		return 0;
 
-int jffs2_get_inode_nodes(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
-			  struct rb_root *tnp, struct jffs2_full_dirent **fdp,
-			  uint32_t *highest_version, uint32_t *latest_mctime,
-			  uint32_t *mctime_ver)
-{
-	struct jffs2_raw_node_ref *ref, *valid_ref;
-	struct rb_root ret_tn = RB_ROOT;
-	struct jffs2_full_dirent *ret_fd = NULL;
-	union jffs2_node_union node;
-	size_t retlen;
-	int err;
+	newfrag = jffs2_alloc_node_frag();
+	if (unlikely(!newfrag))
+		return -ENOMEM;
 
-	*mctime_ver = 0;
+	D2(printk(KERN_DEBUG "adding node %04x-%04x @0x%08x on flash, newfrag *%p\n",
+		  fn->ofs, fn->ofs+fn->size, ref_offset(fn->raw), newfrag));
 	
-	D1(printk(KERN_DEBUG "jffs2_get_inode_nodes(): ino #%u\n", f->inocache->ino));
-
-	spin_lock(&c->erase_completion_lock);
-
-	valid_ref = jffs2_first_valid_node(f->inocache->nodes);
-
-	if (!valid_ref && (f->inocache->ino != 1))
-		printk(KERN_WARNING "Eep. No valid nodes for ino #%u\n", f->inocache->ino);
-
-	while (valid_ref) {
-		/* We can hold a pointer to a non-obsolete node without the spinlock,
-		   but _obsolete_ nodes may disappear at any time, if the block
-		   they're in gets erased. So if we mark 'ref' obsolete while we're
-		   not holding the lock, it can go away immediately. For that reason,
-		   we find the next valid node first, before processing 'ref'.
-		*/
-		ref = valid_ref;
-		valid_ref = jffs2_first_valid_node(ref->next_in_ino);
-		spin_unlock(&c->erase_completion_lock);
-
-		cond_resched();
-
-		/* FIXME: point() */
-		err = jffs2_flash_read(c, (ref_offset(ref)), 
-				       min_t(uint32_t, ref_totlen(c, NULL, ref), sizeof(node)),
-				       &retlen, (void *)&node);
-		if (err) {
-			printk(KERN_WARNING "error %d reading node at 0x%08x in get_inode_nodes()\n", err, ref_offset(ref));
-			goto free_out;
-		}
-			
-		switch (je16_to_cpu(node.u.nodetype)) {
-			
-		case JFFS2_NODETYPE_DIRENT:
-			D1(printk(KERN_DEBUG "Node at %08x (%d) is a dirent node\n", ref_offset(ref), ref_flags(ref)));
-			
-			if (retlen < sizeof(node.d)) {
-				printk(KERN_WARNING "Warning! Short read dirent at %#08x\n", ref_offset(ref));
-				err = -EIO;
-				goto free_out;
-			}
-
-			err = read_direntry(c, ref, &node.d, retlen, &ret_fd, latest_mctime, mctime_ver);
-			if (err == 1) {
-				jffs2_mark_node_obsolete(c, ref);
-				break;
-			} else if (unlikely(err))
-				goto free_out;
-			
-			if (je32_to_cpu(node.d.version) > *highest_version)
-				*highest_version = je32_to_cpu(node.d.version);
-
-			break;
-
-		case JFFS2_NODETYPE_INODE:
-			D1(printk(KERN_DEBUG "Node at %08x (%d) is a data node\n", ref_offset(ref), ref_flags(ref)));
-			
-			if (retlen < sizeof(node.i)) {
-				printk(KERN_WARNING "Warning! Short read dnode at %#08x\n", ref_offset(ref));
-				err = -EIO;
-				goto free_out;
-			}
-
-			err = read_dnode(c, ref, &node.i, retlen, &ret_tn, latest_mctime, mctime_ver);
-			if (err == 1) {
-				jffs2_mark_node_obsolete(c, ref);
-				break;
-			} else if (unlikely(err))
-				goto free_out;
-
-			if (je32_to_cpu(node.i.version) > *highest_version)
-				*highest_version = je32_to_cpu(node.i.version);
-			
-			D1(printk(KERN_DEBUG "version %d, highest_version now %d\n",
-					je32_to_cpu(node.i.version), *highest_version));
-
-			break;
-
-		default:
-			/* Check we've managed to read at least the common node header */
-			if (retlen < sizeof(struct jffs2_unknown_node)) {
-				printk(KERN_WARNING "Warning! Short read unknown node at %#08x\n",
-						ref_offset(ref));
-				return -EIO;
-			}
-
-			err = read_unknown(c, ref, &node.u, retlen);
-			if (err == 1) {
-				jffs2_mark_node_obsolete(c, ref);
-				break;
-			} else if (unlikely(err))
-				goto free_out;
+	newfrag->ofs = fn->ofs;
+	newfrag->size = fn->size;
+	newfrag->node = fn;
+	newfrag->node->frags = 1;
+
+	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);
+	}
 
+	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);
 		}
-		spin_lock(&c->erase_completion_lock);
-
 	}
-	spin_unlock(&c->erase_completion_lock);
-	*tnp = ret_tn;
-	*fdp = ret_fd;
-
+	jffs2_dbg_fragtree_paranoia_check_nolock(f);
+	jffs2_dbg_dump_fragtree_nolock(f);
 	return 0;
-
- free_out:
-	jffs2_free_tmp_dnode_info_list(&ret_tn);
-	jffs2_free_full_dirent_list(ret_fd);
-	return err;
 }
 
+
 void jffs2_set_inocache_state(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic, int state)
 {
 	spin_lock(&c->inocache_lock);
@@ -773,29 +542,3 @@
 		cond_resched();
 	}
 }
-
-void jffs2_fragtree_insert(struct jffs2_node_frag *newfrag, struct jffs2_node_frag *base)
-{
-	struct rb_node *parent = &base->rb;
-	struct rb_node **link = &parent;
-
-	D2(printk(KERN_DEBUG "jffs2_fragtree_insert(%p; %d-%d, %p)\n", newfrag, 
-		  newfrag->ofs, newfrag->ofs+newfrag->size, base));
-
-	while (*link) {
-		parent = *link;
-		base = rb_entry(parent, struct jffs2_node_frag, rb);
-	
-		D2(printk(KERN_DEBUG "fragtree_insert considering frag at 0x%x\n", base->ofs));
-		if (newfrag->ofs > base->ofs)
-			link = &base->rb.rb_right;
-		else if (newfrag->ofs < base->ofs)
-			link = &base->rb.rb_left;
-		else {
-			printk(KERN_CRIT "Duplicate frag at %08x (%p,%p)\n", newfrag->ofs, newfrag, base);
-			BUG();
-		}
-	}
-
-	rb_link_node(&newfrag->rb, &base->rb, link);
-}

Index: nodelist.h
===================================================================
RCS file: /home/cvs/mtd/fs/jffs2/nodelist.h,v
retrieving revision 1.134
retrieving revision 1.135
diff -u -r1.134 -r1.135
--- nodelist.h	24 Jul 2005 15:29:56 -0000	1.134
+++ nodelist.h	27 Jul 2005 14:46:11 -0000	1.135
@@ -297,10 +297,6 @@
 
 /* nodelist.c */
 void jffs2_add_fd_to_list(struct jffs2_sb_info *c, struct jffs2_full_dirent *new, struct jffs2_full_dirent **list);
-int jffs2_get_inode_nodes(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
-			  struct rb_root *tnp, struct jffs2_full_dirent **fdp,
-			  uint32_t *highest_version, uint32_t *latest_mctime,
-			  uint32_t *mctime_ver);
 void jffs2_set_inocache_state(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic, int state);
 struct jffs2_inode_cache *jffs2_get_ino_cache(struct jffs2_sb_info *c, uint32_t ino);
 void jffs2_add_ino_cache (struct jffs2_sb_info *c, struct jffs2_inode_cache *new);
@@ -309,10 +305,11 @@
 void jffs2_free_raw_node_refs(struct jffs2_sb_info *c);
 struct jffs2_node_frag *jffs2_lookup_node_frag(struct rb_root *fragtree, uint32_t offset);
 void jffs2_kill_fragtree(struct rb_root *root, struct jffs2_sb_info *c_delete);
-void jffs2_fragtree_insert(struct jffs2_node_frag *newfrag, struct jffs2_node_frag *base);
 struct rb_node *rb_next(struct rb_node *);
 struct rb_node *rb_prev(struct rb_node *);
 void rb_replace_node(struct rb_node *victim, struct rb_node *new, struct rb_root *root);
+void jffs2_obsolete_node_frag(struct jffs2_sb_info *c, struct jffs2_node_frag *this);
+int jffs2_add_full_dnode_to_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, struct jffs2_full_dnode *fn);
 
 /* nodemgmt.c */
 int jffs2_thread_should_wake(struct jffs2_sb_info *c);
@@ -337,7 +334,6 @@
 
 /* readinode.c */
 void jffs2_truncate_fragtree (struct jffs2_sb_info *c, struct rb_root *list, uint32_t size);
-int jffs2_add_full_dnode_to_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, struct jffs2_full_dnode *fn);
 int jffs2_do_read_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, 
 			uint32_t ino, struct jffs2_raw_inode *latest_node);
 int jffs2_do_crccheck_inode(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic);

Index: readinode.c
===================================================================
RCS file: /home/cvs/mtd/fs/jffs2/readinode.c,v
retrieving revision 1.130
retrieving revision 1.131
diff -u -r1.130 -r1.131
--- readinode.c	24 Jul 2005 15:29:56 -0000	1.130
+++ readinode.c	27 Jul 2005 14:46:11 -0000	1.131
@@ -20,376 +20,537 @@
 #include <linux/compiler.h>
 #include "nodelist.h"
 
-static int jffs2_add_frag_to_fragtree(struct jffs2_sb_info *c, struct rb_root *list, struct jffs2_node_frag *newfrag);
-
-static void jffs2_obsolete_node_frag(struct jffs2_sb_info *c, struct jffs2_node_frag *this)
+void jffs2_truncate_fragtree (struct jffs2_sb_info *c, struct rb_root *list, uint32_t size)
 {
-	if (this->node) {
-		this->node->frags--;
-		if (!this->node->frags) {
-			/* The node has no valid frags left. It's totally obsoleted */
-			D2(printk(KERN_DEBUG "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));
-			jffs2_mark_node_obsolete(c, this->node->raw);
-			jffs2_free_full_dnode(this->node);
-		} else {
-			D2(printk(KERN_DEBUG "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));
-			mark_ref_normal(this->node->raw);
+	struct jffs2_node_frag *frag = jffs2_lookup_node_frag(list, size);
+
+	D1(printk(KERN_DEBUG "Truncating fraglist 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) {
+			D1(printk(KERN_DEBUG "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);
+
+		D1(printk(KERN_DEBUG "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;
 	}
-	jffs2_free_node_frag(this);
 }
 
-/* Given an inode, probably with existing list of fragments, add the new node
- * to the fragment list.
+/* 
+ * Put a new tmp_dnode_info into the temporaty RB-tree, keeping the list in 
+ * order of increasing version.
  */
-int jffs2_add_full_dnode_to_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, struct jffs2_full_dnode *fn)
+static void jffs2_add_tn_to_tree(struct jffs2_tmp_dnode_info *tn, struct rb_root *list)
 {
-	int ret;
-	struct jffs2_node_frag *newfrag;
+	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;
+        }
 
-	D1(printk(KERN_DEBUG "jffs2_add_full_dnode_to_inode(ino #%u, f %p, fn %p)\n", f->inocache->ino, f, fn));
+	rb_link_node(&tn->rb, parent, p);
+	rb_insert_color(&tn->rb, list);
+}
 
-	if (unlikely(!fn->size))
-		return 0;
+static void jffs2_free_tmp_dnode_info_list(struct rb_root *list)
+{
+	struct rb_node *this;
+	struct jffs2_tmp_dnode_info *tn;
 
-	newfrag = jffs2_alloc_node_frag();
-	if (unlikely(!newfrag))
-		return -ENOMEM;
+	this = list->rb_node;
 
-	D2(printk(KERN_DEBUG "adding node %04x-%04x @0x%08x on flash, newfrag *%p\n",
-		  fn->ofs, fn->ofs+fn->size, ref_offset(fn->raw), newfrag));
-	
-	newfrag->ofs = fn->ofs;
-	newfrag->size = fn->size;
-	newfrag->node = fn;
-	newfrag->node->frags = 1;
+	/* 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;
+}
 
-	ret = jffs2_add_frag_to_fragtree(c, &f->fragtree, newfrag);
-	if (unlikely(ret))
-		return ret;
+static void jffs2_free_full_dirent_list(struct jffs2_full_dirent *fd)
+{
+	struct jffs2_full_dirent *next;
 
-	/* 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);
+	while (fd) {
+		next = fd->next;
+		jffs2_free_full_dirent(fd);
+		fd = next;
 	}
+}
 
-	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);
-		}
+/* 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;
+		D1(printk(KERN_DEBUG "node at 0x%08x is obsoleted. Ignoring.\n", ref_offset(ref)));
+		ref = ref->next_in_ino;
 	}
-	jffs2_dbg_fragtree_paranoia_check_nolock(f);
-	jffs2_dbg_dump_fragtree_nolock(f);
-	return 0;
+	return NULL;
 }
 
-/* Doesn't set inode->i_size */
-static int jffs2_add_frag_to_fragtree(struct jffs2_sb_info *c, struct rb_root *list, struct jffs2_node_frag *newfrag)
+/*
+ * 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_node_frag *this;
-	uint32_t lastend;
-
-	/* Skip all the nodes which are completed before this one starts */
-	this = jffs2_lookup_node_frag(list, newfrag->node->ofs);
+	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)))) {
+		printk(KERN_ERR "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;
 
-	if (this) {
-		D2(printk(KERN_DEBUG "j_a_f_d_t_f: 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));
-		lastend = this->ofs + this->size;
-	} else {
-		D2(printk(KERN_DEBUG "j_a_f_d_t_f: Lookup gave no frag\n"));
-		lastend = 0;
+	fd->raw = ref;
+	fd->version = je32_to_cpu(rd->version);
+	fd->ino = je32_to_cpu(rd->ino);
+	fd->type = rd->type;
+
+	/* Pick out the mctime of the latest dirent */
+	if(fd->version > *mctime_ver) {
+		*mctime_ver = fd->version;
+		*latest_mctime = je32_to_cpu(rd->mctime);
 	}
-			  
-	/* See if we ran off the end of the list */
-	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);
-		}
 
-		if (lastend < newfrag->node->ofs) {
-			/* ... and we need to put a hole in before the new node */
-			struct jffs2_node_frag *holefrag = jffs2_alloc_node_frag();
-			if (!holefrag) {
-				jffs2_free_node_frag(newfrag);
-				return -ENOMEM;
-			}
-			holefrag->ofs = lastend;
-			holefrag->size = newfrag->node->ofs - lastend;
-			holefrag->node = NULL;
-			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 */
-				D2(printk(KERN_DEBUG "Adding hole frag (%p) on right of node at (%p)\n", holefrag, this));
-				rb_link_node(&holefrag->rb, &this->rb, &this->rb.rb_right);
-			} else {
-				D2(printk(KERN_DEBUG "Adding hole frag (%p) at root of tree\n", holefrag));
-				rb_link_node(&holefrag->rb, NULL, &list->rb_node);
-			}
-			rb_insert_color(&holefrag->rb, list);
-			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 */
-			D2(printk(KERN_DEBUG "Adding new frag (%p) on right of node at (%p)\n", newfrag, this));
-			rb_link_node(&newfrag->rb, &this->rb, &this->rb.rb_right);			
-		} else {
-			D2(printk(KERN_DEBUG "Adding new frag (%p) at root of tree\n", newfrag));
-			rb_link_node(&newfrag->rb, NULL, &list->rb_node);
+	/* 
+	 * Copy as much of the name as possible from the raw
+	 * dirent we've already read from the flash.
+	 */
+	if (read > sizeof(*rd))
+		memcpy(&fd->name[0], &rd->name[0],
+		       min_t(uint32_t, rd->nsize, (read - sizeof(*rd)) ));
+		
+	/* Do we need to copy any more of the name directly from the flash? */
+	if (rd->nsize + sizeof(*rd) > read) {
+		/* FIXME: point() */
+		int err;
+		int already = read - sizeof(*rd);
+			
+		err = jffs2_flash_read(c, (ref_offset(ref)) + read, 
+				rd->nsize - already, &read, &fd->name[already]);
+		if (unlikely(read != rd->nsize - already) && likely(!err))
+			return -EIO;
+			
+		if (unlikely(err)) {
+			printk(KERN_WARNING "Read remainder of name: error %d\n", err);
+			jffs2_free_full_dirent(fd);
+			return -EIO;
 		}
-		rb_insert_color(&newfrag->rb, list);
-		return 0;
 	}
+	
+	fd->nhash = full_name_hash(fd->name, rd->nsize);
+	fd->next = NULL;
+	fd->name[rd->nsize] = '\0';
+	
+	/*
+	 * Wheee. We now have a complete jffs2_full_dirent structure, with
+	 * the name in it and everything. Link it into the list 
+	 */
+	D1(printk(KERN_DEBUG "Adding fd \"%s\", ino #%u\n", fd->name, fd->ino));
 
-	D2(printk(KERN_DEBUG "j_a_f_d_t_f: dealing with 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));
+	jffs2_add_fd_to_list(c, fd, fdp);
 
-	/* 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  
-	 */
-	if (newfrag->ofs > this->ofs) {
-		/* This node isn't completely obsoleted. The start of it remains valid */
+	return 0;
+}
 
-		/* 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 */
-			struct jffs2_node_frag *newfrag2 = jffs2_alloc_node_frag();
-			if (!newfrag2) {
-				jffs2_free_node_frag(newfrag);
-				return -ENOMEM;
-			}
-			D2(printk(KERN_DEBUG "split old frag 0x%04x-0x%04x -->", this->ofs, this->ofs+this->size);
-			if (this->node)
-				printk("phys 0x%08x\n", ref_offset(this->node->raw));
-			else 
-				printk("hole\n");
-			   )
-			
-			/* New second frag pointing to this's node */
-			newfrag2->ofs = newfrag->ofs + newfrag->size;
-			newfrag2->size = (this->ofs+this->size) - newfrag2->ofs;
-			newfrag2->node = this->node;
-			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);
-			rb_insert_color(&newfrag->rb, list);
-			
-			jffs2_fragtree_insert(newfrag2, newfrag);
-			rb_insert_color(&newfrag2->rb, list);
-			
-			return 0;
-		}
-		/* New node just reduces 'this' frag in size, doesn't split it */
-		this->size = newfrag->ofs - this->ofs;
-
-		/* Again, we know it lives down here in the tree */
-		jffs2_fragtree_insert(newfrag, this);
-		rb_insert_color(&newfrag->rb, list);
-	} else {
-		/* New frag starts at the same point as 'this' used to. Replace 
-		   it in the tree without doing a delete and insertion */
-		D2(printk(KERN_DEBUG "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));
+/*
+ * Helper function for jffs2_get_inode_nodes().
+ * It is called every time an inode node is found.
+ *
+ * Returns: 0 on succes;
+ * 	    1 if the node should be marked obsolete;
+ * 	    negative error code on failure.
+ */
+static inline int
+read_dnode(struct jffs2_sb_info *c,
+	   struct jffs2_raw_node_ref *ref,
+	   struct jffs2_raw_inode *rd,
+	   uint32_t read,
+	   struct rb_root *tnp,
+	   int32_t *latest_mctime,
+	   uint32_t *mctime_ver)
+{
+	struct jffs2_eraseblock *jeb;
+	struct jffs2_tmp_dnode_info *tn;
 	
-		rb_replace_node(&this->rb, &newfrag->rb, list);
+	/* Obsoleted. This cannot happen, surely? dwmw2 20020308 */
+	BUG_ON(ref_obsolete(ref));
+
+	/* If we've never checked the CRCs on this node, check them now */
+	if (ref_flags(ref) == REF_UNCHECKED) {
+		uint32_t crc, len;
+
+		crc = crc32(0, rd, sizeof(*rd) - 8);
+		if (unlikely(crc != je32_to_cpu(rd->node_crc))) {
+			printk(KERN_WARNING "Header CRC failed on node at %#08x: read %#08x, calculated %#08x\n",
+					ref_offset(ref), je32_to_cpu(rd->node_crc), crc);
+			return 1;
+		}
 		
-		if (newfrag->ofs + newfrag->size >= this->ofs+this->size) {
-			D2(printk(KERN_DEBUG "Obsoleting node frag %p (%x-%x)\n", this, this->ofs, this->ofs+this->size));
-			jffs2_obsolete_node_frag(c, this);
+		/* Sanity checks */
+		if (unlikely(je32_to_cpu(rd->offset) > je32_to_cpu(rd->isize)) ||
+		    unlikely(PAD(je32_to_cpu(rd->csize) + sizeof(*rd)) != PAD(je32_to_cpu(rd->totlen)))) {
+			printk(KERN_WARNING "Inode corrupted at %#08x, totlen %d, #ino  %d, version %d, "
+				"isize %d, csize %d, dsize %d \n",
+				ref_offset(ref),  je32_to_cpu(rd->totlen),  je32_to_cpu(rd->ino),
+				je32_to_cpu(rd->version),  je32_to_cpu(rd->isize), 
+				je32_to_cpu(rd->csize), je32_to_cpu(rd->dsize));
+			return 1;
+		}
+
+		if (rd->compr != JFFS2_COMPR_ZERO && je32_to_cpu(rd->csize)) {
+			unsigned char *buf = NULL;
+			uint32_t pointed = 0;
+			int err;
+#ifndef __ECOS
+			if (c->mtd->point) {
+				err = c->mtd->point (c->mtd, ref_offset(ref) + sizeof(*rd), je32_to_cpu(rd->csize),
+						     &read, &buf);
+				if (unlikely(read < je32_to_cpu(rd->csize)) && likely(!err)) {
+					D1(printk(KERN_DEBUG "MTD point returned len too short: 0x%zx\n", read));
+					c->mtd->unpoint(c->mtd, buf, ref_offset(ref) + sizeof(*rd),
+							je32_to_cpu(rd->csize));
+				} else if (unlikely(err)){
+					D1(printk(KERN_DEBUG "MTD point failed %d\n", err));
+				} else
+					pointed = 1; /* succefully pointed to device */
+			}
+#endif					
+			if(!pointed){
+				buf = kmalloc(je32_to_cpu(rd->csize), GFP_KERNEL);
+				if (!buf)
+					return -ENOMEM;
+				
+				err = jffs2_flash_read(c, ref_offset(ref) + sizeof(*rd), je32_to_cpu(rd->csize),
+							&read, buf);
+				if (unlikely(read != je32_to_cpu(rd->csize)) && likely(!err))
+					err = -EIO;
+				if (err) {
+					kfree(buf);
+					return err;
+				}
+			}
+			crc = crc32(0, buf, je32_to_cpu(rd->csize));
+			if(!pointed)
+				kfree(buf);
+#ifndef __ECOS
+			else
+				c->mtd->unpoint(c->mtd, buf, ref_offset(ref) + sizeof(*rd), je32_to_cpu(rd->csize));
+#endif
+
+			if (crc != je32_to_cpu(rd->data_crc)) {
+				printk(KERN_NOTICE "Data CRC failed on node at %#08x: read %#08x, calculated %#08x\n",
+				       ref_offset(ref), je32_to_cpu(rd->data_crc), crc);
+				return 1;
+			}
+			
+		}
+
+		/* Mark the node as having been checked and fix the accounting accordingly */
+		jeb = &c->blocks[ref->flash_offset / c->sector_size];
+		len = ref_totlen(c, jeb, ref);
+
+		spin_lock(&c->erase_completion_lock);
+		jeb->used_size += len;
+		jeb->unchecked_size -= len;
+		c->used_size += len;
+		c->unchecked_size -= len;
+
+		/* If node covers at least a whole page, or if it starts at the 
+		   beginning of a page and runs to the end of the file, or if 
+		   it's a hole node, mark it REF_PRISTINE, else REF_NORMAL. 
+
+		   If it's actually overlapped, it'll get made NORMAL (or OBSOLETE) 
+		   when the overlapping node(s) get added to the tree anyway. 
+		*/
+		if ((je32_to_cpu(rd->dsize) >= PAGE_CACHE_SIZE) ||
+		    ( ((je32_to_cpu(rd->offset) & (PAGE_CACHE_SIZE-1))==0) &&
+		      (je32_to_cpu(rd->dsize) + je32_to_cpu(rd->offset) == je32_to_cpu(rd->isize)))) {
+			D1(printk(KERN_DEBUG "Marking node at %#08x REF_PRISTINE\n", ref_offset(ref)));
+			ref->flash_offset = ref_offset(ref) | REF_PRISTINE;
 		} else {
-			this->ofs += newfrag->size;
-			this->size -= newfrag->size;
+			D1(printk(KERN_DEBUG "Marking node at %#08x REF_NORMAL\n", ref_offset(ref)));
+			ref->flash_offset = ref_offset(ref) | REF_NORMAL;
+		}
+		spin_unlock(&c->erase_completion_lock);
+	}
 
-			jffs2_fragtree_insert(this, newfrag);
-			rb_insert_color(&this->rb, list);
-			return 0;
-		}
-	}
-	/* 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. */
-		D2(printk(KERN_DEBUG "Obsoleting node frag %p (%x-%x) and removing from tree\n", this, this->ofs, this->ofs+this->size));
-		rb_erase(&this->rb, list);
-		jffs2_obsolete_node_frag(c, this);
+	tn = jffs2_alloc_tmp_dnode_info();
+	if (!tn) {
+		D1(printk(KERN_DEBUG "alloc tn failed\n"));
+		return -ENOMEM;
 	}
-	/* Now we're pointing at the first frag which isn't totally obsoleted by 
-	   the new frag */
 
-	if (!this || newfrag->ofs + newfrag->size == this->ofs) {
-		return 0;
+	tn->fn = jffs2_alloc_full_dnode();
+	if (!tn->fn) {
+		D1(printk(KERN_DEBUG "alloc fn failed\n"));
+		jffs2_free_tmp_dnode_info(tn);
+		return -ENOMEM;
 	}
-	/* 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);
+	
+	tn->version = je32_to_cpu(rd->version);
+	tn->fn->ofs = je32_to_cpu(rd->offset);
+	tn->fn->raw = ref;
+	
+	/* There was a bug where we wrote hole nodes out with
+	   csize/dsize swapped. Deal with it */
+	if (rd->compr == JFFS2_COMPR_ZERO && !je32_to_cpu(rd->dsize) && je32_to_cpu(rd->csize))
+		tn->fn->size = je32_to_cpu(rd->csize);
+	else // normal case...
+		tn->fn->size = je32_to_cpu(rd->dsize);
+
+	D1(printk(KERN_DEBUG "dnode @%08x: ver %u, offset %#04x, dsize %#04x\n",
+		  ref_offset(ref), je32_to_cpu(rd->version),
+		  je32_to_cpu(rd->offset), je32_to_cpu(rd->dsize)));
+	
+	jffs2_add_tn_to_tree(tn, tnp);
 
 	return 0;
 }
 
-void jffs2_truncate_fragtree (struct jffs2_sb_info *c, struct rb_root *list, uint32_t size)
+/*
+ * Helper function for jffs2_get_inode_nodes().
+ * It is called every time an unknown node is found.
+ *
+ * Returns: 0 on succes;
+ * 	    1 if the node should be marked obsolete;
+ * 	    negative error code on failure.
+ */
+static inline int
+read_unknown(struct jffs2_sb_info *c,
+	     struct jffs2_raw_node_ref *ref,
+	     struct jffs2_unknown_node *un,
+	     uint32_t read)
 {
-	struct jffs2_node_frag *frag = jffs2_lookup_node_frag(list, size);
+	/* We don't mark unknown nodes as REF_UNCHECKED */
+	BUG_ON(ref_flags(ref) == REF_UNCHECKED);
+	
+	un->nodetype = cpu_to_je16(JFFS2_NODE_ACCURATE | je16_to_cpu(un->nodetype));
 
-	D1(printk(KERN_DEBUG "Truncating fraglist to 0x%08x bytes\n", size));
+	if (crc32(0, un, sizeof(struct jffs2_unknown_node) - 4) != je32_to_cpu(un->hdr_crc)) {
 
-	/* We know frag->ofs <= size. That's what lookup does for us */
-	if (frag && frag->ofs != size) {
-		if (frag->ofs+frag->size >= size) {
-			D1(printk(KERN_DEBUG "Truncating frag 0x%08x-0x%08x\n", frag->ofs, frag->ofs+frag->size));
-			frag->size = size - frag->ofs;
+		/* Hmmm. This should have been caught at scan time. */
+		printk(KERN_WARNING "Warning! Node header CRC failed at %#08x. "
+				"But it must have been OK earlier.\n", ref_offset(ref));
+		D1(printk(KERN_DEBUG "Node was: { %#04x, %#04x, %#08x, %#08x }\n", 
+			je16_to_cpu(un->magic), je16_to_cpu(un->nodetype),
+			je32_to_cpu(un->totlen), je32_to_cpu(un->hdr_crc)));
+		return 1;
+	} else {
+		switch(je16_to_cpu(un->nodetype) & JFFS2_COMPAT_MASK) {
+
+		case JFFS2_FEATURE_INCOMPAT:
+			printk(KERN_NOTICE "Unknown INCOMPAT nodetype %#04X at %#08x\n",
+					je16_to_cpu(un->nodetype), ref_offset(ref));
+			/* EEP */
+			BUG();
+			break;
+
+		case JFFS2_FEATURE_ROCOMPAT:
+			printk(KERN_NOTICE "Unknown ROCOMPAT nodetype %#04X at %#08x\n",
+					je16_to_cpu(un->nodetype), ref_offset(ref));
+			BUG_ON(!(c->flags & JFFS2_SB_FLAG_RO));
+			break;
+
+		case JFFS2_FEATURE_RWCOMPAT_COPY:
+			printk(KERN_NOTICE "Unknown RWCOMPAT_COPY nodetype %#04X at %#08x\n",
+					je16_to_cpu(un->nodetype), ref_offset(ref));
+			break;
+
+		case JFFS2_FEATURE_RWCOMPAT_DELETE:
+			printk(KERN_NOTICE "Unknown RWCOMPAT_DELETE nodetype %#04X at %#08x\n",
+					je16_to_cpu(un->nodetype), ref_offset(ref));
+			return 1;
 		}
-		frag = frag_next(frag);
 	}
-	while (frag && frag->ofs >= size) {
-		struct jffs2_node_frag *next = frag_next(frag);
 
-		D1(printk(KERN_DEBUG "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;
-	}
+	return 0;
 }
 
-/* Scan the list of all nodes present for this ino, build map of versions, etc. */
-
-static int jffs2_do_read_inode_internal(struct jffs2_sb_info *c, 
-					struct jffs2_inode_info *f,
-					struct jffs2_raw_inode *latest_node);
+/* Get tmp_dnode_info and full_dirent for all non-obsolete nodes associated
+   with this ino, returning the former in order of version */
 
-int jffs2_do_read_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, 
-			uint32_t ino, struct jffs2_raw_inode *latest_node)
+static int jffs2_get_inode_nodes(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
+				 struct rb_root *tnp, struct jffs2_full_dirent **fdp,
+				 uint32_t *highest_version, uint32_t *latest_mctime,
+				 uint32_t *mctime_ver)
 {
-	D2(printk(KERN_DEBUG "jffs2_do_read_inode(): getting inocache\n"));
+	struct jffs2_raw_node_ref *ref, *valid_ref;
+	struct rb_root ret_tn = RB_ROOT;
+	struct jffs2_full_dirent *ret_fd = NULL;
+	union jffs2_node_union node;
+	size_t retlen;
+	int err;
 
- retry_inocache:
-	spin_lock(&c->inocache_lock);
-	f->inocache = jffs2_get_ino_cache(c, ino);
+	*mctime_ver = 0;
+	
+	D1(printk(KERN_DEBUG "jffs2_get_inode_nodes(): ino #%u\n", f->inocache->ino));
 
-	D2(printk(KERN_DEBUG "jffs2_do_read_inode(): Got inocache at %p\n", f->inocache));
+	spin_lock(&c->erase_completion_lock);
 
-	if (f->inocache) {
-		/* Check its state. We may need to wait before we can use it */
-		switch(f->inocache->state) {
-		case INO_STATE_UNCHECKED:
-		case INO_STATE_CHECKEDABSENT:
-			f->inocache->state = INO_STATE_READING;
-			break;
+	valid_ref = jffs2_first_valid_node(f->inocache->nodes);
+
+	if (!valid_ref && (f->inocache->ino != 1))
+		printk(KERN_WARNING "Eep. No valid nodes for ino #%u\n", f->inocache->ino);
+
+	while (valid_ref) {
+		/* We can hold a pointer to a non-obsolete node without the spinlock,
+		   but _obsolete_ nodes may disappear at any time, if the block
+		   they're in gets erased. So if we mark 'ref' obsolete while we're
+		   not holding the lock, it can go away immediately. For that reason,
+		   we find the next valid node first, before processing 'ref'.
+		*/
+		ref = valid_ref;
+		valid_ref = jffs2_first_valid_node(ref->next_in_ino);
+		spin_unlock(&c->erase_completion_lock);
+
+		cond_resched();
+
+		/* FIXME: point() */
+		err = jffs2_flash_read(c, (ref_offset(ref)), 
+				       min_t(uint32_t, ref_totlen(c, NULL, ref), sizeof(node)),
+				       &retlen, (void *)&node);
+		if (err) {
+			printk(KERN_WARNING "error %d reading node at 0x%08x in get_inode_nodes()\n", err, ref_offset(ref));
+			goto free_out;
+		}
 			
-		case INO_STATE_CHECKING:
-		case INO_STATE_GC:
-			/* If it's in either of these states, we need
-			   to wait for whoever's got it to finish and
-			   put it back. */
-			D1(printk(KERN_DEBUG "jffs2_get_ino_cache_read waiting for ino #%u in state %d\n",
-				  ino, f->inocache->state));
-			sleep_on_spinunlock(&c->inocache_wq, &c->inocache_lock);
-			goto retry_inocache;
+		switch (je16_to_cpu(node.u.nodetype)) {
+			
+		case JFFS2_NODETYPE_DIRENT:
+			D1(printk(KERN_DEBUG "Node at %08x (%d) is a dirent node\n", ref_offset(ref), ref_flags(ref)));
+			
+			if (retlen < sizeof(node.d)) {
+				printk(KERN_WARNING "Warning! Short read dirent at %#08x\n", ref_offset(ref));
+				err = -EIO;
+				goto free_out;
+			}
+
+			err = read_direntry(c, ref, &node.d, retlen, &ret_fd, latest_mctime, mctime_ver);
+			if (err == 1) {
+				jffs2_mark_node_obsolete(c, ref);
+				break;
+			} else if (unlikely(err))
+				goto free_out;
+			
+			if (je32_to_cpu(node.d.version) > *highest_version)
+				*highest_version = je32_to_cpu(node.d.version);
 
-		case INO_STATE_READING:
-		case INO_STATE_PRESENT:
-			/* Eep. This should never happen. It can
-			happen if Linux calls read_inode() again
-			before clear_inode() has finished though. */
-			printk(KERN_WARNING "Eep. Trying to read_inode #%u when it's already in state %d!\n", ino, f->inocache->state);
-			/* Fail. That's probably better than allowing it to succeed */
-			f->inocache = NULL;
 			break;
 
-		default:
-			BUG();
-		}
-	}
-	spin_unlock(&c->inocache_lock);
+		case JFFS2_NODETYPE_INODE:
+			D1(printk(KERN_DEBUG "Node at %08x (%d) is a data node\n", ref_offset(ref), ref_flags(ref)));
+			
+			if (retlen < sizeof(node.i)) {
+				printk(KERN_WARNING "Warning! Short read dnode at %#08x\n", ref_offset(ref));
+				err = -EIO;
+				goto free_out;
+			}
 
-	if (!f->inocache && ino == 1) {
-		/* Special case - no root inode on medium */
-		f->inocache = jffs2_alloc_inode_cache();
-		if (!f->inocache) {
-			printk(KERN_CRIT "jffs2_do_read_inode(): Cannot allocate inocache for root inode\n");
-			return -ENOMEM;
-		}
-		D1(printk(KERN_DEBUG "jffs2_do_read_inode(): Creating inocache for root inode\n"));
-		memset(f->inocache, 0, sizeof(struct jffs2_inode_cache));
-		f->inocache->ino = f->inocache->nlink = 1;
-		f->inocache->nodes = (struct jffs2_raw_node_ref *)f->inocache;
-		f->inocache->state = INO_STATE_READING;
-		jffs2_add_ino_cache(c, f->inocache);
-	}
-	if (!f->inocache) {
-		printk(KERN_WARNING "jffs2_do_read_inode() on nonexistent ino %u\n", ino);
-		return -ENOENT;
-	}
+			err = read_dnode(c, ref, &node.i, retlen, &ret_tn, latest_mctime, mctime_ver);
+			if (err == 1) {
+				jffs2_mark_node_obsolete(c, ref);
+				break;
+			} else if (unlikely(err))
+				goto free_out;
 
-	return jffs2_do_read_inode_internal(c, f, latest_node);
-}
+			if (je32_to_cpu(node.i.version) > *highest_version)
+				*highest_version = je32_to_cpu(node.i.version);
+			
+			D1(printk(KERN_DEBUG "version %d, highest_version now %d\n",
+					je32_to_cpu(node.i.version), *highest_version));
 
-int jffs2_do_crccheck_inode(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic)
-{
-	struct jffs2_raw_inode n;
-	struct jffs2_inode_info *f = kmalloc(sizeof(*f), GFP_KERNEL);
-	int ret;
+			break;
 
-	if (!f)
-		return -ENOMEM;
+		default:
+			/* Check we've managed to read at least the common node header */
+			if (retlen < sizeof(struct jffs2_unknown_node)) {
+				printk(KERN_WARNING "Warning! Short read unknown node at %#08x\n",
+						ref_offset(ref));
+				return -EIO;
+			}
 
-	memset(f, 0, sizeof(*f));
-	init_MUTEX_LOCKED(&f->sem);
-	f->inocache = ic;
+			err = read_unknown(c, ref, &node.u, retlen);
+			if (err == 1) {
+				jffs2_mark_node_obsolete(c, ref);
+				break;
+			} else if (unlikely(err))
+				goto free_out;
+
+		}
+		spin_lock(&c->erase_completion_lock);
 
-	ret = jffs2_do_read_inode_internal(c, f, &n);
-	if (!ret) {
-		up(&f->sem);
-		jffs2_do_clear_inode(c, f);
 	}
-	kfree (f);
-	return ret;
+	spin_unlock(&c->erase_completion_lock);
+	*tnp = ret_tn;
+	*fdp = ret_fd;
+
+	return 0;
+
+ free_out:
+	jffs2_free_tmp_dnode_info_list(&ret_tn);
+	jffs2_free_full_dirent_list(ret_fd);
+	return err;
 }
 
 static int jffs2_do_read_inode_internal(struct jffs2_sb_info *c, 
@@ -618,6 +779,96 @@
 	return 0;
 }
 
+/* Scan the list of all nodes present for this ino, build map of versions, etc. */
+int jffs2_do_read_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, 
+			uint32_t ino, struct jffs2_raw_inode *latest_node)
+{
+	D2(printk(KERN_DEBUG "jffs2_do_read_inode(): getting inocache\n"));
+
+ retry_inocache:
+	spin_lock(&c->inocache_lock);
+	f->inocache = jffs2_get_ino_cache(c, ino);
+
+	D2(printk(KERN_DEBUG "jffs2_do_read_inode(): Got inocache at %p\n", f->inocache));
+
+	if (f->inocache) {
+		/* Check its state. We may need to wait before we can use it */
+		switch(f->inocache->state) {
+		case INO_STATE_UNCHECKED:
+		case INO_STATE_CHECKEDABSENT:
+			f->inocache->state = INO_STATE_READING;
+			break;
+			
+		case INO_STATE_CHECKING:
+		case INO_STATE_GC:
+			/* If it's in either of these states, we need
+			   to wait for whoever's got it to finish and
+			   put it back. */
+			D1(printk(KERN_DEBUG "jffs2_get_ino_cache_read waiting for ino #%u in state %d\n",
+				  ino, f->inocache->state));
+			sleep_on_spinunlock(&c->inocache_wq, &c->inocache_lock);
+			goto retry_inocache;
+
+		case INO_STATE_READING:
+		case INO_STATE_PRESENT:
+			/* Eep. This should never happen. It can
+			happen if Linux calls read_inode() again
+			before clear_inode() has finished though. */
+			printk(KERN_WARNING "Eep. Trying to read_inode #%u when it's already in state %d!\n", ino, f->inocache->state);
+			/* Fail. That's probably better than allowing it to succeed */
+			f->inocache = NULL;
+			break;
+
+		default:
+			BUG();
+		}
+	}
+	spin_unlock(&c->inocache_lock);
+
+	if (!f->inocache && ino == 1) {
+		/* Special case - no root inode on medium */
+		f->inocache = jffs2_alloc_inode_cache();
+		if (!f->inocache) {
+			printk(KERN_CRIT "jffs2_do_read_inode(): Cannot allocate inocache for root inode\n");
+			return -ENOMEM;
+		}
+		D1(printk(KERN_DEBUG "jffs2_do_read_inode(): Creating inocache for root inode\n"));
+		memset(f->inocache, 0, sizeof(struct jffs2_inode_cache));
+		f->inocache->ino = f->inocache->nlink = 1;
+		f->inocache->nodes = (struct jffs2_raw_node_ref *)f->inocache;
+		f->inocache->state = INO_STATE_READING;
+		jffs2_add_ino_cache(c, f->inocache);
+	}
+	if (!f->inocache) {
+		printk(KERN_WARNING "jffs2_do_read_inode() on nonexistent ino %u\n", ino);
+		return -ENOENT;
+	}
+
+	return jffs2_do_read_inode_internal(c, f, latest_node);
+}
+
+int jffs2_do_crccheck_inode(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic)
+{
+	struct jffs2_raw_inode n;
+	struct jffs2_inode_info *f = kmalloc(sizeof(*f), GFP_KERNEL);
+	int ret;
+
+	if (!f)
+		return -ENOMEM;
+
+	memset(f, 0, sizeof(*f));
+	init_MUTEX_LOCKED(&f->sem);
+	f->inocache = ic;
+
+	ret = jffs2_do_read_inode_internal(c, f, &n);
+	if (!ret) {
+		up(&f->sem);
+		jffs2_do_clear_inode(c, f);
+	}
+	kfree (f);
+	return ret;
+}
+
 void jffs2_do_clear_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f)
 {
 	struct jffs2_full_dirent *fd, *fds;





More information about the linux-mtd-cvs mailing list