xref: /openbmc/linux/fs/jffs2/readinode.c (revision 2b79adcc)
11da177e4SLinus Torvalds /*
21da177e4SLinus Torvalds  * JFFS2 -- Journalling Flash File System, Version 2.
31da177e4SLinus Torvalds  *
41da177e4SLinus Torvalds  * Copyright (C) 2001-2003 Red Hat, Inc.
51da177e4SLinus Torvalds  *
61da177e4SLinus Torvalds  * Created by David Woodhouse <dwmw2@infradead.org>
71da177e4SLinus Torvalds  *
81da177e4SLinus Torvalds  * For licensing information, see the file 'LICENCE' in this directory.
91da177e4SLinus Torvalds  *
102b79adccSArtem B. Bityutskiy  * $Id: readinode.c,v 1.127 2005/07/17 11:13:46 dedekind Exp $
111da177e4SLinus Torvalds  *
121da177e4SLinus Torvalds  */
131da177e4SLinus Torvalds 
141da177e4SLinus Torvalds #include <linux/kernel.h>
151da177e4SLinus Torvalds #include <linux/slab.h>
161da177e4SLinus Torvalds #include <linux/fs.h>
171da177e4SLinus Torvalds #include <linux/crc32.h>
181da177e4SLinus Torvalds #include <linux/pagemap.h>
191da177e4SLinus Torvalds #include <linux/mtd/mtd.h>
201da177e4SLinus Torvalds #include <linux/compiler.h>
211da177e4SLinus Torvalds #include "nodelist.h"
221da177e4SLinus Torvalds 
231da177e4SLinus Torvalds static int jffs2_add_frag_to_fragtree(struct jffs2_sb_info *c, struct rb_root *list, struct jffs2_node_frag *newfrag);
241da177e4SLinus Torvalds 
251da177e4SLinus Torvalds static void jffs2_obsolete_node_frag(struct jffs2_sb_info *c, struct jffs2_node_frag *this)
261da177e4SLinus Torvalds {
271da177e4SLinus Torvalds 	if (this->node) {
281da177e4SLinus Torvalds 		this->node->frags--;
291da177e4SLinus Torvalds 		if (!this->node->frags) {
301da177e4SLinus Torvalds 			/* The node has no valid frags left. It's totally obsoleted */
311da177e4SLinus Torvalds 			D2(printk(KERN_DEBUG "Marking old node @0x%08x (0x%04x-0x%04x) obsolete\n",
321da177e4SLinus Torvalds 				  ref_offset(this->node->raw), this->node->ofs, this->node->ofs+this->node->size));
331da177e4SLinus Torvalds 			jffs2_mark_node_obsolete(c, this->node->raw);
341da177e4SLinus Torvalds 			jffs2_free_full_dnode(this->node);
351da177e4SLinus Torvalds 		} else {
361da177e4SLinus Torvalds 			D2(printk(KERN_DEBUG "Marking old node @0x%08x (0x%04x-0x%04x) REF_NORMAL. frags is %d\n",
371da177e4SLinus Torvalds 				  ref_offset(this->node->raw), this->node->ofs, this->node->ofs+this->node->size,
381da177e4SLinus Torvalds 				  this->node->frags));
391da177e4SLinus Torvalds 			mark_ref_normal(this->node->raw);
401da177e4SLinus Torvalds 		}
411da177e4SLinus Torvalds 
421da177e4SLinus Torvalds 	}
431da177e4SLinus Torvalds 	jffs2_free_node_frag(this);
441da177e4SLinus Torvalds }
451da177e4SLinus Torvalds 
461da177e4SLinus Torvalds /* Given an inode, probably with existing list of fragments, add the new node
471da177e4SLinus Torvalds  * to the fragment list.
481da177e4SLinus Torvalds  */
491da177e4SLinus Torvalds int jffs2_add_full_dnode_to_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, struct jffs2_full_dnode *fn)
501da177e4SLinus Torvalds {
511da177e4SLinus Torvalds 	int ret;
521da177e4SLinus Torvalds 	struct jffs2_node_frag *newfrag;
531da177e4SLinus Torvalds 
541da177e4SLinus Torvalds 	D1(printk(KERN_DEBUG "jffs2_add_full_dnode_to_inode(ino #%u, f %p, fn %p)\n", f->inocache->ino, f, fn));
551da177e4SLinus Torvalds 
56336d2ff7SArtem B. Bityuckiy 	if (unlikely(!fn->size))
57336d2ff7SArtem B. Bityuckiy 		return 0;
58336d2ff7SArtem B. Bityuckiy 
591da177e4SLinus Torvalds 	newfrag = jffs2_alloc_node_frag();
601da177e4SLinus Torvalds 	if (unlikely(!newfrag))
611da177e4SLinus Torvalds 		return -ENOMEM;
621da177e4SLinus Torvalds 
631da177e4SLinus Torvalds 	D2(printk(KERN_DEBUG "adding node %04x-%04x @0x%08x on flash, newfrag *%p\n",
641da177e4SLinus Torvalds 		  fn->ofs, fn->ofs+fn->size, ref_offset(fn->raw), newfrag));
651da177e4SLinus Torvalds 
661da177e4SLinus Torvalds 	newfrag->ofs = fn->ofs;
671da177e4SLinus Torvalds 	newfrag->size = fn->size;
681da177e4SLinus Torvalds 	newfrag->node = fn;
691da177e4SLinus Torvalds 	newfrag->node->frags = 1;
701da177e4SLinus Torvalds 
711da177e4SLinus Torvalds 	ret = jffs2_add_frag_to_fragtree(c, &f->fragtree, newfrag);
721da177e4SLinus Torvalds 	if (ret)
731da177e4SLinus Torvalds 		return ret;
741da177e4SLinus Torvalds 
751da177e4SLinus Torvalds 	/* If we now share a page with other nodes, mark either previous
761da177e4SLinus Torvalds 	   or next node REF_NORMAL, as appropriate.  */
771da177e4SLinus Torvalds 	if (newfrag->ofs & (PAGE_CACHE_SIZE-1)) {
781da177e4SLinus Torvalds 		struct jffs2_node_frag *prev = frag_prev(newfrag);
791da177e4SLinus Torvalds 
801da177e4SLinus Torvalds 		mark_ref_normal(fn->raw);
811da177e4SLinus Torvalds 		/* If we don't start at zero there's _always_ a previous */
821da177e4SLinus Torvalds 		if (prev->node)
831da177e4SLinus Torvalds 			mark_ref_normal(prev->node->raw);
841da177e4SLinus Torvalds 	}
851da177e4SLinus Torvalds 
861da177e4SLinus Torvalds 	if ((newfrag->ofs+newfrag->size) & (PAGE_CACHE_SIZE-1)) {
871da177e4SLinus Torvalds 		struct jffs2_node_frag *next = frag_next(newfrag);
881da177e4SLinus Torvalds 
891da177e4SLinus Torvalds 		if (next) {
901da177e4SLinus Torvalds 			mark_ref_normal(fn->raw);
911da177e4SLinus Torvalds 			if (next->node)
921da177e4SLinus Torvalds 				mark_ref_normal(next->node->raw);
931da177e4SLinus Torvalds 		}
941da177e4SLinus Torvalds 	}
95730554d9SArtem B. Bityutskiy 	D2(jffs2_dbg_fragtree_paranoia_check(f));
96730554d9SArtem B. Bityutskiy 	D2(jffs2_dbg_dump_fragtree(f));
971da177e4SLinus Torvalds 	return 0;
981da177e4SLinus Torvalds }
991da177e4SLinus Torvalds 
1001da177e4SLinus Torvalds /* Doesn't set inode->i_size */
1011da177e4SLinus Torvalds static int jffs2_add_frag_to_fragtree(struct jffs2_sb_info *c, struct rb_root *list, struct jffs2_node_frag *newfrag)
1021da177e4SLinus Torvalds {
1031da177e4SLinus Torvalds 	struct jffs2_node_frag *this;
1041da177e4SLinus Torvalds 	uint32_t lastend;
1051da177e4SLinus Torvalds 
1061da177e4SLinus Torvalds 	/* Skip all the nodes which are completed before this one starts */
1071da177e4SLinus Torvalds 	this = jffs2_lookup_node_frag(list, newfrag->node->ofs);
1081da177e4SLinus Torvalds 
1091da177e4SLinus Torvalds 	if (this) {
1101da177e4SLinus Torvalds 		D2(printk(KERN_DEBUG "j_a_f_d_t_f: Lookup gave frag 0x%04x-0x%04x; phys 0x%08x (*%p)\n",
1111da177e4SLinus Torvalds 			  this->ofs, this->ofs+this->size, this->node?(ref_offset(this->node->raw)):0xffffffff, this));
1121da177e4SLinus Torvalds 		lastend = this->ofs + this->size;
1131da177e4SLinus Torvalds 	} else {
1141da177e4SLinus Torvalds 		D2(printk(KERN_DEBUG "j_a_f_d_t_f: Lookup gave no frag\n"));
1151da177e4SLinus Torvalds 		lastend = 0;
1161da177e4SLinus Torvalds 	}
1171da177e4SLinus Torvalds 
1181da177e4SLinus Torvalds 	/* See if we ran off the end of the list */
1191da177e4SLinus Torvalds 	if (lastend <= newfrag->ofs) {
1201da177e4SLinus Torvalds 		/* We did */
1211da177e4SLinus Torvalds 
1221da177e4SLinus Torvalds 		/* Check if 'this' node was on the same page as the new node.
1231da177e4SLinus Torvalds 		   If so, both 'this' and the new node get marked REF_NORMAL so
1241da177e4SLinus Torvalds 		   the GC can take a look.
1251da177e4SLinus Torvalds 		*/
1261da177e4SLinus Torvalds 		if (lastend && (lastend-1) >> PAGE_CACHE_SHIFT == newfrag->ofs >> PAGE_CACHE_SHIFT) {
1271da177e4SLinus Torvalds 			if (this->node)
1281da177e4SLinus Torvalds 				mark_ref_normal(this->node->raw);
1291da177e4SLinus Torvalds 			mark_ref_normal(newfrag->node->raw);
1301da177e4SLinus Torvalds 		}
1311da177e4SLinus Torvalds 
1321da177e4SLinus Torvalds 		if (lastend < newfrag->node->ofs) {
1331da177e4SLinus Torvalds 			/* ... and we need to put a hole in before the new node */
1341da177e4SLinus Torvalds 			struct jffs2_node_frag *holefrag = jffs2_alloc_node_frag();
1351da177e4SLinus Torvalds 			if (!holefrag) {
1361da177e4SLinus Torvalds 				jffs2_free_node_frag(newfrag);
1371da177e4SLinus Torvalds 				return -ENOMEM;
1381da177e4SLinus Torvalds 			}
1391da177e4SLinus Torvalds 			holefrag->ofs = lastend;
1401da177e4SLinus Torvalds 			holefrag->size = newfrag->node->ofs - lastend;
1411da177e4SLinus Torvalds 			holefrag->node = NULL;
1421da177e4SLinus Torvalds 			if (this) {
1431da177e4SLinus Torvalds 				/* By definition, the 'this' node has no right-hand child,
1441da177e4SLinus Torvalds 				   because there are no frags with offset greater than it.
1451da177e4SLinus Torvalds 				   So that's where we want to put the hole */
1461da177e4SLinus Torvalds 				D2(printk(KERN_DEBUG "Adding hole frag (%p) on right of node at (%p)\n", holefrag, this));
1471da177e4SLinus Torvalds 				rb_link_node(&holefrag->rb, &this->rb, &this->rb.rb_right);
1481da177e4SLinus Torvalds 			} else {
1491da177e4SLinus Torvalds 				D2(printk(KERN_DEBUG "Adding hole frag (%p) at root of tree\n", holefrag));
1501da177e4SLinus Torvalds 				rb_link_node(&holefrag->rb, NULL, &list->rb_node);
1511da177e4SLinus Torvalds 			}
1521da177e4SLinus Torvalds 			rb_insert_color(&holefrag->rb, list);
1531da177e4SLinus Torvalds 			this = holefrag;
1541da177e4SLinus Torvalds 		}
1551da177e4SLinus Torvalds 		if (this) {
1561da177e4SLinus Torvalds 			/* By definition, the 'this' node has no right-hand child,
1571da177e4SLinus Torvalds 			   because there are no frags with offset greater than it.
1581da177e4SLinus Torvalds 			   So that's where we want to put the hole */
1591da177e4SLinus Torvalds 			D2(printk(KERN_DEBUG "Adding new frag (%p) on right of node at (%p)\n", newfrag, this));
1601da177e4SLinus Torvalds 			rb_link_node(&newfrag->rb, &this->rb, &this->rb.rb_right);
1611da177e4SLinus Torvalds 		} else {
1621da177e4SLinus Torvalds 			D2(printk(KERN_DEBUG "Adding new frag (%p) at root of tree\n", newfrag));
1631da177e4SLinus Torvalds 			rb_link_node(&newfrag->rb, NULL, &list->rb_node);
1641da177e4SLinus Torvalds 		}
1651da177e4SLinus Torvalds 		rb_insert_color(&newfrag->rb, list);
1661da177e4SLinus Torvalds 		return 0;
1671da177e4SLinus Torvalds 	}
1681da177e4SLinus Torvalds 
1691da177e4SLinus Torvalds 	D2(printk(KERN_DEBUG "j_a_f_d_t_f: dealing with frag 0x%04x-0x%04x; phys 0x%08x (*%p)\n",
1701da177e4SLinus Torvalds 		  this->ofs, this->ofs+this->size, this->node?(ref_offset(this->node->raw)):0xffffffff, this));
1711da177e4SLinus Torvalds 
1721da177e4SLinus Torvalds 	/* OK. 'this' is pointing at the first frag that newfrag->ofs at least partially obsoletes,
1731da177e4SLinus Torvalds 	 * - i.e. newfrag->ofs < this->ofs+this->size && newfrag->ofs >= this->ofs
1741da177e4SLinus Torvalds 	 */
1751da177e4SLinus Torvalds 	if (newfrag->ofs > this->ofs) {
1761da177e4SLinus Torvalds 		/* This node isn't completely obsoleted. The start of it remains valid */
1771da177e4SLinus Torvalds 
1781da177e4SLinus Torvalds 		/* Mark the new node and the partially covered node REF_NORMAL -- let
1791da177e4SLinus Torvalds 		   the GC take a look at them */
1801da177e4SLinus Torvalds 		mark_ref_normal(newfrag->node->raw);
1811da177e4SLinus Torvalds 		if (this->node)
1821da177e4SLinus Torvalds 			mark_ref_normal(this->node->raw);
1831da177e4SLinus Torvalds 
1841da177e4SLinus Torvalds 		if (this->ofs + this->size > newfrag->ofs + newfrag->size) {
1851da177e4SLinus Torvalds 			/* The new node splits 'this' frag into two */
1861da177e4SLinus Torvalds 			struct jffs2_node_frag *newfrag2 = jffs2_alloc_node_frag();
1871da177e4SLinus Torvalds 			if (!newfrag2) {
1881da177e4SLinus Torvalds 				jffs2_free_node_frag(newfrag);
1891da177e4SLinus Torvalds 				return -ENOMEM;
1901da177e4SLinus Torvalds 			}
1911da177e4SLinus Torvalds 			D2(printk(KERN_DEBUG "split old frag 0x%04x-0x%04x -->", this->ofs, this->ofs+this->size);
1921da177e4SLinus Torvalds 			if (this->node)
1931da177e4SLinus Torvalds 				printk("phys 0x%08x\n", ref_offset(this->node->raw));
1941da177e4SLinus Torvalds 			else
1951da177e4SLinus Torvalds 				printk("hole\n");
1961da177e4SLinus Torvalds 			   )
1971da177e4SLinus Torvalds 
1981da177e4SLinus Torvalds 			/* New second frag pointing to this's node */
1991da177e4SLinus Torvalds 			newfrag2->ofs = newfrag->ofs + newfrag->size;
2001da177e4SLinus Torvalds 			newfrag2->size = (this->ofs+this->size) - newfrag2->ofs;
2011da177e4SLinus Torvalds 			newfrag2->node = this->node;
2021da177e4SLinus Torvalds 			if (this->node)
2031da177e4SLinus Torvalds 				this->node->frags++;
2041da177e4SLinus Torvalds 
2051da177e4SLinus Torvalds 			/* Adjust size of original 'this' */
2061da177e4SLinus Torvalds 			this->size = newfrag->ofs - this->ofs;
2071da177e4SLinus Torvalds 
2081da177e4SLinus Torvalds 			/* Now, we know there's no node with offset
2091da177e4SLinus Torvalds 			   greater than this->ofs but smaller than
2101da177e4SLinus Torvalds 			   newfrag2->ofs or newfrag->ofs, for obvious
2111da177e4SLinus Torvalds 			   reasons. So we can do a tree insert from
2121da177e4SLinus Torvalds 			   'this' to insert newfrag, and a tree insert
2131da177e4SLinus Torvalds 			   from newfrag to insert newfrag2. */
2141da177e4SLinus Torvalds 			jffs2_fragtree_insert(newfrag, this);
2151da177e4SLinus Torvalds 			rb_insert_color(&newfrag->rb, list);
2161da177e4SLinus Torvalds 
2171da177e4SLinus Torvalds 			jffs2_fragtree_insert(newfrag2, newfrag);
2181da177e4SLinus Torvalds 			rb_insert_color(&newfrag2->rb, list);
2191da177e4SLinus Torvalds 
2201da177e4SLinus Torvalds 			return 0;
2211da177e4SLinus Torvalds 		}
2221da177e4SLinus Torvalds 		/* New node just reduces 'this' frag in size, doesn't split it */
2231da177e4SLinus Torvalds 		this->size = newfrag->ofs - this->ofs;
2241da177e4SLinus Torvalds 
2251da177e4SLinus Torvalds 		/* Again, we know it lives down here in the tree */
2261da177e4SLinus Torvalds 		jffs2_fragtree_insert(newfrag, this);
2271da177e4SLinus Torvalds 		rb_insert_color(&newfrag->rb, list);
2281da177e4SLinus Torvalds 	} else {
2291da177e4SLinus Torvalds 		/* New frag starts at the same point as 'this' used to. Replace
2301da177e4SLinus Torvalds 		   it in the tree without doing a delete and insertion */
2311da177e4SLinus Torvalds 		D2(printk(KERN_DEBUG "Inserting newfrag (*%p),%d-%d in before 'this' (*%p),%d-%d\n",
2321da177e4SLinus Torvalds 			  newfrag, newfrag->ofs, newfrag->ofs+newfrag->size,
2331da177e4SLinus Torvalds 			  this, this->ofs, this->ofs+this->size));
2341da177e4SLinus Torvalds 
2351da177e4SLinus Torvalds 		rb_replace_node(&this->rb, &newfrag->rb, list);
2361da177e4SLinus Torvalds 
2371da177e4SLinus Torvalds 		if (newfrag->ofs + newfrag->size >= this->ofs+this->size) {
2381da177e4SLinus Torvalds 			D2(printk(KERN_DEBUG "Obsoleting node frag %p (%x-%x)\n", this, this->ofs, this->ofs+this->size));
2391da177e4SLinus Torvalds 			jffs2_obsolete_node_frag(c, this);
2401da177e4SLinus Torvalds 		} else {
2411da177e4SLinus Torvalds 			this->ofs += newfrag->size;
2421da177e4SLinus Torvalds 			this->size -= newfrag->size;
2431da177e4SLinus Torvalds 
2441da177e4SLinus Torvalds 			jffs2_fragtree_insert(this, newfrag);
2451da177e4SLinus Torvalds 			rb_insert_color(&this->rb, list);
2461da177e4SLinus Torvalds 			return 0;
2471da177e4SLinus Torvalds 		}
2481da177e4SLinus Torvalds 	}
2491da177e4SLinus Torvalds 	/* OK, now we have newfrag added in the correct place in the tree, but
2501da177e4SLinus Torvalds 	   frag_next(newfrag) may be a fragment which is overlapped by it
2511da177e4SLinus Torvalds 	*/
2521da177e4SLinus Torvalds 	while ((this = frag_next(newfrag)) && newfrag->ofs + newfrag->size >= this->ofs + this->size) {
2531da177e4SLinus Torvalds 		/* 'this' frag is obsoleted completely. */
2541da177e4SLinus Torvalds 		D2(printk(KERN_DEBUG "Obsoleting node frag %p (%x-%x) and removing from tree\n", this, this->ofs, this->ofs+this->size));
2551da177e4SLinus Torvalds 		rb_erase(&this->rb, list);
2561da177e4SLinus Torvalds 		jffs2_obsolete_node_frag(c, this);
2571da177e4SLinus Torvalds 	}
2581da177e4SLinus Torvalds 	/* Now we're pointing at the first frag which isn't totally obsoleted by
2591da177e4SLinus Torvalds 	   the new frag */
2601da177e4SLinus Torvalds 
2611da177e4SLinus Torvalds 	if (!this || newfrag->ofs + newfrag->size == this->ofs) {
2621da177e4SLinus Torvalds 		return 0;
2631da177e4SLinus Torvalds 	}
2641da177e4SLinus Torvalds 	/* Still some overlap but we don't need to move it in the tree */
2651da177e4SLinus Torvalds 	this->size = (this->ofs + this->size) - (newfrag->ofs + newfrag->size);
2661da177e4SLinus Torvalds 	this->ofs = newfrag->ofs + newfrag->size;
2671da177e4SLinus Torvalds 
2681da177e4SLinus Torvalds 	/* And mark them REF_NORMAL so the GC takes a look at them */
2691da177e4SLinus Torvalds 	if (this->node)
2701da177e4SLinus Torvalds 		mark_ref_normal(this->node->raw);
2711da177e4SLinus Torvalds 	mark_ref_normal(newfrag->node->raw);
2721da177e4SLinus Torvalds 
2731da177e4SLinus Torvalds 	return 0;
2741da177e4SLinus Torvalds }
2751da177e4SLinus Torvalds 
2761da177e4SLinus Torvalds void jffs2_truncate_fraglist (struct jffs2_sb_info *c, struct rb_root *list, uint32_t size)
2771da177e4SLinus Torvalds {
2781da177e4SLinus Torvalds 	struct jffs2_node_frag *frag = jffs2_lookup_node_frag(list, size);
2791da177e4SLinus Torvalds 
2801da177e4SLinus Torvalds 	D1(printk(KERN_DEBUG "Truncating fraglist to 0x%08x bytes\n", size));
2811da177e4SLinus Torvalds 
2821da177e4SLinus Torvalds 	/* We know frag->ofs <= size. That's what lookup does for us */
2831da177e4SLinus Torvalds 	if (frag && frag->ofs != size) {
2841da177e4SLinus Torvalds 		if (frag->ofs+frag->size >= size) {
2851da177e4SLinus Torvalds 			D1(printk(KERN_DEBUG "Truncating frag 0x%08x-0x%08x\n", frag->ofs, frag->ofs+frag->size));
2861da177e4SLinus Torvalds 			frag->size = size - frag->ofs;
2871da177e4SLinus Torvalds 		}
2881da177e4SLinus Torvalds 		frag = frag_next(frag);
2891da177e4SLinus Torvalds 	}
2901da177e4SLinus Torvalds 	while (frag && frag->ofs >= size) {
2911da177e4SLinus Torvalds 		struct jffs2_node_frag *next = frag_next(frag);
2921da177e4SLinus Torvalds 
2931da177e4SLinus Torvalds 		D1(printk(KERN_DEBUG "Removing frag 0x%08x-0x%08x\n", frag->ofs, frag->ofs+frag->size));
2941da177e4SLinus Torvalds 		frag_erase(frag, list);
2951da177e4SLinus Torvalds 		jffs2_obsolete_node_frag(c, frag);
2961da177e4SLinus Torvalds 		frag = next;
2971da177e4SLinus Torvalds 	}
2981da177e4SLinus Torvalds }
2991da177e4SLinus Torvalds 
3001da177e4SLinus Torvalds /* Scan the list of all nodes present for this ino, build map of versions, etc. */
3011da177e4SLinus Torvalds 
3021da177e4SLinus Torvalds static int jffs2_do_read_inode_internal(struct jffs2_sb_info *c,
3031da177e4SLinus Torvalds 					struct jffs2_inode_info *f,
3041da177e4SLinus Torvalds 					struct jffs2_raw_inode *latest_node);
3051da177e4SLinus Torvalds 
3061da177e4SLinus Torvalds int jffs2_do_read_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
3071da177e4SLinus Torvalds 			uint32_t ino, struct jffs2_raw_inode *latest_node)
3081da177e4SLinus Torvalds {
3091da177e4SLinus Torvalds 	D2(printk(KERN_DEBUG "jffs2_do_read_inode(): getting inocache\n"));
3101da177e4SLinus Torvalds 
3111da177e4SLinus Torvalds  retry_inocache:
3121da177e4SLinus Torvalds 	spin_lock(&c->inocache_lock);
3131da177e4SLinus Torvalds 	f->inocache = jffs2_get_ino_cache(c, ino);
3141da177e4SLinus Torvalds 
3151da177e4SLinus Torvalds 	D2(printk(KERN_DEBUG "jffs2_do_read_inode(): Got inocache at %p\n", f->inocache));
3161da177e4SLinus Torvalds 
3171da177e4SLinus Torvalds 	if (f->inocache) {
3181da177e4SLinus Torvalds 		/* Check its state. We may need to wait before we can use it */
3191da177e4SLinus Torvalds 		switch(f->inocache->state) {
3201da177e4SLinus Torvalds 		case INO_STATE_UNCHECKED:
3211da177e4SLinus Torvalds 		case INO_STATE_CHECKEDABSENT:
3221da177e4SLinus Torvalds 			f->inocache->state = INO_STATE_READING;
3231da177e4SLinus Torvalds 			break;
3241da177e4SLinus Torvalds 
3251da177e4SLinus Torvalds 		case INO_STATE_CHECKING:
3261da177e4SLinus Torvalds 		case INO_STATE_GC:
3271da177e4SLinus Torvalds 			/* If it's in either of these states, we need
3281da177e4SLinus Torvalds 			   to wait for whoever's got it to finish and
3291da177e4SLinus Torvalds 			   put it back. */
3301da177e4SLinus Torvalds 			D1(printk(KERN_DEBUG "jffs2_get_ino_cache_read waiting for ino #%u in state %d\n",
3311da177e4SLinus Torvalds 				  ino, f->inocache->state));
3321da177e4SLinus Torvalds 			sleep_on_spinunlock(&c->inocache_wq, &c->inocache_lock);
3331da177e4SLinus Torvalds 			goto retry_inocache;
3341da177e4SLinus Torvalds 
3351da177e4SLinus Torvalds 		case INO_STATE_READING:
3361da177e4SLinus Torvalds 		case INO_STATE_PRESENT:
3371da177e4SLinus Torvalds 			/* Eep. This should never happen. It can
3381da177e4SLinus Torvalds 			happen if Linux calls read_inode() again
3391da177e4SLinus Torvalds 			before clear_inode() has finished though. */
3401da177e4SLinus Torvalds 			printk(KERN_WARNING "Eep. Trying to read_inode #%u when it's already in state %d!\n", ino, f->inocache->state);
3411da177e4SLinus Torvalds 			/* Fail. That's probably better than allowing it to succeed */
3421da177e4SLinus Torvalds 			f->inocache = NULL;
3431da177e4SLinus Torvalds 			break;
3441da177e4SLinus Torvalds 
3451da177e4SLinus Torvalds 		default:
3461da177e4SLinus Torvalds 			BUG();
3471da177e4SLinus Torvalds 		}
3481da177e4SLinus Torvalds 	}
3491da177e4SLinus Torvalds 	spin_unlock(&c->inocache_lock);
3501da177e4SLinus Torvalds 
3511da177e4SLinus Torvalds 	if (!f->inocache && ino == 1) {
3521da177e4SLinus Torvalds 		/* Special case - no root inode on medium */
3531da177e4SLinus Torvalds 		f->inocache = jffs2_alloc_inode_cache();
3541da177e4SLinus Torvalds 		if (!f->inocache) {
3551da177e4SLinus Torvalds 			printk(KERN_CRIT "jffs2_do_read_inode(): Cannot allocate inocache for root inode\n");
3561da177e4SLinus Torvalds 			return -ENOMEM;
3571da177e4SLinus Torvalds 		}
3581da177e4SLinus Torvalds 		D1(printk(KERN_DEBUG "jffs2_do_read_inode(): Creating inocache for root inode\n"));
3591da177e4SLinus Torvalds 		memset(f->inocache, 0, sizeof(struct jffs2_inode_cache));
3601da177e4SLinus Torvalds 		f->inocache->ino = f->inocache->nlink = 1;
3611da177e4SLinus Torvalds 		f->inocache->nodes = (struct jffs2_raw_node_ref *)f->inocache;
3621da177e4SLinus Torvalds 		f->inocache->state = INO_STATE_READING;
3631da177e4SLinus Torvalds 		jffs2_add_ino_cache(c, f->inocache);
3641da177e4SLinus Torvalds 	}
3651da177e4SLinus Torvalds 	if (!f->inocache) {
3661da177e4SLinus Torvalds 		printk(KERN_WARNING "jffs2_do_read_inode() on nonexistent ino %u\n", ino);
3671da177e4SLinus Torvalds 		return -ENOENT;
3681da177e4SLinus Torvalds 	}
3691da177e4SLinus Torvalds 
3701da177e4SLinus Torvalds 	return jffs2_do_read_inode_internal(c, f, latest_node);
3711da177e4SLinus Torvalds }
3721da177e4SLinus Torvalds 
3731da177e4SLinus Torvalds int jffs2_do_crccheck_inode(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic)
3741da177e4SLinus Torvalds {
3751da177e4SLinus Torvalds 	struct jffs2_raw_inode n;
3761da177e4SLinus Torvalds 	struct jffs2_inode_info *f = kmalloc(sizeof(*f), GFP_KERNEL);
3771da177e4SLinus Torvalds 	int ret;
3781da177e4SLinus Torvalds 
3791da177e4SLinus Torvalds 	if (!f)
3801da177e4SLinus Torvalds 		return -ENOMEM;
3811da177e4SLinus Torvalds 
3821da177e4SLinus Torvalds 	memset(f, 0, sizeof(*f));
3831da177e4SLinus Torvalds 	init_MUTEX_LOCKED(&f->sem);
3841da177e4SLinus Torvalds 	f->inocache = ic;
3851da177e4SLinus Torvalds 
3861da177e4SLinus Torvalds 	ret = jffs2_do_read_inode_internal(c, f, &n);
3871da177e4SLinus Torvalds 	if (!ret) {
3881da177e4SLinus Torvalds 		up(&f->sem);
3891da177e4SLinus Torvalds 		jffs2_do_clear_inode(c, f);
3901da177e4SLinus Torvalds 	}
3911da177e4SLinus Torvalds 	kfree (f);
3921da177e4SLinus Torvalds 	return ret;
3931da177e4SLinus Torvalds }
3941da177e4SLinus Torvalds 
3951da177e4SLinus Torvalds static int jffs2_do_read_inode_internal(struct jffs2_sb_info *c,
3961da177e4SLinus Torvalds 					struct jffs2_inode_info *f,
3971da177e4SLinus Torvalds 					struct jffs2_raw_inode *latest_node)
3981da177e4SLinus Torvalds {
3999dee7503SDavid Woodhouse 	struct jffs2_tmp_dnode_info *tn = NULL;
4009dee7503SDavid Woodhouse 	struct rb_root tn_list;
4019dee7503SDavid Woodhouse 	struct rb_node *rb, *repl_rb;
4021da177e4SLinus Torvalds 	struct jffs2_full_dirent *fd_list;
4031da177e4SLinus Torvalds 	struct jffs2_full_dnode *fn = NULL;
4041da177e4SLinus Torvalds 	uint32_t crc;
4051da177e4SLinus Torvalds 	uint32_t latest_mctime, mctime_ver;
4061da177e4SLinus Torvalds 	uint32_t mdata_ver = 0;
4071da177e4SLinus Torvalds 	size_t retlen;
4081da177e4SLinus Torvalds 	int ret;
4091da177e4SLinus Torvalds 
4101da177e4SLinus Torvalds 	D1(printk(KERN_DEBUG "jffs2_do_read_inode_internal(): ino #%u nlink is %d\n", f->inocache->ino, f->inocache->nlink));
4111da177e4SLinus Torvalds 
4121da177e4SLinus Torvalds 	/* Grab all nodes relevant to this ino */
4131da177e4SLinus Torvalds 	ret = jffs2_get_inode_nodes(c, f, &tn_list, &fd_list, &f->highest_version, &latest_mctime, &mctime_ver);
4141da177e4SLinus Torvalds 
4151da177e4SLinus Torvalds 	if (ret) {
4161da177e4SLinus Torvalds 		printk(KERN_CRIT "jffs2_get_inode_nodes() for ino %u returned %d\n", f->inocache->ino, ret);
4171da177e4SLinus Torvalds 		if (f->inocache->state == INO_STATE_READING)
4181da177e4SLinus Torvalds 			jffs2_set_inocache_state(c, f->inocache, INO_STATE_CHECKEDABSENT);
4191da177e4SLinus Torvalds 		return ret;
4201da177e4SLinus Torvalds 	}
4211da177e4SLinus Torvalds 	f->dents = fd_list;
4221da177e4SLinus Torvalds 
4239dee7503SDavid Woodhouse 	rb = rb_first(&tn_list);
4241da177e4SLinus Torvalds 
4259dee7503SDavid Woodhouse 	while (rb) {
4269dee7503SDavid Woodhouse 		tn = rb_entry(rb, struct jffs2_tmp_dnode_info, rb);
4271da177e4SLinus Torvalds 		fn = tn->fn;
4281da177e4SLinus Torvalds 
4291da177e4SLinus Torvalds 		if (f->metadata) {
4301da177e4SLinus Torvalds 			if (likely(tn->version >= mdata_ver)) {
4311da177e4SLinus Torvalds 				D1(printk(KERN_DEBUG "Obsoleting old metadata at 0x%08x\n", ref_offset(f->metadata->raw)));
4321da177e4SLinus Torvalds 				jffs2_mark_node_obsolete(c, f->metadata->raw);
4331da177e4SLinus Torvalds 				jffs2_free_full_dnode(f->metadata);
4341da177e4SLinus Torvalds 				f->metadata = NULL;
4351da177e4SLinus Torvalds 
4361da177e4SLinus Torvalds 				mdata_ver = 0;
4371da177e4SLinus Torvalds 			} else {
4381da177e4SLinus Torvalds 				/* This should never happen. */
4391da177e4SLinus Torvalds 				printk(KERN_WARNING "Er. New metadata at 0x%08x with ver %d is actually older than previous ver %d at 0x%08x\n",
4401da177e4SLinus Torvalds 					  ref_offset(fn->raw), tn->version, mdata_ver, ref_offset(f->metadata->raw));
4411da177e4SLinus Torvalds 				jffs2_mark_node_obsolete(c, fn->raw);
4421da177e4SLinus Torvalds 				jffs2_free_full_dnode(fn);
4431da177e4SLinus Torvalds 				/* Fill in latest_node from the metadata, not this one we're about to free... */
4441da177e4SLinus Torvalds 				fn = f->metadata;
4451da177e4SLinus Torvalds 				goto next_tn;
4461da177e4SLinus Torvalds 			}
4471da177e4SLinus Torvalds 		}
4481da177e4SLinus Torvalds 
4491da177e4SLinus Torvalds 		if (fn->size) {
4501da177e4SLinus Torvalds 			jffs2_add_full_dnode_to_inode(c, f, fn);
4511da177e4SLinus Torvalds 		} else {
4521da177e4SLinus Torvalds 			/* Zero-sized node at end of version list. Just a metadata update */
4531da177e4SLinus Torvalds 			D1(printk(KERN_DEBUG "metadata @%08x: ver %d\n", ref_offset(fn->raw), tn->version));
4541da177e4SLinus Torvalds 			f->metadata = fn;
4551da177e4SLinus Torvalds 			mdata_ver = tn->version;
4561da177e4SLinus Torvalds 		}
4571da177e4SLinus Torvalds 	next_tn:
4589dee7503SDavid Woodhouse 		BUG_ON(rb->rb_left);
4599dee7503SDavid Woodhouse 		if (rb->rb_parent && rb->rb_parent->rb_left == rb) {
4609dee7503SDavid Woodhouse 			/* We were then left-hand child of our parent. We need
4619dee7503SDavid Woodhouse 			   to move our own right-hand child into our place. */
4629dee7503SDavid Woodhouse 			repl_rb = rb->rb_right;
4639dee7503SDavid Woodhouse 			if (repl_rb)
4649dee7503SDavid Woodhouse 				repl_rb->rb_parent = rb->rb_parent;
4659dee7503SDavid Woodhouse 		} else
4669dee7503SDavid Woodhouse 			repl_rb = NULL;
4679dee7503SDavid Woodhouse 
4689dee7503SDavid Woodhouse 		rb = rb_next(rb);
4699dee7503SDavid Woodhouse 
4709dee7503SDavid Woodhouse 		/* Remove the spent tn from the tree; don't bother rebalancing
4719dee7503SDavid Woodhouse 		   but put our right-hand child in our own place. */
4729dee7503SDavid Woodhouse 		if (tn->rb.rb_parent) {
4739dee7503SDavid Woodhouse 			if (tn->rb.rb_parent->rb_left == &tn->rb)
4749dee7503SDavid Woodhouse 				tn->rb.rb_parent->rb_left = repl_rb;
4759dee7503SDavid Woodhouse 			else if (tn->rb.rb_parent->rb_right == &tn->rb)
4769dee7503SDavid Woodhouse 				tn->rb.rb_parent->rb_right = repl_rb;
4779dee7503SDavid Woodhouse 			else BUG();
4789dee7503SDavid Woodhouse 		} else if (tn->rb.rb_right)
4799dee7503SDavid Woodhouse 			tn->rb.rb_right->rb_parent = NULL;
4809dee7503SDavid Woodhouse 
4811da177e4SLinus Torvalds 		jffs2_free_tmp_dnode_info(tn);
4821da177e4SLinus Torvalds 	}
483730554d9SArtem B. Bityutskiy 	jffs2_dbg_fragtree_paranoia_check(f);
4841da177e4SLinus Torvalds 
4851da177e4SLinus Torvalds 	if (!fn) {
4861da177e4SLinus Torvalds 		/* No data nodes for this inode. */
4871da177e4SLinus Torvalds 		if (f->inocache->ino != 1) {
4881da177e4SLinus Torvalds 			printk(KERN_WARNING "jffs2_do_read_inode(): No data nodes found for ino #%u\n", f->inocache->ino);
4891da177e4SLinus Torvalds 			if (!fd_list) {
4901da177e4SLinus Torvalds 				if (f->inocache->state == INO_STATE_READING)
4911da177e4SLinus Torvalds 					jffs2_set_inocache_state(c, f->inocache, INO_STATE_CHECKEDABSENT);
4921da177e4SLinus Torvalds 				return -EIO;
4931da177e4SLinus Torvalds 			}
4941da177e4SLinus Torvalds 			printk(KERN_WARNING "jffs2_do_read_inode(): But it has children so we fake some modes for it\n");
4951da177e4SLinus Torvalds 		}
4961da177e4SLinus Torvalds 		latest_node->mode = cpu_to_jemode(S_IFDIR|S_IRUGO|S_IWUSR|S_IXUGO);
4971da177e4SLinus Torvalds 		latest_node->version = cpu_to_je32(0);
4981da177e4SLinus Torvalds 		latest_node->atime = latest_node->ctime = latest_node->mtime = cpu_to_je32(0);
4991da177e4SLinus Torvalds 		latest_node->isize = cpu_to_je32(0);
5001da177e4SLinus Torvalds 		latest_node->gid = cpu_to_je16(0);
5011da177e4SLinus Torvalds 		latest_node->uid = cpu_to_je16(0);
5021da177e4SLinus Torvalds 		if (f->inocache->state == INO_STATE_READING)
5031da177e4SLinus Torvalds 			jffs2_set_inocache_state(c, f->inocache, INO_STATE_PRESENT);
5041da177e4SLinus Torvalds 		return 0;
5051da177e4SLinus Torvalds 	}
5061da177e4SLinus Torvalds 
5071da177e4SLinus Torvalds 	ret = jffs2_flash_read(c, ref_offset(fn->raw), sizeof(*latest_node), &retlen, (void *)latest_node);
5081da177e4SLinus Torvalds 	if (ret || retlen != sizeof(*latest_node)) {
5091da177e4SLinus Torvalds 		printk(KERN_NOTICE "MTD read in jffs2_do_read_inode() failed: Returned %d, %zd of %zd bytes read\n",
5101da177e4SLinus Torvalds 		       ret, retlen, sizeof(*latest_node));
5111da177e4SLinus Torvalds 		/* FIXME: If this fails, there seems to be a memory leak. Find it. */
5121da177e4SLinus Torvalds 		up(&f->sem);
5131da177e4SLinus Torvalds 		jffs2_do_clear_inode(c, f);
5141da177e4SLinus Torvalds 		return ret?ret:-EIO;
5151da177e4SLinus Torvalds 	}
5161da177e4SLinus Torvalds 
5171da177e4SLinus Torvalds 	crc = crc32(0, latest_node, sizeof(*latest_node)-8);
5181da177e4SLinus Torvalds 	if (crc != je32_to_cpu(latest_node->node_crc)) {
5191da177e4SLinus Torvalds 		printk(KERN_NOTICE "CRC failed for read_inode of inode %u at physical location 0x%x\n", f->inocache->ino, ref_offset(fn->raw));
5201da177e4SLinus Torvalds 		up(&f->sem);
5211da177e4SLinus Torvalds 		jffs2_do_clear_inode(c, f);
5221da177e4SLinus Torvalds 		return -EIO;
5231da177e4SLinus Torvalds 	}
5241da177e4SLinus Torvalds 
5251da177e4SLinus Torvalds 	switch(jemode_to_cpu(latest_node->mode) & S_IFMT) {
5261da177e4SLinus Torvalds 	case S_IFDIR:
5271da177e4SLinus Torvalds 		if (mctime_ver > je32_to_cpu(latest_node->version)) {
5281da177e4SLinus Torvalds 			/* The times in the latest_node are actually older than
5291da177e4SLinus Torvalds 			   mctime in the latest dirent. Cheat. */
5301da177e4SLinus Torvalds 			latest_node->ctime = latest_node->mtime = cpu_to_je32(latest_mctime);
5311da177e4SLinus Torvalds 		}
5321da177e4SLinus Torvalds 		break;
5331da177e4SLinus Torvalds 
5341da177e4SLinus Torvalds 
5351da177e4SLinus Torvalds 	case S_IFREG:
5361da177e4SLinus Torvalds 		/* If it was a regular file, truncate it to the latest node's isize */
5371da177e4SLinus Torvalds 		jffs2_truncate_fraglist(c, &f->fragtree, je32_to_cpu(latest_node->isize));
5381da177e4SLinus Torvalds 		break;
5391da177e4SLinus Torvalds 
5401da177e4SLinus Torvalds 	case S_IFLNK:
5411da177e4SLinus Torvalds 		/* Hack to work around broken isize in old symlink code.
5421da177e4SLinus Torvalds 		   Remove this when dwmw2 comes to his senses and stops
5431da177e4SLinus Torvalds 		   symlinks from being an entirely gratuitous special
5441da177e4SLinus Torvalds 		   case. */
5451da177e4SLinus Torvalds 		if (!je32_to_cpu(latest_node->isize))
5461da177e4SLinus Torvalds 			latest_node->isize = latest_node->dsize;
54732f1a95dSArtem B. Bityuckiy 
54832f1a95dSArtem B. Bityuckiy 		if (f->inocache->state != INO_STATE_CHECKING) {
54932f1a95dSArtem B. Bityuckiy 			/* Symlink's inode data is the target path. Read it and
5502b79adccSArtem B. Bityutskiy 			 * keep in RAM to facilitate quick follow symlink
5512b79adccSArtem B. Bityutskiy 			 * operation. */
5522b79adccSArtem B. Bityutskiy 			f->target = kmalloc(je32_to_cpu(latest_node->csize) + 1, GFP_KERNEL);
5532b79adccSArtem B. Bityutskiy 			if (!f->target) {
55432f1a95dSArtem B. Bityuckiy 				printk(KERN_WARNING "Can't allocate %d bytes of memory "
55532f1a95dSArtem B. Bityuckiy 						"for the symlink target path cache\n",
55632f1a95dSArtem B. Bityuckiy 						je32_to_cpu(latest_node->csize));
55732f1a95dSArtem B. Bityuckiy 				up(&f->sem);
55832f1a95dSArtem B. Bityuckiy 				jffs2_do_clear_inode(c, f);
55932f1a95dSArtem B. Bityuckiy 				return -ENOMEM;
56032f1a95dSArtem B. Bityuckiy 			}
56132f1a95dSArtem B. Bityuckiy 
56232f1a95dSArtem B. Bityuckiy 			ret = jffs2_flash_read(c, ref_offset(fn->raw) + sizeof(*latest_node),
5632b79adccSArtem B. Bityutskiy 						je32_to_cpu(latest_node->csize), &retlen, (char *)f->target);
56432f1a95dSArtem B. Bityuckiy 
56532f1a95dSArtem B. Bityuckiy 			if (ret  || retlen != je32_to_cpu(latest_node->csize)) {
56632f1a95dSArtem B. Bityuckiy 				if (retlen != je32_to_cpu(latest_node->csize))
56732f1a95dSArtem B. Bityuckiy 					ret = -EIO;
5682b79adccSArtem B. Bityutskiy 				kfree(f->target);
5692b79adccSArtem B. Bityutskiy 				f->target = NULL;
57032f1a95dSArtem B. Bityuckiy 				up(&f->sem);
57132f1a95dSArtem B. Bityuckiy 				jffs2_do_clear_inode(c, f);
57232f1a95dSArtem B. Bityuckiy 				return -ret;
57332f1a95dSArtem B. Bityuckiy 			}
57432f1a95dSArtem B. Bityuckiy 
5752b79adccSArtem B. Bityutskiy 			f->target[je32_to_cpu(latest_node->csize)] = '\0';
57632f1a95dSArtem B. Bityuckiy 			D1(printk(KERN_DEBUG "jffs2_do_read_inode(): symlink's target '%s' cached\n",
5772b79adccSArtem B. Bityutskiy 						f->target));
57832f1a95dSArtem B. Bityuckiy 		}
57932f1a95dSArtem B. Bityuckiy 
5801da177e4SLinus Torvalds 		/* fall through... */
5811da177e4SLinus Torvalds 
5821da177e4SLinus Torvalds 	case S_IFBLK:
5831da177e4SLinus Torvalds 	case S_IFCHR:
5841da177e4SLinus Torvalds 		/* Certain inode types should have only one data node, and it's
5851da177e4SLinus Torvalds 		   kept as the metadata node */
5861da177e4SLinus Torvalds 		if (f->metadata) {
5871da177e4SLinus Torvalds 			printk(KERN_WARNING "Argh. Special inode #%u with mode 0%o had metadata node\n",
5881da177e4SLinus Torvalds 			       f->inocache->ino, jemode_to_cpu(latest_node->mode));
5891da177e4SLinus Torvalds 			up(&f->sem);
5901da177e4SLinus Torvalds 			jffs2_do_clear_inode(c, f);
5911da177e4SLinus Torvalds 			return -EIO;
5921da177e4SLinus Torvalds 		}
5931da177e4SLinus Torvalds 		if (!frag_first(&f->fragtree)) {
5941da177e4SLinus Torvalds 			printk(KERN_WARNING "Argh. Special inode #%u with mode 0%o has no fragments\n",
5951da177e4SLinus Torvalds 			       f->inocache->ino, jemode_to_cpu(latest_node->mode));
5961da177e4SLinus Torvalds 			up(&f->sem);
5971da177e4SLinus Torvalds 			jffs2_do_clear_inode(c, f);
5981da177e4SLinus Torvalds 			return -EIO;
5991da177e4SLinus Torvalds 		}
6001da177e4SLinus Torvalds 		/* ASSERT: f->fraglist != NULL */
6011da177e4SLinus Torvalds 		if (frag_next(frag_first(&f->fragtree))) {
6021da177e4SLinus Torvalds 			printk(KERN_WARNING "Argh. Special inode #%u with mode 0x%x had more than one node\n",
6031da177e4SLinus Torvalds 			       f->inocache->ino, jemode_to_cpu(latest_node->mode));
6041da177e4SLinus Torvalds 			/* FIXME: Deal with it - check crc32, check for duplicate node, check times and discard the older one */
6051da177e4SLinus Torvalds 			up(&f->sem);
6061da177e4SLinus Torvalds 			jffs2_do_clear_inode(c, f);
6071da177e4SLinus Torvalds 			return -EIO;
6081da177e4SLinus Torvalds 		}
6091da177e4SLinus Torvalds 		/* OK. We're happy */
6101da177e4SLinus Torvalds 		f->metadata = frag_first(&f->fragtree)->node;
6111da177e4SLinus Torvalds 		jffs2_free_node_frag(frag_first(&f->fragtree));
6121da177e4SLinus Torvalds 		f->fragtree = RB_ROOT;
6131da177e4SLinus Torvalds 		break;
6141da177e4SLinus Torvalds 	}
6151da177e4SLinus Torvalds 	if (f->inocache->state == INO_STATE_READING)
6161da177e4SLinus Torvalds 		jffs2_set_inocache_state(c, f->inocache, INO_STATE_PRESENT);
6171da177e4SLinus Torvalds 
6181da177e4SLinus Torvalds 	return 0;
6191da177e4SLinus Torvalds }
6201da177e4SLinus Torvalds 
6211da177e4SLinus Torvalds void jffs2_do_clear_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f)
6221da177e4SLinus Torvalds {
6231da177e4SLinus Torvalds 	struct jffs2_full_dirent *fd, *fds;
6241da177e4SLinus Torvalds 	int deleted;
6251da177e4SLinus Torvalds 
6261da177e4SLinus Torvalds 	down(&f->sem);
6271da177e4SLinus Torvalds 	deleted = f->inocache && !f->inocache->nlink;
6281da177e4SLinus Torvalds 
62967e345d1SDavid Woodhouse 	if (f->inocache && f->inocache->state != INO_STATE_CHECKING)
63067e345d1SDavid Woodhouse 		jffs2_set_inocache_state(c, f->inocache, INO_STATE_CLEARING);
63167e345d1SDavid Woodhouse 
6321da177e4SLinus Torvalds 	if (f->metadata) {
6331da177e4SLinus Torvalds 		if (deleted)
6341da177e4SLinus Torvalds 			jffs2_mark_node_obsolete(c, f->metadata->raw);
6351da177e4SLinus Torvalds 		jffs2_free_full_dnode(f->metadata);
6361da177e4SLinus Torvalds 	}
6371da177e4SLinus Torvalds 
6381da177e4SLinus Torvalds 	jffs2_kill_fragtree(&f->fragtree, deleted?c:NULL);
6391da177e4SLinus Torvalds 
6402b79adccSArtem B. Bityutskiy 	if (f->target) {
6412b79adccSArtem B. Bityutskiy 		kfree(f->target);
6422b79adccSArtem B. Bityutskiy 		f->target = NULL;
64332f1a95dSArtem B. Bityuckiy 	}
6441da177e4SLinus Torvalds 
6452b79adccSArtem B. Bityutskiy 	fds = f->dents;
6461da177e4SLinus Torvalds 	while(fds) {
6471da177e4SLinus Torvalds 		fd = fds;
6481da177e4SLinus Torvalds 		fds = fd->next;
6491da177e4SLinus Torvalds 		jffs2_free_full_dirent(fd);
6501da177e4SLinus Torvalds 	}
6511da177e4SLinus Torvalds 
65267e345d1SDavid Woodhouse 	if (f->inocache && f->inocache->state != INO_STATE_CHECKING) {
6531da177e4SLinus Torvalds 		jffs2_set_inocache_state(c, f->inocache, INO_STATE_CHECKEDABSENT);
65467e345d1SDavid Woodhouse 		if (f->inocache->nodes == (void *)f->inocache)
65567e345d1SDavid Woodhouse 			jffs2_del_ino_cache(c, f->inocache);
65667e345d1SDavid Woodhouse 	}
6571da177e4SLinus Torvalds 
6581da177e4SLinus Torvalds 	up(&f->sem);
6591da177e4SLinus Torvalds }
660