xref: /openbmc/linux/fs/jffs2/readinode.c (revision 67e345d1)
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  *
1067e345d1SDavid Woodhouse  * $Id: readinode.c,v 1.118 2005/02/27 23:01:33 dwmw2 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 #if CONFIG_JFFS2_FS_DEBUG >= 2
261da177e4SLinus Torvalds static void jffs2_print_fragtree(struct rb_root *list, int permitbug)
271da177e4SLinus Torvalds {
281da177e4SLinus Torvalds 	struct jffs2_node_frag *this = frag_first(list);
291da177e4SLinus Torvalds 	uint32_t lastofs = 0;
301da177e4SLinus Torvalds 	int buggy = 0;
311da177e4SLinus Torvalds 
321da177e4SLinus Torvalds 	while(this) {
331da177e4SLinus Torvalds 		if (this->node)
341da177e4SLinus Torvalds 			printk(KERN_DEBUG "frag %04x-%04x: 0x%08x(%d) on flash (*%p). left (%p), right (%p), parent (%p)\n",
351da177e4SLinus Torvalds 			       this->ofs, this->ofs+this->size, ref_offset(this->node->raw), ref_flags(this->node->raw),
361da177e4SLinus Torvalds 			       this, frag_left(this), frag_right(this), frag_parent(this));
371da177e4SLinus Torvalds 		else
381da177e4SLinus Torvalds 			printk(KERN_DEBUG "frag %04x-%04x: hole (*%p). left (%p} right (%p), parent (%p)\n", this->ofs,
391da177e4SLinus Torvalds 			       this->ofs+this->size, this, frag_left(this), frag_right(this), frag_parent(this));
401da177e4SLinus Torvalds 		if (this->ofs != lastofs)
411da177e4SLinus Torvalds 			buggy = 1;
421da177e4SLinus Torvalds 		lastofs = this->ofs+this->size;
431da177e4SLinus Torvalds 		this = frag_next(this);
441da177e4SLinus Torvalds 	}
451da177e4SLinus Torvalds 	if (buggy && !permitbug) {
461da177e4SLinus Torvalds 		printk(KERN_CRIT "Frag tree got a hole in it\n");
471da177e4SLinus Torvalds 		BUG();
481da177e4SLinus Torvalds 	}
491da177e4SLinus Torvalds }
501da177e4SLinus Torvalds 
511da177e4SLinus Torvalds void jffs2_print_frag_list(struct jffs2_inode_info *f)
521da177e4SLinus Torvalds {
531da177e4SLinus Torvalds 	jffs2_print_fragtree(&f->fragtree, 0);
541da177e4SLinus Torvalds 
551da177e4SLinus Torvalds 	if (f->metadata) {
561da177e4SLinus Torvalds 		printk(KERN_DEBUG "metadata at 0x%08x\n", ref_offset(f->metadata->raw));
571da177e4SLinus Torvalds 	}
581da177e4SLinus Torvalds }
591da177e4SLinus Torvalds #endif
601da177e4SLinus Torvalds 
611da177e4SLinus Torvalds #if CONFIG_JFFS2_FS_DEBUG >= 1
621da177e4SLinus Torvalds static int jffs2_sanitycheck_fragtree(struct jffs2_inode_info *f)
631da177e4SLinus Torvalds {
641da177e4SLinus Torvalds 	struct jffs2_node_frag *frag;
651da177e4SLinus Torvalds 	int bitched = 0;
661da177e4SLinus Torvalds 
671da177e4SLinus Torvalds 	for (frag = frag_first(&f->fragtree); frag; frag = frag_next(frag)) {
681da177e4SLinus Torvalds 
691da177e4SLinus Torvalds 		struct jffs2_full_dnode *fn = frag->node;
701da177e4SLinus Torvalds 		if (!fn || !fn->raw)
711da177e4SLinus Torvalds 			continue;
721da177e4SLinus Torvalds 
731da177e4SLinus Torvalds 		if (ref_flags(fn->raw) == REF_PRISTINE) {
741da177e4SLinus Torvalds 
751da177e4SLinus Torvalds 			if (fn->frags > 1) {
761da177e4SLinus Torvalds 				printk(KERN_WARNING "REF_PRISTINE node at 0x%08x had %d frags. Tell dwmw2\n", ref_offset(fn->raw), fn->frags);
771da177e4SLinus Torvalds 				bitched = 1;
781da177e4SLinus Torvalds 			}
791da177e4SLinus Torvalds 			/* A hole node which isn't multi-page should be garbage-collected
801da177e4SLinus Torvalds 			   and merged anyway, so we just check for the frag size here,
811da177e4SLinus Torvalds 			   rather than mucking around with actually reading the node
821da177e4SLinus Torvalds 			   and checking the compression type, which is the real way
831da177e4SLinus Torvalds 			   to tell a hole node. */
841da177e4SLinus Torvalds 			if (frag->ofs & (PAGE_CACHE_SIZE-1) && frag_prev(frag) && frag_prev(frag)->size < PAGE_CACHE_SIZE && frag_prev(frag)->node) {
851da177e4SLinus Torvalds 				printk(KERN_WARNING "REF_PRISTINE node at 0x%08x had a previous non-hole frag in the same page. Tell dwmw2\n",
861da177e4SLinus Torvalds 				       ref_offset(fn->raw));
871da177e4SLinus Torvalds 				bitched = 1;
881da177e4SLinus Torvalds 			}
891da177e4SLinus Torvalds 
901da177e4SLinus Torvalds 			if ((frag->ofs+frag->size) & (PAGE_CACHE_SIZE-1) && frag_next(frag) && frag_next(frag)->size < PAGE_CACHE_SIZE && frag_next(frag)->node) {
911da177e4SLinus Torvalds 				printk(KERN_WARNING "REF_PRISTINE node at 0x%08x (%08x-%08x) had a following non-hole frag in the same page. Tell dwmw2\n",
921da177e4SLinus Torvalds 				       ref_offset(fn->raw), frag->ofs, frag->ofs+frag->size);
931da177e4SLinus Torvalds 				bitched = 1;
941da177e4SLinus Torvalds 			}
951da177e4SLinus Torvalds 		}
961da177e4SLinus Torvalds 	}
971da177e4SLinus Torvalds 
981da177e4SLinus Torvalds 	if (bitched) {
991da177e4SLinus Torvalds 		struct jffs2_node_frag *thisfrag;
1001da177e4SLinus Torvalds 
1011da177e4SLinus Torvalds 		printk(KERN_WARNING "Inode is #%u\n", f->inocache->ino);
1021da177e4SLinus Torvalds 		thisfrag = frag_first(&f->fragtree);
1031da177e4SLinus Torvalds 		while (thisfrag) {
1041da177e4SLinus Torvalds 			if (!thisfrag->node) {
1051da177e4SLinus Torvalds 				printk("Frag @0x%x-0x%x; node-less hole\n",
1061da177e4SLinus Torvalds 				       thisfrag->ofs, thisfrag->size + thisfrag->ofs);
1071da177e4SLinus Torvalds 			} else if (!thisfrag->node->raw) {
1081da177e4SLinus Torvalds 				printk("Frag @0x%x-0x%x; raw-less hole\n",
1091da177e4SLinus Torvalds 				       thisfrag->ofs, thisfrag->size + thisfrag->ofs);
1101da177e4SLinus Torvalds 			} else {
1111da177e4SLinus Torvalds 				printk("Frag @0x%x-0x%x; raw at 0x%08x(%d) (0x%x-0x%x)\n",
1121da177e4SLinus Torvalds 				       thisfrag->ofs, thisfrag->size + thisfrag->ofs,
1131da177e4SLinus Torvalds 				       ref_offset(thisfrag->node->raw), ref_flags(thisfrag->node->raw),
1141da177e4SLinus Torvalds 				       thisfrag->node->ofs, thisfrag->node->ofs+thisfrag->node->size);
1151da177e4SLinus Torvalds 			}
1161da177e4SLinus Torvalds 			thisfrag = frag_next(thisfrag);
1171da177e4SLinus Torvalds 		}
1181da177e4SLinus Torvalds 	}
1191da177e4SLinus Torvalds 	return bitched;
1201da177e4SLinus Torvalds }
1211da177e4SLinus Torvalds #endif /* D1 */
1221da177e4SLinus Torvalds 
1231da177e4SLinus Torvalds static void jffs2_obsolete_node_frag(struct jffs2_sb_info *c, struct jffs2_node_frag *this)
1241da177e4SLinus Torvalds {
1251da177e4SLinus Torvalds 	if (this->node) {
1261da177e4SLinus Torvalds 		this->node->frags--;
1271da177e4SLinus Torvalds 		if (!this->node->frags) {
1281da177e4SLinus Torvalds 			/* The node has no valid frags left. It's totally obsoleted */
1291da177e4SLinus Torvalds 			D2(printk(KERN_DEBUG "Marking old node @0x%08x (0x%04x-0x%04x) obsolete\n",
1301da177e4SLinus Torvalds 				  ref_offset(this->node->raw), this->node->ofs, this->node->ofs+this->node->size));
1311da177e4SLinus Torvalds 			jffs2_mark_node_obsolete(c, this->node->raw);
1321da177e4SLinus Torvalds 			jffs2_free_full_dnode(this->node);
1331da177e4SLinus Torvalds 		} else {
1341da177e4SLinus Torvalds 			D2(printk(KERN_DEBUG "Marking old node @0x%08x (0x%04x-0x%04x) REF_NORMAL. frags is %d\n",
1351da177e4SLinus Torvalds 				  ref_offset(this->node->raw), this->node->ofs, this->node->ofs+this->node->size,
1361da177e4SLinus Torvalds 				  this->node->frags));
1371da177e4SLinus Torvalds 			mark_ref_normal(this->node->raw);
1381da177e4SLinus Torvalds 		}
1391da177e4SLinus Torvalds 
1401da177e4SLinus Torvalds 	}
1411da177e4SLinus Torvalds 	jffs2_free_node_frag(this);
1421da177e4SLinus Torvalds }
1431da177e4SLinus Torvalds 
1441da177e4SLinus Torvalds /* Given an inode, probably with existing list of fragments, add the new node
1451da177e4SLinus Torvalds  * to the fragment list.
1461da177e4SLinus Torvalds  */
1471da177e4SLinus Torvalds int jffs2_add_full_dnode_to_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, struct jffs2_full_dnode *fn)
1481da177e4SLinus Torvalds {
1491da177e4SLinus Torvalds 	int ret;
1501da177e4SLinus Torvalds 	struct jffs2_node_frag *newfrag;
1511da177e4SLinus Torvalds 
1521da177e4SLinus Torvalds 	D1(printk(KERN_DEBUG "jffs2_add_full_dnode_to_inode(ino #%u, f %p, fn %p)\n", f->inocache->ino, f, fn));
1531da177e4SLinus Torvalds 
1541da177e4SLinus Torvalds 	newfrag = jffs2_alloc_node_frag();
1551da177e4SLinus Torvalds 	if (unlikely(!newfrag))
1561da177e4SLinus Torvalds 		return -ENOMEM;
1571da177e4SLinus Torvalds 
1581da177e4SLinus Torvalds 	D2(printk(KERN_DEBUG "adding node %04x-%04x @0x%08x on flash, newfrag *%p\n",
1591da177e4SLinus Torvalds 		  fn->ofs, fn->ofs+fn->size, ref_offset(fn->raw), newfrag));
1601da177e4SLinus Torvalds 
1611da177e4SLinus Torvalds 	if (unlikely(!fn->size)) {
1621da177e4SLinus Torvalds 		jffs2_free_node_frag(newfrag);
1631da177e4SLinus Torvalds 		return 0;
1641da177e4SLinus Torvalds 	}
1651da177e4SLinus Torvalds 
1661da177e4SLinus Torvalds 	newfrag->ofs = fn->ofs;
1671da177e4SLinus Torvalds 	newfrag->size = fn->size;
1681da177e4SLinus Torvalds 	newfrag->node = fn;
1691da177e4SLinus Torvalds 	newfrag->node->frags = 1;
1701da177e4SLinus Torvalds 
1711da177e4SLinus Torvalds 	ret = jffs2_add_frag_to_fragtree(c, &f->fragtree, newfrag);
1721da177e4SLinus Torvalds 	if (ret)
1731da177e4SLinus Torvalds 		return ret;
1741da177e4SLinus Torvalds 
1751da177e4SLinus Torvalds 	/* If we now share a page with other nodes, mark either previous
1761da177e4SLinus Torvalds 	   or next node REF_NORMAL, as appropriate.  */
1771da177e4SLinus Torvalds 	if (newfrag->ofs & (PAGE_CACHE_SIZE-1)) {
1781da177e4SLinus Torvalds 		struct jffs2_node_frag *prev = frag_prev(newfrag);
1791da177e4SLinus Torvalds 
1801da177e4SLinus Torvalds 		mark_ref_normal(fn->raw);
1811da177e4SLinus Torvalds 		/* If we don't start at zero there's _always_ a previous */
1821da177e4SLinus Torvalds 		if (prev->node)
1831da177e4SLinus Torvalds 			mark_ref_normal(prev->node->raw);
1841da177e4SLinus Torvalds 	}
1851da177e4SLinus Torvalds 
1861da177e4SLinus Torvalds 	if ((newfrag->ofs+newfrag->size) & (PAGE_CACHE_SIZE-1)) {
1871da177e4SLinus Torvalds 		struct jffs2_node_frag *next = frag_next(newfrag);
1881da177e4SLinus Torvalds 
1891da177e4SLinus Torvalds 		if (next) {
1901da177e4SLinus Torvalds 			mark_ref_normal(fn->raw);
1911da177e4SLinus Torvalds 			if (next->node)
1921da177e4SLinus Torvalds 				mark_ref_normal(next->node->raw);
1931da177e4SLinus Torvalds 		}
1941da177e4SLinus Torvalds 	}
1951da177e4SLinus Torvalds 	D2(if (jffs2_sanitycheck_fragtree(f)) {
1961da177e4SLinus Torvalds 		   printk(KERN_WARNING "Just added node %04x-%04x @0x%08x on flash, newfrag *%p\n",
1971da177e4SLinus Torvalds 			  fn->ofs, fn->ofs+fn->size, ref_offset(fn->raw), newfrag);
1981da177e4SLinus Torvalds 		   return 0;
1991da177e4SLinus Torvalds 	   })
2001da177e4SLinus Torvalds 	D2(jffs2_print_frag_list(f));
2011da177e4SLinus Torvalds 	return 0;
2021da177e4SLinus Torvalds }
2031da177e4SLinus Torvalds 
2041da177e4SLinus Torvalds /* Doesn't set inode->i_size */
2051da177e4SLinus Torvalds static int jffs2_add_frag_to_fragtree(struct jffs2_sb_info *c, struct rb_root *list, struct jffs2_node_frag *newfrag)
2061da177e4SLinus Torvalds {
2071da177e4SLinus Torvalds 	struct jffs2_node_frag *this;
2081da177e4SLinus Torvalds 	uint32_t lastend;
2091da177e4SLinus Torvalds 
2101da177e4SLinus Torvalds 	/* Skip all the nodes which are completed before this one starts */
2111da177e4SLinus Torvalds 	this = jffs2_lookup_node_frag(list, newfrag->node->ofs);
2121da177e4SLinus Torvalds 
2131da177e4SLinus Torvalds 	if (this) {
2141da177e4SLinus Torvalds 		D2(printk(KERN_DEBUG "j_a_f_d_t_f: Lookup gave frag 0x%04x-0x%04x; phys 0x%08x (*%p)\n",
2151da177e4SLinus Torvalds 			  this->ofs, this->ofs+this->size, this->node?(ref_offset(this->node->raw)):0xffffffff, this));
2161da177e4SLinus Torvalds 		lastend = this->ofs + this->size;
2171da177e4SLinus Torvalds 	} else {
2181da177e4SLinus Torvalds 		D2(printk(KERN_DEBUG "j_a_f_d_t_f: Lookup gave no frag\n"));
2191da177e4SLinus Torvalds 		lastend = 0;
2201da177e4SLinus Torvalds 	}
2211da177e4SLinus Torvalds 
2221da177e4SLinus Torvalds 	/* See if we ran off the end of the list */
2231da177e4SLinus Torvalds 	if (lastend <= newfrag->ofs) {
2241da177e4SLinus Torvalds 		/* We did */
2251da177e4SLinus Torvalds 
2261da177e4SLinus Torvalds 		/* Check if 'this' node was on the same page as the new node.
2271da177e4SLinus Torvalds 		   If so, both 'this' and the new node get marked REF_NORMAL so
2281da177e4SLinus Torvalds 		   the GC can take a look.
2291da177e4SLinus Torvalds 		*/
2301da177e4SLinus Torvalds 		if (lastend && (lastend-1) >> PAGE_CACHE_SHIFT == newfrag->ofs >> PAGE_CACHE_SHIFT) {
2311da177e4SLinus Torvalds 			if (this->node)
2321da177e4SLinus Torvalds 				mark_ref_normal(this->node->raw);
2331da177e4SLinus Torvalds 			mark_ref_normal(newfrag->node->raw);
2341da177e4SLinus Torvalds 		}
2351da177e4SLinus Torvalds 
2361da177e4SLinus Torvalds 		if (lastend < newfrag->node->ofs) {
2371da177e4SLinus Torvalds 			/* ... and we need to put a hole in before the new node */
2381da177e4SLinus Torvalds 			struct jffs2_node_frag *holefrag = jffs2_alloc_node_frag();
2391da177e4SLinus Torvalds 			if (!holefrag) {
2401da177e4SLinus Torvalds 				jffs2_free_node_frag(newfrag);
2411da177e4SLinus Torvalds 				return -ENOMEM;
2421da177e4SLinus Torvalds 			}
2431da177e4SLinus Torvalds 			holefrag->ofs = lastend;
2441da177e4SLinus Torvalds 			holefrag->size = newfrag->node->ofs - lastend;
2451da177e4SLinus Torvalds 			holefrag->node = NULL;
2461da177e4SLinus Torvalds 			if (this) {
2471da177e4SLinus Torvalds 				/* By definition, the 'this' node has no right-hand child,
2481da177e4SLinus Torvalds 				   because there are no frags with offset greater than it.
2491da177e4SLinus Torvalds 				   So that's where we want to put the hole */
2501da177e4SLinus Torvalds 				D2(printk(KERN_DEBUG "Adding hole frag (%p) on right of node at (%p)\n", holefrag, this));
2511da177e4SLinus Torvalds 				rb_link_node(&holefrag->rb, &this->rb, &this->rb.rb_right);
2521da177e4SLinus Torvalds 			} else {
2531da177e4SLinus Torvalds 				D2(printk(KERN_DEBUG "Adding hole frag (%p) at root of tree\n", holefrag));
2541da177e4SLinus Torvalds 				rb_link_node(&holefrag->rb, NULL, &list->rb_node);
2551da177e4SLinus Torvalds 			}
2561da177e4SLinus Torvalds 			rb_insert_color(&holefrag->rb, list);
2571da177e4SLinus Torvalds 			this = holefrag;
2581da177e4SLinus Torvalds 		}
2591da177e4SLinus Torvalds 		if (this) {
2601da177e4SLinus Torvalds 			/* By definition, the 'this' node has no right-hand child,
2611da177e4SLinus Torvalds 			   because there are no frags with offset greater than it.
2621da177e4SLinus Torvalds 			   So that's where we want to put the hole */
2631da177e4SLinus Torvalds 			D2(printk(KERN_DEBUG "Adding new frag (%p) on right of node at (%p)\n", newfrag, this));
2641da177e4SLinus Torvalds 			rb_link_node(&newfrag->rb, &this->rb, &this->rb.rb_right);
2651da177e4SLinus Torvalds 		} else {
2661da177e4SLinus Torvalds 			D2(printk(KERN_DEBUG "Adding new frag (%p) at root of tree\n", newfrag));
2671da177e4SLinus Torvalds 			rb_link_node(&newfrag->rb, NULL, &list->rb_node);
2681da177e4SLinus Torvalds 		}
2691da177e4SLinus Torvalds 		rb_insert_color(&newfrag->rb, list);
2701da177e4SLinus Torvalds 		return 0;
2711da177e4SLinus Torvalds 	}
2721da177e4SLinus Torvalds 
2731da177e4SLinus Torvalds 	D2(printk(KERN_DEBUG "j_a_f_d_t_f: dealing with frag 0x%04x-0x%04x; phys 0x%08x (*%p)\n",
2741da177e4SLinus Torvalds 		  this->ofs, this->ofs+this->size, this->node?(ref_offset(this->node->raw)):0xffffffff, this));
2751da177e4SLinus Torvalds 
2761da177e4SLinus Torvalds 	/* OK. 'this' is pointing at the first frag that newfrag->ofs at least partially obsoletes,
2771da177e4SLinus Torvalds 	 * - i.e. newfrag->ofs < this->ofs+this->size && newfrag->ofs >= this->ofs
2781da177e4SLinus Torvalds 	 */
2791da177e4SLinus Torvalds 	if (newfrag->ofs > this->ofs) {
2801da177e4SLinus Torvalds 		/* This node isn't completely obsoleted. The start of it remains valid */
2811da177e4SLinus Torvalds 
2821da177e4SLinus Torvalds 		/* Mark the new node and the partially covered node REF_NORMAL -- let
2831da177e4SLinus Torvalds 		   the GC take a look at them */
2841da177e4SLinus Torvalds 		mark_ref_normal(newfrag->node->raw);
2851da177e4SLinus Torvalds 		if (this->node)
2861da177e4SLinus Torvalds 			mark_ref_normal(this->node->raw);
2871da177e4SLinus Torvalds 
2881da177e4SLinus Torvalds 		if (this->ofs + this->size > newfrag->ofs + newfrag->size) {
2891da177e4SLinus Torvalds 			/* The new node splits 'this' frag into two */
2901da177e4SLinus Torvalds 			struct jffs2_node_frag *newfrag2 = jffs2_alloc_node_frag();
2911da177e4SLinus Torvalds 			if (!newfrag2) {
2921da177e4SLinus Torvalds 				jffs2_free_node_frag(newfrag);
2931da177e4SLinus Torvalds 				return -ENOMEM;
2941da177e4SLinus Torvalds 			}
2951da177e4SLinus Torvalds 			D2(printk(KERN_DEBUG "split old frag 0x%04x-0x%04x -->", this->ofs, this->ofs+this->size);
2961da177e4SLinus Torvalds 			if (this->node)
2971da177e4SLinus Torvalds 				printk("phys 0x%08x\n", ref_offset(this->node->raw));
2981da177e4SLinus Torvalds 			else
2991da177e4SLinus Torvalds 				printk("hole\n");
3001da177e4SLinus Torvalds 			   )
3011da177e4SLinus Torvalds 
3021da177e4SLinus Torvalds 			/* New second frag pointing to this's node */
3031da177e4SLinus Torvalds 			newfrag2->ofs = newfrag->ofs + newfrag->size;
3041da177e4SLinus Torvalds 			newfrag2->size = (this->ofs+this->size) - newfrag2->ofs;
3051da177e4SLinus Torvalds 			newfrag2->node = this->node;
3061da177e4SLinus Torvalds 			if (this->node)
3071da177e4SLinus Torvalds 				this->node->frags++;
3081da177e4SLinus Torvalds 
3091da177e4SLinus Torvalds 			/* Adjust size of original 'this' */
3101da177e4SLinus Torvalds 			this->size = newfrag->ofs - this->ofs;
3111da177e4SLinus Torvalds 
3121da177e4SLinus Torvalds 			/* Now, we know there's no node with offset
3131da177e4SLinus Torvalds 			   greater than this->ofs but smaller than
3141da177e4SLinus Torvalds 			   newfrag2->ofs or newfrag->ofs, for obvious
3151da177e4SLinus Torvalds 			   reasons. So we can do a tree insert from
3161da177e4SLinus Torvalds 			   'this' to insert newfrag, and a tree insert
3171da177e4SLinus Torvalds 			   from newfrag to insert newfrag2. */
3181da177e4SLinus Torvalds 			jffs2_fragtree_insert(newfrag, this);
3191da177e4SLinus Torvalds 			rb_insert_color(&newfrag->rb, list);
3201da177e4SLinus Torvalds 
3211da177e4SLinus Torvalds 			jffs2_fragtree_insert(newfrag2, newfrag);
3221da177e4SLinus Torvalds 			rb_insert_color(&newfrag2->rb, list);
3231da177e4SLinus Torvalds 
3241da177e4SLinus Torvalds 			return 0;
3251da177e4SLinus Torvalds 		}
3261da177e4SLinus Torvalds 		/* New node just reduces 'this' frag in size, doesn't split it */
3271da177e4SLinus Torvalds 		this->size = newfrag->ofs - this->ofs;
3281da177e4SLinus Torvalds 
3291da177e4SLinus Torvalds 		/* Again, we know it lives down here in the tree */
3301da177e4SLinus Torvalds 		jffs2_fragtree_insert(newfrag, this);
3311da177e4SLinus Torvalds 		rb_insert_color(&newfrag->rb, list);
3321da177e4SLinus Torvalds 	} else {
3331da177e4SLinus Torvalds 		/* New frag starts at the same point as 'this' used to. Replace
3341da177e4SLinus Torvalds 		   it in the tree without doing a delete and insertion */
3351da177e4SLinus Torvalds 		D2(printk(KERN_DEBUG "Inserting newfrag (*%p),%d-%d in before 'this' (*%p),%d-%d\n",
3361da177e4SLinus Torvalds 			  newfrag, newfrag->ofs, newfrag->ofs+newfrag->size,
3371da177e4SLinus Torvalds 			  this, this->ofs, this->ofs+this->size));
3381da177e4SLinus Torvalds 
3391da177e4SLinus Torvalds 		rb_replace_node(&this->rb, &newfrag->rb, list);
3401da177e4SLinus Torvalds 
3411da177e4SLinus Torvalds 		if (newfrag->ofs + newfrag->size >= this->ofs+this->size) {
3421da177e4SLinus Torvalds 			D2(printk(KERN_DEBUG "Obsoleting node frag %p (%x-%x)\n", this, this->ofs, this->ofs+this->size));
3431da177e4SLinus Torvalds 			jffs2_obsolete_node_frag(c, this);
3441da177e4SLinus Torvalds 		} else {
3451da177e4SLinus Torvalds 			this->ofs += newfrag->size;
3461da177e4SLinus Torvalds 			this->size -= newfrag->size;
3471da177e4SLinus Torvalds 
3481da177e4SLinus Torvalds 			jffs2_fragtree_insert(this, newfrag);
3491da177e4SLinus Torvalds 			rb_insert_color(&this->rb, list);
3501da177e4SLinus Torvalds 			return 0;
3511da177e4SLinus Torvalds 		}
3521da177e4SLinus Torvalds 	}
3531da177e4SLinus Torvalds 	/* OK, now we have newfrag added in the correct place in the tree, but
3541da177e4SLinus Torvalds 	   frag_next(newfrag) may be a fragment which is overlapped by it
3551da177e4SLinus Torvalds 	*/
3561da177e4SLinus Torvalds 	while ((this = frag_next(newfrag)) && newfrag->ofs + newfrag->size >= this->ofs + this->size) {
3571da177e4SLinus Torvalds 		/* 'this' frag is obsoleted completely. */
3581da177e4SLinus Torvalds 		D2(printk(KERN_DEBUG "Obsoleting node frag %p (%x-%x) and removing from tree\n", this, this->ofs, this->ofs+this->size));
3591da177e4SLinus Torvalds 		rb_erase(&this->rb, list);
3601da177e4SLinus Torvalds 		jffs2_obsolete_node_frag(c, this);
3611da177e4SLinus Torvalds 	}
3621da177e4SLinus Torvalds 	/* Now we're pointing at the first frag which isn't totally obsoleted by
3631da177e4SLinus Torvalds 	   the new frag */
3641da177e4SLinus Torvalds 
3651da177e4SLinus Torvalds 	if (!this || newfrag->ofs + newfrag->size == this->ofs) {
3661da177e4SLinus Torvalds 		return 0;
3671da177e4SLinus Torvalds 	}
3681da177e4SLinus Torvalds 	/* Still some overlap but we don't need to move it in the tree */
3691da177e4SLinus Torvalds 	this->size = (this->ofs + this->size) - (newfrag->ofs + newfrag->size);
3701da177e4SLinus Torvalds 	this->ofs = newfrag->ofs + newfrag->size;
3711da177e4SLinus Torvalds 
3721da177e4SLinus Torvalds 	/* And mark them REF_NORMAL so the GC takes a look at them */
3731da177e4SLinus Torvalds 	if (this->node)
3741da177e4SLinus Torvalds 		mark_ref_normal(this->node->raw);
3751da177e4SLinus Torvalds 	mark_ref_normal(newfrag->node->raw);
3761da177e4SLinus Torvalds 
3771da177e4SLinus Torvalds 	return 0;
3781da177e4SLinus Torvalds }
3791da177e4SLinus Torvalds 
3801da177e4SLinus Torvalds void jffs2_truncate_fraglist (struct jffs2_sb_info *c, struct rb_root *list, uint32_t size)
3811da177e4SLinus Torvalds {
3821da177e4SLinus Torvalds 	struct jffs2_node_frag *frag = jffs2_lookup_node_frag(list, size);
3831da177e4SLinus Torvalds 
3841da177e4SLinus Torvalds 	D1(printk(KERN_DEBUG "Truncating fraglist to 0x%08x bytes\n", size));
3851da177e4SLinus Torvalds 
3861da177e4SLinus Torvalds 	/* We know frag->ofs <= size. That's what lookup does for us */
3871da177e4SLinus Torvalds 	if (frag && frag->ofs != size) {
3881da177e4SLinus Torvalds 		if (frag->ofs+frag->size >= size) {
3891da177e4SLinus Torvalds 			D1(printk(KERN_DEBUG "Truncating frag 0x%08x-0x%08x\n", frag->ofs, frag->ofs+frag->size));
3901da177e4SLinus Torvalds 			frag->size = size - frag->ofs;
3911da177e4SLinus Torvalds 		}
3921da177e4SLinus Torvalds 		frag = frag_next(frag);
3931da177e4SLinus Torvalds 	}
3941da177e4SLinus Torvalds 	while (frag && frag->ofs >= size) {
3951da177e4SLinus Torvalds 		struct jffs2_node_frag *next = frag_next(frag);
3961da177e4SLinus Torvalds 
3971da177e4SLinus Torvalds 		D1(printk(KERN_DEBUG "Removing frag 0x%08x-0x%08x\n", frag->ofs, frag->ofs+frag->size));
3981da177e4SLinus Torvalds 		frag_erase(frag, list);
3991da177e4SLinus Torvalds 		jffs2_obsolete_node_frag(c, frag);
4001da177e4SLinus Torvalds 		frag = next;
4011da177e4SLinus Torvalds 	}
4021da177e4SLinus Torvalds }
4031da177e4SLinus Torvalds 
4041da177e4SLinus Torvalds /* Scan the list of all nodes present for this ino, build map of versions, etc. */
4051da177e4SLinus Torvalds 
4061da177e4SLinus Torvalds static int jffs2_do_read_inode_internal(struct jffs2_sb_info *c,
4071da177e4SLinus Torvalds 					struct jffs2_inode_info *f,
4081da177e4SLinus Torvalds 					struct jffs2_raw_inode *latest_node);
4091da177e4SLinus Torvalds 
4101da177e4SLinus Torvalds int jffs2_do_read_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
4111da177e4SLinus Torvalds 			uint32_t ino, struct jffs2_raw_inode *latest_node)
4121da177e4SLinus Torvalds {
4131da177e4SLinus Torvalds 	D2(printk(KERN_DEBUG "jffs2_do_read_inode(): getting inocache\n"));
4141da177e4SLinus Torvalds 
4151da177e4SLinus Torvalds  retry_inocache:
4161da177e4SLinus Torvalds 	spin_lock(&c->inocache_lock);
4171da177e4SLinus Torvalds 	f->inocache = jffs2_get_ino_cache(c, ino);
4181da177e4SLinus Torvalds 
4191da177e4SLinus Torvalds 	D2(printk(KERN_DEBUG "jffs2_do_read_inode(): Got inocache at %p\n", f->inocache));
4201da177e4SLinus Torvalds 
4211da177e4SLinus Torvalds 	if (f->inocache) {
4221da177e4SLinus Torvalds 		/* Check its state. We may need to wait before we can use it */
4231da177e4SLinus Torvalds 		switch(f->inocache->state) {
4241da177e4SLinus Torvalds 		case INO_STATE_UNCHECKED:
4251da177e4SLinus Torvalds 		case INO_STATE_CHECKEDABSENT:
4261da177e4SLinus Torvalds 			f->inocache->state = INO_STATE_READING;
4271da177e4SLinus Torvalds 			break;
4281da177e4SLinus Torvalds 
4291da177e4SLinus Torvalds 		case INO_STATE_CHECKING:
4301da177e4SLinus Torvalds 		case INO_STATE_GC:
4311da177e4SLinus Torvalds 			/* If it's in either of these states, we need
4321da177e4SLinus Torvalds 			   to wait for whoever's got it to finish and
4331da177e4SLinus Torvalds 			   put it back. */
4341da177e4SLinus Torvalds 			D1(printk(KERN_DEBUG "jffs2_get_ino_cache_read waiting for ino #%u in state %d\n",
4351da177e4SLinus Torvalds 				  ino, f->inocache->state));
4361da177e4SLinus Torvalds 			sleep_on_spinunlock(&c->inocache_wq, &c->inocache_lock);
4371da177e4SLinus Torvalds 			goto retry_inocache;
4381da177e4SLinus Torvalds 
4391da177e4SLinus Torvalds 		case INO_STATE_READING:
4401da177e4SLinus Torvalds 		case INO_STATE_PRESENT:
4411da177e4SLinus Torvalds 			/* Eep. This should never happen. It can
4421da177e4SLinus Torvalds 			happen if Linux calls read_inode() again
4431da177e4SLinus Torvalds 			before clear_inode() has finished though. */
4441da177e4SLinus Torvalds 			printk(KERN_WARNING "Eep. Trying to read_inode #%u when it's already in state %d!\n", ino, f->inocache->state);
4451da177e4SLinus Torvalds 			/* Fail. That's probably better than allowing it to succeed */
4461da177e4SLinus Torvalds 			f->inocache = NULL;
4471da177e4SLinus Torvalds 			break;
4481da177e4SLinus Torvalds 
4491da177e4SLinus Torvalds 		default:
4501da177e4SLinus Torvalds 			BUG();
4511da177e4SLinus Torvalds 		}
4521da177e4SLinus Torvalds 	}
4531da177e4SLinus Torvalds 	spin_unlock(&c->inocache_lock);
4541da177e4SLinus Torvalds 
4551da177e4SLinus Torvalds 	if (!f->inocache && ino == 1) {
4561da177e4SLinus Torvalds 		/* Special case - no root inode on medium */
4571da177e4SLinus Torvalds 		f->inocache = jffs2_alloc_inode_cache();
4581da177e4SLinus Torvalds 		if (!f->inocache) {
4591da177e4SLinus Torvalds 			printk(KERN_CRIT "jffs2_do_read_inode(): Cannot allocate inocache for root inode\n");
4601da177e4SLinus Torvalds 			return -ENOMEM;
4611da177e4SLinus Torvalds 		}
4621da177e4SLinus Torvalds 		D1(printk(KERN_DEBUG "jffs2_do_read_inode(): Creating inocache for root inode\n"));
4631da177e4SLinus Torvalds 		memset(f->inocache, 0, sizeof(struct jffs2_inode_cache));
4641da177e4SLinus Torvalds 		f->inocache->ino = f->inocache->nlink = 1;
4651da177e4SLinus Torvalds 		f->inocache->nodes = (struct jffs2_raw_node_ref *)f->inocache;
4661da177e4SLinus Torvalds 		f->inocache->state = INO_STATE_READING;
4671da177e4SLinus Torvalds 		jffs2_add_ino_cache(c, f->inocache);
4681da177e4SLinus Torvalds 	}
4691da177e4SLinus Torvalds 	if (!f->inocache) {
4701da177e4SLinus Torvalds 		printk(KERN_WARNING "jffs2_do_read_inode() on nonexistent ino %u\n", ino);
4711da177e4SLinus Torvalds 		return -ENOENT;
4721da177e4SLinus Torvalds 	}
4731da177e4SLinus Torvalds 
4741da177e4SLinus Torvalds 	return jffs2_do_read_inode_internal(c, f, latest_node);
4751da177e4SLinus Torvalds }
4761da177e4SLinus Torvalds 
4771da177e4SLinus Torvalds int jffs2_do_crccheck_inode(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic)
4781da177e4SLinus Torvalds {
4791da177e4SLinus Torvalds 	struct jffs2_raw_inode n;
4801da177e4SLinus Torvalds 	struct jffs2_inode_info *f = kmalloc(sizeof(*f), GFP_KERNEL);
4811da177e4SLinus Torvalds 	int ret;
4821da177e4SLinus Torvalds 
4831da177e4SLinus Torvalds 	if (!f)
4841da177e4SLinus Torvalds 		return -ENOMEM;
4851da177e4SLinus Torvalds 
4861da177e4SLinus Torvalds 	memset(f, 0, sizeof(*f));
4871da177e4SLinus Torvalds 	init_MUTEX_LOCKED(&f->sem);
4881da177e4SLinus Torvalds 	f->inocache = ic;
4891da177e4SLinus Torvalds 
4901da177e4SLinus Torvalds 	ret = jffs2_do_read_inode_internal(c, f, &n);
4911da177e4SLinus Torvalds 	if (!ret) {
4921da177e4SLinus Torvalds 		up(&f->sem);
4931da177e4SLinus Torvalds 		jffs2_do_clear_inode(c, f);
4941da177e4SLinus Torvalds 	}
4951da177e4SLinus Torvalds 	kfree (f);
4961da177e4SLinus Torvalds 	return ret;
4971da177e4SLinus Torvalds }
4981da177e4SLinus Torvalds 
4991da177e4SLinus Torvalds static int jffs2_do_read_inode_internal(struct jffs2_sb_info *c,
5001da177e4SLinus Torvalds 					struct jffs2_inode_info *f,
5011da177e4SLinus Torvalds 					struct jffs2_raw_inode *latest_node)
5021da177e4SLinus Torvalds {
5031da177e4SLinus Torvalds 	struct jffs2_tmp_dnode_info *tn_list, *tn;
5041da177e4SLinus Torvalds 	struct jffs2_full_dirent *fd_list;
5051da177e4SLinus Torvalds 	struct jffs2_full_dnode *fn = NULL;
5061da177e4SLinus Torvalds 	uint32_t crc;
5071da177e4SLinus Torvalds 	uint32_t latest_mctime, mctime_ver;
5081da177e4SLinus Torvalds 	uint32_t mdata_ver = 0;
5091da177e4SLinus Torvalds 	size_t retlen;
5101da177e4SLinus Torvalds 	int ret;
5111da177e4SLinus Torvalds 
5121da177e4SLinus Torvalds 	D1(printk(KERN_DEBUG "jffs2_do_read_inode_internal(): ino #%u nlink is %d\n", f->inocache->ino, f->inocache->nlink));
5131da177e4SLinus Torvalds 
5141da177e4SLinus Torvalds 	/* Grab all nodes relevant to this ino */
5151da177e4SLinus Torvalds 	ret = jffs2_get_inode_nodes(c, f, &tn_list, &fd_list, &f->highest_version, &latest_mctime, &mctime_ver);
5161da177e4SLinus Torvalds 
5171da177e4SLinus Torvalds 	if (ret) {
5181da177e4SLinus Torvalds 		printk(KERN_CRIT "jffs2_get_inode_nodes() for ino %u returned %d\n", f->inocache->ino, ret);
5191da177e4SLinus Torvalds 		if (f->inocache->state == INO_STATE_READING)
5201da177e4SLinus Torvalds 			jffs2_set_inocache_state(c, f->inocache, INO_STATE_CHECKEDABSENT);
5211da177e4SLinus Torvalds 		return ret;
5221da177e4SLinus Torvalds 	}
5231da177e4SLinus Torvalds 	f->dents = fd_list;
5241da177e4SLinus Torvalds 
5251da177e4SLinus Torvalds 	while (tn_list) {
5261da177e4SLinus Torvalds 		tn = tn_list;
5271da177e4SLinus Torvalds 
5281da177e4SLinus Torvalds 		fn = tn->fn;
5291da177e4SLinus Torvalds 
5301da177e4SLinus Torvalds 		if (f->metadata) {
5311da177e4SLinus Torvalds 			if (likely(tn->version >= mdata_ver)) {
5321da177e4SLinus Torvalds 				D1(printk(KERN_DEBUG "Obsoleting old metadata at 0x%08x\n", ref_offset(f->metadata->raw)));
5331da177e4SLinus Torvalds 				jffs2_mark_node_obsolete(c, f->metadata->raw);
5341da177e4SLinus Torvalds 				jffs2_free_full_dnode(f->metadata);
5351da177e4SLinus Torvalds 				f->metadata = NULL;
5361da177e4SLinus Torvalds 
5371da177e4SLinus Torvalds 				mdata_ver = 0;
5381da177e4SLinus Torvalds 			} else {
5391da177e4SLinus Torvalds 				/* This should never happen. */
5401da177e4SLinus Torvalds 				printk(KERN_WARNING "Er. New metadata at 0x%08x with ver %d is actually older than previous ver %d at 0x%08x\n",
5411da177e4SLinus Torvalds 					  ref_offset(fn->raw), tn->version, mdata_ver, ref_offset(f->metadata->raw));
5421da177e4SLinus Torvalds 				jffs2_mark_node_obsolete(c, fn->raw);
5431da177e4SLinus Torvalds 				jffs2_free_full_dnode(fn);
5441da177e4SLinus Torvalds 				/* Fill in latest_node from the metadata, not this one we're about to free... */
5451da177e4SLinus Torvalds 				fn = f->metadata;
5461da177e4SLinus Torvalds 				goto next_tn;
5471da177e4SLinus Torvalds 			}
5481da177e4SLinus Torvalds 		}
5491da177e4SLinus Torvalds 
5501da177e4SLinus Torvalds 		if (fn->size) {
5511da177e4SLinus Torvalds 			jffs2_add_full_dnode_to_inode(c, f, fn);
5521da177e4SLinus Torvalds 		} else {
5531da177e4SLinus Torvalds 			/* Zero-sized node at end of version list. Just a metadata update */
5541da177e4SLinus Torvalds 			D1(printk(KERN_DEBUG "metadata @%08x: ver %d\n", ref_offset(fn->raw), tn->version));
5551da177e4SLinus Torvalds 			f->metadata = fn;
5561da177e4SLinus Torvalds 			mdata_ver = tn->version;
5571da177e4SLinus Torvalds 		}
5581da177e4SLinus Torvalds 	next_tn:
5591da177e4SLinus Torvalds 		tn_list = tn->next;
5601da177e4SLinus Torvalds 		jffs2_free_tmp_dnode_info(tn);
5611da177e4SLinus Torvalds 	}
5621da177e4SLinus Torvalds 	D1(jffs2_sanitycheck_fragtree(f));
5631da177e4SLinus Torvalds 
5641da177e4SLinus Torvalds 	if (!fn) {
5651da177e4SLinus Torvalds 		/* No data nodes for this inode. */
5661da177e4SLinus Torvalds 		if (f->inocache->ino != 1) {
5671da177e4SLinus Torvalds 			printk(KERN_WARNING "jffs2_do_read_inode(): No data nodes found for ino #%u\n", f->inocache->ino);
5681da177e4SLinus Torvalds 			if (!fd_list) {
5691da177e4SLinus Torvalds 				if (f->inocache->state == INO_STATE_READING)
5701da177e4SLinus Torvalds 					jffs2_set_inocache_state(c, f->inocache, INO_STATE_CHECKEDABSENT);
5711da177e4SLinus Torvalds 				return -EIO;
5721da177e4SLinus Torvalds 			}
5731da177e4SLinus Torvalds 			printk(KERN_WARNING "jffs2_do_read_inode(): But it has children so we fake some modes for it\n");
5741da177e4SLinus Torvalds 		}
5751da177e4SLinus Torvalds 		latest_node->mode = cpu_to_jemode(S_IFDIR|S_IRUGO|S_IWUSR|S_IXUGO);
5761da177e4SLinus Torvalds 		latest_node->version = cpu_to_je32(0);
5771da177e4SLinus Torvalds 		latest_node->atime = latest_node->ctime = latest_node->mtime = cpu_to_je32(0);
5781da177e4SLinus Torvalds 		latest_node->isize = cpu_to_je32(0);
5791da177e4SLinus Torvalds 		latest_node->gid = cpu_to_je16(0);
5801da177e4SLinus Torvalds 		latest_node->uid = cpu_to_je16(0);
5811da177e4SLinus Torvalds 		if (f->inocache->state == INO_STATE_READING)
5821da177e4SLinus Torvalds 			jffs2_set_inocache_state(c, f->inocache, INO_STATE_PRESENT);
5831da177e4SLinus Torvalds 		return 0;
5841da177e4SLinus Torvalds 	}
5851da177e4SLinus Torvalds 
5861da177e4SLinus Torvalds 	ret = jffs2_flash_read(c, ref_offset(fn->raw), sizeof(*latest_node), &retlen, (void *)latest_node);
5871da177e4SLinus Torvalds 	if (ret || retlen != sizeof(*latest_node)) {
5881da177e4SLinus Torvalds 		printk(KERN_NOTICE "MTD read in jffs2_do_read_inode() failed: Returned %d, %zd of %zd bytes read\n",
5891da177e4SLinus Torvalds 		       ret, retlen, sizeof(*latest_node));
5901da177e4SLinus Torvalds 		/* FIXME: If this fails, there seems to be a memory leak. Find it. */
5911da177e4SLinus Torvalds 		up(&f->sem);
5921da177e4SLinus Torvalds 		jffs2_do_clear_inode(c, f);
5931da177e4SLinus Torvalds 		return ret?ret:-EIO;
5941da177e4SLinus Torvalds 	}
5951da177e4SLinus Torvalds 
5961da177e4SLinus Torvalds 	crc = crc32(0, latest_node, sizeof(*latest_node)-8);
5971da177e4SLinus Torvalds 	if (crc != je32_to_cpu(latest_node->node_crc)) {
5981da177e4SLinus 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));
5991da177e4SLinus Torvalds 		up(&f->sem);
6001da177e4SLinus Torvalds 		jffs2_do_clear_inode(c, f);
6011da177e4SLinus Torvalds 		return -EIO;
6021da177e4SLinus Torvalds 	}
6031da177e4SLinus Torvalds 
6041da177e4SLinus Torvalds 	switch(jemode_to_cpu(latest_node->mode) & S_IFMT) {
6051da177e4SLinus Torvalds 	case S_IFDIR:
6061da177e4SLinus Torvalds 		if (mctime_ver > je32_to_cpu(latest_node->version)) {
6071da177e4SLinus Torvalds 			/* The times in the latest_node are actually older than
6081da177e4SLinus Torvalds 			   mctime in the latest dirent. Cheat. */
6091da177e4SLinus Torvalds 			latest_node->ctime = latest_node->mtime = cpu_to_je32(latest_mctime);
6101da177e4SLinus Torvalds 		}
6111da177e4SLinus Torvalds 		break;
6121da177e4SLinus Torvalds 
6131da177e4SLinus Torvalds 
6141da177e4SLinus Torvalds 	case S_IFREG:
6151da177e4SLinus Torvalds 		/* If it was a regular file, truncate it to the latest node's isize */
6161da177e4SLinus Torvalds 		jffs2_truncate_fraglist(c, &f->fragtree, je32_to_cpu(latest_node->isize));
6171da177e4SLinus Torvalds 		break;
6181da177e4SLinus Torvalds 
6191da177e4SLinus Torvalds 	case S_IFLNK:
6201da177e4SLinus Torvalds 		/* Hack to work around broken isize in old symlink code.
6211da177e4SLinus Torvalds 		   Remove this when dwmw2 comes to his senses and stops
6221da177e4SLinus Torvalds 		   symlinks from being an entirely gratuitous special
6231da177e4SLinus Torvalds 		   case. */
6241da177e4SLinus Torvalds 		if (!je32_to_cpu(latest_node->isize))
6251da177e4SLinus Torvalds 			latest_node->isize = latest_node->dsize;
6261da177e4SLinus Torvalds 		/* fall through... */
6271da177e4SLinus Torvalds 
6281da177e4SLinus Torvalds 	case S_IFBLK:
6291da177e4SLinus Torvalds 	case S_IFCHR:
6301da177e4SLinus Torvalds 		/* Certain inode types should have only one data node, and it's
6311da177e4SLinus Torvalds 		   kept as the metadata node */
6321da177e4SLinus Torvalds 		if (f->metadata) {
6331da177e4SLinus Torvalds 			printk(KERN_WARNING "Argh. Special inode #%u with mode 0%o had metadata node\n",
6341da177e4SLinus Torvalds 			       f->inocache->ino, jemode_to_cpu(latest_node->mode));
6351da177e4SLinus Torvalds 			up(&f->sem);
6361da177e4SLinus Torvalds 			jffs2_do_clear_inode(c, f);
6371da177e4SLinus Torvalds 			return -EIO;
6381da177e4SLinus Torvalds 		}
6391da177e4SLinus Torvalds 		if (!frag_first(&f->fragtree)) {
6401da177e4SLinus Torvalds 			printk(KERN_WARNING "Argh. Special inode #%u with mode 0%o has no fragments\n",
6411da177e4SLinus Torvalds 			       f->inocache->ino, jemode_to_cpu(latest_node->mode));
6421da177e4SLinus Torvalds 			up(&f->sem);
6431da177e4SLinus Torvalds 			jffs2_do_clear_inode(c, f);
6441da177e4SLinus Torvalds 			return -EIO;
6451da177e4SLinus Torvalds 		}
6461da177e4SLinus Torvalds 		/* ASSERT: f->fraglist != NULL */
6471da177e4SLinus Torvalds 		if (frag_next(frag_first(&f->fragtree))) {
6481da177e4SLinus Torvalds 			printk(KERN_WARNING "Argh. Special inode #%u with mode 0x%x had more than one node\n",
6491da177e4SLinus Torvalds 			       f->inocache->ino, jemode_to_cpu(latest_node->mode));
6501da177e4SLinus Torvalds 			/* FIXME: Deal with it - check crc32, check for duplicate node, check times and discard the older one */
6511da177e4SLinus Torvalds 			up(&f->sem);
6521da177e4SLinus Torvalds 			jffs2_do_clear_inode(c, f);
6531da177e4SLinus Torvalds 			return -EIO;
6541da177e4SLinus Torvalds 		}
6551da177e4SLinus Torvalds 		/* OK. We're happy */
6561da177e4SLinus Torvalds 		f->metadata = frag_first(&f->fragtree)->node;
6571da177e4SLinus Torvalds 		jffs2_free_node_frag(frag_first(&f->fragtree));
6581da177e4SLinus Torvalds 		f->fragtree = RB_ROOT;
6591da177e4SLinus Torvalds 		break;
6601da177e4SLinus Torvalds 	}
6611da177e4SLinus Torvalds 	if (f->inocache->state == INO_STATE_READING)
6621da177e4SLinus Torvalds 		jffs2_set_inocache_state(c, f->inocache, INO_STATE_PRESENT);
6631da177e4SLinus Torvalds 
6641da177e4SLinus Torvalds 	return 0;
6651da177e4SLinus Torvalds }
6661da177e4SLinus Torvalds 
6671da177e4SLinus Torvalds void jffs2_do_clear_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f)
6681da177e4SLinus Torvalds {
6691da177e4SLinus Torvalds 	struct jffs2_full_dirent *fd, *fds;
6701da177e4SLinus Torvalds 	int deleted;
6711da177e4SLinus Torvalds 
6721da177e4SLinus Torvalds 	down(&f->sem);
6731da177e4SLinus Torvalds 	deleted = f->inocache && !f->inocache->nlink;
6741da177e4SLinus Torvalds 
67567e345d1SDavid Woodhouse 	if (f->inocache && f->inocache->state != INO_STATE_CHECKING)
67667e345d1SDavid Woodhouse 		jffs2_set_inocache_state(c, f->inocache, INO_STATE_CLEARING);
67767e345d1SDavid Woodhouse 
6781da177e4SLinus Torvalds 	if (f->metadata) {
6791da177e4SLinus Torvalds 		if (deleted)
6801da177e4SLinus Torvalds 			jffs2_mark_node_obsolete(c, f->metadata->raw);
6811da177e4SLinus Torvalds 		jffs2_free_full_dnode(f->metadata);
6821da177e4SLinus Torvalds 	}
6831da177e4SLinus Torvalds 
6841da177e4SLinus Torvalds 	jffs2_kill_fragtree(&f->fragtree, deleted?c:NULL);
6851da177e4SLinus Torvalds 
6861da177e4SLinus Torvalds 	fds = f->dents;
6871da177e4SLinus Torvalds 
6881da177e4SLinus Torvalds 	while(fds) {
6891da177e4SLinus Torvalds 		fd = fds;
6901da177e4SLinus Torvalds 		fds = fd->next;
6911da177e4SLinus Torvalds 		jffs2_free_full_dirent(fd);
6921da177e4SLinus Torvalds 	}
6931da177e4SLinus Torvalds 
69467e345d1SDavid Woodhouse 	if (f->inocache && f->inocache->state != INO_STATE_CHECKING) {
6951da177e4SLinus Torvalds 		jffs2_set_inocache_state(c, f->inocache, INO_STATE_CHECKEDABSENT);
69667e345d1SDavid Woodhouse 		if (f->inocache->nodes == (void *)f->inocache)
69767e345d1SDavid Woodhouse 			jffs2_del_ino_cache(c, f->inocache);
69867e345d1SDavid Woodhouse 	}
6991da177e4SLinus Torvalds 
7001da177e4SLinus Torvalds 	up(&f->sem);
7011da177e4SLinus Torvalds }
702