This is the mail archive of the ecos-patches@sources.redhat.com mailing list for the eCos project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Re: Pooled memory allocation for JFFS2


On Fri, 2003-11-21 at 15:10 -0700, Gary Thomas wrote:
> I think it would be good to put this into one of the common .h files,
> perhaps <linux/jffs2.h>

... which as observed then actually need to be _included_ in compr.c
otherwise you get no compression at all. And once you fix that you have
to fix up the fact that we were passing the wrong pointer to all the
compressor functions...

Here's a patch to bring CVS up to date with my current tree. (Note
src/file-ecos.c and src/jffs2port.h can be removed.)

Note also the evilness with cyg_cdir_dir in jffs2_umount(). Other than
that I'm vaguely happy with the eCos port now, although the TODO list
isn't empty:

 - Fill in the skeleton gcthread.c
 - Check and fix locking of icache mangling in fs-ecos.c
 - Fix unmount of root file system after chdir().
 - Make a read-only CDL option. (Will function-sections and gc-sections 
   make this fairly ifdef-free?)
 - Fix atomicity of renames. Why was the unlink() added?
 - Further cleanup -- should the functions in dir-ecos.c take 'struct
   dirsearch' instead of various components thereof, or should each of
   those functions just be moved inside its only caller in fs-ecos.c?
 - Fix up RedBoot fileio support.

There's definitely still scope for further cleanups but at least it's
not actively making me unhappy now :)

-- 
dwmw2

Index: ChangeLog
===================================================================
RCS file: /cvs/ecos/ecos/packages/fs/jffs2/current/ChangeLog,v
retrieving revision 1.21
diff -u -p -r1.21 ChangeLog
--- ChangeLog	25 Nov 2003 16:41:08 -0000	1.21
+++ ChangeLog	26 Nov 2003 22:24:12 -0000
@@ -1,3 +1,18 @@
+2003-11-26  David Woodhouse  <dwmw2@redhat.com>
+
+	JFFS2 cleanup and import of newer code. Remove last vestiges of
+	Linuxisms such as 'struct inode' from the core code, leaving eCos
+	with no excuse, and renaming the eCos 'struct inode' to make that
+	point. Fix i_count handling throughout. Clean up remaining Linuxisms
+	such as 'struct qstr' to the point where jffs2port.h can be removed.
+	Add skeleton for background garbage-collect thread. Fix compression
+	so that it's actually called, and even with the right pointers. Turn
+	on -Werror again. Zero tolerance is a good thing. Make the i_mode
+	conversion functions non-inline to avoid warnings. Fix namespace
+	pollution (of all but ^jffs2_* at least). Move physical flash I/O
+	functions into separate file flashio.c for relatively easy
+	substitution. Various other cruftectomy.
+
 2003-11-25  Andrew Lunn  <andrew.lunn@ascom.ch>
 
 	* src/fs-ecos.c: ARM gcc 3.2.3 is also broken. Complain with any
Index: cdl/jffs2.cdl
===================================================================
RCS file: /cvs/ecos/ecos/packages/fs/jffs2/current/cdl/jffs2.cdl,v
retrieving revision 1.12
diff -u -p -r1.12 jffs2.cdl
--- cdl/jffs2.cdl	21 Nov 2003 19:05:24 -0000	1.12
+++ cdl/jffs2.cdl	26 Nov 2003 22:24:12 -0000
@@ -4,7 +4,7 @@
 #
 #      JFFS2 Filesystem configuration data
 #
-#      $Id: jffs2.cdl,v 1.6 2003/11/20 16:41:57 dwmw2 Exp $
+#      $Id: jffs2.cdl,v 1.10 2003/11/26 15:55:34 dwmw2 Exp $
 #
 # ====================================================================
 #####ECOSGPLCOPYRIGHTBEGIN####
@@ -72,7 +72,19 @@ cdl_package CYGPKG_FS_JFFS2 {
     implements     CYGINT_IO_FILEIO_FS      
 
     compile        -library=libextras.a fs-ecos.c
-    compile        build.c scan.c malloc-ecos.c nodelist.c nodemgmt.c readinode.c erase.c dir-ecos.c write.c gc.c read.c compr.c file-ecos.c
+    compile        build.c scan.c malloc-ecos.c nodelist.c nodemgmt.c readinode.c erase.c dir-ecos.c write.c gc.c read.c compr.c
+    # This could be overridden by an alternative direct I/O method.
+    compile        flashio.c
+
+    cdl_option CYGOPT_FS_JFFS2_GCTHREAD {
+	display         "Support garbage-collection background thread"
+	flavor          bool
+	compile         gcthread.c
+#       requires        0
+        description     "
+            Enable background garbage collection thread, for making 
+	    free space ahead of time."
+    }
 
     cdl_option CYGOPT_FS_JFFS2_NAND {
 	display         "Support for NAND flash"
@@ -168,7 +180,7 @@ cdl_package CYGPKG_FS_JFFS2 {
 	no_define
 	# We add -D__ECOS to trigger eCos-specific code in places.
 	# We add -Werror because I find it useful.
-	default_value { "-D__ECOS -nostdinc -iwithprefix include" }
+	default_value { "-D__ECOS -nostdinc -iwithprefix include -Werror" }
 	description   "
 	    This option modifies the set of compiler flags for
             building the JFFS2 package.
Index: src/compr.c
===================================================================
RCS file: /cvs/ecos/ecos/packages/fs/jffs2/current/src/compr.c,v
retrieving revision 1.4
diff -u -p -r1.4 compr.c
--- src/compr.c	20 Nov 2003 16:52:36 -0000	1.4
+++ src/compr.c	26 Nov 2003 22:24:13 -0000
@@ -7,7 +7,7 @@
  *
  * For licensing information, see the file 'LICENCE' in this directory.
  *
- * $Id: compr.c,v 1.29 2003/11/20 16:40:35 dwmw2 Exp $
+ * $Id: compr.c,v 1.31 2003/11/26 13:01:12 dwmw2 Exp $
  *
  */
 
@@ -17,6 +17,7 @@
 #include <linux/types.h>
 #include <linux/slab.h>
 #include <linux/jffs2.h>
+#include "nodelist.h"
 
 int jffs2_zlib_compress(unsigned char *data_in, unsigned char *cpage_out, uint32_t *sourcelen, uint32_t *dstlen);
 void jffs2_zlib_decompress(unsigned char *data_in, unsigned char *cpage_out, uint32_t srclen, uint32_t destlen);
@@ -58,26 +59,26 @@ unsigned char jffs2_compress(unsigned ch
 	}
 
 #ifdef JFFS2_USE_ZLIB
-	ret = jffs2_zlib_compress(data_in, cpage_out, datalen, cdatalen);
+	ret = jffs2_zlib_compress(data_in, *cpage_out, datalen, cdatalen);
 	if (!ret) {
 		return JFFS2_COMPR_ZLIB;
 	}
 #endif
 #ifdef JFFS2_USE_DYNRUBIN
-	ret = jffs2_dynrubin_compress(data_in, cpage_out, datalen, cdatalen);
+	ret = jffs2_dynrubin_compress(data_in, *cpage_out, datalen, cdatalen);
 	if (!ret) {
 		return JFFS2_COMPR_DYNRUBIN;
 	}
 #endif
 #ifdef JFFS2_USE_RUBINMIPS
-	ret = jffs2_rubinmips_compress(data_in, cpage_out, datalen, cdatalen);
+	ret = jffs2_rubinmips_compress(data_in, *cpage_out, datalen, cdatalen);
 	if (!ret) {
 		return JFFS2_COMPR_RUBINMIPS;
 	}
 #endif
 #ifdef JFFS2_USE_RTIME
 	/* rtime does manage to recompress already-compressed data */
-	ret = jffs2_rtime_compress(data_in, cpage_out, datalen, cdatalen);
+	ret = jffs2_rtime_compress(data_in, *cpage_out, datalen, cdatalen);
 	if (!ret) {
 		return JFFS2_COMPR_RTIME;
 	}
Index: src/dir-ecos.c
===================================================================
RCS file: /cvs/ecos/ecos/packages/fs/jffs2/current/src/dir-ecos.c,v
retrieving revision 1.4
diff -u -p -r1.4 dir-ecos.c
--- src/dir-ecos.c	20 Nov 2003 16:52:36 -0000	1.4
+++ src/dir-ecos.c	26 Nov 2003 22:24:13 -0000
@@ -1,13 +1,13 @@
 /*
  * JFFS2 -- Journalling Flash File System, Version 2.
  *
- * Copyright (C) 2001, 2002 Red Hat, Inc.
+ * Copyright (C) 2001-2003 Red Hat, Inc.
  *
  * Created by David Woodhouse <dwmw2@cambridge.redhat.com>
  *
  * For licensing information, see the file 'LICENCE' in this directory.
  *
- * $Id: dir-ecos.c,v 1.4 2003/11/20 16:41:58 dwmw2 Exp $
+ * $Id: dir-ecos.c,v 1.10 2003/11/26 15:55:35 dwmw2 Exp $
  *
  */
 
@@ -17,18 +17,15 @@
 
 /***********************************************************************/
 
-
-/* We keep the dirent list sorted in increasing order of name hash,
-   and we use the same hash function as the dentries. Makes this 
-   nice and simple
-*/
-struct inode *jffs2_lookup(struct inode *dir_i, struct qstr *d_name)
+/* Takes length argument because it can be either NUL-terminated or '/'-terminated */
+struct _inode *jffs2_lookup(struct _inode *dir_i, const unsigned char *d_name, int namelen)
 {
 	struct jffs2_inode_info *dir_f;
 	struct jffs2_sb_info *c;
 	struct jffs2_full_dirent *fd = NULL, *fd_list;
 	uint32_t ino = 0;
-	struct inode *inode = NULL;
+	uint32_t hash = full_name_hash(d_name, namelen);
+	struct _inode *inode = NULL;
 
 	D1(printk("jffs2_lookup()\n"));
 
@@ -38,11 +35,11 @@ struct inode *jffs2_lookup(struct inode 
 	down(&dir_f->sem);
 
 	/* NB: The 2.2 backport will need to explicitly check for '.' and '..' here */
-	for (fd_list = dir_f->dents; fd_list && fd_list->nhash <= d_name->hash; fd_list = fd_list->next) {
-		if (fd_list->nhash == d_name->hash && 
+	for (fd_list = dir_f->dents; fd_list && fd_list->nhash <= hash; fd_list = fd_list->next) {
+		if (fd_list->nhash == hash && 
 		    (!fd || fd_list->version > fd->version) &&
-		    strlen(fd_list->name) == d_name->len &&
-		    !strncmp(fd_list->name, d_name->name, d_name->len)) {
+		    strlen(fd_list->name) == namelen &&
+		    !strncmp(fd_list->name, d_name, namelen)) {
 			fd = fd_list;
 		}
 	}
@@ -50,9 +47,9 @@ struct inode *jffs2_lookup(struct inode 
 		ino = fd->ino;
 	up(&dir_f->sem);
 	if (ino) {
-		inode = iget(dir_i->i_sb, ino);
+		inode = jffs2_iget(dir_i->i_sb, ino);
 		if (!inode) {
-			printk("iget() failed for ino #%u\n", ino);
+			printk("jffs2_iget() failed for ino #%u\n", ino);
 			return (ERR_PTR(-EIO));
 		}
 	}
@@ -64,13 +61,13 @@ struct inode *jffs2_lookup(struct inode 
 
 
 
-int jffs2_create(struct inode *dir_i, struct qstr *d_name, int mode,
-                 struct inode **new_i)
+int jffs2_create(struct _inode *dir_i, const unsigned char *d_name, int mode,
+                 struct _inode **new_i)
 {
 	struct jffs2_raw_inode *ri;
 	struct jffs2_inode_info *f, *dir_f;
 	struct jffs2_sb_info *c;
-	struct inode *inode;
+	struct _inode *inode;
 	int ret;
 
 	ri = jffs2_alloc_raw_inode();
@@ -93,12 +90,11 @@ int jffs2_create(struct inode *dir_i, st
 	dir_f = JFFS2_INODE_INFO(dir_i);
 
 	ret = jffs2_do_create(c, dir_f, f, ri, 
-			      d_name->name, d_name->len);
+			      d_name, strlen(d_name));
 
 	if (ret) {
-		jffs2_clear_inode(inode);
-		make_bad_inode(inode);
-		iput(inode);
+		inode->i_nlink = 0;
+		jffs2_iput(inode);
 		jffs2_free_raw_inode(ri);
 		return ret;
 	}
@@ -114,15 +110,15 @@ int jffs2_create(struct inode *dir_i, st
 /***********************************************************************/
 
 
-int jffs2_unlink(struct inode *dir_i, struct inode *d_inode, struct qstr *d_name)
+int jffs2_unlink(struct _inode *dir_i, struct _inode *d_inode, const unsigned char *d_name)
 {
 	struct jffs2_sb_info *c = JFFS2_SB_INFO(dir_i->i_sb);
 	struct jffs2_inode_info *dir_f = JFFS2_INODE_INFO(dir_i);
 	struct jffs2_inode_info *dead_f = JFFS2_INODE_INFO(d_inode);
 	int ret;
 
-	ret = jffs2_do_unlink(c, dir_f, d_name->name, 
-			       d_name->len, dead_f);
+	ret = jffs2_do_unlink(c, dir_f, d_name, 
+			       strlen(d_name), dead_f);
 	if (dead_f->inocache)
 		d_inode->i_nlink = dead_f->inocache->nlink;
 	return ret;
@@ -130,7 +126,7 @@ int jffs2_unlink(struct inode *dir_i, st
 /***********************************************************************/
 
 
-int jffs2_link (struct inode *old_d_inode, struct inode *dir_i, struct qstr *d_name)
+int jffs2_link (struct _inode *old_d_inode, struct _inode *dir_i, const unsigned char *d_name)
 {
 	struct jffs2_sb_info *c = JFFS2_SB_INFO(old_d_inode->i_sb);
 	struct jffs2_inode_info *f = JFFS2_INODE_INFO(old_d_inode);
@@ -141,7 +137,7 @@ int jffs2_link (struct inode *old_d_inod
 	uint8_t type = (old_d_inode->i_mode & S_IFMT) >> 12;
 	if (!type) type = DT_REG;
 
-	ret = jffs2_do_link(c, dir_f, f->inocache->ino, type, d_name->name, d_name->len);
+	ret = jffs2_do_link(c, dir_f, f->inocache->ino, type, d_name, strlen(d_name));
 
 	if (!ret) {
 		down(&f->sem);
@@ -151,11 +147,11 @@ int jffs2_link (struct inode *old_d_inod
 	return ret;
 }
 
-int jffs2_mkdir (struct inode *dir_i, struct qstr *d_name, int mode, struct inode **new_i)
+int jffs2_mkdir (struct _inode *dir_i, const unsigned char *d_name, int mode)
 {
 	struct jffs2_inode_info *f, *dir_f;
 	struct jffs2_sb_info *c;
-	struct inode *inode;
+	struct _inode *inode;
 	struct jffs2_raw_inode *ri;
 	struct jffs2_raw_dirent *rd;
 	struct jffs2_full_dnode *fn;
@@ -175,7 +171,7 @@ int jffs2_mkdir (struct inode *dir_i, st
 	/* Try to reserve enough space for both node and dirent. 
 	 * Just the node will do for now, though 
 	 */
-	namelen = d_name->len;
+	namelen = strlen(d_name);
 	ret = jffs2_reserve_space(c, sizeof(*ri), &phys_ofs, &alloclen, ALLOC_NORMAL);
 
 	if (ret) {
@@ -204,7 +200,8 @@ int jffs2_mkdir (struct inode *dir_i, st
 		/* Eeek. Wave bye bye */
 		up(&f->sem);
 		jffs2_complete_reservation(c);
-		jffs2_clear_inode(inode);
+		inode->i_nlink = 0;
+		jffs2_iput(inode);
 		return PTR_ERR(fn);
 	}
 	/* No data here. Only a metadata node, which will be 
@@ -217,7 +214,8 @@ int jffs2_mkdir (struct inode *dir_i, st
 	ret = jffs2_reserve_space(c, sizeof(*rd)+namelen, &phys_ofs, &alloclen, ALLOC_NORMAL);
 	if (ret) {
 		/* Eep. */
-		jffs2_clear_inode(inode);
+		inode->i_nlink = 0;
+		jffs2_iput(inode);
 		return ret;
 	}
 	
@@ -225,7 +223,8 @@ int jffs2_mkdir (struct inode *dir_i, st
 	if (!rd) {
 		/* Argh. Now we treat it like a normal delete */
 		jffs2_complete_reservation(c);
-		jffs2_clear_inode(inode);
+		inode->i_nlink = 0;
+		jffs2_iput(inode);
 		return -ENOMEM;
 	}
 
@@ -244,9 +243,9 @@ int jffs2_mkdir (struct inode *dir_i, st
 	rd->nsize = namelen;
 	rd->type = DT_DIR;
 	rd->node_crc = cpu_to_je32(crc32(0, rd, sizeof(*rd)-8));
-	rd->name_crc = cpu_to_je32(crc32(0, d_name->name, namelen));
+	rd->name_crc = cpu_to_je32(crc32(0, d_name, namelen));
 
-	fd = jffs2_write_dirent(c, dir_f, rd, d_name->name, namelen, phys_ofs, ALLOC_NORMAL);
+	fd = jffs2_write_dirent(c, dir_f, rd, d_name, namelen, phys_ofs, ALLOC_NORMAL);
 	
 	jffs2_complete_reservation(c);
 	jffs2_free_raw_dirent(rd);
@@ -255,7 +254,8 @@ int jffs2_mkdir (struct inode *dir_i, st
 		/* dirent failed to write. Delete the inode normally 
 		   as if it were the final unlink() */
 		up(&dir_f->sem);
-		jffs2_clear_inode(inode);
+		inode->i_nlink = 0;
+		jffs2_iput(inode);
 		return PTR_ERR(fd);
 	}
 
@@ -264,11 +264,11 @@ int jffs2_mkdir (struct inode *dir_i, st
 	jffs2_add_fd_to_list(c, fd, &dir_f->dents);
 	up(&dir_f->sem);
 
-	*new_i = inode;
+	jffs2_iput(inode);
 	return 0;
 }
 
-int jffs2_rmdir (struct inode *dir_i, struct inode *d_inode, struct qstr *d_name)
+int jffs2_rmdir (struct _inode *dir_i, struct _inode *d_inode, const unsigned char *d_name)
 {
 	struct jffs2_inode_info *f = JFFS2_INODE_INFO(d_inode);
 	struct jffs2_full_dirent *fd;
@@ -280,8 +280,8 @@ int jffs2_rmdir (struct inode *dir_i, st
 	return jffs2_unlink(dir_i, d_inode, d_name);
 }
 
-int jffs2_rename (struct inode *old_dir_i, struct inode *d_inode, struct qstr *old_d_name,
-                        struct inode *new_dir_i, struct qstr *new_d_name)
+int jffs2_rename (struct _inode *old_dir_i, struct _inode *d_inode, const unsigned char *old_d_name,
+		  struct _inode *new_dir_i, const unsigned char *new_d_name)
 {
 	int ret;
 	struct jffs2_sb_info *c = JFFS2_SB_INFO(old_dir_i->i_sb);
@@ -331,7 +331,7 @@ int jffs2_rename (struct inode *old_dir_
 
 	ret = jffs2_do_link(c, JFFS2_INODE_INFO(new_dir_i), 
 			    d_inode->i_ino, type,
-			    new_d_name->name, new_d_name->len);
+			    new_d_name, strlen(new_d_name));
 
 	if (ret)
 		return ret;
@@ -349,7 +349,7 @@ int jffs2_rename (struct inode *old_dir_
 
 	/* Unlink the original */
 	ret = jffs2_do_unlink(c, JFFS2_INODE_INFO(old_dir_i), 
-		      old_d_name->name, old_d_name->len, NULL);
+		      old_d_name, strlen(old_d_name), NULL);
 
 	if (ret) {
 		/* Oh shit. We really ought to make a single node which can do both atomically */
Index: src/erase.c
===================================================================
RCS file: /cvs/ecos/ecos/packages/fs/jffs2/current/src/erase.c,v
retrieving revision 1.5
diff -u -p -r1.5 erase.c
--- src/erase.c	20 Nov 2003 16:52:36 -0000	1.5
+++ src/erase.c	26 Nov 2003 22:24:13 -0000
@@ -7,7 +7,7 @@
  *
  * For licensing information, see the file 'LICENCE' in this directory.
  *
- * $Id: erase.c,v 1.57 2003/11/04 14:46:13 dwmw2 Exp $
+ * $Id: erase.c,v 1.58 2003/11/26 13:02:46 dwmw2 Exp $
  *
  */
 
@@ -281,11 +281,6 @@ static void jffs2_free_all_node_refs(str
 		jffs2_free_raw_node_ref(ref);
 	}
 	jeb->last_node = NULL;
-}
-
-void jffs2_erase_pending_trigger(struct jffs2_sb_info *c)
-{
-	OFNI_BS_2SFFJ(c)->s_dirt = 1;
 }
 
 static void jffs2_mark_erased_block(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb)
Index: src/fs-ecos.c
===================================================================
RCS file: /cvs/ecos/ecos/packages/fs/jffs2/current/src/fs-ecos.c,v
retrieving revision 1.15
diff -u -p -r1.15 fs-ecos.c
--- src/fs-ecos.c	25 Nov 2003 16:41:08 -0000	1.15
+++ src/fs-ecos.c	26 Nov 2003 22:24:13 -0000
@@ -1,32 +1,31 @@
 /*
  * JFFS2 -- Journalling Flash File System, Version 2.
  *
- * Copyright (C) 2001, 2002 Red Hat, Inc.
+ * Copyright (C) 2001-2003 Red Hat, Inc.
  *
  * Created by Dominic Ostrowski <dominic.ostrowski@3glab.com>
  * Contributors: David Woodhouse, Nick Garnett, Richard Panton.
  *
  * For licensing information, see the file 'LICENCE' in this directory.
  *
- * $Id: fs-ecos.c,v 1.11 2003/11/20 16:41:58 dwmw2 Exp $
+ * $Id: fs-ecos.c,v 1.27 2003/11/26 22:10:18 dwmw2 Exp $
  *
  */
 
 #include <linux/types.h>
 #include <linux/stat.h>
 #include <linux/kernel.h>
-#include "jffs2port.h"
 #include <linux/jffs2.h>
 #include <linux/jffs2_fs_sb.h>
 #include <linux/jffs2_fs_i.h>
 #include <linux/pagemap.h>
+#include <linux/crc32.h>
 #include "nodelist.h"
 
 #include <errno.h>
 #include <string.h>
 #include <cyg/io/io.h>
 #include <cyg/io/config_keys.h>
-#include <cyg/io/flash.h>
 
 #if (__GNUC__ == 3) && (__GNUC_MINOR__ == 2) && defined (__ARM_ARCH_4__)
 #error This compiler is known to be broken. Please see:
@@ -40,44 +39,48 @@
 static int jffs2_mount(cyg_fstab_entry * fste, cyg_mtab_entry * mte);
 static int jffs2_umount(cyg_mtab_entry * mte);
 static int jffs2_open(cyg_mtab_entry * mte, cyg_dir dir, const char *name,
-                      int mode, cyg_file * fte);
+		      int mode, cyg_file * fte);
 static int jffs2_ops_unlink(cyg_mtab_entry * mte, cyg_dir dir,
-                            const char *name);
+			    const char *name);
 static int jffs2_ops_mkdir(cyg_mtab_entry * mte, cyg_dir dir, const char *name);
 static int jffs2_ops_rmdir(cyg_mtab_entry * mte, cyg_dir dir, const char *name);
 static int jffs2_ops_rename(cyg_mtab_entry * mte, cyg_dir dir1,
-                            const char *name1, cyg_dir dir2, const char *name2);
+			    const char *name1, cyg_dir dir2, const char *name2);
 static int jffs2_ops_link(cyg_mtab_entry * mte, cyg_dir dir1, const char *name1,
-                          cyg_dir dir2, const char *name2, int type);
+			  cyg_dir dir2, const char *name2, int type);
 static int jffs2_opendir(cyg_mtab_entry * mte, cyg_dir dir, const char *name,
-                         cyg_file * fte);
+			 cyg_file * fte);
 static int jffs2_chdir(cyg_mtab_entry * mte, cyg_dir dir, const char *name,
-                       cyg_dir * dir_out);
+		       cyg_dir * dir_out);
 static int jffs2_stat(cyg_mtab_entry * mte, cyg_dir dir, const char *name,
-                      struct stat *buf);
+		      struct stat *buf);
 static int jffs2_getinfo(cyg_mtab_entry * mte, cyg_dir dir, const char *name,
-                         int key, void *buf, int len);
+			 int key, void *buf, int len);
 static int jffs2_setinfo(cyg_mtab_entry * mte, cyg_dir dir, const char *name,
-                         int key, void *buf, int len);
+			 int key, void *buf, int len);
 
 // File operations
 static int jffs2_fo_read(struct CYG_FILE_TAG *fp, struct CYG_UIO_TAG *uio);
 static int jffs2_fo_write(struct CYG_FILE_TAG *fp, struct CYG_UIO_TAG *uio);
 static int jffs2_fo_lseek(struct CYG_FILE_TAG *fp, off_t * pos, int whence);
 static int jffs2_fo_ioctl(struct CYG_FILE_TAG *fp, CYG_ADDRWORD com,
-                          CYG_ADDRWORD data);
+			  CYG_ADDRWORD data);
 static int jffs2_fo_fsync(struct CYG_FILE_TAG *fp, int mode);
 static int jffs2_fo_close(struct CYG_FILE_TAG *fp);
 static int jffs2_fo_fstat(struct CYG_FILE_TAG *fp, struct stat *buf);
 static int jffs2_fo_getinfo(struct CYG_FILE_TAG *fp, int key, void *buf,
-                            int len);
+			    int len);
 static int jffs2_fo_setinfo(struct CYG_FILE_TAG *fp, int key, void *buf,
-                            int len);
+			    int len);
 
 // Directory operations
 static int jffs2_fo_dirread(struct CYG_FILE_TAG *fp, struct CYG_UIO_TAG *uio);
 static int jffs2_fo_dirlseek(struct CYG_FILE_TAG *fp, off_t * pos, int whence);
 
+
+static int jffs2_read_inode (struct _inode *inode);
+static void jffs2_clear_inode (struct _inode *inode);
+
 //==========================================================================
 // Filesystem table entries
 
@@ -88,33 +91,33 @@ static int jffs2_fo_dirlseek(struct CYG_
 // we should never block in any filesystem operations.
 
 FSTAB_ENTRY(jffs2_fste, "jffs2", 0,
-            CYG_SYNCMODE_FILE_FILESYSTEM | CYG_SYNCMODE_IO_FILESYSTEM,
-            jffs2_mount,
-            jffs2_umount,
-            jffs2_open,
-            jffs2_ops_unlink,
-            jffs2_ops_mkdir,
-            jffs2_ops_rmdir,
-            jffs2_ops_rename,
-            jffs2_ops_link,
-            jffs2_opendir,
-            jffs2_chdir, jffs2_stat, jffs2_getinfo, jffs2_setinfo);
+	    CYG_SYNCMODE_FILE_FILESYSTEM | CYG_SYNCMODE_IO_FILESYSTEM,
+	    jffs2_mount,
+	    jffs2_umount,
+	    jffs2_open,
+	    jffs2_ops_unlink,
+	    jffs2_ops_mkdir,
+	    jffs2_ops_rmdir,
+	    jffs2_ops_rename,
+	    jffs2_ops_link,
+	    jffs2_opendir,
+	    jffs2_chdir, jffs2_stat, jffs2_getinfo, jffs2_setinfo);
 
 // -------------------------------------------------------------------------
 // File operations.
 // This set of file operations are used for normal open files.
 
 static cyg_fileops jffs2_fileops = {
-        jffs2_fo_read,
-        jffs2_fo_write,
-        jffs2_fo_lseek,
-        jffs2_fo_ioctl,
-        cyg_fileio_seltrue,
-        jffs2_fo_fsync,
-        jffs2_fo_close,
-        jffs2_fo_fstat,
-        jffs2_fo_getinfo,
-        jffs2_fo_setinfo
+	jffs2_fo_read,
+	jffs2_fo_write,
+	jffs2_fo_lseek,
+	jffs2_fo_ioctl,
+	cyg_fileio_seltrue,
+	jffs2_fo_fsync,
+	jffs2_fo_close,
+	jffs2_fo_fstat,
+	jffs2_fo_getinfo,
+	jffs2_fo_setinfo
 };
 
 // -------------------------------------------------------------------------
@@ -124,35 +127,34 @@ static cyg_fileops jffs2_fileops = {
 // close entries are functional.
 
 static cyg_fileops jffs2_dirops = {
-        jffs2_fo_dirread,
-        (cyg_fileop_write *) cyg_fileio_enosys,
-        jffs2_fo_dirlseek,
-        (cyg_fileop_ioctl *) cyg_fileio_enosys,
-        cyg_fileio_seltrue,
-        (cyg_fileop_fsync *) cyg_fileio_enosys,
-        jffs2_fo_close,
-        (cyg_fileop_fstat *) cyg_fileio_enosys,
-        (cyg_fileop_getinfo *) cyg_fileio_enosys,
-        (cyg_fileop_setinfo *) cyg_fileio_enosys
+	jffs2_fo_dirread,
+	(cyg_fileop_write *) cyg_fileio_enosys,
+	jffs2_fo_dirlseek,
+	(cyg_fileop_ioctl *) cyg_fileio_enosys,
+	cyg_fileio_seltrue,
+	(cyg_fileop_fsync *) cyg_fileio_enosys,
+	jffs2_fo_close,
+	(cyg_fileop_fstat *) cyg_fileio_enosys,
+	(cyg_fileop_getinfo *) cyg_fileio_enosys,
+	(cyg_fileop_setinfo *) cyg_fileio_enosys
 };
 
 //==========================================================================
 // STATIC VARIABLES !!!
 
-static char read_write_buffer[PAGE_CACHE_SIZE]; //avoids malloc when user may be under memory pressure
-static char gc_buffer[PAGE_CACHE_SIZE]; //avoids malloc when user may be under memory pressure
+static unsigned char gc_buffer[PAGE_CACHE_SIZE];	//avoids malloc when user may be under memory pressure
 static unsigned char n_fs_mounted = 0;  // a counter to track the number of jffs2 instances mounted
 
 //==========================================================================
 // Directory operations
 
 struct jffs2_dirsearch {
-        struct inode *dir;      // directory to search
-        const char *path;       // path to follow
-        struct inode *node;     // Node found
-        const char *name;       // last name fragment used
-        int namelen;            // name fragment length
-        cyg_bool last;          // last name in path?
+	struct _inode *dir;	// directory to search
+	const char *path;	// path to follow
+	struct _inode *node;	// Node found
+	const char *name;	// last name fragment used
+	int namelen;		// name fragment length
+	cyg_bool last;		// last name in path?
 };
 
 typedef struct jffs2_dirsearch jffs2_dirsearch;
@@ -160,53 +162,37 @@ typedef struct jffs2_dirsearch jffs2_dir
 //==========================================================================
 // Ref count and nlink management
 
-// -------------------------------------------------------------------------
-// dec_refcnt()
-// Decrment the reference count on an inode. If this makes the ref count
-// zero, then this inode can be freed.
-
-static int dec_refcnt(struct inode *node)
-{
-        int err = ENOERR;
-        node->i_count--;
-
-        // In JFFS2 inode's are temporary in ram structures that are free'd when the usage i_count drops to 0
-        // The i_nlink however is managed by JFFS2 and is unrelated to usage
-        if (node->i_count == 0) {
-                // This inode is not in use, so delete it.
-                iput(node);
-        }
-
-        return err;
-}
 
 // FIXME: This seems like real cruft. Wouldn't it be better just to do the
 // right thing?
-static void icache_evict(struct inode *root_i, struct inode *i)
+static void icache_evict(struct _inode *root_i, struct _inode *i)
 {
-        struct inode *cached_inode;
-        struct inode *next_inode;
+	struct _inode *this = root_i, *next;
 
-        D2(printf("icache_evict\n"));
-        // If this is an absolute search path from the root,
-        // remove all cached inodes with i_count of zero (these are only 
-        // held where needed for dotdot filepaths)
-        if (i == root_i) {
-                for (cached_inode = root_i; cached_inode != NULL;
-                     cached_inode = next_inode) {
-                        next_inode = cached_inode->i_cache_next;
-                        if (cached_inode->i_count == 0) {
-                                cached_inode->i_cache_prev->i_cache_next = cached_inode->i_cache_next;  // Previous entry points ahead of us
-                                if (cached_inode->i_cache_next != NULL)
-                                        cached_inode->i_cache_next->i_cache_prev = cached_inode->i_cache_prev;  // Next entry points behind us
-                                jffs2_clear_inode(cached_inode);
-                                D2(printf
-                                   ("free icache_evict inode %x $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$\n",
-                                    cached_inode));
-                                free(cached_inode);
-                        }
-                }
-        }
+ restart:
+	D2(printf("icache_evict\n"));
+	// If this is an absolute search path from the root,
+	// remove all cached inodes with i_count of zero (these are only 
+	// held where needed for dotdot filepaths)
+	while (this) {
+		next = this->i_cache_next;
+		if (this != i && this->i_count == 0) {
+			struct _inode *parent = this->i_parent;
+			if (this->i_cache_next)
+				this->i_cache_next->i_cache_prev = this->i_cache_prev;
+			if (this->i_cache_prev)
+				this->i_cache_prev->i_cache_next = this->i_cache_next;
+			jffs2_clear_inode(this);
+			memset(this, 0x5a, sizeof(*this));
+			free(this);
+			if (parent && parent != this) {
+				parent->i_count--;
+				this = root_i;
+				goto restart;
+			}
+		}
+		this = next;
+	}
 }
 
 //==========================================================================
@@ -217,16 +203,18 @@ static void icache_evict(struct inode *r
 // Initialize a dirsearch object to start a search
 
 static void init_dirsearch(jffs2_dirsearch * ds,
-                           struct inode *dir, const char *name)
+			   struct _inode *dir, const char *name)
 {
-        D2(printf("init_dirsearch name = %s\n", name));
-        D2(printf("init_dirsearch dir = %x\n", dir));
-        ds->dir = dir;
-        ds->path = name;
-        ds->node = dir;
-        ds->name = name;
-        ds->namelen = 0;
-        ds->last = false;
+	D2(printf("init_dirsearch name = %s\n", name));
+	D2(printf("init_dirsearch dir = %x\n", dir));
+
+	dir->i_count++;
+	ds->dir = dir;
+	ds->path = name;
+	ds->node = dir;
+	ds->name = name;
+	ds->namelen = 0;
+	ds->last = false;
 }
 
 // -------------------------------------------------------------------------
@@ -236,87 +224,72 @@ static void init_dirsearch(jffs2_dirsear
 
 static int find_entry(jffs2_dirsearch * ds)
 {
-        unsigned long hash;
-        struct qstr this;
-        unsigned int c;
-        const char *hashname;
-
-        struct inode *dir = ds->dir;
-        const char *name = ds->path;
-        const char *n = name;
-        char namelen = 0;
-        struct inode *d;
-
-        D2(printf("find_entry\n"));
-
-        // check that we really have a directory
-        if (!S_ISDIR(dir->i_mode))
-                return ENOTDIR;
-
-        // Isolate the next element of the path name. 
-        while (*n != '\0' && *n != '/')
-                n++, namelen++;
-
-        // If we terminated on a NUL, set last flag.
-        if (*n == '\0')
-                ds->last = true;
-
-        // update name in dirsearch object
-        ds->name = name;
-        ds->namelen = namelen;
-
-        if (name[0] == '.')
-                switch (namelen) {
-                default:
-                        break;
-                case 2:
-                        // Dot followed by not Dot, treat as any other name 
-                        if (name[1] != '.')
-                                break;
-                        // Dot Dot 
-                        // Move back up the search path
-                        D2(printf("find_entry found ..\n"));
-                        ds->node = ds->dir->i_parent;
-                        if (ds->dir->i_count == 0) {
-                                iput(ds->dir);  // This inode may be evicted
-                                ds->dir = NULL;
-                        }
-                        return ENOERR;
-                case 1:
-                        // Dot is consumed
-                        D2(printf("find_entry found .\n"));
-                        ds->node = ds->dir;
-                        return ENOERR;
-                }
-        // Here we have the name and its length set up.
-        // Search the directory for a matching entry
-
-        hashname = name;
-        this.name = hashname;
-        c = *(const unsigned char *) hashname;
-
-        hash = init_name_hash();
-        do {
-                hashname++;
-                hash = partial_name_hash(c, hash);
-                c = *(const unsigned char *) hashname;
-        } while (c && (c != '/'));
-        this.len = hashname - (const char *) this.name;
-        this.hash = end_name_hash(hash);
-
-        D2(printf("find_entry for name = %s\n", ds->path));
-        d = jffs2_lookup(dir, &this);
-        D2(printf("find_entry got dir = %x\n", d));
-
-        if (d == NULL)
-                return ENOENT;
-
-        // The back path for dotdot to follow
-        d->i_parent = dir;
-        // pass back the node we have found
-        ds->node = d;
-
-        return ENOERR;
+	struct _inode *dir = ds->dir;
+	const char *name = ds->path;
+	const char *n = name;
+	char namelen = 0;
+	struct _inode *d;
+
+	D2(printf("find_entry\n"));
+
+	// check that we really have a directory
+	if (!S_ISDIR(dir->i_mode))
+		return ENOTDIR;
+
+	// Isolate the next element of the path name. 
+	while (*n != '\0' && *n != '/')
+		n++, namelen++;
+
+	// If we terminated on a NUL, set last flag.
+	if (*n == '\0')
+		ds->last = true;
+
+	// update name in dirsearch object
+	ds->name = name;
+	ds->namelen = namelen;
+
+	if (name[0] == '.')
+		switch (namelen) {
+		default:
+			break;
+		case 2:
+			// Dot followed by not Dot, treat as any other name 
+			if (name[1] != '.')
+				break;
+			// Dot Dot 
+			// Move back up the search path
+			D2(printf("find_entry found ..\n"));
+			ds->dir = ds->node;
+			ds->node = ds->dir->i_parent;
+			ds->node->i_count++;
+			return ENOERR;
+		case 1:
+			// Dot is consumed
+			D2(printf("find_entry found .\n"));
+			ds->node = ds->dir;
+			ds->dir->i_count++;
+			return ENOERR;
+		}
+
+	// Here we have the name and its length set up.
+	// Search the directory for a matching entry
+
+	D2(printf("find_entry for name = %s\n", ds->path));
+	d = jffs2_lookup(dir, name, namelen);
+	D2(printf("find_entry got dir = %x\n", d));
+
+	if (d == NULL)
+		return ENOENT;
+
+	// If it's a new directory inode, increase refcount on its parent
+	if (S_ISDIR(d->i_mode) && !d->i_parent) {
+		d->i_parent = dir;
+		dir->i_count++;
+	}
+
+	// pass back the node we have found
+	ds->node = d;
+	return ENOERR;
 
 }
 
@@ -325,107 +298,113 @@ static int find_entry(jffs2_dirsearch * 
 // Main interface to directory search code. This is used in all file
 // level operations to locate the object named by the pathname.
 
+// Returns with use count incremented on both the sought object and 
+// the directory it was found in
 static int jffs2_find(jffs2_dirsearch * d)
 {
-        int err;
+	int err;
 
-        D2(printf("jffs2_find for path =%s\n", d->path));
-        // Short circuit empty paths
-        if (*(d->path) == '\0')
-                return ENOERR;
-
-        // iterate down directory tree until we find the object
-        // we want.
-        for (;;) {
-                err = find_entry(d);
-
-                if (err != ENOERR)
-                        return err;
-
-                if (d->last)
-                        return ENOERR;
-
-                // every inode traversed in the find is temporary and should be free'd
-                //iput(d->dir);
-
-                // Update dirsearch object to search next directory.
-                d->dir = d->node;
-                d->path += d->namelen;
-                if (*(d->path) == '/')
-                        d->path++;      // skip dirname separators
-        }
+	D2(printf("jffs2_find for path =%s\n", d->path));
+
+	// Short circuit empty paths
+	if (*(d->path) == '\0') {
+		d->node->i_count++;
+		return ENOERR;
+	}
+
+	// iterate down directory tree until we find the object
+	// we want.
+	for (;;) {
+		err = find_entry(d);
+
+		if (err != ENOERR)
+			return err;
+
+		if (d->last)
+			return ENOERR;
+
+		/* We're done with it, although it we found a subdir that
+		   will have caused the refcount to have been increased */
+		jffs2_iput(d->dir);
+
+		// Update dirsearch object to search next directory.
+		d->dir = d->node;
+		d->path += d->namelen;
+		if (*(d->path) == '/')
+			d->path++;	// skip dirname separators
+	}
 }
 
 //==========================================================================
 // Pathconf support
 // This function provides support for pathconf() and fpathconf().
 
-static int jffs2_pathconf(struct inode *node, struct cyg_pathconf_info *info)
+static int jffs2_pathconf(struct _inode *node, struct cyg_pathconf_info *info)
 {
-        int err = ENOERR;
-        D2(printf("jffs2_pathconf\n"));
+	int err = ENOERR;
+	D2(printf("jffs2_pathconf\n"));
 
-        switch (info->name) {
-        case _PC_LINK_MAX:
-                info->value = LINK_MAX;
-                break;
-
-        case _PC_MAX_CANON:
-                info->value = -1;       // not supported
-                err = EINVAL;
-                break;
-
-        case _PC_MAX_INPUT:
-                info->value = -1;       // not supported
-                err = EINVAL;
-                break;
-
-        case _PC_NAME_MAX:
-                info->value = NAME_MAX;
-                break;
-
-        case _PC_PATH_MAX:
-                info->value = PATH_MAX;
-                break;
-
-        case _PC_PIPE_BUF:
-                info->value = -1;       // not supported
-                err = EINVAL;
-                break;
-
-        case _PC_ASYNC_IO:
-                info->value = -1;       // not supported
-                err = EINVAL;
-                break;
-
-        case _PC_CHOWN_RESTRICTED:
-                info->value = -1;       // not supported
-                err = EINVAL;
-                break;
-
-        case _PC_NO_TRUNC:
-                info->value = 0;
-                break;
-
-        case _PC_PRIO_IO:
-                info->value = 0;
-                break;
-
-        case _PC_SYNC_IO:
-                info->value = 0;
-                break;
-
-        case _PC_VDISABLE:
-                info->value = -1;       // not supported
-                err = EINVAL;
-                break;
-
-        default:
-                err = EINVAL;
-                break;
-        }
+	switch (info->name) {
+	case _PC_LINK_MAX:
+		info->value = LINK_MAX;
+		break;
+
+	case _PC_MAX_CANON:
+		info->value = -1;	// not supported
+		err = EINVAL;
+		break;
+
+	case _PC_MAX_INPUT:
+		info->value = -1;	// not supported
+		err = EINVAL;
+		break;
+
+	case _PC_NAME_MAX:
+		info->value = NAME_MAX;
+		break;
+
+	case _PC_PATH_MAX:
+		info->value = PATH_MAX;
+		break;
+
+	case _PC_PIPE_BUF:
+		info->value = -1;	// not supported
+		err = EINVAL;
+		break;
+
+	case _PC_ASYNC_IO:
+		info->value = -1;	// not supported
+		err = EINVAL;
+		break;
+
+	case _PC_CHOWN_RESTRICTED:
+		info->value = -1;	// not supported
+		err = EINVAL;
+		break;
+
+	case _PC_NO_TRUNC:
+		info->value = 0;
+		break;
+
+	case _PC_PRIO_IO:
+		info->value = 0;
+		break;
+
+	case _PC_SYNC_IO:
+		info->value = 0;
+		break;
+
+	case _PC_VDISABLE:
+		info->value = -1;	// not supported
+		err = EINVAL;
+		break;
+
+	default:
+		err = EINVAL;
+		break;
+	}
 
-        return err;
+	return err;
 }
 
 //==========================================================================
@@ -437,193 +416,214 @@ static int jffs2_pathconf(struct inode *
 // filesystem.
 static int jffs2_read_super(struct super_block *sb)
 {
-        struct jffs2_sb_info *c;
-        struct inode *root_i;
-        Cyg_ErrNo err;
-        cyg_uint32 len;
-        cyg_io_flash_getconfig_devsize_t ds;
-        cyg_io_flash_getconfig_blocksize_t bs;
-
-        D1(printk(KERN_DEBUG "jffs2: read_super\n"));
-
-        c = JFFS2_SB_INFO(sb);
-
-        len = sizeof (ds);
-        err = cyg_io_get_config(sb->s_dev,
-                                CYG_IO_GET_CONFIG_FLASH_DEVSIZE, &ds, &len);
-        if (err != ENOERR) {
-                D1(printf
-                   ("jffs2: cyg_io_get_config failed to get dev size: %d\n",
-                    err));
-                return err;
-        }
-        len = sizeof (bs);
-        bs.offset = 0;
-        err = cyg_io_get_config(sb->s_dev,
-                                CYG_IO_GET_CONFIG_FLASH_BLOCKSIZE, &bs, &len);
-        if (err != ENOERR) {
-                D1(printf
-                   ("jffs2: cyg_io_get_config failed to get block size: %d\n",
-                    err));
-                return err;
-        }
+	struct jffs2_sb_info *c;
+	Cyg_ErrNo err;
+	cyg_uint32 len;
+	cyg_io_flash_getconfig_devsize_t ds;
+	cyg_io_flash_getconfig_blocksize_t bs;
+
+	D1(printk(KERN_DEBUG "jffs2: read_super\n"));
+
+	c = JFFS2_SB_INFO(sb);
+
+	len = sizeof (ds);
+	err = cyg_io_get_config(sb->s_dev,
+				CYG_IO_GET_CONFIG_FLASH_DEVSIZE, &ds, &len);
+	if (err != ENOERR) {
+		D1(printf
+		   ("jffs2: cyg_io_get_config failed to get dev size: %d\n",
+		    err));
+		return err;
+	}
+	len = sizeof (bs);
+	bs.offset = 0;
+	err = cyg_io_get_config(sb->s_dev,
+				CYG_IO_GET_CONFIG_FLASH_BLOCKSIZE, &bs, &len);
+	if (err != ENOERR) {
+		D1(printf
+		   ("jffs2: cyg_io_get_config failed to get block size: %d\n",
+		    err));
+		return err;
+	}
+
+	c->sector_size = bs.block_size;
+	c->flash_size = ds.dev_size;
+	c->cleanmarker_size = sizeof(struct jffs2_unknown_node);
+
+	err = jffs2_do_mount_fs(c);
+	if (err)
+		return -err;
+
+	D1(printk(KERN_DEBUG "jffs2_read_super(): Getting root inode\n"));
+	sb->s_root = jffs2_iget(sb, 1);
+	if (IS_ERR(sb->s_root)) {
+		D1(printk(KERN_WARNING "get root inode failed\n"));
+		err = PTR_ERR(sb->s_root);
+		sb->s_root = NULL;
+		goto out_nodes;
+	}
 
-        c->sector_size = bs.block_size;
-        c->flash_size = ds.dev_size;
-        c->cleanmarker_size = sizeof(struct jffs2_unknown_node);
-
-        err = jffs2_do_mount_fs(c);
-        if (err)
-                return -err;
-
-        D1(printk(KERN_DEBUG "jffs2_read_super(): Getting root inode\n"));
-        root_i = iget(sb, 1);
-        if (is_bad_inode(root_i)) {
-                D1(printk(KERN_WARNING "get root inode failed\n"));
-                err = EIO;
-                goto out_nodes;
-        }
+	return 0;
 
-        D1(printk(KERN_DEBUG "jffs2_read_super(): d_alloc_root()\n"));
-        sb->s_root = d_alloc_root(root_i);
-        if (!sb->s_root) {
-                err = ENOMEM;
-                goto out_root_i;
-        }
-        sb->s_blocksize = PAGE_CACHE_SIZE;
-        sb->s_blocksize_bits = PAGE_CACHE_SHIFT;
-        sb->s_magic = JFFS2_SUPER_MAGIC;
-
-        return 0;
-
-      out_root_i:
-        iput(root_i);
       out_nodes:
-        jffs2_free_ino_caches(c);
-        jffs2_free_raw_node_refs(c);
-        free(c->blocks);
+	jffs2_free_ino_caches(c);
+	jffs2_free_raw_node_refs(c);
+	free(c->blocks);
 
-        return err;
+	return err;
 }
 
 static int jffs2_mount(cyg_fstab_entry * fste, cyg_mtab_entry * mte)
 {
-        extern cyg_mtab_entry cyg_mtab[], cyg_mtab_end;
-        struct super_block *jffs2_sb = NULL;
-        struct jffs2_sb_info *c;
-        cyg_mtab_entry *m;
-        cyg_io_handle_t t;
-        Cyg_ErrNo err;
-
-        D2(printf("jffs2_mount\n"));
-
-        err = cyg_io_lookup(mte->devname, &t);
-        if (err != ENOERR)
-                return -err;
-
-        // Iterate through the mount table to see if we're mounted
-        // FIXME: this should be done better - perhaps if the superblock
-        // can be stored as an inode in the icache.
-        for (m = &cyg_mtab[0]; m != &cyg_mtab_end; m++) {
-                // stop if there are more than the configured maximum
-                if (m - &cyg_mtab[0] >= CYGNUM_FILEIO_MTAB_MAX) {
-                        m = &cyg_mtab_end;
-                        break;
-                }
-                if (m->valid && strcmp(m->fsname, "jffs2") == 0 &&
-                    strcmp(m->devname, mte->devname) == 0) {
-                        jffs2_sb = (struct super_block *) m->data;
-                }
-        }
-
-        if (jffs2_sb == NULL) {
-                jffs2_sb = malloc(sizeof (struct super_block));
-
-                if (jffs2_sb == NULL)
-                        return ENOMEM;
-
-                c = JFFS2_SB_INFO(jffs2_sb);
-                memset(jffs2_sb, 0, sizeof (struct super_block));
-                jffs2_sb->s_dev = t;
-
-                c->inocache_list = malloc(sizeof(struct jffs2_inode_cache *) * INOCACHE_HASHSIZE);
-                if (!c->inocache_list) {
-                        free(jffs2_sb);
-                        return ENOMEM;
-                }
-                memset(c->inocache_list, 0, sizeof(struct jffs2_inode_cache *) * INOCACHE_HASHSIZE);
+	extern cyg_mtab_entry cyg_mtab[], cyg_mtab_end;
+	struct super_block *jffs2_sb = NULL;
+	struct jffs2_sb_info *c;
+	cyg_mtab_entry *m;
+	cyg_io_handle_t t;
+	Cyg_ErrNo err;
+
+	D2(printf("jffs2_mount\n"));
+
+	err = cyg_io_lookup(mte->devname, &t);
+	if (err != ENOERR)
+		return -err;
+
+	// Iterate through the mount table to see if we're mounted
+	// FIXME: this should be done better - perhaps if the superblock
+	// can be stored as an inode in the icache.
+	for (m = &cyg_mtab[0]; m != &cyg_mtab_end; m++) {
+		// stop if there are more than the configured maximum
+		if (m - &cyg_mtab[0] >= CYGNUM_FILEIO_MTAB_MAX) {
+			m = &cyg_mtab_end;
+			break;
+		}
+		if (m->valid && strcmp(m->fsname, "jffs2") == 0 &&
+		    strcmp(m->devname, mte->devname) == 0) {
+			jffs2_sb = (struct super_block *) m->data;
+		}
+	}
+
+	if (jffs2_sb == NULL) {
+		jffs2_sb = malloc(sizeof (struct super_block));
+
+		if (jffs2_sb == NULL)
+			return ENOMEM;
+
+		c = JFFS2_SB_INFO(jffs2_sb);
+		memset(jffs2_sb, 0, sizeof (struct super_block));
+		jffs2_sb->s_dev = t;
+
+		c->inocache_list = malloc(sizeof(struct jffs2_inode_cache *) * INOCACHE_HASHSIZE);
+		if (!c->inocache_list) {
+			free(jffs2_sb);
+			return ENOMEM;
+		}
+		memset(c->inocache_list, 0, sizeof(struct jffs2_inode_cache *) * INOCACHE_HASHSIZE);
                 if (n_fs_mounted++ == 0)
                         jffs2_create_slab_caches(); // No error check, cannot fail
 
-                err = jffs2_read_super(jffs2_sb);
+		err = jffs2_read_super(jffs2_sb);
 
-                if (err) {
+		if (err) {
                         if (--n_fs_mounted == 0)
                                 jffs2_destroy_slab_caches();
                         
-                        free(jffs2_sb);
-                        free(c->inocache_list);
-                        return err;
-                }
-
-                jffs2_sb->s_root->i_parent = jffs2_sb->s_root;  // points to itself, no dotdot paths above mountpoint
-                jffs2_sb->s_root->i_cache_prev = NULL;  // root inode, so always null
-                jffs2_sb->s_root->i_cache_next = NULL;
-                jffs2_sb->s_root->i_count = 1;  // Ensures the root inode is always in ram until umount
-
-                D2(printf("jffs2_mount erasing pending blocks\n"));
-                jffs2_erase_pending_blocks(c,0);
-        }
-        mte->data = (CYG_ADDRWORD) jffs2_sb;
+			free(jffs2_sb);
+			free(c->inocache_list);
+			return err;
+		}
+
+		jffs2_sb->s_root->i_parent = jffs2_sb->s_root;	// points to itself, no dotdot paths above mountpoint
+		jffs2_sb->s_root->i_cache_prev = NULL;	// root inode, so always null
+		jffs2_sb->s_root->i_cache_next = NULL;
+		jffs2_sb->s_root->i_count = 1;	// Ensures the root inode is always in ram until umount
+
+		D2(printf("jffs2_mount erasing pending blocks\n"));
+		jffs2_erase_pending_blocks(c,0);
+#ifdef CYGOPT_FS_JFFS2_GCTHREAD
+		jffs2_start_garbage_collect_thread(c);
+#endif
+	}
+	mte->data = (CYG_ADDRWORD) jffs2_sb;
 
-        jffs2_sb->s_mount_count++;
-        mte->root = (cyg_dir) jffs2_sb->s_root;
-        D2(printf("jffs2_mounted superblock at %x\n", mte->root));
+	jffs2_sb->s_mount_count++;
+	mte->root = (cyg_dir) jffs2_sb->s_root;
+	D2(printf("jffs2_mounted superblock at %x\n", mte->root));
 
-        return ENOERR;
+	return ENOERR;
 }
 
+extern cyg_dir cyg_cdir_dir;
+
 // -------------------------------------------------------------------------
 // jffs2_umount()
 // Unmount the filesystem. 
 
 static int jffs2_umount(cyg_mtab_entry * mte)
 {
-        struct inode *root = (struct inode *) mte->root;
-        struct super_block *jffs2_sb = root->i_sb;
-        struct jffs2_sb_info *c = JFFS2_SB_INFO(jffs2_sb);
-
-        D2(printf("jffs2_umount\n"));
-
-        // Only really umount if this is the only mount
-        if (jffs2_sb->s_mount_count == 1) {
-
-                if (root->i_cache_next != NULL) // root icount was set to 1 on mount
-                        return EBUSY;
-                
-                dec_refcnt(root);       // Time to free the root inode
-
-                //Clear root inode
-                //root_i = NULL;
-
-                // Clean up the super block and root inode
-                jffs2_free_ino_caches(c);
-                jffs2_free_raw_node_refs(c);
-                free(c->blocks);
-                free(c->inocache_list);
-                free(jffs2_sb);
-                // Clear superblock & root pointer
-                mte->root = CYG_DIR_NULL;
+	struct _inode *root = (struct _inode *) mte->root;
+	struct super_block *jffs2_sb = root->i_sb;
+	struct jffs2_sb_info *c = JFFS2_SB_INFO(jffs2_sb);
+
+	D2(printf("jffs2_umount\n"));
+
+	// Only really umount if this is the only mount
+	if (jffs2_sb->s_mount_count == 1) {
+		icache_evict(root, NULL);
+		if (root->i_cache_next != NULL)	{
+			struct _inode *inode = root;
+			printf("Refuse to unmount.\n");
+			while (inode) {
+				printf("Ino #%u has use count %d\n",
+				       inode->i_ino, inode->i_count);
+				inode = inode->i_cache_next;
+			}
+			// root icount was set to 1 on mount
+			return EBUSY;
+                }
+		if (root->i_count == 2 &&
+		    cyg_cdir_dir == (cyg_dir)root &&
+		    !strcmp(mte->name, "/")) {
+			/* If we were mounted on root, there's no
+			   way for the cwd to change out and free 
+			   the file system for unmounting. So we hack
+			   it -- if cwd is '/' we unset it. Perhaps
+			   we should allow chdir(NULL) to unset
+			   cyg_cdir_dir? */
+			cyg_cdir_dir = CYG_DIR_NULL;
+			jffs2_iput(root);
+		}
+		if (root->i_count != 1) {
+			printf("Ino #1 has use count %d\n",
+			       root->i_count);
+			return EBUSY;
+		}
+#ifdef CYGOPT_FS_JFFS2_GCTHREAD
+		jffs2_stop_garbage_collect_thread(c);
+#endif
+		jffs2_iput(root);	// Time to free the root inode
+		free(root);
+		//Clear root inode
+		//root_i = NULL;
+
+		// Clean up the super block and root inode
+		jffs2_free_ino_caches(c);
+		jffs2_free_raw_node_refs(c);
+		free(c->blocks);
+		free(c->inocache_list);
+		free(jffs2_sb);
+		// Clear superblock & root pointer
+		mte->root = CYG_DIR_NULL;
                 mte->data = 0;
-                mte->fs->data = 0;      // fstab entry, visible to all mounts. No current mount
-                // That's all folks.
-                D2(printf("jffs2_umount No current mounts\n"));
-        } else {
-                jffs2_sb->s_mount_count--;
+		mte->fs->data = 0;	// fstab entry, visible to all mounts. No current mount
+		// That's all folks.
+		D2(printf("jffs2_umount No current mounts\n"));
+	} else {
+		jffs2_sb->s_mount_count--;
         }
         if (--n_fs_mounted == 0)
                 jffs2_destroy_slab_caches();        
-        return ENOERR;
+	return ENOERR;
 }
 
 // -------------------------------------------------------------------------
@@ -631,94 +631,78 @@ static int jffs2_umount(cyg_mtab_entry *
 // Open a file for reading or writing.
 
 static int jffs2_open(cyg_mtab_entry * mte, cyg_dir dir, const char *name,
-                      int mode, cyg_file * file)
+		      int mode, cyg_file * file)
 {
 
-        jffs2_dirsearch ds;
-        struct inode *node = NULL;
-        int err;
-
-        D2(printf("jffs2_open\n"));
-
-        icache_evict((struct inode *) mte->root, (struct inode *) dir);
-
-        init_dirsearch(&ds, (struct inode *) dir, name);
+	jffs2_dirsearch ds;
+	struct _inode *node = NULL;
+	int err;
+
+	D2(printf("jffs2_open\n"));
+
+	init_dirsearch(&ds, (struct _inode *) dir, name);
+
+	err = jffs2_find(&ds);
+
+	if (err == ENOENT) {
+		if (ds.last && (mode & O_CREAT)) {
+
+			// No node there, if the O_CREAT bit is set then we must
+			// create a new one. The dir and name fields of the dirsearch
+			// object will have been updated so we know where to put it.
+
+			err = jffs2_create(ds.dir, ds.name, S_IRUGO|S_IXUGO|S_IWUSR|S_IFREG, &node);
+
+			if (err != 0) {
+				//Possible orphaned inode on the flash - but will be gc'd
+				return err;
+			}
+
+			err = ENOERR;
+		}
+	} else if (err == ENOERR) {
+		// The node exists. If the O_CREAT and O_EXCL bits are set, we
+		// must fail the open.
+
+		if ((mode & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL)) {
+			jffs2_iput(ds.node);
+			err = EEXIST;
+		} else
+			node = ds.node;
+	}
+
+	// Finished with the directory now 
+	jffs2_iput(ds.dir);
+
+	if (err != ENOERR)
+		return err;
+
+	// Check that we actually have a file here
+	if (S_ISDIR(node->i_mode)) {
+		jffs2_iput(node);
+		return EISDIR;
+	}
+
+	if (mode & O_TRUNC) {
+		struct jffs2_inode_info *f = JFFS2_INODE_INFO(node);
+		struct jffs2_sb_info *c = JFFS2_SB_INFO(node->i_sb);
+		// If the O_TRUNC bit is set we must clean out the file data.
+
+		node->i_size = 0;
+		jffs2_truncate_fraglist(c, &f->fragtree, 0);
+		// Update file times
+		node->i_ctime = node->i_mtime = cyg_timestamp();
+	}
+
+	// Initialise the file object
+	file->f_flag |= mode & CYG_FILE_MODE_MASK;
+	file->f_type = CYG_FILE_TYPE_FILE;
+	file->f_ops = &jffs2_fileops;
+	file->f_offset = (mode & O_APPEND) ? node->i_size : 0;
+	file->f_data = (CYG_ADDRWORD) node;
+	file->f_xops = 0;
 
-        err = jffs2_find(&ds);
-
-        if (err == ENOENT) {
-                if (ds.last && (mode & O_CREAT)) {
-                        unsigned long hash;
-                        struct qstr this;
-                        unsigned int c;
-                        const char *hashname;
-
-                        // No node there, if the O_CREAT bit is set then we must
-                        // create a new one. The dir and name fields of the dirsearch
-                        // object will have been updated so we know where to put it.
-
-                        hashname = ds.name;
-                        this.name = hashname;
-                        c = *(const unsigned char *) hashname;
-
-                        hash = init_name_hash();
-                        do {
-                                hashname++;
-                                hash = partial_name_hash(c, hash);
-                                c = *(const unsigned char *) hashname;
-                        } while (c && (c != '/'));
-                        this.len = hashname - (const char *) this.name;
-                        this.hash = end_name_hash(hash);
-
-                        err = jffs2_create(ds.dir, &this, S_IRUGO|S_IXUGO|S_IWUSR|S_IFREG, &node);
-
-                        if (err != 0) {
-                                //Possible orphaned inode on the flash - but will be gc'd
-                                return err;
-                        }
-
-                        err = ENOERR;
-                }
-        } else if (err == ENOERR) {
-                // The node exists. If the O_CREAT and O_EXCL bits are set, we
-                // must fail the open.
-
-                if ((mode & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL))
-                        err = EEXIST;
-                else
-                        node = ds.node;
-        }
-
-        if (err == ENOERR && (mode & O_TRUNC)) {
-                struct jffs2_inode_info *f = JFFS2_INODE_INFO(node);
-                struct jffs2_sb_info *c = JFFS2_SB_INFO(node->i_sb);
-                // If the O_TRUNC bit is set we must clean out the file data.
-
-                node->i_size = 0;
-                jffs2_truncate_fraglist(c, &f->fragtree, 0);
-                // Update file times
-                node->i_ctime = node->i_mtime = cyg_timestamp();
-        }
-
-        if (err != ENOERR)
-                return err;
-
-        // Check that we actually have a file here
-        if (S_ISDIR(node->i_mode))
-                return EISDIR;
-
-        node->i_count++;        // Count successful open
-
-        // Initialize the file object
-
-        file->f_flag |= mode & CYG_FILE_MODE_MASK;
-        file->f_type = CYG_FILE_TYPE_FILE;
-        file->f_ops = &jffs2_fileops;
-        file->f_offset = (mode & O_APPEND) ? node->i_size : 0;
-        file->f_data = (CYG_ADDRWORD) node;
-        file->f_xops = 0;
-
-        return ENOERR;
+	return ENOERR;
 }
 
 // -------------------------------------------------------------------------
@@ -727,46 +711,34 @@ static int jffs2_open(cyg_mtab_entry * m
 
 static int jffs2_ops_unlink(cyg_mtab_entry * mte, cyg_dir dir, const char *name)
 {
-        unsigned long hash;
-        struct qstr this;
-        unsigned int c;
-        const char *hashname;
-        jffs2_dirsearch ds;
-        int err;
-
-        D2(printf("jffs2_ops_unlink\n"));
-
-        icache_evict((struct inode *) mte->root, (struct inode *) dir);
+	jffs2_dirsearch ds;
+	int err;
 
-        init_dirsearch(&ds, (struct inode *) dir, name);
+	D2(printf("jffs2_ops_unlink\n"));
 
-        err = jffs2_find(&ds);
+	init_dirsearch(&ds, (struct _inode *) dir, name);
 
-        if (err != ENOERR)
-                return err;
+	err = jffs2_find(&ds);
 
-        // Cannot unlink directories, use rmdir() instead
-        if (S_ISDIR(ds.node->i_mode))
-                return EPERM;
+	if (err != ENOERR) {
+		jffs2_iput(ds.dir);
+		return err;
+	}
 
-        // Delete it from its directory
+	// Cannot unlink directories, use rmdir() instead
+	if (S_ISDIR(ds.node->i_mode)) {
+		jffs2_iput(ds.dir);
+		jffs2_iput(ds.node);
+		return EPERM;
+	}
 
-        hashname = ds.name;
-        this.name = hashname;
-        c = *(const unsigned char *) hashname;
+	// Delete it from its directory
 
-        hash = init_name_hash();
-        do {
-                hashname++;
-                hash = partial_name_hash(c, hash);
-                c = *(const unsigned char *) hashname;
-        } while (c && (c != '/'));
-        this.len = hashname - (const char *) this.name;
-        this.hash = end_name_hash(hash);
+	err = jffs2_unlink(ds.dir, ds.node, ds.name);
+	jffs2_iput(ds.dir);
+	jffs2_iput(ds.node);
 
-        err = jffs2_unlink(ds.dir, ds.node, &this);
-
-        return err;
+	return -err;
 }
 
 // -------------------------------------------------------------------------
@@ -775,57 +747,33 @@ static int jffs2_ops_unlink(cyg_mtab_ent
 
 static int jffs2_ops_mkdir(cyg_mtab_entry * mte, cyg_dir dir, const char *name)
 {
-        jffs2_dirsearch ds;
-        struct inode *node = NULL;
-        int err;
-
-        D2(printf("jffs2_ops_mkdir\n"));
-
-        icache_evict((struct inode *) mte->root, (struct inode *) dir);
-
-        init_dirsearch(&ds, (struct inode *) dir, name);
-
-        err = jffs2_find(&ds);
-
-        if (err == ENOENT) {
-                if (ds.last) {
-                        unsigned long hash;
-                        struct qstr this;
-                        unsigned int c;
-                        const char *hashname;
-                        // The entry does not exist, and it is the last element in
-                        // the pathname, so we can create it here.
-
-                        hashname = ds.name;
-                        this.name = hashname;
-                        c = *(const unsigned char *) hashname;
-
-                        hash = init_name_hash();
-                        do {
-                                hashname++;
-                                hash = partial_name_hash(c, hash);
-                                c = *(const unsigned char *) hashname;
-                        } while (c && (c != '/'));
-                        this.len = hashname - (const char *) this.name;
-                        this.hash = end_name_hash(hash);
-
-                        err = jffs2_mkdir(ds.dir, &this, 0, &node);
+	jffs2_dirsearch ds;
+	int err;
 
-                        if (err != 0)
-                                return ENOSPC;
+	D2(printf("jffs2_ops_mkdir\n"));
 
-                }
-                // If this was not the last element, then and intermediate
-                // directory does not exist.
-        } else {
-                // If there we no error, something already exists with that
-                // name, so we cannot create another one.
+	init_dirsearch(&ds, (struct _inode *) dir, name);
 
-                if (err == ENOERR)
-                        err = EEXIST;
-        }
+	err = jffs2_find(&ds);
 
-        return err;
+	if (err == ENOENT) {
+		if (ds.last) {
+			// The entry does not exist, and it is the last element in
+			// the pathname, so we can create it here.
+
+			err = -jffs2_mkdir(ds.dir, ds.name, S_IRUGO|S_IXUGO|S_IWUSR);
+		}
+		// If this was not the last element, then an intermediate
+		// directory does not exist.
+	} else {
+		// If there we no error, something already exists with that
+		// name, so we cannot create another one.
+		jffs2_iput(ds.node);
+		if (err == ENOERR)
+			err = EEXIST;
+	}
+	jffs2_iput(ds.dir);
+	return err;
 }
 
 // -------------------------------------------------------------------------
@@ -834,47 +782,32 @@ static int jffs2_ops_mkdir(cyg_mtab_entr
 
 static int jffs2_ops_rmdir(cyg_mtab_entry * mte, cyg_dir dir, const char *name)
 {
-        unsigned long hash;
-        struct qstr this;
-        unsigned int c;
-        const char *hashname;
-        jffs2_dirsearch ds;
-        int err;
-
-        D2(printf("jffs2_ops_rmdir\n"));
-
-        icache_evict((struct inode *) mte->root, (struct inode *) dir);
-
-        init_dirsearch(&ds, (struct inode *) dir, name);
-
-        err = jffs2_find(&ds);
+	jffs2_dirsearch ds;
+	int err;
 
-        if (err != ENOERR)
-                return err;
+	D2(printf("jffs2_ops_rmdir\n"));
 
-        // Check that this is actually a directory.
-        if (!S_ISDIR(ds.node->i_mode))
-                return EPERM;
+	init_dirsearch(&ds, (struct _inode *) dir, name);
 
-        // Delete the entry. 
-        hashname = ds.name;
-        this.name = hashname;
-        c = *(const unsigned char *) hashname;
+	err = jffs2_find(&ds);
 
-        hash = init_name_hash();
-        do {
-                hashname++;
-                hash = partial_name_hash(c, hash);
-                c = *(const unsigned char *) hashname;
-        } while (c && (c != '/'));
-        this.len = hashname - (const char *) this.name;
-        this.hash = end_name_hash(hash);
+	if (err != ENOERR) {
+		jffs2_iput(ds.dir);
+		return err;
+	}
 
-        err = jffs2_rmdir(ds.dir, ds.node, &this);
+	// Check that this is actually a directory.
+	if (!S_ISDIR(ds.node->i_mode)) {
+		jffs2_iput(ds.dir);
+		jffs2_iput(ds.node);
+		return EPERM;
+	}
 
-        return err;
+	err = jffs2_rmdir(ds.dir, ds.node, ds.name);
 
-        return ENOERR;
+	jffs2_iput(ds.dir);
+	jffs2_iput(ds.node);
+	return -err;
 }
 
 // -------------------------------------------------------------------------
@@ -882,96 +815,95 @@ static int jffs2_ops_rmdir(cyg_mtab_entr
 // Rename a file/dir.
 
 static int jffs2_ops_rename(cyg_mtab_entry * mte, cyg_dir dir1,
-                            const char *name1, cyg_dir dir2, const char *name2)
+			    const char *name1, cyg_dir dir2, const char *name2)
 {
-        unsigned long hash;
-        struct qstr this1, this2;
-        unsigned int c;
-        const char *hashname;
-        jffs2_dirsearch ds1, ds2;
-        int err;
-
-        D2(printf("jffs2_ops_rename\n"));
-
-        init_dirsearch(&ds1, (struct inode *) dir1, name1);
-
-        err = jffs2_find(&ds1);
-
-        if (err != ENOERR)
-                return err;
-
-        init_dirsearch(&ds2, (struct inode *) dir2, name2);
-
-        err = jffs2_find(&ds2);
-
-        // Allow through renames to non-existent objects.
-        if (ds2.last && err == ENOENT)
-                ds2.node = NULL, err = ENOERR;
-
-        if (err != ENOERR)
-                return err;
-
-        // Null rename, just return
-        if (ds1.node == ds2.node)
-                return ENOERR;
-
-        hashname = ds1.name;
-        this1.name = hashname;
-        c = *(const unsigned char *) hashname;
-
-        hash = init_name_hash();
-        do {
-                hashname++;
-                hash = partial_name_hash(c, hash);
-                c = *(const unsigned char *) hashname;
-        } while (c && (c != '/'));
-        this1.len = hashname - (const char *) this1.name;
-        this1.hash = end_name_hash(hash);
-
-        hashname = ds2.name;
-        this2.name = hashname;
-        c = *(const unsigned char *) hashname;
-
-        hash = init_name_hash();
-        do {
-                hashname++;
-                hash = partial_name_hash(c, hash);
-                c = *(const unsigned char *) hashname;
-        } while (c && (c != '/'));
-        this2.len = hashname - (const char *) this2.name;
-        this2.hash = end_name_hash(hash);
-
-        // First deal with any entry that is at the destination
-        if (ds2.node) {
-                // Check that we are renaming like-for-like
-
-                if (!S_ISDIR(ds1.node->i_mode) && S_ISDIR(ds2.node->i_mode))
-                        return EISDIR;
+	jffs2_dirsearch ds1, ds2;
+	int err;
 
-                if (S_ISDIR(ds1.node->i_mode) && !S_ISDIR(ds2.node->i_mode))
-                        return ENOTDIR;
+	D2(printf("jffs2_ops_rename\n"));
 
-                // Now delete the destination directory entry
+	init_dirsearch(&ds1, (struct _inode *) dir1, name1);
 
-                err = jffs2_unlink(ds2.dir, ds2.node, &this2);
-
-                if (err != 0)
-                        return err;
-
-        }
-        // Now we know that there is no clashing node at the destination,
-        // make a new direntry at the destination and delete the old entry
-        // at the source.
-
-        err = jffs2_rename(ds1.dir, ds1.node, &this1, ds2.dir, &this2);
-
-        // Update directory times
-        if (err == 0)
-                ds1.dir->i_ctime =
-                    ds1.dir->i_mtime =
-                    ds2.dir->i_ctime = ds2.dir->i_mtime = cyg_timestamp();
-
-        return err;
+	err = jffs2_find(&ds1);
+
+	if (err != ENOERR) {
+		jffs2_iput(ds1.dir);
+		return err;
+	}
+
+	init_dirsearch(&ds2, (struct _inode *) dir2, name2);
+
+	err = jffs2_find(&ds2);
+
+	// Allow through renames to non-existent objects.
+	if (ds2.last && err == ENOENT) {
+		ds2.node = NULL;
+		err = ENOERR;
+	}
+
+	if (err != ENOERR) {
+		jffs2_iput(ds1.dir);
+		jffs2_iput(ds1.node);
+		jffs2_iput(ds2.dir);
+		return err;
+	}
+
+	// Null rename, just return
+	if (ds1.node == ds2.node) {
+		err = ENOERR;
+		goto out;
+	}
+
+	// First deal with any entry that is at the destination
+	if (ds2.node) {
+		// Check that we are renaming like-for-like
+
+		if (!S_ISDIR(ds1.node->i_mode) && S_ISDIR(ds2.node->i_mode)) {
+			err = EISDIR;
+			goto out;
+		}
+
+		if (S_ISDIR(ds1.node->i_mode) && !S_ISDIR(ds2.node->i_mode)) {
+			err = ENOTDIR;
+			goto out;
+		}
+
+		// Now delete the destination directory entry
+		/* Er, what happened to atomicity of rename()? */
+		err = -jffs2_unlink(ds2.dir, ds2.node, ds2.name);
+
+		if (err != 0)
+			goto out;
+
+	}
+	// Now we know that there is no clashing node at the destination,
+	// make a new direntry at the destination and delete the old entry
+	// at the source.
+
+	err = -jffs2_rename(ds1.dir, ds1.node, ds1.name, ds2.dir, ds2.name);
+
+	// Update directory times
+	if (!err)
+		ds1.dir->i_ctime =
+		    ds1.dir->i_mtime =
+		    ds2.dir->i_ctime = ds2.dir->i_mtime = cyg_timestamp();
+ out:
+	jffs2_iput(ds1.dir);
+	jffs2_iput(ds1.node);
+	if (S_ISDIR(ds1.node->i_mode)) {
+		/* Renamed a directory to elsewhere... so fix up its
+		   i_parent pointer and the i_counts of its old and
+		   new parents. */
+		jffs2_iput(ds1.node->i_parent);
+		ds1.node->i_parent = ds2.dir;
+		/* We effectively increase its use count by not... */
+	} else {
+		jffs2_iput(ds2.dir); /* ... doing this */
+	}
+	if (ds2.node)
+		jffs2_iput(ds2.node);
+ 
+	return -err;
 }
 
 // -------------------------------------------------------------------------
@@ -979,66 +911,67 @@ static int jffs2_ops_rename(cyg_mtab_ent
 // Make a new directory entry for a file.
 
 static int jffs2_ops_link(cyg_mtab_entry * mte, cyg_dir dir1, const char *name1,
-                          cyg_dir dir2, const char *name2, int type)
+			  cyg_dir dir2, const char *name2, int type)
 {
-        unsigned long hash;
-        struct qstr this;
-        unsigned int c;
-        const char *hashname;
-        jffs2_dirsearch ds1, ds2;
-        int err;
-
-        D2(printf("jffs2_ops_link\n"));
+	jffs2_dirsearch ds1, ds2;
+	int err;
 
-        // Only do hard links for now in this filesystem
-        if (type != CYG_FSLINK_HARD)
-                return EINVAL;
+	D2(printf("jffs2_ops_link\n"));
 
-        init_dirsearch(&ds1, (struct inode *) dir1, name1);
+	// Only do hard links for now in this filesystem
+	if (type != CYG_FSLINK_HARD)
+		return EINVAL;
 
-        err = jffs2_find(&ds1);
+	init_dirsearch(&ds1, (struct _inode *) dir1, name1);
 
-        if (err != ENOERR)
-                return err;
+	err = jffs2_find(&ds1);
 
-        init_dirsearch(&ds2, (struct inode *) dir2, name2);
+	if (err != ENOERR) {
+		jffs2_iput(ds1.dir);
+		return err;
+	}
 
-        err = jffs2_find(&ds2);
+	init_dirsearch(&ds2, (struct _inode *) dir2, name2);
 
-        // Don't allow links to existing objects
-        if (err == ENOERR)
-                return EEXIST;
+	err = jffs2_find(&ds2);
 
-        // Allow through links to non-existing terminal objects
-        if (ds2.last && err == ENOENT)
-                ds2.node = NULL, err = ENOERR;
+	// Don't allow links to existing objects
+	if (err == ENOERR) {
+		jffs2_iput(ds1.dir);
+		jffs2_iput(ds1.node);
+		jffs2_iput(ds2.dir);
+		jffs2_iput(ds2.node);
+		return EEXIST;
+	}
 
-        if (err != ENOERR)
-                return err;
+	// Allow through links to non-existing terminal objects
+	if (ds2.last && err == ENOENT) {
+		jffs2_iput(ds2.node);
+		ds2.node = NULL;
+		err = ENOERR;
+	}
 
-        // Now we know that there is no existing node at the destination,
-        // make a new direntry at the destination.
+	if (err != ENOERR) {
+		jffs2_iput(ds1.dir);
+		jffs2_iput(ds1.node);
+		jffs2_iput(ds2.dir);
+		return err;
+	}
 
-        hashname = ds2.name;
-        this.name = hashname;
-        c = *(const unsigned char *) hashname;
+	// Now we know that there is no existing node at the destination,
+	// make a new direntry at the destination.
 
-        hash = init_name_hash();
-        do {
-                hashname++;
-                hash = partial_name_hash(c, hash);
-                c = *(const unsigned char *) hashname;
-        } while (c && (c != '/'));
-        this.len = hashname - (const char *) this.name;
-        this.hash = end_name_hash(hash);
+	err = jffs2_link(ds1.node, ds2.dir, ds2.name);
 
-        err = jffs2_link(ds1.node, ds2.dir, &this);
+	if (err == 0)
+		ds1.node->i_ctime =
+		    ds2.dir->i_ctime = ds2.dir->i_mtime = cyg_timestamp();
 
-        if (err == 0)
-                ds1.node->i_ctime =
-                    ds2.dir->i_ctime = ds2.dir->i_mtime = cyg_timestamp();
+	jffs2_iput(ds1.dir);
+	jffs2_iput(ds1.node);
+	jffs2_iput(ds2.dir);
 
-        return err;
+	return -err;
 }
 
 // -------------------------------------------------------------------------
@@ -1046,38 +979,38 @@ static int jffs2_ops_link(cyg_mtab_entry
 // Open a directory for reading.
 
 static int jffs2_opendir(cyg_mtab_entry * mte, cyg_dir dir, const char *name,
-                         cyg_file * file)
+			 cyg_file * file)
 {
-        jffs2_dirsearch ds;
-        int err;
-
-        D2(printf("jffs2_opendir\n"));
+	jffs2_dirsearch ds;
+	int err;
 
-        icache_evict((struct inode *) mte->root, (struct inode *) dir);
+	D2(printf("jffs2_opendir\n"));
 
-        init_dirsearch(&ds, (struct inode *) dir, name);
+	init_dirsearch(&ds, (struct _inode *) dir, name);
 
-        err = jffs2_find(&ds);
+	err = jffs2_find(&ds);
 
-        if (err != ENOERR)
-                return err;
+	jffs2_iput(ds.dir);
 
-        // check it is really a directory.
-        if (!S_ISDIR(ds.node->i_mode))
-                return ENOTDIR;
+	if (err != ENOERR)
+		return err;
 
-        ds.node->i_count++;     // Count successful open
+	// check it is really a directory.
+	if (!S_ISDIR(ds.node->i_mode)) {
+		jffs2_iput(ds.node);
+		return ENOTDIR;
+	}
 
-        // Initialize the file object, setting the f_ops field to a
-        // special set of file ops.
+	// Initialize the file object, setting the f_ops field to a
+	// special set of file ops.
 
-        file->f_type = CYG_FILE_TYPE_FILE;
-        file->f_ops = &jffs2_dirops;
-        file->f_offset = 0;
-        file->f_data = (CYG_ADDRWORD) ds.node;
-        file->f_xops = 0;
+	file->f_type = CYG_FILE_TYPE_FILE;
+	file->f_ops = &jffs2_dirops;
+	file->f_offset = 0;
+	file->f_data = (CYG_ADDRWORD) ds.node;
+	file->f_xops = 0;
 
-        return ENOERR;
+	return ENOERR;
 
 }
 
@@ -1086,48 +1019,43 @@ static int jffs2_opendir(cyg_mtab_entry 
 // Change directory support.
 
 static int jffs2_chdir(cyg_mtab_entry * mte, cyg_dir dir, const char *name,
-                       cyg_dir * dir_out)
+		       cyg_dir * dir_out)
 {
-        D2(printf("jffs2_chdir\n"));
+	D2(printf("jffs2_chdir\n"));
 
-        if (dir_out != NULL) {
-                // This is a request to get a new directory pointer in
-                // *dir_out.
+	if (dir_out != NULL) {
+		// This is a request to get a new directory pointer in
+		// *dir_out.
 
-                jffs2_dirsearch ds;
-                int err;
+		jffs2_dirsearch ds;
+		int err;
 
-                icache_evict((struct inode *) mte->root, (struct inode *) dir);
+		init_dirsearch(&ds, (struct _inode *) dir, name);
 
-                init_dirsearch(&ds, (struct inode *) dir, name);
+		err = jffs2_find(&ds);
+		jffs2_iput(ds.dir);
 
-                err = jffs2_find(&ds);
+		if (err != ENOERR)
+			return err;
 
-                if (err != ENOERR)
-                        return err;
+		// check it is a directory
+		if (!S_ISDIR(ds.node->i_mode))
+			return ENOTDIR;
 
-                // check it is a directory
-                if (!S_ISDIR(ds.node->i_mode))
-                        return ENOTDIR;
+		// Pass it out
+		*dir_out = (cyg_dir) ds.node;
+	} else {
+		// If no output dir is required, this means that the mte and
+		// dir arguments are the current cdir setting and we should
+		// forget this fact.
 
-                // Increment ref count to keep this directory in existance
-                // while it is the current cdir.
-                ds.node->i_count++;
+		struct _inode *node = (struct _inode *) dir;
 
-                // Pass it out
-                *dir_out = (cyg_dir) ds.node;
-        } else {
-                // If no output dir is required, this means that the mte and
-                // dir arguments are the current cdir setting and we should
-                // forget this fact.
+		// Just decrement directory reference count.
+		jffs2_iput(node);
+	}
 
-                struct inode *node = (struct inode *) dir;
-
-                // Just decrement directory reference count.
-                dec_refcnt(node);
-        }
-
-        return ENOERR;
+	return ENOERR;
 }
 
 // -------------------------------------------------------------------------
@@ -1135,37 +1063,36 @@ static int jffs2_chdir(cyg_mtab_entry * 
 // Get struct stat info for named object.
 
 static int jffs2_stat(cyg_mtab_entry * mte, cyg_dir dir, const char *name,
-                      struct stat *buf)
+		      struct stat *buf)
 {
-        jffs2_dirsearch ds;
-        int err;
+	jffs2_dirsearch ds;
+	int err;
 
-        D2(printf("jffs2_stat\n"));
+	D2(printf("jffs2_stat\n"));
 
-        icache_evict((struct inode *) mte->root, (struct inode *) dir);
+	init_dirsearch(&ds, (struct _inode *) dir, name);
 
-        init_dirsearch(&ds, (struct inode *) dir, name);
+	err = jffs2_find(&ds);
+	jffs2_iput(ds.dir);
 
-        err = jffs2_find(&ds);
+	if (err != ENOERR)
+		return err;
 
-        if (err != ENOERR)
-                return err;
+	// Fill in the status
+	buf->st_mode = ds.node->i_mode;
+	buf->st_ino = ds.node->i_ino;
+	buf->st_dev = 0;
+	buf->st_nlink = ds.node->i_nlink;
+	buf->st_uid = 0;
+	buf->st_gid = 0;
+	buf->st_size = ds.node->i_size;
+	buf->st_atime = ds.node->i_atime;
+	buf->st_mtime = ds.node->i_mtime;
+	buf->st_ctime = ds.node->i_ctime;
 
-        // Fill in the status
-        buf->st_mode = ds.node->i_mode;
-        buf->st_ino = ds.node->i_ino;
-        buf->st_dev = 0;
-        buf->st_nlink = ds.node->i_nlink;
-        buf->st_uid = 0;
-        buf->st_gid = 0;
-        buf->st_size = ds.node->i_size;
-        buf->st_atime = ds.node->i_atime;
-        buf->st_mtime = ds.node->i_mtime;
-        buf->st_ctime = ds.node->i_ctime;
+	jffs2_iput(ds.node);
 
-        return err;
-
-        return ENOERR;
+	return ENOERR;
 }
 
 // -------------------------------------------------------------------------
@@ -1173,33 +1100,32 @@ static int jffs2_stat(cyg_mtab_entry * m
 // Getinfo. Currently only support pathconf().
 
 static int jffs2_getinfo(cyg_mtab_entry * mte, cyg_dir dir, const char *name,
-                         int key, void *buf, int len)
+			 int key, void *buf, int len)
 {
-        jffs2_dirsearch ds;
-        int err;
-
-        D2(printf("jffs2_getinfo\n"));
+	jffs2_dirsearch ds;
+	int err;
 
-        icache_evict((struct inode *) mte->root, (struct inode *) dir);
+	D2(printf("jffs2_getinfo\n"));
 
-        init_dirsearch(&ds, (struct inode *) dir, name);
+	init_dirsearch(&ds, (struct _inode *) dir, name);
 
-        err = jffs2_find(&ds);
+	err = jffs2_find(&ds);
+	jffs2_iput(ds.dir);
 
-        if (err != ENOERR)
-                return err;
+	if (err != ENOERR)
+		return err;
 
-        switch (key) {
-        case FS_INFO_CONF:
-                err = jffs2_pathconf(ds.node, (struct cyg_pathconf_info *) buf);
-                break;
+	switch (key) {
+	case FS_INFO_CONF:
+		err = jffs2_pathconf(ds.node, (struct cyg_pathconf_info *) buf);
+		break;
 
-        default:
-                err = EINVAL;
-        }
-        return err;
+	default:
+		err = EINVAL;
+	}
 
-        return ENOERR;
+	jffs2_iput(ds.node);
+	return err;
 }
 
 // -------------------------------------------------------------------------
@@ -1207,13 +1133,13 @@ static int jffs2_getinfo(cyg_mtab_entry 
 // Setinfo. Nothing to support here at present.
 
 static int jffs2_setinfo(cyg_mtab_entry * mte, cyg_dir dir, const char *name,
-                         int key, void *buf, int len)
+			 int key, void *buf, int len)
 {
-        // No setinfo keys supported at present
+	// No setinfo keys supported at present
 
-        D2(printf("jffs2_setinfo\n"));
+	D2(printf("jffs2_setinfo\n"));
 
-        return EINVAL;
+	return EINVAL;
 }
 
 //==========================================================================
@@ -1225,150 +1151,185 @@ static int jffs2_setinfo(cyg_mtab_entry 
 
 static int jffs2_fo_read(struct CYG_FILE_TAG *fp, struct CYG_UIO_TAG *uio)
 {
-        struct inode *inode = (struct inode *) fp->f_data;
-        struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode);
-        struct jffs2_sb_info *c = JFFS2_SB_INFO(inode->i_sb);
-        int i;
-        ssize_t resid = uio->uio_resid;
-        off_t pos = fp->f_offset;
-
-        down(&f->sem);
-
-        // Loop over the io vectors until there are none left
-        for (i = 0; i < uio->uio_iovcnt && pos < inode->i_size; i++) {
-                int ret;
-                cyg_iovec *iov = &uio->uio_iov[i];
-                off_t len = min(iov->iov_len, inode->i_size - pos);
-
-                D2(printf("jffs2_fo_read inode size %d\n", inode->i_size));
-
-                ret =
-                    jffs2_read_inode_range(c, f,
-                                           (unsigned char *) iov->iov_base, pos,
-                                           len);
-                if (ret) {
-                        D1(printf
-                           ("jffs2_fo_read(): read_inode_range failed %d\n",
-                            ret));
-                        uio->uio_resid = resid;
-                        up(&f->sem);
-                        return -ret;
-                }
-                resid -= len;
-                pos += len;
-        }
-
-        // We successfully read some data, update the node's access time
-        // and update the file offset and transfer residue.
+	struct _inode *inode = (struct _inode *) fp->f_data;
+	struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode);
+	struct jffs2_sb_info *c = JFFS2_SB_INFO(inode->i_sb);
+	int i;
+	ssize_t resid = uio->uio_resid;
+	off_t pos = fp->f_offset;
+
+	down(&f->sem);
+
+	// Loop over the io vectors until there are none left
+	for (i = 0; i < uio->uio_iovcnt && pos < inode->i_size; i++) {
+		int ret;
+		cyg_iovec *iov = &uio->uio_iov[i];
+		off_t len = min(iov->iov_len, inode->i_size - pos);
+
+		D2(printf("jffs2_fo_read inode size %d\n", inode->i_size));
+
+		ret =
+		    jffs2_read_inode_range(c, f,
+					   (unsigned char *) iov->iov_base, pos,
+					   len);
+		if (ret) {
+			D1(printf
+			   ("jffs2_fo_read(): read_inode_range failed %d\n",
+			    ret));
+			uio->uio_resid = resid;
+			up(&f->sem);
+			return -ret;
+		}
+		resid -= len;
+		pos += len;
+	}
+
+	// We successfully read some data, update the node's access time
+	// and update the file offset and transfer residue.
 
-        inode->i_atime = cyg_timestamp();
+	inode->i_atime = cyg_timestamp();
 
-        uio->uio_resid = resid;
-        fp->f_offset = pos;
+	uio->uio_resid = resid;
+	fp->f_offset = pos;
 
-        up(&f->sem);
+	up(&f->sem);
 
-        return ENOERR;
+	return ENOERR;
 }
 
+
 // -------------------------------------------------------------------------
 // jffs2_fo_write()
 // Write data to file.
+static int jffs2_extend_file (struct _inode *inode, struct jffs2_raw_inode *ri,
+		       unsigned long offset)
+{
+	struct jffs2_sb_info *c = JFFS2_SB_INFO(inode->i_sb);
+	struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode);
+	struct jffs2_full_dnode *fn;
+	uint32_t phys_ofs, alloc_len;
+	int ret = 0;
+
+	/* Make new hole frag from old EOF to new page */
+	D1(printk(KERN_DEBUG "Writing new hole frag 0x%x-0x%x between current EOF and new page\n",
+		  (unsigned int)inode->i_size, offset));
+
+	ret = jffs2_reserve_space(c, sizeof(ri), &phys_ofs, &alloc_len, ALLOC_NORMAL);
+	if (ret)
+		return ret;
+
+	down(&f->sem);
+
+	ri->magic = cpu_to_je16(JFFS2_MAGIC_BITMASK);
+	ri->nodetype = cpu_to_je16(JFFS2_NODETYPE_INODE);
+	ri->totlen = cpu_to_je32(sizeof(ri));
+	ri->hdr_crc = cpu_to_je32(crc32(0, &ri, sizeof(struct jffs2_unknown_node)-4));
+
+	ri->version = cpu_to_je32(++f->highest_version);
+	ri->isize = cpu_to_je32(max((uint32_t)inode->i_size, offset));
+
+	ri->offset = cpu_to_je32(inode->i_size);
+	ri->dsize = cpu_to_je32(offset - inode->i_size);
+	ri->csize = cpu_to_je32(0);
+	ri->compr = JFFS2_COMPR_ZERO;
+	ri->node_crc = cpu_to_je32(crc32(0, &ri, sizeof(ri)-8));
+	ri->data_crc = cpu_to_je32(0);
+		
+	fn = jffs2_write_dnode(c, f, ri, NULL, 0, phys_ofs, ALLOC_NORMAL);
+	jffs2_complete_reservation(c);
+	if (IS_ERR(fn)) {
+		ret = PTR_ERR(fn);
+		up(&f->sem);
+		return ret;
+	}
+	ret = jffs2_add_full_dnode_to_inode(c, f, fn);
+	if (f->metadata) {
+		jffs2_mark_node_obsolete(c, f->metadata->raw);
+		jffs2_free_full_dnode(f->metadata);
+		f->metadata = NULL;
+	}
+	if (ret) {
+		D1(printk(KERN_DEBUG "Eep. add_full_dnode_to_inode() failed in prepare_write, returned %d\n", ret));
+		jffs2_mark_node_obsolete(c, fn->raw);
+		jffs2_free_full_dnode(fn);
+		up(&f->sem);
+		return ret;
+	}
+	inode->i_size = offset;
+	up(&f->sem);
+	return 0;
+}
 
 static int jffs2_fo_write(struct CYG_FILE_TAG *fp, struct CYG_UIO_TAG *uio)
 {
-        struct page write_page;
-        off_t page_start_pos;
-        struct inode *node = (struct inode *) fp->f_data;
-        off_t pos = fp->f_offset;
-        ssize_t resid = uio->uio_resid;
-        int i;
-
-        memset(&read_write_buffer, 0, PAGE_CACHE_SIZE);
-        write_page.virtual = &read_write_buffer;
-
-        // If the APPEND mode bit was supplied, force all writes to
-        // the end of the file.
-        if (fp->f_flag & CYG_FAPPEND)
-                pos = fp->f_offset = node->i_size;
-
-        // Check that pos is within current file size, or at the very end.
-        if (pos < 0 || pos > node->i_size)
-                return EINVAL;
-
-        // Now loop over the iovecs until they are all done, or
-        // we get an error.
-        for (i = 0; i < uio->uio_iovcnt; i++) {
-                cyg_iovec *iov = &uio->uio_iov[i];
-                char *buf = (char *) iov->iov_base;
-                off_t len = iov->iov_len;
-
-                // loop over the vector writing it to the file until it has
-                // all been done.
-                while (len > 0) {
-                        //cyg_uint8 *fbuf;
-                        //size_t bsize;
-                        size_t writtenlen;
-                        off_t l = len;
-                        int err;
-
-                        write_page.index = 0;
-
-                        page_start_pos = pos;
-                        while (page_start_pos >= (PAGE_CACHE_SIZE)) {
-                                write_page.index++;
-                                page_start_pos -= PAGE_CACHE_SIZE;
-                        }
-
-                        if (l > PAGE_CACHE_SIZE - page_start_pos)
-                                l = PAGE_CACHE_SIZE - page_start_pos;
-
-                        D2(printf
-                           ("jffs2_fo_write write_page.index %d\n",
-                            write_page.index));
-                        D2(printf
-                           ("jffs2_fo_write page_start_pos %d\n",
-                            page_start_pos));
-                        D2(printf("jffs2_fo_write transfer size %d\n", l));
-
-                        err =
-                            jffs2_prepare_write(node, &write_page,
-                                                page_start_pos,
-                                                page_start_pos + l);
-
-                        if (err != 0)
-                                return err;
-
-                        // copy data in
-                        memcpy(&read_write_buffer[page_start_pos], buf, l);
-
-                        writtenlen =
-                            jffs2_commit_write(node, &write_page,
-                                               page_start_pos,
-                                               page_start_pos + l);
-
-                        if (writtenlen != l)
-                                return ENOSPC;
-
-                        // Update working vars
-                        len -= l;
-                        buf += l;
-                        pos += l;
-                        resid -= l;
-                }
-        }
+	struct _inode *inode = (struct _inode *) fp->f_data;
+	off_t pos = fp->f_offset;
+	ssize_t resid = uio->uio_resid;
+	struct jffs2_raw_inode ri;
+	struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode);
+	struct jffs2_sb_info *c = JFFS2_SB_INFO(inode->i_sb);
+	int i;
+
+	// If the APPEND mode bit was supplied, force all writes to
+	// the end of the file.
+	if (fp->f_flag & CYG_FAPPEND)
+		pos = fp->f_offset = inode->i_size;
+
+	if (pos < 0)
+		return EINVAL;
+
+	memset(&ri, 0, sizeof(ri));
+
+	ri.ino = cpu_to_je32(f->inocache->ino);
+	ri.mode = cpu_to_jemode(inode->i_mode);
+	ri.uid = cpu_to_je16(inode->i_uid);
+	ri.gid = cpu_to_je16(inode->i_gid);
+	ri.atime = ri.ctime = ri.mtime = cpu_to_je32(cyg_timestamp());
+
+	if (pos > inode->i_size) {
+		int err;
+		ri.version = cpu_to_je32(++f->highest_version);
+		err = jffs2_extend_file(inode, &ri, pos);
+		if (err)
+			return -err;
+	}
+
+	// Now loop over the iovecs until they are all done, or
+	// we get an error.
+	for (i = 0; i < uio->uio_iovcnt; i++) {
+		cyg_iovec *iov = &uio->uio_iov[i];
+		char *buf = (char *) iov->iov_base;
+		off_t len = iov->iov_len;
+
+		size_t writtenlen;
+		int err;
+
+		D2(printf("jffs2_fo_write page_start_pos %d\n", pos));
+		D2(printf("jffs2_fo_write transfer size %d\n", l));
+
+		err = jffs2_write_inode_range(c, f, &ri, buf,
+					      pos, len, &writtenlen);
+		if (err)
+			return -err;
+		
+		if (writtenlen != len)
+			return ENOSPC;
+
+		pos += len;
+		resid -= len;
+	}
+
+	// We wrote some data successfully, update the modified and access
+	// times of the inode, increase its size appropriately, and update
+	// the file offset and transfer residue.
+	inode->i_mtime = inode->i_ctime = je32_to_cpu(ri.mtime);
+	if (pos > inode->i_size)
+		inode->i_size = pos;
 
-        // We wrote some data successfully, update the modified and access
-        // times of the node, increase its size appropriately, and update
-        // the file offset and transfer residue.
-        node->i_mtime = node->i_ctime = cyg_timestamp();
-        if (pos > node->i_size)
-                node->i_size = pos;
+	uio->uio_resid = resid;
+	fp->f_offset = pos;
 
-        uio->uio_resid = resid;
-        fp->f_offset = pos;
-
-        return ENOERR;
+	return ENOERR;
 }
 
 // -------------------------------------------------------------------------
@@ -1377,39 +1338,39 @@ static int jffs2_fo_write(struct CYG_FIL
 
 static int jffs2_fo_lseek(struct CYG_FILE_TAG *fp, off_t * apos, int whence)
 {
-        struct inode *node = (struct inode *) fp->f_data;
-        off_t pos = *apos;
-
-        D2(printf("jffs2_fo_lseek\n"));
-
-        switch (whence) {
-        case SEEK_SET:
-                // Pos is already where we want to be.
-                break;
-
-        case SEEK_CUR:
-                // Add pos to current offset.
-                pos += fp->f_offset;
-                break;
-
-        case SEEK_END:
-                // Add pos to file size.
-                pos += node->i_size;
-                break;
+	struct _inode *node = (struct _inode *) fp->f_data;
+	off_t pos = *apos;
 
-        default:
-                return EINVAL;
-        }
+	D2(printf("jffs2_fo_lseek\n"));
 
-        // Check that pos is still within current file size, or at the
-        // very end.
-        if (pos < 0 || pos > node->i_size)
-                return EINVAL;
+	switch (whence) {
+	case SEEK_SET:
+		// Pos is already where we want to be.
+		break;
+
+	case SEEK_CUR:
+		// Add pos to current offset.
+		pos += fp->f_offset;
+		break;
+
+	case SEEK_END:
+		// Add pos to file size.
+		pos += node->i_size;
+		break;
+
+	default:
+		return EINVAL;
+	}
+
+	// Check that pos is still within current file size, or at the
+	// very end.
+	if (pos < 0 || pos > node->i_size)
+		return EINVAL;
 
-        // All OK, set fp offset and return new position.
-        *apos = fp->f_offset = pos;
+	// All OK, set fp offset and return new position.
+	*apos = fp->f_offset = pos;
 
-        return ENOERR;
+	return ENOERR;
 }
 
 // -------------------------------------------------------------------------
@@ -1417,13 +1378,13 @@ static int jffs2_fo_lseek(struct CYG_FIL
 // Handle ioctls. Currently none are defined.
 
 static int jffs2_fo_ioctl(struct CYG_FILE_TAG *fp, CYG_ADDRWORD com,
-                          CYG_ADDRWORD data)
+			  CYG_ADDRWORD data)
 {
-        // No Ioctls currenly defined.
+	// No Ioctls currenly defined.
 
-        D2(printf("jffs2_fo_ioctl\n"));
+	D2(printf("jffs2_fo_ioctl\n"));
 
-        return EINVAL;
+	return EINVAL;
 }
 
 // -------------------------------------------------------------------------
@@ -1432,12 +1393,12 @@ static int jffs2_fo_ioctl(struct CYG_FIL
 
 static int jffs2_fo_fsync(struct CYG_FILE_TAG *fp, int mode)
 {
-        // Data is always permanently where it belongs, nothing to do
-        // here.
+	// Data is always permanently where it belongs, nothing to do
+	// here.
 
-        D2(printf("jffs2_fo_fsync\n"));
+	D2(printf("jffs2_fo_fsync\n"));
 
-        return ENOERR;
+	return ENOERR;
 }
 
 // -------------------------------------------------------------------------
@@ -1447,15 +1408,15 @@ static int jffs2_fo_fsync(struct CYG_FIL
 
 static int jffs2_fo_close(struct CYG_FILE_TAG *fp)
 {
-        struct inode *node = (struct inode *) fp->f_data;
+	struct _inode *node = (struct _inode *) fp->f_data;
 
-        D2(printf("jffs2_fo_close\n"));
+	D2(printf("jffs2_fo_close\n"));
 
-        dec_refcnt(node);
+	jffs2_iput(node);
 
-        fp->f_data = 0;         // zero data pointer
+	fp->f_data = 0;		// zero data pointer
 
-        return ENOERR;
+	return ENOERR;
 }
 
 // -------------------------------------------------------------------------
@@ -1464,23 +1425,23 @@ static int jffs2_fo_close(struct CYG_FIL
 
 static int jffs2_fo_fstat(struct CYG_FILE_TAG *fp, struct stat *buf)
 {
-        struct inode *node = (struct inode *) fp->f_data;
+	struct _inode *node = (struct _inode *) fp->f_data;
 
-        D2(printf("jffs2_fo_fstat\n"));
+	D2(printf("jffs2_fo_fstat\n"));
 
-        // Fill in the status
-        buf->st_mode = node->i_mode;
-        buf->st_ino = node->i_ino;
-        buf->st_dev = 0;
-        buf->st_nlink = node->i_nlink;
-        buf->st_uid = 0;
-        buf->st_gid = 0;
-        buf->st_size = node->i_size;
-        buf->st_atime = node->i_atime;
-        buf->st_mtime = node->i_mtime;
-        buf->st_ctime = node->i_ctime;
+	// Fill in the status
+	buf->st_mode = node->i_mode;
+	buf->st_ino = node->i_ino;
+	buf->st_dev = 0;
+	buf->st_nlink = node->i_nlink;
+	buf->st_uid = 0;
+	buf->st_gid = 0;
+	buf->st_size = node->i_size;
+	buf->st_atime = node->i_atime;
+	buf->st_mtime = node->i_mtime;
+	buf->st_ctime = node->i_ctime;
 
-        return ENOERR;
+	return ENOERR;
 }
 
 // -------------------------------------------------------------------------
@@ -1488,24 +1449,24 @@ static int jffs2_fo_fstat(struct CYG_FIL
 // Get info. Currently only supports fpathconf().
 
 static int jffs2_fo_getinfo(struct CYG_FILE_TAG *fp, int key, void *buf,
-                            int len)
+			    int len)
 {
-        struct inode *node = (struct inode *) fp->f_data;
-        int err;
+	struct _inode *node = (struct _inode *) fp->f_data;
+	int err;
 
-        D2(printf("jffs2_fo_getinfo\n"));
+	D2(printf("jffs2_fo_getinfo\n"));
 
-        switch (key) {
-        case FS_INFO_CONF:
-                err = jffs2_pathconf(node, (struct cyg_pathconf_info *) buf);
-                break;
+	switch (key) {
+	case FS_INFO_CONF:
+		err = jffs2_pathconf(node, (struct cyg_pathconf_info *) buf);
+		break;
 
-        default:
-                err = EINVAL;
-        }
-        return err;
+	default:
+		err = EINVAL;
+	}
+	return err;
 
-        return ENOERR;
+	return ENOERR;
 }
 
 // -------------------------------------------------------------------------
@@ -1513,13 +1474,13 @@ static int jffs2_fo_getinfo(struct CYG_F
 // Set info. Nothing supported here.
 
 static int jffs2_fo_setinfo(struct CYG_FILE_TAG *fp, int key, void *buf,
-                            int len)
+			    int len)
 {
-        // No setinfo key supported at present
+	// No setinfo key supported at present
 
-        D2(printf("jffs2_fo_setinfo\n"));
+	D2(printf("jffs2_fo_setinfo\n"));
 
-        return ENOERR;
+	return ENOERR;
 }
 
 //==========================================================================
@@ -1531,83 +1492,83 @@ static int jffs2_fo_setinfo(struct CYG_F
 
 static __inline void filldir(char *nbuf, int nlen, const char *name, int namlen)
 {
-        int len = nlen < namlen ? nlen : namlen;
-        memcpy(nbuf, name, len);
-        nbuf[len] = '\0';
+	int len = nlen < namlen ? nlen : namlen;
+	memcpy(nbuf, name, len);
+	nbuf[len] = '\0';
 }
 
 static int jffs2_fo_dirread(struct CYG_FILE_TAG *fp, struct CYG_UIO_TAG *uio)
 {
-        struct inode *d_inode = (struct inode *) fp->f_data;
-        struct dirent *ent = (struct dirent *) uio->uio_iov[0].iov_base;
-        char *nbuf = ent->d_name;
-        int nlen = sizeof (ent->d_name) - 1;
-        off_t len = uio->uio_iov[0].iov_len;
-        struct jffs2_inode_info *f;
-        struct jffs2_sb_info *c;
-        struct inode *inode = d_inode;
-        struct jffs2_full_dirent *fd;
-        unsigned long offset, curofs;
-        int found = 1;
-
-        if (len < sizeof (struct dirent))
-                return EINVAL;
-
-        D1(printk
-           (KERN_DEBUG "jffs2_readdir() for dir_i #%lu\n", d_inode->i_ino));
-
-        f = JFFS2_INODE_INFO(inode);
-        c = JFFS2_SB_INFO(inode->i_sb);
-
-        offset = fp->f_offset;
-
-        if (offset == 0) {
-                D1(printk
-                   (KERN_DEBUG "Dirent 0: \".\", ino #%lu\n", inode->i_ino));
-                filldir(nbuf, nlen, ".", 1);
-                goto out;
-        }
-        if (offset == 1) {
-                filldir(nbuf, nlen, "..", 2);
-                goto out;
-        }
-
-        curofs = 1;
-        down(&f->sem);
-        for (fd = f->dents; fd; fd = fd->next) {
-
-                curofs++;
-                /* First loop: curofs = 2; offset = 2 */
-                if (curofs < offset) {
-                        D2(printk
-                           (KERN_DEBUG
-                            "Skipping dirent: \"%s\", ino #%u, type %d, because curofs %ld < offset %ld\n",
-                            fd->name, fd->ino, fd->type, curofs, offset));
-                        continue;
-                }
-                if (!fd->ino) {
-                        D2(printk
-                           (KERN_DEBUG "Skipping deletion dirent \"%s\"\n",
-                            fd->name));
-                        offset++;
-                        continue;
-                }
-                D2(printk
-                   (KERN_DEBUG "Dirent %ld: \"%s\", ino #%u, type %d\n", offset,
-                    fd->name, fd->ino, fd->type));
-                filldir(nbuf, nlen, fd->name, strlen(fd->name));
-                goto out_sem;
-        }
-        /* Reached the end of the directory */
-        found = 0;
+	struct _inode *d_inode = (struct _inode *) fp->f_data;
+	struct dirent *ent = (struct dirent *) uio->uio_iov[0].iov_base;
+	char *nbuf = ent->d_name;
+	int nlen = sizeof (ent->d_name) - 1;
+	off_t len = uio->uio_iov[0].iov_len;
+	struct jffs2_inode_info *f;
+	struct jffs2_sb_info *c;
+	struct _inode *inode = d_inode;
+	struct jffs2_full_dirent *fd;
+	unsigned long offset, curofs;
+	int found = 1;
+
+	if (len < sizeof (struct dirent))
+		return EINVAL;
+
+	D1(printk
+	   (KERN_DEBUG "jffs2_readdir() for dir_i #%lu\n", d_inode->i_ino));
+
+	f = JFFS2_INODE_INFO(inode);
+	c = JFFS2_SB_INFO(inode->i_sb);
+
+	offset = fp->f_offset;
+
+	if (offset == 0) {
+		D1(printk
+		   (KERN_DEBUG "Dirent 0: \".\", ino #%lu\n", inode->i_ino));
+		filldir(nbuf, nlen, ".", 1);
+		goto out;
+	}
+	if (offset == 1) {
+		filldir(nbuf, nlen, "..", 2);
+		goto out;
+	}
+
+	curofs = 1;
+	down(&f->sem);
+	for (fd = f->dents; fd; fd = fd->next) {
+
+		curofs++;
+		/* First loop: curofs = 2; offset = 2 */
+		if (curofs < offset) {
+			D2(printk
+			   (KERN_DEBUG
+			    "Skipping dirent: \"%s\", ino #%u, type %d, because curofs %ld < offset %ld\n",
+			    fd->name, fd->ino, fd->type, curofs, offset));
+			continue;
+		}
+		if (!fd->ino) {
+			D2(printk
+			   (KERN_DEBUG "Skipping deletion dirent \"%s\"\n",
+			    fd->name));
+			offset++;
+			continue;
+		}
+		D2(printk
+		   (KERN_DEBUG "Dirent %ld: \"%s\", ino #%u, type %d\n", offset,
+		    fd->name, fd->ino, fd->type));
+		filldir(nbuf, nlen, fd->name, strlen(fd->name));
+		goto out_sem;
+	}
+	/* Reached the end of the directory */
+	found = 0;
       out_sem:
-        up(&f->sem);
+	up(&f->sem);
       out:
-        fp->f_offset = ++offset;
-        if (found) {
-                uio->uio_resid -= sizeof (struct dirent);
-        }
-        return ENOERR;
+	fp->f_offset = ++offset;
+	if (found) {
+		uio->uio_resid -= sizeof (struct dirent);
+	}
+	return ENOERR;
 }
 
 // -------------------------------------------------------------------------
@@ -1616,16 +1577,16 @@ static int jffs2_fo_dirread(struct CYG_F
 
 static int jffs2_fo_dirlseek(struct CYG_FILE_TAG *fp, off_t * pos, int whence)
 {
-        // Only allow SEEK_SET to zero
+	// Only allow SEEK_SET to zero
 
-        D2(printf("jffs2_fo_dirlseek\n"));
+	D2(printf("jffs2_fo_dirlseek\n"));
 
-        if (whence != SEEK_SET || *pos != 0)
-                return EINVAL;
+	if (whence != SEEK_SET || *pos != 0)
+		return EINVAL;
 
-        *pos = fp->f_offset = 0;
+	*pos = fp->f_offset = 0;
 
-        return ENOERR;
+	return ENOERR;
 }
 
 //==========================================================================
@@ -1636,347 +1597,179 @@ static int jffs2_fo_dirlseek(struct CYG_
 //
 //==========================================================================
 
-struct page *read_cache_page(unsigned long index,
-                             int (*filler) (void *, struct page *), void *data)
+unsigned char *jffs2_gc_fetch_page(struct jffs2_sb_info *c, 
+				   struct jffs2_inode_info *f, 
+				   unsigned long offset,
+				   unsigned long *priv)
 {
-        // Only called in gc.c jffs2_garbage_collect_dnode
-        // but gets a real page for the specified inode
+	/* FIXME: This works only with one file system mounted at a time */
+	int ret;
 
-        int err;
-        struct page *gc_page = malloc(sizeof (struct page));
+	ret = jffs2_read_inode_range(c, f, gc_buffer, offset, PAGE_CACHE_SIZE);
+	if (ret)
+		return ERR_PTR(ret);
 
-        printf("read_cache_page\n");
-        memset(&gc_buffer, 0, PAGE_CACHE_SIZE);
-
-        if (gc_page != NULL) {
-                gc_page->virtual = &gc_buffer;
-                gc_page->index = index;
-
-                err = filler(data, gc_page);
-                if (err < 0) {
-                        free(gc_page);
-                        gc_page = NULL;
-                }
-        }
-
-        return gc_page;
+	return gc_buffer;
 }
 
-void page_cache_release(struct page *pg)
+void jffs2_gc_release_page(struct jffs2_sb_info *c,
+			   unsigned char *ptr,
+			   unsigned long *priv)
 {
-
-        // Only called in gc.c jffs2_garbage_collect_dnode
-        // but should free the page malloc'd by read_cache_page
-
-        printf("page_cache_release\n");
-        free(pg);
+	/* Do nothing */
 }
 
-struct inode *new_inode(struct super_block *sb)
+static struct _inode *new_inode(struct super_block *sb)
 {
 
-        // Only called in write.c jffs2_new_inode
-        // Always adds itself to inode cache
+	// Only called in write.c jffs2_new_inode
+	// Always adds itself to inode cache
 
-        struct inode *inode;
-        struct inode *cached_inode;
+	struct _inode *inode;
+	struct _inode *cached_inode;
 
-        inode = malloc(sizeof (struct inode));
-        if (inode == NULL)
-                return 0;
+	inode = malloc(sizeof (struct _inode));
+	if (inode == NULL)
+		return 0;
 
-        D2(printf
-           ("malloc new_inode %x ####################################\n",
-            inode));
+	D2(printf
+	   ("malloc new_inode %x ####################################\n",
+	    inode));
 
-        memset(inode, 0, sizeof (struct inode));
-        inode->i_sb = sb;
-        inode->i_ino = 1;
-        inode->i_count = 0;     //1; // Let ecos manage the open count
+	memset(inode, 0, sizeof (struct _inode));
+	inode->i_sb = sb;
+	inode->i_ino = 1;
+	inode->i_count = 1;
+	inode->i_nlink = 1;	// Let JFFS2 manage the link count
+	inode->i_size = 0;
 
-        inode->i_nlink = 1;     // Let JFFS2 manage the link count
-        inode->i_size = 0;
+	inode->i_cache_next = NULL;	// Newest inode, about to be cached
 
-        inode->i_cache_next = NULL;     // Newest inode, about to be cached
+	// Add to the icache
+	for (cached_inode = sb->s_root; cached_inode != NULL;
+	     cached_inode = cached_inode->i_cache_next) {
+		if (cached_inode->i_cache_next == NULL) {
+			cached_inode->i_cache_next = inode;	// Current last in cache points to newcomer
+			inode->i_cache_prev = cached_inode;	// Newcomer points back to last
+			break;
+		}
+	}
 
-        // Add to the icache
-        for (cached_inode = sb->s_root; cached_inode != NULL;
-             cached_inode = cached_inode->i_cache_next) {
-                if (cached_inode->i_cache_next == NULL) {
-                        cached_inode->i_cache_next = inode;     // Current last in cache points to newcomer
-                        inode->i_cache_prev = cached_inode;     // Newcomer points back to last
-                        break;
-                }
-        }
-
-        return inode;
+	return inode;
 }
-struct inode *ilookup(struct super_block *sb, cyg_uint32 ino)
+
+static struct _inode *ilookup(struct super_block *sb, cyg_uint32 ino)
 {
-        struct inode *inode = NULL;
+	struct _inode *inode = NULL;
 
-        D2(printf("ilookup\n"));
-        // Check for this inode in the cache
-        for (inode = sb->s_root; inode != NULL; inode = inode->i_cache_next) {
-                if (inode->i_ino == ino) {
-                        inode->i_count++;
-                        break;
-                }
-        }
+	D2(printf("ilookup\n"));
+	// Check for this inode in the cache
+	for (inode = sb->s_root; inode != NULL; inode = inode->i_cache_next) {
+		if (inode->i_ino == ino) {
+			inode->i_count++;
+			break;
+		}
+	}
 
-        return inode;
+	return inode;
 }
 
-struct inode *iget(struct super_block *sb, cyg_uint32 ino)
+struct _inode *jffs2_iget(struct super_block *sb, cyg_uint32 ino)
 {
+	// Called in super.c jffs2_read_super, dir.c jffs2_lookup,
+	// and gc.c jffs2_garbage_collect_pass
 
-        // Substitute for iget drops straight through to reading the 
-        // inode from disk if it is not in the inode cache
-
-        // Called in super.c jffs2_read_super, dir.c jffs2_lookup,
-        // and gc.c jffs2_garbage_collect_pass
+	// Must first check for cached inode 
+	// If this fails let new_inode create one
 
-        // Must first check for cached inode 
-        // If this fails let new_inode create one
+	struct _inode *inode;
+	int err;
 
-        struct inode *inode;
+	D2(printf("jffs2_iget\n"));
 
-        D2(printf("iget\n"));
+	inode = ilookup(sb, ino);
+	if (inode)
+		return inode;
 
-        inode = ilookup(sb, ino);
-        if (inode)
-                return inode;
+	// Not cached, so malloc it
+	inode = new_inode(sb);
+	if (inode == NULL)
+		return 0;
 
-        // Not cached, so malloc it
-        inode = new_inode(sb);
-        if (inode == NULL)
-                return 0;
+	inode->i_ino = ino;
 
-        inode->i_ino = ino;
-        jffs2_read_inode(inode);
-        inode->i_count = 1;
-        return inode;
+	err = jffs2_read_inode(inode);
+	if (err) {
+		printf("jffs2_read_inode() failed\n");
+		jffs2_iput(inode);
+		inode = NULL;
+		return ERR_PTR(err);
+	}
+	return inode;
 }
 
-void iput(struct inode *i)
-{
-
-        // Called in dec_refcnt, jffs2_find 
-        // (and jffs2_open and jffs2_ops_mkdir?)
-        // super.c jffs2_read_super,
-        // and gc.c jffs2_garbage_collect_pass
-
-        struct inode *cached_inode;
-
-        D2(printf
-           ("free iput inode %x $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$\n", i));
-        if (i && i->i_count) {
-                /* Added by dwmw2. iget/iput in Linux track the use count,
-                   don't just unconditionally free it */
-                printf("iput called for used inode\n");
-                return;
-        }
-        if (i != NULL) {
-                // Remove from the icache
-                for (cached_inode = i->i_sb->s_root; cached_inode != NULL;
-                     cached_inode = cached_inode->i_cache_next) {
-                        if (cached_inode == i) {
-                                if(cached_inode->i_cache_prev != NULL) {
-                                        cached_inode->i_cache_prev->i_cache_next = cached_inode->i_cache_next;  // Previous entry points ahead of us
-                                        if (cached_inode->i_cache_next != NULL)
-                                                cached_inode->i_cache_next->i_cache_prev = cached_inode->i_cache_prev;  // Next entry points behind us
-                                }
-                                break;
-                        }
-                }
-                // inode has been seperated from the cache
-                jffs2_clear_inode(i);
-                free(i);
-        }
-}
+// -------------------------------------------------------------------------
+// Decrement the reference count on an inode. If this makes the ref count
+// zero, then this inode can be freed.
 
-static int return_EIO(void)
+void jffs2_iput(struct _inode *i)
 {
-        return -EIO;
+	// Called in jffs2_find 
+	// (and jffs2_open and jffs2_ops_mkdir?)
+	// super.c jffs2_read_super,
+	// and gc.c jffs2_garbage_collect_pass
+ recurse:
+	if (!i) {
+		printf("jffs2_iput() called with NULL inode\n");
+		// and let it fault... 
+	}
+
+	i->i_count--;
+
+	if (i->i_count < 0)
+		BUG();
+
+	if (i->i_count)
+		return;
+
+	if (!i->i_nlink) {
+		struct _inode *parent;
+
+		// Remove from the icache linked list and free immediately
+		if (i->i_cache_prev)
+			i->i_cache_prev->i_cache_next = i->i_cache_next;
+		if (i->i_cache_next)
+			i->i_cache_next->i_cache_prev = i->i_cache_prev;
+
+		parent = i->i_parent;
+		jffs2_clear_inode(i);
+		memset(i, 0x5a, sizeof(*i));
+		free(i);
+
+		if (parent && parent != i) {
+			i = parent;
+			goto recurse;
+		}
+
+	} else {
+		// Evict some _other_ inode with i_count zero, leaving
+		// this latest one in the cache for a while 
+		icache_evict(i->i_sb->s_root, i);
+	}
 }
 
-#define EIO_ERROR ((void *) (return_EIO))
 
-void make_bad_inode(struct inode *inode)
-{
-
-        // In readinode.c JFFS2 checks whether the inode has appropriate
-        // content for its marked type
-
-        D2(printf("make_bad_inode\n"));
-
-        inode->i_mode = S_IFREG;
-        inode->i_atime = inode->i_mtime = inode->i_ctime = cyg_timestamp();
-        inode->i_op = EIO_ERROR;
-        inode->i_fop = EIO_ERROR;
-}
-
-int is_bad_inode(struct inode *inode)
-{
-
-        // Called in super.c jffs2_read_super,
-        // and gc.c jffs2_garbage_collect_pass
-
-        D2(printf("is_bad_inode\n"));
-
-        return (inode->i_op == EIO_ERROR);
-        /*if(i == NULL)
-           return 1;
-           return 0; */
-}
-
-cyg_bool jffs2_flash_read(struct jffs2_sb_info * c,
-                          cyg_uint32 read_buffer_offset, const size_t size,
-                          size_t * return_size, char *write_buffer)
-{
-        Cyg_ErrNo err;
-        cyg_uint32 len = size;
-        struct super_block *sb = OFNI_BS_2SFFJ(c);
-
-        //D2(printf("FLASH READ\n"));
-        //D2(printf("read address = %x\n", CYGNUM_FS_JFFS2_BASE_ADDRESS + read_buffer_offset));
-        //D2(printf("write address = %x\n", write_buffer));
-        //D2(printf("size = %x\n", size));
-        err = cyg_io_bread(sb->s_dev, write_buffer, &len, read_buffer_offset);
-
-        *return_size = (size_t) len;
-        return ((err == ENOERR) ? ENOERR : -EIO);
-}
-
-cyg_bool jffs2_flash_write(struct jffs2_sb_info * c,
-                           cyg_uint32 write_buffer_offset, const size_t size,
-                           size_t * return_size, char *read_buffer)
-{
-
-        Cyg_ErrNo err;
-        cyg_uint32 len = size;
-        struct super_block *sb = OFNI_BS_2SFFJ(c);
-
-        //    D2(printf("FLASH WRITE ENABLED!!!\n"));
-        //    D2(printf("write address = %x\n", CYGNUM_FS_JFFS2_BASE_ADDRESS + write_buffer_offset));
-        //    D2(printf("read address = %x\n", read_buffer));
-        //    D2(printf("size = %x\n", size));
-
-        err = cyg_io_bwrite(sb->s_dev, read_buffer, &len, write_buffer_offset);
-        *return_size = (size_t) len;
-
-        return ((err == ENOERR) ? ENOERR : -EIO);
-}
-
-int
-jffs2_flash_direct_writev(struct jffs2_sb_info *c, const struct iovec *vecs,
-                   unsigned long count, loff_t to, size_t * retlen)
-{
-        unsigned long i;
-        size_t totlen = 0, thislen;
-        int ret = 0;
-
-        for (i = 0; i < count; i++) {
-                // writes need to be aligned but the data we're passed may not be
-                // Observation suggests most unaligned writes are small, so we
-                // optimize for that case.
-
-                if (((vecs[i].iov_len & (sizeof (int) - 1))) ||
-                    (((unsigned long) vecs[i].
-                      iov_base & (sizeof (unsigned long) - 1)))) {
-                        // are there iov's after this one? Or is it so much we'd need
-                        // to do multiple writes anyway?
-                        if ((i + 1) < count || vecs[i].iov_len > 256) {
-                                // cop out and malloc
-                                unsigned long j;
-                                ssize_t sizetomalloc = 0, totvecsize = 0;
-                                char *cbuf, *cbufptr;
-
-                                for (j = i; j < count; j++)
-                                        totvecsize += vecs[j].iov_len;
-
-                                // pad up in case unaligned
-                                sizetomalloc = totvecsize + sizeof (int) - 1;
-                                sizetomalloc &= ~(sizeof (int) - 1);
-                                cbuf = (char *) malloc(sizetomalloc);
-                                // malloc returns aligned memory
-                                if (!cbuf) {
-                                        ret = -ENOMEM;
-                                        goto writev_out;
-                                }
-                                cbufptr = cbuf;
-                                for (j = i; j < count; j++) {
-                                        memcpy(cbufptr, vecs[j].iov_base,
-                                               vecs[j].iov_len);
-                                        cbufptr += vecs[j].iov_len;
-                                }
-                                ret =
-                                    jffs2_flash_write(c, to, sizetomalloc,
-                                                      &thislen, cbuf);
-                                if (thislen > totvecsize)       // in case it was aligned up
-                                        thislen = totvecsize;
-                                totlen += thislen;
-                                free(cbuf);
-                                goto writev_out;
-                        } else {
-                                // otherwise optimize for the common case
-                                int buf[256 / sizeof (int)];    // int, so int aligned
-                                size_t lentowrite;
-
-                                lentowrite = vecs[i].iov_len;
-                                // pad up in case its unaligned
-                                lentowrite += sizeof (int) - 1;
-                                lentowrite &= ~(sizeof (int) - 1);
-                                memcpy(buf, vecs[i].iov_base, lentowrite);
-
-                                ret =
-                                    jffs2_flash_write(c, to, lentowrite,
-                                                      &thislen, (char *) &buf);
-                                if (thislen > vecs[i].iov_len)
-                                        thislen = vecs[i].iov_len;
-                        }       // else
-                } else
-                        ret =
-                            jffs2_flash_write(c, to, vecs[i].iov_len, &thislen,
-                                              vecs[i].iov_base);
-                totlen += thislen;
-                if (ret || thislen != vecs[i].iov_len)
-                        break;
-                to += vecs[i].iov_len;
-        }
-      writev_out:
-        if (retlen)
-                *retlen = totlen;
+// -------------------------------------------------------------------------
+// EOF jffs2.c
 
-        return ret;
-}
 
-cyg_bool jffs2_flash_erase(struct jffs2_sb_info * c,
-                           struct jffs2_eraseblock * jeb)
+static inline void jffs2_init_inode_info(struct jffs2_inode_info *f)
 {
-        cyg_io_flash_getconfig_erase_t e;
-        void *err_addr;
-        Cyg_ErrNo err;
-        cyg_uint32 len = sizeof (e);
-        struct super_block *sb = OFNI_BS_2SFFJ(c);
-
-        e.offset = jeb->offset;
-        e.len = c->sector_size;
-        e.err_address = &err_addr;
-
-        //        D2(printf("FLASH ERASE ENABLED!!!\n"));
-        //        D2(printf("erase address = %x\n", CYGNUM_FS_JFFS2_BASE_ADDRESS + jeb->offset));
-        //        D2(printf("size = %x\n", c->sector_size));
-
-        err = cyg_io_get_config(sb->s_dev, CYG_IO_GET_CONFIG_FLASH_ERASE,
-                                &e, &len);
-
-        return (err != ENOERR || e.flasherr != 0);
+	memset(f, 0, sizeof(*f));
+	init_MUTEX_LOCKED(&f->sem);
 }
 
-// -------------------------------------------------------------------------
-// EOF jffs2.c
-void jffs2_clear_inode (struct inode *inode)
+static void jffs2_clear_inode (struct _inode *inode)
 {
         /* We can forget about this inode for now - drop all
          *  the nodelists associated with it, etc.
@@ -1992,83 +1785,227 @@ void jffs2_clear_inode (struct inode *in
 
 /* jffs2_new_inode: allocate a new inode and inocache, add it to the hash,
    fill in the raw_inode while you're at it. */
-struct inode *jffs2_new_inode (struct inode *dir_i, int mode, struct jffs2_raw_inode *ri)
+struct _inode *jffs2_new_inode (struct _inode *dir_i, int mode, struct jffs2_raw_inode *ri)
 {
-        struct inode *inode;
-        struct super_block *sb = dir_i->i_sb;
-        struct jffs2_sb_info *c;
-        struct jffs2_inode_info *f;
-        int ret;
-
-        D1(printk(KERN_DEBUG "jffs2_new_inode(): dir_i %ld, mode 0x%x\n", dir_i->i_ino, mode));
-
-        c = JFFS2_SB_INFO(sb);
-        
-        inode = new_inode(sb);
-        
-        if (!inode)
-                return ERR_PTR(-ENOMEM);
-
-        f = JFFS2_INODE_INFO(inode);
-        jffs2_init_inode_info(f);
-
-        memset(ri, 0, sizeof(*ri));
-        /* Set OS-specific defaults for new inodes */
-        ri->uid = ri->gid = cpu_to_je16(0);
-        ri->mode =  cpu_to_jemode(mode);
-        ret = jffs2_do_new_inode (c, f, mode, ri);
-        if (ret) {
-                make_bad_inode(inode);
-                iput(inode);
-                return ERR_PTR(ret);
-        }
-        inode->i_nlink = 1;
-        inode->i_ino = je32_to_cpu(ri->ino);
-        inode->i_mode = jemode_to_cpu(ri->mode);
-        inode->i_gid = je16_to_cpu(ri->gid);
-        inode->i_uid = je16_to_cpu(ri->uid);
-        inode->i_atime = inode->i_ctime = inode->i_mtime = cyg_timestamp();
-        ri->atime = ri->mtime = ri->ctime = cpu_to_je32(inode->i_mtime);
-
-        inode->i_size = 0;
-
-        insert_inode_hash(inode);
-
-        return inode;
-}
-
-
-void jffs2_read_inode (struct inode *inode)
-{
-        struct jffs2_inode_info *f;
-        struct jffs2_sb_info *c;
-        struct jffs2_raw_inode latest_node;
-        int ret;
-
-        D1(printk(KERN_DEBUG "jffs2_read_inode(): inode->i_ino == %lu\n", inode->i_ino));
-
-        f = JFFS2_INODE_INFO(inode);
-        c = JFFS2_SB_INFO(inode->i_sb);
-
-        jffs2_init_inode_info(f);
-        
-        ret = jffs2_do_read_inode(c, f, inode->i_ino, &latest_node);
-
-        if (ret) {
-                make_bad_inode(inode);
-                up(&f->sem);
-                return;
-        }
-        inode->i_mode = jemode_to_cpu(latest_node.mode);
-        inode->i_uid = je16_to_cpu(latest_node.uid);
-        inode->i_gid = je16_to_cpu(latest_node.gid);
-        inode->i_size = je32_to_cpu(latest_node.isize);
-        inode->i_atime = je32_to_cpu(latest_node.atime);
-        inode->i_mtime = je32_to_cpu(latest_node.mtime);
-        inode->i_ctime = je32_to_cpu(latest_node.ctime);
-
-        inode->i_nlink = f->inocache->nlink;
-        up(&f->sem);
-
-        D1(printk(KERN_DEBUG "jffs2_read_inode() returning\n"));
+	struct _inode *inode;
+	struct super_block *sb = dir_i->i_sb;
+	struct jffs2_sb_info *c;
+	struct jffs2_inode_info *f;
+	int ret;
+
+	D1(printk(KERN_DEBUG "jffs2_new_inode(): dir_i %ld, mode 0x%x\n", dir_i->i_ino, mode));
+
+	c = JFFS2_SB_INFO(sb);
+	
+	inode = new_inode(sb);
+	
+	if (!inode)
+		return ERR_PTR(-ENOMEM);
+
+	f = JFFS2_INODE_INFO(inode);
+	jffs2_init_inode_info(f);
+
+	memset(ri, 0, sizeof(*ri));
+	/* Set OS-specific defaults for new inodes */
+	ri->uid = ri->gid = cpu_to_je16(0);
+	ri->mode =  cpu_to_jemode(mode);
+	ret = jffs2_do_new_inode (c, f, mode, ri);
+	if (ret) {
+		jffs2_iput(inode);
+		return ERR_PTR(ret);
+	}
+	inode->i_nlink = 1;
+	inode->i_ino = je32_to_cpu(ri->ino);
+	inode->i_mode = jemode_to_cpu(ri->mode);
+	inode->i_gid = je16_to_cpu(ri->gid);
+	inode->i_uid = je16_to_cpu(ri->uid);
+	inode->i_atime = inode->i_ctime = inode->i_mtime = cyg_timestamp();
+	ri->atime = ri->mtime = ri->ctime = cpu_to_je32(inode->i_mtime);
+
+	inode->i_size = 0;
+
+	return inode;
+}
+
+
+static int jffs2_read_inode (struct _inode *inode)
+{
+	struct jffs2_inode_info *f;
+	struct jffs2_sb_info *c;
+	struct jffs2_raw_inode latest_node;
+	int ret;
+
+	D1(printk(KERN_DEBUG "jffs2_read_inode(): inode->i_ino == %lu\n", inode->i_ino));
+
+	f = JFFS2_INODE_INFO(inode);
+	c = JFFS2_SB_INFO(inode->i_sb);
+
+	jffs2_init_inode_info(f);
+	
+	ret = jffs2_do_read_inode(c, f, inode->i_ino, &latest_node);
+
+	if (ret) {
+		up(&f->sem);
+		return ret;
+	}
+	inode->i_mode = jemode_to_cpu(latest_node.mode);
+	inode->i_uid = je16_to_cpu(latest_node.uid);
+	inode->i_gid = je16_to_cpu(latest_node.gid);
+	inode->i_size = je32_to_cpu(latest_node.isize);
+	inode->i_atime = je32_to_cpu(latest_node.atime);
+	inode->i_mtime = je32_to_cpu(latest_node.mtime);
+	inode->i_ctime = je32_to_cpu(latest_node.ctime);
+
+	inode->i_nlink = f->inocache->nlink;
+	up(&f->sem);
+
+	D1(printk(KERN_DEBUG "jffs2_read_inode() returning\n"));
+	return 0;
+}
+
+
+void jffs2_gc_release_inode(struct jffs2_sb_info *c,
+				   struct jffs2_inode_info *f)
+{
+	jffs2_iput(OFNI_EDONI_2SFFJ(f));
+}
+
+struct jffs2_inode_info *jffs2_gc_fetch_inode(struct jffs2_sb_info *c,
+						     int inum, int nlink)
+{
+	struct _inode *inode;
+	struct jffs2_inode_cache *ic;
+	if (!nlink) {
+		/* The inode has zero nlink but its nodes weren't yet marked
+		   obsolete. This has to be because we're still waiting for 
+		   the final (close() and) jffs2_iput() to happen.
+
+		   There's a possibility that the final jffs2_iput() could have 
+		   happened while we were contemplating. In order to ensure
+		   that we don't cause a new read_inode() (which would fail)
+		   for the inode in question, we use ilookup() in this case
+		   instead of jffs2_iget().
+
+		   The nlink can't _become_ zero at this point because we're 
+		   holding the alloc_sem, and jffs2_do_unlink() would also
+		   need that while decrementing nlink on any inode.
+		*/
+		inode = ilookup(OFNI_BS_2SFFJ(c), inum);
+		if (!inode) {
+			D1(printk(KERN_DEBUG "ilookup() failed for ino #%u; inode is probably deleted.\n",
+				  inum));
+
+			spin_lock(&c->inocache_lock);
+			ic = jffs2_get_ino_cache(c, inum);
+			if (!ic) {
+				D1(printk(KERN_DEBUG "Inode cache for ino #%u is gone.\n", inum));
+				spin_unlock(&c->inocache_lock);
+				return NULL;
+			}
+			if (ic->stat