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->state != INO_STATE_CHECKEDABSENT) {
+				/* Wait for progress. Don't just loop */
+				D1(printk(KERN_DEBUG "Waiting for ino #%u in state %d\n",
+					  ic->ino, ic->state));
+				sleep_on_spinunlock(&c->inocache_wq, &c->inocache_lock);
+			} else {
+				spin_unlock(&c->inocache_lock);
+			}
+
+			return NULL;
+		}
+	} else {
+		/* Inode has links to it still; they're not going away because
+		   jffs2_do_unlink() would need the alloc_sem and we have it.
+		   Just jffs2_iget() it, and if read_inode() is necessary that's OK.
+		*/
+		inode = jffs2_iget(OFNI_BS_2SFFJ(c), inum);
+		if (IS_ERR(inode))
+			return (void *)inode;
+	}
+
+	return JFFS2_INODE_INFO(inode);
+}
+
+
+
+uint32_t jffs2_from_os_mode(uint32_t osmode)
+{
+	uint32_t jmode = ((osmode & S_IRUSR)?00400:0) |
+		((osmode & S_IWUSR)?00200:0) |
+		((osmode & S_IXUSR)?00100:0) |
+		((osmode & S_IRGRP)?00040:0) |
+		((osmode & S_IWGRP)?00020:0) |
+		((osmode & S_IXGRP)?00010:0) |
+		((osmode & S_IROTH)?00004:0) |
+		((osmode & S_IWOTH)?00002:0) |
+		((osmode & S_IXOTH)?00001:0);
+
+	switch (osmode & S_IFMT) {
+	case S_IFSOCK:
+		return jmode | 0140000;
+	case S_IFLNK:
+		return jmode | 0120000;
+	case S_IFREG:
+		return jmode | 0100000;
+	case S_IFBLK:
+		return jmode | 0060000;
+	case S_IFDIR:
+		return jmode | 0040000;
+	case S_IFCHR:
+		return jmode | 0020000;
+	case S_IFIFO:
+		return jmode | 0010000;
+	case S_ISUID:
+		return jmode | 0004000;
+	case S_ISGID:
+		return jmode | 0002000;
+#ifdef S_ISVTX
+	case S_ISVTX:
+		return jmode | 0001000;
+#endif
+	}
+	printf("os_to_jffs2_mode() cannot convert 0x%x\n", osmode);
+	BUG();
+	return 0;
+}
+
+uint32_t jffs2_to_os_mode (uint32_t jmode)
+{
+	uint32_t osmode = ((jmode & 00400)?S_IRUSR:0) |
+		((jmode & 00200)?S_IWUSR:0) |
+		((jmode & 00100)?S_IXUSR:0) |
+		((jmode & 00040)?S_IRGRP:0) |
+		((jmode & 00020)?S_IWGRP:0) |
+		((jmode & 00010)?S_IXGRP:0) |
+		((jmode & 00004)?S_IROTH:0) |
+		((jmode & 00002)?S_IWOTH:0) |
+		((jmode & 00001)?S_IXOTH:0);
+
+	switch(jmode & 00170000) {
+	case 0140000:
+		return osmode | S_IFSOCK;
+	case 0120000:
+		return osmode | S_IFLNK;
+	case 0100000:
+		return osmode | S_IFREG;
+	case 0060000:
+		return osmode | S_IFBLK;
+	case 0040000:
+		return osmode | S_IFDIR;
+	case 0020000:
+		return osmode | S_IFCHR;
+	case 0010000:
+		return osmode | S_IFIFO;
+	case 0004000:
+		return osmode | S_ISUID;
+	case 0002000:
+		return osmode | S_ISGID;
+#ifdef S_ISVTX
+	case 0001000:
+		return osmode | S_ISVTX;
+#endif
+	}
+	printf("jffs2_to_os_mode() cannot convert 0x%x\n", osmode);
+	BUG();
+	return 0;
 }
Index: src/gc.c
===================================================================
RCS file: /cvs/ecos/ecos/packages/fs/jffs2/current/src/gc.c,v
retrieving revision 1.5
diff -u -p -r1.5 gc.c
--- src/gc.c	20 Nov 2003 16:52:36 -0000	1.5
+++ src/gc.c	26 Nov 2003 22:24:14 -0000
@@ -7,7 +7,7 @@
  *
  * For licensing information, see the file 'LICENCE' in this directory.
  *
- * $Id: gc.c,v 1.129 2003/11/20 16:40:14 dwmw2 Exp $
+ * $Id: gc.c,v 1.131 2003/11/24 12:07:28 dwmw2 Exp $
  *
  */
 
@@ -36,7 +36,7 @@ static int jffs2_garbage_collect_dnode(s
 				       struct jffs2_inode_info *f, struct jffs2_full_dnode *fn,
 				       uint32_t start, uint32_t end);
 static int jffs2_garbage_collect_live(struct jffs2_sb_info *c,  struct jffs2_eraseblock *jeb,
-			       struct jffs2_raw_node_ref *raw, struct jffs2_inode_cache *ic);
+			       struct jffs2_raw_node_ref *raw, struct jffs2_inode_info *f);
 
 /* Called with erase_completion_lock held */
 static struct jffs2_eraseblock *jffs2_find_gc_block(struct jffs2_sb_info *c)
@@ -112,10 +112,11 @@ static struct jffs2_eraseblock *jffs2_fi
  */
 int jffs2_garbage_collect_pass(struct jffs2_sb_info *c)
 {
+	struct jffs2_inode_info *f;
 	struct jffs2_inode_cache *ic;
 	struct jffs2_eraseblock *jeb;
 	struct jffs2_raw_node_ref *raw;
-	int ret = 0;
+	int ret = 0, inum, nlink;
 
 	if (down_interruptible(&c->alloc_sem))
 		return -EINTR;
@@ -249,7 +250,9 @@ int jffs2_garbage_collect_pass(struct jf
 
 	ic = jffs2_raw_ref_to_ic(raw);
 
-	/* We need to hold the inocache */
+	/* We need to hold the inocache. Either the erase_completion_lock or
+	   the inocache_lock are sufficient; we trade down since the inocache_lock 
+	   causes less contention. */
 	spin_lock(&c->inocache_lock);
 
 	spin_unlock(&c->erase_completion_lock);
@@ -343,14 +346,26 @@ int jffs2_garbage_collect_pass(struct jf
 		/* Fall through if it wanted us to, with inocache_lock held */
 	}
 
-	/* FIXME: We still have the inocache_lock held. This is ugly.
-	   It's done to prevent the fairly unlikely race where the
-	   gcblock is entirely obsoleted by the final close of a file
-	   which had the only valid nodes in the block, followed by
-	   erasure, followed by freeing of the ic because the erased
-	   block(s) held _all_ the nodes of that inode.... never been
-	   seen but it's vaguely possible. */
-	ret = jffs2_garbage_collect_live(c, jeb, raw, ic);
+	/* Prevent the fairly unlikely race where the gcblock is
+	   entirely obsoleted by the final close of a file which had
+	   the only valid nodes in the block, followed by erasure,
+	   followed by freeing of the ic because the erased block(s)
+	   held _all_ the nodes of that inode.... never been seen but
+	   it's vaguely possible. */
+
+	inum = ic->ino;
+	nlink = ic->nlink;
+	spin_unlock(&c->inocache_lock);
+
+	f = jffs2_gc_fetch_inode(c, inum, nlink);
+	if (IS_ERR(f))
+		return PTR_ERR(f);
+	if (!f)
+		return 0;
+
+	ret = jffs2_garbage_collect_live(c, jeb, raw, f);
+
+	jffs2_gc_release_inode(c, f);
 
  release_sem:
 	up(&c->alloc_sem);
@@ -374,78 +389,14 @@ int jffs2_garbage_collect_pass(struct jf
 }
 
 static int jffs2_garbage_collect_live(struct jffs2_sb_info *c,  struct jffs2_eraseblock *jeb,
-				      struct jffs2_raw_node_ref *raw, struct jffs2_inode_cache *ic)
+				      struct jffs2_raw_node_ref *raw, struct jffs2_inode_info *f)
 {
-	struct jffs2_inode_info *f;
 	struct jffs2_node_frag *frag;
 	struct jffs2_full_dnode *fn = NULL;
 	struct jffs2_full_dirent *fd;
 	uint32_t start = 0, end = 0, nrfrags = 0;
-	struct inode *inode;
 	int ret = 0;
 
-	if (!ic->nlink) {
-		int inum = ic->ino;
-
-		spin_unlock(&c->inocache_lock);
-
-		/* 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) iput() to happen.
-
-		   There's a possibility that the final 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 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 0;
-			}
-			if (ic->state != INO_STATE_CHECKEDABSENT) {
-				/* Wait for progress. Don't just loop */
-				D1(printk(KERN_DEBUG "Waiting for ino #%u in state %d\n",
-					  ic->ino, ic->state));
-				sleep_on_spinunlock(&c->inocache_wq, &c->inocache_lock);
-			} else {
-				spin_unlock(&c->inocache_lock);
-			}
-
-			return 0;
-		}
-	} else {
-		spin_unlock(&c->inocache_lock);
-
-		/* Inode has links to it still; they're not going away because
-		   jffs2_do_unlink() would need the alloc_sem and we have it.
-		   Just iget() it, and if read_inode() is necessary that's OK.
-		*/
-		inode = iget(OFNI_BS_2SFFJ(c), ic->ino);
-		if (!inode)
-			return -ENOMEM;
-	}
-	if (is_bad_inode(inode)) {
-		printk(KERN_NOTICE "Eep. read_inode() failed for ino #%u. nlink %d, state %d\n",
-		       ic->ino, ic->nlink, ic->state);
-		/* NB. This will happen again. We need to do something appropriate here. */
-		ret = -EIO;
-		goto put_out;
-	}
-
-	f = JFFS2_INODE_INFO(inode);
 	down(&f->sem);
 
 	/* Now we have the lock for this inode. Check that it's still the one at the head
@@ -486,10 +437,10 @@ static int jffs2_garbage_collect_live(st
 	}
 	if (fn) {
 		if (ref_flags(raw) == REF_PRISTINE) {
-			ret = jffs2_garbage_collect_pristine(c, ic, raw);
+			ret = jffs2_garbage_collect_pristine(c, f->inocache, raw);
 			if (!ret) {
 				/* Urgh. Return it sensibly. */
-				frag->node->raw = ic->nodes;
+				frag->node->raw = f->inocache->nodes;
 			}	
 			if (ret != -EBADFD)
 				goto upnout;
@@ -526,8 +477,6 @@ static int jffs2_garbage_collect_live(st
 	}
  upnout:
 	up(&f->sem);
- put_out:
-	iput(inode);
 
 	return ret;
 }
@@ -1075,10 +1024,9 @@ static int jffs2_garbage_collect_dnode(s
 	uint32_t alloclen, phys_ofs, offset, orig_end, orig_start;	
 	int ret = 0;
 	unsigned char *comprbuf = NULL, *writebuf;
-	struct page *pg;
+	unsigned long pg;
 	unsigned char *pg_ptr;
-	/* FIXME: */ struct inode *inode = OFNI_EDONI_2SFFJ(f);
-
+ 
 	memset(&ri, 0, sizeof(ri));
 
 	D1(printk(KERN_DEBUG "Writing replacement dnode for ino #%u from offset 0x%x to 0x%x\n",
@@ -1217,16 +1165,12 @@ static int jffs2_garbage_collect_dnode(s
 	 *    page OK. We'll actually write it out again in commit_write, which is a little
 	 *    suboptimal, but at least we're correct.
 	 */
-#ifdef __ECOS
-	pg = read_cache_page(start >> PAGE_CACHE_SHIFT, (void *)jffs2_do_readpage_unlock, inode);
-#else
-	pg = read_cache_page(inode->i_mapping, start >> PAGE_CACHE_SHIFT, (void *)jffs2_do_readpage_unlock, inode);
-#endif
-	if (IS_ERR(pg)) {
-		printk(KERN_WARNING "read_cache_page() returned error: %ld\n", PTR_ERR(pg));
-		return PTR_ERR(pg);
+	pg_ptr = jffs2_gc_fetch_page(c, f, start, &pg);
+
+	if (IS_ERR(pg_ptr)) {
+		printk(KERN_WARNING "read_cache_page() returned error: %ld\n", PTR_ERR(pg_ptr));
+		return PTR_ERR(pg_ptr);
 	}
-	pg_ptr = (char *)kmap(pg);
 
 	offset = start;
 	while(offset < orig_end) {
@@ -1287,10 +1231,7 @@ static int jffs2_garbage_collect_dnode(s
 		}
 	}
 
-	kunmap(pg);
-	/* XXX: Does the page get freed automatically? */
-	/* AAA: Judging by the unmount getting stuck in __wait_on_page, nope. */
-	page_cache_release(pg);
+	jffs2_gc_release_page(c, pg_ptr, &pg);
 	return ret;
 }
 
Index: src/malloc-ecos.c
===================================================================
RCS file: /cvs/ecos/ecos/packages/fs/jffs2/current/src/malloc-ecos.c,v
retrieving revision 1.4
diff -u -p -r1.4 malloc-ecos.c
--- src/malloc-ecos.c	20 Nov 2003 21:06:26 -0000	1.4
+++ src/malloc-ecos.c	26 Nov 2003 22:24:14 -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: malloc-ecos.c,v 1.2 2003/01/09 13:53:44 dwmw2 Exp $
+ * $Id: malloc-ecos.c,v 1.4 2003/11/26 15:55:35 dwmw2 Exp $
  *
  */
 
Index: src/nodelist.h
===================================================================
RCS file: /cvs/ecos/ecos/packages/fs/jffs2/current/src/nodelist.h,v
retrieving revision 1.5
diff -u -p -r1.5 nodelist.h
--- src/nodelist.h	20 Nov 2003 16:52:36 -0000	1.5
+++ src/nodelist.h	26 Nov 2003 22:24:14 -0000
@@ -7,7 +7,7 @@
  *
  * For licensing information, see the file 'LICENCE' in this directory.
  *
- * $Id: nodelist.h,v 1.113 2003/11/20 16:40:14 dwmw2 Exp $
+ * $Id: nodelist.h,v 1.115 2003/11/26 15:30:58 dwmw2 Exp $
  *
  */
 
@@ -378,6 +378,7 @@ struct rb_node *rb_prev(struct rb_node *
 void rb_replace_node(struct rb_node *victim, struct rb_node *new, struct rb_root *root);
 
 /* nodemgmt.c */
+int jffs2_thread_should_wake(struct jffs2_sb_info *c);
 int jffs2_reserve_space(struct jffs2_sb_info *c, uint32_t minsize, uint32_t *ofs, uint32_t *len, int prio);
 int jffs2_reserve_space_gc(struct jffs2_sb_info *c, uint32_t minsize, uint32_t *ofs, uint32_t *len);
 int jffs2_add_physical_node_ref(struct jffs2_sb_info *c, struct jffs2_raw_node_ref *new);
@@ -453,7 +454,6 @@ int jffs2_do_mount_fs(struct jffs2_sb_in
 /* erase.c */
 void jffs2_erase_block(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb);
 void jffs2_erase_pending_blocks(struct jffs2_sb_info *c, int count);
-void jffs2_erase_pending_trigger(struct jffs2_sb_info *c);
 
 #ifdef CONFIG_JFFS2_FS_NAND
 /* wbuf.c */
Index: src/nodemgmt.c
===================================================================
RCS file: /cvs/ecos/ecos/packages/fs/jffs2/current/src/nodemgmt.c,v
retrieving revision 1.5
diff -u -p -r1.5 nodemgmt.c
--- src/nodemgmt.c	20 Nov 2003 16:52:36 -0000	1.5
+++ src/nodemgmt.c	26 Nov 2003 22:24:14 -0000
@@ -7,7 +7,7 @@
  *
  * For licensing information, see the file 'LICENCE' in this directory.
  *
- * $Id: nodemgmt.c,v 1.106 2003/11/03 17:33:54 dwmw2 Exp $
+ * $Id: nodemgmt.c,v 1.107 2003/11/26 15:30:58 dwmw2 Exp $
  *
  */
 
@@ -708,3 +708,34 @@ void jffs2_dump_block_lists(struct jffs2
 	}
 }
 #endif /* CONFIG_JFFS2_FS_DEBUG */
+
+int jffs2_thread_should_wake(struct jffs2_sb_info *c)
+{
+	int ret = 0;
+	uint32_t dirty;
+
+	if (c->unchecked_size) {
+		D1(printk(KERN_DEBUG "jffs2_thread_should_wake(): unchecked_size %d, checked_ino #%d\n",
+			  c->unchecked_size, c->checked_ino));
+		return 1;
+	}
+
+	/* dirty_size contains blocks on erase_pending_list
+	 * those blocks are counted in c->nr_erasing_blocks.
+	 * If one block is actually erased, it is not longer counted as dirty_space
+	 * but it is counted in c->nr_erasing_blocks, so we add it and subtract it
+	 * with c->nr_erasing_blocks * c->sector_size again.
+	 * Blocks on erasable_list are counted as dirty_size, but not in c->nr_erasing_blocks
+	 * This helps us to force gc and pick eventually a clean block to spread the load.
+	 */
+	dirty = c->dirty_size + c->erasing_size - c->nr_erasing_blocks * c->sector_size;
+
+	if (c->nr_free_blocks + c->nr_erasing_blocks < c->resv_blocks_gctrigger && 
+			(dirty > c->nospc_dirty_size)) 
+		ret = 1;
+
+	D1(printk(KERN_DEBUG "jffs2_thread_should_wake(): nr_free_blocks %d, nr_erasing_blocks %d, dirty_size 0x%x: %s\n", 
+		  c->nr_free_blocks, c->nr_erasing_blocks, c->dirty_size, ret?"yes":"no"));
+
+	return ret;
+}
Index: src/os-ecos.h
===================================================================
RCS file: /cvs/ecos/ecos/packages/fs/jffs2/current/src/os-ecos.h,v
retrieving revision 1.7
diff -u -p -r1.7 os-ecos.h
--- src/os-ecos.h	20 Nov 2003 21:06:26 -0000	1.7
+++ src/os-ecos.h	26 Nov 2003 22:24:14 -0000
@@ -1,13 +1,13 @@
 /*
  * JFFS2 -- Journalling Flash File System, Version 2.
  *
- * Copyright (C) 2002 Red Hat, Inc.
+ * Copyright (C) 2002-2003 Red Hat, Inc.
  *
  * Created by David Woodhouse <dwmw2@cambridge.redhat.com>
  *
  * For licensing information, see the file 'LICENCE' in this directory.
  *
- * $Id: os-ecos.h,v 1.8 2003/11/20 16:41:58 dwmw2 Exp $
+ * $Id: os-ecos.h,v 1.17 2003/11/26 15:55:35 dwmw2 Exp $
  *
  */
 
@@ -20,92 +20,53 @@
 #include <asm/atomic.h>
 #include <linux/stat.h>
 #include <linux/compiler.h>
-#include "jffs2port.h"
 
-#ifndef CONFIG_JFFS2_FS_DEBUG
-# define CONFIG_JFFS2_FS_DEBUG 0
-#endif
+#include <pkgconf/system.h>
+#include <pkgconf/hal.h>
+#include <pkgconf/io_fileio.h>
 
-static inline uint32_t os_to_jffs2_mode(uint32_t osmode)
-{
-	uint32_t jmode = ((osmode & S_IRUSR)?00400:0) |
-		((osmode & S_IWUSR)?00200:0) |
-		((osmode & S_IXUSR)?00100:0) |
-		((osmode & S_IRGRP)?00040:0) |
-		((osmode & S_IWGRP)?00020:0) |
-		((osmode & S_IXGRP)?00010:0) |
-		((osmode & S_IROTH)?00004:0) |
-		((osmode & S_IWOTH)?00002:0) |
-		((osmode & S_IXOTH)?00001:0);
-
-	switch (osmode & S_IFMT) {
-	case S_IFSOCK:
-		return jmode | 0140000;
-	case S_IFLNK:
-		return jmode | 0120000;
-	case S_IFREG:
-		return jmode | 0100000;
-	case S_IFBLK:
-		return jmode | 0060000;
-	case S_IFDIR:
-		return jmode | 0040000;
-	case S_IFCHR:
-		return jmode | 0020000;
-	case S_IFIFO:
-		return jmode | 0010000;
-	case S_ISUID:
-		return jmode | 0004000;
-	case S_ISGID:
-		return jmode | 0002000;
-#ifdef S_ISVTX
-	case S_ISVTX:
-		return jmode | 0001000;
-#endif
-	}
-	printf("os_to_jffs2_mode() cannot convert 0x%x\n", osmode);
-	BUG();
-	return 0;
-}
+#include <cyg/infra/cyg_trac.h>        // tracing macros
+#include <cyg/infra/cyg_ass.h>         // assertion macros
 
-static inline uint32_t jffs2_to_os_mode (uint32_t jmode)
-{
-	uint32_t osmode = ((jmode & 00400)?S_IRUSR:0) |
-		((jmode & 00200)?S_IWUSR:0) |
-		((jmode & 00100)?S_IXUSR:0) |
-		((jmode & 00040)?S_IRGRP:0) |
-		((jmode & 00020)?S_IWGRP:0) |
-		((jmode & 00010)?S_IXGRP:0) |
-		((jmode & 00004)?S_IROTH:0) |
-		((jmode & 00002)?S_IWOTH:0) |
-		((jmode & 00001)?S_IXOTH:0);
-
-	switch(jmode & 00170000) {
-	case 0140000:
-		return osmode | S_IFSOCK;
-	case 0120000:
-		return osmode | S_IFLNK;
-	case 0100000:
-		return osmode | S_IFREG;
-	case 0060000:
-		return osmode | S_IFBLK;
-	case 0040000:
-		return osmode | S_IFDIR;
-	case 0020000:
-		return osmode | S_IFCHR;
-	case 0010000:
-		return osmode | S_IFIFO;
-	case 0004000:
-		return osmode | S_ISUID;
-	case 0002000:
-		return osmode | S_ISGID;
-#ifdef S_ISVTX
-	case 0001000:
-		return osmode | S_ISVTX;
-#endif
+#include <unistd.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <dirent.h>
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <cyg/fileio/fileio.h>
+
+#include <cyg/hal/drv_api.h>
+#include <cyg/infra/diag.h>
+
+#include <cyg/io/flash.h>
+
+#include <linux/types.h>
+#include <linux/list.h>
+#include <asm/bug.h>
+
+#define printf diag_printf
+
+struct _inode;
+struct super_block;
+
+struct iovec {
+        void *iov_base;
+        ssize_t iov_len; 
+};
+
+static inline unsigned int full_name_hash(const unsigned char * name, unsigned int len) {
+
+	unsigned hash = 0;
+ 	while (len--) {
+		hash = (hash << 4) | (hash >> 28);
+		hash ^= *(name++);
 	}
-	printf("jffs2_to_os_mode() cannot convert 0x%x\n", osmode);
-	BUG();
-	return 0;
+	return hash;
 }
 
  /* Read-only operation not currently implemented on eCos */
@@ -115,7 +76,7 @@ static inline uint32_t jffs2_to_os_mode 
 #define jffs2_can_mark_obsolete(c) (1)
 
 #define JFFS2_INODE_INFO(i) (&(i)->jffs2_i)
-#define OFNI_EDONI_2SFFJ(f)  ((struct inode *) ( ((char *)f) - ((char *)(&((struct inode *)NULL)->jffs2_i)) ) )
+#define OFNI_EDONI_2SFFJ(f)  ((struct _inode *) ( ((char *)f) - ((char *)(&((struct _inode *)NULL)->jffs2_i)) ) )
  
 #define JFFS2_F_I_SIZE(f) (OFNI_EDONI_2SFFJ(f)->i_size)
 #define JFFS2_F_I_MODE(f) (OFNI_EDONI_2SFFJ(f)->i_mode)
@@ -126,108 +87,98 @@ static inline uint32_t jffs2_to_os_mode 
 #define JFFS2_F_I_ATIME(f) (OFNI_EDONI_2SFFJ(f)->i_atime)
 
 /* FIXME: eCos doesn't hav a concept of device major/minor numbers */
-#define JFFS2_F_I_RDEV_MIN(f) (MINOR(to_kdev_t(OFNI_EDONI_2SFFJ(f)->i_rdev)))
-#define JFFS2_F_I_RDEV_MAJ(f) (MAJOR(to_kdev_t(OFNI_EDONI_2SFFJ(f)->i_rdev)))
-
+#define JFFS2_F_I_RDEV_MIN(f) ((OFNI_EDONI_2SFFJ(f)->i_rdev)&0xff)
+#define JFFS2_F_I_RDEV_MAJ(f) ((OFNI_EDONI_2SFFJ(f)->i_rdev)>>8)
 
-//#define ITIME(sec) (sec)
-//#define I_SEC(x) (x)
 #define get_seconds cyg_timestamp
 
-struct inode {
-	//struct list_head	i_hash;
-	//struct list_head	i_list;
-	struct list_head	i_dentry;
-
+struct _inode {
 	cyg_uint32		i_ino;
-	atomic_t		i_count;
-	//kdev_t			i_dev;
+
+	int			i_count;
 	mode_t			i_mode;
-	nlink_t			i_nlink;
+	nlink_t			i_nlink; // Could we dispense with this?
 	uid_t			i_uid;
 	gid_t			i_gid;
-	kdev_t			i_rdev;
-	off_t			i_size;
 	time_t			i_atime;
 	time_t			i_mtime;
 	time_t			i_ctime;
-	unsigned long		i_blksize;
-	unsigned long		i_blocks;
-	//unsigned long		i_version;
-	//struct semaphore	i_sem;
-	//struct semaphore	i_zombie;
-	struct inode_operations	*i_op;
-	struct file_operations	*i_fop;	// former ->i_op->default_file_ops 
-	struct super_block	*i_sb;
-	//wait_queue_head_t	i_wait;
-	//struct file_lock	*i_flock;
-	//struct address_space	*i_mapping;
-	//struct address_space	i_data;	
-	//struct dquot		*i_dquot[MAXQUOTAS];
-	//struct pipe_inode_info	*i_pipe;
-	//struct block_device	*i_bdev;
-
-	//unsigned long		i_state;
-
-	unsigned int		i_flags;
-	//unsigned char		i_sock;
-
-	atomic_t		i_writecount;
-	//unsigned int		i_attr_flags;
-	//uint32_t			i_generation;
-	struct jffs2_inode_info	jffs2_i;
+//	union {
+		unsigned short	i_rdev; // For devices only
+		struct _inode *	i_parent; // For directories only
+		off_t		i_size; // For files only
+//	};
+	struct super_block *	i_sb;
 
-        struct inode *i_parent;
+	struct jffs2_inode_info	jffs2_i;
 
-        struct inode *i_cache_prev;
-        struct inode *i_cache_next;
+        struct _inode *		i_cache_prev; // We need doubly-linked?
+        struct _inode *		i_cache_next;
 };
 
 #define JFFS2_SB_INFO(sb) (&(sb)->jffs2_sb)
-
 #define OFNI_BS_2SFFJ(c)  ((struct super_block *) ( ((char *)c) - ((char *)(&((struct super_block *)NULL)->jffs2_sb)) ) )
 
 struct super_block {
-	unsigned long		s_blocksize;
-	unsigned char		s_blocksize_bits;
-	unsigned char		s_dirt;
-	//struct super_operations	*s_op;
-	unsigned long		s_flags;
-	unsigned long		s_magic;
-	//struct dentry		*s_root;
-	struct inode		*s_root;
-	struct jffs2_sb_info jffs2_sb;
-        unsigned long       s_mount_count;
-    cyg_io_handle_t     s_dev;
+	struct jffs2_sb_info	jffs2_sb;
+	struct _inode *		s_root;
+        unsigned long		s_mount_count;
+	cyg_io_handle_t		s_dev;
 };
 
 #define sleep_on_spinunlock(wq, sl) do { ; } while(0)
 #define EBADFD 32767
 
 /* background.c */
+#ifdef CYGOPT_FS_JFFS2_GCTHREAD
+void jffs2_garbage_collect_trigger(struct jffs2_sb_info *c);
+void jffs2_start_garbage_collect_thread(struct jffs2_sb_info *c);
+void jffs2_stop_garbage_collect_thread(struct jffs2_sb_info *c);
+#else
 static inline void jffs2_garbage_collect_trigger(struct jffs2_sb_info *c)
 {
 	/* We don't have a GC thread in eCos (yet) */
 }
+#endif
 
-/* dir.c */
-extern struct file_operations jffs2_dir_operations;
-extern struct inode_operations jffs2_dir_inode_operations;
-
-/* file.c */
-extern struct file_operations jffs2_file_operations;
-extern struct inode_operations jffs2_file_inode_operations;
-extern struct address_space_operations jffs2_file_address_operations;
-int jffs2_null_fsync(struct file *, struct dentry *, int);
-int jffs2_setattr (struct dentry *dentry, struct iattr *iattr);
-int jffs2_do_readpage_nolock (struct inode *inode, struct page *pg);
-int jffs2_do_readpage_unlock (struct inode *inode, struct page *pg);
-//int jffs2_readpage (struct file *, struct page *);
-int jffs2_readpage (struct inode *d_inode, struct page *pg);
-//int jffs2_prepare_write (struct file *, struct page *, unsigned, unsigned);
-int jffs2_prepare_write (struct inode *d_inode, struct page *pg, unsigned start, unsigned end);
-//int jffs2_commit_write (struct file *, struct page *, unsigned, unsigned);
-int jffs2_commit_write (struct inode *d_inode, struct page *pg, unsigned start, unsigned end);
+/* fs-ecos.c */
+struct _inode *jffs2_new_inode (struct _inode *dir_i, int mode, struct jffs2_raw_inode *ri);
+struct _inode *jffs2_iget(struct super_block *sb, cyg_uint32 ino);
+void jffs2_iput(struct _inode * i);
+void jffs2_gc_release_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f);
+struct jffs2_inode_info *jffs2_gc_fetch_inode(struct jffs2_sb_info *c, int inum, int nlink);
+unsigned char *jffs2_gc_fetch_page(struct jffs2_sb_info *c, struct jffs2_inode_info *f, 
+				   unsigned long offset, unsigned long *priv);
+void jffs2_gc_release_page(struct jffs2_sb_info *c, unsigned char *pg, unsigned long *priv);
+
+/* Avoid polluting eCos namespace with names not starting in jffs2_ */
+#define os_to_jffs2_mode(x) jffs2_from_os_mode(x)
+uint32_t jffs2_from_os_mode(uint32_t osmode);
+uint32_t jffs2_to_os_mode (uint32_t jmode);
+
+
+/* flashio.c */
+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_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);
+int jffs2_flash_direct_writev(struct jffs2_sb_info *c, const struct iovec *vecs,
+			      unsigned long count, loff_t to, size_t *retlen);
+cyg_bool jffs2_flash_erase(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb);
+
+// dir-ecos.c
+struct _inode *jffs2_lookup(struct _inode *dir_i, const unsigned char *name, int namelen);
+int jffs2_create(struct _inode *dir_i, const unsigned char *d_name, int mode, struct _inode **new_i);
+int jffs2_mkdir (struct _inode *dir_i, const unsigned char *d_name, int mode);
+int jffs2_link (struct _inode *old_d_inode, struct _inode *dir_i, const unsigned char *d_name);
+int jffs2_unlink(struct _inode *dir_i, struct _inode *d_inode, const unsigned char *d_name);
+int jffs2_rmdir (struct _inode *dir_i, struct _inode *d_inode, const unsigned char *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);
+
+/* erase.c */
+static inline void jffs2_erase_pending_trigger(struct jffs2_sb_info *c)
+{ }
 
 #ifndef CONFIG_JFFS2_FS_NAND
 #define jffs2_can_mark_obsolete(c) (1)
@@ -247,16 +198,9 @@ int jffs2_commit_write (struct inode *d_
 #else
 #error no nand yet
 #endif
-struct inode *jffs2_new_inode (struct inode *dir_i, int mode, struct jffs2_raw_inode *ri);
-void jffs2_clear_inode (struct inode *inode);
-void jffs2_read_inode (struct inode *inode);
-
-static inline void jffs2_init_inode_info(struct jffs2_inode_info *f)
-{
-	memset(f, 0, sizeof(*f));
-	init_MUTEX_LOCKED(&f->sem);
-}
 
+#ifndef BUG_ON
 #define BUG_ON(x) do { if (unlikely(x)) BUG(); } while(0)
+#endif
 
 #endif /* __JFFS2_OS_ECOS_H__ */
Index: src/write.c
===================================================================
RCS file: /cvs/ecos/ecos/packages/fs/jffs2/current/src/write.c,v
retrieving revision 1.4
diff -u -p -r1.4 write.c
--- src/write.c	20 Nov 2003 16:52:36 -0000	1.4
+++ src/write.c	26 Nov 2003 22:24:14 -0000
@@ -7,7 +7,7 @@
  *
  * For licensing information, see the file 'LICENCE' in this directory.
  *
- * $Id: write.c,v 1.77 2003/11/18 21:14:02 dwmw2 Exp $
+ * $Id: write.c,v 1.78 2003/11/24 16:07:01 dwmw2 Exp $
  *
  */
 
@@ -373,7 +373,7 @@ int jffs2_write_inode_range(struct jffs2
 			break;
 		}
 		down(&f->sem);
-		datalen = writelen;
+		datalen = min_t(uint32_t, writelen, PAGE_CACHE_SIZE - (offset & (PAGE_CACHE_SIZE-1)));
 		cdatalen = min_t(uint32_t, alloclen - sizeof(*ri), writelen);
 
 		comprtype = jffs2_compress(buf, &comprbuf, &datalen, &cdatalen);

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]