changeset 570:a7947d436e85

Update to latest public JFFS2 sources
author gthomas
date Wed, 05 Feb 2003 00:00:40 +0000
parents 0d98f58e80cb
children 0f8cd8aca826
files packages/fs/jffs2/current/ChangeLog packages/fs/jffs2/current/cdl/jffs2.cdl packages/fs/jffs2/current/include/linux/jffs2.h packages/fs/jffs2/current/include/linux/jffs2_fs_i.h packages/fs/jffs2/current/include/linux/jffs2_fs_sb.h packages/fs/jffs2/current/src/background.c packages/fs/jffs2/current/src/build.c packages/fs/jffs2/current/src/compr.c packages/fs/jffs2/current/src/compr_rtime.c packages/fs/jffs2/current/src/compr_rubin.c packages/fs/jffs2/current/src/compr_zlib.c packages/fs/jffs2/current/src/crc32.h packages/fs/jffs2/current/src/dir-ecos.c packages/fs/jffs2/current/src/erase.c packages/fs/jffs2/current/src/file-ecos.c packages/fs/jffs2/current/src/fs-ecos.c packages/fs/jffs2/current/src/gc.c packages/fs/jffs2/current/src/jffs2.c packages/fs/jffs2/current/src/jffs2.h packages/fs/jffs2/current/src/jffs2_fs_i.h packages/fs/jffs2/current/src/jffs2_fs_sb.h packages/fs/jffs2/current/src/jffs2port.h packages/fs/jffs2/current/src/list.h packages/fs/jffs2/current/src/malloc-ecos.c packages/fs/jffs2/current/src/nodelist.c packages/fs/jffs2/current/src/nodelist.h packages/fs/jffs2/current/src/nodemgmt.c packages/fs/jffs2/current/src/os-ecos.h packages/fs/jffs2/current/src/pushpull.h packages/fs/jffs2/current/src/read.c packages/fs/jffs2/current/src/readinode.c packages/fs/jffs2/current/src/scan.c packages/fs/jffs2/current/src/write.c
diffstat 33 files changed, 5648 insertions(+), 4247 deletions(-) [+]
line wrap: on
line diff
--- a/packages/fs/jffs2/current/ChangeLog
+++ b/packages/fs/jffs2/current/ChangeLog
@@ -1,3 +1,41 @@
+2003-02-04  Gary Thomas  <gary@mlbassoc.com> on behalf of
+2003-02-04  David Woodhouse <dwmw2@cambridge.redhat.com>	
+
+	* src/write.c: 
+	* src/scan.c: 
+	* src/readinode.c: 
+	* src/read.c: 
+	* src/pushpull.h: 
+	* src/os-ecos.h: 
+	* src/nodemgmt.c: 
+	* src/nodelist.h: 
+	* src/nodelist.c: 
+	* src/malloc-ecos.c: 
+	* src/jffs2port.h: 
+	* src/gc.c: 
+	* src/file-ecos.c: 
+	* src/erase.c: 
+	* src/dir-ecos.c: 
+	* src/compr_zlib.c: 
+	* src/compr_rubin.c: 
+	* src/compr_rtime.c: 
+	* src/compr.c: 
+	* src/build.c: 
+	* cdl/jffs2.cdl: Update to latest public JFFS2 codebase.
+
+	* src/list.h: 
+	* src/jffs2_fs_sb.h: 
+	* src/jffs2_fs_i.h: 
+	* src/jffs2.h: 
+	* src/jffs2.c: 
+	* src/crc32.h: 
+	* src/background.c: Removed/renamed file(s).
+
+	* src/fs-ecos.c: 
+	* include/linux/jffs2_fs_sb.h: 
+	* include/linux/jffs2_fs_i.h: 
+	* include/linux/jffs2.h: New file(s).
+
 2002-12-06  Andrew Lunn  <andrew.lunn@ascom.ch>
 
 	* cdl/jffs2.cdl: Implements the CYGINT_IO_FILEIO_FS interface.
--- a/packages/fs/jffs2/current/cdl/jffs2.cdl
+++ b/packages/fs/jffs2/current/cdl/jffs2.cdl
@@ -40,7 +40,7 @@
 # ====================================================================
 ######DESCRIPTIONBEGIN####
 #
-# Author(s):      dominic ostrowski
+# Author(s):      David Woodhouse, Dominic Ostrowski
 # Original data:  ported from JFFS2 by David Woodhouse
 # Contributors:   dominic.ostrowski@3glab.com
 # Date:           2000-08-28
@@ -52,13 +52,12 @@
 cdl_package CYGPKG_FS_JFFS2 {
     display        "JFFS2 filesystem"
     doc            doc/index.html
-    include_dir    cyg/jffs2
+    include_dir    ""
 
     requires       CYGPKG_IO_FILEIO
     requires       CYGPKG_IO_FLASH
     requires       CYGPKG_COMPRESS_ZLIB
     requires       CYGINT_ISO_MALLOC
-    requires       CYGPKG_CRC
 
     requires       CYGPKG_ISOINFRA
     requires       CYGPKG_ERROR
@@ -66,10 +65,12 @@ cdl_package CYGPKG_FS_JFFS2 {
     requires       CYGINT_ISO_ERRNO_CODES
     requires       CYGPKG_IO_FLASH_BLOCK_DEVICE
     requires       CYGPKG_IO_FILEIO_INODE
+    requires       CYGPKG_LINUX_COMPAT
+    requires       CYGPKG_CRC
 
     implements     CYGINT_IO_FILEIO_FS      
 
-    compile        -library=libextras.a jffs2.c
+    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 compr_zlib.c compr_rtime.c compr_rubin.c file-ecos.c
 
     cdl_option CYGPKG_FS_JFFS2_CFLAGS_ADD {
@@ -78,7 +79,7 @@ cdl_package CYGPKG_FS_JFFS2 {
 	no_define
 	# We add -D__ECOS to trigger eCos-specific code in places.
 	# We add -Werror because I find it useful.
-	default_value { "-D__ECOS -Werror" }
+	default_value { "-D__ECOS -nostdinc -iwithprefix include -Werror" }
 	description   "
 	    This option modifies the set of compiler flags for
             building the JFFS2 package.
@@ -100,6 +101,31 @@ cdl_package CYGPKG_FS_JFFS2 {
             the set of global flags if present."
         }
 
+    cdl_option CYGNUM_FS_JFFS2_SECTOR_SIZE {
+       display         "Physical sector size of the flash devices"
+       flavor          data
+       default_value   { 0x10000 }
+       description     "64 KiB for synthetic target."
+    }
+
+    cdl_option CYGNUM_FS_JFFS2_FLASH_SIZE {
+       display         "Amount of flash committed to JFFS2"
+       flavor          data
+       default_value   { 0x100000 }
+       description     "1 MiB (giving 16 sectors, realistic minimum!)"
+    }
+
+    cdl_option CYGNUM_FS_JFFS2_BASE_ADDRESS {
+       display         "Base of the JFFS2 filesystem in memory"
+       flavor          data
+       default_value   { 0x40000000 }
+       description     "Set this value to the base address of the JFFS2
+               filesystem in memory. By coincidence, this matches the
+               address at which the synthetic target will mmap its
+               backing store. Perhaps we should make it work through
+               something other than coincidence?"
+    }
+
     # ----------------------------------------------------------------
     # Tests
 
new file mode 100644
--- /dev/null
+++ b/packages/fs/jffs2/current/include/linux/jffs2.h
@@ -0,0 +1,188 @@
+/*
+ * JFFS2 -- Journalling Flash File System, Version 2.
+ *
+ * Copyright (C) 2001, 2002 Red Hat, Inc.
+ *
+ * Created by David Woodhouse <dwmw2@cambridge.redhat.com>
+ *
+ * For licensing information, see the file 'LICENCE' in the 
+ * jffs2 directory.
+ *
+ * $Id: jffs2.h,v 1.29 2003/01/22 11:59:23 dwmw2 Exp $
+ *
+ */
+
+#ifndef __LINUX_JFFS2_H__
+#define __LINUX_JFFS2_H__
+
+/* You must include something which defines the C99 uintXX_t types. 
+   We don't do it from here because this file is used in too many
+   different environments. */
+
+#define JFFS2_SUPER_MAGIC 0x72b6
+
+/* Values we may expect to find in the 'magic' field */
+#define JFFS2_OLD_MAGIC_BITMASK 0x1984
+#define JFFS2_MAGIC_BITMASK 0x1985
+#define KSAMTIB_CIGAM_2SFFJ 0x5981 /* For detecting wrong-endian fs */
+#define JFFS2_EMPTY_BITMASK 0xffff
+#define JFFS2_DIRTY_BITMASK 0x0000
+
+/* We only allow a single char for length, and 0xFF is empty flash so
+   we don't want it confused with a real length. Hence max 254.
+*/
+#define JFFS2_MAX_NAME_LEN 254
+
+/* How small can we sensibly write nodes? */
+#define JFFS2_MIN_DATA_LEN 128
+
+#define JFFS2_COMPR_NONE	0x00
+#define JFFS2_COMPR_ZERO	0x01
+#define JFFS2_COMPR_RTIME	0x02
+#define JFFS2_COMPR_RUBINMIPS	0x03
+#define JFFS2_COMPR_COPY	0x04
+#define JFFS2_COMPR_DYNRUBIN	0x05
+#define JFFS2_COMPR_ZLIB	0x06
+/* Compatibility flags. */
+#define JFFS2_COMPAT_MASK 0xc000      /* What do to if an unknown nodetype is found */
+#define JFFS2_NODE_ACCURATE 0x2000
+/* INCOMPAT: Fail to mount the filesystem */
+#define JFFS2_FEATURE_INCOMPAT 0xc000
+/* ROCOMPAT: Mount read-only */
+#define JFFS2_FEATURE_ROCOMPAT 0x8000
+/* RWCOMPAT_COPY: Mount read/write, and copy the node when it's GC'd */
+#define JFFS2_FEATURE_RWCOMPAT_COPY 0x4000
+/* RWCOMPAT_DELETE: Mount read/write, and delete the node when it's GC'd */
+#define JFFS2_FEATURE_RWCOMPAT_DELETE 0x0000
+
+#define JFFS2_NODETYPE_DIRENT (JFFS2_FEATURE_INCOMPAT | JFFS2_NODE_ACCURATE | 1)
+#define JFFS2_NODETYPE_INODE (JFFS2_FEATURE_INCOMPAT | JFFS2_NODE_ACCURATE | 2)
+#define JFFS2_NODETYPE_CLEANMARKER (JFFS2_FEATURE_RWCOMPAT_DELETE | JFFS2_NODE_ACCURATE | 3)
+#define JFFS2_NODETYPE_PADDING (JFFS2_FEATURE_RWCOMPAT_DELETE | JFFS2_NODE_ACCURATE | 4)
+
+// Maybe later...
+//#define JFFS2_NODETYPE_CHECKPOINT (JFFS2_FEATURE_RWCOMPAT_DELETE | JFFS2_NODE_ACCURATE | 3)
+//#define JFFS2_NODETYPE_OPTIONS (JFFS2_FEATURE_RWCOMPAT_COPY | JFFS2_NODE_ACCURATE | 4)
+
+
+#define JFFS2_INO_FLAG_PREREAD	  1	/* Do read_inode() for this one at 
+					   mount time, don't wait for it to 
+					   happen later */
+#define JFFS2_INO_FLAG_USERCOMPR  2	/* User has requested a specific 
+					   compression type */
+
+
+/* These can go once we've made sure we've caught all uses without
+   byteswapping */
+
+typedef struct {
+	uint32_t v32;
+} __attribute__((packed))  jint32_t;
+
+typedef struct {
+	uint32_t m;
+} __attribute__((packed))  jmode_t;
+
+typedef struct {
+	uint16_t v16;
+} __attribute__((packed)) jint16_t;
+
+#define JFFS2_NATIVE_ENDIAN
+
+/* Note we handle mode bits conversion from JFFS2 (i.e. Linux) to/from
+   whatever OS we're actually running on here too. */
+
+#if defined(JFFS2_NATIVE_ENDIAN)
+#define cpu_to_je16(x) ((jint16_t){x})
+#define cpu_to_je32(x) ((jint32_t){x})
+#define cpu_to_jemode(x) ((jmode_t){os_to_jffs2_mode(x)})
+
+#define je16_to_cpu(x) ((x).v16)
+#define je32_to_cpu(x) ((x).v32)
+#define jemode_to_cpu(x) (jffs2_to_os_mode((x).m))
+#elif defined(JFFS2_BIG_ENDIAN)
+#define cpu_to_je16(x) ((jint16_t){cpu_to_be16(x)})
+#define cpu_to_je32(x) ((jint32_t){cpu_to_be32(x)})
+#define cpu_to_jemode(x) ((jmode_t){cpu_to_be32(os_to_jffs2_mode(x))})
+
+#define je16_to_cpu(x) (be16_to_cpu(x.v16))
+#define je32_to_cpu(x) (be32_to_cpu(x.v32))
+#define jemode_to_cpu(x) (be32_to_cpu(jffs2_to_os_mode((x).m)))
+#elif defined(JFFS2_LITTLE_ENDIAN)
+#define cpu_to_je16(x) ((jint16_t){cpu_to_le16(x)})
+#define cpu_to_je32(x) ((jint32_t){cpu_to_le32(x)})
+#define cpu_to_jemode(x) ((jmode_t){cpu_to_le32(os_to_jffs2_mode(x))})
+
+#define je16_to_cpu(x) (le16_to_cpu(x.v16))
+#define je32_to_cpu(x) (le32_to_cpu(x.v32))
+#define jemode_to_cpu(x) (le32_to_cpu(jffs2_to_os_mode((x).m)))
+#else 
+#error wibble
+#endif
+
+struct jffs2_unknown_node
+{
+	/* All start like this */
+	jint16_t magic;
+	jint16_t nodetype;
+	jint32_t totlen; /* So we can skip over nodes we don't grok */
+	jint32_t hdr_crc;
+} __attribute__((packed));
+
+struct jffs2_raw_dirent
+{
+	jint16_t magic;
+	jint16_t nodetype;	/* == JFFS_NODETYPE_DIRENT */
+	jint32_t totlen;
+	jint32_t hdr_crc;
+	jint32_t pino;
+	jint32_t version;
+	jint32_t ino; /* == zero for unlink */
+	jint32_t mctime;
+	uint8_t nsize;
+	uint8_t type;
+	uint8_t unused[2];
+	jint32_t node_crc;
+	jint32_t name_crc;
+	uint8_t name[0];
+} __attribute__((packed));
+
+/* The JFFS2 raw inode structure: Used for storage on physical media.  */
+/* The uid, gid, atime, mtime and ctime members could be longer, but 
+   are left like this for space efficiency. If and when people decide
+   they really need them extended, it's simple enough to add support for
+   a new type of raw node.
+*/
+struct jffs2_raw_inode
+{
+	jint16_t magic;      /* A constant magic number.  */
+	jint16_t nodetype;   /* == JFFS_NODETYPE_INODE */
+	jint32_t totlen;     /* Total length of this node (inc data, etc.) */
+	jint32_t hdr_crc;
+	jint32_t ino;        /* Inode number.  */
+	jint32_t version;    /* Version number.  */
+	jmode_t mode;       /* The file's type or mode.  */
+	jint16_t uid;        /* The file's owner.  */
+	jint16_t gid;        /* The file's group.  */
+	jint32_t isize;      /* Total resultant size of this inode (used for truncations)  */
+	jint32_t atime;      /* Last access time.  */
+	jint32_t mtime;      /* Last modification time.  */
+	jint32_t ctime;      /* Change time.  */
+	jint32_t offset;     /* Where to begin to write.  */
+	jint32_t csize;      /* (Compressed) data size */
+	jint32_t dsize;	     /* Size of the node's data. (after decompression) */
+	uint8_t compr;       /* Compression algorithm used */
+	uint8_t usercompr;   /* Compression algorithm requested by the user */
+	jint16_t flags;	     /* See JFFS2_INO_FLAG_* */
+	jint32_t data_crc;   /* CRC for the (compressed) data.  */
+	jint32_t node_crc;   /* CRC for the raw inode (excluding data)  */
+	uint8_t data[0];
+} __attribute__((packed));
+
+union jffs2_node_union {
+	struct jffs2_raw_inode i;
+	struct jffs2_raw_dirent d;
+	struct jffs2_unknown_node u;
+};
+
+#endif /* __LINUX_JFFS2_H__ */
new file mode 100644
--- /dev/null
+++ b/packages/fs/jffs2/current/include/linux/jffs2_fs_i.h
@@ -0,0 +1,46 @@
+/* $Id: jffs2_fs_i.h,v 1.16 2003/01/09 14:03:21 dwmw2 Exp $ */
+
+#ifndef _JFFS2_FS_I
+#define _JFFS2_FS_I
+
+#include <linux/version.h>
+#include <linux/rbtree.h>
+
+struct jffs2_inode_info {
+	/* We need an internal semaphore similar to inode->i_sem.
+	   Unfortunately, we can't used the existing one, because
+	   either the GC would deadlock, or we'd have to release it
+	   before letting GC proceed. Or we'd have to put ugliness
+	   into the GC code so it didn't attempt to obtain the i_sem
+	   for the inode(s) which are already locked */
+	struct semaphore sem;
+
+	/* The highest (datanode) version number used for this ino */
+	uint32_t highest_version;
+
+	/* List of data fragments which make up the file */
+	struct rb_root fragtree;
+
+	/* There may be one datanode which isn't referenced by any of the
+	   above fragments, if it contains a metadata update but no actual
+	   data - or if this is a directory inode */
+	/* This also holds the _only_ dnode for symlinks/device nodes, 
+	   etc. */
+	struct jffs2_full_dnode *metadata;
+
+	/* Directory entries */
+	struct jffs2_full_dirent *dents;
+
+	/* Some stuff we just have to keep in-core at all times, for each inode. */
+	struct jffs2_inode_cache *inocache;
+
+	uint16_t flags;
+	uint8_t usercompr;
+#if !defined (__ECOS)
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,2)
+	struct inode vfs_inode;
+#endif
+#endif
+};
+
+#endif /* _JFFS2_FS_I */
new file mode 100644
--- /dev/null
+++ b/packages/fs/jffs2/current/include/linux/jffs2_fs_sb.h
@@ -0,0 +1,99 @@
+/* $Id: jffs2_fs_sb.h,v 1.37 2003/01/17 16:04:44 dwmw2 Exp $ */
+
+#ifndef _JFFS2_FS_SB
+#define _JFFS2_FS_SB
+
+#include <linux/types.h>
+#include <linux/spinlock.h>
+#include <linux/workqueue.h>
+#include <linux/completion.h>
+#include <asm/semaphore.h>
+#include <linux/timer.h>
+#include <linux/wait.h>
+#include <linux/list.h>
+
+#define JFFS2_SB_FLAG_RO 1
+#define JFFS2_SB_FLAG_MOUNTING 2
+
+/* A struct for the overall file system control.  Pointers to
+   jffs2_sb_info structs are named `c' in the source code.  
+   Nee jffs_control
+*/
+struct jffs2_sb_info {
+	struct mtd_info *mtd;
+
+	uint32_t highest_ino;
+	uint32_t checked_ino;
+
+	unsigned int flags;
+
+	struct task_struct *gc_task;	/* GC task struct */
+	struct semaphore gc_thread_start; /* GC thread start mutex */
+	struct completion gc_thread_exit; /* GC thread exit completion port */
+
+	struct semaphore alloc_sem;	/* Used to protect all the following 
+					   fields, and also to protect against
+					   out-of-order writing of nodes.
+					   And GC.
+					*/
+	uint32_t cleanmarker_size;	/* Size of an _inline_ CLEANMARKER
+					 (i.e. zero for OOB CLEANMARKER */
+
+	uint32_t flash_size;
+	uint32_t used_size;
+	uint32_t dirty_size;
+	uint32_t wasted_size;
+	uint32_t free_size;
+	uint32_t erasing_size;
+	uint32_t bad_size;
+	uint32_t sector_size;
+	uint32_t unchecked_size;
+
+	uint32_t nr_free_blocks;
+	uint32_t nr_erasing_blocks;
+
+	uint32_t nr_blocks;
+	struct jffs2_eraseblock *blocks;	/* The whole array of blocks. Used for getting blocks 
+						 * from the offset (blocks[ofs / sector_size]) */
+	struct jffs2_eraseblock *nextblock;	/* The block we're currently filling */
+
+	struct jffs2_eraseblock *gcblock;	/* The block we're currently garbage-collecting */
+
+	struct list_head clean_list;		/* Blocks 100% full of clean data */
+	struct list_head very_dirty_list;	/* Blocks with lots of dirty space */
+	struct list_head dirty_list;		/* Blocks with some dirty space */
+	struct list_head erasable_list;		/* Blocks which are completely dirty, and need erasing */
+	struct list_head erasable_pending_wbuf_list;	/* Blocks which need erasing but only after the current wbuf is flushed */
+	struct list_head erasing_list;		/* Blocks which are currently erasing */
+	struct list_head erase_pending_list;	/* Blocks which need erasing now */
+	struct list_head erase_complete_list;	/* Blocks which are erased and need the clean marker written to them */
+	struct list_head free_list;		/* Blocks which are free and ready to be used */
+	struct list_head bad_list;		/* Bad blocks. */
+	struct list_head bad_used_list;		/* Bad blocks with valid data in. */
+
+	spinlock_t erase_completion_lock;	/* Protect free_list and erasing_list 
+						   against erase completion handler */
+	wait_queue_head_t erase_wait;		/* For waiting for erases to complete */
+
+	wait_queue_head_t inocache_wq;
+	struct jffs2_inode_cache **inocache_list;
+	spinlock_t inocache_lock;
+	
+	/* Sem to allow jffs2_garbage_collect_deletion_dirent to
+	   drop the erase_completion_lock while it's holding a pointer 
+	   to an obsoleted node. I don't like this. Alternatives welcomed. */
+	struct semaphore erase_free_sem;
+
+	/* Write-behind buffer for NAND flash */
+	unsigned char *wbuf;
+	uint32_t wbuf_ofs;
+	uint32_t wbuf_len;
+	uint32_t wbuf_pagesize;
+	struct work_struct wbuf_task;		/* task for timed wbuf flush */
+	struct timer_list wbuf_timer;		/* timer for flushing wbuf */
+
+	/* OS-private pointer for getting back to master superblock info */
+	void *os_priv;
+};
+
+#endif /* _JFFS2_FB_SB */
deleted file mode 100644
--- a/packages/fs/jffs2/current/src/background.c
+++ /dev/null
@@ -1,159 +0,0 @@
-/*
- * JFFS2 -- Journalling Flash File System, Version 2.
- *
- * Copyright (C) 2001 Red Hat, Inc.
- *
- * Created by David Woodhouse <dwmw2@cambridge.redhat.com>
- *
- * For licensing information, see the file 'LICENCE' in this directory.
- *
- * $Id: background.c,v 1.16 2001/10/08 09:22:38 dwmw2 Exp $
- *
- */
-
-#define __KERNEL_SYSCALLS__
-
-#include <linux/kernel.h>
-#include <linux/sched.h>
-#include <linux/unistd.h>
-#include <linux/jffs2.h>
-#include <linux/mtd/mtd.h>
-#include <linux/interrupt.h>
-#include <linux/completion.h>
-#include "nodelist.h"
-
-
-static int jffs2_garbage_collect_thread(void *);
-static int thread_should_wake(struct jffs2_sb_info *c);
-
-void jffs2_garbage_collect_trigger(struct jffs2_sb_info *c)
-{
-	spin_lock_bh(&c->erase_completion_lock);
-        if (c->gc_task && thread_should_wake(c))
-                send_sig(SIGHUP, c->gc_task, 1);
-	spin_unlock_bh(&c->erase_completion_lock);
-}
-
-/* This must only ever be called when no GC thread is currently running */
-int jffs2_start_garbage_collect_thread(struct jffs2_sb_info *c)
-{
-	pid_t pid;
-	int ret = 0;
-
-	if (c->gc_task)
-		BUG();
-
-	init_MUTEX_LOCKED(&c->gc_thread_start);
-	init_completion(&c->gc_thread_exit);
-
-	pid = kernel_thread(jffs2_garbage_collect_thread, c, CLONE_FS|CLONE_FILES);
-	if (pid < 0) {
-		printk(KERN_WARNING "fork failed for JFFS2 garbage collect thread: %d\n", -pid);
-		complete(&c->gc_thread_exit);
-		ret = pid;
-	} else {
-		/* Wait for it... */
-		D1(printk(KERN_DEBUG "JFFS2: Garbage collect thread is pid %d\n", pid));
-		down(&c->gc_thread_start);
-	}
- 
-	return ret;
-}
-
-void jffs2_stop_garbage_collect_thread(struct jffs2_sb_info *c)
-{
-	spin_lock_bh(&c->erase_completion_lock);
-	if (c->gc_task) {
-		D1(printk(KERN_DEBUG "jffs2: Killing GC task %d\n", c->gc_task->pid));
-		send_sig(SIGKILL, c->gc_task, 1);
-	}
-	spin_unlock_bh(&c->erase_completion_lock);
-	wait_for_completion(&c->gc_thread_exit);
-}
-
-static int jffs2_garbage_collect_thread(void *_c)
-{
-	struct jffs2_sb_info *c = _c;
-
-	daemonize();
-	current->tty = NULL;
-	c->gc_task = current;
-	up(&c->gc_thread_start);
-
-        sprintf(current->comm, "jffs2_gcd_mtd%d", c->mtd->index);
-
-	/* FIXME in the 2.2 backport */
-	current->nice = 10;
-
-	for (;;) {
-		spin_lock_irq(&current->sigmask_lock);
-		siginitsetinv (&current->blocked, sigmask(SIGHUP) | sigmask(SIGKILL) | sigmask(SIGSTOP) | sigmask(SIGCONT));
-		recalc_sigpending(current);
-		spin_unlock_irq(&current->sigmask_lock);
-
-		if (!thread_should_wake(c)) {
-                        set_current_state (TASK_INTERRUPTIBLE);
-			D1(printk(KERN_DEBUG "jffs2_garbage_collect_thread sleeping...\n"));
-			/* Yes, there's a race here; we checked thread_should_wake() before
-			   setting current->state to TASK_INTERRUPTIBLE. But it doesn't
-			   matter - We don't care if we miss a wakeup, because the GC thread
-			   is only an optimisation anyway. */
-			schedule();
-		}
-                
-		if (current->need_resched)
-			schedule();
-
-                /* Put_super will send a SIGKILL and then wait on the sem. 
-                 */
-                while (signal_pending(current)) {
-                        siginfo_t info;
-                        unsigned long signr;
-
-                        spin_lock_irq(&current->sigmask_lock);
-                        signr = dequeue_signal(&current->blocked, &info);
-                        spin_unlock_irq(&current->sigmask_lock);
-
-                        switch(signr) {
-                        case SIGSTOP:
-                                D1(printk(KERN_DEBUG "jffs2_garbage_collect_thread(): SIGSTOP received.\n"));
-                                set_current_state(TASK_STOPPED);
-                                schedule();
-                                break;
-
-                        case SIGKILL:
-                                D1(printk(KERN_DEBUG "jffs2_garbage_collect_thread(): SIGKILL received.\n"));
-				spin_lock_bh(&c->erase_completion_lock);
-                                c->gc_task = NULL;
-				spin_unlock_bh(&c->erase_completion_lock);
-				complete_and_exit(&c->gc_thread_exit, 0);
-
-			case SIGHUP:
-				D1(printk(KERN_DEBUG "jffs2_garbage_collect_thread(): SIGHUP received.\n"));
-				break;
-			default:
-				D1(printk(KERN_DEBUG "jffs2_garbage_collect_thread(): signal %ld received\n", signr));
-
-                        }
-                }
-		/* We don't want SIGHUP to interrupt us. STOP and KILL are OK though. */
-		spin_lock_irq(&current->sigmask_lock);
-		siginitsetinv (&current->blocked, sigmask(SIGKILL) | sigmask(SIGSTOP) | sigmask(SIGCONT));
-		recalc_sigpending(current);
-		spin_unlock_irq(&current->sigmask_lock);
-
-		D1(printk(KERN_DEBUG "jffs2_garbage_collect_thread(): pass\n"));
-		jffs2_garbage_collect_pass(c);
-	}
-}
-
-static int thread_should_wake(struct jffs2_sb_info *c)
-{
-	D1(printk(KERN_DEBUG "thread_should_wake(): nr_free_blocks %d, nr_erasing_blocks %d, dirty_size 0x%x\n", 
-		  c->nr_free_blocks, c->nr_erasing_blocks, c->dirty_size));
-	if (c->nr_free_blocks + c->nr_erasing_blocks < JFFS2_RESERVED_BLOCKS_GCTRIGGER &&
-	    c->dirty_size > c->sector_size)
-		return 1;
-	else 
-		return 0;
-}
--- a/packages/fs/jffs2/current/src/build.c
+++ b/packages/fs/jffs2/current/src/build.c
@@ -7,14 +7,13 @@
  *
  * For licensing information, see the file 'LICENCE' in this directory.
  *
- * $Id: build.c,v 1.22 2002/01/09 16:30:57 dwmw2 Exp $
+ * $Id: build.c,v 1.44 2003/01/17 16:04:16 dwmw2 Exp $
  *
  */
 
-#ifndef __ECOS
 #include <linux/kernel.h>
+#include <linux/sched.h>
 #include <linux/slab.h>
-#endif
 #include "nodelist.h"
 
 int jffs2_build_inode_pass1(struct jffs2_sb_info *, struct jffs2_inode_cache *);
@@ -36,13 +35,18 @@ static int jffs2_build_filesystem(struct
 
 	/* First, scan the medium and build all the inode caches with
 	   lists of physical nodes */
+
+	c->flags |= JFFS2_SB_FLAG_MOUNTING;
 	ret = jffs2_scan_medium(c);
+	c->flags &= ~JFFS2_SB_FLAG_MOUNTING;
+
 	if (ret)
 		return ret;
 
 	D1(printk(KERN_DEBUG "Scanned flash completely\n"));
-	/* Now build the data map for each inode, marking obsoleted nodes
-	   as such, and also increase nlink of any children. */
+	D1(jffs2_dump_block_lists(c));
+
+	/* Now scan the directory tree, increasing nlink according to every dirent found. */
 	for_each_inode(i, c, ic) {
 		D1(printk(KERN_DEBUG "Pass 1: ino #%u\n", ic->ino));
 		ret = jffs2_build_inode_pass1(c, ic);
@@ -50,8 +54,10 @@ static int jffs2_build_filesystem(struct
 			D1(printk(KERN_WARNING "Eep. jffs2_build_inode_pass1 for ino %d returned %d\n", ic->ino, ret));
 			return ret;
 		}
+		cond_resched();
 	}
 	D1(printk(KERN_DEBUG "Pass 1 complete\n"));
+	D1(jffs2_dump_block_lists(c));
 
 	/* Next, scan for inodes with nlink == 0 and remove them. If
 	   they were directories, then decrement the nlink of their
@@ -66,6 +72,8 @@ static int jffs2_build_filesystem(struct
 			if (ic->nlink)
 				continue;
 			
+			/* XXX: Can get high latency here. Move the cond_resched() from the end of the loop? */
+
 			ret = jffs2_build_remove_unlinked_inode(c, ic);
 			if (ret)
 				break;
@@ -73,101 +81,52 @@ static int jffs2_build_filesystem(struct
 		   and furthermore that it had children and their nlink has now
 		   gone to zero too. So we have to restart the scan. */
 		} 
+		D1(jffs2_dump_block_lists(c));
+
+		cond_resched();
+	
 	} while(ret == -EAGAIN);
-	
+
 	D1(printk(KERN_DEBUG "Pass 2 complete\n"));
 	
 	/* Finally, we can scan again and free the dirent nodes and scan_info structs */
 	for_each_inode(i, c, ic) {
-		struct jffs2_scan_info *scan = ic->scan;
 		struct jffs2_full_dirent *fd;
 		D1(printk(KERN_DEBUG "Pass 3: ino #%u, ic %p, nodes %p\n", ic->ino, ic, ic->nodes));
-		if (!scan) {
-			if (ic->nlink) {
-				D1(printk(KERN_WARNING "Why no scan struct for ino #%u which has nlink %d?\n", ic->ino, ic->nlink));
-			}
-			continue;
-		}
-		ic->scan = NULL;
-		while(scan->dents) {
-			fd = scan->dents;
-			scan->dents = fd->next;
+
+		while(ic->scan_dents) {
+			fd = ic->scan_dents;
+			ic->scan_dents = fd->next;
 			jffs2_free_full_dirent(fd);
 		}
-		kfree(scan);
+		ic->scan_dents = NULL;
+		cond_resched();
 	}
 	D1(printk(KERN_DEBUG "Pass 3 complete\n"));
+	D1(jffs2_dump_block_lists(c));
+
+	/* Rotate the lists by some number to ensure wear levelling */
+	jffs2_rotate_lists(c);
 
 	return ret;
 }
-	
+
 int jffs2_build_inode_pass1(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic)
 {
-	struct jffs2_tmp_dnode_info *tn;
 	struct jffs2_full_dirent *fd;
-	struct jffs2_node_frag *fraglist = NULL;
-	struct jffs2_tmp_dnode_info *metadata = NULL;
 
 	D1(printk(KERN_DEBUG "jffs2_build_inode building inode #%u\n", ic->ino));
+
 	if (ic->ino > c->highest_ino)
 		c->highest_ino = ic->ino;
 
-	if (!ic->scan->tmpnodes && ic->ino != 1) {
-		D1(printk(KERN_DEBUG "jffs2_build_inode: ino #%u has no data nodes!\n", ic->ino));
-	}
-	/* Build the list to make sure any obsolete nodes are marked as such */
-	while(ic->scan->tmpnodes) {
-		tn = ic->scan->tmpnodes;
-		ic->scan->tmpnodes = tn->next;
-		
-		if (metadata && tn->version > metadata->version) {
-			D1(printk(KERN_DEBUG "jffs2_build_inode_pass1 ignoring old metadata at 0x%08x\n",
-				  metadata->fn->raw->flash_offset &~3));
-			
-			jffs2_free_full_dnode(metadata->fn);
-			jffs2_free_tmp_dnode_info(metadata);
-			metadata = NULL;
-		}
-			
-		if (tn->fn->size) {
-			jffs2_add_full_dnode_to_fraglist (c, &fraglist, tn->fn);
-			jffs2_free_tmp_dnode_info(tn);
-		} else {
-			if (!metadata) {
-				metadata = tn;
-			} else {
-				D1(printk(KERN_DEBUG "jffs2_build_inode_pass1 ignoring new metadata at 0x%08x\n",
-					  tn->fn->raw->flash_offset &~3));
-				
-				jffs2_free_full_dnode(tn->fn);
-				jffs2_free_tmp_dnode_info(tn);
-			}
-		}
-	}
-		
-	/* OK. Now clear up */
-	if (metadata) {
-		jffs2_free_full_dnode(metadata->fn);
-		jffs2_free_tmp_dnode_info(metadata);
-	}
-	metadata = NULL;
-	
-	while (fraglist) {
-		struct jffs2_node_frag *frag;
-		frag = fraglist;
-		fraglist = fraglist->next;
-		
-		if (frag->node && !(--frag->node->frags)) {
-			jffs2_free_full_dnode(frag->node);
-		}
-		jffs2_free_node_frag(frag);
-	}
-
-	/* Now for each child, increase nlink */
-	for(fd=ic->scan->dents; fd; fd = fd->next) {
+	/* For each child, increase nlink */
+	for(fd=ic->scan_dents; fd; fd = fd->next) {
 		struct jffs2_inode_cache *child_ic;
 		if (!fd->ino)
 			continue;
+		
+		/* XXX: Can get high latency here with huge directories */
 
 		child_ic = jffs2_get_ino_cache(c, fd->ino);
 		if (!child_ic) {
@@ -178,6 +137,10 @@ int jffs2_build_inode_pass1(struct jffs2
 
 		if (child_ic->nlink++ && fd->type == DT_DIR) {
 			printk(KERN_NOTICE "Child dir \"%s\" (ino #%u) of dir ino #%u appears to be a hard link\n", fd->name, fd->ino, ic->ino);
+			if (fd->ino == 1 && ic->ino == 1) {
+				printk(KERN_NOTICE "This is mostly harmless, and probably caused by creating a JFFS2 image\n");
+				printk(KERN_NOTICE "using a buggy version of mkfs.jffs2. Use at least v1.17.\n");
+			}
 			/* What do we do about it? */
 		}
 		D1(printk(KERN_DEBUG "Increased nlink for child \"%s\" (ino #%u)\n", fd->name, fd->ino));
@@ -192,26 +155,33 @@ int jffs2_build_remove_unlinked_inode(st
 	struct jffs2_full_dirent *fd;
 	int ret = 0;
 
-	if(!ic->scan) {
-		D1(printk(KERN_DEBUG "ino #%u was already removed\n", ic->ino));
-		return 0;
-	}
-
 	D1(printk(KERN_DEBUG "JFFS2: Removing ino #%u with nlink == zero.\n", ic->ino));
 	
 	for (raw = ic->nodes; raw != (void *)ic; raw = raw->next_in_ino) {
-		D1(printk(KERN_DEBUG "obsoleting node at 0x%08x\n", raw->flash_offset&~3));
+		D1(printk(KERN_DEBUG "obsoleting node at 0x%08x\n", ref_offset(raw)));
 		jffs2_mark_node_obsolete(c, raw);
 	}
 
-	if (ic->scan->dents) {
-		printk(KERN_NOTICE "Inode #%u was a directory with children - removing those too...\n", ic->ino);
-	
-		while(ic->scan->dents) {
+	if (ic->scan_dents) {
+		int whinged = 0;
+		D1(printk(KERN_DEBUG "Inode #%u was a directory which may have children...\n", ic->ino));
+
+		while(ic->scan_dents) {
 			struct jffs2_inode_cache *child_ic;
 
-			fd = ic->scan->dents;
-			ic->scan->dents = fd->next;
+			fd = ic->scan_dents;
+			ic->scan_dents = fd->next;
+
+			if (!fd->ino) {
+				/* It's a deletion dirent. Ignore it */
+				D1(printk(KERN_DEBUG "Child \"%s\" is a deletion dirent, skipping...\n", fd->name));
+				jffs2_free_full_dirent(fd);
+				continue;
+			}
+			if (!whinged) {
+				whinged = 1;
+				printk(KERN_NOTICE "Inode #%u was a directory with children - removing those too...\n", ic->ino);
+			}
 
 			D1(printk(KERN_DEBUG "Removing child \"%s\", ino #%u\n",
 				  fd->name, fd->ino));
@@ -219,6 +189,7 @@ int jffs2_build_remove_unlinked_inode(st
 			child_ic = jffs2_get_ino_cache(c, fd->ino);
 			if (!child_ic) {
 				printk(KERN_NOTICE "Cannot remove child \"%s\", ino #%u, because it doesn't exist\n", fd->name, fd->ino);
+				jffs2_free_full_dirent(fd);
 				continue;
 			}
 			jffs2_free_full_dirent(fd);
@@ -226,8 +197,6 @@ int jffs2_build_remove_unlinked_inode(st
 		}
 		ret = -EAGAIN;
 	}
-	kfree(ic->scan);
-	ic->scan = NULL;
 
 	/*
 	   We don't delete the inocache from the hash list and free it yet. 
@@ -251,21 +220,27 @@ int jffs2_do_mount_fs(struct jffs2_sb_in
 		c->blocks[i].offset = i * c->sector_size;
 		c->blocks[i].free_size = c->sector_size;
 		c->blocks[i].dirty_size = 0;
+		c->blocks[i].wasted_size = 0;
+		c->blocks[i].unchecked_size = 0;
 		c->blocks[i].used_size = 0;
 		c->blocks[i].first_node = NULL;
 		c->blocks[i].last_node = NULL;
 	}
 
-	spin_lock_init(&c->nodelist_lock);
 	init_MUTEX(&c->alloc_sem);
+	init_MUTEX(&c->erase_free_sem);
 	init_waitqueue_head(&c->erase_wait);
+	init_waitqueue_head(&c->inocache_wq);
 	spin_lock_init(&c->erase_completion_lock);
 	spin_lock_init(&c->inocache_lock);
 
 	INIT_LIST_HEAD(&c->clean_list);
+	INIT_LIST_HEAD(&c->very_dirty_list);
 	INIT_LIST_HEAD(&c->dirty_list);
+	INIT_LIST_HEAD(&c->erasable_list);
 	INIT_LIST_HEAD(&c->erasing_list);
 	INIT_LIST_HEAD(&c->erase_pending_list);
+	INIT_LIST_HEAD(&c->erasable_pending_wbuf_list);
 	INIT_LIST_HEAD(&c->erase_complete_list);
 	INIT_LIST_HEAD(&c->free_list);
 	INIT_LIST_HEAD(&c->bad_list);
--- a/packages/fs/jffs2/current/src/compr.c
+++ b/packages/fs/jffs2/current/src/compr.c
@@ -7,18 +7,16 @@
  *
  * For licensing information, see the file 'LICENCE' in this directory.
  *
- * $Id: compr.c,v 1.23 2002/01/25 01:49:26 dwmw2 Exp $
+ * $Id: compr.c,v 1.26 2003/01/12 13:21:28 dwmw2 Exp $
  *
  */
 
-#ifdef __KERNEL__
+#if defined(__KERNEL__) || defined (__ECOS)
 #include <linux/kernel.h>
 #include <linux/string.h>
 #include <linux/errno.h>
-#include <linux/jffs2.h>
-#elif defined(__ECOS)
-#include "jffs2.h"
-#else /* Userland */
+#include <linux/types.h>
+#else 
 #define KERN_DEBUG
 #define KERN_NOTICE
 #define KERN_WARNING
@@ -26,8 +24,9 @@
 #include <stdio.h>
 #include <stdint.h>
 #include <errno.h>
+#endif
+
 #include <linux/jffs2.h>
-#endif
 
 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);
--- a/packages/fs/jffs2/current/src/compr_rtime.c
+++ b/packages/fs/jffs2/current/src/compr_rtime.c
@@ -7,7 +7,7 @@
  *
  * For licensing information, see the file 'LICENCE' in this directory.
  *
- * $Id: compr_rtime.c,v 1.8 2002/01/25 01:49:26 dwmw2 Exp $
+ * $Id: compr_rtime.c,v 1.9 2002/05/20 14:56:37 dwmw2 Exp $
  *
  *
  * Very simple lz77-ish encoder.
@@ -21,14 +21,10 @@
  *
  */
 
-#ifdef __ECOS
-#include "jffs2port.h"
-#else
 #include <linux/kernel.h>
 #include <linux/types.h>
 #include <linux/errno.h>
 #include <linux/string.h> 
-#endif
 
 /* _compress returns the compressed size, -1 if bigger */
 int jffs2_rtime_compress(unsigned char *data_in, unsigned char *cpage_out, 
--- a/packages/fs/jffs2/current/src/compr_rubin.c
+++ b/packages/fs/jffs2/current/src/compr_rubin.c
@@ -7,17 +7,13 @@
  *
  * For licensing information, see the file 'LICENCE' in this directory.
  *
- * $Id: compr_rubin.c,v 1.16 2002/01/25 01:49:26 dwmw2 Exp $
+ * $Id: compr_rubin.c,v 1.17 2002/05/20 14:56:37 dwmw2 Exp $
  *
  */
 
  
-#ifdef __ECOS
-#include "jffs2port.h"
-#else
 #include <linux/string.h>
 #include <linux/types.h>
-#endif
 #include "compr_rubin.h"
 #include "histo_mips.h"
 
--- a/packages/fs/jffs2/current/src/compr_zlib.c
+++ b/packages/fs/jffs2/current/src/compr_zlib.c
@@ -7,62 +7,23 @@
  *
  * For licensing information, see the file 'LICENCE' in this directory.
  *
- * $Id: compr_zlib.c,v 1.12 2002/01/25 01:49:26 dwmw2 Exp $
+ * $Id: compr_zlib.c,v 1.22 2003/01/12 13:21:28 dwmw2 Exp $
  *
  */
 
-/* Be careful - there are three environments are supported by this file. 
- * - Linux kernel.
- * - eCos kernel.
- * - Userspace (mkfs.jffs2).
- */
+#if !defined(__KERNEL__) && !defined(__ECOS)
+#error "The userspace support got too messy and was removed. Update your mkfs.jffs2"
+#endif
 
-#ifdef __ECOS
-	/* Compiling in eCos (or RedBoot) */
-#include <cyg/compress/zlib.h>
-#include "jffs2port.h"
-#include "nodelist.h"
-
-#elif defined(__KERNEL__)
-	/* Compiling in Linux kernel */
 #include <linux/config.h>
 #include <linux/kernel.h>
 #include <linux/mtd/compatmac.h> /* for min() */
 #include <linux/slab.h>
-#include <linux/jffs2.h>
+#include <linux/zlib.h>
+#include <linux/zutil.h>
+#include <asm/semaphore.h>
 #include "nodelist.h"
 
-static void *zalloc(void *opaque, unsigned nr, unsigned size)
-{
-	/* How much does it request? Should we use vmalloc? Or be dynamic? */
-	return kmalloc(nr * size, GFP_KERNEL);
-}
-
-static void zfree(void *opaque, void *addr)
-{
-	kfree(addr);
-}
-#ifdef CONFIG_ZLIB /* 2.5 kernels will have a shared zlib, at last. */
-#include <linux/zlib.h>
-#else
-#include "zlib.h"
-#endif
-
-#else
-	/* Compiling in Linux(ish) userspace */
-#define min(x,y) ((x)<(y)?(x):(y))
-#ifndef D1
-#define D1(x)
-#endif
-#define KERN_DEBUG
-#define KERN_NOTICE
-#define KERN_WARNING
-#define printk printf
-#include <stdint.h>
-#include <stdio.h>
-#include <zlib.h>
-#endif
-
 	/* Plan: call deflate() with avail_in == *sourcelen, 
 		avail_out = *dstlen - 12 and flush == Z_FINISH. 
 		If it doesn't manage to finish,	call it again with
@@ -72,98 +33,145 @@ static void zfree(void *opaque, void *ad
 	*/
 #define STREAM_END_SPACE 12
 
+static DECLARE_MUTEX(deflate_sem);
+static DECLARE_MUTEX(inflate_sem);
+static z_stream inf_strm, def_strm;
+
+#ifdef __KERNEL__ /* Linux-only */
+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());
+		return -ENOMEM;
+	}
+	D1(printk(KERN_DEBUG "Allocated %d bytes for deflate workspace\n", zlib_deflate_workspacesize()));
+	inf_strm.workspace = vmalloc(zlib_inflate_workspacesize());
+	if (!inf_strm.workspace) {
+		printk(KERN_WARNING "Failed to allocate %d bytes for inflate workspace\n", zlib_inflate_workspacesize());
+		vfree(def_strm.workspace);
+		return -ENOMEM;
+	}
+	D1(printk(KERN_DEBUG "Allocated %d bytes for inflate workspace\n", zlib_inflate_workspacesize()));
+	return 0;
+}
+
+void jffs2_zlib_exit(void)
+{
+	vfree(def_strm.workspace);
+	vfree(inf_strm.workspace);
+}
+#endif /* __KERNEL__ */
+
 int jffs2_zlib_compress(unsigned char *data_in, unsigned char *cpage_out, 
 		   uint32_t *sourcelen, uint32_t *dstlen)
 {
-	z_stream strm;
 	int ret;
 
 	if (*dstlen <= STREAM_END_SPACE)
 		return -1;
 
-#ifdef __KERNEL__ /* Linux needs own alloc/free funcs */
-	strm.zalloc = zalloc;
-	strm.zfree = zfree;
-#else /* eCos and userspace don't */
-	strm.zalloc = (void *)0;
-	strm.zfree = (void *)0;
-#endif
+	down(&deflate_sem);
 
-	if (Z_OK != deflateInit(&strm, 3)) {
+	if (Z_OK != zlib_deflateInit(&def_strm, 3)) {
 		printk(KERN_WARNING "deflateInit failed\n");
+		up(&deflate_sem);
 		return -1;
 	}
-	strm.next_in = data_in;
-	strm.total_in = 0;
+
+	def_strm.next_in = data_in;
+	def_strm.total_in = 0;
 	
-	strm.next_out = cpage_out;
-	strm.total_out = 0;
+	def_strm.next_out = cpage_out;
+	def_strm.total_out = 0;
 
-	while (strm.total_out < *dstlen - STREAM_END_SPACE && strm.total_in < *sourcelen) {
-		strm.avail_out = *dstlen - (strm.total_out + STREAM_END_SPACE);
-		strm.avail_in = min((unsigned)(*sourcelen-strm.total_in), strm.avail_out);
+	while (def_strm.total_out < *dstlen - STREAM_END_SPACE && def_strm.total_in < *sourcelen) {
+		def_strm.avail_out = *dstlen - (def_strm.total_out + STREAM_END_SPACE);
+		def_strm.avail_in = min((unsigned)(*sourcelen-def_strm.total_in), def_strm.avail_out);
 		D1(printk(KERN_DEBUG "calling deflate with avail_in %d, avail_out %d\n",
-			  strm.avail_in, strm.avail_out));
-		ret = deflate(&strm, Z_PARTIAL_FLUSH);
+			  def_strm.avail_in, def_strm.avail_out));
+		ret = zlib_deflate(&def_strm, Z_PARTIAL_FLUSH);
 		D1(printk(KERN_DEBUG "deflate returned with avail_in %d, avail_out %d, total_in %ld, total_out %ld\n", 
-			  strm.avail_in, strm.avail_out, strm.total_in, strm.total_out));
+			  def_strm.avail_in, def_strm.avail_out, def_strm.total_in, def_strm.total_out));
 		if (ret != Z_OK) {
 			D1(printk(KERN_DEBUG "deflate in loop returned %d\n", ret));
-			deflateEnd(&strm);
+			zlib_deflateEnd(&def_strm);
+			up(&deflate_sem);
 			return -1;
 		}
 	}
-	strm.avail_out += STREAM_END_SPACE;
-	strm.avail_in = 0;
-	ret = deflate(&strm, Z_FINISH);
+	def_strm.avail_out += STREAM_END_SPACE;
+	def_strm.avail_in = 0;
+	ret = zlib_deflate(&def_strm, Z_FINISH);
+	zlib_deflateEnd(&def_strm);
+
 	if (ret != Z_STREAM_END) {
 		D1(printk(KERN_DEBUG "final deflate returned %d\n", ret));
-		deflateEnd(&strm);
-		return -1;
+		ret = -1;
+		goto out;
 	}
-	deflateEnd(&strm);
-
-	D1(printk(KERN_DEBUG "zlib compressed %ld bytes into %ld\n", strm.total_in, strm.total_out));
 
-	if (strm.total_out >= strm.total_in)
-		return -1;
-
+	if (def_strm.total_out >= def_strm.total_in) {
+		D1(printk(KERN_DEBUG "zlib compressed %ld bytes into %ld; failing\n",
+			  def_strm.total_in, def_strm.total_out));
+		ret = -1;
+		goto out;
+	}
 
-	*dstlen = strm.total_out;
-	*sourcelen = strm.total_in;
-	return 0;
+	D1(printk(KERN_DEBUG "zlib compressed %ld bytes into %ld\n",
+		  def_strm.total_in, def_strm.total_out));
+
+	*dstlen = def_strm.total_out;
+	*sourcelen = def_strm.total_in;
+	ret = 0;
+ out:
+	up(&deflate_sem);
+	return ret;
 }
 
 void jffs2_zlib_decompress(unsigned char *data_in, unsigned char *cpage_out,
 		      uint32_t srclen, uint32_t destlen)
 {
-	z_stream strm;
 	int ret;
+	int wbits = MAX_WBITS;
+
+	down(&inflate_sem);
+
+	inf_strm.next_in = data_in;
+	inf_strm.avail_in = srclen;
+	inf_strm.total_in = 0;
+	
+	inf_strm.next_out = cpage_out;
+	inf_strm.avail_out = destlen;
+	inf_strm.total_out = 0;
 
-#ifdef __KERNEL__
-	strm.zalloc = zalloc;
-	strm.zfree = zfree;
-#else
-	strm.zalloc = (void *)0;
-	strm.zfree = (void *)0;
-#endif
+	/* If it's deflate, and it's got no preset dictionary, then
+	   we can tell zlib to skip the adler32 check. */
+	if (srclen > 2 && !(data_in[1] & PRESET_DICT) &&
+	    ((data_in[0] & 0x0f) == Z_DEFLATED) &&
+	    !(((data_in[0]<<8) + data_in[1]) % 31)) {
 
-	if (Z_OK != inflateInit(&strm)) {
+		D2(printk(KERN_DEBUG "inflate skipping adler32\n"));
+		wbits = -((data_in[0] >> 4) + 8);
+		inf_strm.next_in += 2;
+		inf_strm.avail_in -= 2;
+	} else {
+		/* Let this remain D1 for now -- it should never happen */
+		D1(printk(KERN_DEBUG "inflate not skipping adler32\n"));
+	}
+
+
+	if (Z_OK != zlib_inflateInit2(&inf_strm, wbits)) {
 		printk(KERN_WARNING "inflateInit failed\n");
+		up(&inflate_sem);
 		return;
 	}
-	strm.next_in = data_in;
-	strm.avail_in = srclen;
-	strm.total_in = 0;
-	
-	strm.next_out = cpage_out;
-	strm.avail_out = destlen;
-	strm.total_out = 0;
 
-	while((ret = inflate(&strm, Z_FINISH)) == Z_OK)
+	while((ret = zlib_inflate(&inf_strm, Z_FINISH)) == Z_OK)
 		;
 	if (ret != Z_STREAM_END) {
 		printk(KERN_NOTICE "inflate returned %d\n", ret);
 	}
-	inflateEnd(&strm);
+	zlib_inflateEnd(&inf_strm);
+	up(&inflate_sem);
 }
deleted file mode 100644
--- a/packages/fs/jffs2/current/src/crc32.h
+++ /dev/null
@@ -1,22 +0,0 @@
-#ifndef CRC32_H
-#define CRC32_H
-
-/* $Id: crc32.h,v 1.4 2002/01/03 15:20:44 dwmw2 Exp $ */
-
-#ifdef __ECOS
-#include "jffs2port.h"
-#else
-#include <linux/types.h>
-#endif
-
-extern const uint32_t crc32_table[256];
-
-/* Return a 32-bit CRC of the contents of the buffer. */
-
-static inline uint32_t 
-crc32(uint32_t val, const void *ss, int len)
-{
-	return cyg_crc32_accumulate(val,(void *)ss,len);
-}
-
-#endif
--- a/packages/fs/jffs2/current/src/dir-ecos.c
+++ b/packages/fs/jffs2/current/src/dir-ecos.c
@@ -7,13 +7,13 @@
  *
  * For licensing information, see the file 'LICENCE' in this directory.
  *
- * In sync with:
- * $Id: dir.c,v 1.57 2002/01/28 22:25:07 dwmw2 Exp $
+ * $Id: dir-ecos.c,v 1.3 2003/01/22 00:04:13 dwmw2 Exp $
  *
  */
 
+#include <linux/kernel.h>
+#include <linux/crc32.h>
 #include "nodelist.h"
-#include "crc32.h"
 
 /***********************************************************************/
 
@@ -30,7 +30,7 @@ struct inode *jffs2_lookup(struct inode 
 	uint32_t ino = 0;
 	struct inode *inode = NULL;
 
-	D1(printk(KERN_DEBUG "jffs2_lookup()\n"));
+	D1(printk("jffs2_lookup()\n"));
 
 	dir_f = JFFS2_INODE_INFO(dir_i);
 	c = JFFS2_SB_INFO(dir_i->i_sb);
@@ -52,7 +52,7 @@ struct inode *jffs2_lookup(struct inode 
 	if (ino) {
 		inode = iget(dir_i->i_sb, ino);
 		if (!inode) {
-			printk(KERN_WARNING "iget() failed for ino #%u\n", ino);
+			printk("iget() failed for ino #%u\n", ino);
 			return (ERR_PTR(-EIO));
 		}
 	}
@@ -123,7 +123,8 @@ int jffs2_unlink(struct inode *dir_i, st
 
 	ret = jffs2_do_unlink(c, dir_f, d_name->name, 
 			       d_name->len, dead_f);
-	d_inode->i_nlink = dead_f->inocache->nlink;
+	if (dead_f->inocache)
+		d_inode->i_nlink = dead_f->inocache->nlink;
 	return ret;
 }
 /***********************************************************************/
@@ -193,8 +194,8 @@ int jffs2_mkdir (struct inode *dir_i, st
 
 	f = JFFS2_INODE_INFO(inode);
 
-	ri->data_crc = 0;
-	ri->node_crc = crc32(0, ri, sizeof(*ri)-8);
+	ri->data_crc = cpu_to_je32(0);
+	ri->node_crc = cpu_to_je32(crc32(0, ri, sizeof(*ri)-8));
 	
 	fn = jffs2_write_dnode(c, f, ri, NULL, 0, phys_ofs, &writtenlen);
 
@@ -240,19 +241,19 @@ int jffs2_mkdir (struct inode *dir_i, st
 	dir_f = JFFS2_INODE_INFO(dir_i);
 	down(&dir_f->sem);
 
-	rd->magic = JFFS2_MAGIC_BITMASK;
-	rd->nodetype = JFFS2_NODETYPE_DIRENT;
-	rd->totlen = sizeof(*rd) + namelen;
-	rd->hdr_crc = crc32(0, rd, sizeof(struct jffs2_unknown_node)-4);
+	rd->magic = cpu_to_je16(JFFS2_MAGIC_BITMASK);
+	rd->nodetype = cpu_to_je16(JFFS2_NODETYPE_DIRENT);
+	rd->totlen = cpu_to_je32(sizeof(*rd) + namelen);
+	rd->hdr_crc = cpu_to_je32(crc32(0, rd, sizeof(struct jffs2_unknown_node)-4));
 
-	rd->pino = dir_i->i_ino;
-	rd->version = ++dir_f->highest_version;
-	rd->ino = inode->i_ino;
-	rd->mctime = CURRENT_TIME;
+	rd->pino = cpu_to_je32(dir_i->i_ino);
+	rd->version = cpu_to_je32(++dir_f->highest_version);
+	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 = crc32(0, rd, sizeof(*rd)-8);
-	rd->name_crc = crc32(0, d_name->name, namelen);
+	rd->node_crc = cpu_to_je32(crc32(0, rd, sizeof(*rd)-8));
+	rd->name_crc = cpu_to_je32(crc32(0, d_name->name, namelen));
 
 	fd = jffs2_write_dirent(c, dir_f, rd, d_name->name, namelen, phys_ofs, &writtenlen);
 	
@@ -293,8 +294,39 @@ int jffs2_rename (struct inode *old_dir_
 {
 	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;
 
+#if 0 /* FIXME -- this really doesn't belong in individual file systems. 
+	 The fileio code ought to do this for us, or at least part of it */
+	if (new_dentry->d_inode) {
+		if (S_ISDIR(d_inode->i_mode) && 
+		    !S_ISDIR(new_dentry->d_inode->i_mode)) {
+			/* Cannot rename directory over non-directory */
+			return -EINVAL;
+		}
+
+		victim_f = JFFS2_INODE_INFO(new_dentry->d_inode);
+
+		if (S_ISDIR(new_dentry->d_inode->i_mode)) {
+			struct jffs2_full_dirent *fd;
+
+			if (!S_ISDIR(d_inode->i_mode)) {
+				/* Cannot rename non-directory over directory */
+				return -EINVAL;
+			}
+			down(&victim_f->sem);
+			for (fd = victim_f->dents; fd; fd = fd->next) {
+				if (fd->ino) {
+					up(&victim_f->sem);
+					return -ENOTEMPTY;
+				}
+			}
+			up(&victim_f->sem);
+		}
+	}
+#endif
+
 	/* XXX: We probably ought to alloc enough space for
 	   both nodes at the same time. Writing the new link, 
 	   then getting -ENOSPC, is quite bad :)
@@ -313,20 +345,30 @@ int jffs2_rename (struct inode *old_dir_
 	if (ret)
 		return ret;
 
+	if (victim_f) {
+		/* There was a victim. Kill it off nicely */
+		/* Don't oops if the victim was a dirent pointing to an
+		   inode which didn't exist. */
+		if (victim_f->inocache) {
+			down(&victim_f->sem);
+			victim_f->inocache->nlink--;
+			up(&victim_f->sem);
+		}
+	}
+
 	/* Unlink the original */
 	ret = jffs2_do_unlink(c, JFFS2_INODE_INFO(old_dir_i), 
 		      old_d_name->name, old_d_name->len, NULL);
 
-	/* We don't touch inode->i_nlink */
-
 	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);
-		d_inode->i_nlink = f->inocache->nlink++;
+		if (f->inocache)
+			d_inode->i_nlink = f->inocache->nlink++;
 		up(&f->sem);
 
-               printk(KERN_NOTICE "jffs2_rename(): Link succeeded, unlink failed (err %d). You now have a hard link\n", ret);
+		printk(KERN_NOTICE "jffs2_rename(): Link succeeded, unlink failed (err %d). You now have a hard link\n", ret);
 	}
 	return ret;
 }
--- a/packages/fs/jffs2/current/src/erase.c
+++ b/packages/fs/jffs2/current/src/erase.c
@@ -7,18 +7,18 @@
  *
  * For licensing information, see the file 'LICENCE' in this directory.
  *
- * $Id: erase.c,v 1.30 2002/01/09 13:25:57 dwmw2 Exp $
+ * $Id: erase.c,v 1.49 2003/01/21 18:11:28 dwmw2 Exp $
  *
  */
 
-#ifndef __ECOS
 #include <linux/kernel.h>
 #include <linux/slab.h>
 #include <linux/mtd/mtd.h>
-#include <linux/interrupt.h>
-#endif
+#include <linux/compiler.h>
+#include <linux/crc32.h>
+#include <linux/sched.h>
+#include <linux/pagemap.h>
 #include "nodelist.h"
-#include "crc32.h"
 
 struct erase_priv_struct {
 	struct jffs2_eraseblock *jeb;
@@ -28,29 +28,31 @@ struct erase_priv_struct {
 #ifndef __ECOS
 static void jffs2_erase_callback(struct erase_info *);
 #endif
+static void jffs2_erase_failed(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb);
 static void jffs2_erase_succeeded(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb);
 static void jffs2_free_all_node_refs(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb);
+static void jffs2_mark_erased_block(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb);
 
 void jffs2_erase_block(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb)
 {
 	int ret;
 #ifdef __ECOS
-	ret = jffs2_flash_erase(c, jeb);
-	if (!ret) {
-		jffs2_erase_succeeded(c, jeb);
-		return;
-	}
+       ret = jffs2_flash_erase(c, jeb);
+       if (!ret) {
+               jffs2_erase_succeeded(c, jeb);
+               return;
+       }
 #else /* Linux */
 	struct erase_info *instr;
 
 	instr = kmalloc(sizeof(struct erase_info) + sizeof(struct erase_priv_struct), GFP_KERNEL);
 	if (!instr) {
 		printk(KERN_WARNING "kmalloc for struct erase_info in jffs2_erase_block failed. Refiling block for later\n");
-		spin_lock_bh(&c->erase_completion_lock);
+		spin_lock(&c->erase_completion_lock);
 		list_del(&jeb->list);
 		list_add(&jeb->list, &c->erase_pending_list);
 		c->erasing_size -= c->sector_size;
-		spin_unlock_bh(&c->erase_completion_lock);
+		spin_unlock(&c->erase_completion_lock);
 		return;
 	}
 
@@ -65,20 +67,25 @@ void jffs2_erase_block(struct jffs2_sb_i
 	((struct erase_priv_struct *)instr->priv)->jeb = jeb;
 	((struct erase_priv_struct *)instr->priv)->c = c;
 
+	/* NAND , read out the fail counter, if possible */
+	if (!jffs2_can_mark_obsolete(c)) 
+		jffs2_nand_read_failcnt(c,jeb);
+		
 	ret = c->mtd->erase(c->mtd, instr);
 	if (!ret)
 		return;
 
 	kfree(instr);
-#endif
+#endif /* __ECOS */
+
 	if (ret == -ENOMEM || ret == -EAGAIN) {
 		/* Erase failed immediately. Refile it on the list */
 		D1(printk(KERN_DEBUG "Erase at 0x%08x failed: %d. Refiling on erase_pending_list\n", jeb->offset, ret));
-		spin_lock_bh(&c->erase_completion_lock);
+		spin_lock(&c->erase_completion_lock);
 		list_del(&jeb->list);
 		list_add(&jeb->list, &c->erase_pending_list);
 		c->erasing_size -= c->sector_size;
-		spin_unlock_bh(&c->erase_completion_lock);
+		spin_unlock(&c->erase_completion_lock);
 		return;
 	}
 
@@ -87,50 +94,62 @@ void jffs2_erase_block(struct jffs2_sb_i
 	else
 		printk(KERN_WARNING "Erase at 0x%08x failed immediately: errno %d\n", jeb->offset, ret);
 
-	spin_lock_bh(&c->erase_completion_lock);
-	list_del(&jeb->list);
-	list_add(&jeb->list, &c->bad_list);
-	c->nr_erasing_blocks--;
-	c->bad_size += c->sector_size;
-	c->erasing_size -= c->sector_size;
-	spin_unlock_bh(&c->erase_completion_lock);
-	wake_up(&c->erase_wait);
+	/* Note: This is almost identical to jffs2_erase_failed() except
+	   for the fact that we used spin_lock() not spin_lock(). If
+	   we could use spin_lock() from a BH, we could merge them.
+	   Or if we abandon the idea that MTD drivers may call the erase
+	   callback from a BH, I suppose :)
+	*/
+	jffs2_erase_failed(c, jeb);
 }
 
 void jffs2_erase_pending_blocks(struct jffs2_sb_info *c)
 {
 	struct jffs2_eraseblock *jeb;
 
-	spin_lock_bh(&c->erase_completion_lock);
-	while (!list_empty(&c->erase_pending_list)) {
+	down(&c->erase_free_sem);
+
+	spin_lock(&c->erase_completion_lock);
 
-		jeb = list_entry(c->erase_pending_list.next, struct jffs2_eraseblock, list);
+	while (!list_empty(&c->erase_complete_list) ||
+	       !list_empty(&c->erase_pending_list)) {
 
-		D1(printk(KERN_DEBUG "Starting erase of pending block 0x%08x\n", jeb->offset));
+		if (!list_empty(&c->erase_complete_list)) {
+			jeb = list_entry(c->erase_complete_list.next, struct jffs2_eraseblock, list);
+			list_del(&jeb->list);
+			spin_unlock(&c->erase_completion_lock);
+			jffs2_mark_erased_block(c, jeb);
 
-		list_del(&jeb->list);
-		c->erasing_size += c->sector_size;
-		c->free_size -= jeb->free_size;
-		c->used_size -= jeb->used_size;
-		c->dirty_size -= jeb->dirty_size;
-		jeb->used_size = jeb->dirty_size = jeb->free_size = 0;
-		jffs2_free_all_node_refs(c, jeb);
-		list_add(&jeb->list, &c->erasing_list);
-		spin_unlock_bh(&c->erase_completion_lock);
-		
-		jffs2_erase_block(c, jeb);
-#ifndef __ECOS
+		} else if (!list_empty(&c->erase_pending_list)) {
+			jeb = list_entry(c->erase_pending_list.next, struct jffs2_eraseblock, list);
+			D1(printk(KERN_DEBUG "Starting erase of pending block 0x%08x\n", jeb->offset));
+			list_del(&jeb->list);
+			c->erasing_size += c->sector_size;
+			c->free_size -= jeb->free_size;
+			c->used_size -= jeb->used_size;
+			c->dirty_size -= jeb->dirty_size;
+			jeb->used_size = jeb->dirty_size = jeb->free_size = 0;
+			jffs2_free_all_node_refs(c, jeb);
+			list_add(&jeb->list, &c->erasing_list);
+			spin_unlock(&c->erase_completion_lock);
+
+			jffs2_erase_block(c, jeb);
+
+		} else {
+			BUG();
+		}
+
 		/* Be nice */
-		if (current->need_resched)
-			schedule();
-#endif
-		spin_lock_bh(&c->erase_completion_lock);
+		cond_resched();
+		spin_lock(&c->erase_completion_lock);
 	}
-	spin_unlock_bh(&c->erase_completion_lock);
+
+	spin_unlock(&c->erase_completion_lock);
 	D1(printk(KERN_DEBUG "jffs2_erase_pending_blocks completed\n"));
+
+	up(&c->erase_free_sem);
 }
 
-
 static void jffs2_erase_succeeded(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb)
 {
 	D1(printk(KERN_DEBUG "Erase completed successfully at 0x%08x\n", jeb->offset));
@@ -138,10 +157,11 @@ static void jffs2_erase_succeeded(struct
 	list_del(&jeb->list);
 	list_add_tail(&jeb->list, &c->erase_complete_list);
 	spin_unlock(&c->erase_completion_lock);
+	/* Ensure that kupdated calls us again to mark them clean */
+	jffs2_erase_pending_trigger(c);
 }
 
-#ifndef __ECOS
-static inline void jffs2_erase_failed(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb)
+static void jffs2_erase_failed(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb)
 {
 	 spin_lock(&c->erase_completion_lock);
 	 c->erasing_size -= c->sector_size;
@@ -153,6 +173,7 @@ static inline void jffs2_erase_failed(st
 	 wake_up(&c->erase_wait);
 }	 
 
+#ifndef __ECOS
 static void jffs2_erase_callback(struct erase_info *instr)
 {
 	struct erase_priv_struct *priv = (void *)instr->priv;
@@ -163,11 +184,9 @@ static void jffs2_erase_callback(struct 
 	} else {
 		jffs2_erase_succeeded(priv->c, priv->jeb);
 	}	
-	/* Make sure someone picks up the block off the erase_complete list */
-	OFNI_BS_2SFFJ(priv->c)->s_dirt = 1;
 	kfree(instr);
 }
-#endif
+#endif /* !__ECOS */
 
 /* Hmmm. Maybe we should accept the extra space it takes and make
    this a standard doubly-linked list? */
@@ -224,7 +243,7 @@ static inline void jffs2_remove_node_ref
 		this = ic->nodes;
 	   
 		while(this) {
-			printk( "0x%08x(%d)->", this->flash_offset & ~3, this->flash_offset &3);
+			printk( "0x%08x(%d)->", ref_offset(this), ref_flags(this));
 			if (++i == 5) {
 				printk("\n" KERN_DEBUG);
 				i=0;
@@ -264,113 +283,133 @@ void jffs2_erase_pending_trigger(struct 
 	OFNI_BS_2SFFJ(c)->s_dirt = 1;
 }
 
-void jffs2_mark_erased_blocks(struct jffs2_sb_info *c)
+static void jffs2_mark_erased_block(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb)
 {
-	static struct jffs2_unknown_node marker = {JFFS2_MAGIC_BITMASK, JFFS2_NODETYPE_CLEANMARKER, sizeof(struct jffs2_unknown_node)};
-	struct jffs2_eraseblock *jeb;
-	struct jffs2_raw_node_ref *marker_ref;
+	struct jffs2_raw_node_ref *marker_ref = NULL;
 	unsigned char *ebuf;
 	size_t retlen;
 	int ret;
 
-	marker.hdr_crc = crc32(0, &marker, sizeof(struct jffs2_unknown_node)-4);
-
-	spin_lock_bh(&c->erase_completion_lock);
-	while (!list_empty(&c->erase_complete_list)) {
-		jeb = list_entry(c->erase_complete_list.next, struct jffs2_eraseblock, list);
-		list_del(&jeb->list);
-		spin_unlock_bh(&c->erase_completion_lock);
-
+	if (!jffs2_cleanmarker_oob(c)) {
 		marker_ref = jffs2_alloc_raw_node_ref();
 		if (!marker_ref) {
 			printk(KERN_WARNING "Failed to allocate raw node ref for clean marker\n");
-			/* Come back later */
+			/* Stick it back on the list from whence it came and come back later */
 			jffs2_erase_pending_trigger(c);
+			spin_lock(&c->erase_completion_lock);
+			list_add(&jeb->list, &c->erase_complete_list);
+			spin_unlock(&c->erase_completion_lock);
 			return;
 		}
+	}
+	ebuf = kmalloc(PAGE_SIZE, GFP_KERNEL);
+	if (!ebuf) {
+		printk(KERN_WARNING "Failed to allocate page buffer for verifying erase at 0x%08x. Assuming it worked\n", jeb->offset);
+	} else {
+		uint32_t ofs = jeb->offset;
 
-		ebuf = kmalloc(PAGE_SIZE, GFP_KERNEL);
-		if (!ebuf) {
-			printk(KERN_WARNING "Failed to allocate page buffer for verifying erase at 0x%08x. Assuming it worked\n", jeb->offset);
-		} else {
-			uint32_t ofs = jeb->offset;
+		D1(printk(KERN_DEBUG "Verifying erase at 0x%08x\n", jeb->offset));
+		while(ofs < jeb->offset + c->sector_size) {
+			uint32_t readlen = min((uint32_t)PAGE_SIZE, jeb->offset + c->sector_size - ofs);
+			int i;
 
-			D1(printk(KERN_DEBUG "Verifying erase at 0x%08x\n", jeb->offset));
-			while(ofs < jeb->offset + c->sector_size) {
-				uint32_t readlen = min((uint32_t)PAGE_SIZE, jeb->offset + c->sector_size - ofs);
-				int i;
+			ret = jffs2_flash_read(c, ofs, readlen, &retlen, ebuf);
+			if (ret) {
+				printk(KERN_WARNING "Read of newly-erased block at 0x%08x failed: %d. Putting on bad_list\n", ofs, ret);
+				goto bad;
+			}
+			if (retlen != readlen) {
+				printk(KERN_WARNING "Short read from newly-erased block at 0x%08x. Wanted %d, got %zd\n", ofs, readlen, retlen);
+				goto bad;
+			}
+			for (i=0; i<readlen; i += sizeof(unsigned long)) {
+				/* It's OK. We know it's properly aligned */
+				unsigned long datum = *(unsigned long *)(&ebuf[i]);
+				if (datum + 1) {
+					printk(KERN_WARNING "Newly-erased block contained word 0x%lx at offset 0x%08x\n", datum, ofs + i);
+				bad: 
+					if (!jffs2_cleanmarker_oob(c))
+						jffs2_free_raw_node_ref(marker_ref);
+					else 
+						jffs2_write_nand_badblock( c ,jeb );
+					kfree(ebuf);
+				bad2:
+					spin_lock(&c->erase_completion_lock);
+					c->erasing_size -= c->sector_size;
+					c->bad_size += c->sector_size;
 
-				ret = jffs2_flash_read(c, ofs, readlen, &retlen, ebuf);
-				if (ret < 0) {
-					printk(KERN_WARNING "Read of newly-erased block at 0x%08x failed: %d. Putting on bad_list\n", ofs, ret);
-					goto bad;
-				}
-				if (retlen != readlen) {
-					printk(KERN_WARNING "Short read from newly-erased block at 0x%08x. Wanted %d, got %d\n", ofs, readlen, retlen);
-					goto bad;
+					list_add_tail(&jeb->list, &c->bad_list);
+					c->nr_erasing_blocks--;
+					spin_unlock(&c->erase_completion_lock);
+					wake_up(&c->erase_wait);
+					return;
 				}
-				for (i=0; i<readlen; i += sizeof(unsigned long)) {
-					/* It's OK. We know it's properly aligned */
-					unsigned long datum = *(unsigned long *)(&ebuf[i]);
-					if (datum + 1) {
-						printk(KERN_WARNING "Newly-erased block contained word 0x%lx at offset 0x%08x\n", datum, ofs + i);
-					bad: 
-						jffs2_free_raw_node_ref(marker_ref);
-						kfree(ebuf);
-					bad2:
-						spin_lock_bh(&c->erase_completion_lock);
-						c->erasing_size -= c->sector_size;
-						c->bad_size += c->sector_size;
+			}
+			ofs += readlen;
+			cond_resched();
+		}
+		kfree(ebuf);
+	}
+					
+	/* Write the erase complete marker */	
+	D1(printk(KERN_DEBUG "Writing erased marker to block at 0x%08x\n", jeb->offset));
+	if (jffs2_cleanmarker_oob(c)) {
 
-						list_add_tail(&jeb->list, &c->bad_list);
-						c->nr_erasing_blocks--;
-						spin_unlock_bh(&c->erase_completion_lock);
-						wake_up(&c->erase_wait);
-						return;
-					}
-				}
-				ofs += readlen;
-			}
-			kfree(ebuf);
-		}
-					
-		/* Write the erase complete marker */	
-		D1(printk(KERN_DEBUG "Writing erased marker to block at 0x%08x\n", jeb->offset));
-		ret = jffs2_flash_write(c, jeb->offset, sizeof(marker), &retlen, (char *)&marker);
+		if (jffs2_write_nand_cleanmarker(c, jeb))
+			goto bad2;
+			
+		jeb->first_node = jeb->last_node = NULL;
+
+		jeb->free_size = c->sector_size;
+		jeb->used_size = 0;
+		jeb->dirty_size = 0;
+		jeb->wasted_size = 0;
+	} else {
+		struct jffs2_unknown_node marker = {
+			.magic =	cpu_to_je16(JFFS2_MAGIC_BITMASK),
+			.nodetype =	cpu_to_je16(JFFS2_NODETYPE_CLEANMARKER),
+			.totlen =	cpu_to_je32(c->cleanmarker_size)
+		};
+
+		marker.hdr_crc = cpu_to_je32(crc32(0, &marker, je32_to_cpu(marker.totlen) - 4));
+
+		ret = jffs2_flash_write(c, jeb->offset, je32_to_cpu(marker.totlen), &retlen, (char *)&marker);
 		if (ret) {
 			printk(KERN_WARNING "Write clean marker to block at 0x%08x failed: %d\n",
 			       jeb->offset, ret);
 			goto bad2;
 		}
-		if (retlen != sizeof(marker)) {
-			printk(KERN_WARNING "Short write to newly-erased block at 0x%08x: Wanted %d, got %d\n",
-			       jeb->offset, sizeof(marker), retlen);
+		if (retlen != je32_to_cpu(marker.totlen)) {
+			printk(KERN_WARNING "Short write to newly-erased block at 0x%08x: Wanted %d, got %zd\n",
+			       jeb->offset, je32_to_cpu(marker.totlen), retlen);
 			goto bad2;
 		}
 
 		marker_ref->next_in_ino = NULL;
 		marker_ref->next_phys = NULL;
-		marker_ref->flash_offset = jeb->offset;
-		marker_ref->totlen = PAD(sizeof(marker));
-
+		marker_ref->flash_offset = jeb->offset | REF_NORMAL;
+		marker_ref->totlen = PAD(je32_to_cpu(marker.totlen));
+			
 		jeb->first_node = jeb->last_node = marker_ref;
-
+			
 		jeb->free_size = c->sector_size - marker_ref->totlen;
 		jeb->used_size = marker_ref->totlen;
 		jeb->dirty_size = 0;
+		jeb->wasted_size = 0;
+	}
 
-		spin_lock_bh(&c->erase_completion_lock);
-		c->erasing_size -= c->sector_size;
-		c->free_size += jeb->free_size;
-		c->used_size += jeb->used_size;
+	spin_lock(&c->erase_completion_lock);
+	c->erasing_size -= c->sector_size;
+	c->free_size += jeb->free_size;
+	c->used_size += jeb->used_size;
 
-		ACCT_SANITY_CHECK(c,jeb);
-		ACCT_PARANOIA_CHECK(jeb);
+	ACCT_SANITY_CHECK(c,jeb);
+	D1(ACCT_PARANOIA_CHECK(jeb));
 
-		list_add_tail(&jeb->list, &c->free_list);
-		c->nr_erasing_blocks--;
-		c->nr_free_blocks++;
-		wake_up(&c->erase_wait);
-	}
-	spin_unlock_bh(&c->erase_completion_lock);
+	list_add_tail(&jeb->list, &c->free_list);
+	c->nr_erasing_blocks--;
+	c->nr_free_blocks++;
+	spin_unlock(&c->erase_completion_lock);
+	wake_up(&c->erase_wait);
 }
+
--- a/packages/fs/jffs2/current/src/file-ecos.c
+++ b/packages/fs/jffs2/current/src/file-ecos.c
@@ -7,18 +7,14 @@
  *
  * For licensing information, see the file 'LICENCE' in this directory.
  *
- * $Id: file.c,v 1.63 2002/01/07 11:18:05 dwmw2 Exp $
+ * $Id: file-ecos.c,v 1.5 2003/01/22 00:04:13 dwmw2 Exp $
  *
  */
 
-//#include <linux/kernel.h>
-//#include <linux/mtd/compatmac.h> /* for min() */
-//#include <linux/slab.h>
-//#include <linux/fs.h>
-//#include <linux/pagemap.h>
-#include "jffs2.h"
+#include <linux/kernel.h>
+#include <linux/pagemap.h>
+#include <linux/crc32.h>
 #include "nodelist.h"
-#include "crc32.h"
 
 int jffs2_do_readpage_nolock (struct inode *inode, struct page *pg)
 {
@@ -80,8 +76,6 @@ int jffs2_prepare_write (struct inode *d
 	uint32_t pageofs = pg->index << PAGE_CACHE_SHIFT;
 	int ret = 0;
 
-	down(&f->sem);
-	//D1(printk(KERN_DEBUG "jffs2_prepare_write() nrpages %ld\n", inode->i_mapping->nrpages));
 	D1(printk(KERN_DEBUG "jffs2_prepare_write()\n"));
 
 	if (pageofs > inode->i_size) {
@@ -95,30 +89,31 @@ int jffs2_prepare_write (struct inode *d
 			  (unsigned int)inode->i_size, pageofs));
 
 		ret = jffs2_reserve_space(c, sizeof(ri), &phys_ofs, &alloc_len, ALLOC_NORMAL);
-		if (ret) {
-			up(&f->sem);
+		if (ret)
 			return ret;
-		}
+
+		down(&f->sem);
 		memset(&ri, 0, sizeof(ri));
 
-		ri.magic = JFFS2_MAGIC_BITMASK;
-		ri.nodetype = JFFS2_NODETYPE_INODE;
-		ri.totlen = sizeof(ri);
-		ri.hdr_crc = crc32(0, &ri, sizeof(struct jffs2_unknown_node)-4);
+		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.ino = f->inocache->ino;
-		ri.version = ++f->highest_version;
-		ri.mode = inode->i_mode;
-		ri.uid = inode->i_uid;
-		ri.gid = inode->i_gid;
-		ri.isize = max((uint32_t)inode->i_size, pageofs);
-		ri.atime = ri.ctime = ri.mtime = CURRENT_TIME;
-		ri.offset = inode->i_size;
-		ri.dsize = pageofs - inode->i_size;
-		ri.csize = 0;
+		ri.ino = cpu_to_je32(f->inocache->ino);
+		ri.version = cpu_to_je32(++f->highest_version);
+		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.isize = cpu_to_je32(max((uint32_t)inode->i_size, pageofs));
+		ri.atime = ri.ctime = ri.mtime = cpu_to_je32(cyg_timestamp());
+		ri.offset = cpu_to_je32(inode->i_size);
+		ri.dsize = cpu_to_je32(pageofs - inode->i_size);
+		ri.csize = cpu_to_je32(0);
 		ri.compr = JFFS2_COMPR_ZERO;
-		ri.node_crc = crc32(0, &ri, sizeof(ri)-8);
-		ri.data_crc = 0;
+		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, NULL);
 		jffs2_complete_reservation(c);
@@ -141,16 +136,18 @@ int jffs2_prepare_write (struct inode *d
 			return ret;
 		}
 		inode->i_size = pageofs;
+		up(&f->sem);
 	}
 	
 
-	/* Read in the page if it wasn't already present */
+	/* Read in the page if it wasn't already present, unless it's a whole page */
         // eCos has no concept of uptodate and by default always reads pages afresh
-	if (!Page_Uptodate(pg) && (start || end < PAGE_SIZE))
+	if (!Page_Uptodate(pg) && (start || end < PAGE_CACHE_SIZE)) {
+		down(&f->sem);
 		ret = jffs2_do_readpage_nolock(inode, pg);
-	//D1(printk(KERN_DEBUG "end prepare_write(). nrpages %ld\n", inode->i_mapping->nrpages));
+		up(&f->sem);
+	}
 	D1(printk(KERN_DEBUG "end prepare_write()\n"));
-	up(&f->sem);
 	return ret;
 }
 
@@ -178,16 +175,16 @@ int jffs2_commit_write (struct inode *d_
 	}
 
 	/* Set the fields that the generic jffs2_write_inode_range() code can't find */
-	ri->ino = inode->i_ino;
-	ri->mode = inode->i_mode;
-	ri->uid = inode->i_uid;
-	ri->gid = inode->i_gid;
-	ri->isize = (uint32_t)inode->i_size;
-	ri->atime = ri->ctime = ri->mtime = CURRENT_TIME;
+	ri->ino = cpu_to_je32(inode->i_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->isize = cpu_to_je32((uint32_t)inode->i_size);
+	ri->atime = ri->ctime = ri->mtime = cpu_to_je32(cyg_timestamp());
 
-	/* We rely on the fact that generic_file_write() currently kmaps the page for us. */
 	ret = jffs2_write_inode_range(c, f, ri, page_address(pg) + start,
-				      (pg->index << PAGE_CACHE_SHIFT) + start, end - start, &writtenlen);
+				      (pg->index << PAGE_CACHE_SHIFT) + start,
+				      end - start, &writtenlen);
 
 	if (ret) {
 		/* There was an error writing. */
@@ -197,7 +194,7 @@ int jffs2_commit_write (struct inode *d_
 	if (writtenlen) {
 		if (inode->i_size < (pg->index << PAGE_CACHE_SHIFT) + start + writtenlen) {
 			inode->i_size = (pg->index << PAGE_CACHE_SHIFT) + start + writtenlen;
-			inode->i_ctime = inode->i_mtime = ri->ctime;
+			inode->i_ctime = inode->i_mtime = je32_to_cpu(ri->ctime);
 		}
 	}
 
new file mode 100644
--- /dev/null
+++ b/packages/fs/jffs2/current/src/fs-ecos.c
@@ -0,0 +1,2050 @@
+/*
+ * JFFS2 -- Journalling Flash File System, Version 2.
+ *
+ * Copyright (C) 2001, 2002 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.8 2003/01/21 18:13:01 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 "nodelist.h"
+
+#include <errno.h>
+#include <string.h>
+#include <cyg/io/io.h>
+#include <cyg/io/config_keys.h>
+#include <cyg/io/flash.h>
+
+//==========================================================================
+// Forward definitions
+
+// 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);
+static int jffs2_ops_unlink(cyg_mtab_entry * mte, cyg_dir dir,
+			    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);
+static int jffs2_ops_link(cyg_mtab_entry * mte, cyg_dir dir1, const char *name1,
+			  cyg_dir dir2, const char *name2, int type);
+static int jffs2_opendir(cyg_mtab_entry * mte, cyg_dir dir, const char *name,
+			 cyg_file * fte);
+static int jffs2_chdir(cyg_mtab_entry * mte, cyg_dir dir, const char *name,
+		       cyg_dir * dir_out);
+static int jffs2_stat(cyg_mtab_entry * mte, cyg_dir dir, const char *name,
+		      struct stat *buf);
+static int jffs2_getinfo(cyg_mtab_entry * mte, cyg_dir dir, const char *name,
+			 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);
+
+// File operations
+static int jffs2_fo_read(struct CYG_FILE_TAG *fp, struct CYG_UIO_TAG *uio);
+static int jffs2_fo_write(struct CYG_FILE_TAG *fp, struct CYG_UIO_TAG *uio);
+static int jffs2_fo_lseek(struct CYG_FILE_TAG *fp, off_t * pos, int whence);
+static int jffs2_fo_ioctl(struct CYG_FILE_TAG *fp, CYG_ADDRWORD com,
+			  CYG_ADDRWORD data);
+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);
+static int jffs2_fo_setinfo(struct CYG_FILE_TAG *fp, int key, void *buf,
+			    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);
+
+//==========================================================================
+// 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.
+
+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);
+
+// -------------------------------------------------------------------------
+// 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
+};
+
+// -------------------------------------------------------------------------
+// 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
+};
+
+//==========================================================================
+// 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
+
+//==========================================================================
+// 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?
+};
+
+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)
+{
+	struct inode *cached_inode;
+	struct inode *next_inode;
+
+	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;	// Prveious 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);
+			}
+		}
+	}
+}
+
+//==========================================================================
+// 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)
+{
+	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;
+}
+
+// -------------------------------------------------------------------------
+// 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;
+
+}
+
+// -------------------------------------------------------------------------
+// jffs2_find()
+// Main interface to directory search code. This is used in all file
+// level operations to locate the object named by the pathname.
+
+static int jffs2_find(jffs2_dirsearch * d)
+{
+	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
+	}
+}
+
+//==========================================================================
+// Pathconf support
+// This function provides support for pathconf() and fpathconf().
+
+static int jffs2_pathconf(struct inode *node, struct cyg_pathconf_info *info)
+{
+	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;
+	}
+
+	return err;
+}
+
+//==========================================================================
+// Filesystem operations
+
+// -------------------------------------------------------------------------
+// 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;
+	}
+
+	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;
+	}
+
+	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);
+
+	return err;
+}
+
+static int jffs2_mount(cyg_fstab_entry * fste, cyg_mtab_entry * mte)
+{
+	extern cyg_mtab_entry mtab[], 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 = &mtab[0]; m != &mtab_end; m++) {
+		// stop if there are more than the configured maximum
+		if (m - &mtab[0] >= CYGNUM_FILEIO_MTAB_MAX) {
+			m = &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);
+
+		err = jffs2_read_super(jffs2_sb);
+
+		if (err) {
+			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);
+	}
+	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));
+
+	return ENOERR;
+}
+
+// -------------------------------------------------------------------------
+// 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"));
+
+	// Decrement the mount count
+	jffs2_sb->s_mount_count--;
+
+	// Only really umount if this is the only mount
+	if (jffs2_sb->s_mount_count == 0) {
+
+		// Check for open/inuse root or any cached inodes
+//if( root->i_count != 1 || root->i_cache_next != NULL) // root icount was set to 1 on mount
+		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 root pointer
+		mte->root = CYG_DIR_NULL;
+		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"));
+	}
+
+	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)
+{
+
+	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);
+
+	if (err == ENOENT) {
+		if (ds.last && (mode & O_CREAT)) {
+			unsigned long hash;
+			struct qstr this;
+			unsigned int c;
+			const char *hashname;
+
+			// No node there, if the O_CREAT bit is set then we must
+			// create a new one. The dir and name fields of the dirsearch
+			// object will have been updated so we know where to put it.
+
+			hashname = ds.name;
+			this.name = hashname;
+			c = *(const unsigned char *) hashname;
+
+			hash = init_name_hash();
+			do {
+				hashname++;
+				hash = partial_name_hash(c, hash);
+				c = *(const unsigned char *) hashname;
+			} while (c && (c != '/'));
+			this.len = hashname - (const char *) this.name;
+			this.hash = end_name_hash(hash);
+
+			err = jffs2_create(ds.dir, &this, S_IRUGO|S_IXUGO|S_IWUSR|S_IFREG, &node);
+
+			if (err != 0) {
+				//Possible orphaned inode on the flash - but will be gc'd
+				return err;
+			}
+
+			err = ENOERR;
+		}
+	} else if (err == ENOERR) {
+		// The node exists. If the O_CREAT and O_EXCL bits are set, we
+		// must fail the open.
+
+		if ((mode & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL))
+			err = EEXIST;
+		else
+			node = ds.node;
+	}
+
+	if (err == ENOERR && (mode & O_TRUNC)) {
+		struct jffs2_inode_info *f = JFFS2_INODE_INFO(node);
+		struct jffs2_sb_info *c = JFFS2_SB_INFO(node->i_sb);
+		// If the O_TRUNC bit is set we must clean out the file data.
+
+		node->i_size = 0;
+		jffs2_truncate_fraglist(c, &f->fragtree, 0);
+		// Update file times
+		node->i_ctime = node->i_mtime = cyg_timestamp();
+	}
+
+	if (err != ENOERR)
+		return err;
+
+	// Check that we actually have a file here
+	if (S_ISDIR(node->i_mode))
+		return EISDIR;
+
+	node->i_count++;	// Count successful open
+
+	// Initialize the file object
+
+	file->f_flag |= mode & CYG_FILE_MODE_MASK;
+	file->f_type = CYG_FILE_TYPE_FILE;
+	file->f_ops = &jffs2_fileops;
+	file->f_offset = (mode & O_APPEND) ? node->i_size : 0;
+	file->f_data = (CYG_ADDRWORD) node;
+	file->f_xops = 0;
+
+	return ENOERR;
+}
+
+// -------------------------------------------------------------------------
+// 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"));
+
+	icache_evict((struct inode *) mte->root, (struct inode *) dir);
+
+	init_dirsearch(&ds, (struct inode *) dir, name);
+
+	err = jffs2_find(&ds);
+
+	if (err != ENOERR)
+		return err;
+
+	// Cannot unlink directories, use rmdir() instead
+	if (S_ISDIR(ds.node->i_mode))
+		return EPERM;
+
+	// Delete it from its directory
+
+	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_unlink(ds.dir, ds.node, &this);
+
+	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);
+
+			err = jffs2_mkdir(ds.dir, &this, 0, &node);
+
+			if (err != 0)
+				return ENOSPC;
+
+		}
+		// 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;
+	}
+
+	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;
+
+	D2(printf("jffs2_ops_rmdir\n"));
+
+	icache_evict((struct inode *) mte->root, (struct inode *) dir);
+
+	init_dirsearch(&ds, (struct inode *) dir, name);
+
+	err = jffs2_find(&ds);
+
+	if (err != ENOERR)
+		return err;
+
+	// Check that this is actually a directory.
+	if (!S_ISDIR(ds.node->i_mode))
+		return EPERM;
+
+	// 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_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)
+{
+	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
+
+		err = jffs2_unlink(ds2.dir, ds2.node, &this2);
+
+		if (err != 0)
+			return err;
+
+	}
+	// Now we know that there is no clashing node at the destination,
+	// make a new direntry at the destination and delete the old entry
+	// at the source.
+
+	err = jffs2_rename(ds1.dir, ds1.node, &this1, ds2.dir, &this2);
+
+	// Update directory times
+	if (err == 0)
+		ds1.dir->i_ctime =
+		    ds1.dir->i_mtime =
+		    ds2.dir->i_ctime = ds2.dir->i_mtime = cyg_timestamp();
+
+	return err;
+}
+
+// -------------------------------------------------------------------------
+// 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)
+{
+	unsigned long hash;
+	struct qstr this;
+	unsigned int c;
+	const char *hashname;
+	jffs2_dirsearch ds1, ds2;
+	int err;
+
+	D2(printf("jffs2_ops_link\n"));
+
+	// Only do hard links for now in this filesystem
+	if (type != CYG_FSLINK_HARD)
+		return EINVAL;
+
+	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);
+
+	// Don't allow links to existing objects
+	if (err == ENOERR)
+		return EEXIST;
+
+	// Allow through links to non-existing terminal objects
+	if (ds2.last && err == ENOENT)
+		ds2.node = NULL, err = ENOERR;
+
+	if (err != ENOERR)
+		return err;
+
+	// Now we know that there is no existing node at the destination,
+	// make a new direntry at the destination.
+
+	hashname = ds2.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_link(ds2.dir, ds1.node, &this);
+
+	if (err == 0)
+		ds1.node->i_ctime =
+		    ds2.dir->i_ctime = ds2.dir->i_mtime = cyg_timestamp();
+
+	return err;
+}
+
+// -------------------------------------------------------------------------
+// jffs2_opendir()
+// Open a directory for reading.
+
+static int jffs2_opendir(cyg_mtab_entry * mte, cyg_dir dir, const char *name,
+			 cyg_file * file)
+{
+	jffs2_dirsearch ds;
+	int err;
+
+	D2(printf("jffs2_opendir\n"));
+
+	icache_evict((struct inode *) mte->root, (struct inode *) dir);
+
+	init_dirsearch(&ds, (struct inode *) dir, name);
+
+	err = jffs2_find(&ds);
+
+	if (err != ENOERR)
+		return err;
+
+	// check it is really a directory.
+	if (!S_ISDIR(ds.node->i_mode))
+		return ENOTDIR;
+
+	ds.node->i_count++;	// Count successful open
+
+	// 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;
+
+	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)
+{
+	D2(printf("jffs2_chdir\n"));
+
+	if (dir_out != NULL) {
+		// This is a request to get a new directory pointer in
+		// *dir_out.
+
+		jffs2_dirsearch ds;
+		int err;
+
+		icache_evict((struct inode *) mte->root, (struct inode *) dir);
+
+		init_dirsearch(&ds, (struct inode *) dir, name);
+
+		err = jffs2_find(&ds);
+
+		if (err != ENOERR)
+			return err;
+
+		// 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.
+
+		struct inode *node = (struct inode *) dir;
+
+		// Just decrement directory reference count.
+		dec_refcnt(node);
+	}
+
+	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)
+{
+	jffs2_dirsearch ds;
+	int err;
+
+	D2(printf("jffs2_stat\n"));
+
+	icache_evict((struct inode *) mte->root, (struct inode *) dir);
+
+	init_dirsearch(&ds, (struct inode *) dir, name);
+
+	err = jffs2_find(&ds);
+
+	if (err != ENOERR)
+		return err;
+
+	// Fill in the status
+	buf->st_mode = ds.node->i_mode;
+	buf->st_ino = (ino_t) ds.node;
+	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;
+
+	return err;
+
+	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)
+{
+	jffs2_dirsearch ds;
+	int err;
+
+	D2(printf("jffs2_getinfo\n"));
+
+	icache_evict((struct inode *) mte->root, (struct inode *) dir);
+
+	init_dirsearch(&ds, (struct inode *) dir, name);
+
+	err = jffs2_find(&ds);
+
+	if (err != ENOERR)
+		return err;
+
+	switch (key) {
+	case FS_INFO_CONF:
+		err = jffs2_pathconf(ds.node, (struct cyg_pathconf_info *) buf);
+		break;
+
+	default:
+		err = EINVAL;
+	}
+	return err;
+
+	return ENOERR;
+}
+
+// -------------------------------------------------------------------------
+// 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)
+{
+	// No setinfo keys supported at present
+
+	D2(printf("jffs2_setinfo\n"));
+
+	return EINVAL;
+}
+
+//==========================================================================
+// File operations
+
+// -------------------------------------------------------------------------
+// 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();
+
+	uio->uio_resid = resid;
+	fp->f_offset = pos;
+
+	up(&f->sem);
+
+	return ENOERR;
+}
+
+// -------------------------------------------------------------------------
+// jffs2_fo_write()
+// Write data to file.
+
+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;
+
+	uio->uio_resid = resid;
+	fp->f_offset = pos;
+
+	return ENOERR;
+}
+
+// -------------------------------------------------------------------------
+// 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"));
+
+	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;
+
+	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)
+{
+	// No Ioctls currenly defined.
+
+	D2(printf("jffs2_fo_ioctl\n"));
+
+	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.
+
+	D2(printf("jffs2_fo_fsync\n"));
+
+	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;
+
+	D2(printf("jffs2_fo_close\n"));
+
+	dec_refcnt(node);
+
+	fp->f_data = 0;		// zero data pointer
+
+	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;
+
+	D2(printf("jffs2_fo_fstat\n"));
+
+	// Fill in the status
+	buf->st_mode = node->i_mode;
+	buf->st_ino = (ino_t) node;
+	buf->st_dev = 0;
+	buf->st_nlink = node->i_nlink;
+	buf->st_uid = 0;
+	buf->st_gid = 0;
+	buf->st_size = node->i_size;
+	buf->st_atime = node->i_atime;
+	buf->st_mtime = node->i_mtime;
+	buf->st_ctime = node->i_ctime;
+
+	return ENOERR;
+}
+
+// -------------------------------------------------------------------------
+// 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)
+{
+	struct inode *node = (struct inode *) fp->f_data;
+	int err;
+
+	D2(printf("jffs2_fo_getinfo\n"));
+
+	switch (key) {
+	case FS_INFO_CONF:
+		err = jffs2_pathconf(node, (struct cyg_pathconf_info *) buf);
+		break;
+
+	default:
+		err = EINVAL;
+	}
+	return err;
+
+	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)
+{
+	// No setinfo key supported at present
+
+	D2(printf("jffs2_fo_setinfo\n"));
+
+	return ENOERR;
+}
+
+//==========================================================================
+// Directory operations
+
+// -------------------------------------------------------------------------
+// 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';
+}
+
+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;
+      out_sem:
+	up(&f->sem);
+      out:
+	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
+
+	D2(printf("jffs2_fo_dirlseek\n"));
+
+	if (whence != SEEK_SET || *pos != 0)
+		return EINVAL;
+
+	*pos = fp->f_offset = 0;
+
+	return ENOERR;
+}
+
+//==========================================================================
+// 
+// Called by JFFS2
+// ===============
+// 
+//
+//==========================================================================
+
+struct page *read_cache_page(unsigned long index,
+			     int (*filler) (void *, struct page *), void *data)
+{
+	// 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));
+
+	printf("read_cache_page\n");
+	memset(&gc_buffer, 0, PAGE_CACHE_SIZE);
+
+	if (gc_page != NULL) {
+		gc_page->virtual = &gc_buffer;
+		gc_page->index = index;
+
+		err = filler(data, gc_page);
+		if (err < 0) {
+			free(gc_page);
+			gc_page = NULL;
+		}
+	}
+
+	return gc_page;
+}
+
+void page_cache_release(struct page *pg)
+{
+
+	// 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);
+}
+
+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;
+
+	inode = malloc(sizeof (struct inode));
+	if (inode == NULL)
+		return 0;
+
+	D2(printf
+	   ("malloc new_inode %x ####################################\n",
+	    inode));
+
+	memset(inode, 0, sizeof (struct inode));
+	inode->i_sb = sb;
+	inode->i_ino = 1;
+	inode->i_count = 0;	//1; // Let ecos manage the open count
+
+	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
+
+	// 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;
+}
+
+struct inode *iget(struct super_block *sb, cyg_uint32 ino)
+{
+
+	// Substitute for iget drops straight through to reading the 
+	// inode from disk if it is not in the inode cache
+
+	// Called in super.c jffs2_read_super, dir.c jffs2_lookup,
+	// and gc.c jffs2_garbage_collect_pass
+
+	// Must first check for cached inode 
+	// If this fails let new_inode create one
+
+	struct inode *inode;
+
+	D2(printf("iget\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)
+			return inode;
+	}
+	inode = NULL;
+
+	// Not cached, so malloc it
+	inode = new_inode(sb);
+	if (inode == NULL)
+		return 0;
+
+	inode->i_ino = ino;
+	jffs2_read_inode(inode);
+
+	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) {
+				cached_inode->i_cache_prev->i_cache_next = cached_inode->i_cache_next;	// Prveious 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);
+	}
+}
+
+static int return_EIO(void)
+{
+	return -EIO;
+}
+
+#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);
+}
+
+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);
+}
+
+int
+jffs2_flash_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);
+}
+
+// -------------------------------------------------------------------------
+// EOF jffs2.c
+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);
+        struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode);
+
+        D1(printk(KERN_DEBUG "jffs2_clear_inode(): ino #%lu mode %o\n", inode->i_ino, inode->i_mode));
+
+        jffs2_do_clear_inode(c, f);
+}
+
+
+/* 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 *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"));
+}
--- a/packages/fs/jffs2/current/src/gc.c
+++ b/packages/fs/jffs2/current/src/gc.c
@@ -7,21 +7,22 @@
  *
  * For licensing information, see the file 'LICENCE' in this directory.
  *
- * $Id: gc.c,v 1.60 2002/01/20 16:55:52 dwmw2 Exp $
+ * $Id: gc.c,v 1.100 2003/01/22 14:43:51 dwmw2 Exp $
  *
  */
 
-#ifndef __ECOS
 #include <linux/kernel.h>
 #include <linux/mtd/mtd.h>
 #include <linux/slab.h>
-#include <linux/sched.h>
-#include <linux/interrupt.h>
 #include <linux/pagemap.h>
-#endif
+#include <linux/crc32.h>
+#include <linux/compiler.h>
+#include <linux/stat.h>
 #include "nodelist.h"
-#include "crc32.h"
 
+static int jffs2_garbage_collect_pristine(struct jffs2_sb_info *c, 
+					  struct jffs2_inode_cache *ic,
+					  struct jffs2_raw_node_ref *raw);
 static int jffs2_garbage_collect_metadata(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, 
 					struct jffs2_inode_info *f, struct jffs2_full_dnode *fd);
 static int jffs2_garbage_collect_dirent(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, 
@@ -34,20 +35,33 @@ static int jffs2_garbage_collect_hole(st
 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);
 
 /* Called with erase_completion_lock held */
 static struct jffs2_eraseblock *jffs2_find_gc_block(struct jffs2_sb_info *c)
 {
 	struct jffs2_eraseblock *ret;
 	struct list_head *nextlist = NULL;
+	int n = jiffies % 128;
 
 	/* Pick an eraseblock to garbage collect next. This is where we'll
 	   put the clever wear-levelling algorithms. Eventually.  */
+	/* We possibly want to favour the dirtier blocks more when the
+	   number of free blocks is low. */
 	if (!list_empty(&c->bad_used_list) && c->nr_free_blocks > JFFS2_RESERVED_BLOCKS_GCBAD) {
 		D1(printk(KERN_DEBUG "Picking block from bad_used_list to GC next\n"));
 		nextlist = &c->bad_used_list;
-	} else if (jiffies % 100 && !list_empty(&c->dirty_list)) {
-		/* Most of the time, pick one off the dirty list */
+	} else if (n < 50 && !list_empty(&c->erasable_list)) {
+		/* Note that most of them will have gone directly to be erased. 
+		   So don't favour the erasable_list _too_ much. */
+		D1(printk(KERN_DEBUG "Picking block from erasable_list to GC next\n"));
+		nextlist = &c->erasable_list;
+	} else if (n < 110 && !list_empty(&c->very_dirty_list)) {
+		/* Most of the time, pick one off the very_dirty list */
+		D1(printk(KERN_DEBUG "Picking block from very_dirty_list to GC next\n"));
+		nextlist = &c->very_dirty_list;
+	} else if (n < 126 && !list_empty(&c->dirty_list)) {
 		D1(printk(KERN_DEBUG "Picking block from dirty_list to GC next\n"));
 		nextlist = &c->dirty_list;
 	} else if (!list_empty(&c->clean_list)) {
@@ -57,9 +71,16 @@ static struct jffs2_eraseblock *jffs2_fi
 		D1(printk(KERN_DEBUG "Picking block from dirty_list to GC next (clean_list was empty)\n"));
 
 		nextlist = &c->dirty_list;
+	} else if (!list_empty(&c->very_dirty_list)) {
+		D1(printk(KERN_DEBUG "Picking block from very_dirty_list to GC next (clean_list and dirty_list were empty)\n"));
+		nextlist = &c->very_dirty_list;
+	} else if (!list_empty(&c->erasable_list)) {
+		D1(printk(KERN_DEBUG "Picking block from erasable_list to GC next (clean_list and {very_,}dirty_list were empty)\n"));
+
+		nextlist = &c->erasable_list;
 	} else {
-		/* Eep. Both were empty */
-		printk(KERN_NOTICE "jffs2: No clean _or_ dirty blocks to GC from! Where are they all?\n");
+		/* Eep. All were empty */
+		printk(KERN_NOTICE "jffs2: No clean, dirty _or_ erasable blocks to GC from! Where are they all?\n");
 		return NULL;
 	}
 
@@ -71,6 +92,17 @@ static struct jffs2_eraseblock *jffs2_fi
 		printk(KERN_WARNING "Eep. ret->gc_node for block at 0x%08x is NULL\n", ret->offset);
 		BUG();
 	}
+	
+	/* Have we accidentally picked a clean block with wasted space ? */
+	if (ret->wasted_size) {
+		D1(printk(KERN_DEBUG "Converting wasted_size %08x to dirty_size\n", ret->wasted_size));
+		ret->dirty_size += ret->wasted_size;
+		c->wasted_size -= ret->wasted_size;
+		c->dirty_size += ret->wasted_size;
+		ret->wasted_size = 0;
+	}
+
+	D1(jffs2_dump_block_lists(c));
 	return ret;
 }
 
@@ -80,21 +112,90 @@ static struct jffs2_eraseblock *jffs2_fi
  */
 int jffs2_garbage_collect_pass(struct jffs2_sb_info *c)
 {
+	struct jffs2_inode_cache *ic;
 	struct jffs2_eraseblock *jeb;
-	struct jffs2_inode_info *f;
 	struct jffs2_raw_node_ref *raw;
-	struct jffs2_node_frag *frag;
-	struct jffs2_full_dnode *fn = NULL;
-	struct jffs2_full_dirent *fd;
-	uint32_t start = 0, end = 0, nrfrags = 0;
 	uint32_t inum;
-	struct inode *inode;
 	int ret = 0;
 
 	if (down_interruptible(&c->alloc_sem))
 		return -EINTR;
 
-	spin_lock_bh(&c->erase_completion_lock);
+	for (;;) {
+		struct jffs2_inode_cache *ic;
+
+		spin_lock(&c->erase_completion_lock);
+		if (!c->unchecked_size)
+			break;
+
+		/* We can't start doing GC yet. We haven't finished checking
+		   the node CRCs etc. Do it now. */
+		
+		/* checked_ino is protected by the alloc_sem */
+		if (c->checked_ino > c->highest_ino) {
+			printk(KERN_CRIT "Checked all inodes but still 0x%x bytes of unchecked space?\n",
+			       c->unchecked_size);
+			D1(jffs2_dump_block_lists(c));
+			spin_unlock(&c->erase_completion_lock);
+			BUG();
+		}
+
+		spin_unlock(&c->erase_completion_lock);
+
+		spin_lock(&c->inocache_lock);
+
+		ic = jffs2_get_ino_cache(c, c->checked_ino++);
+
+		if (!ic) {
+			spin_unlock(&c->inocache_lock);
+			continue;
+		}
+
+		if (!ic->nlink) {
+			D1(printk(KERN_DEBUG "Skipping check of ino #%d with nlink zero\n",
+				  ic->ino));
+			spin_unlock(&c->inocache_lock);
+			continue;
+		}
+		switch(ic->state) {
+		case INO_STATE_CHECKEDABSENT:
+		case INO_STATE_PRESENT:
+			D1(printk(KERN_DEBUG "Skipping ino #%u already checked\n", ic->ino));
+			spin_unlock(&c->inocache_lock);
+			continue;
+
+		case INO_STATE_GC:
+		case INO_STATE_CHECKING:
+			printk(KERN_WARNING "Inode #%u is in state %d during CRC check phase!\n", ic->ino, ic->state);
+			spin_unlock(&c->inocache_lock);
+			BUG();
+
+		case INO_STATE_READING:
+			/* We need to wait for it to finish, lest we move on
+			   and trigger the BUG() above while we haven't yet 
+			   finished checking all its nodes */
+			D1(printk(KERN_DEBUG "Waiting for ino #%u to finish reading\n", ic->ino));
+			up(&c->alloc_sem);
+			sleep_on_spinunlock(&c->inocache_wq, &c->inocache_lock);
+			return 0;
+
+		default:
+			BUG();
+
+		case INO_STATE_UNCHECKED:
+			;
+		}
+		ic->state = INO_STATE_CHECKING;
+		spin_unlock(&c->inocache_lock);
+
+		D1(printk(KERN_DEBUG "jffs2_garbage_collect_pass() triggering inode scan of ino#%d\n", ic->ino));
+
+		ret = jffs2_do_crccheck_inode(c, ic);
+		if (!ret)
+			jffs2_set_inocache_state(c, ic, INO_STATE_CHECKEDABSENT);
+		up(&c->alloc_sem);
+		return ret;
+	}
 
 	/* First, work out which block we're garbage-collecting */
 	jeb = c->gcblock;
@@ -104,51 +205,181 @@ int jffs2_garbage_collect_pass(struct jf
 
 	if (!jeb) {
 		printk(KERN_NOTICE "jffs2: Couldn't find erase block to garbage collect!\n");
-		spin_unlock_bh(&c->erase_completion_lock);
+		spin_unlock(&c->erase_completion_lock);
 		up(&c->alloc_sem);
 		return -EIO;
 	}
 
-	D1(printk(KERN_DEBUG "garbage collect from block at phys 0x%08x\n", jeb->offset));
+	D1(printk(KERN_DEBUG "GC from block %08x, used_size %08x, dirty_size %08x, free_size %08x\n", jeb->offset, jeb->used_size, jeb->dirty_size, jeb->free_size));
+	D1(if (c->nextblock)
+	   printk(KERN_DEBUG "Nextblock at  %08x, used_size %08x, dirty_size %08x, wasted_size %08x, free_size %08x\n", c->nextblock->offset, c->nextblock->used_size, c->nextblock->dirty_size, c->nextblock->wasted_size, c->nextblock->free_size));
 
-	if (!jeb->used_size)
+	if (!jeb->used_size) {
+		up(&c->alloc_sem);
 		goto eraseit;
+	}
 
 	raw = jeb->gc_node;
 			
-	while(raw->flash_offset & 1) {
-		D1(printk(KERN_DEBUG "Node at 0x%08x is obsolete... skipping\n", raw->flash_offset &~3));
+	while(ref_obsolete(raw)) {
+		D1(printk(KERN_DEBUG "Node at 0x%08x is obsolete... skipping\n", ref_offset(raw)));
 		jeb->gc_node = raw = raw->next_phys;
 		if (!raw) {
 			printk(KERN_WARNING "eep. End of raw list while still supposedly nodes to GC\n");
 			printk(KERN_WARNING "erase block at 0x%08x. free_size 0x%08x, dirty_size 0x%08x, used_size 0x%08x\n", 
 			       jeb->offset, jeb->free_size, jeb->dirty_size, jeb->used_size);
-			spin_unlock_bh(&c->erase_completion_lock);
+			spin_unlock(&c->erase_completion_lock);
 			up(&c->alloc_sem);
 			BUG();
 		}
 	}
-	D1(printk(KERN_DEBUG "Going to garbage collect node at 0x%08x\n", raw->flash_offset &~3));
+	D1(printk(KERN_DEBUG "Going to garbage collect node at 0x%08x\n", ref_offset(raw)));
 	if (!raw->next_in_ino) {
 		/* Inode-less node. Clean marker, snapshot or something like that */
-		spin_unlock_bh(&c->erase_completion_lock);
+		/* FIXME: If it's something that needs to be copied, including something
+		   we don't grok that has JFFS2_NODETYPE_RWCOMPAT_COPY, we should do so */
+		spin_unlock(&c->erase_completion_lock);
 		jffs2_mark_node_obsolete(c, raw);
+		up(&c->alloc_sem);
 		goto eraseit_lock;
 	}
 						     
 	inum = jffs2_raw_ref_to_inum(raw);
 	D1(printk(KERN_DEBUG "Inode number is #%u\n", inum));
 
-	spin_unlock_bh(&c->erase_completion_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), inum));
+
+	/* Three possibilities:
+	   1. Inode is already in-core. We must iget it and do proper
+	      updating to its fragtree, etc.
+	   2. Inode is not in-core, node is REF_PRISTINE. We lock the
+	      inocache to prevent a read_inode(), copy the node intact.
+	   3. Inode is not in-core, node is not pristine. We must iget()
+	      and take the slow path.
+	*/
+	spin_lock(&c->inocache_lock);
+	ic = jffs2_get_ino_cache(c, inum);
+
+	/* This should never fail unless I'm particularly stupid.
+	   So we don't check before dereferencing it */
+
+	switch(ic->state) {
+	case INO_STATE_CHECKEDABSENT:
+		/* It's been checked, but it's not currently in-core. 
+		   We can just copy any pristine nodes, but have
+		   to prevent anyone else from doing read_inode() while
+		   we're at it, so we set the state accordingly */
+		if (ref_flags(raw) == REF_PRISTINE)
+			ic->state = INO_STATE_GC;
+		else {
+			D1(printk("Ino #%u is absent but node not REF_PRISTINE. Reading.\n", 
+				  inum));
+		}
+		break;
 
-	D1(printk(KERN_DEBUG "jffs2_garbage_collect_pass collecting from block @0x%08x. Node @0x%08x, ino #%u\n", jeb->offset, raw->flash_offset&~3, inum));
+	case INO_STATE_PRESENT:
+	case INO_STATE_UNCHECKED:
+		/* It's in-core or hasn't been checked. GC must iget() it. */
+		break;
+
+	case INO_STATE_CHECKING:
+		/* Should never happen. We should have finished checking
+		   by the time we actually start doing any GC. */
+		BUG();
+
+	
+	case INO_STATE_GC:
+		/* Should never happen. We are holding the alloc_sem, 
+		   no other garbage collection can happen. Note that we
+		   do depend on this later when deciding to do a simple
+		   node copy */
+		BUG();
+			 
+	case INO_STATE_READING:
+		/* Someone's currently trying to read it. We must wait for
+		   them to finish and then go through the full iget() route
+		   to do the GC. However, sometimes read_inode() needs to get
+		   the alloc_sem() (for marking nodes invalid) so we must
+		   drop the alloc_sem before sleeping. */
+
+		up(&c->alloc_sem);
+		D1(printk(KERN_DEBUG "jffs2_garbage_collect_pass() waiting for ino #%u in state %d\n",
+			  inum, ic->state));
+		sleep_on_spinunlock(&c->inocache_wq, &c->inocache_lock);
+		/* And because we dropped the alloc_sem we must start again from the 
+		   beginning. Ponder chance of livelock here -- we're returning success
+		   without actually making any progress.
 
-	inode = iget(OFNI_BS_2SFFJ(c), inum);
+		   Q: What are the chances that the inode is back in INO_STATE_READING 
+		   again by the time we next enter this function? And that this happens
+		   enough times to cause a real delay?
+
+		   A: Small enough that I don't care :) 
+		*/
+		return 0;
+
+	}
+
+	spin_unlock(&c->inocache_lock);
+
+	/* OK. Now if the inode is in state INO_STATE_GC, we are going to copy the
+	   node intact, and we don't have to muck about with the fragtree etc. 
+	   because we know it's not in-core. If it _was_ in-core, we go through
+	   all the iget() crap anyway */
+
+	if (ic->state == INO_STATE_GC) {
+		ret = jffs2_garbage_collect_pristine(c, ic, raw);
+		jffs2_set_inocache_state(c, ic, INO_STATE_CHECKEDABSENT);
+
+		if (ret != -EBADFD)
+			goto release_sem;
+
+		/* Fall through if it wanted us to */
+	}
+
+	ret = jffs2_garbage_collect_live(c, jeb, raw, ic);
+
+ release_sem:
+	up(&c->alloc_sem);
+
+ eraseit_lock:
+	/* If we've finished this block, start it erasing */
+	spin_lock(&c->erase_completion_lock);
+
+ eraseit:
+	if (c->gcblock && !c->gcblock->used_size) {
+		D1(printk(KERN_DEBUG "Block at 0x%08x completely obsoleted by GC. Moving to erase_pending_list\n", c->gcblock->offset));
+		/* We're GC'ing an empty block? */
+		list_add_tail(&c->gcblock->list, &c->erase_pending_list);
+		c->gcblock = NULL;
+		c->nr_erasing_blocks++;
+		jffs2_erase_pending_trigger(c);
+	}
+	spin_unlock(&c->erase_completion_lock);
+
+	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_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;
+
+	inode = iget(OFNI_BS_2SFFJ(c), ic->ino);
 	if (is_bad_inode(inode)) {
-		printk(KERN_NOTICE "Eep. read_inode() failed for ino #%u\n", inum);
+		printk(KERN_NOTICE "Eep. read_inode() failed for ino #%u\n", ic->ino);
 		/* NB. This will happen again. We need to do something appropriate here. */
+		up(&c->alloc_sem);
 		iput(inode);
-		up(&c->alloc_sem);
 		return -EIO;
 	}
 
@@ -158,7 +389,7 @@ int jffs2_garbage_collect_pass(struct jf
 	/* Now we have the lock for this inode. Check that it's still the one at the head
 	   of the list. */
 
-	if (raw->flash_offset & 1) {
+	if (ref_obsolete(raw)) {
 		D1(printk(KERN_DEBUG "node to be GC'd was obsoleted in the meantime.\n"));
 		/* They'll call again */
 		goto upnout;
@@ -169,11 +400,36 @@ int jffs2_garbage_collect_pass(struct jf
 		ret = jffs2_garbage_collect_metadata(c, jeb, f, fn);
 		goto upnout;
 	}
-	
-	for (frag = f->fraglist; frag; frag = frag->next) {
+
+	/* FIXME. Read node and do lookup? */
+	for (frag = frag_first(&f->fragtree); frag; frag = frag_next(frag)) {
 		if (frag->node && frag->node->raw == raw) {
 			fn = frag->node;
 			end = frag->ofs + frag->size;
+#if 1 /* Temporary debugging sanity checks, till we're ready to _trust_ the REF_PRISTINE flag stuff */ 
+			if (!nrfrags && ref_flags(fn->raw) == REF_PRISTINE) {
+				if (fn->frags > 1) {
+					printk(KERN_WARNING "REF_PRISTINE node at 0x%08x had %d frags. Tell dwmw2\n", ref_offset(raw), fn->frags);
+					mark_ref_normal(raw);
+				}
+				/* A hole node which isn't multi-page should be garbage-collected
+				   and merged anyway, so we just check for the frag size here,
+				   rather than mucking around with actually reading the node
+				   and checking the compression type, which is the real way
+				   to tell a hole node. */
+				if (frag->ofs & (PAGE_CACHE_SIZE-1) && frag_prev(frag) && frag_prev(frag)->size < PAGE_CACHE_SIZE) {
+					printk(KERN_WARNING "REF_PRISTINE node at 0x%08x had a previous non-hole frag in the same page. Tell dwmw2\n",
+					       ref_offset(raw));
+					mark_ref_normal(raw);
+				}
+
+				if ((frag->ofs+frag->size) & (PAGE_CACHE_SIZE-1) && frag_next(frag) && frag_next(frag)->size < PAGE_CACHE_SIZE) {
+					printk(KERN_WARNING "REF_PRISTINE node at 0x%08x (%08x-%08x) had a following non-hole frag in the same page. Tell dwmw2\n",
+					       ref_offset(raw), frag->ofs, frag->ofs+frag->size);
+					mark_ref_normal(raw);
+				}
+			}
+#endif
 			if (!nrfrags++)
 				start = frag->ofs;
 			if (nrfrags == frag->node->frags)
@@ -181,6 +437,15 @@ int jffs2_garbage_collect_pass(struct jf
 		}
 	}
 	if (fn) {
+		if (ref_flags(raw) == REF_PRISTINE) {
+			ret = jffs2_garbage_collect_pristine(c, ic, raw);
+			if (!ret) {
+				/* Urgh. Return it sensibly. */
+				frag->node->raw = ic->nodes;
+			}	
+			if (ret != -EBADFD)
+				goto upnout;
+		}
 		/* We found a datanode. Do the GC */
 		if((start >> PAGE_CACHE_SHIFT) < ((end-1) >> PAGE_CACHE_SHIFT)) {
 			/* It crosses a page boundary. Therefore, it must be a hole. */
@@ -204,8 +469,8 @@ int jffs2_garbage_collect_pass(struct jf
 		ret = jffs2_garbage_collect_deletion_dirent(c, jeb, f, fd);
 	} else {
 		printk(KERN_WARNING "Raw node at 0x%08x wasn't in node lists for ino #%u\n",
-		       raw->flash_offset&~3, f->inocache->ino);
-		if (raw->flash_offset & 1) {
+		       ref_offset(raw), f->inocache->ino);
+		if (ref_obsolete(raw)) {
 			printk(KERN_WARNING "But it's obsolete so we don't mind too much\n");
 		} else {
 			ret = -EIO;
@@ -215,23 +480,143 @@ int jffs2_garbage_collect_pass(struct jf
 	up(&f->sem);
 	iput(inode);
 
- eraseit_lock:
-	/* If we've finished this block, start it erasing */
-	spin_lock_bh(&c->erase_completion_lock);
+	return ret;
+}
+
+static int jffs2_garbage_collect_pristine(struct jffs2_sb_info *c, 
+					  struct jffs2_inode_cache *ic,
+					  struct jffs2_raw_node_ref *raw)
+{
+	union jffs2_node_union *node;
+	struct jffs2_raw_node_ref *nraw;
+	size_t retlen;
+	int ret;
+	uint32_t phys_ofs, alloclen;
+	uint32_t crc;
+
+	D1(printk(KERN_DEBUG "Going to GC REF_PRISTINE node at 0x%08x\n", ref_offset(raw)));
+
+	/* Ask for a small amount of space (or the totlen if smaller) because we
+	   don't want to force wastage of the end of a block if splitting would
+	   work. */
+	ret = jffs2_reserve_space_gc(c, min_t(uint32_t, sizeof(struct jffs2_raw_inode) + JFFS2_MIN_DATA_LEN, raw->totlen),
+				     &phys_ofs, &alloclen);
+	if (ret)
+		return ret;
+
+	if (alloclen < raw->totlen) {
+		/* Doesn't fit untouched. We'll go the old route and split it */
+		return -EBADFD;
+	}
+
+	node = kmalloc(raw->totlen, GFP_KERNEL);
+	if (!node)
+               return -ENOMEM;
+
+	ret = jffs2_flash_read(c, ref_offset(raw), raw->totlen, &retlen, (char *)node);
+	if (!ret && retlen != raw->totlen)
+		ret = -EIO;
+	if (ret)
+		goto out_node;
+
+	crc = crc32(0, node, sizeof(struct jffs2_unknown_node)-4);
+	if (je32_to_cpu(node->u.hdr_crc) != crc) {
+		printk(KERN_WARNING "Header CRC failed on REF_PRISTINE node at 0x%08x: Read 0x%08x, calculated 0x%08x\n",
+		       ref_offset(raw), je32_to_cpu(node->u.hdr_crc), crc);
+		goto bail;
+	}
+
+	switch(je16_to_cpu(node->u.nodetype)) {
+	case JFFS2_NODETYPE_INODE:
+		crc = crc32(0, node, sizeof(node->i)-8);
+		if (je32_to_cpu(node->i.node_crc) != crc) {
+			printk(KERN_WARNING "Node CRC failed on REF_PRISTINE data node at 0x%08x: Read 0x%08x, calculated 0x%08x\n",
+			       ref_offset(raw), je32_to_cpu(node->i.node_crc), crc);
+			goto bail;
+		}
+
+		if (je32_to_cpu(node->i.dsize)) {
+			crc = crc32(0, node->i.data, je32_to_cpu(node->i.csize));
+			if (je32_to_cpu(node->i.data_crc) != crc) {
+				printk(KERN_WARNING "Data CRC failed on REF_PRISTINE data node at 0x%08x: Read 0x%08x, calculated 0x%08x\n",
+				       ref_offset(raw), je32_to_cpu(node->i.data_crc), crc);
+				goto bail;
+			}
+		}
+		break;
 
- eraseit:
-	if (c->gcblock && !c->gcblock->used_size) {
-		D1(printk(KERN_DEBUG "Block at 0x%08x completely obsoleted by GC. Moving to erase_pending_list\n", c->gcblock->offset));
-		/* We're GC'ing an empty block? */
-		list_add_tail(&c->gcblock->list, &c->erase_pending_list);
-		c->gcblock = NULL;
-		c->nr_erasing_blocks++;
-		jffs2_erase_pending_trigger(c);
+	case JFFS2_NODETYPE_DIRENT:
+		crc = crc32(0, node, sizeof(node->d)-8);
+		if (je32_to_cpu(node->d.node_crc) != crc) {
+			printk(KERN_WARNING "Node CRC failed on REF_PRISTINE dirent node at 0x%08x: Read 0x%08x, calculated 0x%08x\n",
+			       ref_offset(raw), je32_to_cpu(node->d.node_crc), crc);
+			goto bail;
+		}
+
+		if (node->d.nsize) {
+			crc = crc32(0, node->d.name, node->d.nsize);
+			if (je32_to_cpu(node->d.name_crc) != crc) {
+				printk(KERN_WARNING "Name CRC failed on REF_PRISTINE dirent ode at 0x%08x: Read 0x%08x, calculated 0x%08x\n",
+				       ref_offset(raw), je32_to_cpu(node->d.name_crc), crc);
+				goto bail;
+			}
+		}
+		break;
+	default:
+		printk(KERN_WARNING "Unknown node type for REF_PRISTINE node at 0x%08x: 0x%04x\n", 
+		       ref_offset(raw), je16_to_cpu(node->u.nodetype));
+		goto bail;
+	}
+
+	nraw = jffs2_alloc_raw_node_ref();
+	if (!nraw) {
+		ret = -ENOMEM;
+		goto out_node;
 	}
-	spin_unlock_bh(&c->erase_completion_lock);
-	up(&c->alloc_sem);
+	nraw->flash_offset = phys_ofs;
+	nraw->totlen = raw->totlen;
+	nraw->next_phys = NULL;
+
+	/* OK, all the CRCs are good; this node can just be copied as-is. */
+
+	ret = jffs2_flash_write(c, phys_ofs, raw->totlen, &retlen, (char *)node);
+	if (ret || (retlen != raw->totlen)) {
+		printk(KERN_NOTICE "Write of %d bytes at 0x%08x failed. returned %d, retlen %zd\n",
+                       raw->totlen, phys_ofs, ret, retlen);
+		if (retlen) {
+                        /* Doesn't belong to any inode */
+			nraw->next_in_ino = NULL;
 
+			nraw->flash_offset |= REF_OBSOLETE;
+			jffs2_add_physical_node_ref(c, nraw);
+			jffs2_mark_node_obsolete(c, nraw);
+		} else {
+			printk(KERN_NOTICE "Not marking the space at 0x%08x as dirty because the flash driver returned retlen zero\n", nraw->flash_offset);
+                        jffs2_free_raw_node_ref(raw);
+		}
+		if (!ret)
+			ret = -EIO;
+		goto out_node;
+	}
+	nraw->flash_offset |= REF_PRISTINE;
+	jffs2_add_physical_node_ref(c, nraw);
+
+	/* Link into per-inode list. This is safe because of the ic
+	   state being INO_STATE_GC. Note that if we're doing this
+	   for an inode which is in-code, the 'nraw' pointer is then
+	   going to be fetched from ic->nodes by our caller. */
+        nraw->next_in_ino = ic->nodes;
+        ic->nodes = nraw;
+
+	jffs2_mark_node_obsolete(c, raw);
+	D1(printk(KERN_DEBUG "WHEEE! GC REF_PRISTINE node at 0x%08x succeeded\n", ref_offset(raw)));
+
+ out_node:
+	kfree(node);
 	return ret;
+ bail:
+	ret = -EBADFD;
+	goto out_node;
 }
 
 static int jffs2_garbage_collect_metadata(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, 
@@ -244,8 +629,8 @@ static int jffs2_garbage_collect_metadat
 	uint32_t alloclen, phys_ofs;
 	int ret;
 
-	if ((JFFS2_F_I_MODE(f) & S_IFMT) == S_IFBLK ||
-	    (JFFS2_F_I_MODE(f) & S_IFMT) == S_IFCHR) {
+	if (S_ISBLK(JFFS2_F_I_MODE(f)) ||
+	    S_ISCHR(JFFS2_F_I_MODE(f)) ) {
 		/* For these, we don't actually need to read the old node */
 		/* FIXME: for minor or major > 255. */
 		dev =  ((JFFS2_F_I_RDEV_MAJ(f) << 8) | 
@@ -253,7 +638,7 @@ static int jffs2_garbage_collect_metadat
 		mdata = (char *)&dev;
 		mdatalen = sizeof(dev);
 		D1(printk(KERN_DEBUG "jffs2_garbage_collect_metadata(): Writing %d bytes of kdev_t\n", mdatalen));
-	} else if ((JFFS2_F_I_MODE(f) & S_IFMT) == S_IFLNK) {
+	} else if (S_ISLNK(JFFS2_F_I_MODE(f))) {
 		mdatalen = fn->size;
 		mdata = kmalloc(fn->size, GFP_KERNEL);
 		if (!mdata) {
@@ -272,32 +657,32 @@ static int jffs2_garbage_collect_metadat
 	
 	ret = jffs2_reserve_space_gc(c, sizeof(ri) + mdatalen, &phys_ofs, &alloclen);
 	if (ret) {
-		printk(KERN_WARNING "jffs2_reserve_space_gc of %d bytes for garbage_collect_metadata failed: %d\n",
+		printk(KERN_WARNING "jffs2_reserve_space_gc of %zd bytes for garbage_collect_metadata failed: %d\n",
 		       sizeof(ri)+ mdatalen, ret);
 		goto out;
 	}
 	
 	memset(&ri, 0, sizeof(ri));
-	ri.magic = JFFS2_MAGIC_BITMASK;
-	ri.nodetype = JFFS2_NODETYPE_INODE;
-	ri.totlen = sizeof(ri) + mdatalen;
-	ri.hdr_crc = crc32(0, &ri, sizeof(struct jffs2_unknown_node)-4);
+	ri.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK);
+	ri.nodetype = cpu_to_je16(JFFS2_NODETYPE_INODE);
+	ri.totlen = cpu_to_je32(sizeof(ri) + mdatalen);
+	ri.hdr_crc = cpu_to_je32(crc32(0, &ri, sizeof(struct jffs2_unknown_node)-4));
 
-	ri.ino = f->inocache->ino;
-	ri.version = ++f->highest_version;
-	ri.mode = JFFS2_F_I_MODE(f);
-	ri.uid = JFFS2_F_I_UID(f);
-	ri.gid = JFFS2_F_I_GID(f);
-	ri.isize = JFFS2_F_I_SIZE(f);
-	ri.atime = JFFS2_F_I_ATIME(f);
-	ri.ctime = JFFS2_F_I_CTIME(f);
-	ri.mtime = JFFS2_F_I_MTIME(f);
-	ri.offset = 0;
-	ri.csize = mdatalen;
-	ri.dsize = mdatalen;
+	ri.ino = cpu_to_je32(f->inocache->ino);
+	ri.version = cpu_to_je32(++f->highest_version);
+	ri.mode = cpu_to_jemode(JFFS2_F_I_MODE(f));
+	ri.uid = cpu_to_je16(JFFS2_F_I_UID(f));
+	ri.gid = cpu_to_je16(JFFS2_F_I_GID(f));
+	ri.isize = cpu_to_je32(JFFS2_F_I_SIZE(f));
+	ri.atime = cpu_to_je32(JFFS2_F_I_ATIME(f));
+	ri.ctime = cpu_to_je32(JFFS2_F_I_CTIME(f));
+	ri.mtime = cpu_to_je32(JFFS2_F_I_MTIME(f));
+	ri.offset = cpu_to_je32(0);
+	ri.csize = cpu_to_je32(mdatalen);
+	ri.dsize = cpu_to_je32(mdatalen);
 	ri.compr = JFFS2_COMPR_NONE;
-	ri.node_crc = crc32(0, &ri, sizeof(ri)-8);
-	ri.data_crc = crc32(0, mdata, mdatalen);
+	ri.node_crc = cpu_to_je32(crc32(0, &ri, sizeof(ri)-8));
+	ri.data_crc = cpu_to_je32(crc32(0, mdata, mdatalen));
 
 	new_fn = jffs2_write_dnode(c, f, &ri, mdata, mdatalen, phys_ofs, NULL);
 
@@ -310,7 +695,7 @@ static int jffs2_garbage_collect_metadat
 	jffs2_free_full_dnode(fn);
 	f->metadata = new_fn;
  out:
-	if ((JFFS2_F_I_MODE(f) & S_IFMT) == S_IFLNK)
+	if (S_ISLNK(JFFS2_F_I_MODE(f)))
 		kfree(mdata);
 	return ret;
 }
@@ -323,23 +708,23 @@ static int jffs2_garbage_collect_dirent(
 	uint32_t alloclen, phys_ofs;
 	int ret;
 
-	rd.magic = JFFS2_MAGIC_BITMASK;
-	rd.nodetype = JFFS2_NODETYPE_DIRENT;
+	rd.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK);
+	rd.nodetype = cpu_to_je16(JFFS2_NODETYPE_DIRENT);
 	rd.nsize = strlen(fd->name);
-	rd.totlen = sizeof(rd) + rd.nsize;
-	rd.hdr_crc = crc32(0, &rd, sizeof(struct jffs2_unknown_node)-4);
+	rd.totlen = cpu_to_je32(sizeof(rd) + rd.nsize);
+	rd.hdr_crc = cpu_to_je32(crc32(0, &rd, sizeof(struct jffs2_unknown_node)-4));
 
-	rd.pino = f->inocache->ino;
-	rd.version = ++f->highest_version;
-	rd.ino = fd->ino;
-	rd.mctime = max(JFFS2_F_I_MTIME(f), JFFS2_F_I_CTIME(f));
+	rd.pino = cpu_to_je32(f->inocache->ino);
+	rd.version = cpu_to_je32(++f->highest_version);
+	rd.ino = cpu_to_je32(fd->ino);
+	rd.mctime = cpu_to_je32(max(JFFS2_F_I_MTIME(f), JFFS2_F_I_CTIME(f)));
 	rd.type = fd->type;
-	rd.node_crc = crc32(0, &rd, sizeof(rd)-8);
-	rd.name_crc = crc32(0, fd->name, rd.nsize);
+	rd.node_crc = cpu_to_je32(crc32(0, &rd, sizeof(rd)-8));
+	rd.name_crc = cpu_to_je32(crc32(0, fd->name, rd.nsize));
 	
 	ret = jffs2_reserve_space_gc(c, sizeof(rd)+rd.nsize, &phys_ofs, &alloclen);
 	if (ret) {
-		printk(KERN_WARNING "jffs2_reserve_space_gc of %d bytes for garbage_collect_dirent failed: %d\n",
+		printk(KERN_WARNING "jffs2_reserve_space_gc of %zd bytes for garbage_collect_dirent failed: %d\n",
 		       sizeof(rd)+rd.nsize, ret);
 		return ret;
 	}
@@ -373,9 +758,14 @@ static int jffs2_garbage_collect_deletio
 		uint32_t name_crc = crc32(0, fd->name, name_len);
 		char *namebuf = NULL;
 
+		/* Prevent the erase code from nicking the obsolete node refs while
+		   we're looking at them. I really don't like this extra lock but
+		   can't see any alternative. Suggestions on a postcard to... */
+		down(&c->erase_free_sem);
+
 		for (raw = f->inocache->nodes; raw != (void *)f->inocache; raw = raw->next_in_ino) {
 			/* We only care about obsolete ones */
-			if (!(raw->flash_offset & 1))
+			if (!(ref_obsolete(raw)))
 				continue;
 
 			/* Doesn't matter if there's one in the same erase block. We're going to 
@@ -385,58 +775,60 @@ static int jffs2_garbage_collect_deletio
 				continue;
 
 			/* This is an obsolete node belonging to the same directory */
-			ret = jffs2_flash_read(c, raw->flash_offset & ~3, sizeof(struct jffs2_unknown_node), &retlen, (char *)&rd);
+			ret = jffs2_flash_read(c, ref_offset(raw), sizeof(struct jffs2_unknown_node), &retlen, (char *)&rd);
 			if (ret) {
-				printk(KERN_WARNING "jffs2_g_c_deletion_dirent(): Read error (%d) reading header from obsolete node at %08x\n", ret, raw->flash_offset & ~3);
-				/* If we can't read it, we don't need to continune to obsolete it. Continue */
+				printk(KERN_WARNING "jffs2_g_c_deletion_dirent(): Read error (%d) reading header from obsolete node at %08x\n", ret, ref_offset(raw));
+				/* If we can't read it, we don't need to continue to obsolete it. Continue */
 				continue;
 			}
 			if (retlen != sizeof(struct jffs2_unknown_node)) {
-				printk(KERN_WARNING "jffs2_g_c_deletion_dirent(): Short read (%d not %d) reading header from obsolete node at %08x\n",
-				       retlen, sizeof(struct jffs2_unknown_node), raw->flash_offset & ~3);
+				printk(KERN_WARNING "jffs2_g_c_deletion_dirent(): Short read (%zd not %zd) reading header from obsolete node at %08x\n",
+				       retlen, sizeof(struct jffs2_unknown_node), ref_offset(raw));
 				continue;
 			}
-			if (rd.nodetype != JFFS2_NODETYPE_DIRENT ||
-			    PAD(rd.totlen) != PAD(sizeof(rd) + name_len))
+			if (je16_to_cpu(rd.nodetype) != JFFS2_NODETYPE_DIRENT ||
+			    PAD(je32_to_cpu(rd.totlen)) != PAD(sizeof(rd) + name_len))
 				continue;
 
 			/* OK, it's a dirent node, it's the right length. We have to take a 
 			   closer look at it... */
-			ret = jffs2_flash_read(c, raw->flash_offset & ~3, sizeof(rd), &retlen, (char *)&rd);
+			ret = jffs2_flash_read(c, ref_offset(raw), sizeof(rd), &retlen, (char *)&rd);
 			if (ret) {
-				printk(KERN_WARNING "jffs2_g_c_deletion_dirent(): Read error (%d) reading from obsolete node at %08x\n", ret, raw->flash_offset & ~3);
+				printk(KERN_WARNING "jffs2_g_c_deletion_dirent(): Read error (%d) reading from obsolete node at %08x\n", ret, ref_offset(raw));
 				/* If we can't read it, we don't need to continune to obsolete it. Continue */
 				continue;
 			}
-			if (retlen != sizeof(struct jffs2_unknown_node)) {
-				printk(KERN_WARNING "jffs2_g_c_deletion_dirent(): Short read (%d not %d) reading from obsolete node at %08x\n",
-				       retlen, sizeof(struct jffs2_unknown_node), raw->flash_offset & ~3);
+			if (retlen != sizeof(rd)) {
+				printk(KERN_WARNING "jffs2_g_c_deletion_dirent(): Short read (%zd not %zd) reading from obsolete node at %08x\n",
+				       retlen, sizeof(rd), ref_offset(raw));
 				continue;
 			}
 
 			/* If the name CRC doesn't match, skip */
-			if (rd.name_crc != name_crc)
+			if (je32_to_cpu(rd.name_crc) != name_crc)
 				continue;
 			/* If the name length doesn't match, or it's another deletion dirent, skip */
-			if (rd.nsize != name_len || !rd.ino)
+			if (rd.nsize != name_len || !je32_to_cpu(rd.ino))
 				continue;
 
 			/* OK, check the actual name now */
 			if (!namebuf) {
 				namebuf = kmalloc(name_len + 1, GFP_KERNEL);
-				if (!namebuf)
+				if (!namebuf) {
+					up(&c->erase_free_sem);
 					return -ENOMEM;
+				}
 			}
 			/* We read the extra byte before it so it's a word-aligned read */
-			ret = jffs2_flash_read(c, (raw->flash_offset & ~3)+sizeof(rd)-1, name_len+1, &retlen, namebuf);
+			ret = jffs2_flash_read(c, (ref_offset(raw))+sizeof(rd)-1, name_len+1, &retlen, namebuf);
 			if (ret) {
-				printk(KERN_WARNING "jffs2_g_c_deletion_dirent(): Read error (%d) reading name from obsolete node at %08x\n", ret, raw->flash_offset & ~3);
+				printk(KERN_WARNING "jffs2_g_c_deletion_dirent(): Read error (%d) reading name from obsolete node at %08x\n", ret, ref_offset(raw));
 				/* If we can't read it, we don't need to continune to obsolete it. Continue */
 				continue;
 			}
-			if (retlen != sizeof(rd)) {
-				printk(KERN_WARNING "jffs2_g_c_deletion_dirent(): Short read (%d not %d) reading name from obsolete node at %08x\n",
-				       retlen, name_len, raw->flash_offset & ~3);
+			if (retlen != name_len+1) {
+				printk(KERN_WARNING "jffs2_g_c_deletion_dirent(): Short read (%zd not %d) reading name from obsolete node at %08x\n",
+				       retlen, name_len+1, ref_offset(raw));
 				continue;
 			}
 			if (memcmp(namebuf+1, fd->name, name_len))
@@ -449,8 +841,12 @@ static int jffs2_garbage_collect_deletio
 			if (namebuf)
 				kfree(namebuf);
 
+			up(&c->erase_free_sem);
 			return jffs2_garbage_collect_dirent(c, jeb, f, fd);
 		}
+
+		up(&c->erase_free_sem);
+
 		if (namebuf) 
 			kfree(namebuf);
 	}
@@ -492,63 +888,66 @@ static int jffs2_garbage_collect_hole(st
 		uint32_t crc;
 		/* It's partially obsoleted by a later write. So we have to 
 		   write it out again with the _same_ version as before */
-		ret = jffs2_flash_read(c, fn->raw->flash_offset & ~3, sizeof(ri), &readlen, (char *)&ri);
+		ret = jffs2_flash_read(c, ref_offset(fn->raw), sizeof(ri), &readlen, (char *)&ri);
 		if (readlen != sizeof(ri) || ret) {
-			printk(KERN_WARNING "Node read failed in jffs2_garbage_collect_hole. Ret %d, retlen %d. Data will be lost by writing new hold node\n", ret, readlen);
+			printk(KERN_WARNING "Node read failed in jffs2_garbage_collect_hole. Ret %d, retlen %zd. Data will be lost by writing new hole node\n", ret, readlen);
 			goto fill;
 		}
-		if (ri.nodetype != JFFS2_NODETYPE_INODE) {
+		if (je16_to_cpu(ri.nodetype) != JFFS2_NODETYPE_INODE) {
 			printk(KERN_WARNING "jffs2_garbage_collect_hole: Node at 0x%08x had node type 0x%04x instead of JFFS2_NODETYPE_INODE(0x%04x)\n",
-			       fn->raw->flash_offset & ~3, ri.nodetype, JFFS2_NODETYPE_INODE);
+			       ref_offset(fn->raw),
+			       je16_to_cpu(ri.nodetype), JFFS2_NODETYPE_INODE);
 			return -EIO;
 		}
-		if (ri.totlen != sizeof(ri)) {
-			printk(KERN_WARNING "jffs2_garbage_collect_hole: Node at 0x%08x had totlen 0x%x instead of expected 0x%x\n",
-			       fn->raw->flash_offset & ~3, ri.totlen, sizeof(ri));
+		if (je32_to_cpu(ri.totlen) != sizeof(ri)) {
+			printk(KERN_WARNING "jffs2_garbage_collect_hole: Node at 0x%08x had totlen 0x%x instead of expected 0x%zx\n",
+			       ref_offset(fn->raw),
+			       je32_to_cpu(ri.totlen), sizeof(ri));
 			return -EIO;
 		}
 		crc = crc32(0, &ri, sizeof(ri)-8);
-		if (crc != ri.node_crc) {
+		if (crc != je32_to_cpu(ri.node_crc)) {
 			printk(KERN_WARNING "jffs2_garbage_collect_hole: Node at 0x%08x had CRC 0x%08x which doesn't match calculated CRC 0x%08x\n",
-			       fn->raw->flash_offset & ~3, ri.node_crc, crc);
+			       ref_offset(fn->raw), 
+			       je32_to_cpu(ri.node_crc), crc);
 			/* FIXME: We could possibly deal with this by writing new holes for each frag */
 			printk(KERN_WARNING "Data in the range 0x%08x to 0x%08x of inode #%u will be lost\n", 
 			       start, end, f->inocache->ino);
 			goto fill;
 		}
 		if (ri.compr != JFFS2_COMPR_ZERO) {
-			printk(KERN_WARNING "jffs2_garbage_collect_hole: Node 0x%08x wasn't a hole node!\n", fn->raw->flash_offset & ~3);
+			printk(KERN_WARNING "jffs2_garbage_collect_hole: Node 0x%08x wasn't a hole node!\n", ref_offset(fn->raw));
 			printk(KERN_WARNING "Data in the range 0x%08x to 0x%08x of inode #%u will be lost\n", 
 			       start, end, f->inocache->ino);
 			goto fill;
 		}
 	} else {
 	fill:
-		ri.magic = JFFS2_MAGIC_BITMASK;
-		ri.nodetype = JFFS2_NODETYPE_INODE;
-		ri.totlen = sizeof(ri);
-		ri.hdr_crc = crc32(0, &ri, sizeof(struct jffs2_unknown_node)-4);
+		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.ino = f->inocache->ino;
-		ri.version = ++f->highest_version;
-		ri.offset = start;
-		ri.csize = end - start;
-		ri.dsize = 0;
+		ri.ino = cpu_to_je32(f->inocache->ino);
+		ri.version = cpu_to_je32(++f->highest_version);
+		ri.offset = cpu_to_je32(start);
+		ri.dsize = cpu_to_je32(end - start);
+		ri.csize = cpu_to_je32(0);
 		ri.compr = JFFS2_COMPR_ZERO;
 	}
-	ri.mode = JFFS2_F_I_MODE(f);
-	ri.uid = JFFS2_F_I_UID(f);
-	ri.gid = JFFS2_F_I_GID(f);
-	ri.isize = JFFS2_F_I_SIZE(f);
-	ri.atime = JFFS2_F_I_ATIME(f);
-	ri.ctime = JFFS2_F_I_CTIME(f);
-	ri.mtime = JFFS2_F_I_MTIME(f);
-	ri.data_crc = 0;
-	ri.node_crc = crc32(0, &ri, sizeof(ri)-8);
+	ri.mode = cpu_to_jemode(JFFS2_F_I_MODE(f));
+	ri.uid = cpu_to_je16(JFFS2_F_I_UID(f));
+	ri.gid = cpu_to_je16(JFFS2_F_I_GID(f));
+	ri.isize = cpu_to_je32(JFFS2_F_I_SIZE(f));
+	ri.atime = cpu_to_je32(JFFS2_F_I_ATIME(f));
+	ri.ctime = cpu_to_je32(JFFS2_F_I_CTIME(f));
+	ri.mtime = cpu_to_je32(JFFS2_F_I_MTIME(f));
+	ri.data_crc = cpu_to_je32(0);
+	ri.node_crc = cpu_to_je32(crc32(0, &ri, sizeof(ri)-8));
 
 	ret = jffs2_reserve_space_gc(c, sizeof(ri), &phys_ofs, &alloclen);
 	if (ret) {
-		printk(KERN_WARNING "jffs2_reserve_space_gc of %d bytes for garbage_collect_hole failed: %d\n",
+		printk(KERN_WARNING "jffs2_reserve_space_gc of %zd bytes for garbage_collect_hole failed: %d\n",
 		       sizeof(ri), ret);
 		return ret;
 	}
@@ -558,16 +957,30 @@ static int jffs2_garbage_collect_hole(st
 		printk(KERN_WARNING "Error writing new hole node: %ld\n", PTR_ERR(new_fn));
 		return PTR_ERR(new_fn);
 	}
-	if (ri.version == f->highest_version) {
+	if (je32_to_cpu(ri.version) == f->highest_version) {
 		jffs2_add_full_dnode_to_inode(c, f, new_fn);
 		if (f->metadata) {
 			jffs2_mark_node_obsolete(c, f->metadata->raw);
 			jffs2_free_full_dnode(f->metadata);
 			f->metadata = NULL;
-			return 0;
 		}
+		return 0;
 	}
-	for (frag = f->fraglist; frag; frag = frag->next) {
+
+	/* 
+	 * We should only get here in the case where the node we are
+	 * replacing had more than one frag, so we kept the same version
+	 * number as before. (Except in case of error -- see 'goto fill;' 
+	 * above.)
+	 */
+	D1(if(unlikely(fn->frags <= 1)) {
+		printk(KERN_WARNING "jffs2_garbage_collect_hole: Replacing fn with %d frag(s) but new ver %d != highest_version %d of ino #%d\n",
+		       fn->frags, je32_to_cpu(ri.version), f->highest_version,
+		       je32_to_cpu(ri.ino));
+	});
+
+	for (frag = jffs2_lookup_node_frag(&f->fragtree, fn->ofs); 
+	     frag; frag = frag_next(frag)) {
 		if (frag->ofs > fn->size + fn->ofs)
 			break;
 		if (frag->node == fn) {
@@ -611,7 +1024,6 @@ static int jffs2_garbage_collect_dnode(s
 
 	orig_end = end;
 
-
 	/* If we're looking at the last node in the block we're
 	   garbage-collecting, we allow ourselves to merge as if the
 	   block was already erasing. We're likely to be GC'ing a
@@ -643,9 +1055,11 @@ static int jffs2_garbage_collect_dnode(s
 	 *    page OK. We'll actually write it out again in commit_write, which is a little
 	 *    suboptimal, but at least we're correct.
 	 */
-	//pg = read_cache_page(inode->i_mapping, start >> PAGE_CACHE_SHIFT, (void *)jffs2_do_readpage_unlock, inode);
+#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);
@@ -662,11 +1076,11 @@ static int jffs2_garbage_collect_dnode(s
 		ret = jffs2_reserve_space_gc(c, sizeof(ri) + JFFS2_MIN_DATA_LEN, &phys_ofs, &alloclen);
 
 		if (ret) {
-			printk(KERN_WARNING "jffs2_reserve_space_gc of %d bytes for garbage_collect_dnode failed: %d\n",
+			printk(KERN_WARNING "jffs2_reserve_space_gc of %zd bytes for garbage_collect_dnode failed: %d\n",
 			       sizeof(ri)+ JFFS2_MIN_DATA_LEN, ret);
 			break;
 		}
-		cdatalen = min(alloclen - sizeof(ri), end - offset);
+		cdatalen = min_t(uint32_t, alloclen - sizeof(ri), end - offset);
 		datalen = end - offset;
 
 		writebuf = pg_ptr + (offset & (PAGE_CACHE_SIZE -1));
@@ -679,26 +1093,26 @@ static int jffs2_garbage_collect_dnode(s
 		} else {
 			datalen = cdatalen;
 		}
-		ri.magic = JFFS2_MAGIC_BITMASK;
-		ri.nodetype = JFFS2_NODETYPE_INODE;
-		ri.totlen = sizeof(ri) + cdatalen;
-		ri.hdr_crc = crc32(0, &ri, sizeof(struct jffs2_unknown_node)-4);
+		ri.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK);
+		ri.nodetype = cpu_to_je16(JFFS2_NODETYPE_INODE);
+		ri.totlen = cpu_to_je32(sizeof(ri) + cdatalen);
+		ri.hdr_crc = cpu_to_je32(crc32(0, &ri, sizeof(struct jffs2_unknown_node)-4));
 
-		ri.ino = f->inocache->ino;
-		ri.version = ++f->highest_version;
-		ri.mode = JFFS2_F_I_MODE(f);
-		ri.uid = JFFS2_F_I_UID(f);
-		ri.gid = JFFS2_F_I_GID(f);
-		ri.isize = JFFS2_F_I_SIZE(f);
-		ri.atime = JFFS2_F_I_ATIME(f);
-		ri.ctime = JFFS2_F_I_CTIME(f);
-		ri.mtime = JFFS2_F_I_MTIME(f);
-		ri.offset = offset;
-		ri.csize = cdatalen;
-		ri.dsize = datalen;
+		ri.ino = cpu_to_je32(f->inocache->ino);
+		ri.version = cpu_to_je32(++f->highest_version);
+		ri.mode = cpu_to_jemode(JFFS2_F_I_MODE(f));
+		ri.uid = cpu_to_je16(JFFS2_F_I_UID(f));
+		ri.gid = cpu_to_je16(JFFS2_F_I_GID(f));
+		ri.isize = cpu_to_je32(JFFS2_F_I_SIZE(f));
+		ri.atime = cpu_to_je32(JFFS2_F_I_ATIME(f));
+		ri.ctime = cpu_to_je32(JFFS2_F_I_CTIME(f));
+		ri.mtime = cpu_to_je32(JFFS2_F_I_MTIME(f));
+		ri.offset = cpu_to_je32(offset);
+		ri.csize = cpu_to_je32(cdatalen);
+		ri.dsize = cpu_to_je32(datalen);
 		ri.compr = comprtype;
-		ri.node_crc = crc32(0, &ri, sizeof(ri)-8);
-		ri.data_crc = crc32(0, writebuf, cdatalen);
+		ri.node_crc = cpu_to_je32(crc32(0, &ri, sizeof(ri)-8));
+		ri.data_crc = cpu_to_je32(crc32(0, writebuf, cdatalen));
 	
 		new_fn = jffs2_write_dnode(c, f, &ri, writebuf, cdatalen, phys_ofs, NULL);
 
deleted file mode 100644
--- a/packages/fs/jffs2/current/src/jffs2.c
+++ /dev/null
@@ -1,1968 +0,0 @@
-//==========================================================================
-//
-//      jffs2.c
-//
-//      JFFS2 file system
-//
-//==========================================================================
-//####ECOSGPLCOPYRIGHTBEGIN####
-// -------------------------------------------
-// This file is part of eCos, the Embedded Configurable Operating System.
-// Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc.
-//
-// eCos is free software; you can redistribute it and/or modify it under
-// the terms of the GNU General Public License as published by the Free
-// Software Foundation; either version 2 or (at your option) any later version.
-//
-// eCos is distributed in the hope that it will be useful, but WITHOUT ANY
-// WARRANTY; without even the implied warranty of MERCHANTABILITY or
-// FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
-// for more details.
-//
-// You should have received a copy of the GNU General Public License along
-// with eCos; if not, write to the Free Software Foundation, Inc.,
-// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
-//
-// As a special exception, if other files instantiate templates or use macros
-// or inline functions from this file, or you compile this file and link it
-// with other works to produce a work based on this file, this file does not
-// by itself cause the resulting work to be covered by the GNU General Public
-// License. However the source code for this file must still be made available
-// in accordance with section (3) of the GNU General Public License.
-//
-// This exception does not invalidate any other reasons why a work based on
-// this file might be covered by the GNU General Public License.
-//
-// Alternative licenses for eCos may be arranged by contacting Red Hat, Inc.
-// at http://sources.redhat.com/ecos/ecos-license/
-// -------------------------------------------
-//####ECOSGPLCOPYRIGHTEND####
-//==========================================================================
-//#####DESCRIPTIONBEGIN####
-//
-// Author(s):           dominic.ostrowski@3glab.com
-// Contributors:        nickg, richard.panton@3glab.com
-// Date:                2000-07-25
-// Purpose:             JFFS2 file system
-// Description:         This is the JFFS2 flash filesystem for eCos. 
-//                      
-//####DESCRIPTIONEND####
-//
-//==========================================================================
-// 
-// General Description
-// ===================
-// 
-//
-//==========================================================================
-
-#include "jffs2port.h"
-#include "jffs2.h"
-#include "jffs2_fs_sb.h"
-#include "jffs2_fs_i.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>
-
-//==========================================================================
-// Forward definitions
-
-// 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 );
-static int jffs2_ops_unlink   ( cyg_mtab_entry *mte, cyg_dir dir, 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 );
-static int jffs2_ops_link     ( cyg_mtab_entry *mte, cyg_dir dir1, const char *name1,
-                             cyg_dir dir2, const char *name2, int type );
-static int jffs2_opendir  ( cyg_mtab_entry *mte, cyg_dir dir, const char *name,
-                             cyg_file *fte );
-static int jffs2_chdir    ( cyg_mtab_entry *mte, cyg_dir dir, const char *name,
-                             cyg_dir *dir_out );
-static int jffs2_stat     ( cyg_mtab_entry *mte, cyg_dir dir, const char *name,
-                             struct stat *buf);
-static int jffs2_getinfo  ( cyg_mtab_entry *mte, cyg_dir dir, const char *name,
-                             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 );
-
-// File operations
-static int jffs2_fo_read      (struct CYG_FILE_TAG *fp, struct CYG_UIO_TAG *uio);
-static int jffs2_fo_write     (struct CYG_FILE_TAG *fp, struct CYG_UIO_TAG *uio);
-static int jffs2_fo_lseek     (struct CYG_FILE_TAG *fp, off_t *pos, int whence );
-static int jffs2_fo_ioctl     (struct CYG_FILE_TAG *fp, CYG_ADDRWORD com,
-                                CYG_ADDRWORD data);
-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 );
-static int jffs2_fo_setinfo   (struct CYG_FILE_TAG *fp, int key, void *buf, 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 );
-
-
-//==========================================================================
-// 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.
-
-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);
-
-// -------------------------------------------------------------------------
-// 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
-};
-
-// -------------------------------------------------------------------------
-// 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
-};
-
-
-//==========================================================================
-// 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
-
-//==========================================================================
-// 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?
-};
-
-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) {
-    struct inode *cached_inode;
-    struct inode *next_inode;
-    
-    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; // Prveious 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);
-            }
-        }
-    }
-}
-
-//==========================================================================
-// 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)
-{
-    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;    
-}
-
-
-// -------------------------------------------------------------------------
-// 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;
-
-}
-
-// -------------------------------------------------------------------------
-// jffs2_find()
-// Main interface to directory search code. This is used in all file
-// level operations to locate the object named by the pathname.
-
-static int jffs2_find( jffs2_dirsearch *d )
-{
-    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
-    }
-}
-
-//==========================================================================
-// Pathconf support
-// This function provides support for pathconf() and fpathconf().
-
-static int jffs2_pathconf( struct inode *node, struct cyg_pathconf_info *info )
-{
-    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;
-    }
-
-    return err;
-}
-
-//==========================================================================
-// Filesystem operations
-
-// -------------------------------------------------------------------------
-// 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);
-	memset(c, 0, sizeof(*c));
-        
-        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;
-
-	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;
-	}
-
-	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);
-
-	return err;
-}
-
-static int jffs2_mount    ( cyg_fstab_entry *fste, cyg_mtab_entry *mte )
-{
-    extern cyg_mtab_entry mtab[], 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 = &mtab[0]; m != &mtab_end; m++ )
-    {
-        // stop if there are more than the configured maximum
-        if( m-&mtab[0] >= CYGNUM_FILEIO_MTAB_MAX )
-        {
-            m = &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 ENOSPC;
-
-	c = JFFS2_SB_INFO(jffs2_sb);
-        memset(jffs2_sb, 0, sizeof(struct super_block));
-        jffs2_sb->s_dev = t;
-
-	err = jffs2_read_super(jffs2_sb);
-
-	if (err) {
-            free(jffs2_sb);
-            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);
-	jffs2_mark_erased_blocks(c);
-    }
-    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));
-
-    return ENOERR;
-}
-
-// -------------------------------------------------------------------------
-// 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"));
-
-    // Decrement the mount count
-    jffs2_sb->s_mount_count--;
-
-    // Only really umount if this is the only mount
-    if (jffs2_sb->s_mount_count == 0) {
-
-        // Check for open/inuse root or any cached inodes
-//if( root->i_count != 1 || root->i_cache_next != NULL) // root icount was set to 1 on mount
-        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);
-
-        // Clear root pointer
-        mte->root = CYG_DIR_NULL;
-    
-        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"));
-    }
-        
-    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 )
-{
-
-    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 );
-
-    if( err == ENOENT )
-    {
-        if( ds.last && (mode & O_CREAT) )
-        {
-            unsigned long hash;
-            struct qstr this;
-            unsigned int c;
-            const char * hashname;
-
-            // No node there, if the O_CREAT bit is set then we must
-            // create a new one. The dir and name fields of the dirsearch
-            // object will have been updated so we know where to put it.
-
-            hashname = ds.name;
-            this.name = hashname;
-            c = *(const unsigned char *)hashname;
-
-            hash = init_name_hash();
-            do {
-                hashname++;
-                hash = partial_name_hash(c, hash);
-                c = *(const unsigned char *)hashname;
-            } while (c && (c != '/'));
-            this.len = hashname - (const char *) this.name;
-            this.hash = end_name_hash(hash);
-
-            err = jffs2_create(ds.dir, &this, 0644, &node);
-
-            if( err != 0 )
-            {
-                //Possible orphaned inode on the flash - but will be gc'd
-                return err;
-            }
-        
-            err = ENOERR;
-        }
-    }
-    else if( err == ENOERR )
-    {
-        // The node exists. If the O_CREAT and O_EXCL bits are set, we
-        // must fail the open.
-        
-        if( (mode & (O_CREAT|O_EXCL)) == (O_CREAT|O_EXCL) )
-            err = EEXIST;
-        else node = ds.node;
-    }
-    
-    if( err == ENOERR && (mode & O_TRUNC ) )
-    {
-        // If the O_TRUNC bit is set we must clean out the file data.
-
-        node->i_size = 0;
-
-        // Update file times
-        node->i_ctime =
-        node->i_mtime = cyg_timestamp();
-    }
-
-    if( err != ENOERR ) return err;
-
-    // Check that we actually have a file here
-    if( S_ISDIR(node->i_mode) ) return EISDIR;
-
-    node->i_count++;       // Count successful open
-    
-    // Initialize the file object
-    
-    file->f_flag        |= mode & CYG_FILE_MODE_MASK;
-    file->f_type        = CYG_FILE_TYPE_FILE;
-    file->f_ops         = &jffs2_fileops;
-    file->f_offset      = (mode&O_APPEND) ? node->i_size : 0;
-    file->f_data        = (CYG_ADDRWORD)node;  
-    file->f_xops        = 0;
-
-    return ENOERR;
-}
-
-// -------------------------------------------------------------------------
-// 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"));
-
-    icache_evict((struct inode *)mte->root, (struct inode *)dir);
-
-    init_dirsearch( &ds, (struct inode *)dir, name );
-    
-    err = jffs2_find( &ds );
-
-    if( err != ENOERR ) return err;
-
-    // Cannot unlink directories, use rmdir() instead
-    if( S_ISDIR(ds.node->i_mode) )
-        return EPERM;
-
-    // Delete it from its directory
-
-    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_unlink(ds.dir, ds.node, &this);
-    
-    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);
-
-            err = jffs2_mkdir(ds.dir, &this, 0, &node);
-
-            if( err != 0 )
-                return ENOSPC;
-
-        }
-        // 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;
-    }
-
-    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;
-
-    D2(printf("jffs2_ops_rmdir\n"));
-
-    icache_evict((struct inode *)mte->root, (struct inode *)dir);
-
-    init_dirsearch( &ds, (struct inode *)dir, name );
-    
-    err = jffs2_find( &ds );
-
-    if( err != ENOERR ) return err;
-
-    // Check that this is actually a directory.
-    if( !S_ISDIR(ds.node->i_mode) )
-        return EPERM;
-
-    // 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_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 )
-{
-    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
-        
-        err = jffs2_unlink(ds2.dir, ds2.node, &this2);
-    
-        if( err != 0 ) return err;
-
-    }
-
-    // Now we know that there is no clashing node at the destination,
-    // make a new direntry at the destination and delete the old entry
-    // at the source.
-
-    err  = jffs2_rename(ds1.dir, ds1.node, &this1, ds2.dir, &this2);
-
-    // Update directory times
-    if( err == 0 )
-        ds1.dir->i_ctime =
-        ds1.dir->i_mtime =
-        ds2.dir->i_ctime =
-        ds2.dir->i_mtime = cyg_timestamp();
-            
-    return err;
-}
-
-// -------------------------------------------------------------------------
-// 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 )
-{
-    unsigned long hash;
-    struct qstr this;
-    unsigned int c;
-    const char * hashname;
-    jffs2_dirsearch ds1, ds2;
-    int err;
-
-    D2(printf("jffs2_ops_link\n"));
-
-    // Only do hard links for now in this filesystem
-    if( type != CYG_FSLINK_HARD )
-        return EINVAL;
-    
-    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 );
-
-    // Don't allow links to existing objects
-    if( err == ENOERR ) return EEXIST;
-    
-    // Allow through links to non-existing terminal objects
-    if( ds2.last && err == ENOENT )
-        ds2.node = NULL, err = ENOERR;
-
-    if( err != ENOERR ) return err;
-    
-    // Now we know that there is no existing node at the destination,
-    // make a new direntry at the destination.
-
-    hashname = ds2.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_link(ds2.dir, ds1.node, &this);
-
-    if( err == 0 )
-        ds1.node->i_ctime =
-        ds2.dir->i_ctime =
-        ds2.dir->i_mtime = cyg_timestamp();
-            
-    return err;
-}
-
-// -------------------------------------------------------------------------
-// jffs2_opendir()
-// Open a directory for reading.
-
-static int jffs2_opendir  ( cyg_mtab_entry *mte, cyg_dir dir, const char *name,
-                             cyg_file *file )
-{
-    jffs2_dirsearch ds;
-    int err;
-
-    D2(printf("jffs2_opendir\n"));
-
-    icache_evict((struct inode *)mte->root, (struct inode *)dir);
-
-    init_dirsearch( &ds, (struct inode *)dir, name );
-    
-    err = jffs2_find( &ds );
-
-    if( err != ENOERR ) return err;
-
-    // check it is really a directory.
-    if( !S_ISDIR(ds.node->i_mode) ) return ENOTDIR;
-
-    ds.node->i_count++;       // Count successful open
-    
-    // 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;
-
-        
-    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 )
-{
-    D2(printf("jffs2_chdir\n"));
-
-    if( dir_out != NULL )
-    {
-        // This is a request to get a new directory pointer in
-        // *dir_out.
-
-        jffs2_dirsearch ds;
-        int err;
-
-        icache_evict((struct inode *)mte->root, (struct inode *)dir);
-
-        init_dirsearch( &ds, (struct inode *)dir, name );
-    
-        err = jffs2_find( &ds );
-
-        if( err != ENOERR ) return err;
-
-        // 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.
-
-        struct inode *node = (struct inode *)dir;
-
-        // Just decrement directory reference count.
-        dec_refcnt( node );
-    }
-        
-    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)
-{
-    jffs2_dirsearch ds;
-    int err;
-
-    D2(printf("jffs2_stat\n"));
-
-    icache_evict((struct inode *)mte->root, (struct inode *)dir);
-
-    init_dirsearch( &ds, (struct inode *)dir, name );
-    
-    err = jffs2_find( &ds );
-
-    if( err != ENOERR ) return err;
-
-    // Fill in the status
-    buf->st_mode        = ds.node->i_mode;
-    buf->st_ino         = (ino_t)ds.node;
-    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;
-    
-    return err;
-
-    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 )
-{
-    jffs2_dirsearch ds;
-    int err;
-
-    D2(printf("jffs2_getinfo\n"));
-
-    icache_evict((struct inode *)mte->root, (struct inode *)dir);
-
-    init_dirsearch( &ds, (struct inode *)dir, name );
-    
-    err = jffs2_find( &ds );
-
-    if( err != ENOERR ) return err;
-
-    switch( key )
-    {
-    case FS_INFO_CONF:
-        err = jffs2_pathconf( ds.node, (struct cyg_pathconf_info *)buf );
-        break;
-        
-    default:
-        err = EINVAL;
-    }
-    return err;
-
-    return ENOERR;
-}
-
-// -------------------------------------------------------------------------
-// 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 )
-{
-    // No setinfo keys supported at present
-    
-    D2(printf("jffs2_setinfo\n"));
-
-    return EINVAL;
-}
-
-
-//==========================================================================
-// File operations
-
-// -------------------------------------------------------------------------
-// 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();
-
-    uio->uio_resid = resid;
-    fp->f_offset = pos;
-
-    up(&f->sem);
-    
-    return ENOERR;
-}
-
-// -------------------------------------------------------------------------
-// jffs2_fo_write()
-// Write data to file.
-
-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;    
-
-    uio->uio_resid = resid;
-    fp->f_offset = pos;
-    
-    return ENOERR;
-}
-
-// -------------------------------------------------------------------------
-// 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"));
-
-    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;
-    
-    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)
-{
-    // No Ioctls currenly defined.
-
-    D2(printf("jffs2_fo_ioctl\n"));
-
-    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.
-  
-    D2(printf("jffs2_fo_fsync\n"));
-
-    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;
-
-    D2(printf("jffs2_fo_close\n"));
-
-    dec_refcnt( node );
-
-    fp->f_data = 0;     // zero data pointer
-    
-    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;
-
-    D2(printf("jffs2_fo_fstat\n"));
-
-    // Fill in the status
-    buf->st_mode        = node->i_mode;
-    buf->st_ino         = (ino_t)node;
-    buf->st_dev         = 0;
-    buf->st_nlink       = node->i_nlink;
-    buf->st_uid         = 0;
-    buf->st_gid         = 0;
-    buf->st_size        = node->i_size;
-    buf->st_atime       = node->i_atime;
-    buf->st_mtime       = node->i_mtime;
-    buf->st_ctime       = node->i_ctime;
-    
-    return ENOERR;
-}
-
-// -------------------------------------------------------------------------
-// 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 )
-{
-    struct inode *node = (struct inode *)fp->f_data;    
-    int err;
-
-    D2(printf("jffs2_fo_getinfo\n"));
-
-    switch( key )
-    {
-    case FS_INFO_CONF:
-        err = jffs2_pathconf( node, (struct cyg_pathconf_info *)buf );
-        break;
-        
-    default:
-        err = EINVAL;
-    }
-    return err;
-
-    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 )
-{
-    // No setinfo key supported at present
-    
-    D2(printf("jffs2_fo_setinfo\n"));
-
-    return ENOERR;
-}
-
-
-//==========================================================================
-// Directory operations
-
-// -------------------------------------------------------------------------
-// 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';
-}
-
-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;
- out_sem:
-	up(&f->sem);
- out:
-    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
-    
-    D2(printf("jffs2_fo_dirlseek\n"));
-
-    if( whence != SEEK_SET || *pos != 0)
-        return EINVAL;
-
-    *pos = fp->f_offset = 0;
-    
-    return ENOERR;
-}
-
-
-//==========================================================================
-// 
-// Called by JFFS2
-// ===============
-// 
-//
-//==========================================================================
-
-
-struct page *read_cache_page(unsigned long index,
-				int (*filler)(void *,struct page*),
-				void *data)
-{
-        // 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));
-
-printf("read_cache_page\n");
-        memset(&gc_buffer, 0, PAGE_CACHE_SIZE);
-
-        if(gc_page != NULL) {
-            gc_page->virtual = &gc_buffer;
-            gc_page->index = index;
-
-	    err = filler(data, gc_page);
-	    if (err < 0) {
-                free(gc_page);
-                gc_page = NULL;
-	    }
-        }
-
-	return gc_page;
-}
-
-void page_cache_release(struct page * pg) {
-
-        // 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);
-}
-
-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;
-
-	inode = malloc(sizeof(struct inode));
-	if (inode == NULL)
-                return 0;
-
-        D2(printf("malloc new_inode %x ####################################\n", inode));
-
-        memset(inode, 0, sizeof(struct inode));
-        inode->i_sb = sb;
-        inode->i_ino = 1;
-	inode->i_count = 0;//1; // Let ecos manage the open count
-
-        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
-
-        // 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;
-}
-
-struct inode * iget(struct super_block *sb, cyg_uint32 ino) {
-
-        // Substitute for iget drops straight through to reading the 
-        // inode from disk if it is not in the inode cache
-
-        // Called in super.c jffs2_read_super, dir.c jffs2_lookup,
-        // and gc.c jffs2_garbage_collect_pass
-
-        // Must first check for cached inode 
-        // If this fails let new_inode create one
-
-	struct inode * inode;
-
-        D2(printf("iget\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)
-                return inode;
-        }
-        inode = NULL;
-
-        // Not cached, so malloc it
-        inode = new_inode(sb);
-	if (inode == NULL)
-                return 0;
-
-        inode->i_ino = ino;
-        jffs2_read_inode(inode);
-	
-	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) {
-                    cached_inode->i_cache_prev->i_cache_next = cached_inode->i_cache_next; // Prveious 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);
-        }
-}
-
-static int return_EIO(void)
-{
-	return -EIO;
-}
-
-#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 = CURRENT_TIME;
-	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 );
-}
-
-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 );
-}
-
-int
-jffs2_flash_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 );
-}
-
-// -------------------------------------------------------------------------
-// EOF jffs2.c
deleted file mode 100644
--- a/packages/fs/jffs2/current/src/jffs2.h
+++ /dev/null
@@ -1,145 +0,0 @@
-/*
- * JFFS2 -- Journalling Flash File System, Version 2.
- *
- * Copyright (C) 2001, 2002 Red Hat, Inc.
- *
- * Created by David Woodhouse <dwmw2@cambridge.redhat.com>
- *
- * For licensing information, see the file 'LICENCE' in this directory.
- *
- * $Id: jffs2.h,v 1.22 2002/01/09 13:27:58 dwmw2 Exp $
- *
- */
-
-#ifndef __LINUX_JFFS2_H__
-#define __LINUX_JFFS2_H__
-
-#ifdef __ECOS
-#include "jffs2port.h"
-#else
-#include <linux/posix_types.h>
-#endif
-
-#define JFFS2_SUPER_MAGIC 0x72b6
-
-/* Values we may expect to find in the 'magic' field */
-#define JFFS2_OLD_MAGIC_BITMASK 0x1984
-#define JFFS2_MAGIC_BITMASK 0x1985
-#define KSAMTIB_CIGAM_2SFFJ 0x5981 /* For detecting wrong-endian fs */
-#define JFFS2_EMPTY_BITMASK 0xffff
-#define JFFS2_DIRTY_BITMASK 0x0000
-
-/* We only allow a single char for length, and 0xFF is empty flash so
-   we don't want it confused with a real length. Hence max 254.
-*/
-#define JFFS2_MAX_NAME_LEN 254
-
-/* How small can we sensibly write nodes? */
-#define JFFS2_MIN_DATA_LEN 128
-
-#define JFFS2_COMPR_NONE	0x00
-#define JFFS2_COMPR_ZERO	0x01
-#define JFFS2_COMPR_RTIME	0x02
-#define JFFS2_COMPR_RUBINMIPS	0x03
-#define JFFS2_COMPR_COPY	0x04
-#define JFFS2_COMPR_DYNRUBIN	0x05
-#define JFFS2_COMPR_ZLIB	0x06
-/* Compatibility flags. */
-#define JFFS2_COMPAT_MASK 0xc000      /* What do to if an unknown nodetype is found */
-#define JFFS2_NODE_ACCURATE 0x2000
-/* INCOMPAT: Fail to mount the filesystem */
-#define JFFS2_FEATURE_INCOMPAT 0xc000
-/* ROCOMPAT: Mount read-only */
-#define JFFS2_FEATURE_ROCOMPAT 0x8000
-/* RWCOMPAT_COPY: Mount read/write, and copy the node when it's GC'd */
-#define JFFS2_FEATURE_RWCOMPAT_COPY 0x4000
-/* RWCOMPAT_DELETE: Mount read/write, and delete the node when it's GC'd */
-#define JFFS2_FEATURE_RWCOMPAT_DELETE 0x0000
-
-#define JFFS2_NODETYPE_DIRENT (JFFS2_FEATURE_INCOMPAT | JFFS2_NODE_ACCURATE | 1)
-#define JFFS2_NODETYPE_INODE (JFFS2_FEATURE_INCOMPAT | JFFS2_NODE_ACCURATE | 2)
-#define JFFS2_NODETYPE_CLEANMARKER (JFFS2_FEATURE_RWCOMPAT_DELETE | JFFS2_NODE_ACCURATE | 3)
-
-// Maybe later...
-//#define JFFS2_NODETYPE_CHECKPOINT (JFFS2_FEATURE_RWCOMPAT_DELETE | JFFS2_NODE_ACCURATE | 3)
-//#define JFFS2_NODETYPE_OPTIONS (JFFS2_FEATURE_RWCOMPAT_COPY | JFFS2_NODE_ACCURATE | 4)
-
-/* Same as the non_ECC versions, but with extra space for real 
- * ECC instead of just the checksum. For use on NAND flash 
- */
-//#define JFFS2_NODETYPE_DIRENT_ECC (JFFS2_FEATURE_INCOMPAT | JFFS2_NODE_ACCURATE | 5)
-//#define JFFS2_NODETYPE_INODE_ECC (JFFS2_FEATURE_INCOMPAT | JFFS2_NODE_ACCURATE | 6)
-
-#define JFFS2_INO_FLAG_PREREAD	  1	/* Do read_inode() for this one at 
-					   mount time, don't wait for it to 
-					   happen later */
-#define JFFS2_INO_FLAG_USERCOMPR  2	/* User has requested a specific 
-					   compression type */
-
-
-struct jffs2_unknown_node
-{
-	/* All start like this */
-	uint16_t magic;
-	uint16_t nodetype;
-	uint32_t totlen; /* So we can skip over nodes we don't grok */
-	uint32_t hdr_crc;
-} __attribute__((packed));
-
-struct jffs2_raw_dirent
-{
-	uint16_t magic;
-	uint16_t nodetype;	/* == JFFS_NODETYPE_DIRENT */
-	uint32_t totlen;
-	uint32_t hdr_crc;
-	uint32_t pino;
-	uint32_t version;
-	uint32_t ino; /* == zero for unlink */
-	uint32_t mctime;
-	uint8_t nsize;
-	uint8_t type;
-	uint8_t unused[2];
-	uint32_t node_crc;
-	uint32_t name_crc;
-	uint8_t name[0];
-} __attribute__((packed));
-
-/* The JFFS2 raw inode structure: Used for storage on physical media.  */
-/* The uid, gid, atime, mtime and ctime members could be longer, but 
-   are left like this for space efficiency. If and when people decide
-   they really need them extended, it's simple enough to add support for
-   a new type of raw node.
-*/
-struct jffs2_raw_inode
-{
-	uint16_t magic;      /* A constant magic number.  */
-	uint16_t nodetype;   /* == JFFS_NODETYPE_INODE */
-	uint32_t totlen;     /* Total length of this node (inc data, etc.) */
-	uint32_t hdr_crc;
-	uint32_t ino;        /* Inode number.  */
-	uint32_t version;    /* Version number.  */
-	uint32_t mode;       /* The file's type or mode.  */
-	uint16_t uid;        /* The file's owner.  */
-	uint16_t gid;        /* The file's group.  */
-	uint32_t isize;      /* Total resultant size of this inode (used for truncations)  */
-	uint32_t atime;      /* Last access time.  */
-	uint32_t mtime;      /* Last modification time.  */
-	uint32_t ctime;      /* Change time.  */
-	uint32_t offset;     /* Where to begin to write.  */
-	uint32_t csize;      /* (Compressed) data size */
-	uint32_t dsize;	     /* Size of the node's data. (after decompression) */
-	uint8_t compr;       /* Compression algorithm used */
-	uint8_t usercompr;   /* Compression algorithm requested by the user */
-	uint16_t flags;	     /* See JFFS2_INO_FLAG_* */
-	uint32_t data_crc;   /* CRC for the (compressed) data.  */
-	uint32_t node_crc;   /* CRC for the raw inode (excluding data)  */
-//	uint8_t data[dsize];
-} __attribute__((packed));
-
-union jffs2_node_union {
-	struct jffs2_raw_inode i;
-	struct jffs2_raw_dirent d;
-	struct jffs2_unknown_node u;
-};
-
-#endif /* __LINUX_JFFS2_H__ */
deleted file mode 100644
--- a/packages/fs/jffs2/current/src/jffs2_fs_i.h
+++ /dev/null
@@ -1,55 +0,0 @@
-/* $Id: jffs2_fs_i.h,v 1.10 2002/01/09 11:44:23 dwmw2 Exp $ */
-
-#ifndef _JFFS2_FS_I
-#define _JFFS2_FS_I
-
-/* Include the pipe_inode_info at the beginning so that we can still
-   use the storage space in the inode when we have a pipe inode.
-   This sucks.
-*/
-
-#undef THISSUCKS /* Only for 2.2 */
-#ifdef THISSUCKS
-#include <linux/pipe_fs_i.h>
-#endif
-
-struct jffs2_inode_info {
-#ifdef THISSUCKS
-        struct pipe_inode_info pipecrap;
-#endif
-	/* We need an internal semaphore similar to inode->i_sem.
-	   Unfortunately, we can't used the existing one, because
-	   either the GC would deadlock, or we'd have to release it
-	   before letting GC proceed. Or we'd have to put ugliness
-	   into the GC code so it didn't attempt to obtain the i_sem
-	   for the inode(s) which are already locked */
-	struct semaphore sem;
-
-	/* The highest (datanode) version number used for this ino */
-	uint32_t highest_version;
-
-	/* List of data fragments which make up the file */
-	struct jffs2_node_frag *fraglist;
-
-	/* There may be one datanode which isn't referenced by any of the
-	   above fragments, if it contains a metadata update but no actual
-	   data - or if this is a directory inode */
-	/* This also holds the _only_ dnode for symlinks/device nodes, 
-	   etc. */
-	struct jffs2_full_dnode *metadata;
-
-	/* Directory entries */
-	struct jffs2_full_dirent *dents;
-
-	/* Some stuff we just have to keep in-core at all times, for each inode. */
-	struct jffs2_inode_cache *inocache;
-
-	/* Keep a pointer to the last physical node in the list. We don't 
-	   use the doubly-linked lists because we don't want to increase
-	   the memory usage that much. This is simpler */
-	//	struct jffs2_raw_node_ref *lastnode;
-	uint16_t flags;
-	uint8_t usercompr;
-};
-
-#endif /* _JFFS2_FS_I */
deleted file mode 100644
--- a/packages/fs/jffs2/current/src/jffs2_fs_sb.h
+++ /dev/null
@@ -1,81 +0,0 @@
-/* $Id: jffs2_fs_sb.h,v 1.18 2002/01/09 11:44:23 dwmw2 Exp $ */
-
-#ifndef _JFFS2_FS_SB
-#define _JFFS2_FS_SB
-
-#ifdef __ECOS
-#include "list.h"
-#else
-#include <linux/types.h>
-#include <linux/spinlock.h>
-#include <linux/completion.h>
-#include <asm/semaphore.h>
-#include <linux/list.h>
-#endif
-
-#define INOCACHE_HASHSIZE 1
-
-#define JFFS2_SB_FLAG_RO 1
-
-/* A struct for the overall file system control.  Pointers to
-   jffs2_sb_info structs are named `c' in the source code.  
-   Nee jffs_control
-*/
-struct jffs2_sb_info {
-	struct mtd_info *mtd;
-
-	uint32_t highest_ino;
-	unsigned int flags;
-	spinlock_t nodelist_lock;
-
-	//	pid_t thread_pid;		/* GC thread's PID */
-	struct task_struct *gc_task;	/* GC task struct */
-#ifndef __ECOS
-	struct semaphore gc_thread_start; /* GC thread start mutex */
-	struct completion gc_thread_exit; /* GC thread exit completion port */
-#endif
-	//	uint32_t gc_minfree_threshold;	/* GC trigger thresholds */
-	//	uint32_t gc_maxdirty_threshold;
-
-	struct semaphore alloc_sem;	/* Used to protect all the following 
-					   fields, and also to protect against
-					   out-of-order writing of nodes.
-					   And GC.
-					*/
-	uint32_t flash_size;
-	uint32_t used_size;
-	uint32_t dirty_size;
-	uint32_t free_size;
-	uint32_t erasing_size;
-	uint32_t bad_size;
-	uint32_t sector_size;
-	//	uint32_t min_free_size;
-	//	uint32_t max_chunk_size;
-
-	uint32_t nr_free_blocks;
-	uint32_t nr_erasing_blocks;
-
-	uint32_t nr_blocks;
-	struct jffs2_eraseblock *blocks;	/* The whole array of blocks. Used for getting blocks 
-						 * from the offset (blocks[ofs / sector_size]) */
-	struct jffs2_eraseblock *nextblock;	/* The block we're currently filling */
-
-	struct jffs2_eraseblock *gcblock;	/* The block we're currently garbage-collecting */
-
-	struct list_head clean_list;		/* Blocks 100% full of clean data */
-	struct list_head dirty_list;		/* Blocks with some dirty space */
-	struct list_head erasing_list;		/* Blocks which are currently erasing */
-	struct list_head erase_pending_list;	/* Blocks which need erasing */
-	struct list_head erase_complete_list;	/* Blocks which are erased and need the clean marker written to them */
-	struct list_head free_list;		/* Blocks which are free and ready to be used */
-	struct list_head bad_list;		/* Bad blocks. */
-	struct list_head bad_used_list;		/* Bad blocks with valid data in. */
-
-	spinlock_t erase_completion_lock;	/* Protect free_list and erasing_list 
-						   against erase completion handler */
-	wait_queue_head_t erase_wait;		/* For waiting for erases to complete */
-	struct jffs2_inode_cache *inocache_list[INOCACHE_HASHSIZE];
-	spinlock_t inocache_lock;
-};
-
-#endif /* _JFFS2_FB_SB */
--- a/packages/fs/jffs2/current/src/jffs2port.h
+++ b/packages/fs/jffs2/current/src/jffs2port.h
@@ -1,6 +1,8 @@
 #ifndef __LINUX_JFFS2PORT_H__
 #define __LINUX_JFFS2PORT_H__
 
+/* $Id: jffs2port.h,v 1.3 2003/01/21 18:13:22 dwmw2 Exp $ */
+
 #include <pkgconf/system.h>
 #include <pkgconf/hal.h>
 //#include <pkgconf/kernel.h>
@@ -27,92 +29,12 @@
 
 #include <cyg/io/flash.h>
 
-#include <cyg/crc/crc.h>
-
 #include <pkgconf/fs_jffs2.h>
-
-#include "list.h"
-
-// ARM related constants
-#define PAGE_SHIFT 0xC
-#define PAGE_SIZE  (0x1 << PAGE_SHIFT)
-#define PAGE_CACHE_SHIFT        PAGE_SHIFT
-#define PAGE_CACHE_SIZE         PAGE_SIZE
-
-//#define BITS_PER_LONG 32
-
+#include <linux/types.h>
+#include <linux/list.h>
+#include <asm/bug.h>
 // Linux types
 #define printf diag_printf
-#define printk diag_printf
-#define BUG() diag_printf("BUG()\n")
-
-#define uint8_t cyg_uint8
-#define uint16_t cyg_uint16
-#define uint32_t cyg_uint32
-#define loff_t off_t
-
-#define min(x,y) (x<y?x:y)
-#define max(x,y) (x<y?y:x)
-#define min_t(t, x,y) ((t)x<(t)y?(t)x:(t)y)
-
-#define atomic_t int
-#define atomic_inc(atom) (*atom)++
-#define atomic_dec(atom) (*atom)--
-#define atomic_read(atom) (*atom)
-
-#define kmalloc(a,b) malloc(a)
-#define kfree(a) free(a)
-
-#define PageLocked(pg) 1
-#define Page_Uptodate(pg) 0
-#define UnlockPage(pg) 
-#define PAGE_BUG(pg) BUG()
-#define ClearPageUptodate(pg)
-#define SetPageError(pg)
-#define ClearPageError(pg)
-#define SetPageUptodate(pg)
-
-#define jiffies 100
-
-#define ERR_PTR(err) (void*)(err)
-#define PTR_ERR(err) (cyg_int32)(err)
-#define IS_ERR(err) (err==NULL)
-
-#define CURRENT_TIME cyg_timestamp()
-
-//#define spinlock_t cyg_spinlock_t
-typedef struct { } spinlock_t;
-#define SPIN_LOCK_UNLOCKED (spinlock_t) { }
-#define spin_lock_init(lock) do{} while (0)
-#define spin_lock(lock) do{} while (0)
-#define spin_unlock(lock) do{} while (0)
-#define spin_lock_bh(lock) do{} while (0)
-#define spin_unlock_bh(lock) do{} while (0)
-
-struct semaphore {
-	cyg_drv_mutex_t x;
-};
-
-#define init_MUTEX(sem) cyg_drv_mutex_init((cyg_drv_mutex_t *)sem)
-#define init_MUTEX_LOCKED(sem) do { cyg_drv_mutex_init((cyg_drv_mutex_t *)sem); cyg_drv_mutex_lock((cyg_drv_mutex_t *)sem); } while(0)
-#define down(sem) cyg_drv_mutex_lock((cyg_drv_mutex_t *)sem)
-#define down_interruptible(sem) ({ cyg_drv_mutex_lock((cyg_drv_mutex_t *)sem), 0; })
-#define up(sem) cyg_drv_mutex_unlock((cyg_drv_mutex_t *)sem)
-
-#define wait_queue_head_t cyg_uint32
-#define init_waitqueue_head(wait) do{} while (0)
-#define add_wait_queue(wait,new_wait) do{} while (0)
-#define remove_wait_queue(wait,old_wait) do{} while (0)
-#define DECLARE_WAITQUEUE(wait,current) do{} while (0)
-
-#define	KERN_EMERG              "<0>" // system is unusable
-#define	KERN_ALERT              "<1>" // action must be taken immediately
-#define	KERN_CRIT               "<2>" // critical conditions
-#define	KERN_ERR                "<3>" // error conditions
-#define	KERN_WARNING            "<4>" // warning conditions
-#define	KERN_NOTICE             "<5>" // normal but significant condition
-#define	KERN_INFO               "<6>" // informational
-#define	KERN_DEBUG              "<7>" // debug-level messages
 
 // Structures used by VFS
 
@@ -127,7 +49,7 @@ struct qstr {
 #define DNAME_INLINE_LEN 16
 
 struct dentry {
-	atomic_t d_count;
+	//	atomic_t d_count;
 	unsigned int d_flags;
 	struct inode  * d_inode;	/* Where the name belongs to - NULL is negative */
 	struct dentry * d_parent;	/* parent directory */
@@ -225,33 +147,12 @@ struct inode_operations {
 	//int (*getattr) (struct dentry *, struct iattr *);
 };
 
-/*
- * File types
- */
-#define DT_UNKNOWN	0
-#define DT_DIR		4
-#define DT_REG		8
 
 struct iovec {
         void *iov_base;
         ssize_t iov_len; 
 };
 
-// FIXME: we must provide some translation so that Linux-created jffs2
-// partitions can be read on eCos and vice versa
-
-#define S_IFMT 0xF9 // Quick fix for clash on ecos / linux mode bits
-#define S_IFDIR __stat_mode_DIR
-#define S_IFREG __stat_mode_REG
-#define S_IFBLK __stat_mode_BLK
-#define S_IFCHR __stat_mode_CHR
-#define S_IFLNK 0x101
-#define S_IFSOCK 0x102
-#define S_IFIFO __stat_mode_FIFO
-#define S_IRUGO 0x103
-#define S_IXUGO 0x104
-#define S_IRWXUGO 0x105
-
 
 // called by JFFS2
 
@@ -291,8 +192,6 @@ cyg_bool jffs2_flash_write(struct jffs2_
 int jffs2_flash_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);
 
-static inline void wake_up(wait_queue_head_t *erase_wait)
-{ /* Only used for waking up threads blocks on erases. Not used in eCos */ }
 
 // calls to JFFS2
 
deleted file mode 100644
--- a/packages/fs/jffs2/current/src/list.h
+++ /dev/null
@@ -1,95 +0,0 @@
-#ifndef CYGONCE_FS_JFFS2_LIST_H
-#define CYGONCE_FS_JFFS2_LIST_H
-
-/*
- * JFFS2 -- Journalling Flash File System, Version 2.
- *
- * Copyright (C) 2002 Red Hat, Inc.
- *
- * Created by Jonathan Larmour <jlarmour@redhat.com>
- *
- * For licensing information, see the file 'LICENCE' in this directory.
- *
- * $Id: $
- *
- */
-
-/* -----------------------------------------------------------------------*/
-
-/* Doubly linked list implementation to replace the GPL'd one used in
-   the Linux kernel. */
-
-#include <stddef.h>
-#include <cyg/infra/cyg_type.h>
-
-/* TYPES */
-
-struct list_head {
-    struct list_head *next;
-    struct list_head *prev;
-};
-
-/* MACROS */
-
-#define INIT_LIST_HEAD( _list_ )              \
-CYG_MACRO_START                               \
-(_list_)->next = (_list_)->prev = (_list_);   \
-CYG_MACRO_END
-
-/* FUNCTIONS */
-
-/* Insert an entry _after_ the specified entry */
-static __inline__ void
-list_add( struct list_head *newent, struct list_head *afterthisent )
-{
-    struct list_head *next = afterthisent->next;
-    newent->next = next;
-    newent->prev = afterthisent;
-    afterthisent->next = newent;
-    next->prev = newent;
-} /* list_add() */
-
-/* Insert an entry _before_ the specified entry */
-static __inline__ void
-list_add_tail( struct list_head *newent, struct list_head *beforethisent )
-{
-    struct list_head *prev = beforethisent->prev;
-    newent->prev = prev;
-    newent->next = beforethisent;
-    beforethisent->prev = newent;
-    prev->next = newent;
-} /* list_add_tail() */
-
-/* Delete the specified entry */
-static __inline__ void
-list_del( struct list_head *ent )
-{
-    ent->prev->next = ent->next;
-    ent->next->prev = ent->prev;
-} /* list_del() */
-
-/* Is this list empty? */
-static __inline__ int
-list_empty( struct list_head *list )
-{
-    return ( list->next == list );
-} /* list_empty() */
-
-/* list_entry - Assuming you have a struct of type _type_ that contains a
-   list which has the name _member_ in that struct type, then given the
-   address of that list in the struct, _list_, this returns the address
-   of the container structure */
-
-#define list_entry( _list_, _type_, _member_ ) \
-    ((_type_ *)((char *)(_list_)-(char *)(offsetof(_type_,_member_))))
-
-/* list_for_each - using _ent_, iterate through list _list_ */
-
-#define list_for_each( _ent_, _list_ )   \
-    for ( (_ent_) = (_list_)->next;      \
-    (_ent_) != (_list_);                 \
-    (_ent_) = (_ent_)->next )
-
-/* -----------------------------------------------------------------------*/
-#endif /* #ifndef CYGONCE_FS_JFFS2_LIST_H */
-/* EOF list.h */
--- a/packages/fs/jffs2/current/src/malloc-ecos.c
+++ b/packages/fs/jffs2/current/src/malloc-ecos.c
@@ -7,10 +7,11 @@
  *
  * For licensing information, see the file 'LICENCE' in this directory.
  *
- * $Id$
+ * $Id: malloc-ecos.c,v 1.2 2003/01/09 13:53:44 dwmw2 Exp $
  *
  */
 
+#include <linux/kernel.h>
 #include "nodelist.h"
 
 struct jffs2_full_dirent *jffs2_alloc_full_dirent(int namesize)
--- a/packages/fs/jffs2/current/src/nodelist.c
+++ b/packages/fs/jffs2/current/src/nodelist.c
@@ -7,15 +7,18 @@
  *
  * For licensing information, see the file 'LICENCE' in this directory.
  *
- * $Id: nodelist.c,v 1.36 2002/01/09 16:52:59 dwmw2 Exp $
+ * $Id: nodelist.c,v 1.75 2003/01/21 18:11:28 dwmw2 Exp $
  *
  */
 
-#ifndef __ECOS
 #include <linux/kernel.h>
+#include <linux/sched.h>
 #include <linux/fs.h>
 #include <linux/mtd/mtd.h>
-#endif
+#include <linux/rbtree.h>
+#include <linux/crc32.h>
+#include <linux/slab.h>
+#include <linux/pagemap.h>
 #include "nodelist.h"
 
 void jffs2_add_fd_to_list(struct jffs2_sb_info *c, struct jffs2_full_dirent *new, struct jffs2_full_dirent **list)
@@ -46,7 +49,7 @@ void jffs2_add_fd_to_list(struct jffs2_s
 	*prev = new;
 
  out:
-	D1(while(*list) {
+	D2(while(*list) {
 		printk(KERN_DEBUG "Dirent \"%s\" (hash 0x%08x, ino #%u\n", (*list)->name, (*list)->nhash, (*list)->ino);
 		list = &(*list)->next;
 	});
@@ -95,7 +98,8 @@ static void jffs2_free_full_dirent_list(
 
 int jffs2_get_inode_nodes(struct jffs2_sb_info *c, ino_t ino, struct jffs2_inode_info *f,
 			  struct jffs2_tmp_dnode_info **tnp, struct jffs2_full_dirent **fdp,
-			  uint32_t *highest_version)
+			  uint32_t *highest_version, uint32_t *latest_mctime,
+			  uint32_t *mctime_ver)
 {
 	struct jffs2_raw_node_ref *ref = f->inocache->nodes;
 	struct jffs2_tmp_dnode_info *tn, *ret_tn = NULL;
@@ -105,44 +109,63 @@ int jffs2_get_inode_nodes(struct jffs2_s
 	size_t retlen;
 	int err;
 
+	*mctime_ver = 0;
+
 	D1(printk(KERN_DEBUG "jffs2_get_inode_nodes(): ino #%lu\n", ino));
 	if (!f->inocache->nodes) {
-		printk(KERN_WARNING "Eep. no nodes for ino #%lu\n", ino);
+		printk(KERN_WARNING "Eep. no nodes for ino #%lu\n", (unsigned long)ino);
 	}
+
+	spin_lock(&c->erase_completion_lock);
+
 	for (ref = f->inocache->nodes; ref && ref->next_in_ino; ref = ref->next_in_ino) {
 		/* Work out whether it's a data node or a dirent node */
-		if (ref->flash_offset & 1) {
+		if (ref_obsolete(ref)) {
 			/* FIXME: On NAND flash we may need to read these */
-			D1(printk(KERN_DEBUG "node at 0x%08x is obsoleted. Ignoring.\n", ref->flash_offset &~3));
+			D1(printk(KERN_DEBUG "node at 0x%08x is obsoleted. Ignoring.\n", ref_offset(ref)));
 			continue;
 		}
-		err = jffs2_flash_read(c, (ref->flash_offset & ~3), min(ref->totlen, sizeof(node)), &retlen, (void *)&node);
+		/* We can hold a pointer to a non-obsolete node without the spinlock,
+		   but _obsolete_ nodes may disappear at any time, if the block
+		   they're in gets erased */
+		spin_unlock(&c->erase_completion_lock);
+
+		cond_resched();
+
+		/* FIXME: point() */
+		err = jffs2_flash_read(c, (ref_offset(ref)), min_t(uint32_t, ref->totlen, sizeof(node)), &retlen, (void *)&node);
 		if (err) {
-			printk(KERN_WARNING "error %d reading node at 0x%08x in get_inode_nodes()\n", err, (ref->flash_offset) & ~3);
+			printk(KERN_WARNING "error %d reading node at 0x%08x in get_inode_nodes()\n", err, ref_offset(ref));
 			goto free_out;
 		}
 			
 
 			/* Check we've managed to read at least the common node header */
-		if (retlen < min(ref->totlen, sizeof(node.u))) {
+		if (retlen < min_t(uint32_t, ref->totlen, sizeof(node.u))) {
 			printk(KERN_WARNING "short read in get_inode_nodes()\n");
 			err = -EIO;
 			goto free_out;
 		}
 			
-		switch (node.u.nodetype) {
+		switch (je16_to_cpu(node.u.nodetype)) {
 		case JFFS2_NODETYPE_DIRENT:
-			D1(printk(KERN_DEBUG "Node at %08x is a dirent node\n", ref->flash_offset &~3));
+			D1(printk(KERN_DEBUG "Node at %08x (%d) is a dirent node\n", ref_offset(ref), ref_flags(ref)));
+			if (ref_flags(ref) == REF_UNCHECKED) {
+				printk(KERN_WARNING "BUG: Dirent node at 0x%08x never got checked? How?\n", ref_offset(ref));
+				BUG();
+			}
 			if (retlen < sizeof(node.d)) {
 				printk(KERN_WARNING "short read in get_inode_nodes()\n");
 				err = -EIO;
 				goto free_out;
 			}
-			if (node.d.version > *highest_version)
-				*highest_version = node.d.version;
-			if (ref->flash_offset & 1) {
-				/* Obsoleted */
-				continue;
+			if (je32_to_cpu(node.d.version) > *highest_version)
+				*highest_version = je32_to_cpu(node.d.version);
+			if (ref_obsolete(ref)) {
+				/* Obsoleted. This cannot happen, surely? dwmw2 20020308 */
+				printk(KERN_ERR "Dirent node at 0x%08x became obsolete while we weren't looking\n",
+				       ref_offset(ref));
+				BUG();
 			}
 			fd = jffs2_alloc_full_dirent(node.d.nsize+1);
 			if (!fd) {
@@ -151,22 +174,30 @@ int jffs2_get_inode_nodes(struct jffs2_s
 			}
 			memset(fd,0,sizeof(struct jffs2_full_dirent) + node.d.nsize+1);
 			fd->raw = ref;
-			fd->version = node.d.version;
-			fd->ino = node.d.ino;
+			fd->version = je32_to_cpu(node.d.version);
+			fd->ino = je32_to_cpu(node.d.ino);
 			fd->type = node.d.type;
-				/* memcpy as much of the name as possible from the raw
-				   dirent we've already read from the flash
-				*/
+
+			/* Pick out the mctime of the latest dirent */
+			if(fd->version > *mctime_ver) {
+				*mctime_ver = fd->version;
+				*latest_mctime = je32_to_cpu(node.d.mctime);
+			}
+
+			/* memcpy as much of the name as possible from the raw
+			   dirent we've already read from the flash
+			*/
 			if (retlen > sizeof(struct jffs2_raw_dirent))
-				memcpy(&fd->name[0], &node.d.name[0], min((uint32_t)node.d.nsize, (retlen-sizeof(struct jffs2_raw_dirent))));
+				memcpy(&fd->name[0], &node.d.name[0], min_t(uint32_t, node.d.nsize, (retlen-sizeof(struct jffs2_raw_dirent))));
 				
-				/* Do we need to copy any more of the name directly
-				   from the flash?
-				*/
+			/* Do we need to copy any more of the name directly
+			   from the flash?
+			*/
 			if (node.d.nsize + sizeof(struct jffs2_raw_dirent) > retlen) {
+				/* FIXME: point() */
 				int already = retlen - sizeof(struct jffs2_raw_dirent);
 					
-				err = jffs2_flash_read(c, (ref->flash_offset & ~3) + retlen, 
+				err = jffs2_flash_read(c, (ref_offset(ref)) + retlen, 
 						   node.d.nsize - already, &retlen, &fd->name[already]);
 				if (!err && retlen != node.d.nsize - already)
 					err = -EIO;
@@ -187,21 +218,112 @@ int jffs2_get_inode_nodes(struct jffs2_s
 			break;
 
 		case JFFS2_NODETYPE_INODE:
-			D1(printk(KERN_DEBUG "Node at %08x is a data node\n", ref->flash_offset &~3));
+			D1(printk(KERN_DEBUG "Node at %08x (%d) is a data node\n", ref_offset(ref), ref_flags(ref)));
 			if (retlen < sizeof(node.i)) {
 				printk(KERN_WARNING "read too short for dnode\n");
 				err = -EIO;
 				goto free_out;
 			}
-			if (node.d.version > *highest_version)
-				*highest_version = node.i.version;
-			D1(printk(KERN_DEBUG "version %d, highest_version now %d\n", node.d.version, *highest_version));
+			if (je32_to_cpu(node.i.version) > *highest_version)
+				*highest_version = je32_to_cpu(node.i.version);
+			D1(printk(KERN_DEBUG "version %d, highest_version now %d\n", je32_to_cpu(node.i.version), *highest_version));
+
+			if (ref_obsolete(ref)) {
+				/* Obsoleted. This cannot happen, surely? dwmw2 20020308 */
+				printk(KERN_ERR "Inode node at 0x%08x became obsolete while we weren't looking\n",
+				       ref_offset(ref));
+				BUG();
+			}
+
+			/* If we've never checked the CRCs on this node, check them now. */
+			if (ref_flags(ref) == REF_UNCHECKED) {
+				uint32_t crc;
+				struct jffs2_eraseblock *jeb;
+
+				crc = crc32(0, &node, sizeof(node.i)-8);
+				if (crc != je32_to_cpu(node.i.node_crc)) {
+					printk(KERN_NOTICE "jffs2_get_inode_nodes(): CRC failed on node at 0x%08x: Read 0x%08x, calculated 0x%08x\n",
+					       ref_offset(ref), je32_to_cpu(node.i.node_crc), crc);
+					jffs2_mark_node_obsolete(c, ref);
+					spin_lock(&c->erase_completion_lock);
+					continue;
+				}
 
-			if (ref->flash_offset & 1) {
-				D1(printk(KERN_DEBUG "obsoleted\n"));
-				/* Obsoleted */
-				continue;
+				if (node.i.compr != JFFS2_COMPR_ZERO && je32_to_cpu(node.i.csize)) {
+					unsigned char *buf=NULL;
+					uint32_t pointed = 0;
+#ifndef __ECOS
+					if (c->mtd->point) {
+						err = c->mtd->point (c->mtd, ref_offset(ref) + sizeof(node.i), je32_to_cpu(node.i.csize),
+								     &retlen, &buf);
+						if (!err && retlen < je32_to_cpu(node.i.csize)) {
+							D1(printk(KERN_DEBUG "MTD point returned len too short: 0x%zx\n", retlen));
+							c->mtd->unpoint(c->mtd, buf, ref_offset(ref) + sizeof(node.i), je32_to_cpu(node.i.csize));
+						} else if (err){
+							D1(printk(KERN_DEBUG "MTD point failed %d\n", err));
+						} else
+							pointed = 1; /* succefully pointed to device */
+					}
+#endif					
+					if(!pointed){
+						buf = kmalloc(je32_to_cpu(node.i.csize), GFP_KERNEL);
+						if (!buf)
+							return -ENOMEM;
+						
+						err = jffs2_flash_read(c, ref_offset(ref) + sizeof(node.i), je32_to_cpu(node.i.csize),
+								       &retlen, buf);
+						if (!err && retlen != je32_to_cpu(node.i.csize))
+							err = -EIO;
+						if (err) {
+							kfree(buf);
+							return err;
+						}
+					}
+					crc = crc32(0, buf, je32_to_cpu(node.i.csize));
+					if(!pointed)
+						kfree(buf);
+#ifndef __ECOS
+					else
+						c->mtd->unpoint(c->mtd, buf, ref_offset(ref) + sizeof(node.i), je32_to_cpu(node.i.csize));
+#endif
+
+					if (crc != je32_to_cpu(node.i.data_crc)) {
+						printk(KERN_NOTICE "jffs2_get_inode_nodes(): Data CRC failed on node at 0x%08x: Read 0x%08x, calculated 0x%08x\n",
+						       ref_offset(ref), je32_to_cpu(node.i.data_crc), crc);
+						jffs2_mark_node_obsolete(c, ref);
+						spin_lock(&c->erase_completion_lock);
+						continue;
+					}
+					
+				}
+
+				/* Mark the node as having been checked and fix the accounting accordingly */
+				spin_lock(&c->erase_completion_lock);
+				jeb = &c->blocks[ref->flash_offset / c->sector_size];
+				jeb->used_size += ref->totlen;
+				jeb->unchecked_size -= ref->totlen;
+				c->used_size += ref->totlen;
+				c->unchecked_size -= ref->totlen;
+
+				/* If node covers at least a whole page, or if it starts at the 
+				   beginning of a page and runs to the end of the file, or if 
+				   it's a hole node, mark it REF_PRISTINE, else REF_NORMAL. 
+
+				   If it's actually overlapped, it'll get made NORMAL (or OBSOLETE) 
+				   when the overlapping node(s) get added to the tree anyway. 
+				*/
+				if ((je32_to_cpu(node.i.dsize) >= PAGE_CACHE_SIZE) ||
+				    ( ((je32_to_cpu(node.i.offset)&(PAGE_CACHE_SIZE-1))==0) &&
+				      (je32_to_cpu(node.i.dsize)+je32_to_cpu(node.i.offset) ==  je32_to_cpu(node.i.isize)))) {
+					D1(printk(KERN_DEBUG "Marking node at 0x%08x REF_PRISTINE\n", ref_offset(ref)));
+					ref->flash_offset = ref_offset(ref) | REF_PRISTINE;
+				} else {
+					D1(printk(KERN_DEBUG "Marking node at 0x%08x REF_NORMAL\n", ref_offset(ref)));
+					ref->flash_offset = ref_offset(ref) | REF_NORMAL;
+				}
+				spin_unlock(&c->erase_completion_lock);
 			}
+
 			tn = jffs2_alloc_tmp_dnode_info();
 			if (!tn) {
 				D1(printk(KERN_DEBUG "alloc tn failed\n"));
@@ -216,33 +338,76 @@ int jffs2_get_inode_nodes(struct jffs2_s
 				jffs2_free_tmp_dnode_info(tn);
 				goto free_out;
 			}
-			tn->version = node.i.version;
-			tn->fn->ofs = node.i.offset;
-			tn->fn->size = node.i.dsize;
+			tn->version = je32_to_cpu(node.i.version);
+			tn->fn->ofs = je32_to_cpu(node.i.offset);
+			/* There was a bug where we wrote hole nodes out with
+			   csize/dsize swapped. Deal with it */
+			if (node.i.compr == JFFS2_COMPR_ZERO && !je32_to_cpu(node.i.dsize) && je32_to_cpu(node.i.csize))
+				tn->fn->size = je32_to_cpu(node.i.csize);
+			else // normal case...
+				tn->fn->size = je32_to_cpu(node.i.dsize);
 			tn->fn->raw = ref;
-			D1(printk(KERN_DEBUG "dnode @%08x: ver %u, offset %04x, dsize %04x\n", ref->flash_offset &~3, node.i.version, node.i.offset, node.i.dsize));
+			D1(printk(KERN_DEBUG "dnode @%08x: ver %u, offset %04x, dsize %04x\n",
+				  ref_offset(ref), je32_to_cpu(node.i.version),
+				  je32_to_cpu(node.i.offset), je32_to_cpu(node.i.dsize)));
 			jffs2_add_tn_to_list(tn, &ret_tn);
 			break;
 
 		default:
-			switch(node.u.nodetype & JFFS2_COMPAT_MASK) {
+			if (ref_flags(ref) == REF_UNCHECKED) {
+				struct jffs2_eraseblock *jeb;
+
+				printk(KERN_ERR "Eep. Unknown node type %04x at %08x was marked REF_UNCHECKED\n",
+				       je16_to_cpu(node.u.nodetype), ref_offset(ref));
+
+				/* Mark the node as having been checked and fix the accounting accordingly */
+				spin_lock(&c->erase_completion_lock);
+				jeb = &c->blocks[ref->flash_offset / c->sector_size];
+				jeb->used_size += ref->totlen;
+				jeb->unchecked_size -= ref->totlen;
+				c->used_size += ref->totlen;
+				c->unchecked_size -= ref->totlen;
+
+				mark_ref_normal(ref);
+				spin_unlock(&c->erase_completion_lock);
+			}
+			node.u.nodetype = cpu_to_je16(JFFS2_NODE_ACCURATE | je16_to_cpu(node.u.nodetype));
+			if (crc32(0, &node, sizeof(struct jffs2_unknown_node)-4) != je32_to_cpu(node.u.hdr_crc)) {
+				/* Hmmm. This should have been caught at scan time. */
+				printk(KERN_ERR "Node header CRC failed at %08x. But it must have been OK earlier.\n",
+				       ref_offset(ref));
+				printk(KERN_ERR "Node was: { %04x, %04x, %08x, %08x }\n", 
+				       je16_to_cpu(node.u.magic), je16_to_cpu(node.u.nodetype), je32_to_cpu(node.u.totlen),
+				       je32_to_cpu(node.u.hdr_crc));
+				jffs2_mark_node_obsolete(c, ref);
+			} else switch(je16_to_cpu(node.u.nodetype) & JFFS2_COMPAT_MASK) {
 			case JFFS2_FEATURE_INCOMPAT:
-				printk(KERN_NOTICE "Unknown INCOMPAT nodetype %04X at %08X\n", node.u.nodetype, ref->flash_offset & ~3);
+				printk(KERN_NOTICE "Unknown INCOMPAT nodetype %04X at %08x\n", je16_to_cpu(node.u.nodetype), ref_offset(ref));
+				/* EEP */
+				BUG();
 				break;
 			case JFFS2_FEATURE_ROCOMPAT:
-				printk(KERN_NOTICE "Unknown ROCOMPAT nodetype %04X at %08X\n", node.u.nodetype, ref->flash_offset & ~3);
+				printk(KERN_NOTICE "Unknown ROCOMPAT nodetype %04X at %08x\n", je16_to_cpu(node.u.nodetype), ref_offset(ref));
+				if (!(c->flags & JFFS2_SB_FLAG_RO))
+					BUG();
 				break;
 			case JFFS2_FEATURE_RWCOMPAT_COPY:
-				printk(KERN_NOTICE "Unknown RWCOMPAT_COPY nodetype %04X at %08X\n", node.u.nodetype, ref->flash_offset & ~3);
+				printk(KERN_NOTICE "Unknown RWCOMPAT_COPY nodetype %04X at %08x\n", je16_to_cpu(node.u.nodetype), ref_offset(ref));
 				break;
 			case JFFS2_FEATURE_RWCOMPAT_DELETE:
-				printk(KERN_NOTICE "Unknown RWCOMPAT_DELETE nodetype %04X at %08X\n", node.u.nodetype, ref->flash_offset & ~3);
+				printk(KERN_NOTICE "Unknown RWCOMPAT_DELETE nodetype %04X at %08x\n", je16_to_cpu(node.u.nodetype), ref_offset(ref));
+				jffs2_mark_node_obsolete(c, ref);
 				break;
 			}
+
 		}
+		spin_lock(&c->erase_completion_lock);
+
 	}
+	spin_unlock(&c->erase_completion_lock);
 	*tnp = ret_tn;
 	*fdp = ret_fd;
+
 	return 0;
 
  free_out:
@@ -251,19 +416,30 @@ int jffs2_get_inode_nodes(struct jffs2_s
 	return err;
 }
 
+void jffs2_set_inocache_state(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic, int state)
+{
+	spin_lock(&c->inocache_lock);
+	ic->state = state;
+	wake_up(&c->inocache_wq);
+	spin_unlock(&c->inocache_lock);
+}
+
+/* During mount, this needs no locking. During normal operation, its
+   callers want to do other stuff while still holding the inocache_lock.
+   Rather than introducing special case get_ino_cache functions or 
+   callbacks, we just let the caller do the locking itself. */
+   
 struct jffs2_inode_cache *jffs2_get_ino_cache(struct jffs2_sb_info *c, int ino)
 {
 	struct jffs2_inode_cache *ret;
 
 	D2(printk(KERN_DEBUG "jffs2_get_ino_cache(): ino %u\n", ino));
-	spin_lock (&c->inocache_lock);
+
 	ret = c->inocache_list[ino % INOCACHE_HASHSIZE];
 	while (ret && ret->ino < ino) {
 		ret = ret->next;
 	}
-
-	spin_unlock(&c->inocache_lock);
-
+	
 	if (ret && ret->ino != ino)
 		ret = NULL;
 
@@ -284,6 +460,7 @@ void jffs2_add_ino_cache (struct jffs2_s
 	}
 	new->next = *prev;
 	*prev = new;
+
 	spin_unlock(&c->inocache_lock);
 }
 
@@ -301,6 +478,7 @@ void jffs2_del_ino_cache(struct jffs2_sb
 	if ((*prev) == old) {
 		*prev = old->next;
 	}
+
 	spin_unlock(&c->inocache_lock);
 }
 
@@ -337,3 +515,126 @@ void jffs2_free_raw_node_refs(struct jff
 	}
 }
 	
+struct jffs2_node_frag *jffs2_lookup_node_frag(struct rb_root *fragtree, uint32_t offset)
+{
+	/* The common case in lookup is that there will be a node 
+	   which precisely matches. So we go looking for that first */
+	struct rb_node *next;
+	struct jffs2_node_frag *prev = NULL;
+	struct jffs2_node_frag *frag = NULL;
+
+	D2(printk(KERN_DEBUG "jffs2_lookup_node_frag(%p, %d)\n", fragtree, offset));
+
+	next = fragtree->rb_node;
+
+	while(next) {
+		frag = rb_entry(next, struct jffs2_node_frag, rb);
+
+		D2(printk(KERN_DEBUG "Considering frag %d-%d (%p). left %p, right %p\n",
+			  frag->ofs, frag->ofs+frag->size, frag, frag->rb.rb_left, frag->rb.rb_right));
+		if (frag->ofs + frag->size <= offset) {
+			D2(printk(KERN_DEBUG "Going right from frag %d-%d, before the region we care about\n",
+				  frag->ofs, frag->ofs+frag->size));
+			/* Remember the closest smaller match on the way down */
+			if (!prev || frag->ofs > prev->ofs)
+				prev = frag;
+			next = frag->rb.rb_right;
+		} else if (frag->ofs > offset) {
+			D2(printk(KERN_DEBUG "Going left from frag %d-%d, after the region we care about\n",
+				  frag->ofs, frag->ofs+frag->size));
+			next = frag->rb.rb_left;
+		} else {
+			D2(printk(KERN_DEBUG "Returning frag %d,%d, matched\n",
+				  frag->ofs, frag->ofs+frag->size));
+			return frag;
+		}
+	}
+
+	/* Exact match not found. Go back up looking at each parent,
+	   and return the closest smaller one */
+
+	if (prev)
+		D2(printk(KERN_DEBUG "No match. Returning frag %d,%d, closest previous\n",
+			  prev->ofs, prev->ofs+prev->size));
+	else 
+		D2(printk(KERN_DEBUG "Returning NULL, empty fragtree\n"));
+	
+	return prev;
+}
+
+/* Pass 'c' argument to indicate that nodes should be marked obsolete as
+   they're killed. */
+void jffs2_kill_fragtree(struct rb_root *root, struct jffs2_sb_info *c)
+{
+	struct jffs2_node_frag *frag;
+	struct jffs2_node_frag *parent;
+
+	if (!root->rb_node)
+		return;
+
+	frag = (rb_entry(root->rb_node, struct jffs2_node_frag, rb));
+
+	while(frag) {
+		if (frag->rb.rb_left) {
+			D2(printk(KERN_DEBUG "Going left from frag (%p) %d-%d\n", 
+				  frag, frag->ofs, frag->ofs+frag->size));
+			frag = frag_left(frag);
+			continue;
+		}
+		if (frag->rb.rb_right) {
+			D2(printk(KERN_DEBUG "Going right from frag (%p) %d-%d\n", 
+				  frag, frag->ofs, frag->ofs+frag->size));
+			frag = frag_right(frag);
+			continue;
+		}
+
+		D2(printk(KERN_DEBUG "jffs2_kill_fragtree: frag at 0x%x-0x%x: node %p, frags %d--\n",
+			  frag->ofs, frag->ofs+frag->size, frag->node,
+			  frag->node?frag->node->frags:0));
+			
+		if (frag->node && !(--frag->node->frags)) {
+			/* Not a hole, and it's the final remaining frag 
+			   of this node. Free the node */
+			if (c)
+				jffs2_mark_node_obsolete(c, frag->node->raw);
+			
+			jffs2_free_full_dnode(frag->node);
+		}
+		parent = frag_parent(frag);
+		if (parent) {
+			if (frag_left(parent) == frag)
+				parent->rb.rb_left = NULL;
+			else 
+				parent->rb.rb_right = NULL;
+		}
+
+		jffs2_free_node_frag(frag);
+		frag = parent;
+	}
+}
+
+void jffs2_fragtree_insert(struct jffs2_node_frag *newfrag, struct jffs2_node_frag *base)
+{
+	struct rb_node *parent = &base->rb;
+	struct rb_node **link = &parent;
+
+	D2(printk(KERN_DEBUG "jffs2_fragtree_insert(%p; %d-%d, %p)\n", newfrag, 
+		  newfrag->ofs, newfrag->ofs+newfrag->size, base));
+
+	while (*link) {
+		parent = *link;
+		base = rb_entry(parent, struct jffs2_node_frag, rb);
+	
+		D2(printk(KERN_DEBUG "fragtree_insert considering frag at 0x%x\n", base->ofs));
+		if (newfrag->ofs > base->ofs)
+			link = &base->rb.rb_right;
+		else if (newfrag->ofs < base->ofs)
+			link = &base->rb.rb_left;
+		else {
+			printk(KERN_CRIT "Duplicate frag at %08x (%p,%p)\n", newfrag->ofs, newfrag, base);
+			BUG();
+		}
+	}
+
+	rb_link_node(&newfrag->rb, &base->rb, link);
+}
--- a/packages/fs/jffs2/current/src/nodelist.h
+++ b/packages/fs/jffs2/current/src/nodelist.h
@@ -7,31 +7,27 @@
  *
  * For licensing information, see the file 'LICENCE' in this directory.
  *
- * $Id: nodelist.h,v 1.60 2002/01/24 00:45:11 dwmw2 Exp $
+ * $Id: nodelist.h,v 1.92 2003/01/22 14:43:29 dwmw2 Exp $
  *
  */
 
 #ifndef __JFFS2_NODELIST_H__
 #define __JFFS2_NODELIST_H__
 
-#ifdef __ECOS
-#include "jffs2port.h"
-#include "jffs2.h"
-#include "jffs2_fs_sb.h"
-#include "jffs2_fs_i.h"
-#include "os-ecos.h"
-#else 
 #include <linux/config.h>
 #include <linux/fs.h>
-
-#include <linux/mtd/compatmac.h> /* For min/max in older kernels */
+#include <linux/types.h>
 #include <linux/jffs2.h>
 #include <linux/jffs2_fs_sb.h>
 #include <linux/jffs2_fs_i.h>
+
+#ifdef __ECOS
+#include "os-ecos.h"
+#else
+#include <linux/mtd/compatmac.h> /* For min/max in older kernels */
 #include "os-linux.h"
 #endif
 
-
 #ifndef CONFIG_JFFS2_FS_DEBUG
 #define CONFIG_JFFS2_FS_DEBUG 2
 #endif
@@ -62,16 +58,22 @@ struct jffs2_raw_node_ref
 		for this inode instead. The inode_cache will have NULL in the first
 		word so you know when you've got there :) */
 	struct jffs2_raw_node_ref *next_phys;
-	//	uint32_t ino;
 	uint32_t flash_offset;
 	uint32_t totlen;
-//	uint16_t nodetype;
 	
         /* flash_offset & 3 always has to be zero, because nodes are
 	   always aligned at 4 bytes. So we have a couple of extra bits
 	   to play with. So we set the least significant bit to 1 to
 	   signify that the node is obsoleted by later nodes.
 	*/
+#define REF_UNCHECKED	0	/* We haven't yet checked the CRC or built its inode */
+#define REF_OBSOLETE	1	/* Obsolete, can be completely ignored */
+#define REF_PRISTINE	2	/* Completely clean. GC without looking */
+#define REF_NORMAL	3	/* Possibly overlapped. Read the page and write again on GC */
+#define ref_flags(ref)		((ref)->flash_offset & 3)
+#define ref_offset(ref)		((ref)->flash_offset & ~3)
+#define ref_obsolete(ref)	(((ref)->flash_offset & 3) == REF_OBSOLETE)
+#define mark_ref_normal(ref)    do { (ref)->flash_offset = ref_offset(ref) | REF_NORMAL; } while(0)
 };
 
 /* 
@@ -92,19 +94,35 @@ struct jffs2_raw_node_ref_list {
    a pointer to the first physical node which is part of this inode, too.
 */
 struct jffs2_inode_cache {
-	struct jffs2_scan_info *scan; /* Used during scan to hold
-		temporary lists of nodes, and later must be set to
+	struct jffs2_full_dirent *scan_dents; /* Used during scan to hold
+		temporary lists of dirents, and later must be set to
 		NULL to mark the end of the raw_node_ref->next_in_ino
 		chain. */
 	struct jffs2_inode_cache *next;
 	struct jffs2_raw_node_ref *nodes;
 	uint32_t ino;
 	int nlink;
+	int state;
 };
 
+/* Inode states for 'state' above. We need the 'GC' state to prevent
+   someone from doing a read_inode() while we're moving a 'REF_PRISTINE'
+   node without going through all the iget() nonsense */
+#define INO_STATE_UNCHECKED	0	/* CRC checks not yet done */
+#define INO_STATE_CHECKING	1	/* CRC checks in progress */
+#define INO_STATE_PRESENT	2	/* In core */
+#define INO_STATE_CHECKEDABSENT	3	/* Checked, cleared again */
+#define INO_STATE_GC		4	/* GCing a 'pristine' node */
+#define INO_STATE_READING	5	/* In read_inode() */
+
+#define INOCACHE_HASHSIZE 128
+
 struct jffs2_scan_info {
 	struct jffs2_full_dirent *dents;
 	struct jffs2_tmp_dnode_info *tmpnodes;
+	/* Latest i_size info */
+	uint32_t version;
+	uint32_t isize;
 };
 /*
   Larger representation of a raw node, kept in-core only when the 
@@ -150,11 +168,10 @@ struct jffs2_full_dirent
 */
 struct jffs2_node_frag
 {
-	struct jffs2_node_frag *next;
+	struct rb_node rb;
 	struct jffs2_full_dnode *node; /* NULL for holes */
 	uint32_t size;
 	uint32_t ofs; /* Don't really need this, but optimisation */
-	uint32_t node_ofs; /* offset within the physical node */
 };
 
 struct jffs2_eraseblock
@@ -163,8 +180,10 @@ struct jffs2_eraseblock
 	int bad_count;
 	uint32_t offset;		/* of this block in the MTD */
 
+	uint32_t unchecked_size;
 	uint32_t used_size;
 	uint32_t dirty_size;
+	uint32_t wasted_size;
 	uint32_t free_size;	/* Note that sector_size - free_size
 				   is the address of the first free space */
 	struct jffs2_raw_node_ref *first_node;
@@ -182,25 +201,28 @@ struct jffs2_eraseblock
 };
 
 #define ACCT_SANITY_CHECK(c, jeb) do { \
-	if (jeb->used_size + jeb->dirty_size + jeb->free_size != c->sector_size) { \
+	if (jeb->used_size + jeb->dirty_size + jeb->free_size + jeb->wasted_size + jeb->unchecked_size != c->sector_size) { \
 		printk(KERN_NOTICE "Eeep. Space accounting for block at 0x%08x is screwed\n", jeb->offset); \
-		printk(KERN_NOTICE "free 0x%08x + dirty 0x%08x + used %08x != total %08x\n", \
-		jeb->free_size, jeb->dirty_size, jeb->used_size, c->sector_size); \
+		printk(KERN_NOTICE "free 0x%08x + dirty 0x%08x + used %08x + wasted %08x + unchecked %08x != total %08x\n", \
+		jeb->free_size, jeb->dirty_size, jeb->used_size, jeb->wasted_size, jeb->unchecked_size, c->sector_size); \
 		BUG(); \
 	} \
-	if (c->used_size + c->dirty_size + c->free_size + c->erasing_size + c->bad_size != c->flash_size) { \
+	if (c->used_size + c->dirty_size + c->free_size + c->erasing_size + c->bad_size + c->wasted_size + c->unchecked_size != c->flash_size) { \
 		printk(KERN_NOTICE "Eeep. Space accounting superblock info is screwed\n"); \
-		printk(KERN_NOTICE "free 0x%08x + dirty 0x%08x + used %08x + erasing %08x + bad %08x != total %08x\n", \
-		c->free_size, c->dirty_size, c->used_size, c->erasing_size, c->bad_size, c->flash_size); \
+		printk(KERN_NOTICE "free 0x%08x + dirty 0x%08x + used %08x + erasing %08x + bad %08x + wasted %08x + unchecked %08x != total %08x\n", \
+		c->free_size, c->dirty_size, c->used_size, c->erasing_size, c->bad_size, c->wasted_size, c->unchecked_size, c->flash_size); \
 		BUG(); \
 	} \
 } while(0)
 
 #define ACCT_PARANOIA_CHECK(jeb) do { \
 		uint32_t my_used_size = 0; \
+		uint32_t my_unchecked_size = 0; \
 		struct jffs2_raw_node_ref *ref2 = jeb->first_node; \
 		while (ref2) { \
-			if (!(ref2->flash_offset & 1)) \
+			if (ref_flags(ref2) == REF_UNCHECKED) \
+				my_unchecked_size += ref2->totlen; \
+			else if (!ref_obsolete(ref2)) \
 				my_used_size += ref2->totlen; \
 			ref2 = ref2->next_phys; \
 		} \
@@ -208,6 +230,10 @@ struct jffs2_eraseblock
 			printk(KERN_NOTICE "Calculated used size %08x != stored used size %08x\n", my_used_size, jeb->used_size); \
 			BUG(); \
 		} \
+		if (my_unchecked_size != jeb->unchecked_size) { \
+			printk(KERN_NOTICE "Calculated unchecked size %08x != stored unchecked size %08x\n", my_unchecked_size, jeb->unchecked_size); \
+			BUG(); \
+		} \
 	} while(0)
 
 #define ALLOC_NORMAL	0	/* Normal allocation */
@@ -216,12 +242,18 @@ struct jffs2_eraseblock
 
 #define JFFS2_RESERVED_BLOCKS_BASE 3						/* Number of free blocks there must be before we... */
 #define JFFS2_RESERVED_BLOCKS_WRITE (JFFS2_RESERVED_BLOCKS_BASE + 2)		/* ... allow a normal filesystem write */
-#define JFFS2_RESERVED_BLOCKS_DELETION (JFFS2_RESERVED_BLOCKS_BASE + 1)		/* ... allow a normal filesystem deletion */
+#define JFFS2_RESERVED_BLOCKS_DELETION (JFFS2_RESERVED_BLOCKS_BASE)		/* ... allow a normal filesystem deletion */
 #define JFFS2_RESERVED_BLOCKS_GCTRIGGER (JFFS2_RESERVED_BLOCKS_BASE + 3)	/* ... wake up the GC thread */
 #define JFFS2_RESERVED_BLOCKS_GCBAD (JFFS2_RESERVED_BLOCKS_BASE + 1)		/* ... pick a block from the bad_list to GC */
 #define JFFS2_RESERVED_BLOCKS_GCMERGE (JFFS2_RESERVED_BLOCKS_BASE)		/* ... merge pages when garbage collecting */
 
 
+/* How much dirty space before it goes on the very_dirty_list */
+#define VERYDIRTY(c, size) ((size) >= ((c)->sector_size / 2))
+
+/* check if dirty space is more than 255 Byte */
+#define ISDIRTY(size) ((size) >  sizeof (struct jffs2_raw_inode) + JFFS2_MIN_DATA_LEN) 
+
 #define PAD(x) (((x)+3)&~3)
 
 static inline int jffs2_raw_ref_to_inum(struct jffs2_raw_node_ref *raw)
@@ -233,28 +265,55 @@ static inline int jffs2_raw_ref_to_inum(
 	return ((struct jffs2_inode_cache *)raw)->ino;
 }
 
+static inline struct jffs2_node_frag *frag_first(struct rb_root *root)
+{
+	struct rb_node *node = root->rb_node;
+
+	if (!node)
+		return NULL;
+	while(node->rb_left)
+		node = node->rb_left;
+	return rb_entry(node, struct jffs2_node_frag, rb);
+}
+#define rb_parent(rb) ((rb)->rb_parent)
+#define frag_next(frag) rb_entry(rb_next(&(frag)->rb), struct jffs2_node_frag, rb)
+#define frag_prev(frag) rb_entry(rb_prev(&(frag)->rb), struct jffs2_node_frag, rb)
+#define frag_parent(frag) rb_entry(rb_parent(&(frag)->rb), struct jffs2_node_frag, rb)
+#define frag_left(frag) rb_entry((frag)->rb.rb_left, struct jffs2_node_frag, rb)
+#define frag_right(frag) rb_entry((frag)->rb.rb_right, struct jffs2_node_frag, rb)
+#define frag_erase(frag, list) rb_erase(&frag->rb, list);
+
 /* nodelist.c */
 D1(void jffs2_print_frag_list(struct jffs2_inode_info *f));
 void jffs2_add_fd_to_list(struct jffs2_sb_info *c, struct jffs2_full_dirent *new, struct jffs2_full_dirent **list);
 void jffs2_add_tn_to_list(struct jffs2_tmp_dnode_info *tn, struct jffs2_tmp_dnode_info **list);
 int jffs2_get_inode_nodes(struct jffs2_sb_info *c, ino_t ino, struct jffs2_inode_info *f,
 			  struct jffs2_tmp_dnode_info **tnp, struct jffs2_full_dirent **fdp,
-			  uint32_t *highest_version);
+			  uint32_t *highest_version, uint32_t *latest_mctime,
+			  uint32_t *mctime_ver);
+void jffs2_set_inocache_state(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic, int state);
 struct jffs2_inode_cache *jffs2_get_ino_cache(struct jffs2_sb_info *c, int ino);
 void jffs2_add_ino_cache (struct jffs2_sb_info *c, struct jffs2_inode_cache *new);
 void jffs2_del_ino_cache(struct jffs2_sb_info *c, struct jffs2_inode_cache *old);
 void jffs2_free_ino_caches(struct jffs2_sb_info *c);
 void jffs2_free_raw_node_refs(struct jffs2_sb_info *c);
+struct jffs2_node_frag *jffs2_lookup_node_frag(struct rb_root *fragtree, uint32_t offset);
+void jffs2_kill_fragtree(struct rb_root *root, struct jffs2_sb_info *c_delete);
+void jffs2_fragtree_insert(struct jffs2_node_frag *newfrag, struct jffs2_node_frag *base);
+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_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, uint32_t len, int dirty);
+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);
+void jffs2_dump_block_lists(struct jffs2_sb_info *c);
 
 /* write.c */
-struct inode *jffs2_new_inode (struct inode *dir_i, int mode, struct jffs2_raw_inode *ri);
+int jffs2_do_new_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, uint32_t mode, struct jffs2_raw_inode *ri);
 struct jffs2_full_dnode *jffs2_write_dnode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, struct jffs2_raw_inode *ri, const unsigned char *data, uint32_t datalen, uint32_t flash_ofs,  uint32_t *writelen);
 struct jffs2_full_dirent *jffs2_write_dirent(struct jffs2_sb_info *c, struct jffs2_inode_info *f, struct jffs2_raw_dirent *rd, const unsigned char *name, uint32_t namelen, uint32_t flash_ofs,  uint32_t *writelen);
 int jffs2_write_inode_range(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
@@ -266,13 +325,11 @@ int jffs2_do_link (struct jffs2_sb_info 
 
 
 /* readinode.c */
-void jffs2_truncate_fraglist (struct jffs2_sb_info *c, struct jffs2_node_frag **list, uint32_t size);
-int jffs2_add_full_dnode_to_fraglist(struct jffs2_sb_info *c, struct jffs2_node_frag **list, struct jffs2_full_dnode *fn);
+void jffs2_truncate_fraglist (struct jffs2_sb_info *c, struct rb_root *list, uint32_t size);
 int jffs2_add_full_dnode_to_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, struct jffs2_full_dnode *fn);
-void jffs2_read_inode (struct inode *);
 int jffs2_do_read_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, 
 			uint32_t ino, struct jffs2_raw_inode *latest_node);
-void jffs2_clear_inode (struct inode *);
+int jffs2_do_crccheck_inode(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic);
 void jffs2_do_clear_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f);
 
 /* malloc.c */
@@ -314,6 +371,7 @@ int jffs2_decompress(unsigned char compr
 
 /* scan.c */
 int jffs2_scan_medium(struct jffs2_sb_info *c);
+void jffs2_rotate_lists(struct jffs2_sb_info *c);
 
 /* build.c */
 int jffs2_do_mount_fs(struct jffs2_sb_info *c);
@@ -321,7 +379,18 @@ int jffs2_do_mount_fs(struct jffs2_sb_in
 /* erase.c */
 void jffs2_erase_block(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb);
 void jffs2_erase_pending_blocks(struct jffs2_sb_info *c);
-void jffs2_mark_erased_blocks(struct jffs2_sb_info *c);
 void jffs2_erase_pending_trigger(struct jffs2_sb_info *c);
 
+#ifdef CONFIG_JFFS2_FS_NAND
+/* wbuf.c */
+int jffs2_flush_wbuf(struct jffs2_sb_info *c, int pad);
+int jffs2_check_nand_cleanmarker(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb);
+int jffs2_write_nand_cleanmarker(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb);
+int jffs2_nand_read_failcnt(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb);
+#endif
+
+/* compr_zlib.c */
+int jffs2_zlib_init(void);
+void jffs2_zlib_exit(void);
+
 #endif /* __JFFS2_NODELIST_H__ */
--- a/packages/fs/jffs2/current/src/nodemgmt.c
+++ b/packages/fs/jffs2/current/src/nodemgmt.c
@@ -7,16 +7,15 @@
  *
  * For licensing information, see the file 'LICENCE' in this directory.
  *
- * $Id: nodemgmt.c,v 1.52 2002/01/19 23:16:41 dwmw2 Exp $
+ * $Id: nodemgmt.c,v 1.92 2003/01/22 14:40:26 dwmw2 Exp $
  *
  */
 
-#ifndef __ECOS
 #include <linux/kernel.h>
 #include <linux/slab.h>
 #include <linux/mtd/mtd.h>
-#include <linux/interrupt.h>
-#endif
+#include <linux/compiler.h>
+#include <linux/sched.h> /* For cond_resched() */
 #include "nodelist.h"
 
 /**
@@ -56,7 +55,7 @@ int jffs2_reserve_space(struct jffs2_sb_
 
 	D1(printk(KERN_DEBUG "jffs2_reserve_space(): alloc sem got\n"));
 
-	spin_lock_bh(&c->erase_completion_lock);
+	spin_lock(&c->erase_completion_lock);
 
 	/* this needs a little more thought */
 	while(ret == -EAGAIN) {
@@ -64,28 +63,30 @@ int jffs2_reserve_space(struct jffs2_sb_
 			int ret;
 
 			up(&c->alloc_sem);
-			if (c->dirty_size < c->sector_size) {
-				D1(printk(KERN_DEBUG "Short on space, but total dirty size 0x%08x < sector size 0x%08x, so -ENOSPC\n", c->dirty_size, c->sector_size));
-				spin_unlock_bh(&c->erase_completion_lock);
+			
+			if (c->dirty_size + c->unchecked_size < c->sector_size) {
+				D1(printk(KERN_DEBUG "dirty size 0x%08x + unchecked_size 0x%08x < sector size 0x%08x, returning -ENOSPC\n",
+					  c->dirty_size, c->unchecked_size, c->sector_size));
+				spin_unlock(&c->erase_completion_lock);
 				return -ENOSPC;
 			}
-			D1(printk(KERN_DEBUG "Triggering GC pass. nr_free_blocks %d, nr_erasing_blocks %d, free_size 0x%08x, dirty_size 0x%08x, used_size 0x%08x, erasing_size 0x%08x, bad_size 0x%08x (total 0x%08x of 0x%08x)\n",
-				  c->nr_free_blocks, c->nr_erasing_blocks, c->free_size, c->dirty_size, c->used_size, c->erasing_size, c->bad_size,
-				  c->free_size + c->dirty_size + c->used_size + c->erasing_size + c->bad_size, c->flash_size));
-			spin_unlock_bh(&c->erase_completion_lock);
+			
+			D1(printk(KERN_DEBUG "Triggering GC pass. nr_free_blocks %d, nr_erasing_blocks %d, free_size 0x%08x, dirty_size 0x%08x, wasted_size 0x%08x, used_size 0x%08x, erasing_size 0x%08x, bad_size 0x%08x (total 0x%08x of 0x%08x)\n",
+				  c->nr_free_blocks, c->nr_erasing_blocks, c->free_size, c->dirty_size, c->wasted_size, c->used_size, c->erasing_size, c->bad_size,
+				  c->free_size + c->dirty_size + c->wasted_size + c->used_size + c->erasing_size + c->bad_size, c->flash_size));
+			spin_unlock(&c->erase_completion_lock);
 			
 			ret = jffs2_garbage_collect_pass(c);
 			if (ret)
 				return ret;
-#ifndef __ECOS
-			if (current->need_resched)
-				schedule();
+
+			cond_resched();
 
 			if (signal_pending(current))
 				return -EINTR;
-#endif
+
 			down(&c->alloc_sem);
-			spin_lock_bh(&c->erase_completion_lock);
+			spin_lock(&c->erase_completion_lock);
 		}
 
 		ret = jffs2_do_reserve_space(c, minsize, ofs, len);
@@ -93,7 +94,7 @@ int jffs2_reserve_space(struct jffs2_sb_
 			D1(printk(KERN_DEBUG "jffs2_reserve_space: ret is %d\n", ret));
 		}
 	}
-	spin_unlock_bh(&c->erase_completion_lock);
+	spin_unlock(&c->erase_completion_lock);
 	if (ret)
 		up(&c->alloc_sem);
 	return ret;
@@ -106,14 +107,14 @@ int jffs2_reserve_space_gc(struct jffs2_
 
 	D1(printk(KERN_DEBUG "jffs2_reserve_space_gc(): Requested 0x%x bytes\n", minsize));
 
-	spin_lock_bh(&c->erase_completion_lock);
+	spin_lock(&c->erase_completion_lock);
 	while(ret == -EAGAIN) {
 		ret = jffs2_do_reserve_space(c, minsize, ofs, len);
 		if (ret) {
 		        D1(printk(KERN_DEBUG "jffs2_reserve_space_gc: looping, ret is %d\n", ret));
 		}
 	}
-	spin_unlock_bh(&c->erase_completion_lock);
+	spin_unlock(&c->erase_completion_lock);
 	return ret;
 }
 
@@ -125,13 +126,39 @@ static int jffs2_do_reserve_space(struct
  restart:
 	if (jeb && minsize > jeb->free_size) {
 		/* Skip the end of this block and file it as having some dirty space */
-		c->dirty_size += jeb->free_size;
+		/* If there's a pending write to it, flush now */
+		if (c->wbuf_len) {
+			spin_unlock(&c->erase_completion_lock);
+			D1(printk(KERN_DEBUG "jffs2_do_reserve_space: Flushing write buffer\n"));			    
+			jffs2_flush_wbuf(c, 1);
+			spin_lock(&c->erase_completion_lock);
+			/* We know nobody's going to have changed nextblock. Just continue */
+		}
+		c->wasted_size += jeb->free_size;
 		c->free_size -= jeb->free_size;
-		jeb->dirty_size += jeb->free_size;
+		jeb->wasted_size += jeb->free_size;
 		jeb->free_size = 0;
-		D1(printk(KERN_DEBUG "Adding full erase block at 0x%08x to dirty_list (free 0x%08x, dirty 0x%08x, used 0x%08x\n",
+		
+		/* Check, if we have a dirty block now, or if it was dirty already */
+		if (ISDIRTY (jeb->wasted_size + jeb->dirty_size)) {
+			c->dirty_size += jeb->wasted_size;
+			c->wasted_size -= jeb->wasted_size;
+			jeb->dirty_size += jeb->wasted_size;
+			jeb->wasted_size = 0;
+			if (VERYDIRTY(c, jeb->dirty_size)) {
+				D1(printk(KERN_DEBUG "Adding full erase block at 0x%08x to very_dirty_list (free 0x%08x, dirty 0x%08x, used 0x%08x\n",
+				  jeb->offset, jeb->free_size, jeb->dirty_size, jeb->used_size));
+				list_add_tail(&jeb->list, &c->very_dirty_list);
+			} else {
+				D1(printk(KERN_DEBUG "Adding full erase block at 0x%08x to dirty_list (free 0x%08x, dirty 0x%08x, used 0x%08x\n",
+				  jeb->offset, jeb->free_size, jeb->dirty_size, jeb->used_size));
+				list_add_tail(&jeb->list, &c->dirty_list);
+			}
+		} else { 
+			D1(printk(KERN_DEBUG "Adding full erase block at 0x%08x to clean_list (free 0x%08x, dirty 0x%08x, used 0x%08x\n",
 			  jeb->offset, jeb->free_size, jeb->dirty_size, jeb->used_size));
-		list_add_tail(&jeb->list, &c->dirty_list);
+			list_add_tail(&jeb->list, &c->clean_list);
+		}
 		c->nextblock = jeb = NULL;
 	}
 	
@@ -143,12 +170,36 @@ static int jffs2_do_reserve_space(struct
 
 			DECLARE_WAITQUEUE(wait, current);
 			
+			if (!c->nr_erasing_blocks && 
+			    !list_empty(&c->erasable_list)) {
+				struct jffs2_eraseblock *ejeb;
+
+				ejeb = list_entry(c->erasable_list.next, struct jffs2_eraseblock, list);
+				list_del(&ejeb->list);
+				list_add_tail(&ejeb->list, &c->erase_pending_list);
+				c->nr_erasing_blocks++;
+				jffs2_erase_pending_trigger(c);
+				D1(printk(KERN_DEBUG "jffs2_do_reserve_space: Triggering erase of erasable block at 0x%08x\n",
+					  ejeb->offset));
+			}
+
+			if (!c->nr_erasing_blocks && 
+			    !list_empty(&c->erasable_pending_wbuf_list)) {
+				D1(printk(KERN_DEBUG "jffs2_do_reserve_space: Flushing write buffer\n"));
+				/* c->nextblock is NULL, no update to c->nextblock allowed */			    
+				spin_unlock(&c->erase_completion_lock);
+				jffs2_flush_wbuf(c, 1);
+				spin_lock(&c->erase_completion_lock);
+				/* Have another go. It'll be on the erasable_list now */
+				return -EAGAIN;
+			}
+
 			if (!c->nr_erasing_blocks) {
-//			if (list_empty(&c->erasing_list) && list_empty(&c->erase_pending_list) && list_empty(c->erase_complete_list)) {
 				/* Ouch. We're in GC, or we wouldn't have got here.
 				   And there's no space left. At all. */
-				printk(KERN_CRIT "Argh. No free space left for GC. nr_erasing_blocks is %d. nr_free_blocks is %d. (erasingempty: %s, erasependingempty: %s)\n", 
-				       c->nr_erasing_blocks, c->nr_free_blocks, list_empty(&c->erasing_list)?"yes":"no", list_empty(&c->erase_pending_list)?"yes":"no");
+				printk(KERN_CRIT "Argh. No free space left for GC. nr_erasing_blocks is %d. nr_free_blocks is %d. (erasableempty: %s, erasingempty: %s, erasependingempty: %s)\n", 
+				       c->nr_erasing_blocks, c->nr_free_blocks, list_empty(&c->erasable_list)?"yes":"no", 
+				       list_empty(&c->erasing_list)?"yes":"no", list_empty(&c->erase_pending_list)?"yes":"no");
 				return -ENOSPC;
 			}
 			/* Make sure this can't deadlock. Someone has to start the erases
@@ -157,20 +208,20 @@ static int jffs2_do_reserve_space(struct
 			/* In eCos, we don't have a handy kernel thread doing the erases for
 			   us. We do them ourselves right now. */
 			jffs2_erase_pending_blocks(c);
-			jffs2_mark_erased_blocks(c);
 #else
 			set_current_state(TASK_INTERRUPTIBLE);
 			add_wait_queue(&c->erase_wait, &wait);
-			D1(printk(KERN_DEBUG "Waiting for erases to complete. erasing_blocks is %d. (erasingempty: %s, erasependingempty: %s)\n", 
-				  c->nr_erasing_blocks, list_empty(&c->erasing_list)?"yes":"no", list_empty(&c->erase_pending_list)?"yes":"no"));
+			D1(printk(KERN_DEBUG "Waiting for erases to complete. erasing_blocks is %d. (erasableempty: %s, erasingempty: %s, erasependingempty: %s)\n", 
+				  c->nr_erasing_blocks, list_empty(&c->erasable_list)?"yes":"no",
+				  list_empty(&c->erasing_list)?"yes":"no", list_empty(&c->erase_pending_list)?"yes":"no"));
 			if (!list_empty(&c->erase_pending_list)) {
 				D1(printk(KERN_DEBUG "Triggering pending erases\n"));
 				jffs2_erase_pending_trigger(c);
 			}
-			spin_unlock_bh(&c->erase_completion_lock);
+			spin_unlock(&c->erase_completion_lock);
 			schedule();
 			remove_wait_queue(&c->erase_wait, &wait);
-			spin_lock_bh(&c->erase_completion_lock);
+			spin_lock(&c->erase_completion_lock);
 			if (signal_pending(current)) {
 				return -EINTR;
 			}
@@ -185,7 +236,8 @@ static int jffs2_do_reserve_space(struct
 		list_del(next);
 		c->nextblock = jeb = list_entry(next, struct jffs2_eraseblock, list);
 		c->nr_free_blocks--;
-		if (jeb->free_size != c->sector_size - sizeof(struct jffs2_unknown_node)) {
+
+		if (jeb->free_size != c->sector_size - c->cleanmarker_size) {
 			printk(KERN_WARNING "Eep. Block 0x%08x taken from free_list had free_size of 0x%08x!!\n", jeb->offset, jeb->free_size);
 			goto restart;
 		}
@@ -194,6 +246,20 @@ static int jffs2_do_reserve_space(struct
 	   enough space */
 	*ofs = jeb->offset + (c->sector_size - jeb->free_size);
 	*len = jeb->free_size;
+
+	if (c->cleanmarker_size && jeb->used_size == c->cleanmarker_size &&
+	    !jeb->first_node->next_in_ino) {
+		/* Only node in it beforehand was a CLEANMARKER node (we think). 
+		   So mark it obsolete now that there's going to be another node
+		   in the block. This will reduce used_size to zero but We've 
+		   already set c->nextblock so that jffs2_mark_node_obsolete()
+		   won't try to refile it to the dirty_list.
+		*/
+		spin_unlock(&c->erase_completion_lock);
+		jffs2_mark_node_obsolete(c, jeb->first_node);
+		spin_lock(&c->erase_completion_lock);
+	}
+
 	D1(printk(KERN_DEBUG "jffs2_do_reserve_space(): Giving 0x%x bytes at 0x%x\n", *len, *ofs));
 	return 0;
 }
@@ -201,9 +267,9 @@ static int jffs2_do_reserve_space(struct
 /**
  *	jffs2_add_physical_node_ref - add a physical node reference to the list
  *	@c: superblock info
- *	@ofs: physical location of this physical node
+ *	@new: new node reference to add
  *	@len: length of this physical node
- *	@ino: inode number with which this physical node is associated
+ *	@dirty: dirty flag for new node
  *
  *	Should only be used to report nodes for which space has been allocated 
  *	by jffs2_reserve_space.
@@ -211,47 +277,56 @@ static int jffs2_do_reserve_space(struct
  *	Must be called with the alloc_sem held.
  */
  
-int jffs2_add_physical_node_ref(struct jffs2_sb_info *c, struct jffs2_raw_node_ref *new, uint32_t len, int dirty)
+int jffs2_add_physical_node_ref(struct jffs2_sb_info *c, struct jffs2_raw_node_ref *new)
 {
 	struct jffs2_eraseblock *jeb;
+	uint32_t len = new->totlen;
 
-	len = PAD(len);
-	jeb = &c->blocks[(new->flash_offset & ~3) / c->sector_size];
-	D1(printk(KERN_DEBUG "jffs2_add_physical_node_ref(): Node at 0x%x, size 0x%x\n", new->flash_offset & ~3, len));
+	jeb = &c->blocks[new->flash_offset / c->sector_size];
+	D1(printk(KERN_DEBUG "jffs2_add_physical_node_ref(): Node at 0x%x(%d), size 0x%x\n", ref_offset(new), ref_flags(new), len));
 #if 1
-	if (jeb != c->nextblock || (new->flash_offset & ~3) != jeb->offset + (c->sector_size - jeb->free_size)) {
+	if (jeb != c->nextblock || (ref_offset(new)) != jeb->offset + (c->sector_size - jeb->free_size)) {
 		printk(KERN_WARNING "argh. node added in wrong place\n");
 		jffs2_free_raw_node_ref(new);
 		return -EINVAL;
 	}
 #endif
+	spin_lock(&c->erase_completion_lock);
+
 	if (!jeb->first_node)
 		jeb->first_node = new;
 	if (jeb->last_node)
 		jeb->last_node->next_phys = new;
 	jeb->last_node = new;
 
-	spin_lock_bh(&c->erase_completion_lock);
 	jeb->free_size -= len;
 	c->free_size -= len;
-	if (dirty) {
-		new->flash_offset |= 1;
+	if (ref_obsolete(new)) {
 		jeb->dirty_size += len;
 		c->dirty_size += len;
 	} else {
 		jeb->used_size += len;
 		c->used_size += len;
 	}
-	spin_unlock_bh(&c->erase_completion_lock);
+
 	if (!jeb->free_size && !jeb->dirty_size) {
 		/* If it lives on the dirty_list, jffs2_reserve_space will put it there */
 		D1(printk(KERN_DEBUG "Adding full erase block at 0x%08x to clean_list (free 0x%08x, dirty 0x%08x, used 0x%08x\n",
 			  jeb->offset, jeb->free_size, jeb->dirty_size, jeb->used_size));
+		if (c->wbuf_len) {
+			/* Flush the last write in the block if it's outstanding */
+			spin_unlock(&c->erase_completion_lock);
+			jffs2_flush_wbuf(c, 1);
+			spin_lock(&c->erase_completion_lock);
+		}
+
 		list_add_tail(&jeb->list, &c->clean_list);
 		c->nextblock = NULL;
 	}
 	ACCT_SANITY_CHECK(c,jeb);
-	ACCT_PARANOIA_CHECK(jeb);
+	D1(ACCT_PARANOIA_CHECK(jeb));
+
+	spin_unlock(&c->erase_completion_lock);
 
 	return 0;
 }
@@ -276,8 +351,8 @@ void jffs2_mark_node_obsolete(struct jff
 		printk(KERN_NOTICE "EEEEEK. jffs2_mark_node_obsolete called with NULL node\n");
 		return;
 	}
-	if (ref->flash_offset & 1) {
-		D1(printk(KERN_DEBUG "jffs2_mark_node_obsolete called with already obsolete node at 0x%08x\n", ref->flash_offset &~3));
+	if (ref_obsolete(ref)) {
+		D1(printk(KERN_DEBUG "jffs2_mark_node_obsolete called with already obsolete node at 0x%08x\n", ref_offset(ref)));
 		return;
 	}
 	blocknr = ref->flash_offset / c->sector_size;
@@ -286,82 +361,319 @@ void jffs2_mark_node_obsolete(struct jff
 		BUG();
 	}
 	jeb = &c->blocks[blocknr];
-	if (jeb->used_size < ref->totlen) {
-		printk(KERN_NOTICE "raw node of size 0x%08x freed from erase block %d at 0x%08x, but used_size was already 0x%08x\n",
-		       ref->totlen, blocknr, ref->flash_offset, jeb->used_size);
-		BUG();
+
+	spin_lock(&c->erase_completion_lock);
+
+	if (ref_flags(ref) == REF_UNCHECKED) {
+		D1(if (unlikely(jeb->unchecked_size < ref->totlen)) {
+			printk(KERN_NOTICE "raw unchecked node of size 0x%08x freed from erase block %d at 0x%08x, but unchecked_size was already 0x%08x\n",
+			       ref->totlen, blocknr, ref->flash_offset, jeb->used_size);
+			BUG();
+		})
+		D1(printk(KERN_DEBUG "Obsoleting previously unchecked node at 0x%08x of len %x: ", ref_offset(ref), ref->totlen));
+		jeb->unchecked_size -= ref->totlen;
+		c->unchecked_size -= ref->totlen;
+	} else {
+		D1(if (unlikely(jeb->used_size < ref->totlen)) {
+			printk(KERN_NOTICE "raw node of size 0x%08x freed from erase block %d at 0x%08x, but used_size was already 0x%08x\n",
+			       ref->totlen, blocknr, ref->flash_offset, jeb->used_size);
+			BUG();
+		})
+		D1(printk(KERN_DEBUG "Obsoleting node at 0x%08x of len %x: ", ref_offset(ref), ref->totlen));
+		jeb->used_size -= ref->totlen;
+		c->used_size -= ref->totlen;
 	}
 
-	spin_lock_bh(&c->erase_completion_lock);
-	jeb->used_size -= ref->totlen;
-	jeb->dirty_size += ref->totlen;
-	c->used_size -= ref->totlen;
-	c->dirty_size += ref->totlen;
-	ref->flash_offset |= 1;
+	if ((jeb->dirty_size || ISDIRTY(jeb->wasted_size + ref->totlen)) && jeb != c->nextblock) {
+		D1(printk("Dirtying\n"));
+		jeb->dirty_size += ref->totlen + jeb->wasted_size;
+		c->dirty_size += ref->totlen + jeb->wasted_size;
+		c->wasted_size -= jeb->wasted_size;
+		jeb->wasted_size = 0;
+	} else {
+		D1(printk("Wasting\n"));
+		jeb->wasted_size += ref->totlen;
+		c->wasted_size += ref->totlen;	
+	}
+	ref->flash_offset = ref_offset(ref) | REF_OBSOLETE;
 	
 	ACCT_SANITY_CHECK(c, jeb);
 
-	ACCT_PARANOIA_CHECK(jeb);
+	D1(ACCT_PARANOIA_CHECK(jeb));
+
+	if (c->flags & JFFS2_SB_FLAG_MOUNTING) {
+		/* Mount in progress. Don't muck about with the block
+		   lists because they're not ready yet, and don't actually
+		   obliterate nodes that look obsolete. If they weren't 
+		   marked obsolete on the flash at the time they _became_
+		   obsolete, there was probably a reason for that. */
+		spin_unlock(&c->erase_completion_lock);
+		return;
+	}
 
 	if (jeb == c->nextblock) {
 		D2(printk(KERN_DEBUG "Not moving nextblock 0x%08x to dirty/erase_pending list\n", jeb->offset));
+	} else if (!jeb->used_size && !jeb->unchecked_size) {
+		if (jeb == c->gcblock) {
+			D1(printk(KERN_DEBUG "gcblock at 0x%08x completely dirtied. Clearing gcblock...\n", jeb->offset));
+			c->gcblock = NULL;
+		} else {
+			D1(printk(KERN_DEBUG "Eraseblock at 0x%08x completely dirtied. Removing from (dirty?) list...\n", jeb->offset));
+			list_del(&jeb->list);
+		}
+		if (c->wbuf_len) {
+			D1(printk(KERN_DEBUG "...and adding to erasable_pending_wbuf_list\n"));
+			list_add_tail(&jeb->list, &c->erasable_pending_wbuf_list);
+#if 0 /* This check was added to allow us to find places where we added nodes to the lists
+	 after dropping the alloc_sem, and it did that just fine. But it also caused us to
+	 lock the alloc_sem in other places, like clear_inode(), when we wouldn't otherwise
+	 have needed to. So I suspect it's outlived its usefulness. Thomas? */
+
+			/* We've changed the rules slightly. After
+			   writing a node you now mustn't drop the
+			   alloc_sem before you've finished all the
+			   list management - this is so that when we
+			   get here, we know that no other nodes have
+			   been written, and the above check on wbuf
+			   is valid - wbuf_len is nonzero IFF the node
+			   which obsoletes this node is still in the
+			   wbuf.
+
+			   So we BUG() if that new rule is broken, to
+			   make sure we catch it and fix it.
+			*/
+			if (!down_trylock(&c->alloc_sem)) {
+				up(&c->alloc_sem);
+				printk(KERN_CRIT "jffs2_mark_node_obsolete() called with wbuf active but alloc_sem not locked!\n");
+				BUG();
+			}
+#endif
+		} else {
+			if (jiffies & 127) {
+				/* Most of the time, we just erase it immediately. Otherwise we
+				   spend ages scanning it on mount, etc. */
+				D1(printk(KERN_DEBUG "...and adding to erase_pending_list\n"));
+				list_add_tail(&jeb->list, &c->erase_pending_list);
+				c->nr_erasing_blocks++;
+				jffs2_erase_pending_trigger(c);
+			} else {
+				/* Sometimes, however, we leave it elsewhere so it doesn't get
+				   immediately reused, and we spread the load a bit. */
+				D1(printk(KERN_DEBUG "...and adding to erasable_list\n"));
+				list_add_tail(&jeb->list, &c->erasable_list);
+			}				
+		}
+		D1(printk(KERN_DEBUG "Done OK\n"));
 	} else if (jeb == c->gcblock) {
-		D2(printk(KERN_DEBUG "Not moving gcblock 0x%08x to dirty/erase_pending list\n", jeb->offset));
-#if 0 /* We no longer do this here. It can screw the wear levelling. If you have a lot of static
-	 data and a few blocks free, and you just create new files and keep deleting/overwriting
-	 them, then you'd keep erasing and reusing those blocks without ever moving stuff around.
-	 So we leave completely obsoleted blocks on the dirty_list and let the GC delete them 
-	 when it finds them there. That way, we still get the 'once in a while, take a clean block'
-	 to spread out the flash usage */
-	} else if (!jeb->used_size) {
-		D1(printk(KERN_DEBUG "Eraseblock at 0x%08x completely dirtied. Removing from (dirty?) list...\n", jeb->offset));
-		list_del(&jeb->list);
-		D1(printk(KERN_DEBUG "...and adding to erase_pending_list\n"));
-		list_add_tail(&jeb->list, &c->erase_pending_list);
-		c->nr_erasing_blocks++;
-		jffs2_erase_pending_trigger(c);
-		//		OFNI_BS_2SFFJ(c)->s_dirt = 1;
-		D1(printk(KERN_DEBUG "Done OK\n"));
-#endif
-	} else if (jeb->dirty_size == ref->totlen) {
+		D2(printk(KERN_DEBUG "Not moving gcblock 0x%08x to dirty_list\n", jeb->offset));
+	} else if (ISDIRTY(jeb->dirty_size) && !ISDIRTY(jeb->dirty_size - ref->totlen)) {
 		D1(printk(KERN_DEBUG "Eraseblock at 0x%08x is freshly dirtied. Removing from clean list...\n", jeb->offset));
 		list_del(&jeb->list);
 		D1(printk(KERN_DEBUG "...and adding to dirty_list\n"));
 		list_add_tail(&jeb->list, &c->dirty_list);
-	}
-	spin_unlock_bh(&c->erase_completion_lock);
+	} else if (VERYDIRTY(c, jeb->dirty_size) &&
+		   !VERYDIRTY(c, jeb->dirty_size - ref->totlen)) {
+		D1(printk(KERN_DEBUG "Eraseblock at 0x%08x is now very dirty. Removing from dirty list...\n", jeb->offset));
+		list_del(&jeb->list);
+		D1(printk(KERN_DEBUG "...and adding to very_dirty_list\n"));
+		list_add_tail(&jeb->list, &c->very_dirty_list);
+	} else {
+		D1(printk(KERN_DEBUG "Eraseblock at 0x%08x not moved anywhere. (free 0x%08x, dirty 0x%08x, used 0x%08x)\n",
+			  jeb->offset, jeb->free_size, jeb->dirty_size, jeb->used_size)); 
+	}			  	
+
+	spin_unlock(&c->erase_completion_lock);
 
 	if (!jffs2_can_mark_obsolete(c))
 		return;
 	if (jffs2_is_readonly(c))
 		return;
 
-	D1(printk(KERN_DEBUG "obliterating obsoleted node at 0x%08x\n", ref->flash_offset &~3));
-	ret = jffs2_flash_read(c, ref->flash_offset &~3, sizeof(n), &retlen, (char *)&n);
+	D1(printk(KERN_DEBUG "obliterating obsoleted node at 0x%08x\n", ref_offset(ref)));
+	ret = jffs2_flash_read(c, ref_offset(ref), sizeof(n), &retlen, (char *)&n);
 	if (ret) {
-		printk(KERN_WARNING "Read error reading from obsoleted node at 0x%08x: %d\n", ref->flash_offset &~3, ret);
+		printk(KERN_WARNING "Read error reading from obsoleted node at 0x%08x: %d\n", ref_offset(ref), ret);
+		return;
+	}
+	if (retlen != sizeof(n)) {
+		printk(KERN_WARNING "Short read from obsoleted node at 0x%08x: %zd\n", ref_offset(ref), retlen);
+		return;
+	}
+	if (PAD(je32_to_cpu(n.totlen)) != PAD(ref->totlen)) {
+		printk(KERN_WARNING "Node totlen on flash (0x%08x) != totlen in node ref (0x%08x)\n", je32_to_cpu(n.totlen), ref->totlen);
+		return;
+	}
+	if (!(je16_to_cpu(n.nodetype) & JFFS2_NODE_ACCURATE)) {
+		D1(printk(KERN_DEBUG "Node at 0x%08x was already marked obsolete (nodetype 0x%04x\n", ref_offset(ref), je16_to_cpu(n.nodetype)));
+		return;
+	}
+	/* XXX FIXME: This is ugly now */
+	n.nodetype = cpu_to_je16(je16_to_cpu(n.nodetype) & ~JFFS2_NODE_ACCURATE);
+	ret = jffs2_flash_write(c, ref_offset(ref), sizeof(n), &retlen, (char *)&n);
+	if (ret) {
+		printk(KERN_WARNING "Write error in obliterating obsoleted node at 0x%08x: %d\n", ref_offset(ref), ret);
 		return;
 	}
 	if (retlen != sizeof(n)) {
-		printk(KERN_WARNING "Short read from obsoleted node at 0x%08x: %d\n", ref->flash_offset &~3, retlen);
-		return;
-	}
-	if (PAD(n.totlen) != PAD(ref->totlen)) {
-		printk(KERN_WARNING "Node totlen on flash (0x%08x) != totlen in node ref (0x%08x)\n", n.totlen, ref->totlen);
-		return;
-	}
-	if (!(n.nodetype & JFFS2_NODE_ACCURATE)) {
-		D1(printk(KERN_DEBUG "Node at 0x%08x was already marked obsolete (nodetype 0x%04x\n", ref->flash_offset &~3, n.nodetype));
-		return;
-	}
-	n.nodetype &= ~JFFS2_NODE_ACCURATE;
-	ret = jffs2_flash_write(c, ref->flash_offset&~3, sizeof(n), &retlen, (char *)&n);
-	if (ret) {
-		printk(KERN_WARNING "Write error in obliterating obsoleted node at 0x%08x: %d\n", ref->flash_offset &~3, ret);
-		return;
-	}
-	if (retlen != sizeof(n)) {
-		printk(KERN_WARNING "Short write in obliterating obsoleted node at 0x%08x: %d\n", ref->flash_offset &~3, retlen);
+		printk(KERN_WARNING "Short write in obliterating obsoleted node at 0x%08x: %zd\n", ref_offset(ref), retlen);
 		return;
 	}
 }
+
+#if CONFIG_JFFS2_FS_DEBUG > 0
+void jffs2_dump_block_lists(struct jffs2_sb_info *c)
+{
+
+
+	printk(KERN_DEBUG "jffs2_dump_block_lists:\n");
+	printk(KERN_DEBUG "flash_size: %08x\n", c->flash_size);
+	printk(KERN_DEBUG "used_size: %08x\n", c->used_size);
+	printk(KERN_DEBUG "dirty_size: %08x\n", c->dirty_size);
+	printk(KERN_DEBUG "wasted_size: %08x\n", c->wasted_size);
+	printk(KERN_DEBUG "unchecked_size: %08x\n", c->unchecked_size);
+	printk(KERN_DEBUG "free_size: %08x\n", c->free_size);
+	printk(KERN_DEBUG "erasing_size: %08x\n", c->erasing_size);
+	printk(KERN_DEBUG "bad_size: %08x\n", c->bad_size);
+	printk(KERN_DEBUG "sector_size: %08x\n", c->sector_size);
+	printk(KERN_DEBUG "jffs2_reserved_blocks size: %08x\n",c->sector_size * JFFS2_RESERVED_BLOCKS_WRITE);
+
+	if (c->nextblock) {
+		printk(KERN_DEBUG "nextblock: %08x (used %08x, dirty %08x, wasted %08x, unchecked %08x, free %08x)\n",
+		       c->nextblock->offset, c->nextblock->used_size, c->nextblock->dirty_size, c->nextblock->wasted_size, c->nextblock->unchecked_size, c->nextblock->free_size);
+	} else {
+		printk(KERN_DEBUG "nextblock: NULL\n");
+	}
+	if (c->gcblock) {
+		printk(KERN_DEBUG "gcblock: %08x (used %08x, dirty %08x, wasted %08x, unchecked %08x, free %08x)\n",
+		       c->gcblock->offset, c->gcblock->used_size, c->gcblock->dirty_size, c->gcblock->wasted_size, c->gcblock->unchecked_size, c->gcblock->free_size);
+	} else {
+		printk(KERN_DEBUG "gcblock: NULL\n");
+	}
+	if (list_empty(&c->clean_list)) {
+		printk(KERN_DEBUG "clean_list: empty\n");
+	} else {
+		struct list_head *this;
+		int	numblocks = 0;
+		uint32_t dirty = 0;
+
+		list_for_each(this, &c->clean_list) {
+			struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list);
+			numblocks ++;
+			dirty += jeb->wasted_size;
+			printk(KERN_DEBUG "clean_list: %08x (used %08x, dirty %08x, wasted %08x, unchecked %08x, free %08x)\n", jeb->offset, jeb->used_size, jeb->dirty_size, jeb->wasted_size, jeb->unchecked_size, jeb->free_size);
+		}
+		printk (KERN_DEBUG "Contains %d blocks with total wasted size %u, average wasted size: %u\n", numblocks, dirty, dirty / numblocks);
+	}
+	if (list_empty(&c->very_dirty_list)) {
+		printk(KERN_DEBUG "very_dirty_list: empty\n");
+	} else {
+		struct list_head *this;
+		int	numblocks = 0;
+		uint32_t dirty = 0;
+
+		list_for_each(this, &c->very_dirty_list) {
+			struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list);
+			numblocks ++;
+			dirty += jeb->dirty_size;
+			printk(KERN_DEBUG "very_dirty_list: %08x (used %08x, dirty %08x, wasted %08x, unchecked %08x, free %08x)\n",
+			       jeb->offset, jeb->used_size, jeb->dirty_size, jeb->wasted_size, jeb->unchecked_size, jeb->free_size);
+		}
+		printk (KERN_DEBUG "Contains %d blocks with total dirty size %u, average dirty size: %u\n",
+			numblocks, dirty, dirty / numblocks);
+	}
+	if (list_empty(&c->dirty_list)) {
+		printk(KERN_DEBUG "dirty_list: empty\n");
+	} else {
+		struct list_head *this;
+		int	numblocks = 0;
+		uint32_t dirty = 0;
+
+		list_for_each(this, &c->dirty_list) {
+			struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list);
+			numblocks ++;
+			dirty += jeb->dirty_size;
+			printk(KERN_DEBUG "dirty_list: %08x (used %08x, dirty %08x, wasted %08x, unchecked %08x, free %08x)\n",
+			       jeb->offset, jeb->used_size, jeb->dirty_size, jeb->wasted_size, jeb->unchecked_size, jeb->free_size);
+		}
+		printk (KERN_DEBUG "Contains %d blocks with total dirty size %u, average dirty size: %u\n",
+			numblocks, dirty, dirty / numblocks);
+	}
+	if (list_empty(&c->erasable_list)) {
+		printk(KERN_DEBUG "erasable_list: empty\n");
+	} else {
+		struct list_head *this;
+
+		list_for_each(this, &c->erasable_list) {
+			struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list);
+			printk(KERN_DEBUG "erasable_list: %08x (used %08x, dirty %08x, wasted %08x, unchecked %08x, free %08x)\n",
+			       jeb->offset, jeb->used_size, jeb->dirty_size, jeb->wasted_size, jeb->unchecked_size, jeb->free_size);
+		}
+	}
+	if (list_empty(&c->erasing_list)) {
+		printk(KERN_DEBUG "erasing_list: empty\n");
+	} else {
+		struct list_head *this;
+
+		list_for_each(this, &c->erasing_list) {
+			struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list);
+			printk(KERN_DEBUG "erasing_list: %08x (used %08x, dirty %08x, wasted %08x, unchecked %08x, free %08x)\n",
+			       jeb->offset, jeb->used_size, jeb->dirty_size, jeb->wasted_size, jeb->unchecked_size, jeb->free_size);
+		}
+	}
+	if (list_empty(&c->erase_pending_list)) {
+		printk(KERN_DEBUG "erase_pending_list: empty\n");
+	} else {
+		struct list_head *this;
+
+		list_for_each(this, &c->erase_pending_list) {
+			struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list);
+			printk(KERN_DEBUG "erase_pending_list: %08x (used %08x, dirty %08x, wasted %08x, unchecked %08x, free %08x)\n",
+			       jeb->offset, jeb->used_size, jeb->dirty_size, jeb->wasted_size, jeb->unchecked_size, jeb->free_size);
+		}
+	}
+	if (list_empty(&c->erasable_pending_wbuf_list)) {
+		printk(KERN_DEBUG "erasable_pending_wbuf_list: empty\n");
+	} else {
+		struct list_head *this;
+
+		list_for_each(this, &c->erasable_pending_wbuf_list) {
+			struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list);
+			printk(KERN_DEBUG "erase_pending_wbuf_list: %08x (used %08x, dirty %08x, wasted %08x, unchecked %08x, free %08x)\n",
+			       jeb->offset, jeb->used_size, jeb->dirty_size, jeb->wasted_size, jeb->unchecked_size, jeb->free_size);
+		}
+	}
+	if (list_empty(&c->free_list)) {
+		printk(KERN_DEBUG "free_list: empty\n");
+	} else {
+		struct list_head *this;
+
+		list_for_each(this, &c->free_list) {
+			struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list);
+			printk(KERN_DEBUG "free_list: %08x (used %08x, dirty %08x, wasted %08x, unchecked %08x, free %08x)\n",
+			       jeb->offset, jeb->used_size, jeb->dirty_size, jeb->wasted_size, jeb->unchecked_size, jeb->free_size);
+		}
+	}
+	if (list_empty(&c->bad_list)) {
+		printk(KERN_DEBUG "bad_list: empty\n");
+	} else {
+		struct list_head *this;
+
+		list_for_each(this, &c->bad_list) {
+			struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list);
+			printk(KERN_DEBUG "bad_list: %08x (used %08x, dirty %08x, wasted %08x, unchecked %08x, free %08x)\n",
+			       jeb->offset, jeb->used_size, jeb->dirty_size, jeb->wasted_size, jeb->unchecked_size, jeb->free_size);
+		}
+	}
+	if (list_empty(&c->bad_used_list)) {
+		printk(KERN_DEBUG "bad_used_list: empty\n");
+	} else {
+		struct list_head *this;
+
+		list_for_each(this, &c->bad_used_list) {
+			struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list);
+			printk(KERN_DEBUG "bad_used_list: %08x (used %08x, dirty %08x, wasted %08x, unchecked %08x, free %08x)\n",
+			       jeb->offset, jeb->used_size, jeb->dirty_size, jeb->wasted_size, jeb->unchecked_size, jeb->free_size);
+		}
+	}
+}
+#endif /* CONFIG_JFFS2_FS_DEBUG */
--- a/packages/fs/jffs2/current/src/os-ecos.h
+++ b/packages/fs/jffs2/current/src/os-ecos.h
@@ -7,7 +7,7 @@
  *
  * For licensing information, see the file 'LICENCE' in this directory.
  *
- * $Id$
+ * $Id: os-ecos.h,v 1.5 2003/01/21 18:14:27 dwmw2 Exp $
  *
  */
 
@@ -15,9 +15,95 @@
 #define __JFFS2_OS_ECOS_H__
 
 #include <cyg/io/io.h>
+#include <sys/types.h>
+#include <asm/atomic.h>
+#include <linux/stat.h>
+#include "jffs2port.h"
 
 #define CONFIG_JFFS2_FS_DEBUG 0
 
+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;
+}
+
+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
+	}
+	printf("jffs2_to_os_mode() cannot convert 0x%x\n", osmode);
+	BUG();
+	return 0;
+}
+
  /* Read-only operation not currently implemented on eCos */
 #define jffs2_is_readonly(c) (0)
 
@@ -39,6 +125,11 @@
 #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 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;
@@ -106,6 +197,9 @@ struct super_block {
     cyg_io_handle_t     s_dev;
 };
 
+#define sleep_on_spinunlock(wq, sl) do { ; } while(0)
+#define EBADFD 32767
+
 /* background.c */
 static inline void jffs2_garbage_collect_trigger(struct jffs2_sb_info *c)
 {
@@ -131,5 +225,28 @@ int jffs2_prepare_write (struct inode *d
 //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);
 
+#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)
+
+#define jffs2_flush_wbuf(c, flag) do { ; } while(0)
+#define jffs2_nand_read_failcnt(c,jeb) do { ; } while(0)
+#define jffs2_write_nand_badblock(c,jeb) do { ; } while(0)
+#define jffs2_flash_writev jffs2_flash_writev
+#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);
+}
 
 #endif /* __JFFS2_OS_ECOS_H__ */
--- a/packages/fs/jffs2/current/src/pushpull.h
+++ b/packages/fs/jffs2/current/src/pushpull.h
@@ -1,24 +1,20 @@
 /*
  * JFFS2 -- Journalling Flash File System, Version 2.
  *
- * Copyright (C) 2001 Red Hat, Inc.
+ * Copyright (C) 2001, 2002 Red Hat, Inc.
  *
  * Created by David Woodhouse <dwmw2@cambridge.redhat.com>
  *
  * For licensing information, see the file 'LICENCE' in this directory.
  *
- * $Id: pushpull.h,v 1.6 2002/01/25 01:49:26 dwmw2 Exp $
+ * $Id: pushpull.h,v 1.8 2002/05/20 14:56:38 dwmw2 Exp $
  *
  */
 
 #ifndef __PUSHPULL_H__
 #define __PUSHPULL_H__
 
-#ifndef __ECOS
 #include <linux/errno.h>
-#else
-#include <errno.h>
-#endif
 
 struct pushpull {
 	unsigned char *buf;
--- a/packages/fs/jffs2/current/src/read.c
+++ b/packages/fs/jffs2/current/src/read.c
@@ -7,17 +7,16 @@
  *
  * For licensing information, see the file 'LICENCE' in this directory.
  *
- * $Id: read.c,v 1.19 2002/01/14 23:30:26 dwmw2 Exp $
+ * $Id: read.c,v 1.31 2003/01/14 14:06:22 dwmw2 Exp $
  *
  */
 
-#ifndef __ECOS
 #include <linux/kernel.h>
 #include <linux/slab.h>
+#include <linux/crc32.h>
+#include <linux/pagemap.h>
 #include <linux/mtd/mtd.h>
-#endif
 #include "nodelist.h"
-#include "crc32.h"
 
 int jffs2_read_dnode(struct jffs2_sb_info *c, struct jffs2_full_dnode *fd, unsigned char *buf, int ofs, int len)
 {
@@ -32,29 +31,41 @@ int jffs2_read_dnode(struct jffs2_sb_inf
 	if (!ri)
 		return -ENOMEM;
 
-	ret = jffs2_flash_read(c, fd->raw->flash_offset & ~3, sizeof(*ri), &readlen, (char *)ri);
+	ret = jffs2_flash_read(c, ref_offset(fd->raw), sizeof(*ri), &readlen, (char *)ri);
 	if (ret) {
 		jffs2_free_raw_inode(ri);
-		printk(KERN_WARNING "Error reading node from 0x%08x: %d\n", fd->raw->flash_offset & ~3, ret);
+		printk(KERN_WARNING "Error reading node from 0x%08x: %d\n", ref_offset(fd->raw), ret);
 		return ret;
 	}
 	if (readlen != sizeof(*ri)) {
 		jffs2_free_raw_inode(ri);
-		printk(KERN_WARNING "Short read from 0x%08x: wanted 0x%x bytes, got 0x%x\n", 
-		       fd->raw->flash_offset & ~3, sizeof(*ri), readlen);
+		printk(KERN_WARNING "Short read from 0x%08x: wanted 0x%zx bytes, got 0x%zx\n", 
+		       ref_offset(fd->raw), sizeof(*ri), readlen);
 		return -EIO;
 	}
 	crc = crc32(0, ri, sizeof(*ri)-8);
 
-	D1(printk(KERN_DEBUG "Node read from %08x: node_crc %08x, calculated CRC %08x. dsize %x, csize %x, offset %x, buf %p\n", fd->raw->flash_offset & ~3, ri->node_crc, crc, ri->dsize, ri->csize, ri->offset, buf));
-	if (crc != ri->node_crc) {
-		printk(KERN_WARNING "Node CRC %08x != calculated CRC %08x for node at %08x\n", ri->node_crc, crc, fd->raw->flash_offset & ~3);
+	D1(printk(KERN_DEBUG "Node read from %08x: node_crc %08x, calculated CRC %08x. dsize %x, csize %x, offset %x, buf %p\n",
+		  ref_offset(fd->raw), je32_to_cpu(ri->node_crc),
+		  crc, je32_to_cpu(ri->dsize), je32_to_cpu(ri->csize),
+		  je32_to_cpu(ri->offset), buf));
+	if (crc != je32_to_cpu(ri->node_crc)) {
+		printk(KERN_WARNING "Node CRC %08x != calculated CRC %08x for node at %08x\n",
+		       je32_to_cpu(ri->node_crc), crc, ref_offset(fd->raw));
 		ret = -EIO;
 		goto out_ri;
 	}
+	/* There was a bug where we wrote hole nodes out with csize/dsize
+	   swapped. Deal with it */
+	if (ri->compr == JFFS2_COMPR_ZERO && !je32_to_cpu(ri->dsize) && 
+	    je32_to_cpu(ri->csize)) {
+		ri->dsize = ri->csize;
+		ri->csize = cpu_to_je32(0);
+	}
 
-	D1(if(ofs + len > ri->dsize) {
-		printk(KERN_WARNING "jffs2_read_dnode() asked for %d bytes at %d from %d-byte node\n", len, ofs, ri->dsize);
+	D1(if(ofs + len > je32_to_cpu(ri->dsize)) {
+		printk(KERN_WARNING "jffs2_read_dnode() asked for %d bytes at %d from %d-byte node\n",
+		       len, ofs, je32_to_cpu(ri->dsize));
 		ret = -EINVAL;
 		goto out_ri;
 	});
@@ -71,18 +82,18 @@ int jffs2_read_dnode(struct jffs2_sb_inf
 	   Reading partial node and it's uncompressed - read into readbuf, check CRC, and copy 
 	   Reading partial node and it's compressed - read into readbuf, check checksum, decompress to decomprbuf and copy
 	*/
-	if (ri->compr == JFFS2_COMPR_NONE && len == ri->dsize) {
+	if (ri->compr == JFFS2_COMPR_NONE && len == je32_to_cpu(ri->dsize)) {
 		readbuf = buf;
 	} else {
-		readbuf = kmalloc(ri->csize, GFP_KERNEL);
+		readbuf = kmalloc(je32_to_cpu(ri->csize), GFP_KERNEL);
 		if (!readbuf) {
 			ret = -ENOMEM;
 			goto out_ri;
 		}
 	}
 	if (ri->compr != JFFS2_COMPR_NONE) {
-		if (len < ri->dsize) {
-			decomprbuf = kmalloc(ri->dsize, GFP_KERNEL);
+		if (len < je32_to_cpu(ri->dsize)) {
+			decomprbuf = kmalloc(je32_to_cpu(ri->dsize), GFP_KERNEL);
 			if (!decomprbuf) {
 				ret = -ENOMEM;
 				goto out_readbuf;
@@ -94,31 +105,35 @@ int jffs2_read_dnode(struct jffs2_sb_inf
 		decomprbuf = readbuf;
 	}
 
-	D2(printk(KERN_DEBUG "Read %d bytes to %p\n", ri->csize, readbuf));
-	ret = jffs2_flash_read(c, (fd->raw->flash_offset &~3) + sizeof(*ri), ri->csize, &readlen, readbuf);
+	D2(printk(KERN_DEBUG "Read %d bytes to %p\n", je32_to_cpu(ri->csize),
+		  readbuf));
+	ret = jffs2_flash_read(c, (ref_offset(fd->raw)) + sizeof(*ri),
+			       je32_to_cpu(ri->csize), &readlen, readbuf);
 
-	if (!ret && readlen != ri->csize)
+	if (!ret && readlen != je32_to_cpu(ri->csize))
 		ret = -EIO;
 	if (ret)
 		goto out_decomprbuf;
 
-	crc = crc32(0, readbuf, ri->csize);
-	if (crc != ri->data_crc) {
-		printk(KERN_WARNING "Data CRC %08x != calculated CRC %08x for node at %08x\n", ri->data_crc, crc, fd->raw->flash_offset & ~3);
+	crc = crc32(0, readbuf, je32_to_cpu(ri->csize));
+	if (crc != je32_to_cpu(ri->data_crc)) {
+		printk(KERN_WARNING "Data CRC %08x != calculated CRC %08x for node at %08x\n",
+		       je32_to_cpu(ri->data_crc), crc, ref_offset(fd->raw));
 		ret = -EIO;
 		goto out_decomprbuf;
 	}
 	D2(printk(KERN_DEBUG "Data CRC matches calculated CRC %08x\n", crc));
 	if (ri->compr != JFFS2_COMPR_NONE) {
-		D2(printk(KERN_DEBUG "Decompress %d bytes from %p to %d bytes at %p\n", ri->csize, readbuf, ri->dsize, decomprbuf)); 
-		ret = jffs2_decompress(ri->compr, readbuf, decomprbuf, ri->csize, ri->dsize);
+		D2(printk(KERN_DEBUG "Decompress %d bytes from %p to %d bytes at %p\n",
+			  je32_to_cpu(ri->csize), readbuf, je32_to_cpu(ri->dsize), decomprbuf)); 
+		ret = jffs2_decompress(ri->compr, readbuf, decomprbuf, je32_to_cpu(ri->csize), je32_to_cpu(ri->dsize));
 		if (ret) {
 			printk(KERN_WARNING "Error: jffs2_decompress returned %d\n", ret);
 			goto out_decomprbuf;
 		}
 	}
 
-	if (len < ri->dsize) {
+	if (len < je32_to_cpu(ri->dsize)) {
 		memcpy(buf, decomprbuf+ofs, len);
 	}
  out_decomprbuf:
@@ -137,16 +152,14 @@ int jffs2_read_inode_range(struct jffs2_
 			   unsigned char *buf, uint32_t offset, uint32_t len)
 {
 	uint32_t end = offset + len;
-	struct jffs2_node_frag *frag = f->fraglist;
+	struct jffs2_node_frag *frag;
 	int ret;
 
 	D1(printk(KERN_DEBUG "jffs2_read_inode_range: ino #%u, range 0x%08x-0x%08x\n",
 		  f->inocache->ino, offset, offset+len));
 
-	while(frag && frag->ofs + frag->size  <= offset) {
-		D2(printk(KERN_DEBUG "skipping frag %d-%d; before the region we care about\n", frag->ofs, frag->ofs + frag->size));
-		frag = frag->next;
-	}
+	frag = jffs2_lookup_node_frag(&f->fragtree, offset);
+
 	/* XXX FIXME: Where a single physical node actually shows up in two
 	   frags, we read it twice. Don't do that. */
 	/* Now we're pointing at the first frag which overlaps our page */
@@ -164,30 +177,39 @@ int jffs2_read_inode_range(struct jffs2_
 			buf += holesize;
 			offset += holesize;
 			continue;
+		} else if (frag->ofs < offset && (offset & (PAGE_CACHE_SIZE-1)) != 0) {
+			D1(printk(KERN_NOTICE "Eep. Overlap in ino #%u fraglist. frag->ofs = 0x%08x, offset = 0x%08x\n",
+				  f->inocache->ino, frag->ofs, offset));
+			D1(jffs2_print_frag_list(f));
+			memset(buf, 0, end - offset);
+			return -EIO;
 		} else if (!frag->node) {
 			uint32_t holeend = min(end, frag->ofs + frag->size);
 			D1(printk(KERN_DEBUG "Filling frag hole from %d-%d (frag 0x%x 0x%x)\n", offset, holeend, frag->ofs, frag->ofs + frag->size));
 			memset(buf, 0, holeend - offset);
 			buf += holeend - offset;
 			offset = holeend;
-			frag = frag->next;
+			frag = frag_next(frag);
 			continue;
 		} else {
 			uint32_t readlen;
-			uint32_t frag_ofs = offset - frag->ofs; /* Offset within this frag from which we want to start reading */
-			readlen = min(frag->size - frag_ofs, end - offset);
-			D1(printk(KERN_DEBUG "Reading %d-%d from node at 0x%x\n", frag->ofs+frag_ofs, frag->ofs+frag_ofs+readlen, frag->node->raw->flash_offset & ~3));
-			ret = jffs2_read_dnode(c, frag->node, buf, frag->ofs + frag_ofs - frag->node->ofs, readlen);
+			uint32_t fragofs; /* offset within the frag to start reading */
+			
+			fragofs = offset - frag->ofs;
+			readlen = min(frag->size - fragofs, end - offset);
+			D1(printk(KERN_DEBUG "Reading %d-%d from node at 0x%08x (%d)\n",
+				  frag->ofs+fragofs, frag->ofs+fragofs+readlen,
+				  ref_offset(frag->node->raw), ref_flags(frag->node->raw)));
+			ret = jffs2_read_dnode(c, frag->node, buf, fragofs + frag->ofs - frag->node->ofs, readlen);
 			D2(printk(KERN_DEBUG "node read done\n"));
 			if (ret) {
 				D1(printk(KERN_DEBUG"jffs2_read_inode_range error %d\n",ret));
-				memset(buf, 0, frag->size - frag_ofs);
+				memset(buf, 0, readlen);
 				return ret;
 			}
-
-			buf += frag->size - frag_ofs;
-			offset += frag->size - frag_ofs;
-			frag = frag->next;
+			buf += readlen;
+			offset += readlen;
+			frag = frag_next(frag);
 			D2(printk(KERN_DEBUG "node read was OK. Looping\n"));
 		}
 	}
--- a/packages/fs/jffs2/current/src/readinode.c
+++ b/packages/fs/jffs2/current/src/readinode.c
@@ -7,51 +7,56 @@
  *
  * For licensing information, see the file 'LICENCE' in this directory.
  *
- * $Id: readinode.c,v 1.64 2002/01/09 13:25:58 dwmw2 Exp $
+ * $Id: readinode.c,v 1.103 2003/01/22 14:43:29 dwmw2 Exp $
  *
  */
 
-/* Given an inode, probably with existing list of fragments, add the new node
- * to the fragment list.
- */
-
-#ifndef __ECOS
 #include <linux/kernel.h>
 #include <linux/slab.h>
 #include <linux/fs.h>
+#include <linux/crc32.h>
+#include <linux/pagemap.h>
 #include <linux/mtd/mtd.h>
-#endif
+#include <linux/compiler.h>
 #include "nodelist.h"
-#include "crc32.h"
 
+static int jffs2_add_frag_to_fragtree(struct jffs2_sb_info *c, struct rb_root *list, struct jffs2_node_frag *newfrag);
 
-D1(void jffs2_print_frag_list(struct jffs2_inode_info *f)
+#if CONFIG_JFFS2_FS_DEBUG >= 1
+static void jffs2_print_fragtree(struct rb_root *list, int permitbug)
 {
-	struct jffs2_node_frag *this = f->fraglist;
+	struct jffs2_node_frag *this = frag_first(list);
+	uint32_t lastofs = 0;
+	int buggy = 0;
 
 	while(this) {
 		if (this->node)
-			printk(KERN_DEBUG "frag %04x-%04x: 0x%08x on flash (*%p->%p)\n", this->ofs, this->ofs+this->size, this->node->raw->flash_offset &~3, this, this->next);
+			printk(KERN_DEBUG "frag %04x-%04x: 0x%08x(%d) on flash (*%p). left (%p), right (%p), parent (%p)\n",
+			       this->ofs, this->ofs+this->size, ref_offset(this->node->raw), ref_flags(this->node->raw),
+			       this, frag_left(this), frag_right(this), frag_parent(this));
 		else 
-			printk(KERN_DEBUG "frag %04x-%04x: hole (*%p->%p)\n", this->ofs, this->ofs+this->size, this, this->next);
-		this = this->next;
+			printk(KERN_DEBUG "frag %04x-%04x: hole (*%p). left (%p} right (%p), parent (%p)\n", this->ofs, 
+			       this->ofs+this->size, this, frag_left(this), frag_right(this), frag_parent(this));
+		if (this->ofs != lastofs)
+			buggy = 1;
+		lastofs = this->ofs+this->size;
+		this = frag_next(this);
 	}
-	if (f->metadata) {
-		printk(KERN_DEBUG "metadata at 0x%08x\n", f->metadata->raw->flash_offset &~3);
+	if (buggy && !permitbug) {
+		printk(KERN_CRIT "Frag tree got a hole in it\n");
+		BUG();
 	}
-})
-
+}
 
-int jffs2_add_full_dnode_to_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, struct jffs2_full_dnode *fn)
+void jffs2_print_frag_list(struct jffs2_inode_info *f)
 {
-	int ret;
-	D1(printk(KERN_DEBUG "jffs2_add_full_dnode_to_inode(ino #%u, f %p, fn %p)\n", f->inocache->ino, f, fn));
+	jffs2_print_fragtree(&f->fragtree, 0);
 
-	ret = jffs2_add_full_dnode_to_fraglist(c, &f->fraglist, fn);
-
-	D2(jffs2_print_frag_list(f));
-	return ret;
+	if (f->metadata) {
+		printk(KERN_DEBUG "metadata at 0x%08x\n", ref_offset(f->metadata->raw));
+	}
 }
+#endif /* D1 */
 
 static void jffs2_obsolete_node_frag(struct jffs2_sb_info *c, struct jffs2_node_frag *this)
 {
@@ -60,42 +65,38 @@ static void jffs2_obsolete_node_frag(str
 		if (!this->node->frags) {
 			/* The node has no valid frags left. It's totally obsoleted */
 			D2(printk(KERN_DEBUG "Marking old node @0x%08x (0x%04x-0x%04x) obsolete\n",
-				  this->node->raw->flash_offset &~3, this->node->ofs, this->node->ofs+this->node->size));
+				  ref_offset(this->node->raw), this->node->ofs, this->node->ofs+this->node->size));
 			jffs2_mark_node_obsolete(c, this->node->raw);
 			jffs2_free_full_dnode(this->node);
 		} else {
-			D2(printk(KERN_DEBUG "Not marking old node @0x%08x (0x%04x-0x%04x) obsolete. frags is %d\n",
-				  this->node->raw->flash_offset &~3, this->node->ofs, this->node->ofs+this->node->size,
+			D2(printk(KERN_DEBUG "Marking old node @0x%08x (0x%04x-0x%04x) REF_NORMAL. frags is %d\n",
+				  ref_offset(this->node->raw), this->node->ofs, this->node->ofs+this->node->size,
 				  this->node->frags));
+			mark_ref_normal(this->node->raw);
 		}
 		
 	}
 	jffs2_free_node_frag(this);
 }
 
-/* Doesn't set inode->i_size */
-int jffs2_add_full_dnode_to_fraglist(struct jffs2_sb_info *c, struct jffs2_node_frag **list, struct jffs2_full_dnode *fn)
+/* Given an inode, probably with existing list of fragments, add the new node
+ * to the fragment list.
+ */
+int jffs2_add_full_dnode_to_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, struct jffs2_full_dnode *fn)
 {
-	
-	struct jffs2_node_frag *this, **prev, *old;
-	struct jffs2_node_frag *newfrag, *newfrag2;
-	uint32_t lastend = 0;
+	int ret;
+	struct jffs2_node_frag *newfrag;
 
+	D1(printk(KERN_DEBUG "jffs2_add_full_dnode_to_inode(ino #%u, f %p, fn %p)\n", f->inocache->ino, f, fn));
 
 	newfrag = jffs2_alloc_node_frag();
-	if (!newfrag) {
+	if (unlikely(!newfrag))
 		return -ENOMEM;
-	}
 
-	D2(if (fn->raw)
-		printk(KERN_DEBUG "adding node %04x-%04x @0x%08x on flash, newfrag *%p\n", fn->ofs, fn->ofs+fn->size, fn->raw->flash_offset &~3, newfrag);
-	else
-		printk(KERN_DEBUG "adding hole node %04x-%04x on flash, newfrag *%p\n", fn->ofs, fn->ofs+fn->size, newfrag));
+	D2(printk(KERN_DEBUG "adding node %04x-%04x @0x%08x on flash, newfrag *%p\n",
+		  fn->ofs, fn->ofs+fn->size, ref_offset(fn->raw), newfrag));
 	
-	prev = list;
-	this = *list;
-
-	if (!fn->size) {
+	if (unlikely(!fn->size)) {
 		jffs2_free_node_frag(newfrag);
 		return 0;
 	}
@@ -104,141 +105,283 @@ int jffs2_add_full_dnode_to_fraglist(str
 	newfrag->size = fn->size;
 	newfrag->node = fn;
 	newfrag->node->frags = 1;
-	newfrag->next = (void *)0xdeadbeef;
+
+	ret = jffs2_add_frag_to_fragtree(c, &f->fragtree, newfrag);
+	if (ret)
+		return ret;
+
+	/* If we now share a page with other nodes, mark either previous
+	   or next node REF_NORMAL, as appropriate.  */
+	if (newfrag->ofs & (PAGE_CACHE_SIZE-1)) {
+		struct jffs2_node_frag *prev = frag_prev(newfrag);
+
+		mark_ref_normal(fn->raw);
+		/* If we don't start at zero there's _always_ a previous */	
+		if (prev->node)
+			mark_ref_normal(prev->node->raw);
+	}
+
+	if ((newfrag->ofs+newfrag->size) & (PAGE_CACHE_SIZE-1)) {
+		struct jffs2_node_frag *next = frag_next(newfrag);
+		
+		if (next) {
+			mark_ref_normal(fn->raw);
+			if (next->node)
+				mark_ref_normal(next->node->raw);
+		}
+	}
+	D2(jffs2_print_frag_list(f));
+	return 0;
+}
+
+/* Doesn't set inode->i_size */
+static int jffs2_add_frag_to_fragtree(struct jffs2_sb_info *c, struct rb_root *list, struct jffs2_node_frag *newfrag)
+{
+	struct jffs2_node_frag *this;
+	uint32_t lastend;
 
 	/* Skip all the nodes which are completed before this one starts */
-	while(this && fn->ofs >= this->ofs+this->size) {
-		lastend = this->ofs + this->size;
+	this = jffs2_lookup_node_frag(list, newfrag->node->ofs);
 
-		D2(printk(KERN_DEBUG "j_a_f_d_t_f: skipping frag 0x%04x-0x%04x; phys 0x%08x (*%p->%p)\n", 
-			  this->ofs, this->ofs+this->size, this->node?(this->node->raw->flash_offset &~3):0xffffffff, this, this->next));
-		prev = &this->next;
-		this = this->next;
+	if (this) {
+		D2(printk(KERN_DEBUG "j_a_f_d_t_f: Lookup gave frag 0x%04x-0x%04x; phys 0x%08x (*%p)\n",
+			  this->ofs, this->ofs+this->size, this->node?(ref_offset(this->node->raw)):0xffffffff, this));
+		lastend = this->ofs + this->size;
+	} else {
+		D2(printk(KERN_DEBUG "j_a_f_d_t_f: Lookup gave no frag\n"));
+		lastend = 0;
 	}
-
+			  
 	/* See if we ran off the end of the list */
-	if (!this) {
+	if (lastend <= newfrag->ofs) {
 		/* We did */
-		if (lastend < fn->ofs) {
+
+		/* Check if 'this' node was on the same page as the new node.
+		   If so, both 'this' and the new node get marked REF_NORMAL so
+		   the GC can take a look.
+		*/
+		if ((lastend-1) >> PAGE_CACHE_SHIFT == newfrag->ofs >> PAGE_CACHE_SHIFT) {
+			if (this->node)
+				mark_ref_normal(this->node->raw);
+			mark_ref_normal(newfrag->node->raw);
+		}
+
+		if (lastend < newfrag->node->ofs) {
 			/* ... and we need to put a hole in before the new node */
 			struct jffs2_node_frag *holefrag = jffs2_alloc_node_frag();
 			if (!holefrag)
 				return -ENOMEM;
 			holefrag->ofs = lastend;
-			holefrag->size = fn->ofs - lastend;
-			holefrag->next = NULL;
+			holefrag->size = newfrag->node->ofs - lastend;
 			holefrag->node = NULL;
-			*prev = holefrag;
-			prev = &holefrag->next;
+			if (this) {
+				/* By definition, the 'this' node has no right-hand child, 
+				   because there are no frags with offset greater than it.
+				   So that's where we want to put the hole */
+				D2(printk(KERN_DEBUG "Adding hole frag (%p) on right of node at (%p)\n", holefrag, this));
+				rb_link_node(&holefrag->rb, &this->rb, &this->rb.rb_right);
+			} else {
+				D2(printk(KERN_DEBUG "Adding hole frag (%p) at root of tree\n", holefrag));
+				rb_link_node(&holefrag->rb, NULL, &list->rb_node);
+			}
+			rb_insert_color(&holefrag->rb, list);
+			this = holefrag;
 		}
-		newfrag->next = NULL;
-		*prev = newfrag;
+		if (this) {
+			/* By definition, the 'this' node has no right-hand child, 
+			   because there are no frags with offset greater than it.
+			   So that's where we want to put the hole */
+			D2(printk(KERN_DEBUG "Adding new frag (%p) on right of node at (%p)\n", newfrag, this));
+			rb_link_node(&newfrag->rb, &this->rb, &this->rb.rb_right);			
+		} else {
+			D2(printk(KERN_DEBUG "Adding new frag (%p) at root of tree\n", newfrag));
+			rb_link_node(&newfrag->rb, NULL, &list->rb_node);
+		}
+		rb_insert_color(&newfrag->rb, list);
 		return 0;
 	}
 
-	D2(printk(KERN_DEBUG "j_a_f_d_t_f: dealing with frag 0x%04x-0x%04x; phys 0x%08x (*%p->%p)\n", 
-		  this->ofs, this->ofs+this->size, this->node?(this->node->raw->flash_offset &~3):0xffffffff, this, this->next));
+	D2(printk(KERN_DEBUG "j_a_f_d_t_f: dealing with frag 0x%04x-0x%04x; phys 0x%08x (*%p)\n", 
+		  this->ofs, this->ofs+this->size, this->node?(ref_offset(this->node->raw)):0xffffffff, this));
 
-	/* OK. 'this' is pointing at the first frag that fn->ofs at least partially obsoletes,
-	 * - i.e. fn->ofs < this->ofs+this->size && fn->ofs >= this->ofs  
+	/* OK. 'this' is pointing at the first frag that newfrag->ofs at least partially obsoletes,
+	 * - i.e. newfrag->ofs < this->ofs+this->size && newfrag->ofs >= this->ofs  
 	 */
-	if (fn->ofs > this->ofs) {
+	if (newfrag->ofs > this->ofs) {
 		/* This node isn't completely obsoleted. The start of it remains valid */
-		if (this->ofs + this->size > fn->ofs + fn->size) {
+
+		/* Mark the new node and the partially covered node REF_NORMAL -- let
+		   the GC take a look at them */
+		mark_ref_normal(newfrag->node->raw);
+		if (this->node)
+			mark_ref_normal(this->node->raw);
+
+		if (this->ofs + this->size > newfrag->ofs + newfrag->size) {
 			/* The new node splits 'this' frag into two */
-			newfrag2 = jffs2_alloc_node_frag();
+			struct jffs2_node_frag *newfrag2 = jffs2_alloc_node_frag();
 			if (!newfrag2) {
 				jffs2_free_node_frag(newfrag);
 				return -ENOMEM;
 			}
-			D1(printk(KERN_DEBUG "split old frag 0x%04x-0x%04x -->", this->ofs, this->ofs+this->size);
+			D2(printk(KERN_DEBUG "split old frag 0x%04x-0x%04x -->", this->ofs, this->ofs+this->size);
 			if (this->node)
-				printk("phys 0x%08x\n", this->node->raw->flash_offset &~3);
+				printk("phys 0x%08x\n", ref_offset(this->node->raw));
 			else 
 				printk("hole\n");
 			   )
-			newfrag2->ofs = fn->ofs + fn->size;
+			
+			/* New second frag pointing to this's node */
+			newfrag2->ofs = newfrag->ofs + newfrag->size;
 			newfrag2->size = (this->ofs+this->size) - newfrag2->ofs;
-			newfrag2->next = this->next;
 			newfrag2->node = this->node;
 			if (this->node)
 				this->node->frags++;
-			newfrag->next = newfrag2;
-			this->next = newfrag;
+
+			/* Adjust size of original 'this' */
 			this->size = newfrag->ofs - this->ofs;
+
+			/* Now, we know there's no node with offset
+			   greater than this->ofs but smaller than
+			   newfrag2->ofs or newfrag->ofs, for obvious
+			   reasons. So we can do a tree insert from
+			   'this' to insert newfrag, and a tree insert
+			   from newfrag to insert newfrag2. */
+			jffs2_fragtree_insert(newfrag, this);
+			rb_insert_color(&newfrag->rb, list);
+			
+			jffs2_fragtree_insert(newfrag2, newfrag);
+			rb_insert_color(&newfrag2->rb, list);
+			
 			return 0;
 		}
 		/* New node just reduces 'this' frag in size, doesn't split it */
-		this->size = fn->ofs - this->ofs;
-		newfrag->next = this->next;
-		this->next = newfrag;
-		this = newfrag->next;
+		this->size = newfrag->ofs - this->ofs;
+
+		/* Again, we know it lives down here in the tree */
+		jffs2_fragtree_insert(newfrag, this);
+		rb_insert_color(&newfrag->rb, list);
 	} else {
-		D2(printk(KERN_DEBUG "Inserting newfrag (*%p) in before 'this' (*%p)\n", newfrag, this));
-		*prev = newfrag;
-	        newfrag->next = this;
+		/* New frag starts at the same point as 'this' used to. Replace 
+		   it in the tree without doing a delete and insertion */
+		D2(printk(KERN_DEBUG "Inserting newfrag (*%p),%d-%d in before 'this' (*%p),%d-%d\n",
+			  newfrag, newfrag->ofs, newfrag->ofs+newfrag->size,
+			  this, this->ofs, this->ofs+this->size));
+	
+		rb_replace_node(&this->rb, &newfrag->rb, list);
+		
+		if (newfrag->ofs + newfrag->size >= this->ofs+this->size) {
+			D2(printk(KERN_DEBUG "Obsoleting node frag %p (%x-%x)\n", this, this->ofs, this->ofs+this->size));
+			jffs2_obsolete_node_frag(c, this);
+		} else {
+			this->ofs += newfrag->size;
+			this->size -= newfrag->size;
+
+			jffs2_fragtree_insert(this, newfrag);
+			rb_insert_color(&this->rb, list);
+			return 0;
+		}
 	}
-	/* OK, now we have newfrag added in the correct place in the list, but
-	   newfrag->next points to a fragment which may be overlapping it
+	/* OK, now we have newfrag added in the correct place in the tree, but
+	   frag_next(newfrag) may be a fragment which is overlapped by it 
 	*/
-	while (this && newfrag->ofs + newfrag->size >= this->ofs + this->size) {
-		/* 'this' frag is obsoleted. */
-		old = this;
-		this = old->next;
-		jffs2_obsolete_node_frag(c, old);
+	while ((this = frag_next(newfrag)) && newfrag->ofs + newfrag->size >= this->ofs + this->size) {
+		/* 'this' frag is obsoleted completely. */
+		D2(printk(KERN_DEBUG "Obsoleting node frag %p (%x-%x) and removing from tree\n", this, this->ofs, this->ofs+this->size));
+		rb_erase(&this->rb, list);
+		jffs2_obsolete_node_frag(c, this);
 	}
 	/* Now we're pointing at the first frag which isn't totally obsoleted by 
 	   the new frag */
-	newfrag->next = this;
 
 	if (!this || newfrag->ofs + newfrag->size == this->ofs) {
 		return 0;
 	}
-	/* Still some overlap */
+	/* Still some overlap but we don't need to move it in the tree */
 	this->size = (this->ofs + this->size) - (newfrag->ofs + newfrag->size);
 	this->ofs = newfrag->ofs + newfrag->size;
+
+	/* And mark them REF_NORMAL so the GC takes a look at them */
+	if (this->node)
+		mark_ref_normal(this->node->raw);
+	mark_ref_normal(newfrag->node->raw);
+
 	return 0;
 }
 
-void jffs2_truncate_fraglist (struct jffs2_sb_info *c, struct jffs2_node_frag **list, uint32_t size)
+void jffs2_truncate_fraglist (struct jffs2_sb_info *c, struct rb_root *list, uint32_t size)
 {
+	struct jffs2_node_frag *frag = jffs2_lookup_node_frag(list, size);
+
 	D1(printk(KERN_DEBUG "Truncating fraglist to 0x%08x bytes\n", size));
 
-	while (*list) {
-		if ((*list)->ofs >= size) {
-			struct jffs2_node_frag *this = *list;
-			*list = this->next;
-			D1(printk(KERN_DEBUG "Removing frag 0x%08x-0x%08x\n", this->ofs, this->ofs+this->size));
-			jffs2_obsolete_node_frag(c, this);
-			continue;
-		} else if ((*list)->ofs + (*list)->size > size) {
-			D1(printk(KERN_DEBUG "Truncating frag 0x%08x-0x%08x\n", (*list)->ofs, (*list)->ofs + (*list)->size));
-			(*list)->size = size - (*list)->ofs;
+	/* We know frag->ofs <= size. That's what lookup does for us */
+	if (frag && frag->ofs != size) {
+		if (frag->ofs+frag->size >= size) {
+			D1(printk(KERN_DEBUG "Truncating frag 0x%08x-0x%08x\n", frag->ofs, frag->ofs+frag->size));
+			frag->size = size - frag->ofs;
 		}
-		list = &(*list)->next;
+		frag = frag_next(frag);
+	}
+	while (frag && frag->ofs >= size) {
+		struct jffs2_node_frag *next = frag_next(frag);
+
+		D1(printk(KERN_DEBUG "Removing frag 0x%08x-0x%08x\n", frag->ofs, frag->ofs+frag->size));
+		frag_erase(frag, list);
+		jffs2_obsolete_node_frag(c, frag);
+		frag = next;
 	}
 }
 
 /* Scan the list of all nodes present for this ino, build map of versions, etc. */
 
+static int jffs2_do_read_inode_internal(struct jffs2_sb_info *c, 
+					struct jffs2_inode_info *f,
+					struct jffs2_raw_inode *latest_node);
+
 int jffs2_do_read_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, 
 			uint32_t ino, struct jffs2_raw_inode *latest_node)
 {
-	struct jffs2_tmp_dnode_info *tn_list, *tn;
-	struct jffs2_full_dirent *fd_list;
-	struct jffs2_full_dnode *fn = NULL;
-	uint32_t crc;
-	size_t retlen;
-	int ret;
-
-	init_MUTEX(&f->sem);
-
 	D2(printk(KERN_DEBUG "jffs2_do_read_inode(): getting inocache\n"));
 
+ retry_inocache:
+	spin_lock(&c->inocache_lock);
 	f->inocache = jffs2_get_ino_cache(c, ino);
 
 	D2(printk(KERN_DEBUG "jffs2_do_read_inode(): Got inocache at %p\n", f->inocache));
 
+	if (f->inocache) {
+		/* Check its state. We may need to wait before we can use it */
+		switch(f->inocache->state) {
+		case INO_STATE_UNCHECKED:
+		case INO_STATE_CHECKEDABSENT:
+			f->inocache->state = INO_STATE_READING;
+			break;
+			
+		case INO_STATE_CHECKING:
+		case INO_STATE_GC:
+			/* If it's in either of these states, we need
+			   to wait for whoever's got it to finish and
+			   put it back. */
+			D1(printk(KERN_DEBUG "jffs2_get_ino_cache_read waiting for ino #%u in state %d\n",
+				  ino, f->inocache->state));
+			sleep_on_spinunlock(&c->inocache_wq, &c->inocache_lock);
+			goto retry_inocache;
+
+		case INO_STATE_READING:
+		case INO_STATE_PRESENT:
+			/* Eep. This should never happen. It can
+			happen if Linux calls read_inode() again
+			before clear_inode() has finished though. */
+			printk(KERN_WARNING "Eep. Trying to read_inode #%u when it's already in state %d!\n", ino, f->inocache->state);
+			/* Fail. That's probably better than allowing it to succeed */
+			f->inocache = NULL;
+			break;
+
+		default:
+			BUG();
+		}
+	}
+	spin_unlock(&c->inocache_lock);
 	if (!f->inocache && ino == 1) {
 		/* Special case - no root inode on medium */
 		f->inocache = jffs2_alloc_inode_cache();
@@ -250,32 +393,65 @@ int jffs2_do_read_inode(struct jffs2_sb_
 		memset(f->inocache, 0, sizeof(struct jffs2_inode_cache));
 		f->inocache->ino = f->inocache->nlink = 1;
 		f->inocache->nodes = (struct jffs2_raw_node_ref *)f->inocache;
+		f->inocache->state = INO_STATE_READING;
 		jffs2_add_ino_cache(c, f->inocache);
 	}
 	if (!f->inocache) {
 		printk(KERN_WARNING "jffs2_do_read_inode() on nonexistent ino %u\n", ino);
 		return -ENOENT;
 	}
-	D1(printk(KERN_DEBUG "jffs2_do_read_inode(): ino #%u nlink is %d\n", ino, f->inocache->nlink));
+
+	return jffs2_do_read_inode_internal(c, f, latest_node);
+}
+
+int jffs2_do_crccheck_inode(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic)
+{
+	struct jffs2_raw_inode n;
+	struct jffs2_inode_info *f = kmalloc(sizeof(*f), GFP_KERNEL);
+
+	if (!f)
+		return -ENOMEM;
+
+	memset(f, 0, sizeof(*f));
+	init_MUTEX_LOCKED(&f->sem);
+	f->inocache = ic;
+
+	return jffs2_do_read_inode_internal(c, f, &n);
+}
+
+static int jffs2_do_read_inode_internal(struct jffs2_sb_info *c, 
+					struct jffs2_inode_info *f,
+					struct jffs2_raw_inode *latest_node)
+{
+	struct jffs2_tmp_dnode_info *tn_list, *tn;
+	struct jffs2_full_dirent *fd_list;
+	struct jffs2_full_dnode *fn = NULL;
+	uint32_t crc;
+	uint32_t latest_mctime, mctime_ver;
+	uint32_t mdata_ver = 0;
+	size_t retlen;
+	int ret;
+
+	D1(printk(KERN_DEBUG "jffs2_do_read_inode_internal(): ino #%u nlink is %d\n", f->inocache->ino, f->inocache->nlink));
 
 	/* Grab all nodes relevant to this ino */
-	ret = jffs2_get_inode_nodes(c, ino, f, &tn_list, &fd_list, &f->highest_version);
+	ret = jffs2_get_inode_nodes(c, f->inocache->ino, f, &tn_list, &fd_list, &f->highest_version, &latest_mctime, &mctime_ver);
 
 	if (ret) {
-		printk(KERN_CRIT "jffs2_get_inode_nodes() for ino %u returned %d\n", ino, ret);
+		printk(KERN_CRIT "jffs2_get_inode_nodes() for ino %u returned %d\n", f->inocache->ino, ret);
+		if (f->inocache->state == INO_STATE_READING)
+			jffs2_set_inocache_state(c, f->inocache, INO_STATE_CHECKEDABSENT);
 		return ret;
 	}
 	f->dents = fd_list;
 
 	while (tn_list) {
-		static uint32_t mdata_ver = 0;
-
 		tn = tn_list;
 
 		fn = tn->fn;
 
 		if (f->metadata && tn->version > mdata_ver) {
-			D1(printk(KERN_DEBUG "Obsoleting old metadata at 0x%08x\n", f->metadata->raw->flash_offset &~3));
+			D1(printk(KERN_DEBUG "Obsoleting old metadata at 0x%08x\n", ref_offset(f->metadata->raw)));
 			jffs2_mark_node_obsolete(c, f->metadata->raw);
 			jffs2_free_full_dnode(f->metadata);
 			f->metadata = NULL;
@@ -287,7 +463,7 @@ int jffs2_do_read_inode(struct jffs2_sb_
 			jffs2_add_full_dnode_to_inode(c, f, fn);
 		} else {
 			/* Zero-sized node at end of version list. Just a metadata update */
-			D1(printk(KERN_DEBUG "metadata @%08x: ver %d\n", fn->raw->flash_offset &~3, tn->version));
+			D1(printk(KERN_DEBUG "metadata @%08x: ver %d\n", ref_offset(fn->raw), tn->version));
 			f->metadata = fn;
 			mdata_ver = tn->version;
 		}
@@ -296,41 +472,57 @@ int jffs2_do_read_inode(struct jffs2_sb_
 	}
 	if (!fn) {
 		/* No data nodes for this inode. */
-		if (ino != 1) {
-			printk(KERN_WARNING "jffs2_do_read_inode(): No data nodes found for ino #%u\n", ino);
+		if (f->inocache->ino != 1) {
+			printk(KERN_WARNING "jffs2_do_read_inode(): No data nodes found for ino #%u\n", f->inocache->ino);
 			if (!fd_list) {
+				if (f->inocache->state == INO_STATE_READING)
+					jffs2_set_inocache_state(c, f->inocache, INO_STATE_CHECKEDABSENT);
 				return -EIO;
 			}
 			printk(KERN_WARNING "jffs2_do_read_inode(): But it has children so we fake some modes for it\n");
 		}
-		latest_node->mode = S_IFDIR | S_IRUGO | S_IWUSR | S_IXUGO;
-		latest_node->atime = latest_node->ctime = latest_node->mtime = 0;
-		latest_node->isize = 0;
-		latest_node->gid = 0;
-		latest_node->uid = 0;
+		latest_node->mode = cpu_to_jemode(S_IFDIR|S_IRUGO|S_IWUSR|S_IXUGO);
+		latest_node->version = cpu_to_je32(0);
+		latest_node->atime = latest_node->ctime = latest_node->mtime = cpu_to_je32(0);
+		latest_node->isize = cpu_to_je32(0);
+		latest_node->gid = cpu_to_je16(0);
+		latest_node->uid = cpu_to_je16(0);
+		if (f->inocache->state == INO_STATE_READING)
+			jffs2_set_inocache_state(c, f->inocache, INO_STATE_PRESENT);
 		return 0;
 	}
 
-	ret = jffs2_flash_read(c, fn->raw->flash_offset & ~3, sizeof(*latest_node), &retlen, (void *)latest_node);
+	ret = jffs2_flash_read(c, ref_offset(fn->raw), sizeof(*latest_node), &retlen, (void *)latest_node);
 	if (ret || retlen != sizeof(*latest_node)) {
-		printk(KERN_NOTICE "MTD read in jffs2_do_read_inode() failed: Returned %d, %ld of %d bytes read\n",
-		       ret, (long)retlen, sizeof(*latest_node));
+		printk(KERN_NOTICE "MTD read in jffs2_do_read_inode() failed: Returned %d, %zd of %zd bytes read\n",
+		       ret, retlen, sizeof(*latest_node));
 		/* FIXME: If this fails, there seems to be a memory leak. Find it. */
+		up(&f->sem);
 		jffs2_do_clear_inode(c, f);
 		return ret?ret:-EIO;
 	}
 
 	crc = crc32(0, latest_node, sizeof(*latest_node)-8);
-	if (crc != latest_node->node_crc) {
-		printk(KERN_NOTICE "CRC failed for read_inode of inode %u at physical location 0x%x\n", ino, fn->raw->flash_offset & ~3);
+	if (crc != je32_to_cpu(latest_node->node_crc)) {
+		printk(KERN_NOTICE "CRC failed for read_inode of inode %u at physical location 0x%x\n", f->inocache->ino, ref_offset(fn->raw));
+		up(&f->sem);
 		jffs2_do_clear_inode(c, f);
 		return -EIO;
 	}
 
-	switch(latest_node->mode & S_IFMT) {
+	switch(jemode_to_cpu(latest_node->mode) & S_IFMT) {
+	case S_IFDIR:
+		if (mctime_ver > je32_to_cpu(latest_node->version)) {
+			/* The times in the latest_node are actually older than
+			   mctime in the latest dirent. Cheat. */
+			latest_node->ctime = latest_node->mtime = cpu_to_je32(latest_mctime);
+		}
+		break;
+
+			
 	case S_IFREG:
 		/* If it was a regular file, truncate it to the latest node's isize */
-		jffs2_truncate_fraglist(c, &f->fraglist, latest_node->isize);
+		jffs2_truncate_fraglist(c, &f->fragtree, je32_to_cpu(latest_node->isize));
 		break;
 
 	case S_IFLNK:
@@ -338,166 +530,92 @@ int jffs2_do_read_inode(struct jffs2_sb_
 		   Remove this when dwmw2 comes to his senses and stops
 		   symlinks from being an entirely gratuitous special
 		   case. */
-		if (!latest_node->isize)
+		if (!je32_to_cpu(latest_node->isize))
 			latest_node->isize = latest_node->dsize;
 		/* fall through... */
 
 	case S_IFBLK:
 	case S_IFCHR:
-		/* Xertain inode types should have only one data node, and it's
+		/* Certain inode types should have only one data node, and it's
 		   kept as the metadata node */
 		if (f->metadata) {
-			printk(KERN_WARNING "Argh. Special inode #%u with mode 0%o had metadata node\n", ino, latest_node->mode);
+			printk(KERN_WARNING "Argh. Special inode #%u with mode 0%o had metadata node\n",
+			       f->inocache->ino, jemode_to_cpu(latest_node->mode));
+			up(&f->sem);
 			jffs2_do_clear_inode(c, f);
 			return -EIO;
 		}
-		if (!f->fraglist) {
-			printk(KERN_WARNING "Argh. Special inode #%u with mode 0%o has no fragments\n", ino, latest_node->mode);
+		if (!frag_first(&f->fragtree)) {
+			printk(KERN_WARNING "Argh. Special inode #%u with mode 0%o has no fragments\n",
+			       f->inocache->ino, jemode_to_cpu(latest_node->mode));
+			up(&f->sem);
 			jffs2_do_clear_inode(c, f);
 			return -EIO;
 		}
 		/* ASSERT: f->fraglist != NULL */
-		if (f->fraglist->next) {
-			printk(KERN_WARNING "Argh. Special inode #%u with mode 0%o had more than one node\n", ino, latest_node->mode);
+		if (frag_next(frag_first(&f->fragtree))) {
+			printk(KERN_WARNING "Argh. Special inode #%u with mode 0x%x had more than one node\n",
+			       f->inocache->ino, jemode_to_cpu(latest_node->mode));
 			/* FIXME: Deal with it - check crc32, check for duplicate node, check times and discard the older one */
+			up(&f->sem);
 			jffs2_do_clear_inode(c, f);
 			return -EIO;
 		}
 		/* OK. We're happy */
-		f->metadata = f->fraglist->node;
-		jffs2_free_node_frag(f->fraglist);
-		f->fraglist = NULL;
+		f->metadata = frag_first(&f->fragtree)->node;
+		jffs2_free_node_frag(frag_first(&f->fragtree));
+		f->fragtree = RB_ROOT;
 		break;
 	}
+	if (f->inocache->state == INO_STATE_READING)
+		jffs2_set_inocache_state(c, f->inocache, INO_STATE_PRESENT);
 
 	return 0;
 }
 
-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);
-
-	memset(f, 0, sizeof(*f));
-	
-	ret = jffs2_do_read_inode(c, f, inode->i_ino, &latest_node);
-
-	if (ret) {
-		make_bad_inode(inode);
-		return;
-	}
-	inode->i_mode = latest_node.mode;
-	inode->i_uid = latest_node.uid;
-	inode->i_gid = latest_node.gid;
-	inode->i_size = latest_node.isize;
-	inode->i_atime = latest_node.atime;
-	inode->i_mtime = latest_node.mtime;
-	inode->i_ctime = latest_node.ctime;
-
-	inode->i_nlink = f->inocache->nlink;
-
-#ifndef __ECOS
-	inode->i_blksize = PAGE_SIZE;
-	inode->i_blocks = (inode->i_size + 511) >> 9;
-	
-/* ifdef ugliness - when we sanify the eCos version of inode cache this whole
-   routine will probably be replaced */
-
-	switch (inode->i_mode & S_IFMT) {
-		unsigned short rdev;
-
-	case S_IFLNK:
-		inode->i_op = &jffs2_symlink_inode_operations;
-		break;
-		
-	case S_IFDIR:
-		inode->i_op = &jffs2_dir_inode_operations;
-		inode->i_fop = &jffs2_dir_operations;
-		break;
-
-	case S_IFREG:
-		inode->i_op = &jffs2_file_inode_operations;
-		inode->i_fop = &jffs2_file_operations;
-		inode->i_mapping->a_ops = &jffs2_file_address_operations;
-		inode->i_mapping->nrpages = 0;
-		break;
-
-	case S_IFBLK:
-	case S_IFCHR:
-		/* Read the device numbers from the media */
-		D1(printk(KERN_DEBUG "Reading device numbers from flash\n"));
-		if (jffs2_read_dnode(c, f->metadata, (char *)&rdev, 0, sizeof(rdev)) < 0) {
-			/* Eep */
-			printk(KERN_NOTICE "Read device numbers for inode %lu failed\n", (unsigned long)inode->i_ino);
-			jffs2_do_clear_inode(c, f);
-			make_bad_inode(inode);
-			return;
-		}			
-
-	case S_IFSOCK:
-	case S_IFIFO:
-		inode->i_op = &jffs2_file_inode_operations;
-		init_special_inode(inode, inode->i_mode, kdev_t_to_nr(MKDEV(rdev>>8, rdev&0xff)));
-		break;
-
-	default:
-		printk(KERN_WARNING "jffs2_read_inode(): Bogus imode %o for ino %lu\n", inode->i_mode, (unsigned long)inode->i_ino);
-	}
-#endif /* !__ECOS */
-	D1(printk(KERN_DEBUG "jffs2_read_inode() returning\n"));
-}
-
 void jffs2_do_clear_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f)
 {
-	struct jffs2_node_frag *frag, *frags;
 	struct jffs2_full_dirent *fd, *fds;
+	/* I don't think we care about the potential race due to reading this
+	   without f->sem. It can never get undeleted. */
+	int deleted = f->inocache && !f->inocache->nlink;
 
-	frags = f->fraglist;
-	fds = f->dents;
+	/* If it's a deleted inode, grab the alloc_sem. This prevents
+	   jffs2_garbage_collect_pass() from deciding that it wants to
+	   garbage collect one of the nodes we're just about to mark 
+	   obsolete -- by the time we drop alloc_sem and return, all
+	   the nodes are marked obsolete, and jffs2_g_c_pass() won't
+	   call iget() for the inode in question.
+
+	   We also used to do this to keep the temporary BUG() in 
+	   jffs2_mark_node_obsolete() from triggering. 
+	*/
+	if(deleted)
+		down(&c->alloc_sem);
+
+	down(&f->sem);
+
 	if (f->metadata) {
-		if (!f->inocache->nlink)
+		if (deleted)
 			jffs2_mark_node_obsolete(c, f->metadata->raw);
 		jffs2_free_full_dnode(f->metadata);
 	}
 
-	while (frags) {
-		frag = frags;
-		frags = frag->next;
-		D2(printk(KERN_DEBUG "jffs2_do_clear_inode: frag at 0x%x-0x%x: node %p, frags %d--\n", frag->ofs, frag->ofs+frag->size, frag->node, frag->node?frag->node->frags:0));
+	jffs2_kill_fragtree(&f->fragtree, deleted?c:NULL);
 
-		if (frag->node && !(--frag->node->frags)) {
-			/* Not a hole, and it's the final remaining frag of this node. Free the node */
-			if (!f->inocache->nlink)
-				jffs2_mark_node_obsolete(c, frag->node->raw);
+	fds = f->dents;
 
-			jffs2_free_full_dnode(frag->node);
-		}
-		jffs2_free_node_frag(frag);
-	}
 	while(fds) {
 		fd = fds;
 		fds = fd->next;
 		jffs2_free_full_dirent(fd);
 	}
-}
+
+	if (f->inocache && f->inocache->state != INO_STATE_CHECKING)
+		jffs2_set_inocache_state(c, f->inocache, INO_STATE_CHECKEDABSENT);
 
-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);
-	struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode);
-	
-	D1(printk(KERN_DEBUG "jffs2_clear_inode(): ino #%lu mode %o\n", inode->i_ino, inode->i_mode));
+	up(&f->sem);
 
-	jffs2_do_clear_inode(c, f);
+	if(deleted)
+		up(&c->alloc_sem);
 }
-
--- a/packages/fs/jffs2/current/src/scan.c
+++ b/packages/fs/jffs2/current/src/scan.c
@@ -7,19 +7,19 @@
  *
  * For licensing information, see the file 'LICENCE' in this directory.
  *
- * $Id: scan.c,v 1.57 2002/01/09 13:25:58 dwmw2 Exp $
+ * $Id: scan.c,v 1.98 2003/01/22 16:33:39 dwmw2 Exp $
  *
  */
-
-#ifndef __ECOS
 #include <linux/kernel.h>
+#include <linux/sched.h>
 #include <linux/slab.h>
 #include <linux/mtd/mtd.h>
 #include <linux/pagemap.h>
-#endif
+#include <linux/crc32.h>
+#include <linux/compiler.h>
 #include "nodelist.h"
-#include "crc32.h"
 
+#define EMPTY_SCAN_SIZE 1024
 
 #define DIRTY_SPACE(x) do { typeof(x) _x = (x); \
 		c->free_size -= _x; c->dirty_size += _x; \
@@ -29,6 +29,10 @@
 		c->free_size -= _x; c->used_size += _x; \
 		jeb->free_size -= _x ; jeb->used_size += _x; \
 		}while(0)
+#define UNCHECKED_SPACE(x) do { typeof(x) _x = (x); \
+		c->free_size -= _x; c->unchecked_size += _x; \
+		jeb->free_size -= _x ; jeb->unchecked_size += _x; \
+		}while(0)
 
 #define noisy_printk(noise, args...) do { \
 	if (*(noise)) { \
@@ -40,37 +44,75 @@
 	} \
 } while(0)
 
-static int jffs2_scan_eraseblock (struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb);
+static uint32_t pseudo_random;
+
+static int jffs2_scan_eraseblock (struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb,
+				  unsigned char *buf, uint32_t buf_size);
 
 /* These helper functions _must_ increase ofs and also do the dirty/used space accounting. 
  * Returning an error will abort the mount - bad checksums etc. should just mark the space
  * as dirty.
  */
-static int jffs2_scan_empty(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, uint32_t *ofs, int *noise);
-static int jffs2_scan_inode_node(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, uint32_t *ofs);
-static int jffs2_scan_dirent_node(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, uint32_t *ofs);
+static int jffs2_scan_inode_node(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, 
+				 struct jffs2_raw_inode *ri, uint32_t ofs);
+static int jffs2_scan_dirent_node(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb,
+				 struct jffs2_raw_dirent *rd, uint32_t ofs);
 
+#define BLK_STATE_ALLFF		0
+#define BLK_STATE_CLEAN		1
+#define BLK_STATE_PARTDIRTY	2
+#define BLK_STATE_CLEANMARKER	3
+#define BLK_STATE_ALLDIRTY	4
+#define BLK_STATE_BADBLOCK	5
 
 int jffs2_scan_medium(struct jffs2_sb_info *c)
 {
 	int i, ret;
-	uint32_t empty_blocks = 0;
+	uint32_t empty_blocks = 0, bad_blocks = 0;
+	unsigned char *flashbuf = NULL;
+	uint32_t buf_size = 0;
+#ifndef __ECOS
+	size_t pointlen;
 
-	if (!c->blocks) {
-		printk(KERN_WARNING "EEEK! c->blocks is NULL!\n");
-		return -EINVAL;
+	if (c->mtd->point) {
+		ret = c->mtd->point (c->mtd, 0, c->mtd->size, &pointlen, &flashbuf);
+		if (!ret && pointlen < c->mtd->size) {
+			/* Don't muck about if it won't let us point to the whole flash */
+			D1(printk(KERN_DEBUG "MTD point returned len too short: 0x%zx\n", pointlen));
+			c->mtd->unpoint(c->mtd, flashbuf, 0, c->mtd->size);
+			flashbuf = NULL;
+		}
+		if (ret)
+			D1(printk(KERN_DEBUG "MTD point failed %d\n", ret));
 	}
+#endif
+	if (!flashbuf) {
+		/* For NAND it's quicker to read a whole eraseblock at a time,
+		   apparently */
+		if (jffs2_cleanmarker_oob(c))
+			buf_size = c->sector_size;
+		else
+			buf_size = PAGE_SIZE;
+
+		D1(printk(KERN_DEBUG "Allocating readbuf of %d bytes\n", buf_size));
+		flashbuf = kmalloc(buf_size, GFP_KERNEL);
+		if (!flashbuf)
+			return -ENOMEM;
+	}
+
 	for (i=0; i<c->nr_blocks; i++) {
 		struct jffs2_eraseblock *jeb = &c->blocks[i];
 
-		ret = jffs2_scan_eraseblock(c, jeb);
+		ret = jffs2_scan_eraseblock(c, jeb, buf_size?flashbuf:(flashbuf+jeb->offset), buf_size);
+
 		if (ret < 0)
 			return ret;
 
 		ACCT_PARANOIA_CHECK(jeb);
 
 		/* Now decide which list to put it on */
-		if (ret == 1) {
+		switch(ret) {
+		case BLK_STATE_ALLFF:
 			/* 
 			 * Empty block.   Since we can't be sure it 
 			 * was entirely erased, we just queue it for erase
@@ -78,10 +120,12 @@ int jffs2_scan_medium(struct jffs2_sb_in
 			 * is complete.  Meanwhile we still count it as empty
 			 * for later checks.
 			 */
+			empty_blocks++;
 			list_add(&jeb->list, &c->erase_pending_list);
-			empty_blocks++;
 			c->nr_erasing_blocks++;
-		} else if (jeb->used_size == PAD(sizeof(struct jffs2_unknown_node)) && !jeb->first_node->next_in_ino) {
+			break;
+
+		case BLK_STATE_CLEANMARKER:
 			/* Only a CLEANMARKER node is valid */
 			if (!jeb->dirty_size) {
 				/* It's actually free */
@@ -93,10 +137,14 @@ int jffs2_scan_medium(struct jffs2_sb_in
 				list_add(&jeb->list, &c->erase_pending_list);
 				c->nr_erasing_blocks++;
 			}
-		} else if (jeb->used_size > c->sector_size - (2*sizeof(struct jffs2_raw_inode))) {
+			break;
+
+		case BLK_STATE_CLEAN:
                         /* Full (or almost full) of clean data. Clean list */
                         list_add(&jeb->list, &c->clean_list);
-                } else if (jeb->used_size) {
+			break;
+
+		case BLK_STATE_PARTDIRTY:
                         /* Some data, but not full. Dirty list. */
                         /* Except that we want to remember the block with most free space,
                            and stick it in the 'nextblock' position to start writing to it.
@@ -104,57 +152,206 @@ int jffs2_scan_medium(struct jffs2_sb_in
                            not the one with most free space.
                         */
                         if (jeb->free_size > 2*sizeof(struct jffs2_raw_inode) && 
-                                (!c->nextblock || c->nextblock->free_size < jeb->free_size)) {
+			    (jffs2_can_mark_obsolete(c) || jeb->free_size > c->wbuf_pagesize) &&
+			    (!c->nextblock || c->nextblock->free_size < jeb->free_size)) {
                                 /* Better candidate for the next writes to go to */
-                                if (c->nextblock)
-                                        list_add(&c->nextblock->list, &c->dirty_list);
+                                if (c->nextblock) {
+					c->nextblock->dirty_size += c->nextblock->free_size + c->nextblock->wasted_size;
+					c->dirty_size += c->nextblock->free_size + c->nextblock->wasted_size;
+					c->free_size -= c->nextblock->free_size;
+					c->wasted_size -= c->nextblock->wasted_size;
+					c->nextblock->free_size = c->nextblock->wasted_size = 0;
+					if (VERYDIRTY(c, c->nextblock->dirty_size)) {
+						list_add(&c->nextblock->list, &c->very_dirty_list);
+					} else {
+						list_add(&c->nextblock->list, &c->dirty_list);
+					}
+				}
                                 c->nextblock = jeb;
                         } else {
-                                list_add(&jeb->list, &c->dirty_list);
+				jeb->dirty_size += jeb->free_size + jeb->wasted_size;
+				c->dirty_size += jeb->free_size + jeb->wasted_size;
+				c->free_size -= jeb->free_size;
+				c->wasted_size -= jeb->wasted_size;
+				jeb->free_size = jeb->wasted_size = 0;
+				if (VERYDIRTY(c, jeb->dirty_size)) {
+					list_add(&jeb->list, &c->very_dirty_list);
+				} else {
+					list_add(&jeb->list, &c->dirty_list);
+				}
                         }
-		} else {
+			break;
+
+		case BLK_STATE_ALLDIRTY:
 			/* Nothing valid - not even a clean marker. Needs erasing. */
                         /* For now we just put it on the erasing list. We'll start the erases later */
-			printk(KERN_NOTICE "JFFS2: Erase block at 0x%08x is not formatted. It will be erased\n", jeb->offset);
+			D1(printk(KERN_NOTICE "JFFS2: Erase block at 0x%08x is not formatted. It will be erased\n", jeb->offset));
                         list_add(&jeb->list, &c->erase_pending_list);
 			c->nr_erasing_blocks++;
+			break;
+			
+		case BLK_STATE_BADBLOCK:
+			D1(printk(KERN_NOTICE "JFFS2: Block at 0x%08x is bad\n", jeb->offset));
+                        list_add(&jeb->list, &c->bad_list);
+			c->bad_size += c->sector_size;
+			c->free_size -= c->sector_size;
+			bad_blocks++;
+			break;
+		default:
+			printk(KERN_WARNING "jffs2_scan_medium(): unknown block state\n");
+			BUG();	
 		}
 	}
+	
+	/* Nextblock dirty is always seen as wasted, because we cannot recycle it now */
+	if (c->nextblock && (c->nextblock->dirty_size)) {
+		c->nextblock->wasted_size += c->nextblock->dirty_size;
+		c->wasted_size += c->nextblock->dirty_size;
+		c->dirty_size -= c->nextblock->dirty_size;
+		c->nextblock->dirty_size = 0;
+	}
+
+	if (!jffs2_can_mark_obsolete(c) && c->nextblock && (c->nextblock->free_size & (c->wbuf_pagesize-1))) {
+		/* If we're going to start writing into a block which already 
+		   contains data, and the end of the data isn't page-aligned,
+		   skip a little and align it. */
+
+		uint32_t skip = c->nextblock->free_size & (c->wbuf_pagesize-1);
+
+		D1(printk(KERN_DEBUG "jffs2_scan_medium(): Skipping %d bytes in nextblock to ensure page alignment\n",
+			  skip));
+		c->nextblock->wasted_size += skip;
+		c->wasted_size += skip;
+
+		c->nextblock->free_size -= skip;
+		c->free_size -= skip;
+	}
 	if (c->nr_erasing_blocks) {
-		if (!c->used_size && empty_blocks != c->nr_blocks) {
+		if ( !c->used_size && ((empty_blocks+bad_blocks)!= c->nr_blocks || bad_blocks == c->nr_blocks) ) {
 			printk(KERN_NOTICE "Cowardly refusing to erase blocks on filesystem with no valid JFFS2 nodes\n");
+			printk(KERN_NOTICE "empty_blocks %d, bad_blocks %d, c->nr_blocks %d\n",empty_blocks,bad_blocks,c->nr_blocks);
 			return -EIO;
 		}
 		jffs2_erase_pending_trigger(c);
 	}
+	if (buf_size)
+		kfree(flashbuf);
+#ifndef __ECOS
+	else 
+		c->mtd->unpoint(c->mtd, flashbuf, 0, c->mtd->size);
+#endif
 	return 0;
 }
 
-static int jffs2_scan_eraseblock (struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb) {
-	struct jffs2_unknown_node node;
+static int jffs2_fill_scan_buf (struct jffs2_sb_info *c, unsigned char *buf,
+				uint32_t ofs, uint32_t len)
+{
+	int ret;
+	size_t retlen;
+
+	ret = jffs2_flash_read(c, ofs, len, &retlen, buf);
+	if (ret) {
+		D1(printk(KERN_WARNING "mtd->read(0x%x bytes from 0x%x) returned %d\n", len, ofs, ret));
+		return ret;
+	}
+	if (retlen < len) {
+		D1(printk(KERN_WARNING "Read at 0x%x gave only 0x%zx bytes\n", ofs, retlen));
+		return -EIO;
+	}
+	D2(printk(KERN_DEBUG "Read 0x%x bytes from 0x%08x into buf\n", len, ofs));
+	D2(printk(KERN_DEBUG "000: %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n",
+		  buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7], buf[8], buf[9], buf[10], buf[11], buf[12], buf[13], buf[14], buf[15]));
+	return 0;
+}
+
+static int jffs2_scan_eraseblock (struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb,
+				  unsigned char *buf, uint32_t buf_size) {
+	struct jffs2_unknown_node *node;
+	struct jffs2_unknown_node crcnode;
 	uint32_t ofs, prevofs;
-	uint32_t hdr_crc, nodetype;
+	uint32_t hdr_crc, buf_ofs, buf_len;
 	int err;
 	int noise = 0;
+	int wasempty = 0;
+	uint32_t empty_start = 0;
+#ifdef CONFIG_JFFS2_FS_NAND
+	int cleanmarkerfound = 0;
+#endif
 
 	ofs = jeb->offset;
 	prevofs = jeb->offset - 1;
 
 	D1(printk(KERN_DEBUG "jffs2_scan_eraseblock(): Scanning block at 0x%x\n", ofs));
 
-	err = jffs2_scan_empty(c, jeb, &ofs, &noise);
-	if (err) return err;
-	if (ofs == jeb->offset + c->sector_size) {
-		D1(printk(KERN_DEBUG "Block at 0x%08x is empty (erased)\n", jeb->offset));
-		return 1;	/* special return code */
+#ifdef CONFIG_JFFS2_FS_NAND
+	if (jffs2_cleanmarker_oob(c)) {
+		int ret = jffs2_check_nand_cleanmarker(c, jeb);
+		D2(printk(KERN_NOTICE "jffs_check_nand_cleanmarker returned %d\n",ret));
+		/* Even if it's not found, we still scan to see
+		   if the block is empty. We use this information
+		   to decide whether to erase it or not. */
+		switch (ret) {
+		case 0:		cleanmarkerfound = 1; break;
+		case 1: 	break;
+		case 2: 	return BLK_STATE_BADBLOCK;
+		case 3:		return BLK_STATE_ALLDIRTY; /* Block has failed to erase min. once */
+		default: 	return ret;
+		}
+	}
+#endif
+	buf_ofs = jeb->offset;
+
+	if (!buf_size) {
+		buf_len = c->sector_size;
+	} else {
+		buf_len = EMPTY_SCAN_SIZE;
+		err = jffs2_fill_scan_buf(c, buf, buf_ofs, buf_len);
+		if (err)
+			return err;
 	}
 	
+	/* We temporarily use 'ofs' as a pointer into the buffer/jeb */
+	ofs = 0;
+
+	/* Scan only 4KiB of 0xFF before declaring it's empty */
+	while(ofs < EMPTY_SCAN_SIZE && *(uint32_t *)(&buf[ofs]) == 0xFFFFFFFF)
+		ofs += 4;
+
+	if (ofs == EMPTY_SCAN_SIZE) {
+#ifdef CONFIG_JFFS2_FS_NAND
+		if (jffs2_cleanmarker_oob(c)) {
+			/* scan oob, take care of cleanmarker */
+			int ret = jffs2_check_oob_empty(c, jeb, cleanmarkerfound);
+			D2(printk(KERN_NOTICE "jffs2_check_oob_empty returned %d\n",ret));
+			switch (ret) {
+			case 0:		return cleanmarkerfound ? BLK_STATE_CLEANMARKER : BLK_STATE_ALLFF;
+			case 1: 	return BLK_STATE_ALLDIRTY;
+			case 2: 	return BLK_STATE_BADBLOCK; /* case 2/3 are paranoia checks */
+			case 3:		return BLK_STATE_ALLDIRTY; /* Block has failed to erase min. once */
+			default: 	return ret;
+			}
+		}
+#endif
+		D1(printk(KERN_DEBUG "Block at 0x%08x is empty (erased)\n", jeb->offset));
+		return BLK_STATE_ALLFF;	/* OK to erase if all blocks are like this */
+	}
+	if (ofs) {
+		D1(printk(KERN_DEBUG "Free space at %08x ends at %08x\n", jeb->offset,
+			  jeb->offset + ofs));
+		DIRTY_SPACE(ofs);
+	}
+
+	/* Now ofs is a complete physical flash offset as it always was... */
+	ofs += jeb->offset;
+
 	noise = 10;
 
 	while(ofs < jeb->offset + c->sector_size) {
-		size_t retlen;
-		ACCT_PARANOIA_CHECK(jeb);
-		
+
+		D1(ACCT_PARANOIA_CHECK(jeb));
+
+		cond_resched();
+
 		if (ofs & 3) {
 			printk(KERN_WARNING "Eep. ofs 0x%08x not word-aligned!\n", ofs);
 			ofs = (ofs+3)&~3;
@@ -167,93 +364,174 @@ static int jffs2_scan_eraseblock (struct
 			continue;
 		}
 		prevofs = ofs;
-		
-		if (jeb->offset + c->sector_size < ofs + sizeof(node)) {
-			D1(printk(KERN_DEBUG "Fewer than %d bytes left to end of block. Not reading\n", sizeof(struct jffs2_unknown_node)));
+
+		if (jeb->offset + c->sector_size < ofs + sizeof(*node)) {
+			D1(printk(KERN_DEBUG "Fewer than %zd bytes left to end of block. (%x+%x<%x+%zx) Not reading\n", sizeof(struct jffs2_unknown_node),
+				  jeb->offset, c->sector_size, ofs, sizeof(*node)));
 			DIRTY_SPACE((jeb->offset + c->sector_size)-ofs);
 			break;
 		}
 
-		err = jffs2_flash_read(c, ofs, sizeof(node), &retlen, (char *)&node);
-		
-		if (err) {
-			D1(printk(KERN_WARNING "mtd->read(0x%x bytes from 0x%x) returned %d\n", sizeof(node), ofs, err));
-			return err;
+		if (buf_ofs + buf_len < ofs + sizeof(*node)) {
+			buf_len = min_t(uint32_t, buf_size, jeb->offset + c->sector_size - ofs);
+			D1(printk(KERN_DEBUG "Fewer than %zd bytes (node header) left to end of buf. Reading 0x%x at 0x%08x\n",
+				  sizeof(struct jffs2_unknown_node), buf_len, ofs));
+			err = jffs2_fill_scan_buf(c, buf, ofs, buf_len);
+			if (err)
+				return err;
+			buf_ofs = ofs;
 		}
-		if (retlen < sizeof(node)) {
-			D1(printk(KERN_WARNING "Read at 0x%x gave only 0x%x bytes\n", ofs, retlen));
-			DIRTY_SPACE(retlen);
-			ofs += retlen;
+
+		node = (struct jffs2_unknown_node *)&buf[ofs-buf_ofs];
+
+		if (*(uint32_t *)(&buf[ofs-buf_ofs]) == 0xffffffff) {
+			uint32_t inbuf_ofs = ofs - buf_ofs + 4;
+			uint32_t scanend;
+
+			empty_start = ofs;
+			ofs += 4;
+
+			/* If scanning empty space after only a cleanmarker, don't
+			   bother scanning the whole block */
+			if (unlikely(empty_start == jeb->offset + c->cleanmarker_size &&
+				     jeb->offset + EMPTY_SCAN_SIZE < buf_ofs + buf_len))
+				scanend = jeb->offset + EMPTY_SCAN_SIZE - buf_ofs;
+			else
+				scanend = buf_len;
+
+			D1(printk(KERN_DEBUG "Found empty flash at 0x%08x\n", ofs));
+			while (inbuf_ofs < scanend) {
+				if (*(uint32_t *)(&buf[inbuf_ofs]) != 0xffffffff)
+					goto emptyends;
+
+				inbuf_ofs+=4;
+				ofs += 4;
+			}
+			/* Ran off end. */
+			D1(printk(KERN_DEBUG "Empty flash ends normally at 0x%08x\n", ofs));
+
+			if (buf_ofs == jeb->offset && jeb->used_size == PAD(c->cleanmarker_size) && 
+			    !jeb->first_node->next_in_ino && !jeb->dirty_size)
+				return BLK_STATE_CLEANMARKER;
+			wasempty = 1;
+			continue;
+		} else if (wasempty) {
+		emptyends:
+			printk(KERN_WARNING "Empty flash at 0x%08x ends at 0x%08x\n", empty_start, ofs);
+			DIRTY_SPACE(ofs-empty_start);
+			wasempty = 0;
 			continue;
 		}
 
-		if (node.magic == JFFS2_EMPTY_BITMASK && node.nodetype == JFFS2_EMPTY_BITMASK) {
-			D1(printk(KERN_DEBUG "Found empty flash at 0x%x\n", ofs));
-			err = jffs2_scan_empty(c, jeb, &ofs, &noise);
-			if (err) return err;
-			continue;
-		}
-
-		if (ofs == jeb->offset && node.magic == KSAMTIB_CIGAM_2SFFJ) {
+		if (ofs == jeb->offset && je16_to_cpu(node->magic) == KSAMTIB_CIGAM_2SFFJ) {
 			printk(KERN_WARNING "Magic bitmask is backwards at offset 0x%08x. Wrong endian filesystem?\n", ofs);
 			DIRTY_SPACE(4);
 			ofs += 4;
 			continue;
 		}
-		if (node.magic == JFFS2_DIRTY_BITMASK) {
+		if (je16_to_cpu(node->magic) == JFFS2_DIRTY_BITMASK) {
 			D1(printk(KERN_DEBUG "Empty bitmask at 0x%08x\n", ofs));
 			DIRTY_SPACE(4);
 			ofs += 4;
 			continue;
 		}
-		if (node.magic == JFFS2_OLD_MAGIC_BITMASK) {
+		if (je16_to_cpu(node->magic) == JFFS2_OLD_MAGIC_BITMASK) {
 			printk(KERN_WARNING "Old JFFS2 bitmask found at 0x%08x\n", ofs);
 			printk(KERN_WARNING "You cannot use older JFFS2 filesystems with newer kernels\n");
 			DIRTY_SPACE(4);
 			ofs += 4;
 			continue;
 		}
-		if (node.magic != JFFS2_MAGIC_BITMASK) {
+		if (je16_to_cpu(node->magic) != JFFS2_MAGIC_BITMASK) {
 			/* OK. We're out of possibilities. Whinge and move on */
-			noisy_printk(&noise, "jffs2_scan_eraseblock(): Magic bitmask 0x%04x not found at 0x%08x: 0x%04x instead\n", JFFS2_MAGIC_BITMASK, ofs, node.magic);
+			noisy_printk(&noise, "jffs2_scan_eraseblock(): Magic bitmask 0x%04x not found at 0x%08x: 0x%04x instead\n", 
+				     JFFS2_MAGIC_BITMASK, ofs, 
+				     je16_to_cpu(node->magic));
 			DIRTY_SPACE(4);
 			ofs += 4;
 			continue;
 		}
 		/* We seem to have a node of sorts. Check the CRC */
-		nodetype = node.nodetype;
-		node.nodetype |= JFFS2_NODE_ACCURATE;
-		hdr_crc = crc32(0, &node, sizeof(node)-4);
-		node.nodetype = nodetype;
-		if (hdr_crc != node.hdr_crc) {
+		crcnode.magic = node->magic;
+		crcnode.nodetype = cpu_to_je16( je16_to_cpu(node->nodetype) | JFFS2_NODE_ACCURATE);
+		crcnode.totlen = node->totlen;
+		hdr_crc = crc32(0, &crcnode, sizeof(crcnode)-4);
+
+		if (hdr_crc != je32_to_cpu(node->hdr_crc)) {
 			noisy_printk(&noise, "jffs2_scan_eraseblock(): Node at 0x%08x {0x%04x, 0x%04x, 0x%08x) has invalid CRC 0x%08x (calculated 0x%08x)\n",
-				     ofs, node.magic, node.nodetype, node.totlen, node.hdr_crc, hdr_crc);
+				     ofs, je16_to_cpu(node->magic),
+				     je16_to_cpu(node->nodetype), 
+				     je32_to_cpu(node->totlen),
+				     je32_to_cpu(node->hdr_crc),
+				     hdr_crc);
+			DIRTY_SPACE(4);
+			ofs += 4;
+			continue;
+		}
+
+		if (ofs + je32_to_cpu(node->totlen) > 
+		    jeb->offset + c->sector_size) {
+			/* Eep. Node goes over the end of the erase block. */
+			printk(KERN_WARNING "Node at 0x%08x with length 0x%08x would run over the end of the erase block\n",
+			       ofs, je32_to_cpu(node->totlen));
+			printk(KERN_WARNING "Perhaps the file system was created with the wrong erase size?\n");
 			DIRTY_SPACE(4);
 			ofs += 4;
 			continue;
 		}
 
-		switch(node.nodetype | JFFS2_NODE_ACCURATE) {
+		if (!(je16_to_cpu(node->nodetype) & JFFS2_NODE_ACCURATE)) {
+			/* Wheee. This is an obsoleted node */
+			D2(printk(KERN_DEBUG "Node at 0x%08x is obsolete. Skipping\n", ofs));
+			DIRTY_SPACE(PAD(je32_to_cpu(node->totlen)));
+			ofs += PAD(je32_to_cpu(node->totlen));
+			continue;
+		}
+
+		switch(je16_to_cpu(node->nodetype)) {
 		case JFFS2_NODETYPE_INODE:
-			err = jffs2_scan_inode_node(c, jeb, &ofs);
+			if (buf_ofs + buf_len < ofs + sizeof(struct jffs2_raw_inode)) {
+				buf_len = min_t(uint32_t, buf_size, jeb->offset + c->sector_size - ofs);
+				D1(printk(KERN_DEBUG "Fewer than %zd bytes (inode node) left to end of buf. Reading 0x%x at 0x%08x\n",
+					  sizeof(struct jffs2_raw_inode), buf_len, ofs));
+				err = jffs2_fill_scan_buf(c, buf, ofs, buf_len);
+				if (err)
+					return err;
+				buf_ofs = ofs;
+				node = (void *)buf;
+			}
+			err = jffs2_scan_inode_node(c, jeb, (void *)node, ofs);
 			if (err) return err;
+			ofs += PAD(je32_to_cpu(node->totlen));
 			break;
 			
 		case JFFS2_NODETYPE_DIRENT:
-			err = jffs2_scan_dirent_node(c, jeb, &ofs);
+			if (buf_ofs + buf_len < ofs + je32_to_cpu(node->totlen)) {
+				buf_len = min_t(uint32_t, buf_size, jeb->offset + c->sector_size - ofs);
+				D1(printk(KERN_DEBUG "Fewer than %d bytes (dirent node) left to end of buf. Reading 0x%x at 0x%08x\n",
+					  je32_to_cpu(node->totlen), buf_len, ofs));
+				err = jffs2_fill_scan_buf(c, buf, ofs, buf_len);
+				if (err)
+					return err;
+				buf_ofs = ofs;
+				node = (void *)buf;
+			}
+			err = jffs2_scan_dirent_node(c, jeb, (void *)node, ofs);
 			if (err) return err;
+			ofs += PAD(je32_to_cpu(node->totlen));
 			break;
 
 		case JFFS2_NODETYPE_CLEANMARKER:
-			if (node.totlen != sizeof(struct jffs2_unknown_node)) {
+			D1(printk(KERN_DEBUG "CLEANMARKER node found at 0x%08x\n", ofs));
+			if (je32_to_cpu(node->totlen) != c->cleanmarker_size) {
 				printk(KERN_NOTICE "CLEANMARKER node found at 0x%08x has totlen 0x%x != normal 0x%x\n", 
-				       ofs, node.totlen, sizeof(struct jffs2_unknown_node));
+				       ofs, je32_to_cpu(node->totlen), c->cleanmarker_size);
 				DIRTY_SPACE(PAD(sizeof(struct jffs2_unknown_node)));
+				ofs += PAD(sizeof(struct jffs2_unknown_node));
 			} else if (jeb->first_node) {
 				printk(KERN_NOTICE "CLEANMARKER node found at 0x%08x, not first node in block (0x%08x)\n", ofs, jeb->offset);
 				DIRTY_SPACE(PAD(sizeof(struct jffs2_unknown_node)));
 				ofs += PAD(sizeof(struct jffs2_unknown_node));
-				continue;
 			} else {
 				struct jffs2_raw_node_ref *marker_ref = jffs2_alloc_raw_node_ref();
 				if (!marker_ref) {
@@ -262,95 +540,77 @@ static int jffs2_scan_eraseblock (struct
 				}
 				marker_ref->next_in_ino = NULL;
 				marker_ref->next_phys = NULL;
-				marker_ref->flash_offset = ofs;
-				marker_ref->totlen = sizeof(struct jffs2_unknown_node);
+				marker_ref->flash_offset = ofs | REF_NORMAL;
+				marker_ref->totlen = c->cleanmarker_size;
 				jeb->first_node = jeb->last_node = marker_ref;
 			     
-				USED_SPACE(PAD(sizeof(struct jffs2_unknown_node)));
+				USED_SPACE(PAD(c->cleanmarker_size));
+				ofs += PAD(c->cleanmarker_size);
 			}
-			ofs += PAD(sizeof(struct jffs2_unknown_node));
+			break;
+
+		case JFFS2_NODETYPE_PADDING:
+			DIRTY_SPACE(PAD(je32_to_cpu(node->totlen)));
+			ofs += PAD(je32_to_cpu(node->totlen));
 			break;
 
 		default:
-			switch (node.nodetype & JFFS2_COMPAT_MASK) {
+			switch (je16_to_cpu(node->nodetype) & JFFS2_COMPAT_MASK) {
 			case JFFS2_FEATURE_ROCOMPAT:
-				printk(KERN_NOTICE "Read-only compatible feature node (0x%04x) found at offset 0x%08x\n", node.nodetype, ofs);
+				printk(KERN_NOTICE "Read-only compatible feature node (0x%04x) found at offset 0x%08x\n", je16_to_cpu(node->nodetype), ofs);
 			        c->flags |= JFFS2_SB_FLAG_RO;
 				if (!(jffs2_is_readonly(c)))
 					return -EROFS;
-				DIRTY_SPACE(PAD(node.totlen));
-				ofs += PAD(node.totlen);
-				continue;
+				DIRTY_SPACE(PAD(je32_to_cpu(node->totlen)));
+				ofs += PAD(je32_to_cpu(node->totlen));
+				break;
 
 			case JFFS2_FEATURE_INCOMPAT:
-				printk(KERN_NOTICE "Incompatible feature node (0x%04x) found at offset 0x%08x\n", node.nodetype, ofs);
+				printk(KERN_NOTICE "Incompatible feature node (0x%04x) found at offset 0x%08x\n", je16_to_cpu(node->nodetype), ofs);
 				return -EINVAL;
 
 			case JFFS2_FEATURE_RWCOMPAT_DELETE:
-				printk(KERN_NOTICE "Unknown but compatible feature node (0x%04x) found at offset 0x%08x\n", node.nodetype, ofs);
-				DIRTY_SPACE(PAD(node.totlen));
-				ofs += PAD(node.totlen);
+				D1(printk(KERN_NOTICE "Unknown but compatible feature node (0x%04x) found at offset 0x%08x\n", je16_to_cpu(node->nodetype), ofs));
+				DIRTY_SPACE(PAD(je32_to_cpu(node->totlen)));
+				ofs += PAD(je32_to_cpu(node->totlen));
 				break;
 
 			case JFFS2_FEATURE_RWCOMPAT_COPY:
-				printk(KERN_NOTICE "Unknown but compatible feature node (0x%04x) found at offset 0x%08x\n", node.nodetype, ofs);
-				USED_SPACE(PAD(node.totlen));
-				ofs += PAD(node.totlen);
+				D1(printk(KERN_NOTICE "Unknown but compatible feature node (0x%04x) found at offset 0x%08x\n", je16_to_cpu(node->nodetype), ofs));
+				USED_SPACE(PAD(je32_to_cpu(node->totlen)));
+				ofs += PAD(je32_to_cpu(node->totlen));
 				break;
 			}
 		}
 	}
-	D1(printk(KERN_DEBUG "Block at 0x%08x: free 0x%08x, dirty 0x%08x, used 0x%08x\n", jeb->offset, 
-		  jeb->free_size, jeb->dirty_size, jeb->used_size));
-	return 0;
-}
+
+
+	D1(printk(KERN_DEBUG "Block at 0x%08x: free 0x%08x, dirty 0x%08x, unchecked 0x%08x, used 0x%08x\n", jeb->offset, 
+		  jeb->free_size, jeb->dirty_size, jeb->unchecked_size, jeb->used_size));
 
-/* We're pointing at the first empty word on the flash. Scan and account for the whole dirty region */
-static int jffs2_scan_empty(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, uint32_t *startofs, int *noise)
-{
-	uint32_t *buf;
-	uint32_t scanlen = (jeb->offset + c->sector_size) - *startofs;
-	uint32_t curofs = *startofs;
-	
-	buf = kmalloc(min((uint32_t)PAGE_SIZE, scanlen), GFP_KERNEL);
-	if (!buf) {
-		printk(KERN_WARNING "Scan buffer allocation failed\n");
-		return -ENOMEM;
-	}
-	while(scanlen) {
-		size_t retlen;
-		int ret, i;
-		
-		ret = jffs2_flash_read(c, curofs, min((uint32_t)PAGE_SIZE, scanlen), &retlen, (char *)buf);
-		if(ret) {
-			D1(printk(KERN_WARNING "jffs2_scan_empty(): Read 0x%x bytes at 0x%08x returned %d\n", min((uint32_t)PAGE_SIZE, scanlen), curofs, ret));
-			kfree(buf);
-			return ret;
-		}
-		if (retlen < 4) {
-			D1(printk(KERN_WARNING "Eep. too few bytes read in scan_empty()\n"));
-			kfree(buf);
-			return -EIO;
-		}
-		for (i=0; i<(retlen / 4); i++) {
-			if (buf[i] != 0xffffffff) {
-				curofs += i*4;
-
-				noisy_printk(noise, "jffs2_scan_empty(): Empty block at 0x%08x ends at 0x%08x (with 0x%08x)! Marking dirty\n", *startofs, curofs, buf[i]);
-				DIRTY_SPACE(curofs - (*startofs));
-				*startofs = curofs;
-				kfree(buf);
-				return 0;
-			}
-		}
-		scanlen -= retlen&~3;
-		curofs += retlen&~3;
+	/* mark_node_obsolete can add to wasted !! */
+	if (jeb->wasted_size) {
+		jeb->dirty_size += jeb->wasted_size;
+		c->dirty_size += jeb->wasted_size;
+		c->wasted_size -= jeb->wasted_size;
+		jeb->wasted_size = 0;
 	}
 
-	D1(printk(KERN_DEBUG "Empty flash detected from 0x%08x to 0x%08x\n", *startofs, curofs));
-	kfree(buf);
-	*startofs = curofs;
-	return 0;
+	if ((jeb->used_size + jeb->unchecked_size) == PAD(c->cleanmarker_size) && !jeb->dirty_size 
+		&& (!jeb->first_node || jeb->first_node->next_in_ino) )
+		return BLK_STATE_CLEANMARKER;
+		
+	/* move blocks with max 4 byte dirty space to cleanlist */	
+	else if (!ISDIRTY(c->sector_size - (jeb->used_size + jeb->unchecked_size))) {
+		c->dirty_size -= jeb->dirty_size;
+		c->wasted_size += jeb->dirty_size; 
+		jeb->wasted_size += jeb->dirty_size;
+		jeb->dirty_size = 0;
+		return BLK_STATE_CLEAN;
+	} else if (jeb->used_size || jeb->unchecked_size)
+		return BLK_STATE_PARTDIRTY;
+	else
+		return BLK_STATE_ALLDIRTY;
 }
 
 static struct jffs2_inode_cache *jffs2_scan_make_ino_cache(struct jffs2_sb_info *c, uint32_t ino)
@@ -367,13 +627,7 @@ static struct jffs2_inode_cache *jffs2_s
 		return NULL;
 	}
 	memset(ic, 0, sizeof(*ic));
-	ic->scan = kmalloc(sizeof(struct jffs2_scan_info), GFP_KERNEL);
-	if (!ic->scan) {
-		printk(KERN_NOTICE "jffs2_scan_make_inode_cache(): allocation of scan info for inode cache failed\n");
-		jffs2_free_inode_cache(ic);
-		return NULL;
-	}
-	memset(ic->scan, 0, sizeof(*ic->scan));
+
 	ic->ino = ino;
 	ic->nodes = (void *)ic;
 	jffs2_add_ino_cache(c, ic);
@@ -382,108 +636,133 @@ static struct jffs2_inode_cache *jffs2_s
 	return ic;
 }
 
-static int jffs2_scan_inode_node(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, uint32_t *ofs)
+static int jffs2_scan_inode_node(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, 
+				 struct jffs2_raw_inode *ri, uint32_t ofs)
 {
 	struct jffs2_raw_node_ref *raw;
-	struct jffs2_full_dnode *fn;
-	struct jffs2_tmp_dnode_info *tn, **tn_list;
 	struct jffs2_inode_cache *ic;
-	struct jffs2_raw_inode ri;
-	uint32_t crc;
-	uint16_t oldnodetype;
-	int ret;
-	size_t retlen;
-
-	D1(printk(KERN_DEBUG "jffs2_scan_inode_node(): Node at 0x%08x\n", *ofs));
+	uint32_t ino = je32_to_cpu(ri->ino);
 
-	ret = jffs2_flash_read(c, *ofs, sizeof(ri), &retlen, (char *)&ri);
-	if (ret) {
-		printk(KERN_NOTICE "jffs2_scan_inode_node(): Read error at 0x%08x: %d\n", *ofs, ret);
-		return ret;
-	}
-	if (retlen != sizeof(ri)) {
-		printk(KERN_NOTICE "Short read: 0x%x bytes at 0x%08x instead of requested %x\n", 
-		       retlen, *ofs, sizeof(ri));
-		return -EIO;
-	}
-
-	/* We sort of assume that the node was accurate when it was 
-	   first written to the medium :) */
-	oldnodetype = ri.nodetype;
-	ri.nodetype |= JFFS2_NODE_ACCURATE;
-	crc = crc32(0, &ri, sizeof(ri)-8);
-	ri.nodetype = oldnodetype;
+	D1(printk(KERN_DEBUG "jffs2_scan_inode_node(): Node at 0x%08x\n", ofs));
 
-	if(crc != ri.node_crc) {
-		printk(KERN_NOTICE "jffs2_scan_inode_node(): CRC failed on node at 0x%08x: Read 0x%08x, calculated 0x%08x\n",
-		       *ofs, ri.node_crc, crc);
-		/* FIXME: Why do we believe totlen? */
-		DIRTY_SPACE(4);
-		*ofs += 4;
-		return 0;
-	}
-
-	if (ri.csize) {
-		/* Check data CRC too */
-		unsigned char *dbuf;
-		uint32_t crc;
+	/* We do very little here now. Just check the ino# to which we should attribute
+	   this node; we can do all the CRC checking etc. later. There's a tradeoff here -- 
+	   we used to scan the flash once only, reading everything we want from it into
+	   memory, then building all our in-core data structures and freeing the extra
+	   information. Now we allow the first part of the mount to complete a lot quicker,
+	   but we have to go _back_ to the flash in order to finish the CRC checking, etc. 
+	   Which means that the _full_ amount of time to get to proper write mode with GC
+	   operational may actually be _longer_ than before. Sucks to be me. */
 
-		dbuf = kmalloc(PAGE_CACHE_SIZE, GFP_KERNEL);
-		if (!dbuf) {
-			printk(KERN_NOTICE "jffs2_scan_inode_node(): allocation of temporary data buffer for CRC check failed\n");
-			return -ENOMEM;
-		}
-		ret = jffs2_flash_read(c, *ofs+sizeof(ri), ri.csize, &retlen, dbuf);
-		if (ret) {
-			printk(KERN_NOTICE "jffs2_scan_inode_node(): Read error at 0x%08x: %d\n", *ofs+sizeof(ri), ret);
-			kfree(dbuf);
-			return ret;
-		}
-		if (retlen != ri.csize) {
-			printk(KERN_NOTICE "Short read: 0x%x bytes at 0x%08x instead of requested %x\n", 
-			       retlen, *ofs+ sizeof(ri), ri.csize);
-			kfree(dbuf);
-			return -EIO;
-		}
-		crc = crc32(0, dbuf, ri.csize);
-		kfree(dbuf);
-		if (crc != ri.data_crc) {
-			printk(KERN_NOTICE "jffs2_scan_inode_node(): Data CRC failed on node at 0x%08x: Read 0x%08x, calculated 0x%08x\n",
-			       *ofs, ri.data_crc, crc);
-			DIRTY_SPACE(PAD(ri.totlen));
-			*ofs += PAD(ri.totlen);
-			return -0;
-		}
-	}
-
-	/* Wheee. It worked */
 	raw = jffs2_alloc_raw_node_ref();
 	if (!raw) {
 		printk(KERN_NOTICE "jffs2_scan_inode_node(): allocation of node reference failed\n");
 		return -ENOMEM;
 	}
-	tn = jffs2_alloc_tmp_dnode_info();
-	if (!tn) {
-		jffs2_free_raw_node_ref(raw);
+
+	ic = jffs2_get_ino_cache(c, ino);
+	if (!ic) {
+		/* Inocache get failed. Either we read a bogus ino# or it's just genuinely the
+		   first node we found for this inode. Do a CRC check to protect against the former
+		   case */
+		uint32_t crc = crc32(0, ri, sizeof(*ri)-8);
+
+		if (crc != je32_to_cpu(ri->node_crc)) {
+			printk(KERN_NOTICE "jffs2_scan_inode_node(): CRC failed on node at 0x%08x: Read 0x%08x, calculated 0x%08x\n",
+			       ofs, je32_to_cpu(ri->node_crc), crc);
+			/* We believe totlen because the CRC on the node _header_ was OK, just the node itself failed. */
+			DIRTY_SPACE(PAD(je32_to_cpu(ri->totlen)));
+			return 0;
+		}
+		ic = jffs2_scan_make_ino_cache(c, ino);
+		if (!ic) {
+			jffs2_free_raw_node_ref(raw);
+			return -ENOMEM;
+		}
+	}
+
+	/* Wheee. It worked */
+
+	raw->flash_offset = ofs | REF_UNCHECKED;
+	raw->totlen = PAD(je32_to_cpu(ri->totlen));
+	raw->next_phys = NULL;
+	raw->next_in_ino = ic->nodes;
+
+	ic->nodes = raw;
+	if (!jeb->first_node)
+		jeb->first_node = raw;
+	if (jeb->last_node)
+		jeb->last_node->next_phys = raw;
+	jeb->last_node = raw;
+
+	D1(printk(KERN_DEBUG "Node is ino #%u, version %d. Range 0x%x-0x%x\n", 
+		  je32_to_cpu(ri->ino), je32_to_cpu(ri->version),
+		  je32_to_cpu(ri->offset),
+		  je32_to_cpu(ri->offset)+je32_to_cpu(ri->dsize)));
+
+	pseudo_random += je32_to_cpu(ri->version);
+
+	UNCHECKED_SPACE(PAD(je32_to_cpu(ri->totlen)));
+	return 0;
+}
+
+static int jffs2_scan_dirent_node(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, 
+				  struct jffs2_raw_dirent *rd, uint32_t ofs)
+{
+	struct jffs2_raw_node_ref *raw;
+	struct jffs2_full_dirent *fd;
+	struct jffs2_inode_cache *ic;
+	uint32_t crc;
+
+	D1(printk(KERN_DEBUG "jffs2_scan_dirent_node(): Node at 0x%08x\n", ofs));
+
+	/* We don't get here unless the node is still valid, so we don't have to
+	   mask in the ACCURATE bit any more. */
+	crc = crc32(0, rd, sizeof(*rd)-8);
+
+	if (crc != je32_to_cpu(rd->node_crc)) {
+		printk(KERN_NOTICE "jffs2_scan_dirent_node(): Node CRC failed on node at 0x%08x: Read 0x%08x, calculated 0x%08x\n",
+		       ofs, je32_to_cpu(rd->node_crc), crc);
+		/* We believe totlen because the CRC on the node _header_ was OK, just the node itself failed. */
+		DIRTY_SPACE(PAD(je32_to_cpu(rd->totlen)));
+		return 0;
+	}
+
+	pseudo_random += je32_to_cpu(rd->version);
+
+	fd = jffs2_alloc_full_dirent(rd->nsize+1);
+	if (!fd) {
 		return -ENOMEM;
 	}
-	fn = jffs2_alloc_full_dnode();
-	if (!fn) {
-		jffs2_free_tmp_dnode_info(tn);
+	memcpy(&fd->name, rd->name, rd->nsize);
+	fd->name[rd->nsize] = 0;
+
+	crc = crc32(0, fd->name, rd->nsize);
+	if (crc != je32_to_cpu(rd->name_crc)) {
+		printk(KERN_NOTICE "jffs2_scan_dirent_node(): Name CRC failed on node at 0x%08x: Read 0x%08x, calculated 0x%08x\n",
+		       ofs, je32_to_cpu(rd->name_crc), crc);	
+		D1(printk(KERN_NOTICE "Name for which CRC failed is (now) '%s', ino #%d\n", fd->name, je32_to_cpu(rd->ino)));
+		jffs2_free_full_dirent(fd);
+		/* FIXME: Why do we believe totlen? */
+		/* We believe totlen because the CRC on the node _header_ was OK, just the name failed. */
+		DIRTY_SPACE(PAD(je32_to_cpu(rd->totlen)));
+		return 0;
+	}
+	raw = jffs2_alloc_raw_node_ref();
+	if (!raw) {
+		jffs2_free_full_dirent(fd);
+		printk(KERN_NOTICE "jffs2_scan_dirent_node(): allocation of node reference failed\n");
+		return -ENOMEM;
+	}
+	ic = jffs2_scan_make_ino_cache(c, je32_to_cpu(rd->pino));
+	if (!ic) {
+		jffs2_free_full_dirent(fd);
 		jffs2_free_raw_node_ref(raw);
 		return -ENOMEM;
 	}
-	ic = jffs2_scan_make_ino_cache(c, ri.ino);
-	if (!ic) {
-		jffs2_free_full_dnode(fn);
-		jffs2_free_tmp_dnode_info(tn);
-		jffs2_free_raw_node_ref(raw);
-		return -ENOMEM;
-	}
-
-	/* Build the data structures and file them for later */
-	raw->flash_offset = *ofs;
-	raw->totlen = PAD(ri.totlen);
+	
+	raw->totlen = PAD(je32_to_cpu(rd->totlen));
+	raw->flash_offset = ofs | REF_PRISTINE;
 	raw->next_phys = NULL;
 	raw->next_in_ino = ic->nodes;
 	ic->nodes = raw;
@@ -493,174 +772,120 @@ static int jffs2_scan_inode_node(struct 
 		jeb->last_node->next_phys = raw;
 	jeb->last_node = raw;
 
-	D1(printk(KERN_DEBUG "Node is ino #%u, version %d. Range 0x%x-0x%x\n", 
-		  ri.ino, ri.version, ri.offset, ri.offset+ri.dsize));
-
-	for (tn_list = &ic->scan->tmpnodes; *tn_list; tn_list = &((*tn_list)->next)) {
-		if ((*tn_list)->version < ri.version)
-			continue;
-		if ((*tn_list)->version > ri.version) 
-			break;
-		/* Wheee. We've found another instance of the same version number.
-		   We should obsolete one of them. 
-		*/
-		D1(printk(KERN_DEBUG "Duplicate version %d found in ino #%u. Previous one is at 0x%08x\n", ri.version, ic->ino, (*tn_list)->fn->raw->flash_offset &~3));
-		if (!jeb->used_size) {
-			D1(printk(KERN_DEBUG "No valid nodes yet found in this eraseblock 0x%08x, so obsoleting the new instance at 0x%08x\n", 
-				  jeb->offset, raw->flash_offset & ~3));
-			ri.nodetype &= ~JFFS2_NODE_ACCURATE;
-			/* Perhaps we could also mark it as such on the medium. Maybe later */
-		}
-		break;
-	}
-
-	if (ri.nodetype & JFFS2_NODE_ACCURATE) {
-		memset(fn,0,sizeof(*fn));
+	fd->raw = raw;
+	fd->next = NULL;
+	fd->version = je32_to_cpu(rd->version);
+	fd->ino = je32_to_cpu(rd->ino);
+	fd->nhash = full_name_hash(fd->name, rd->nsize);
+	fd->type = rd->type;
+	USED_SPACE(PAD(je32_to_cpu(rd->totlen)));
+	jffs2_add_fd_to_list(c, fd, &ic->scan_dents);
 
-		fn->ofs = ri.offset;
-		fn->size = ri.dsize;
-		fn->frags = 0;
-		fn->raw = raw;
-
-		tn->next = NULL;
-		tn->fn = fn;
-		tn->version = ri.version;
-
-		USED_SPACE(PAD(ri.totlen));
-		jffs2_add_tn_to_list(tn, &ic->scan->tmpnodes);
-		/* Make sure the one we just added is the _last_ in the list
-		   with this version number, so the older ones get obsoleted */
-		while (tn->next && tn->next->version == tn->version) {
-
-			D1(printk(KERN_DEBUG "Shifting new node at 0x%08x after other node at 0x%08x for version %d in list\n",
-				  fn->raw->flash_offset&~3, tn->next->fn->raw->flash_offset &~3, ri.version));
-
-			if(tn->fn != fn)
-				BUG();
-			tn->fn = tn->next->fn;
-			tn->next->fn = fn;
-			tn = tn->next;
-		}
-	} else {
-		jffs2_free_full_dnode(fn);
-		jffs2_free_tmp_dnode_info(tn);
-		raw->flash_offset |= 1;
-		DIRTY_SPACE(PAD(ri.totlen));
-	}		
-	*ofs += PAD(ri.totlen);
 	return 0;
 }
 
-static int jffs2_scan_dirent_node(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, uint32_t *ofs)
+static int count_list(struct list_head *l)
 {
-	struct jffs2_raw_node_ref *raw;
-	struct jffs2_full_dirent *fd;
-	struct jffs2_inode_cache *ic;
-	struct jffs2_raw_dirent rd;
-	uint16_t oldnodetype;
-	int ret;
-	uint32_t crc;
-	size_t retlen;
+	uint32_t count = 0;
+	struct list_head *tmp;
 
-	D1(printk(KERN_DEBUG "jffs2_scan_dirent_node(): Node at 0x%08x\n", *ofs));
-
-	ret = jffs2_flash_read(c, *ofs, sizeof(rd), &retlen, (char *)&rd);
-	if (ret) {
-		printk(KERN_NOTICE "jffs2_scan_dirent_node(): Read error at 0x%08x: %d\n", *ofs, ret);
-		return ret;
+	list_for_each(tmp, l) {
+		count++;
 	}
-	if (retlen != sizeof(rd)) {
-		printk(KERN_NOTICE "Short read: 0x%x bytes at 0x%08x instead of requested %x\n", 
-		       retlen, *ofs, sizeof(rd));
-		return -EIO;
+	return count;
+}
+
+/* Note: This breaks if list_empty(head). I don't care. You
+   might, if you copy this code and use it elsewhere :) */
+static void rotate_list(struct list_head *head, uint32_t count)
+{
+	struct list_head *n = head->next;
+
+	list_del(head);
+	while(count--) {
+		n = n->next;
 	}
+	list_add(head, n);
+}
 
-	/* We sort of assume that the node was accurate when it was 
-	   first written to the medium :) */
-	oldnodetype = rd.nodetype;
-	rd.nodetype |= JFFS2_NODE_ACCURATE;
-	crc = crc32(0, &rd, sizeof(rd)-8);
-	rd.nodetype = oldnodetype;
+void jffs2_rotate_lists(struct jffs2_sb_info *c)
+{
+	uint32_t x;
+	uint32_t rotateby;
 
-	if (crc != rd.node_crc) {
-		printk(KERN_NOTICE "jffs2_scan_dirent_node(): Node CRC failed on node at 0x%08x: Read 0x%08x, calculated 0x%08x\n",
-		       *ofs, rd.node_crc, crc);
-		/* FIXME: Why do we believe totlen? */
-		DIRTY_SPACE(4);
-		*ofs += 4;
-		return 0;
+	x = count_list(&c->clean_list);
+	if (x) {
+		rotateby = pseudo_random % x;
+		D1(printk(KERN_DEBUG "Rotating clean_list by %d\n", rotateby));
+
+		rotate_list((&c->clean_list), rotateby);
+
+		D1(printk(KERN_DEBUG "Erase block at front of clean_list is at %08x\n",
+			  list_entry(c->clean_list.next, struct jffs2_eraseblock, list)->offset));
+	} else {
+		D1(printk(KERN_DEBUG "Not rotating empty clean_list\n"));
 	}
 
-	fd = jffs2_alloc_full_dirent(rd.nsize+1);
-	if (!fd) {
-		return -ENOMEM;
-}
-	ret = jffs2_flash_read(c, *ofs + sizeof(rd), rd.nsize, &retlen, &fd->name[0]);
-	if (ret) {
-		jffs2_free_full_dirent(fd);
-		printk(KERN_NOTICE "jffs2_scan_dirent_node(): Read error at 0x%08x: %d\n", 
-		       *ofs + sizeof(rd), ret);
-		return ret;
-	}
-	if (retlen != rd.nsize) {
-		jffs2_free_full_dirent(fd);
-		printk(KERN_NOTICE "Short read: 0x%x bytes at 0x%08x instead of requested %x\n", 
-		       retlen, *ofs + sizeof(rd), rd.nsize);
-		return -EIO;
+	x = count_list(&c->very_dirty_list);
+	if (x) {
+		rotateby = pseudo_random % x;
+		D1(printk(KERN_DEBUG "Rotating very_dirty_list by %d\n", rotateby));
+
+		rotate_list((&c->very_dirty_list), rotateby);
+
+		D1(printk(KERN_DEBUG "Erase block at front of very_dirty_list is at %08x\n",
+			  list_entry(c->very_dirty_list.next, struct jffs2_eraseblock, list)->offset));
+	} else {
+		D1(printk(KERN_DEBUG "Not rotating empty very_dirty_list\n"));
 	}
-	crc = crc32(0, fd->name, rd.nsize);
-	if (crc != rd.name_crc) {
-		printk(KERN_NOTICE "jffs2_scan_dirent_node(): Name CRC failed on node at 0x%08x: Read 0x%08x, calculated 0x%08x\n",
-		       *ofs, rd.name_crc, crc);	
-		fd->name[rd.nsize]=0;
-		D1(printk(KERN_NOTICE "Name for which CRC failed is (now) '%s', ino #%d\n", fd->name, rd.ino));
-		jffs2_free_full_dirent(fd);
-		/* FIXME: Why do we believe totlen? */
-		DIRTY_SPACE(PAD(rd.totlen));
-		*ofs += PAD(rd.totlen);
-		return 0;
-	}
-	raw = jffs2_alloc_raw_node_ref();
-	if (!raw) {
-		jffs2_free_full_dirent(fd);
-		printk(KERN_NOTICE "jffs2_scan_dirent_node(): allocation of node reference failed\n");
-		return -ENOMEM;
+
+	x = count_list(&c->dirty_list);
+	if (x) {
+		rotateby = pseudo_random % x;
+		D1(printk(KERN_DEBUG "Rotating dirty_list by %d\n", rotateby));
+
+		rotate_list((&c->dirty_list), rotateby);
+
+		D1(printk(KERN_DEBUG "Erase block at front of dirty_list is at %08x\n",
+			  list_entry(c->dirty_list.next, struct jffs2_eraseblock, list)->offset));
+	} else {
+		D1(printk(KERN_DEBUG "Not rotating empty dirty_list\n"));
 	}
-	ic = jffs2_scan_make_ino_cache(c, rd.pino);
-	if (!ic) {
-		jffs2_free_full_dirent(fd);
-		jffs2_free_raw_node_ref(raw);
-		return -ENOMEM;
+
+	x = count_list(&c->erasable_list);
+	if (x) {
+		rotateby = pseudo_random % x;
+		D1(printk(KERN_DEBUG "Rotating erasable_list by %d\n", rotateby));
+
+		rotate_list((&c->erasable_list), rotateby);
+
+		D1(printk(KERN_DEBUG "Erase block at front of erasable_list is at %08x\n",
+			  list_entry(c->erasable_list.next, struct jffs2_eraseblock, list)->offset));
+	} else {
+		D1(printk(KERN_DEBUG "Not rotating empty erasable_list\n"));
 	}
-	
-	raw->totlen = PAD(rd.totlen);
-	raw->flash_offset = *ofs;
-	raw->next_phys = NULL;
-	raw->next_in_ino = ic->nodes;
-	ic->nodes = raw;
-	if (!jeb->first_node)
-		jeb->first_node = raw;
-	if (jeb->last_node)
-		jeb->last_node->next_phys = raw;
-	jeb->last_node = raw;
+
+	if (c->nr_erasing_blocks) {
+		rotateby = pseudo_random % c->nr_erasing_blocks;
+		D1(printk(KERN_DEBUG "Rotating erase_pending_list by %d\n", rotateby));
 
-	if (rd.nodetype & JFFS2_NODE_ACCURATE) {
-		fd->raw = raw;
-		fd->next = NULL;
-		fd->version = rd.version;
-		fd->ino = rd.ino;
-		fd->name[rd.nsize]=0;
-		fd->nhash = full_name_hash(fd->name, rd.nsize);
-		fd->type = rd.type;
+		rotate_list((&c->erase_pending_list), rotateby);
+
+		D1(printk(KERN_DEBUG "Erase block at front of erase_pending_list is at %08x\n",
+			  list_entry(c->erase_pending_list.next, struct jffs2_eraseblock, list)->offset));
+	} else {
+		D1(printk(KERN_DEBUG "Not rotating empty erase_pending_list\n"));
+	}
 
-		USED_SPACE(PAD(rd.totlen));
-		jffs2_add_fd_to_list(c, fd, &ic->scan->dents);
-	} else {
-		raw->flash_offset |= 1;
-		jffs2_free_full_dirent(fd);
+	if (c->nr_free_blocks) {
+		rotateby = pseudo_random % c->nr_free_blocks;
+		D1(printk(KERN_DEBUG "Rotating free_list by %d\n", rotateby));
+
+		rotate_list((&c->free_list), rotateby);
 
-		DIRTY_SPACE(PAD(rd.totlen));
-	} 
-	*ofs += PAD(rd.totlen);
-	return 0;
+		D1(printk(KERN_DEBUG "Erase block at front of free_list is at %08x\n",
+			  list_entry(c->free_list.next, struct jffs2_eraseblock, list)->offset));
+	} else {
+		D1(printk(KERN_DEBUG "Not rotating empty free_list\n"));
+	}
 }
--- a/packages/fs/jffs2/current/src/write.c
+++ b/packages/fs/jffs2/current/src/write.c
@@ -7,18 +7,17 @@
  *
  * For licensing information, see the file 'LICENCE' in this directory.
  *
- * $Id: write.c,v 1.40 2002/01/28 22:25:08 dwmw2 Exp $
+ * $Id: write.c,v 1.65 2003/01/21 18:11:29 dwmw2 Exp $
  *
  */
 
-#ifndef __ECOS
 #include <linux/kernel.h>
 #include <linux/fs.h>
+#include <linux/crc32.h>
 #include <linux/slab.h>
+#include <linux/pagemap.h>
 #include <linux/mtd/mtd.h>
-#endif
 #include "nodelist.h"
-#include "crc32.h"
 
 
 int jffs2_do_new_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, uint32_t mode, struct jffs2_raw_inode *ri)
@@ -29,121 +28,34 @@ int jffs2_do_new_inode(struct jffs2_sb_i
 	if (!ic) {
 		return -ENOMEM;
 	}
+
 	memset(ic, 0, sizeof(*ic));
 
 	init_MUTEX_LOCKED(&f->sem);
 	f->inocache = ic;
 	f->inocache->nlink = 1;
 	f->inocache->nodes = (struct jffs2_raw_node_ref *)f->inocache;
-	f->inocache->ino = ri->ino =  ++c->highest_ino;
-	D1(printk(KERN_DEBUG "jffs2_do_new_inode(): Assigned ino# %d\n", ri->ino));
+	f->inocache->ino = ++c->highest_ino;
+	f->inocache->state = INO_STATE_PRESENT;
+
+	ri->ino = cpu_to_je32(f->inocache->ino);
+
+	D1(printk(KERN_DEBUG "jffs2_do_new_inode(): Assigned ino# %d\n", f->inocache->ino));
 	jffs2_add_ino_cache(c, f->inocache);
 
-	ri->magic = JFFS2_MAGIC_BITMASK;
-	ri->nodetype = JFFS2_NODETYPE_INODE;
-	ri->totlen = PAD(sizeof(*ri));
-	ri->hdr_crc = crc32(0, ri, sizeof(struct jffs2_unknown_node)-4);
-	ri->mode = mode;
-	f->highest_version = ri->version = 1;
+	ri->magic = cpu_to_je16(JFFS2_MAGIC_BITMASK);
+	ri->nodetype = cpu_to_je16(JFFS2_NODETYPE_INODE);
+	ri->totlen = cpu_to_je32(PAD(sizeof(*ri)));
+	ri->hdr_crc = cpu_to_je32(crc32(0, ri, sizeof(struct jffs2_unknown_node)-4));
+	ri->mode = cpu_to_jemode(mode);
+
+	f->highest_version = 1;
+	ri->version = cpu_to_je32(f->highest_version);
 
 	return 0;
 }
 
-/* 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 *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);
-
-	/* Alloc jffs2_inode_info when that's split in 2.5 */
-
-	f = JFFS2_INODE_INFO(inode);
-	memset(f, 0, sizeof(*f));
-
-	memset(ri, 0, sizeof(*ri));
-	/* Set OS-specific defaults for new inodes */
-#ifdef __ECOS
-	ri->uid = ri->gid = 0;
-#else
-	ri->uid = current->fsuid;
-
-	if (dir_i->i_mode & S_ISGID) {
-		ri->gid = dir_i->i_gid;
-		if (S_ISDIR(mode))
-			ri->mode |= S_ISGID;
-	} else {
-		ri->gid = current->fsgid;
-	}
-#endif
-	ri->mode = 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 = ri->ino;
-	inode->i_mode = ri->mode;
-	inode->i_gid = ri->gid;
-	inode->i_uid = ri->uid;
-	inode->i_atime = inode->i_ctime = inode->i_mtime = 
-		ri->atime = ri->mtime = ri->ctime = CURRENT_TIME;
-#ifndef __ECOS
-	inode->i_blksize = PAGE_SIZE;
-	inode->i_blocks = 0;
-#endif
-	inode->i_size = 0;
-
-	insert_inode_hash(inode);
-
-	return inode;
-}
-
-#ifndef __ECOS
-/* This ought to be in core MTD code. All registered MTD devices without writev should have
-   this put in place. Bug the MTD maintainer */
-static int mtd_fake_writev(struct mtd_info *mtd, 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++) {
-		mtd->write(mtd, 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;
-	}
-	if (retlen)
-		*retlen = totlen;
-	return ret;
-}
-
-
-static inline int jffs2_flash_writev(struct jffs2_sb_info *c, const struct iovec *vecs, unsigned long count, loff_t to, size_t *retlen)
-{
-	if (c->mtd->writev)
-		return c->mtd->writev(c->mtd, vecs, count, to, retlen);
-	else
-		return mtd_fake_writev(c->mtd, vecs, count, to, retlen);
-}
-#endif
-
+#if CONFIG_JFFS2_FS_DEBUG > 0
 static void writecheck(struct jffs2_sb_info *c, uint32_t ofs)
 {
 	unsigned char buf[16];
@@ -151,8 +63,8 @@ static void writecheck(struct jffs2_sb_i
 	int ret, i;
 
 	ret = jffs2_flash_read(c, ofs, 16, &retlen, buf);
-	if (ret && retlen != 16) {
-		D1(printk(KERN_DEBUG "read failed or short in writecheck(). ret %d, retlen %d\n", ret, retlen));
+	if (ret || (retlen != 16)) {
+		D1(printk(KERN_DEBUG "read failed or short in writecheck(). ret %d, retlen %zd\n", ret, retlen));
 		return;
 	}
 	ret = 0;
@@ -161,16 +73,15 @@ static void writecheck(struct jffs2_sb_i
 			ret = 1;
 	}
 	if (ret) {
-		printk(KERN_WARNING "ARGH. About to write node to 0x%08x on flash, but there's data already there:\n", ofs);
+		printk(KERN_WARNING "ARGH. About to write node to 0x%08x on flash, but there are data already there:\n", ofs);
 		printk(KERN_WARNING "0x%08x: %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n", 
 		       ofs,
 		       buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7],
 		       buf[8], buf[9], buf[10], buf[11], buf[12], buf[13], buf[14], buf[15]);
 	}
 }
+#endif
 
-	
-	
 
 /* jffs2_write_dnode - given a raw_inode, allocate a full_dnode for it, 
    write it to the flash, link it into the existing inode/fragment list */
@@ -183,8 +94,9 @@ struct jffs2_full_dnode *jffs2_write_dno
 	size_t retlen;
 	struct iovec vecs[2];
 	int ret;
+	unsigned long cnt = 2;
 
-	D1(if(ri->hdr_crc != crc32(0, ri, sizeof(struct jffs2_unknown_node)-4)) {
+	D1(if(je32_to_cpu(ri->hdr_crc) != crc32(0, ri, sizeof(struct jffs2_unknown_node)-4)) {
 		printk(KERN_CRIT "Eep. CRC not correct in jffs2_write_dnode()\n");
 		BUG();
 	}
@@ -194,10 +106,10 @@ struct jffs2_full_dnode *jffs2_write_dno
 	vecs[1].iov_base = (unsigned char *)data;
 	vecs[1].iov_len = datalen;
 
-	writecheck(c, flash_ofs);
+	D1(writecheck(c, flash_ofs));
 
-	if (ri->totlen != sizeof(*ri) + datalen) {
-		printk(KERN_WARNING "jffs2_write_dnode: ri->totlen (0x%08x) != sizeof(*ri) (0x%08x) + datalen (0x%08x)\n", ri->totlen, sizeof(*ri), datalen);
+	if (je32_to_cpu(ri->totlen) != sizeof(*ri) + datalen) {
+		printk(KERN_WARNING "jffs2_write_dnode: ri->totlen (0x%08x) != sizeof(*ri) (0x%08zx) + datalen (0x%08x)\n", je32_to_cpu(ri->totlen), sizeof(*ri), datalen);
 	}
 	raw = jffs2_alloc_raw_node_ref();
 	if (!raw)
@@ -209,17 +121,22 @@ struct jffs2_full_dnode *jffs2_write_dno
 		return ERR_PTR(-ENOMEM);
 	}
 	raw->flash_offset = flash_ofs;
-	raw->totlen = PAD(ri->totlen);
+	raw->totlen = PAD(sizeof(*ri)+datalen);
 	raw->next_phys = NULL;
 
-	fn->ofs = ri->offset;
-	fn->size = ri->dsize;
+	fn->ofs = je32_to_cpu(ri->offset);
+	fn->size = je32_to_cpu(ri->dsize);
 	fn->frags = 0;
 	fn->raw = raw;
 
-	ret = jffs2_flash_writev(c, vecs, datalen?2:1, flash_ofs, &retlen);
+	/* check number of valid vecs */
+	if (!datalen || !data)
+		cnt = 1;
+
+	ret = jffs2_flash_writev(c, vecs, cnt, flash_ofs, &retlen);
+		
 	if (ret || (retlen != sizeof(*ri) + datalen)) {
-		printk(KERN_NOTICE "Write of %d bytes at 0x%08x failed. returned %d, retlen %d\n", 
+		printk(KERN_NOTICE "Write of %zd bytes at 0x%08x failed. returned %d, retlen %zd\n", 
 		       sizeof(*ri)+datalen, flash_ofs, ret, retlen);
 		/* Mark the space as dirtied */
 		if (retlen) {
@@ -231,7 +148,8 @@ struct jffs2_full_dnode *jffs2_write_dno
 			   seem corrupted, in which case the scan would skip over
 			   any node we write before the original intended end of 
 			   this node */
-			jffs2_add_physical_node_ref(c, raw, sizeof(*ri)+datalen, 1);
+			raw->flash_offset |= REF_OBSOLETE;
+			jffs2_add_physical_node_ref(c, raw);
 			jffs2_mark_node_obsolete(c, raw);
 		} else {
 			printk(KERN_NOTICE "Not marking the space at 0x%08x as dirty because the flash driver returned retlen zero\n", raw->flash_offset);
@@ -245,13 +163,27 @@ struct jffs2_full_dnode *jffs2_write_dno
 		return ERR_PTR(ret?ret:-EIO);
 	}
 	/* Mark the space used */
-	jffs2_add_physical_node_ref(c, raw, retlen, 0);
+	/* If node covers at least a whole page, or if it starts at the 
+	   beginning of a page and runs to the end of the file, or if 
+	   it's a hole node, mark it REF_PRISTINE, else REF_NORMAL. 
+	*/
+	if ((je32_to_cpu(ri->dsize) >= PAGE_CACHE_SIZE) ||
+	    ( ((je32_to_cpu(ri->offset)&(PAGE_CACHE_SIZE-1))==0) &&
+	      (je32_to_cpu(ri->dsize)+je32_to_cpu(ri->offset) ==  je32_to_cpu(ri->isize)))) {
+		raw->flash_offset |= REF_PRISTINE;
+	} else {
+		raw->flash_offset |= REF_NORMAL;
+	}
+	jffs2_add_physical_node_ref(c, raw);
 
 	/* Link into per-inode list */
 	raw->next_in_ino = f->inocache->nodes;
 	f->inocache->nodes = raw;
 
-	D1(printk(KERN_DEBUG "jffs2_write_dnode wrote node at 0x%08x with dsize 0x%x, csize 0x%x, node_crc 0x%08x, data_crc 0x%08x, totlen 0x%08x\n", flash_ofs, ri->dsize, ri->csize, ri->node_crc, ri->data_crc, ri->totlen));
+	D1(printk(KERN_DEBUG "jffs2_write_dnode wrote node at 0x%08x(%d) with dsize 0x%x, csize 0x%x, node_crc 0x%08x, data_crc 0x%08x, totlen 0x%08x\n",
+		  flash_ofs, ref_flags(raw), je32_to_cpu(ri->dsize), 
+		  je32_to_cpu(ri->csize), je32_to_cpu(ri->node_crc),
+		  je32_to_cpu(ri->data_crc), je32_to_cpu(ri->totlen)));
 	if (writelen)
 		*writelen = retlen;
 
@@ -267,10 +199,12 @@ struct jffs2_full_dirent *jffs2_write_di
 	struct iovec vecs[2];
 	int ret;
 
-	D1(printk(KERN_DEBUG "jffs2_write_dirent(ino #%u, name at *0x%p \"%s\"->ino #%u, name_crc 0x%08x)\n", rd->pino, name, name, rd->ino, rd->name_crc));
-	writecheck(c, flash_ofs);
+	D1(printk(KERN_DEBUG "jffs2_write_dirent(ino #%u, name at *0x%p \"%s\"->ino #%u, name_crc 0x%08x)\n", 
+		  je32_to_cpu(rd->pino), name, name, je32_to_cpu(rd->ino),
+		  je32_to_cpu(rd->name_crc)));
+	D1(writecheck(c, flash_ofs));
 
-	D1(if(rd->hdr_crc != crc32(0, rd, sizeof(struct jffs2_unknown_node)-4)) {
+	D1(if(je32_to_cpu(rd->hdr_crc) != crc32(0, rd, sizeof(struct jffs2_unknown_node)-4)) {
 		printk(KERN_CRIT "Eep. CRC not correct in jffs2_write_dirent()\n");
 		BUG();
 	}
@@ -292,13 +226,13 @@ struct jffs2_full_dirent *jffs2_write_di
 		return ERR_PTR(-ENOMEM);
 	}
 	raw->flash_offset = flash_ofs;
-	raw->totlen = PAD(rd->totlen);
+	raw->totlen = PAD(sizeof(*rd)+namelen);
 	raw->next_in_ino = f->inocache->nodes;
 	f->inocache->nodes = raw;
 	raw->next_phys = NULL;
 
-	fd->version = rd->version;
-	fd->ino = rd->ino;
+	fd->version = je32_to_cpu(rd->version);
+	fd->ino = je32_to_cpu(rd->ino);
 	fd->nhash = full_name_hash(name, strlen(name));
 	fd->type = rd->type;
 	memcpy(fd->name, name, namelen);
@@ -306,17 +240,18 @@ struct jffs2_full_dirent *jffs2_write_di
 	fd->raw = raw;
 
 	ret = jffs2_flash_writev(c, vecs, 2, flash_ofs, &retlen);
-		if (ret || (retlen != sizeof(*rd) + namelen)) {
-			printk(KERN_NOTICE "Write of %d bytes at 0x%08x failed. returned %d, retlen %d\n", 
+	if (ret || (retlen != sizeof(*rd) + namelen)) {
+		printk(KERN_NOTICE "Write of %zd bytes at 0x%08x failed. returned %d, retlen %zd\n", 
 			       sizeof(*rd)+namelen, flash_ofs, ret, retlen);
 		/* Mark the space as dirtied */
-			if (retlen) {
-				jffs2_add_physical_node_ref(c, raw, sizeof(*rd)+namelen, 1);
-				jffs2_mark_node_obsolete(c, raw);
-			} else {
-				printk(KERN_NOTICE "Not marking the space at 0x%08x as dirty because the flash driver returned retlen zero\n", raw->flash_offset);
-				jffs2_free_raw_node_ref(raw);
-			}
+		if (retlen) {
+			raw->flash_offset |= REF_OBSOLETE;
+			jffs2_add_physical_node_ref(c, raw);
+			jffs2_mark_node_obsolete(c, raw);
+		} else {
+			printk(KERN_NOTICE "Not marking the space at 0x%08x as dirty because the flash driver returned retlen zero\n", raw->flash_offset);
+			jffs2_free_raw_node_ref(raw);
+		}
 
 		/* Release the full_dnode which is now useless, and return */
 		jffs2_free_full_dirent(fd);
@@ -325,7 +260,8 @@ struct jffs2_full_dirent *jffs2_write_di
 		return ERR_PTR(ret?ret:-EIO);
 	}
 	/* Mark the space used */
-	jffs2_add_physical_node_ref(c, raw, retlen, 0);
+	raw->flash_offset |= REF_PRISTINE;
+	jffs2_add_physical_node_ref(c, raw);
 	if (writelen)
 		*writelen = retlen;
 
@@ -362,7 +298,7 @@ int jffs2_write_inode_range(struct jffs2
 		}
 		down(&f->sem);
 		datalen = writelen;
-		cdatalen = min(alloclen - sizeof(*ri), writelen);
+		cdatalen = min_t(uint32_t, alloclen - sizeof(*ri), writelen);
 
 		comprbuf = kmalloc(cdatalen, GFP_KERNEL);
 		if (comprbuf) {
@@ -380,31 +316,30 @@ int jffs2_write_inode_range(struct jffs2
 		   that the comprbuf doesn't need to be kfree()d. 
 		*/
 
-		ri->magic = JFFS2_MAGIC_BITMASK;
-		ri->nodetype = JFFS2_NODETYPE_INODE;
-		ri->totlen = sizeof(*ri) + cdatalen;
-		ri->hdr_crc = crc32(0, ri, sizeof(struct jffs2_unknown_node)-4);
+		ri->magic = cpu_to_je16(JFFS2_MAGIC_BITMASK);
+		ri->nodetype = cpu_to_je16(JFFS2_NODETYPE_INODE);
+		ri->totlen = cpu_to_je32(sizeof(*ri) + cdatalen);
+		ri->hdr_crc = cpu_to_je32(crc32(0, ri, sizeof(struct jffs2_unknown_node)-4));
 
-		ri->ino = f->inocache->ino;
-		ri->version = ++f->highest_version;
-		ri->isize = max(ri->isize, offset + datalen);
-		ri->offset = offset;
-		ri->csize = cdatalen;
-		ri->dsize = datalen;
+		ri->ino = cpu_to_je32(f->inocache->ino);
+		ri->version = cpu_to_je32(++f->highest_version);
+		ri->isize = cpu_to_je32(max(je32_to_cpu(ri->isize), offset + datalen));
+		ri->offset = cpu_to_je32(offset);
+		ri->csize = cpu_to_je32(cdatalen);
+		ri->dsize = cpu_to_je32(datalen);
 		ri->compr = comprtype;
-		ri->node_crc = crc32(0, ri, sizeof(*ri)-8);
-		ri->data_crc = crc32(0, comprbuf, cdatalen);
+		ri->node_crc = cpu_to_je32(crc32(0, ri, sizeof(*ri)-8));
+		ri->data_crc = cpu_to_je32(crc32(0, comprbuf, cdatalen));
 
 		fn = jffs2_write_dnode(c, f, ri, comprbuf, cdatalen, phys_ofs, NULL);
 
-		jffs2_complete_reservation(c);
-
 		if (comprtype != JFFS2_COMPR_NONE)
 			kfree(comprbuf);
 
 		if (IS_ERR(fn)) {
 			ret = PTR_ERR(fn);
 			up(&f->sem);
+			jffs2_complete_reservation(c);
 			break;
 		}
 		ret = jffs2_add_full_dnode_to_inode(c, f, fn);
@@ -413,14 +348,18 @@ int jffs2_write_inode_range(struct jffs2
 			jffs2_free_full_dnode(f->metadata);
 			f->metadata = NULL;
 		}
-		up(&f->sem);
 		if (ret) {
 			/* Eep */
 			D1(printk(KERN_DEBUG "Eep. add_full_dnode_to_inode() failed in commit_write, returned %d\n", ret));
 			jffs2_mark_node_obsolete(c, fn->raw);
 			jffs2_free_full_dnode(fn);
+
+			up(&f->sem);
+			jffs2_complete_reservation(c);
 			break;
 		}
+		up(&f->sem);
+		jffs2_complete_reservation(c);
 		if (!datalen) {
 			printk(KERN_WARNING "Eep. We didn't actually write any data in jffs2_write_inode_range()\n");
 			ret = -EIO;
@@ -450,15 +389,18 @@ int jffs2_do_create(struct jffs2_sb_info
 	 */
 	ret = jffs2_reserve_space(c, sizeof(*ri), &phys_ofs, &alloclen, ALLOC_NORMAL);
 	D1(printk(KERN_DEBUG "jffs2_do_create(): reserved 0x%x bytes\n", alloclen));
-	if (ret)
+	if (ret) {
+		up(&f->sem);
 		return ret;
+	}
 
-	ri->data_crc = 0;
-	ri->node_crc = crc32(0, ri, sizeof(*ri)-8);
+	ri->data_crc = cpu_to_je32(0);
+	ri->node_crc = cpu_to_je32(crc32(0, ri, sizeof(*ri)-8));
 
 	fn = jffs2_write_dnode(c, f, ri, NULL, 0, phys_ofs, &writtenlen);
 
-	D1(printk(KERN_DEBUG "jffs2_do_create created file with mode 0x%x\n", ri->mode));
+	D1(printk(KERN_DEBUG "jffs2_do_create created file with mode 0x%x\n",
+		  jemode_to_cpu(ri->mode)));
 
 	if (IS_ERR(fn)) {
 		D1(printk(KERN_DEBUG "jffs2_write_dnode() failed\n"));
@@ -499,28 +441,28 @@ int jffs2_do_create(struct jffs2_sb_info
 
 	down(&dir_f->sem);
 
-	rd->magic = JFFS2_MAGIC_BITMASK;
-	rd->nodetype = JFFS2_NODETYPE_DIRENT;
-	rd->totlen = sizeof(*rd) + namelen;
-	rd->hdr_crc = crc32(0, rd, sizeof(struct jffs2_unknown_node)-4);
+	rd->magic = cpu_to_je16(JFFS2_MAGIC_BITMASK);
+	rd->nodetype = cpu_to_je16(JFFS2_NODETYPE_DIRENT);
+	rd->totlen = cpu_to_je32(sizeof(*rd) + namelen);
+	rd->hdr_crc = cpu_to_je32(crc32(0, rd, sizeof(struct jffs2_unknown_node)-4));
 
-	rd->pino = dir_f->inocache->ino;
-	rd->version = ++dir_f->highest_version;
+	rd->pino = cpu_to_je32(dir_f->inocache->ino);
+	rd->version = cpu_to_je32(++dir_f->highest_version);
 	rd->ino = ri->ino;
-	rd->mctime = CURRENT_TIME;
+	rd->mctime = ri->ctime;
 	rd->nsize = namelen;
 	rd->type = DT_REG;
-	rd->node_crc = crc32(0, rd, sizeof(*rd)-8);
-	rd->name_crc = crc32(0, name, namelen);
+	rd->node_crc = cpu_to_je32(crc32(0, rd, sizeof(*rd)-8));
+	rd->name_crc = cpu_to_je32(crc32(0, name, namelen));
 
 	fd = jffs2_write_dirent(c, dir_f, rd, name, namelen, phys_ofs, &writtenlen);
 
-	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() */
+		jffs2_complete_reservation(c);
 		up(&dir_f->sem);
 		return PTR_ERR(fd);
 	}
@@ -528,13 +470,16 @@ int jffs2_do_create(struct jffs2_sb_info
 	/* Link the fd into the inode's list, obsoleting an old
 	   one if necessary. */
 	jffs2_add_fd_to_list(c, fd, &dir_f->dents);
+
+	jffs2_complete_reservation(c);
 	up(&dir_f->sem);
 
 	return 0;
 }
 
 
-int jffs2_do_unlink(struct jffs2_sb_info *c, struct jffs2_inode_info *dir_f, const char *name, int namelen, struct jffs2_inode_info *dead_f)
+int jffs2_do_unlink(struct jffs2_sb_info *c, struct jffs2_inode_info *dir_f,
+		    const char *name, int namelen, struct jffs2_inode_info *dead_f)
 {
 	struct jffs2_raw_dirent *rd;
 	struct jffs2_full_dirent *fd;
@@ -554,35 +499,39 @@ int jffs2_do_unlink(struct jffs2_sb_info
 	down(&dir_f->sem);
 
 	/* Build a deletion node */
-	rd->magic = JFFS2_MAGIC_BITMASK;
-	rd->nodetype = JFFS2_NODETYPE_DIRENT;
-	rd->totlen = sizeof(*rd) + namelen;
-	rd->hdr_crc = crc32(0, rd, sizeof(struct jffs2_unknown_node)-4);
+	rd->magic = cpu_to_je16(JFFS2_MAGIC_BITMASK);
+	rd->nodetype = cpu_to_je16(JFFS2_NODETYPE_DIRENT);
+	rd->totlen = cpu_to_je32(sizeof(*rd) + namelen);
+	rd->hdr_crc = cpu_to_je32(crc32(0, rd, sizeof(struct jffs2_unknown_node)-4));
 
-	rd->pino = dir_f->inocache->ino;
-	rd->version = ++dir_f->highest_version;
-	rd->ino = 0;
-	rd->mctime = CURRENT_TIME;
+	rd->pino = cpu_to_je32(dir_f->inocache->ino);
+	rd->version = cpu_to_je32(++dir_f->highest_version);
+	rd->ino = cpu_to_je32(0);
+	rd->mctime = cpu_to_je32(get_seconds());
 	rd->nsize = namelen;
 	rd->type = DT_UNKNOWN;
-	rd->node_crc = crc32(0, rd, sizeof(*rd)-8);
-	rd->name_crc = crc32(0, name, namelen);
+	rd->node_crc = cpu_to_je32(crc32(0, rd, sizeof(*rd)-8));
+	rd->name_crc = cpu_to_je32(crc32(0, name, namelen));
 
 	fd = jffs2_write_dirent(c, dir_f, rd, name, namelen, phys_ofs, NULL);
 	
-	jffs2_complete_reservation(c);
 	jffs2_free_raw_dirent(rd);
 
 	if (IS_ERR(fd)) {
+		jffs2_complete_reservation(c);
 		up(&dir_f->sem);
 		return PTR_ERR(fd);
 	}
 
 	/* File it. This will mark the old one obsolete. */
 	jffs2_add_fd_to_list(c, fd, &dir_f->dents);
+
 	up(&dir_f->sem);
 	
-	if (dead_f) { /* Null if this was a rename not a real unlink */
+	/* dead_f is NULL if this was a rename not a real unlink */
+	/* Also catch the !f->inocache case, where there was a dirent
+	   pointing to an inode which didn't exist. */
+	if (dead_f && dead_f->inocache) { 
 
 		down(&dead_f->sem);
 
@@ -607,6 +556,8 @@ int jffs2_do_unlink(struct jffs2_sb_info
 		up(&dead_f->sem);
 	}
 
+	jffs2_complete_reservation(c);
+
 	return 0;
 }
 
@@ -631,34 +582,36 @@ int jffs2_do_link (struct jffs2_sb_info 
 	down(&dir_f->sem);
 
 	/* Build a deletion node */
-	rd->magic = JFFS2_MAGIC_BITMASK;
-	rd->nodetype = JFFS2_NODETYPE_DIRENT;
-	rd->totlen = sizeof(*rd) + namelen;
-	rd->hdr_crc = crc32(0, rd, sizeof(struct jffs2_unknown_node)-4);
+	rd->magic = cpu_to_je16(JFFS2_MAGIC_BITMASK);
+	rd->nodetype = cpu_to_je16(JFFS2_NODETYPE_DIRENT);
+	rd->totlen = cpu_to_je32(sizeof(*rd) + namelen);
+	rd->hdr_crc = cpu_to_je32(crc32(0, rd, sizeof(struct jffs2_unknown_node)-4));
 
-	rd->pino = dir_f->inocache->ino;
-	rd->version = ++dir_f->highest_version;
-	rd->ino = ino;
-	rd->mctime = CURRENT_TIME;
+	rd->pino = cpu_to_je32(dir_f->inocache->ino);
+	rd->version = cpu_to_je32(++dir_f->highest_version);
+	rd->ino = cpu_to_je32(ino);
+	rd->mctime = cpu_to_je32(get_seconds());
 	rd->nsize = namelen;
 
 	rd->type = type;
 
-	rd->node_crc = crc32(0, rd, sizeof(*rd)-8);
-	rd->name_crc = crc32(0, name, namelen);
+	rd->node_crc = cpu_to_je32(crc32(0, rd, sizeof(*rd)-8));
+	rd->name_crc = cpu_to_je32(crc32(0, name, namelen));
 
 	fd = jffs2_write_dirent(c, dir_f, rd, name, namelen, phys_ofs, NULL);
 	
-	jffs2_complete_reservation(c);
 	jffs2_free_raw_dirent(rd);
 
 	if (IS_ERR(fd)) {
+		jffs2_complete_reservation(c);
 		up(&dir_f->sem);
 		return PTR_ERR(fd);
 	}
 
 	/* File it. This will mark the old one obsolete. */
 	jffs2_add_fd_to_list(c, fd, &dir_f->dents);
+
+	jffs2_complete_reservation(c);
 	up(&dir_f->sem);
 
 	return 0;