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]

Latest JFFS2


... imported without prejudice from MTD :-)

-- 
Gary Thomas <gary@mlbassoc.com>
MLB Associates
Index: fs/jffs2/current/ChangeLog
===================================================================
RCS file: /misc/cvsfiles/ecos/packages/fs/jffs2/current/ChangeLog,v
retrieving revision 1.21
diff -u -5 -p -r1.21 ChangeLog
--- fs/jffs2/current/ChangeLog	25 Nov 2003 16:41:08 -0000	1.21
+++ fs/jffs2/current/ChangeLog	11 Dec 2003 23:28:09 -0000
@@ -1,5 +1,20 @@
+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
 	ARM gcc 3.2 compiler.
 
Index: fs/jffs2/current/cdl/jffs2.cdl
===================================================================
RCS file: /misc/cvsfiles/ecos/packages/fs/jffs2/current/cdl/jffs2.cdl,v
retrieving revision 1.12
diff -u -5 -p -r1.12 jffs2.cdl
--- fs/jffs2/current/cdl/jffs2.cdl	21 Nov 2003 19:05:24 -0000	1.12
+++ fs/jffs2/current/cdl/jffs2.cdl	11 Dec 2003 23:28:08 -0000
@@ -2,11 +2,11 @@
 #
 #      jffs2.cdl
 #
 #      JFFS2 Filesystem configuration data
 #
-#      $Id: jffs2.cdl,v 1.6 2003/11/20 16:41:57 dwmw2 Exp $
+#      $Id: jffs2.cdl,v 1.13 2003/11/29 00:30:30 dwmw2 Exp $
 #
 # ====================================================================
 #####ECOSGPLCOPYRIGHTBEGIN####
 ## -------------------------------------------
 ## This file is part of eCos, the Embedded Configurable Operating System.
@@ -70,11 +70,34 @@ cdl_package CYGPKG_FS_JFFS2 {
     requires       CYGPKG_CRC
 
     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 dir-ecos.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
+# Leave this off till it's been implemented. And don't implement it till
+# icache locking has been made thread-safe.
+ 	requires        CYGPKG_KERNEL && 0
+        description     "
+            Enable background garbage collection thread, for making 
+	    free space ahead of time."
+    }
+
+    cdl_option CYGOPT_FS_JFFS2_WRITE {
+	display         "Include write support for JFFS2"
+	flavor          bool
+	compile		gc.c write.c erase.c
+	default_value   1
+        description     "
+            Enable writing to JFFS2 file systems; not only reading."
+    }
 
     cdl_option CYGOPT_FS_JFFS2_NAND {
 	display         "Support for NAND flash"
 	flavor          bool
 	define          CONFIG_JFFS2_FS_NAND
@@ -164,32 +187,19 @@ cdl_package CYGPKG_FS_JFFS2 {
 
     cdl_option CYGPKG_FS_JFFS2_CFLAGS_ADD {
 	display "Additional compiler flags"
 	flavor  data
 	no_define
-	# We add -D__ECOS to trigger eCos-specific code in places.
-	# We add -Werror because I find it useful.
+	# We add '-D__ECOS' to trigger eCos-specific code in places.
+	# We add '-nostdinc -iwithprefix include' to avoid picking up
+	#    native <linux/*.h> include files when building on Linux.
 	default_value { "-D__ECOS -nostdinc -iwithprefix include" }
 	description   "
 	    This option modifies the set of compiler flags for
             building the JFFS2 package.
             These flags are used in addition
             to the set of global flags."
-        }
-
-    cdl_option CYGPKG_FS_JFFS2_CFLAGS_REMOVE {
-	display "Suppressed compiler flags"
-	flavor  data
-	no_define
-	# We remove -Wpointer-arith so that some of the hacky Linux-compat code 
-	# (in file.c) compiled. We can probably remove it when that's replaced
-	# properly.
-	default_value { "-Wpointer-arith" }
-	description   "
-	    This option modifies the set of compiler flags for
-            building the JFFS2 package. These flags are removed from
-            the set of global flags if present."
         }
 
     # ----------------------------------------------------------------
     # Tests
 
Index: fs/jffs2/current/src/compr.c
===================================================================
RCS file: /misc/cvsfiles/ecos/packages/fs/jffs2/current/src/compr.c,v
retrieving revision 1.4
diff -u -5 -p -r1.4 compr.c
--- fs/jffs2/current/src/compr.c	20 Nov 2003 16:52:36 -0000	1.4
+++ fs/jffs2/current/src/compr.c	11 Dec 2003 23:28:08 -0000
@@ -5,20 +5,21 @@
  *
  * Created by Arjan van de Ven <arjanv@redhat.com>
  *
  * 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.33 2003/11/28 17:22:54 dwmw2 Exp $
  *
  */
 
 #include <linux/kernel.h>
 #include <linux/string.h>
 #include <linux/errno.h>
 #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);
 int jffs2_rtime_compress(unsigned char *data_in, unsigned char *cpage_out, uint32_t *sourcelen, uint32_t *dstlen);
 void jffs2_rtime_decompress(unsigned char *data_in, unsigned char *cpage_out, uint32_t srclen, uint32_t destlen);
@@ -52,46 +53,47 @@ unsigned char jffs2_compress(unsigned ch
 	int ret;
 
 	*cpage_out = kmalloc(*cdatalen, GFP_KERNEL);
 	if (!*cpage_out) {
 		printk(KERN_WARNING "No memory for compressor allocation. Compression failed\n");
-		return JFFS2_COMPR_NONE;
+		goto out;
 	}
 
 #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;
 	}
 #endif
 	kfree(*cpage_out);
 #endif /* Compression */
+ out:
 	*cpage_out = data_in;
 	*datalen = *cdatalen;
 	return JFFS2_COMPR_NONE; /* We failed to compress */
 }
 
-void jffs2_free_comprbuf(unsigned char *orig, unsigned char *comprbuf)
+void jffs2_free_comprbuf(unsigned char *comprbuf, unsigned char *orig)
 {
 	if (orig != comprbuf)
 		kfree(comprbuf);
 }
 
Index: fs/jffs2/current/src/compr_zlib.c
===================================================================
RCS file: /misc/cvsfiles/ecos/packages/fs/jffs2/current/src/compr_zlib.c,v
retrieving revision 1.5
diff -u -5 -p -r1.5 compr_zlib.c
--- fs/jffs2/current/src/compr_zlib.c	20 Nov 2003 16:52:36 -0000	1.5
+++ fs/jffs2/current/src/compr_zlib.c	11 Dec 2003 23:28:08 -0000
@@ -5,22 +5,20 @@
  *
  * Created by David Woodhouse <dwmw2@redhat.com>
  *
  * For licensing information, see the file 'LICENCE' in this directory.
  *
- * $Id: compr_zlib.c,v 1.24 2003/10/04 08:33:06 dwmw2 Exp $
+ * $Id: compr_zlib.c,v 1.25 2003/12/03 09:25:43 dwmw2 Exp $
  *
  */
 
 #if !defined(__KERNEL__) && !defined(__ECOS)
 #error "The userspace support got too messy and was removed. Update your mkfs.jffs2"
 #endif
 
 #include <linux/config.h>
 #include <linux/kernel.h>
-#include <linux/vmalloc.h>
-#include <linux/init.h>
 #include <linux/slab.h>
 #include <linux/zlib.h>
 #include <linux/zutil.h>
 #include <asm/semaphore.h>
 #include "nodelist.h"
@@ -37,10 +35,13 @@
 static DECLARE_MUTEX(deflate_sem);
 static DECLARE_MUTEX(inflate_sem);
 static z_stream inf_strm, def_strm;
 
 #ifdef __KERNEL__ /* Linux-only */
+#include <linux/vmalloc.h>
+#include <linux/init.h>
+
 int __init jffs2_zlib_init(void)
 {
 	def_strm.workspace = vmalloc(zlib_deflate_workspacesize());
 	if (!def_strm.workspace) {
 		printk(KERN_WARNING "Failed to allocate %d bytes for deflate workspace\n", zlib_deflate_workspacesize());
Index: fs/jffs2/current/src/dir-ecos.c
===================================================================
RCS file: /misc/cvsfiles/ecos/packages/fs/jffs2/current/src/dir-ecos.c,v
retrieving revision 1.4
diff -u -5 -p -r1.4 dir-ecos.c
--- fs/jffs2/current/src/dir-ecos.c	20 Nov 2003 16:52:36 -0000	1.4
+++ fs/jffs2/current/src/dir-ecos.c	11 Dec 2003 23:28:08 -0000
@@ -1,60 +1,57 @@
 /*
  * 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 $
  *
  */
 
 #include <linux/kernel.h>
 #include <linux/crc32.h>
 #include "nodelist.h"
 
 /***********************************************************************/
 
-
-/* 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"));
 
 	dir_f = JFFS2_INODE_INFO(dir_i);
 	c = JFFS2_SB_INFO(dir_i->i_sb);
 
 	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;
 		}
 	}
 	if (fd)
 		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));
 		}
 	}
 
 	return inode;
@@ -62,17 +59,17 @@ 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();
 	if (!ri)
 		return -ENOMEM;
@@ -91,16 +88,15 @@ int jffs2_create(struct inode *dir_i, st
 
 	f = JFFS2_INODE_INFO(inode);
 	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;
 	}
 
 	jffs2_free_raw_inode(ri);
@@ -112,52 +108,52 @@ 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;
 }
 /***********************************************************************/
 
 
-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);
 	struct jffs2_inode_info *dir_f = JFFS2_INODE_INFO(dir_i);
 	int ret;
 
 	/* XXX: This is ugly */
 	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);
 		old_d_inode->i_nlink = ++f->inocache->nlink;
 		up(&f->sem);
 	}
 	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;
 	struct jffs2_full_dirent *fd;
 	int namelen;
@@ -173,11 +169,11 @@ int jffs2_mkdir (struct inode *dir_i, st
 	c = JFFS2_SB_INFO(dir_i->i_sb);
 
 	/* 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) {
 		jffs2_free_raw_inode(ri);
 		return ret;
@@ -202,11 +198,12 @@ int jffs2_mkdir (struct inode *dir_i, st
 
 	if (IS_ERR(fn)) {
 		/* 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 
 	   obsoleted by the first data write
 	*/
@@ -215,19 +212,21 @@ int jffs2_mkdir (struct inode *dir_i, st
 
 	jffs2_complete_reservation(c);
 	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;
 	}
 	
 	rd = jffs2_alloc_raw_dirent();
 	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;
 	}
 
 	dir_f = JFFS2_INODE_INFO(dir_i);
 	down(&dir_f->sem);
@@ -242,35 +241,36 @@ int jffs2_mkdir (struct inode *dir_i, st
 	rd->ino = cpu_to_je32(inode->i_ino);
 	rd->mctime = cpu_to_je32(cyg_timestamp());
 	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);
 	
 	if (IS_ERR(fd)) {
 		/* 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);
 	}
 
 	/* Link the fd into the inode's list, obsoleting an old
 	   one if necessary. */
 	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;
 
 	for (fd = f->dents ; fd; fd = fd->next) {
@@ -278,12 +278,12 @@ int jffs2_rmdir (struct inode *dir_i, st
 			return EPERM; //-ENOTEMPTY;
 	}
 	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);
 	struct jffs2_inode_info *victim_f = NULL;
 	uint8_t type;
@@ -329,11 +329,11 @@ int jffs2_rename (struct inode *old_dir_
 	type = (d_inode->i_mode & S_IFMT) >> 12;
 	if (!type) type = DT_REG;
 
 	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;
 
 	if (victim_f) {
@@ -347,11 +347,11 @@ 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 */
 		struct jffs2_inode_info *f = JFFS2_INODE_INFO(d_inode);
 		down(&f->sem);
Index: fs/jffs2/current/src/erase.c
===================================================================
RCS file: /misc/cvsfiles/ecos/packages/fs/jffs2/current/src/erase.c,v
retrieving revision 1.5
diff -u -5 -p -r1.5 erase.c
--- fs/jffs2/current/src/erase.c	20 Nov 2003 16:52:36 -0000	1.5
+++ fs/jffs2/current/src/erase.c	11 Dec 2003 23:28:08 -0000
@@ -5,11 +5,11 @@
  *
  * Created by David Woodhouse <dwmw2@redhat.com>
  *
  * 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 $
  *
  */
 
 #include <linux/kernel.h>
 #include <linux/slab.h>
@@ -279,15 +279,10 @@ static void jffs2_free_all_node_refs(str
 		/* else it was a non-inode node or already removed, so don't bother */
 
 		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)
 {
 	struct jffs2_raw_node_ref *marker_ref = NULL;
Index: fs/jffs2/current/src/fs-ecos.c
===================================================================
RCS file: /misc/cvsfiles/ecos/packages/fs/jffs2/current/src/fs-ecos.c,v
retrieving revision 1.15
diff -u -5 -p -r1.15 fs-ecos.c
--- fs/jffs2/current/src/fs-ecos.c	25 Nov 2003 16:41:08 -0000	1.15
+++ fs/jffs2/current/src/fs-ecos.c	11 Dec 2003 23:28:08 -0000
@@ -1,34 +1,33 @@
 /*
  * 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.33 2003/12/02 10:43:03 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:
 #error http://ecos.sourceware.org/ml/ecos-patches/2003-08/msg00006.html
 #endif
@@ -38,396 +37,399 @@
 
 // Filesystem operations
 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);
+#ifdef CYGOPT_FS_JFFS2_WRITE
 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);
+#endif
 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);
+#ifdef CYGOPT_FS_JFFS2_WRITE
 static int jffs2_fo_write(struct CYG_FILE_TAG *fp, struct CYG_UIO_TAG *uio);
+#endif
 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
 
 // -------------------------------------------------------------------------
 // Fstab entry.
 // This defines the entry in the filesystem table.
 // For simplicity we use _FILESYSTEM synchronization for all accesses since
 // we should never block in any filesystem operations.
 
+#ifdef CYGOPT_FS_JFFS2_WRITE
+FSTAB_ENTRY(jffs2_fste, "jffs2", 0,
+	    CYG_SYNCMODE_FILE_FILESYSTEM | CYG_SYNCMODE_IO_FILE,
+	    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);
+#else
 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_FILE,
+	    jffs2_mount,
+	    jffs2_umount,
+	    jffs2_open,
+	    (cyg_fsop_unlink *)cyg_fileio_erofs,
+	    (cyg_fsop_mkdir *)cyg_fileio_erofs,
+	    (cyg_fsop_rmdir *)cyg_fileio_erofs,
+	    (cyg_fsop_rename *)cyg_fileio_erofs,
+	    (cyg_fsop_link *)cyg_fileio_erofs,
+	    jffs2_opendir,
+	    jffs2_chdir, jffs2_stat, jffs2_getinfo, jffs2_setinfo);
+#endif
 
 // -------------------------------------------------------------------------
 // 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,
+#ifdef CYGOPT_FS_JFFS2_WRITE
+	jffs2_fo_write,
+#else
+	(cyg_fileop_write *) cyg_fileio_erofs,
+#endif
+	jffs2_fo_lseek,
+	jffs2_fo_ioctl,
+	cyg_fileio_seltrue,
+	jffs2_fo_fsync,
+	jffs2_fo_close,
+	jffs2_fo_fstat,
+	jffs2_fo_getinfo,
+	jffs2_fo_setinfo
 };
 
 // -------------------------------------------------------------------------
 // Directory file operations.
 // This set of operations are used for open directories. Most entries
 // point to error-returning stub functions. Only the read, lseek and
 // 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;
 
 //==========================================================================
 // 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;
+	}
 }
 
 //==========================================================================
 // Directory search
 
 // -------------------------------------------------------------------------
 // init_dirsearch()
 // 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;
 }
 
 // -------------------------------------------------------------------------
 // find_entry()
 // Search a single directory for the next name in a path and update the
 // dirsearch object appropriately.
 
 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;
 
 }
 
 // -------------------------------------------------------------------------
 // jffs2_find()
 // 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;
 }
 
 //==========================================================================
 // Filesystem operations
 
@@ -435,787 +437,756 @@ static int jffs2_pathconf(struct inode *
 // jffs2_mount()
 // Process a mount request. This mainly creates a root for the
 // 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"));
+#ifdef CYGOPT_FS_JFFS2_WRITE
+		if (!jffs2_is_readonly(c))
+		    jffs2_erase_pending_blocks(c,0);
+#endif
+#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;
+extern cyg_mtab_entry *cyg_cdir_mtab_entry;
+
 // -------------------------------------------------------------------------
 // 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_mtab_entry == mte &&
+		    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);
+		}
+		/* Argh. The fileio code sets this; never clears it */
+		if (cyg_cdir_mtab_entry == mte)
+			cyg_cdir_mtab_entry = NULL;
+
+		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;
 }
 
 // -------------------------------------------------------------------------
 // jffs2_open()
 // 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);
-
-        err = jffs2_find(&ds);
+	jffs2_dirsearch ds;
+	struct _inode *node = NULL;
+	int err;
 
-        if (err == ENOENT) {
-                if (ds.last && (mode & O_CREAT)) {
-                        unsigned long hash;
-                        struct qstr this;
-                        unsigned int c;
-                        const char *hashname;
+	D2(printf("jffs2_open\n"));
 
-                        // 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.
+	/* If no chdir has been called and we were the first file system
+	   mounted, we get called with dir == NULL. Deal with it */
+	if (!dir)
+		dir = mte->root;
 
-                        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;
-        }
+#ifndef CYGOPT_FS_JFFS2_WRITE
+	if (mode & (O_CREAT|O_TRUNC|O_WRONLY))
+		return EROFS;
+#endif
+	init_dirsearch(&ds, (struct _inode *) dir, name);
 
-        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();
-        }
+	err = jffs2_find(&ds);
 
-        if (err != ENOERR)
-                return err;
+	if (err == ENOENT) {
+#ifdef CYGOPT_FS_JFFS2_WRITE
+		if (ds.last && (mode & O_CREAT)) {
 
-        // Check that we actually have a file here
-        if (S_ISDIR(node->i_mode))
-                return EISDIR;
+			// 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.
 
-        node->i_count++;        // Count successful open
+			err = jffs2_create(ds.dir, ds.name, S_IRUGO|S_IXUGO|S_IWUSR|S_IFREG, &node);
 
-        // Initialize the file object
+			if (err != 0) {
+				//Possible orphaned inode on the flash - but will be gc'd
+				return err;
+			}
 
-        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 = ENOERR;
+		}
+#endif
+	} 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;
+	}
+
+#ifndef CYGOPT_FS_JFFS2_WRITE
+	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();
+	}
+#endif
+	// 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;
 
-        return ENOERR;
+	return ENOERR;
 }
 
+#ifdef CYGOPT_FS_JFFS2_WRITE
 // -------------------------------------------------------------------------
 // jffs2_ops_unlink()
 // Remove a file link from its directory.
 
 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"));
+	jffs2_dirsearch ds;
+	int err;
 
-        icache_evict((struct inode *) mte->root, (struct inode *) dir);
+	D2(printf("jffs2_ops_unlink\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;
+	if (err != ENOERR) {
+		jffs2_iput(ds.dir);
+		return err;
+	}
 
-        // Cannot unlink directories, use rmdir() instead
-        if (S_ISDIR(ds.node->i_mode))
-                return EPERM;
+	// Cannot unlink directories, use rmdir() instead
+	if (S_ISDIR(ds.node->i_mode)) {
+		jffs2_iput(ds.dir);
+		jffs2_iput(ds.node);
+		return EPERM;
+	}
 
-        // Delete it from its directory
+	// Delete it from its directory
 
-        hashname = ds.name;
-        this.name = hashname;
-        c = *(const unsigned char *) hashname;
+	err = jffs2_unlink(ds.dir, ds.node, ds.name);
+	jffs2_iput(ds.dir);
+	jffs2_iput(ds.node);
 
-        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, &this);
-
-        return err;
+	return -err;
 }
 
 // -------------------------------------------------------------------------
 // jffs2_ops_mkdir()
 // Create a new directory.
 
 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);
+	jffs2_dirsearch ds;
+	int err;
 
-                        err = jffs2_mkdir(ds.dir, &this, 0, &node);
+	D2(printf("jffs2_ops_mkdir\n"));
 
-                        if (err != 0)
-                                return ENOSPC;
+	init_dirsearch(&ds, (struct _inode *) dir, name);
 
-                }
-                // 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.
-
-                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;
 }
 
 // -------------------------------------------------------------------------
 // jffs2_ops_rmdir()
 // Remove a directory.
 
 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;
+	jffs2_dirsearch ds;
+	int err;
 
-        D2(printf("jffs2_ops_rmdir\n"));
+	D2(printf("jffs2_ops_rmdir\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);
 
-        err = jffs2_find(&ds);
+	if (err != ENOERR) {
+		jffs2_iput(ds.dir);
+		return err;
+	}
 
-        if (err != ENOERR)
-                return err;
+	// Check that this is actually a directory.
+	if (!S_ISDIR(ds.node->i_mode)) {
+		jffs2_iput(ds.dir);
+		jffs2_iput(ds.node);
+		return EPERM;
+	}
 
-        // Check that this is actually a directory.
-        if (!S_ISDIR(ds.node->i_mode))
-                return EPERM;
+	err = jffs2_rmdir(ds.dir, ds.node, ds.name);
 
-        // Delete the entry. 
-        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_rmdir(ds.dir, ds.node, &this);
-
-        return err;
-
-        return ENOERR;
+	jffs2_iput(ds.dir);
+	jffs2_iput(ds.node);
+	return -err;
 }
 
 // -------------------------------------------------------------------------
 // jffs2_ops_rename()
 // 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;
-
-                if (S_ISDIR(ds1.node->i_mode) && !S_ISDIR(ds2.node->i_mode))
-                        return ENOTDIR;
-
-                // Now delete the destination directory entry
+	jffs2_dirsearch ds1, ds2;
+	int err;
 
-                err = jffs2_unlink(ds2.dir, ds2.node, &this2);
+	D2(printf("jffs2_ops_rename\n"));
 
-                if (err != 0)
-                        return err;
+	init_dirsearch(&ds1, (struct _inode *) dir1, name1);
 
-        }
-        // 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;
 }
 
 // -------------------------------------------------------------------------
 // jffs2_ops_link()
 // 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;
 }
-
+#endif /* CYGOPT_FS_JFFS2_WRITE */
 // -------------------------------------------------------------------------
 // jffs2_opendir()
 // 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;
 
 }
 
 // -------------------------------------------------------------------------
 // jffs2_chdir()
 // 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"));
-
-        if (dir_out != NULL) {
-                // This is a request to get a new directory pointer in
-                // *dir_out.
+	D2(printf("jffs2_chdir\n"));
 
-                jffs2_dirsearch ds;
-                int err;
+	if (dir_out != NULL) {
+		// This is a request to get a new directory pointer in
+		// *dir_out.
 
-                icache_evict((struct inode *) mte->root, (struct inode *) dir);
+		jffs2_dirsearch ds;
+		int err;
 
-                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;
 
-                // 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;
 
-                // Increment ref count to keep this directory in existance
-                // while it is the current cdir.
-                ds.node->i_count++;
+		// 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.
 
-                // 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.
+		struct _inode *node = (struct _inode *) dir;
 
-                struct inode *node = (struct inode *) dir;
+		// Just decrement directory reference count.
+		jffs2_iput(node);
+	}
 
-                // Just decrement directory reference count.
-                dec_refcnt(node);
-        }
-
-        return ENOERR;
+	return ENOERR;
 }
 
 // -------------------------------------------------------------------------
 // jffs2_stat()
 // 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;
-
-        D2(printf("jffs2_stat\n"));
+	jffs2_dirsearch ds;
+	int err;
 
-        icache_evict((struct inode *) mte->root, (struct inode *) dir);
+	D2(printf("jffs2_stat\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;
 
-        // 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 = ds.node->i_uid;
+	buf->st_gid = ds.node->i_gid;
+	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;
 
-        return err;
+	jffs2_iput(ds.node);
 
-        return ENOERR;
+	return ENOERR;
 }
 
 // -------------------------------------------------------------------------
 // jffs2_getinfo()
 // 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;
+	jffs2_dirsearch ds;
+	int err;
 
-        D2(printf("jffs2_getinfo\n"));
+	D2(printf("jffs2_getinfo\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;
+	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;
 }
 
 // -------------------------------------------------------------------------
 // jffs2_setinfo()
 // 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;
 }
 
 //==========================================================================
 // File operations
 
@@ -1223,305 +1194,342 @@ static int jffs2_setinfo(cyg_mtab_entry 
 // jffs2_fo_read()
 // Read data from the file.
 
 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.
-
-        inode->i_atime = cyg_timestamp();
+	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();
+
+	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;
 }
 
+
+#ifdef CYGOPT_FS_JFFS2_WRITE
 // -------------------------------------------------------------------------
 // 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;
-                }
-        }
-
-        // 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;
+	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;
+
+		uint32_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;
 
-        uio->uio_resid = resid;
-        fp->f_offset = pos;
+	uio->uio_resid = resid;
+	fp->f_offset = pos;
 
-        return ENOERR;
+	return ENOERR;
 }
+#endif /* CYGOPT_FS_JFFS2_WRITE */
 
 // -------------------------------------------------------------------------
 // jffs2_fo_lseek()
 // Seek to a new file position.
 
 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"));
+	struct _inode *node = (struct _inode *) fp->f_data;
+	off_t pos = *apos;
 
-        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;
+	D2(printf("jffs2_fo_lseek\n"));
 
-        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;
+	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;
 }
 
 // -------------------------------------------------------------------------
 // jffs2_fo_ioctl()
 // 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;
 }
 
 // -------------------------------------------------------------------------
 // jffs2_fo_fsync().
 // Force the file out to data storage.
 
 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;
 }
 
 // -------------------------------------------------------------------------
 // jffs2_fo_close()
 // Close a file. We just decrement the refcnt and let it go away if
 // that is all that is keeping it here.
 
 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;
 }
 
 // -------------------------------------------------------------------------
 //jffs2_fo_fstat()
 // Get file status.
 
 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 = node->i_uid;
+	buf->st_gid = node->i_gid;
+	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;
 }
 
 // -------------------------------------------------------------------------
 // jffs2_fo_getinfo()
 // 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;
 }
 
 // -------------------------------------------------------------------------
 // jffs2_fo_setinfo()
 // 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;
 }
 
 //==========================================================================
 // Directory operations
 
@@ -1529,456 +1537,288 @@ static int jffs2_fo_setinfo(struct CYG_F
 // jffs2_fo_dirread()
 // Read a single directory entry from a file.
 
 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;
 }
 
 // -------------------------------------------------------------------------
 // jffs2_fo_dirlseek()
 // Seek directory to start.
 
 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;
 }
 
 //==========================================================================
 // 
 // Called by JFFS2
 // ===============
 // 
 //
 //==========================================================================
 
-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
-
-        int err;
-        struct page *gc_page = malloc(sizeof (struct page));
+	/* FIXME: This works only with one file system mounted at a time */
+	int 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;
-                }
-        }
+	ret = jffs2_read_inode_range(c, f, gc_buffer, offset, PAGE_CACHE_SIZE);
+	if (ret)
+		return ERR_PTR(ret);
 
-        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
-
-        struct inode *inode;
-        struct inode *cached_inode;
+	// Only called in write.c jffs2_new_inode
+	// Always adds itself to inode cache
 
-        inode = malloc(sizeof (struct inode));
-        if (inode == NULL)
-                return 0;
+	struct _inode *inode;
+	struct _inode *cached_inode;
 
-        D2(printf
-           ("malloc new_inode %x ####################################\n",
-            inode));
+	inode = malloc(sizeof (struct _inode));
+	if (inode == NULL)
+		return 0;
 
-        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
+	D2(printf
+	   ("malloc new_inode %x ####################################\n",
+	    inode));
 
-        inode->i_nlink = 1;     // Let JFFS2 manage the link count
-        inode->i_size = 0;
+	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_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
+	// Must first check for cached inode 
+	// If this fails let new_inode create one
 
-        // Called in super.c jffs2_read_super, dir.c jffs2_lookup,
-        // and gc.c jffs2_garbage_collect_pass
+	struct _inode *inode;
+	int err;
 
-        // Must first check for cached inode 
-        // If this fails let new_inode create one
+	D2(printf("jffs2_iget\n"));
 
-        struct inode *inode;
+	inode = ilookup(sb, ino);
+	if (inode)
+		return inode;
 
-        D2(printf("iget\n"));
+	// Not cached, so malloc it
+	inode = new_inode(sb);
+	if (inode == NULL)
+		return 0;
 
-        inode = ilookup(sb, ino);
-        if (inode)
-                return inode;
+	inode->i_ino = ino;
 
-        // Not cached, so malloc it
-        inode = new_inode(sb);
-        if (inode == NULL)
-                return 0;
-
-        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.
          */
         struct jffs2_sb_info *c = JFFS2_SB_INFO(inode->i_sb);
@@ -1990,85 +1830,229 @@ 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: fs/jffs2/current/src/gc.c
===================================================================
RCS file: /misc/cvsfiles/ecos/packages/fs/jffs2/current/src/gc.c,v
retrieving revision 1.5
diff -u -5 -p -r1.5 gc.c
--- fs/jffs2/current/src/gc.c	20 Nov 2003 16:52:36 -0000	1.5
+++ fs/jffs2/current/src/gc.c	11 Dec 2003 23:28:08 -0000
@@ -5,11 +5,11 @@
  *
  * Created by David Woodhouse <dwmw2@redhat.com>
  *
  * 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.132 2003/12/01 11:32:11 dwmw2 Exp $
  *
  */
 
 #include <linux/kernel.h>
 #include <linux/mtd/mtd.h>
@@ -34,11 +34,11 @@ static int jffs2_garbage_collect_hole(st
 				      uint32_t start, uint32_t end);
 static int jffs2_garbage_collect_dnode(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb,
 				       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)
 {
 	struct jffs2_eraseblock *ret;
@@ -110,14 +110,15 @@ static struct jffs2_eraseblock *jffs2_fi
  * Make a single attempt to progress GC. Move one node, and possibly
  * start erasing one eraseblock.
  */
 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;
 
 	for (;;) {
@@ -247,11 +248,13 @@ int jffs2_garbage_collect_pass(struct jf
 		goto eraseit_lock;
 	}
 
 	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);
 
 	D1(printk(KERN_DEBUG "jffs2_garbage_collect_pass collecting from block @0x%08x. Node @0x%08x(%d), ino #%u\n", jeb->offset, ref_offset(raw), ref_flags(raw), ic->ino));
@@ -341,18 +344,30 @@ 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);
 
  eraseit_lock:
@@ -372,82 +387,18 @@ int jffs2_garbage_collect_pass(struct jf
 
 	return ret;
 }
 
 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
 	   of the list. */
 
@@ -484,14 +435,14 @@ static int jffs2_garbage_collect_live(st
 				break; /* We've found them all */
 		}
 	}
 	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;
 		}
 		/* We found a datanode. Do the GC */
@@ -524,12 +475,10 @@ static int jffs2_garbage_collect_live(st
 			ret = -EIO;
 		}
 	}
  upnout:
 	up(&f->sem);
- put_out:
-	iput(inode);
 
 	return ret;
 }
 
 static int jffs2_garbage_collect_pristine(struct jffs2_sb_info *c, 
@@ -1073,14 +1022,13 @@ static int jffs2_garbage_collect_dnode(s
 	struct jffs2_full_dnode *new_fn;
 	struct jffs2_raw_inode ri;
 	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",
 		  f->inocache->ino, start, end));
 
@@ -1215,20 +1163,16 @@ static int jffs2_garbage_collect_dnode(s
 	 *    triggered garbage collection in the first place?
 	 * A: I _think_ it's OK. read_cache_page shouldn't deadlock, we'll write out the
 	 *    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) {
 		uint32_t datalen;
 		uint32_t cdatalen;
@@ -1265,11 +1209,11 @@ static int jffs2_garbage_collect_dnode(s
 		ri.offset = cpu_to_je32(offset);
 		ri.csize = cpu_to_je32(cdatalen);
 		ri.dsize = cpu_to_je32(datalen);
 		ri.compr = comprtype;
 		ri.node_crc = cpu_to_je32(crc32(0, &ri, sizeof(ri)-8));
-		ri.data_crc = cpu_to_je32(crc32(0, writebuf, cdatalen));
+		ri.data_crc = cpu_to_je32(crc32(0, comprbuf, cdatalen));
 	
 		new_fn = jffs2_write_dnode(c, f, &ri, comprbuf, cdatalen, phys_ofs, ALLOC_GC);
 
 		jffs2_free_comprbuf(comprbuf, writebuf);
 
@@ -1285,12 +1229,9 @@ static int jffs2_garbage_collect_dnode(s
 			jffs2_free_full_dnode(f->metadata);
 			f->metadata = NULL;
 		}
 	}
 
-	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: fs/jffs2/current/src/malloc-ecos.c
===================================================================
RCS file: /misc/cvsfiles/ecos/packages/fs/jffs2/current/src/malloc-ecos.c,v
retrieving revision 1.4
diff -u -5 -p -r1.4 malloc-ecos.c
--- fs/jffs2/current/src/malloc-ecos.c	20 Nov 2003 21:06:26 -0000	1.4
+++ fs/jffs2/current/src/malloc-ecos.c	11 Dec 2003 23:28:08 -0000
@@ -1,15 +1,15 @@
 /*
  * 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 $
  *
  */
 
 #include <linux/kernel.h>
 #include <cyg/hal/drv_api.h>
Index: fs/jffs2/current/src/nodelist.h
===================================================================
RCS file: /misc/cvsfiles/ecos/packages/fs/jffs2/current/src/nodelist.h,v
retrieving revision 1.5
diff -u -5 -p -r1.5 nodelist.h
--- fs/jffs2/current/src/nodelist.h	20 Nov 2003 16:52:36 -0000	1.5
+++ fs/jffs2/current/src/nodelist.h	11 Dec 2003 23:28:08 -0000
@@ -5,11 +5,11 @@
  *
  * Created by David Woodhouse <dwmw2@redhat.com>
  *
  * 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 $
  *
  */
 
 #ifndef __JFFS2_NODELIST_H__
 #define __JFFS2_NODELIST_H__
@@ -376,10 +376,11 @@ void jffs2_fragtree_insert(struct jffs2_
 struct rb_node *rb_next(struct rb_node *);
 struct rb_node *rb_prev(struct rb_node *);
 void rb_replace_node(struct rb_node *victim, struct rb_node *new, struct rb_root *root);
 
 /* 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);
 void jffs2_complete_reservation(struct jffs2_sb_info *c);
 void jffs2_mark_node_obsolete(struct jffs2_sb_info *c, struct jffs2_raw_node_ref *raw);
@@ -451,11 +452,10 @@ void jffs2_rotate_lists(struct jffs2_sb_
 int jffs2_do_mount_fs(struct jffs2_sb_info *c);
 
 /* 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 */
 int jffs2_flush_wbuf_gc(struct jffs2_sb_info *c, uint32_t ino);
 int jffs2_flush_wbuf_pad(struct jffs2_sb_info *c);
Index: fs/jffs2/current/src/nodemgmt.c
===================================================================
RCS file: /misc/cvsfiles/ecos/packages/fs/jffs2/current/src/nodemgmt.c,v
retrieving revision 1.5
diff -u -5 -p -r1.5 nodemgmt.c
--- fs/jffs2/current/src/nodemgmt.c	20 Nov 2003 16:52:36 -0000	1.5
+++ fs/jffs2/current/src/nodemgmt.c	11 Dec 2003 23:28:08 -0000
@@ -5,11 +5,11 @@
  *
  * Created by David Woodhouse <dwmw2@redhat.com>
  *
  * 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 $
  *
  */
 
 #include <linux/kernel.h>
 #include <linux/slab.h>
@@ -706,5 +706,36 @@ void jffs2_dump_block_lists(struct jffs2
 			       jeb->offset, jeb->used_size, jeb->dirty_size, jeb->wasted_size, jeb->unchecked_size, jeb->free_size);
 		}
 	}
 }
 #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: fs/jffs2/current/src/os-ecos.h
===================================================================
RCS file: /misc/cvsfiles/ecos/packages/fs/jffs2/current/src/os-ecos.h,v
retrieving revision 1.7
diff -u -5 -p -r1.7 os-ecos.h
--- fs/jffs2/current/src/os-ecos.h	20 Nov 2003 21:06:26 -0000	1.7
+++ fs/jffs2/current/src/os-ecos.h	11 Dec 2003 23:28:08 -0000
@@ -1,15 +1,15 @@
 /*
  * 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.19 2003/11/28 11:38:45 dwmw2 Exp $
  *
  */
 
 #ifndef __JFFS2_OS_ECOS_H__
 #define __JFFS2_OS_ECOS_H__
@@ -18,218 +18,172 @@
 #include <cyg/io/io.h>
 #include <sys/types.h>
 #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 */
+#ifdef CYGOPT_FS_JFFS2_WRITE
 #define jffs2_is_readonly(c) (0)
+#else
+#define jffs2_is_readonly(c) (1)
+#endif
 
 /* NAND flash not currently supported on eCos */
 #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)
 #define JFFS2_F_I_UID(f) (OFNI_EDONI_2SFFJ(f)->i_uid)
 #define JFFS2_F_I_GID(f) (OFNI_EDONI_2SFFJ(f)->i_gid)
 #define JFFS2_F_I_CTIME(f) (OFNI_EDONI_2SFFJ(f)->i_ctime)
 #define JFFS2_F_I_MTIME(f) (OFNI_EDONI_2SFFJ(f)->i_mtime)
 #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 sleep_on_spinunlock(wq, sl) spin_unlock(sl)
 #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)
 #define jffs2_cleanmarker_oob(c) (0)
 #define jffs2_write_nand_cleanmarker(c,jeb) (-EIO)
@@ -245,18 +199,11 @@ int jffs2_commit_write (struct inode *d_
 #define jffs2_wbuf_timeout NULL
 #define jffs2_wbuf_process NULL
 #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: fs/jffs2/current/src/write.c
===================================================================
RCS file: /misc/cvsfiles/ecos/packages/fs/jffs2/current/src/write.c,v
retrieving revision 1.4
diff -u -5 -p -r1.4 write.c
--- fs/jffs2/current/src/write.c	20 Nov 2003 16:52:36 -0000	1.4
+++ fs/jffs2/current/src/write.c	11 Dec 2003 23:28:08 -0000
@@ -5,11 +5,11 @@
  *
  * Created by David Woodhouse <dwmw2@redhat.com>
  *
  * 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.79 2003/12/03 09:41:03 dwmw2 Exp $
  *
  */
 
 #include <linux/kernel.h>
 #include <linux/fs.h>
@@ -371,12 +371,12 @@ int jffs2_write_inode_range(struct jffs2
 		if (ret) {
 			D1(printk(KERN_DEBUG "jffs2_reserve_space returned %d\n", ret));
 			break;
 		}
 		down(&f->sem);
-		datalen = writelen;
-		cdatalen = min_t(uint32_t, alloclen - sizeof(*ri), writelen);
+		datalen = min_t(uint32_t, writelen, PAGE_CACHE_SIZE - (offset & (PAGE_CACHE_SIZE-1)));
+		cdatalen = min_t(uint32_t, alloclen - sizeof(*ri), datalen);
 
 		comprtype = jffs2_compress(buf, &comprbuf, &datalen, &cdatalen);
 
 		ri->magic = cpu_to_je16(JFFS2_MAGIC_BITMASK);
 		ri->nodetype = cpu_to_je16(JFFS2_NODETYPE_INODE);
Index: fs/jffs2/current/tests/fileio1.c
===================================================================
RCS file: /misc/cvsfiles/ecos/packages/fs/jffs2/current/tests/fileio1.c,v
retrieving revision 1.2
diff -u -5 -p -r1.2 fileio1.c
--- fs/jffs2/current/tests/fileio1.c	23 May 2002 23:01:39 -0000	1.2
+++ fs/jffs2/current/tests/fileio1.c	11 Dec 2003 23:28:09 -0000
@@ -95,11 +95,11 @@ MTAB_ENTRY( jffs2_mte1,
 #define SHOW_RESULT( _fn, _res ) \
 diag_printf("<FAIL>: " #_fn "() returned %d %s\n", _res, _res<0?strerror(errno):"");
 
 //==========================================================================
 
-#define IOSIZE  100
+#define IOSIZE  1000
 
 #define LONGNAME1       "long_file_name_that_should_take_up_more_than_one_directory_entry_1"
 #define LONGNAME2       "long_file_name_that_should_take_up_more_than_one_directory_entry_2"
 
 
@@ -180,11 +180,11 @@ static void listdir( char *name, int sta
 
 //==========================================================================
 
 static void createfile( char *name, size_t size )
 {
-    char buf[IOSIZE];
+    unsigned char buf[IOSIZE];
     int fd;
     ssize_t wrote;
     int i;
     int err;
 
@@ -253,11 +253,11 @@ static void maxfile( char *name )
 
 //==========================================================================
 
 static void checkfile( char *name )
 {
-    char buf[IOSIZE];
+    unsigned char buf[IOSIZE];
     int fd;
     ssize_t done;
     int i;
     int err;
     off_t pos = 0;
Index: fs/jffs2/current/doc/README.Locking
===================================================================
RCS file: fs/jffs2/current/doc/README.Locking
diff -N fs/jffs2/current/doc/README.Locking
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ fs/jffs2/current/doc/README.Locking	11 Dec 2003 23:28:08 -0000
@@ -0,0 +1,116 @@
+	$Id: README.Locking,v 1.6 2003/11/02 19:27:54 dwmw2 Exp $
+
+	JFFS2 LOCKING DOCUMENTATION
+	---------------------------
+
+At least theoretically, JFFS2 does not require the Big Kernel Lock
+(BKL), which was always helpfully obtained for it by Linux 2.4 VFS
+code. It has its own locking, as described below.
+
+This document attempts to describe the existing locking rules for
+JFFS2. It is not expected to remain perfectly up to date, but ought to
+be fairly close.
+
+
+	alloc_sem
+	---------
+
+The alloc_sem is a per-filesystem semaphore, used primarily to ensure
+contiguous allocation of space on the medium. It is automatically
+obtained during space allocations (jffs2_reserve_space()) and freed
+upon write completion (jffs2_complete_reservation()). Note that
+the garbage collector will obtain this right at the beginning of
+jffs2_garbage_collect_pass() and release it at the end, thereby
+preventing any other write activity on the file system during a
+garbage collect pass.
+
+When writing new nodes, the alloc_sem must be held until the new nodes
+have been properly linked into the data structures for the inode to
+which they belong. This is for the benefit of NAND flash - adding new
+nodes to an inode may obsolete old ones, and by holding the alloc_sem
+until this happens we ensure that any data in the write-buffer at the
+time this happens are part of the new node, not just something that
+was written afterwards. Hence, we can ensure the newly-obsoleted nodes
+don't actually get erased until the write-buffer has been flushed to
+the medium.
+
+With the introduction of NAND flash support and the write-buffer, 
+the alloc_sem is also used to protect the wbuf-related members of the
+jffs2_sb_info structure. Atomically reading the wbuf_len member to see
+if the wbuf is currently holding any data is permitted, though.
+
+Ordering constraints: See f->sem.
+
+
+	File Semaphore f->sem
+	---------------------
+
+This is the JFFS2-internal equivalent of the inode semaphore i->i_sem.
+It protects the contents of the jffs2_inode_info private inode data,
+including the linked list of node fragments (but see the notes below on
+erase_completion_lock), etc.
+
+The reason that the i_sem itself isn't used for this purpose is to
+avoid deadlocks with garbage collection -- the VFS will lock the i_sem
+before calling a function which may need to allocate space. The
+allocation may trigger garbage-collection, which may need to move a
+node belonging to the inode which was locked in the first place by the
+VFS. If the garbage collection code were to attempt to lock the i_sem
+of the inode from which it's garbage-collecting a physical node, this
+lead to deadlock, unless we played games with unlocking the i_sem
+before calling the space allocation functions.
+
+Instead of playing such games, we just have an extra internal
+semaphore, which is obtained by the garbage collection code and also
+by the normal file system code _after_ allocation of space.
+
+Ordering constraints: 
+
+	1. Never attempt to allocate space or lock alloc_sem with 
+	   any f->sem held.
+	2. Never attempt to lock two file semaphores in one thread.
+	   No ordering rules have been made for doing so.
+
+
+	erase_completion_lock spinlock
+	------------------------------
+
+This is used to serialise access to the eraseblock lists, to the
+per-eraseblock lists of physical jffs2_raw_node_ref structures, and
+(NB) the per-inode list of physical nodes. The latter is a special
+case - see below.
+
+As the MTD API no longer permits erase-completion callback functions
+to be called from bottom-half (timer) context (on the basis that nobody
+ever actually implemented such a thing), it's now sufficient to use
+a simple spin_lock() rather than spin_lock_bh().
+
+Note that the per-inode list of physical nodes (f->nodes) is a special
+case. Any changes to _valid_ nodes (i.e. ->flash_offset & 1 == 0) in
+the list are protected by the file semaphore f->sem. But the erase
+code may remove _obsolete_ nodes from the list while holding only the
+erase_completion_lock. So you can walk the list only while holding the
+erase_completion_lock, and can drop the lock temporarily mid-walk as
+long as the pointer you're holding is to a _valid_ node, not an
+obsolete one.
+
+The erase_completion_lock is also used to protect the c->gc_task
+pointer when the garbage collection thread exits. The code to kill the
+GC thread locks it, sends the signal, then unlocks it - while the GC
+thread itself locks it, zeroes c->gc_task, then unlocks on the exit path.
+
+	erase_free_sem
+	--------------
+
+This semaphore is only used by the erase code which frees obsolete
+node references and the jffs2_garbage_collect_deletion_dirent()
+function. The latter function on NAND flash must read _obsolete_ nodes
+to determine whether the 'deletion dirent' under consideration can be
+discarded or whether it is still required to show that an inode has
+been unlinked. Because reading from the flash may sleep, the
+erase_completion_lock cannot be held, so an alternative, more
+heavyweight lock was required to prevent the erase code from freeing
+the jffs2_raw_node_ref structures in question while the garbage
+collection code is looking at them.
+
+Suggestions for alternative solutions to this problem would be welcomed.
Index: fs/jffs2/current/doc/TODO
===================================================================
RCS file: fs/jffs2/current/doc/TODO
diff -N fs/jffs2/current/doc/TODO
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ fs/jffs2/current/doc/TODO	11 Dec 2003 23:28:08 -0000
@@ -0,0 +1,28 @@
+$Id: TODO,v 1.15 2003/10/06 14:50:37 dwmw2 Exp $
+
+ - disable compression in commit_write()?
+ - fine-tune the allocation / GC thresholds
+ - chattr support - turning on/off and tuning compression per-inode
+ - checkpointing (do we need this? scan is quite fast)
+ - make the scan code populate real inodes so read_inode just after 
+	mount doesn't have to read the flash twice for large files.
+	Make this a per-inode option, changable with chattr, so you can
+	decide which inodes should be in-core immediately after mount.
+ - test, test, test
+
+ - NAND flash support:
+	- done :)
+
+ - Optimisations:
+   - Split writes so they go to two separate blocks rather than just c->nextblock.
+	By writing _new_ nodes to one block, and garbage-collected REF_PRISTINE
+	nodes to a different one, we can separate clean nodes from those which
+	are likely to become dirty, and end up with blocks which are each far
+	closer to 100% or 0% clean, hence speeding up later GC progress dramatically.
+   - Stop keeping name in-core with struct jffs2_full_dirent. If we keep the hash in 
+     the full dirent, we only need to go to the flash in lookup() when we think we've
+     got a match, and in readdir(). 
+   - Doubly-linked next_in_ino list to allow us to free obsoleted raw_node_refs immediately?
+   - Remove totlen from jffs2_raw_node_ref? Need to have totlen passed into
+	jffs2_mark_node_obsolete(). Can all callers work it out?
+   - Remove size from jffs2_raw_node_frag. 
Index: fs/jffs2/current/doc/TODO.eCos
===================================================================
RCS file: fs/jffs2/current/doc/TODO.eCos
diff -N fs/jffs2/current/doc/TODO.eCos
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ fs/jffs2/current/doc/TODO.eCos	11 Dec 2003 23:28:08 -0000
@@ -0,0 +1,25 @@
+$Id: TODO.eCos,v 1.2 2003/11/28 11:15:56 dwmw2 Exp $
+
+ - Make symlinks work properly.
+
+ - Fill in the skeleton gcthread.c so it actually does something.
+
+ - Check and possibly fix locking of icache mangling in fs-ecos.c
+
+ - Check that os-ecos.h defines 'spin_lock()' to something appropriate.
+
+ - Fix unmount of root file system after chdir().
+
+ - Fix atomicity of renames. Why was the unlink added before rename?
+
+ - 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?
+
+ - Improve mount time by using pointer directly into flash chip instead
+   of jffs2_flash_read() for the initial scan -- look at the #ifdef
+   __ECOS bit in scan.c for details.
+
+ - Reduce memory usage. There are fields marked for possible removal in
+   struct _inode, and there's the __totlen field in struct
+   jffs2_raw_node_ref as discussed recently.
Index: fs/jffs2/current/src/flashio.c
===================================================================
RCS file: fs/jffs2/current/src/flashio.c
diff -N fs/jffs2/current/src/flashio.c
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ fs/jffs2/current/src/flashio.c	11 Dec 2003 23:28:08 -0000
@@ -0,0 +1,165 @@
+/*
+ * JFFS2 -- Journalling Flash File System, Version 2.
+ *
+ * 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: flashio.c,v 1.1 2003/11/26 14:09:29 dwmw2 Exp $
+ *
+ */
+
+#include <linux/kernel.h>
+#include "nodelist.h"
+
+#include <cyg/io/io.h>
+#include <cyg/io/config_keys.h>
+#include <cyg/io/flash.h>
+
+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;
+
+	return ret;
+}
+
+cyg_bool jffs2_flash_erase(struct jffs2_sb_info * c,
+			   struct jffs2_eraseblock * jeb)
+{
+	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);
+}
+
Index: fs/jffs2/current/src/gcthread.c
===================================================================
RCS file: fs/jffs2/current/src/gcthread.c
diff -N fs/jffs2/current/src/gcthread.c
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ fs/jffs2/current/src/gcthread.c	11 Dec 2003 23:28:08 -0000
@@ -0,0 +1,52 @@
+/*
+ * JFFS2 -- Journalling Flash File System, Version 2.
+ *
+ * Copyright (C) 2001-2003 Red Hat, Inc.
+ *
+ * Created by David Woodhouse <dwmw2@redhat.com>
+ *
+ * For licensing information, see the file 'LICENCE' in this directory.
+ *
+ * $Id: gcthread.c,v 1.1 2003/11/26 15:55:35 dwmw2 Exp $
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/jffs2.h>
+#include "nodelist.h"
+
+
+static void jffs2_garbage_collect_thread(struct jffs2_sb_info *c);
+
+void jffs2_garbage_collect_trigger(struct jffs2_sb_info *c)
+{
+	/* Wake up the thread */
+	(void)&jffs2_garbage_collect_thread;
+}
+
+void jffs2_start_garbage_collect_thread(struct jffs2_sb_info *c)
+{
+	/* Start the thread. Doesn't matter if it fails -- it's only an optimisation anyway */
+}
+
+void jffs2_stop_garbage_collect_thread(struct jffs2_sb_info *c)
+{
+	/* Stop the thread and wait for it if necessary */
+}
+
+
+static void jffs2_garbage_collect_thread(struct jffs2_sb_info *c)
+{
+#define this_thread_should_die() 0
+	while(!this_thread_should_die()) {
+		while(!jffs2_thread_should_wake(c)) {
+			/* Sleep.... */
+			continue;
+		}
+		if (jffs2_garbage_collect_pass(c) == -ENOSPC) {
+			printf("No space for garbage collection. Aborting JFFS2 GC thread\n");
+			break;
+		}
+	}
+}

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