Index: LICENSE.JFFS2
===================================================================
--- LICENSE.JFFS2	(revision 0c0f128e11007cb0d9ebed8b27dbb7cdde18450d)
+++ LICENSE.JFFS2	(revision 0c0f128e11007cb0d9ebed8b27dbb7cdde18450d)
@@ -0,0 +1,30 @@
+The files in this directory and elsewhere which refer to this LICENCE
+file are part of JFFS2, the Journalling Flash File System v2.
+
+	Copyright Â© 2001-2007 Red Hat, Inc. and others
+
+JFFS2 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.
+
+JFFS2 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 JFFS2; 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 these files, or you compile these
+files and link them with other works to produce a work based on these
+files, these files do not by themselves cause the resulting work to be
+covered by the GNU General Public License. However the source code for
+these files 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.
+
Index: cpukit/libfs/src/jffs2/include/linux/jffs2.h
===================================================================
--- cpukit/libfs/src/jffs2/include/linux/jffs2.h	(revision 0c0f128e11007cb0d9ebed8b27dbb7cdde18450d)
+++ cpukit/libfs/src/jffs2/include/linux/jffs2.h	(revision 0c0f128e11007cb0d9ebed8b27dbb7cdde18450d)
@@ -0,0 +1,223 @@
+/*
+ * JFFS2 -- Journalling Flash File System, Version 2.
+ *
+ * Copyright Â© 2001-2007 Red Hat, Inc.
+ * Copyright Â© 2004-2010 David Woodhouse <dwmw2@infradead.org>
+ *
+ * Created by David Woodhouse <dwmw2@infradead.org>
+ *
+ * For licensing information, see the file 'LICENCE' in the
+ * jffs2 directory.
+ */
+
+#ifndef __LINUX_JFFS2_H__
+#define __LINUX_JFFS2_H__
+
+#include <linux/types.h>
+#include <linux/magic.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. */
+
+/* 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 0x8519 /* For detecting wrong-endian fs */
+#define JFFS2_EMPTY_BITMASK 0xffff
+#define JFFS2_DIRTY_BITMASK 0x0000
+
+/* Summary node MAGIC marker */
+#define JFFS2_SUM_MAGIC	0x02851885
+
+/* 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
+#define JFFS2_COMPR_LZO		0x07
+/* 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)
+
+#define JFFS2_NODETYPE_SUMMARY (JFFS2_FEATURE_RWCOMPAT_DELETE | JFFS2_NODE_ACCURATE | 6)
+
+#define JFFS2_NODETYPE_XATTR (JFFS2_FEATURE_INCOMPAT | JFFS2_NODE_ACCURATE | 8)
+#define JFFS2_NODETYPE_XREF (JFFS2_FEATURE_INCOMPAT | JFFS2_NODE_ACCURATE | 9)
+
+/* XATTR Related */
+#define JFFS2_XPREFIX_USER		1	/* for "user." */
+#define JFFS2_XPREFIX_SECURITY		2	/* for "security." */
+#define JFFS2_XPREFIX_ACL_ACCESS	3	/* for "system.posix_acl_access" */
+#define JFFS2_XPREFIX_ACL_DEFAULT	4	/* for "system.posix_acl_default" */
+#define JFFS2_XPREFIX_TRUSTED		5	/* for "trusted.*" */
+
+#define JFFS2_ACL_VERSION		0x0001
+
+// 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 {
+	__u32 v32;
+} __attribute__((packed)) jint32_t;
+
+typedef struct {
+	__u32 m;
+} __attribute__((packed)) jmode_t;
+
+typedef struct {
+	__u16 v16;
+} __attribute__((packed)) jint16_t;
+
+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;
+};
+
+struct jffs2_raw_dirent
+{
+	jint16_t magic;
+	jint16_t nodetype;	/* == JFFS2_NODETYPE_DIRENT */
+	jint32_t totlen;
+	jint32_t hdr_crc;
+	jint32_t pino;
+	jint32_t version;
+	jint32_t ino; /* == zero for unlink */
+	jint32_t mctime;
+	__u8 nsize;
+	__u8 type;
+	__u8 unused[2];
+	jint32_t node_crc;
+	jint32_t name_crc;
+	__u8 name[0];
+};
+
+/* 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;   /* == JFFS2_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) */
+	__u8 compr;       /* Compression algorithm used */
+	__u8 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)  */
+	__u8 data[0];
+};
+
+struct jffs2_raw_xattr {
+	jint16_t magic;
+	jint16_t nodetype;	/* = JFFS2_NODETYPE_XATTR */
+	jint32_t totlen;
+	jint32_t hdr_crc;
+	jint32_t xid;		/* XATTR identifier number */
+	jint32_t version;
+	__u8 xprefix;
+	__u8 name_len;
+	jint16_t value_len;
+	jint32_t data_crc;
+	jint32_t node_crc;
+	__u8 data[0];
+} __attribute__((packed));
+
+struct jffs2_raw_xref
+{
+	jint16_t magic;
+	jint16_t nodetype;	/* = JFFS2_NODETYPE_XREF */
+	jint32_t totlen;
+	jint32_t hdr_crc;
+	jint32_t ino;		/* inode number */
+	jint32_t xid;		/* XATTR identifier number */
+	jint32_t xseqno;	/* xref sequential number */
+	jint32_t node_crc;
+} __attribute__((packed));
+
+struct jffs2_raw_summary
+{
+	jint16_t magic;
+	jint16_t nodetype; 	/* = JFFS2_NODETYPE_SUMMARY */
+	jint32_t totlen;
+	jint32_t hdr_crc;
+	jint32_t sum_num;	/* number of sum entries*/
+	jint32_t cln_mkr;	/* clean marker size, 0 = no cleanmarker */
+	jint32_t padded;	/* sum of the size of padding nodes */
+	jint32_t sum_crc;	/* summary information crc */
+	jint32_t node_crc; 	/* node crc */
+	jint32_t sum[0]; 	/* inode summary info */
+};
+
+union jffs2_node_union
+{
+	struct jffs2_raw_inode i;
+	struct jffs2_raw_dirent d;
+	struct jffs2_raw_xattr x;
+	struct jffs2_raw_xref r;
+	struct jffs2_raw_summary s;
+	struct jffs2_unknown_node u;
+};
+
+/* Data payload for device nodes. */
+union jffs2_device_node {
+	jint16_t old_id;
+	jint32_t new_id;
+};
+
+#endif /* __LINUX_JFFS2_H__ */
Index: cpukit/libfs/src/jffs2/src/LICENCE
===================================================================
--- cpukit/libfs/src/jffs2/src/LICENCE	(revision 0c0f128e11007cb0d9ebed8b27dbb7cdde18450d)
+++ cpukit/libfs/src/jffs2/src/LICENCE	(revision 0c0f128e11007cb0d9ebed8b27dbb7cdde18450d)
@@ -0,0 +1,30 @@
+The files in this directory and elsewhere which refer to this LICENCE
+file are part of JFFS2, the Journalling Flash File System v2.
+
+	Copyright Â© 2001-2007 Red Hat, Inc. and others
+
+JFFS2 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.
+
+JFFS2 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 JFFS2; 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 these files, or you compile these
+files and link them with other works to produce a work based on these
+files, these files do not by themselves cause the resulting work to be
+covered by the GNU General Public License. However the source code for
+these files 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.
+
Index: cpukit/libfs/src/jffs2/src/acl.h
===================================================================
--- cpukit/libfs/src/jffs2/src/acl.h	(revision 0c0f128e11007cb0d9ebed8b27dbb7cdde18450d)
+++ cpukit/libfs/src/jffs2/src/acl.h	(revision 0c0f128e11007cb0d9ebed8b27dbb7cdde18450d)
@@ -0,0 +1,44 @@
+/*
+ * JFFS2 -- Journalling Flash File System, Version 2.
+ *
+ * Copyright Â© 2006  NEC Corporation
+ *
+ * Created by KaiGai Kohei <kaigai@ak.jp.nec.com>
+ *
+ * For licensing information, see the file 'LICENCE' in this directory.
+ *
+ */
+
+struct jffs2_acl_entry {
+	jint16_t	e_tag;
+	jint16_t	e_perm;
+	jint32_t	e_id;
+};
+
+struct jffs2_acl_entry_short {
+	jint16_t	e_tag;
+	jint16_t	e_perm;
+};
+
+struct jffs2_acl_header {
+	jint32_t	a_version;
+};
+
+#ifdef CONFIG_JFFS2_FS_POSIX_ACL
+
+struct posix_acl *jffs2_get_acl(struct inode *inode, int type);
+extern int jffs2_acl_chmod(struct inode *);
+extern int jffs2_init_acl_pre(struct inode *, struct inode *, umode_t *);
+extern int jffs2_init_acl_post(struct inode *);
+
+extern const struct xattr_handler jffs2_acl_access_xattr_handler;
+extern const struct xattr_handler jffs2_acl_default_xattr_handler;
+
+#else
+
+#define jffs2_get_acl				(NULL)
+#define jffs2_acl_chmod(inode)			(0)
+#define jffs2_init_acl_pre(dir_i,inode,mode)	(0)
+#define jffs2_init_acl_post(inode)		(0)
+
+#endif	/* CONFIG_JFFS2_FS_POSIX_ACL */
Index: cpukit/libfs/src/jffs2/src/build.c
===================================================================
--- cpukit/libfs/src/jffs2/src/build.c	(revision 0c0f128e11007cb0d9ebed8b27dbb7cdde18450d)
+++ cpukit/libfs/src/jffs2/src/build.c	(revision 0c0f128e11007cb0d9ebed8b27dbb7cdde18450d)
@@ -0,0 +1,394 @@
+/*
+ * JFFS2 -- Journalling Flash File System, Version 2.
+ *
+ * Copyright Â© 2001-2007 Red Hat, Inc.
+ * Copyright Â© 2004-2010 David Woodhouse <dwmw2@infradead.org>
+ *
+ * Created by David Woodhouse <dwmw2@infradead.org>
+ *
+ * For licensing information, see the file 'LICENCE' in this directory.
+ *
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+#include <linux/mtd/mtd.h>
+#include "nodelist.h"
+
+static void jffs2_build_remove_unlinked_inode(struct jffs2_sb_info *,
+		struct jffs2_inode_cache *, struct jffs2_full_dirent **);
+
+static inline struct jffs2_inode_cache *
+first_inode_chain(int *i, struct jffs2_sb_info *c)
+{
+	for (; *i < c->inocache_hashsize; (*i)++) {
+		if (c->inocache_list[*i])
+			return c->inocache_list[*i];
+	}
+	return NULL;
+}
+
+static inline struct jffs2_inode_cache *
+next_inode(int *i, struct jffs2_inode_cache *ic, struct jffs2_sb_info *c)
+{
+	/* More in this chain? */
+	if (ic->next)
+		return ic->next;
+	(*i)++;
+	return first_inode_chain(i, c);
+}
+
+#define for_each_inode(i, c, ic)			\
+	for (i = 0, ic = first_inode_chain(&i, (c));	\
+	     ic;					\
+	     ic = next_inode(&i, ic, (c)))
+
+
+static void jffs2_build_inode_pass1(struct jffs2_sb_info *c,
+				    struct jffs2_inode_cache *ic)
+{
+	struct jffs2_full_dirent *fd;
+
+	dbg_fsbuild("building directory inode #%u\n", ic->ino);
+
+	/* For each child, increase nlink */
+	for(fd = ic->scan_dents; fd; fd = fd->next) {
+		struct jffs2_inode_cache *child_ic;
+		if (!fd->ino)
+			continue;
+
+		/* we can get high latency here with huge directories */
+
+		child_ic = jffs2_get_ino_cache(c, fd->ino);
+		if (!child_ic) {
+			dbg_fsbuild("child \"%s\" (ino #%u) of dir ino #%u doesn't exist!\n",
+				  fd->name, fd->ino, ic->ino);
+			jffs2_mark_node_obsolete(c, fd->raw);
+			continue;
+		}
+
+		if (fd->type == DT_DIR) {
+			if (child_ic->pino_nlink) {
+				JFFS2_ERROR("child dir \"%s\" (ino #%u) of dir ino #%u appears to be a hard link\n",
+					    fd->name, fd->ino, ic->ino);
+				/* TODO: What do we do about it? */
+			} else {
+				child_ic->pino_nlink = ic->ino;
+			}
+		} else
+			child_ic->pino_nlink++;
+
+		dbg_fsbuild("increased nlink for child \"%s\" (ino #%u)\n", fd->name, fd->ino);
+		/* Can't free scan_dents so far. We might need them in pass 2 */
+	}
+}
+
+/* Scan plan:
+ - Scan physical nodes. Build map of inodes/dirents. Allocate inocaches as we go
+ - Scan directory tree from top down, setting nlink in inocaches
+ - Scan inocaches for inodes with nlink==0
+*/
+static int jffs2_build_filesystem(struct jffs2_sb_info *c)
+{
+	int ret;
+	int i;
+	struct jffs2_inode_cache *ic;
+	struct jffs2_full_dirent *fd;
+	struct jffs2_full_dirent *dead_fds = NULL;
+
+	dbg_fsbuild("build FS data structures\n");
+
+	/* First, scan the medium and build all the inode caches with
+	   lists of physical nodes */
+
+	c->flags |= JFFS2_SB_FLAG_SCANNING;
+	ret = jffs2_scan_medium(c);
+	c->flags &= ~JFFS2_SB_FLAG_SCANNING;
+	if (ret)
+		goto exit;
+
+	dbg_fsbuild("scanned flash completely\n");
+	jffs2_dbg_dump_block_lists_nolock(c);
+
+	dbg_fsbuild("pass 1 starting\n");
+	c->flags |= JFFS2_SB_FLAG_BUILDING;
+	/* Now scan the directory tree, increasing nlink according to every dirent found. */
+	for_each_inode(i, c, ic) {
+		if (ic->scan_dents) {
+			jffs2_build_inode_pass1(c, ic);
+			cond_resched();
+		}
+	}
+
+	dbg_fsbuild("pass 1 complete\n");
+
+	/* Next, scan for inodes with nlink == 0 and remove them. If
+	   they were directories, then decrement the nlink of their
+	   children too, and repeat the scan. As that's going to be
+	   a fairly uncommon occurrence, it's not so evil to do it this
+	   way. Recursion bad. */
+	dbg_fsbuild("pass 2 starting\n");
+
+	for_each_inode(i, c, ic) {
+		if (ic->pino_nlink)
+			continue;
+
+		jffs2_build_remove_unlinked_inode(c, ic, &dead_fds);
+		cond_resched();
+	}
+
+	dbg_fsbuild("pass 2a starting\n");
+
+	while (dead_fds) {
+		fd = dead_fds;
+		dead_fds = fd->next;
+
+		ic = jffs2_get_ino_cache(c, fd->ino);
+
+		if (ic)
+			jffs2_build_remove_unlinked_inode(c, ic, &dead_fds);
+		jffs2_free_full_dirent(fd);
+	}
+
+	dbg_fsbuild("pass 2a complete\n");
+	dbg_fsbuild("freeing temporary data structures\n");
+
+	/* Finally, we can scan again and free the dirent structs */
+	for_each_inode(i, c, ic) {
+		while(ic->scan_dents) {
+			fd = ic->scan_dents;
+			ic->scan_dents = fd->next;
+			jffs2_free_full_dirent(fd);
+		}
+		ic->scan_dents = NULL;
+		cond_resched();
+	}
+	jffs2_build_xattr_subsystem(c);
+	c->flags &= ~JFFS2_SB_FLAG_BUILDING;
+
+	dbg_fsbuild("FS build complete\n");
+
+	/* Rotate the lists by some number to ensure wear levelling */
+	jffs2_rotate_lists(c);
+
+	ret = 0;
+
+exit:
+	if (ret) {
+		for_each_inode(i, c, ic) {
+			while(ic->scan_dents) {
+				fd = ic->scan_dents;
+				ic->scan_dents = fd->next;
+				jffs2_free_full_dirent(fd);
+			}
+		}
+		jffs2_clear_xattr_subsystem(c);
+	}
+
+	return ret;
+}
+
+static void jffs2_build_remove_unlinked_inode(struct jffs2_sb_info *c,
+					struct jffs2_inode_cache *ic,
+					struct jffs2_full_dirent **dead_fds)
+{
+	struct jffs2_raw_node_ref *raw;
+	struct jffs2_full_dirent *fd;
+
+	dbg_fsbuild("removing ino #%u with nlink == zero.\n", ic->ino);
+
+	raw = ic->nodes;
+	while (raw != (void *)ic) {
+		struct jffs2_raw_node_ref *next = raw->next_in_ino;
+		dbg_fsbuild("obsoleting node at 0x%08x\n", ref_offset(raw));
+		jffs2_mark_node_obsolete(c, raw);
+		raw = next;
+	}
+
+	if (ic->scan_dents) {
+		int whinged = 0;
+		dbg_fsbuild("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;
+
+			if (!fd->ino) {
+				/* It's a deletion dirent. Ignore it */
+				dbg_fsbuild("child \"%s\" is a deletion dirent, skipping...\n", fd->name);
+				jffs2_free_full_dirent(fd);
+				continue;
+			}
+			if (!whinged)
+				whinged = 1;
+
+			dbg_fsbuild("removing child \"%s\", ino #%u\n", fd->name, fd->ino);
+
+			child_ic = jffs2_get_ino_cache(c, fd->ino);
+			if (!child_ic) {
+				dbg_fsbuild("cannot remove child \"%s\", ino #%u, because it doesn't exist\n",
+						fd->name, fd->ino);
+				jffs2_free_full_dirent(fd);
+				continue;
+			}
+
+			/* Reduce nlink of the child. If it's now zero, stick it on the
+			   dead_fds list to be cleaned up later. Else just free the fd */
+
+			if (fd->type == DT_DIR)
+				child_ic->pino_nlink = 0;
+			else
+				child_ic->pino_nlink--;
+
+			if (!child_ic->pino_nlink) {
+				dbg_fsbuild("inode #%u (\"%s\") now has no links; adding to dead_fds list.\n",
+					  fd->ino, fd->name);
+				fd->next = *dead_fds;
+				*dead_fds = fd;
+			} else {
+				dbg_fsbuild("inode #%u (\"%s\") has now got nlink %d. Ignoring.\n",
+					  fd->ino, fd->name, child_ic->pino_nlink);
+				jffs2_free_full_dirent(fd);
+			}
+		}
+	}
+
+	/*
+	   We don't delete the inocache from the hash list and free it yet.
+	   The erase code will do that, when all the nodes are completely gone.
+	*/
+}
+
+static void jffs2_calc_trigger_levels(struct jffs2_sb_info *c)
+{
+	uint32_t size;
+
+	/* Deletion should almost _always_ be allowed. We're fairly
+	   buggered once we stop allowing people to delete stuff
+	   because there's not enough free space... */
+	c->resv_blocks_deletion = 2;
+
+	/* Be conservative about how much space we need before we allow writes.
+	   On top of that which is required for deletia, require an extra 2%
+	   of the medium to be available, for overhead caused by nodes being
+	   split across blocks, etc. */
+
+	size = c->flash_size / 50; /* 2% of flash size */
+	size += c->nr_blocks * 100; /* And 100 bytes per eraseblock */
+	size += c->sector_size - 1; /* ... and round up */
+
+	c->resv_blocks_write = c->resv_blocks_deletion + (size / c->sector_size);
+
+	/* When do we let the GC thread run in the background */
+
+	c->resv_blocks_gctrigger = c->resv_blocks_write + 1;
+
+	/* When do we allow garbage collection to merge nodes to make
+	   long-term progress at the expense of short-term space exhaustion? */
+	c->resv_blocks_gcmerge = c->resv_blocks_deletion + 1;
+
+	/* When do we allow garbage collection to eat from bad blocks rather
+	   than actually making progress? */
+	c->resv_blocks_gcbad = 0;//c->resv_blocks_deletion + 2;
+
+	/* What number of 'very dirty' eraseblocks do we allow before we
+	   trigger the GC thread even if we don't _need_ the space. When we
+	   can't mark nodes obsolete on the medium, the old dirty nodes cause
+	   performance problems because we have to inspect and discard them. */
+	c->vdirty_blocks_gctrigger = c->resv_blocks_gctrigger;
+	if (jffs2_can_mark_obsolete(c))
+		c->vdirty_blocks_gctrigger *= 10;
+
+	/* If there's less than this amount of dirty space, don't bother
+	   trying to GC to make more space. It'll be a fruitless task */
+	c->nospc_dirty_size = c->sector_size + (c->flash_size / 100);
+
+	dbg_fsbuild("trigger levels (size %d KiB, block size %d KiB, %d blocks)\n",
+		    c->flash_size / 1024, c->sector_size / 1024, c->nr_blocks);
+	dbg_fsbuild("Blocks required to allow deletion:    %d (%d KiB)\n",
+		  c->resv_blocks_deletion, c->resv_blocks_deletion*c->sector_size/1024);
+	dbg_fsbuild("Blocks required to allow writes:      %d (%d KiB)\n",
+		  c->resv_blocks_write, c->resv_blocks_write*c->sector_size/1024);
+	dbg_fsbuild("Blocks required to quiesce GC thread: %d (%d KiB)\n",
+		  c->resv_blocks_gctrigger, c->resv_blocks_gctrigger*c->sector_size/1024);
+	dbg_fsbuild("Blocks required to allow GC merges:   %d (%d KiB)\n",
+		  c->resv_blocks_gcmerge, c->resv_blocks_gcmerge*c->sector_size/1024);
+	dbg_fsbuild("Blocks required to GC bad blocks:     %d (%d KiB)\n",
+		  c->resv_blocks_gcbad, c->resv_blocks_gcbad*c->sector_size/1024);
+	dbg_fsbuild("Amount of dirty space required to GC: %d bytes\n",
+		  c->nospc_dirty_size);
+	dbg_fsbuild("Very dirty blocks before GC triggered: %d\n",
+		  c->vdirty_blocks_gctrigger);
+}
+
+int jffs2_do_mount_fs(struct jffs2_sb_info *c)
+{
+	int ret;
+	int i;
+	int size;
+
+	c->free_size = c->flash_size;
+	c->nr_blocks = c->flash_size / c->sector_size;
+	size = sizeof(struct jffs2_eraseblock) * c->nr_blocks;
+#ifndef __ECOS
+	if (jffs2_blocks_use_vmalloc(c))
+		c->blocks = vzalloc(size);
+	else
+#endif
+		c->blocks = kzalloc(size, GFP_KERNEL);
+	if (!c->blocks)
+		return -ENOMEM;
+
+	for (i=0; i<c->nr_blocks; i++) {
+		INIT_LIST_HEAD(&c->blocks[i].list);
+		c->blocks[i].offset = i * c->sector_size;
+		c->blocks[i].free_size = c->sector_size;
+	}
+
+	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_checking_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);
+	INIT_LIST_HEAD(&c->bad_used_list);
+	c->highest_ino = 1;
+	c->summary = NULL;
+
+	ret = jffs2_sum_init(c);
+	if (ret)
+		goto out_free;
+
+	if (jffs2_build_filesystem(c)) {
+		dbg_fsbuild("build_fs failed\n");
+		jffs2_free_ino_caches(c);
+		jffs2_free_raw_node_refs(c);
+		ret = -EIO;
+		goto out_free;
+	}
+
+	jffs2_calc_trigger_levels(c);
+
+	return 0;
+
+ out_free:
+#ifndef __ECOS
+	if (jffs2_blocks_use_vmalloc(c))
+		vfree(c->blocks);
+	else
+#endif
+		kfree(c->blocks);
+
+	return ret;
+}
Index: cpukit/libfs/src/jffs2/src/compr.c
===================================================================
--- cpukit/libfs/src/jffs2/src/compr.c	(revision 0c0f128e11007cb0d9ebed8b27dbb7cdde18450d)
+++ cpukit/libfs/src/jffs2/src/compr.c	(revision 0c0f128e11007cb0d9ebed8b27dbb7cdde18450d)
@@ -0,0 +1,418 @@
+/*
+ * JFFS2 -- Journalling Flash File System, Version 2.
+ *
+ * Copyright Â© 2001-2007 Red Hat, Inc.
+ * Copyright Â© 2004-2010 David Woodhouse <dwmw2@infradead.org>
+ * Copyright Â© 2004 Ferenc Havasi <havasi@inf.u-szeged.hu>,
+ *		    University of Szeged, Hungary
+ *
+ * Created by Arjan van de Ven <arjan@infradead.org>
+ *
+ * For licensing information, see the file 'LICENCE' in this directory.
+ *
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include "compr.h"
+
+static DEFINE_SPINLOCK(jffs2_compressor_list_lock);
+
+/* Available compressors are on this list */
+static LIST_HEAD(jffs2_compressor_list);
+
+/* Actual compression mode */
+static int jffs2_compression_mode = JFFS2_COMPR_MODE_PRIORITY;
+
+/* Statistics for blocks stored without compression */
+static uint32_t none_stat_compr_blocks=0,none_stat_decompr_blocks=0,none_stat_compr_size=0;
+
+
+/*
+ * Return 1 to use this compression
+ */
+static int jffs2_is_best_compression(struct jffs2_compressor *this,
+		struct jffs2_compressor *best, uint32_t size, uint32_t bestsize)
+{
+	switch (jffs2_compression_mode) {
+	case JFFS2_COMPR_MODE_SIZE:
+		if (bestsize > size)
+			return 1;
+		return 0;
+	case JFFS2_COMPR_MODE_FAVOURLZO:
+		if ((this->compr == JFFS2_COMPR_LZO) && (bestsize > size))
+			return 1;
+		if ((best->compr != JFFS2_COMPR_LZO) && (bestsize > size))
+			return 1;
+		if ((this->compr == JFFS2_COMPR_LZO) && (bestsize > (size * FAVOUR_LZO_PERCENT / 100)))
+			return 1;
+		if ((bestsize * FAVOUR_LZO_PERCENT / 100) > size)
+			return 1;
+
+		return 0;
+	}
+	/* Shouldn't happen */
+	return 0;
+}
+
+/*
+ * jffs2_selected_compress:
+ * @compr: Explicit compression type to use (ie, JFFS2_COMPR_ZLIB).
+ *	If 0, just take the first available compression mode.
+ * @data_in: Pointer to uncompressed data
+ * @cpage_out: Pointer to returned pointer to buffer for compressed data
+ * @datalen: On entry, holds the amount of data available for compression.
+ *	On exit, expected to hold the amount of data actually compressed.
+ * @cdatalen: On entry, holds the amount of space available for compressed
+ *	data. On exit, expected to hold the actual size of the compressed
+ *	data.
+ *
+ * Returns: the compression type used.  Zero is used to show that the data
+ * could not be compressed; probably because we couldn't find the requested
+ * compression mode.
+ */
+static int jffs2_selected_compress(u8 compr, unsigned char *data_in,
+		unsigned char **cpage_out, u32 *datalen, u32 *cdatalen)
+{
+	struct jffs2_compressor *this;
+	int err, ret = JFFS2_COMPR_NONE;
+	uint32_t orig_slen, orig_dlen;
+	char *output_buf;
+
+	output_buf = kmalloc(*cdatalen, GFP_KERNEL);
+	if (!output_buf) {
+		pr_warn("No memory for compressor allocation. Compression failed.\n");
+		return ret;
+	}
+	orig_slen = *datalen;
+	orig_dlen = *cdatalen;
+	spin_lock(&jffs2_compressor_list_lock);
+	list_for_each_entry(this, &jffs2_compressor_list, list) {
+		/* Skip decompress-only and disabled modules */
+		if (!this->compress || this->disabled)
+			continue;
+
+		/* Skip if not the desired compression type */
+		if (compr && (compr != this->compr))
+			continue;
+
+		/*
+		 * Either compression type was unspecified, or we found our
+		 * compressor; either way, we're good to go.
+		 */
+		this->usecount++;
+		spin_unlock(&jffs2_compressor_list_lock);
+
+		*datalen  = orig_slen;
+		*cdatalen = orig_dlen;
+		err = this->compress(data_in, output_buf, datalen, cdatalen);
+
+		spin_lock(&jffs2_compressor_list_lock);
+		this->usecount--;
+		if (!err) {
+			/* Success */
+			ret = this->compr;
+			this->stat_compr_blocks++;
+			this->stat_compr_orig_size += *datalen;
+			this->stat_compr_new_size += *cdatalen;
+			break;
+		}
+	}
+	spin_unlock(&jffs2_compressor_list_lock);
+	if (ret == JFFS2_COMPR_NONE)
+		kfree(output_buf);
+	else
+		*cpage_out = output_buf;
+
+	return ret;
+}
+
+/* jffs2_compress:
+ * @data_in: Pointer to uncompressed data
+ * @cpage_out: Pointer to returned pointer to buffer for compressed data
+ * @datalen: On entry, holds the amount of data available for compression.
+ *	On exit, expected to hold the amount of data actually compressed.
+ * @cdatalen: On entry, holds the amount of space available for compressed
+ *	data. On exit, expected to hold the actual size of the compressed
+ *	data.
+ *
+ * Returns: Lower byte to be stored with data indicating compression type used.
+ * Zero is used to show that the data could not be compressed - the
+ * compressed version was actually larger than the original.
+ * Upper byte will be used later. (soon)
+ *
+ * If the cdata buffer isn't large enough to hold all the uncompressed data,
+ * jffs2_compress should compress as much as will fit, and should set
+ * *datalen accordingly to show the amount of data which were compressed.
+ */
+uint16_t jffs2_compress(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
+			unsigned char *data_in, unsigned char **cpage_out,
+			uint32_t *datalen, uint32_t *cdatalen)
+{
+	int ret = JFFS2_COMPR_NONE;
+	int mode, compr_ret;
+	struct jffs2_compressor *this, *best=NULL;
+	unsigned char *output_buf = NULL, *tmp_buf;
+	uint32_t orig_slen, orig_dlen;
+	uint32_t best_slen=0, best_dlen=0;
+
+	if (c->mount_opts.override_compr)
+		mode = c->mount_opts.compr;
+	else
+		mode = jffs2_compression_mode;
+
+	switch (mode) {
+	case JFFS2_COMPR_MODE_NONE:
+		break;
+	case JFFS2_COMPR_MODE_PRIORITY:
+		ret = jffs2_selected_compress(0, data_in, cpage_out, datalen,
+				cdatalen);
+		break;
+	case JFFS2_COMPR_MODE_SIZE:
+	case JFFS2_COMPR_MODE_FAVOURLZO:
+		orig_slen = *datalen;
+		orig_dlen = *cdatalen;
+		spin_lock(&jffs2_compressor_list_lock);
+		list_for_each_entry(this, &jffs2_compressor_list, list) {
+			/* Skip decompress-only backwards-compatibility and disabled modules */
+			if ((!this->compress)||(this->disabled))
+				continue;
+			/* Allocating memory for output buffer if necessary */
+			if ((this->compr_buf_size < orig_slen) && (this->compr_buf)) {
+				spin_unlock(&jffs2_compressor_list_lock);
+				kfree(this->compr_buf);
+				spin_lock(&jffs2_compressor_list_lock);
+				this->compr_buf_size=0;
+				this->compr_buf=NULL;
+			}
+			if (!this->compr_buf) {
+				spin_unlock(&jffs2_compressor_list_lock);
+				tmp_buf = kmalloc(orig_slen, GFP_KERNEL);
+				spin_lock(&jffs2_compressor_list_lock);
+				if (!tmp_buf) {
+					pr_warn("No memory for compressor allocation. (%d bytes)\n",
+						orig_slen);
+					continue;
+				}
+				else {
+					this->compr_buf = tmp_buf;
+					this->compr_buf_size = orig_slen;
+				}
+			}
+			this->usecount++;
+			spin_unlock(&jffs2_compressor_list_lock);
+			*datalen  = orig_slen;
+			*cdatalen = orig_dlen;
+			compr_ret = this->compress(data_in, this->compr_buf, datalen, cdatalen);
+			spin_lock(&jffs2_compressor_list_lock);
+			this->usecount--;
+			if (!compr_ret) {
+				if (((!best_dlen) || jffs2_is_best_compression(this, best, *cdatalen, best_dlen))
+						&& (*cdatalen < *datalen)) {
+					best_dlen = *cdatalen;
+					best_slen = *datalen;
+					best = this;
+				}
+			}
+		}
+		if (best_dlen) {
+			*cdatalen = best_dlen;
+			*datalen  = best_slen;
+			output_buf = best->compr_buf;
+			best->compr_buf = NULL;
+			best->compr_buf_size = 0;
+			best->stat_compr_blocks++;
+			best->stat_compr_orig_size += best_slen;
+			best->stat_compr_new_size  += best_dlen;
+			ret = best->compr;
+			*cpage_out = output_buf;
+		}
+		spin_unlock(&jffs2_compressor_list_lock);
+		break;
+	case JFFS2_COMPR_MODE_FORCELZO:
+		ret = jffs2_selected_compress(JFFS2_COMPR_LZO, data_in,
+				cpage_out, datalen, cdatalen);
+		break;
+	case JFFS2_COMPR_MODE_FORCEZLIB:
+		ret = jffs2_selected_compress(JFFS2_COMPR_ZLIB, data_in,
+				cpage_out, datalen, cdatalen);
+		break;
+	default:
+		pr_err("unknown compression mode\n");
+	}
+
+	if (ret == JFFS2_COMPR_NONE) {
+		*cpage_out = data_in;
+		*datalen = *cdatalen;
+		none_stat_compr_blocks++;
+		none_stat_compr_size += *datalen;
+	}
+	return ret;
+}
+
+int jffs2_decompress(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
+		     uint16_t comprtype, unsigned char *cdata_in,
+		     unsigned char *data_out, uint32_t cdatalen, uint32_t datalen)
+{
+	struct jffs2_compressor *this;
+	int ret;
+
+	/* Older code had a bug where it would write non-zero 'usercompr'
+	   fields. Deal with it. */
+	if ((comprtype & 0xff) <= JFFS2_COMPR_ZLIB)
+		comprtype &= 0xff;
+
+	switch (comprtype & 0xff) {
+	case JFFS2_COMPR_NONE:
+		/* This should be special-cased elsewhere, but we might as well deal with it */
+		memcpy(data_out, cdata_in, datalen);
+		none_stat_decompr_blocks++;
+		break;
+	case JFFS2_COMPR_ZERO:
+		memset(data_out, 0, datalen);
+		break;
+	default:
+		spin_lock(&jffs2_compressor_list_lock);
+		list_for_each_entry(this, &jffs2_compressor_list, list) {
+			if (comprtype == this->compr) {
+				this->usecount++;
+				spin_unlock(&jffs2_compressor_list_lock);
+				ret = this->decompress(cdata_in, data_out, cdatalen, datalen);
+				spin_lock(&jffs2_compressor_list_lock);
+				if (ret) {
+					pr_warn("Decompressor \"%s\" returned %d\n",
+						this->name, ret);
+				}
+				else {
+					this->stat_decompr_blocks++;
+				}
+				this->usecount--;
+				spin_unlock(&jffs2_compressor_list_lock);
+				return ret;
+			}
+		}
+		pr_warn("compression type 0x%02x not available\n", comprtype);
+		spin_unlock(&jffs2_compressor_list_lock);
+		return -EIO;
+	}
+	return 0;
+}
+
+int jffs2_register_compressor(struct jffs2_compressor *comp)
+{
+	struct jffs2_compressor *this;
+
+	if (!comp->name) {
+		pr_warn("NULL compressor name at registering JFFS2 compressor. Failed.\n");
+		return -1;
+	}
+	comp->compr_buf_size=0;
+	comp->compr_buf=NULL;
+	comp->usecount=0;
+	comp->stat_compr_orig_size=0;
+	comp->stat_compr_new_size=0;
+	comp->stat_compr_blocks=0;
+	comp->stat_decompr_blocks=0;
+	jffs2_dbg(1, "Registering JFFS2 compressor \"%s\"\n", comp->name);
+
+	spin_lock(&jffs2_compressor_list_lock);
+
+	list_for_each_entry(this, &jffs2_compressor_list, list) {
+		if (this->priority < comp->priority) {
+			list_add(&comp->list, this->list.prev);
+			goto out;
+		}
+	}
+	list_add_tail(&comp->list, &jffs2_compressor_list);
+out:
+	D2(list_for_each_entry(this, &jffs2_compressor_list, list) {
+		printk(KERN_DEBUG "Compressor \"%s\", prio %d\n", this->name, this->priority);
+	})
+
+	spin_unlock(&jffs2_compressor_list_lock);
+
+	return 0;
+}
+
+int jffs2_unregister_compressor(struct jffs2_compressor *comp)
+{
+	D2(struct jffs2_compressor *this);
+
+	jffs2_dbg(1, "Unregistering JFFS2 compressor \"%s\"\n", comp->name);
+
+	spin_lock(&jffs2_compressor_list_lock);
+
+	if (comp->usecount) {
+		spin_unlock(&jffs2_compressor_list_lock);
+		pr_warn("Compressor module is in use. Unregister failed.\n");
+		return -1;
+	}
+	list_del(&comp->list);
+
+	D2(list_for_each_entry(this, &jffs2_compressor_list, list) {
+		printk(KERN_DEBUG "Compressor \"%s\", prio %d\n", this->name, this->priority);
+	})
+	spin_unlock(&jffs2_compressor_list_lock);
+	return 0;
+}
+
+void jffs2_free_comprbuf(unsigned char *comprbuf, unsigned char *orig)
+{
+	if (orig != comprbuf)
+		kfree(comprbuf);
+}
+
+int __init jffs2_compressors_init(void)
+{
+/* Registering compressors */
+#ifdef CONFIG_JFFS2_ZLIB
+	jffs2_zlib_init();
+#endif
+#ifdef CONFIG_JFFS2_RTIME
+	jffs2_rtime_init();
+#endif
+#ifdef CONFIG_JFFS2_RUBIN
+	jffs2_rubinmips_init();
+	jffs2_dynrubin_init();
+#endif
+#ifdef CONFIG_JFFS2_LZO
+	jffs2_lzo_init();
+#endif
+/* Setting default compression mode */
+#ifdef CONFIG_JFFS2_CMODE_NONE
+	jffs2_compression_mode = JFFS2_COMPR_MODE_NONE;
+	jffs2_dbg(1, "default compression mode: none\n");
+#else
+#ifdef CONFIG_JFFS2_CMODE_SIZE
+	jffs2_compression_mode = JFFS2_COMPR_MODE_SIZE;
+	jffs2_dbg(1, "default compression mode: size\n");
+#else
+#ifdef CONFIG_JFFS2_CMODE_FAVOURLZO
+	jffs2_compression_mode = JFFS2_COMPR_MODE_FAVOURLZO;
+	jffs2_dbg(1, "default compression mode: favourlzo\n");
+#else
+	jffs2_dbg(1, "default compression mode: priority\n");
+#endif
+#endif
+#endif
+	return 0;
+}
+
+int jffs2_compressors_exit(void)
+{
+/* Unregistering compressors */
+#ifdef CONFIG_JFFS2_LZO
+	jffs2_lzo_exit();
+#endif
+#ifdef CONFIG_JFFS2_RUBIN
+	jffs2_dynrubin_exit();
+	jffs2_rubinmips_exit();
+#endif
+#ifdef CONFIG_JFFS2_RTIME
+	jffs2_rtime_exit();
+#endif
+#ifdef CONFIG_JFFS2_ZLIB
+	jffs2_zlib_exit();
+#endif
+	return 0;
+}
Index: cpukit/libfs/src/jffs2/src/compr.h
===================================================================
--- cpukit/libfs/src/jffs2/src/compr.h	(revision 0c0f128e11007cb0d9ebed8b27dbb7cdde18450d)
+++ cpukit/libfs/src/jffs2/src/compr.h	(revision 0c0f128e11007cb0d9ebed8b27dbb7cdde18450d)
@@ -0,0 +1,105 @@
+/*
+ * JFFS2 -- Journalling Flash File System, Version 2.
+ *
+ * Copyright Â© 2004   Ferenc Havasi <havasi@inf.u-szeged.hu>,
+ *		      University of Szeged, Hungary
+ * Copyright Â© 2004-2010 David Woodhouse <dwmw2@infradead.org>
+ *
+ * For licensing information, see the file 'LICENCE' in this directory.
+ *
+ */
+
+#ifndef __JFFS2_COMPR_H__
+#define __JFFS2_COMPR_H__
+
+#include <linux/kernel.h>
+#include <linux/vmalloc.h>
+#include <linux/list.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/jffs2.h>
+#include "jffs2_fs_i.h"
+#include "jffs2_fs_sb.h"
+#include "nodelist.h"
+
+#define JFFS2_RUBINMIPS_PRIORITY 10
+#define JFFS2_DYNRUBIN_PRIORITY  20
+#define JFFS2_LZARI_PRIORITY     30
+#define JFFS2_RTIME_PRIORITY     50
+#define JFFS2_ZLIB_PRIORITY      60
+#define JFFS2_LZO_PRIORITY       80
+
+
+#define JFFS2_RUBINMIPS_DISABLED /* RUBINs will be used only */
+#define JFFS2_DYNRUBIN_DISABLED  /*	   for decompression */
+
+#define JFFS2_COMPR_MODE_NONE       0
+#define JFFS2_COMPR_MODE_PRIORITY   1
+#define JFFS2_COMPR_MODE_SIZE       2
+#define JFFS2_COMPR_MODE_FAVOURLZO  3
+#define JFFS2_COMPR_MODE_FORCELZO   4
+#define JFFS2_COMPR_MODE_FORCEZLIB  5
+
+#define FAVOUR_LZO_PERCENT 80
+
+struct jffs2_compressor {
+	struct list_head list;
+	int priority;			/* used by prirority comr. mode */
+	char *name;
+	char compr;			/* JFFS2_COMPR_XXX */
+	int (*compress)(unsigned char *data_in, unsigned char *cpage_out,
+			uint32_t *srclen, uint32_t *destlen);
+	int (*decompress)(unsigned char *cdata_in, unsigned char *data_out,
+			  uint32_t cdatalen, uint32_t datalen);
+	int usecount;
+	int disabled;		/* if set the compressor won't compress */
+	unsigned char *compr_buf;	/* used by size compr. mode */
+	uint32_t compr_buf_size;	/* used by size compr. mode */
+	uint32_t stat_compr_orig_size;
+	uint32_t stat_compr_new_size;
+	uint32_t stat_compr_blocks;
+	uint32_t stat_decompr_blocks;
+};
+
+int jffs2_register_compressor(struct jffs2_compressor *comp);
+int jffs2_unregister_compressor(struct jffs2_compressor *comp);
+
+int jffs2_compressors_init(void);
+int jffs2_compressors_exit(void);
+
+uint16_t jffs2_compress(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
+			unsigned char *data_in, unsigned char **cpage_out,
+			uint32_t *datalen, uint32_t *cdatalen);
+
+int jffs2_decompress(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
+		     uint16_t comprtype, unsigned char *cdata_in,
+		     unsigned char *data_out, uint32_t cdatalen, uint32_t datalen);
+
+void jffs2_free_comprbuf(unsigned char *comprbuf, unsigned char *orig);
+
+/* Compressor modules */
+/* These functions will be called by jffs2_compressors_init/exit */
+
+#ifdef CONFIG_JFFS2_RUBIN
+int jffs2_rubinmips_init(void);
+void jffs2_rubinmips_exit(void);
+int jffs2_dynrubin_init(void);
+void jffs2_dynrubin_exit(void);
+#endif
+#ifdef CONFIG_JFFS2_RTIME
+int jffs2_rtime_init(void);
+void jffs2_rtime_exit(void);
+#endif
+#ifdef CONFIG_JFFS2_ZLIB
+int jffs2_zlib_init(void);
+void jffs2_zlib_exit(void);
+#endif
+#ifdef CONFIG_JFFS2_LZO
+int jffs2_lzo_init(void);
+void jffs2_lzo_exit(void);
+#endif
+
+#endif /* __JFFS2_COMPR_H__ */
Index: cpukit/libfs/src/jffs2/src/compr_rtime.c
===================================================================
--- cpukit/libfs/src/jffs2/src/compr_rtime.c	(revision 0c0f128e11007cb0d9ebed8b27dbb7cdde18450d)
+++ cpukit/libfs/src/jffs2/src/compr_rtime.c	(revision 0c0f128e11007cb0d9ebed8b27dbb7cdde18450d)
@@ -0,0 +1,130 @@
+/*
+ * JFFS2 -- Journalling Flash File System, Version 2.
+ *
+ * Copyright Â© 2001-2007 Red Hat, Inc.
+ * Copyright Â© 2004-2010 David Woodhouse <dwmw2@infradead.org>
+ *
+ * Created by Arjan van de Ven <arjanv@redhat.com>
+ *
+ * For licensing information, see the file 'LICENCE' in this directory.
+ *
+ *
+ *
+ * Very simple lz77-ish encoder.
+ *
+ * Theory of operation: Both encoder and decoder have a list of "last
+ * occurrences" for every possible source-value; after sending the
+ * first source-byte, the second byte indicated the "run" length of
+ * matches
+ *
+ * The algorithm is intended to only send "whole bytes", no bit-messing.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/jffs2.h>
+#include "compr.h"
+
+/* _compress returns the compressed size, -1 if bigger */
+static int jffs2_rtime_compress(unsigned char *data_in,
+				unsigned char *cpage_out,
+				uint32_t *sourcelen, uint32_t *dstlen)
+{
+	short positions[256];
+	int outpos = 0;
+	int pos=0;
+
+	memset(positions,0,sizeof(positions));
+
+	while (pos < (*sourcelen) && outpos <= (*dstlen)-2) {
+		int backpos, runlen=0;
+		unsigned char value;
+
+		value = data_in[pos];
+
+		cpage_out[outpos++] = data_in[pos++];
+
+		backpos = positions[value];
+		positions[value]=pos;
+
+		while ((backpos < pos) && (pos < (*sourcelen)) &&
+		       (data_in[pos]==data_in[backpos++]) && (runlen<255)) {
+			pos++;
+			runlen++;
+		}
+		cpage_out[outpos++] = runlen;
+	}
+
+	if (outpos >= pos) {
+		/* We failed */
+		return -1;
+	}
+
+	/* Tell the caller how much we managed to compress, and how much space it took */
+	*sourcelen = pos;
+	*dstlen = outpos;
+	return 0;
+}
+
+
+static int jffs2_rtime_decompress(unsigned char *data_in,
+				  unsigned char *cpage_out,
+				  uint32_t srclen, uint32_t destlen)
+{
+	short positions[256];
+	int outpos = 0;
+	int pos=0;
+
+	memset(positions,0,sizeof(positions));
+
+	while (outpos<destlen) {
+		unsigned char value;
+		int backoffs;
+		int repeat;
+
+		value = data_in[pos++];
+		cpage_out[outpos++] = value; /* first the verbatim copied byte */
+		repeat = data_in[pos++];
+		backoffs = positions[value];
+
+		positions[value]=outpos;
+		if (repeat) {
+			if (backoffs + repeat >= outpos) {
+				while(repeat) {
+					cpage_out[outpos++] = cpage_out[backoffs++];
+					repeat--;
+				}
+			} else {
+				memcpy(&cpage_out[outpos],&cpage_out[backoffs],repeat);
+				outpos+=repeat;
+			}
+		}
+	}
+	return 0;
+}
+
+static struct jffs2_compressor jffs2_rtime_comp = {
+    .priority = JFFS2_RTIME_PRIORITY,
+    .name = "rtime",
+    .compr = JFFS2_COMPR_RTIME,
+    .compress = &jffs2_rtime_compress,
+    .decompress = &jffs2_rtime_decompress,
+#ifdef JFFS2_RTIME_DISABLED
+    .disabled = 1,
+#else
+    .disabled = 0,
+#endif
+};
+
+int jffs2_rtime_init(void)
+{
+    return jffs2_register_compressor(&jffs2_rtime_comp);
+}
+
+void jffs2_rtime_exit(void)
+{
+    jffs2_unregister_compressor(&jffs2_rtime_comp);
+}
Index: cpukit/libfs/src/jffs2/src/compr_rubin.c
===================================================================
--- cpukit/libfs/src/jffs2/src/compr_rubin.c	(revision 0c0f128e11007cb0d9ebed8b27dbb7cdde18450d)
+++ cpukit/libfs/src/jffs2/src/compr_rubin.c	(revision 0c0f128e11007cb0d9ebed8b27dbb7cdde18450d)
@@ -0,0 +1,457 @@
+/*
+ * JFFS2 -- Journalling Flash File System, Version 2.
+ *
+ * Copyright Â© 2001-2007 Red Hat, Inc.
+ * Copyright Â© 2004-2010 David Woodhouse <dwmw2@infradead.org>
+ *
+ * Created by Arjan van de Ven <arjanv@redhat.com>
+ *
+ * For licensing information, see the file 'LICENCE' in this directory.
+ *
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/string.h>
+#include <linux/types.h>
+#include <linux/jffs2.h>
+#include <linux/errno.h>
+#include "compr.h"
+
+
+#define RUBIN_REG_SIZE   16
+#define UPPER_BIT_RUBIN    (((long) 1)<<(RUBIN_REG_SIZE-1))
+#define LOWER_BITS_RUBIN   ((((long) 1)<<(RUBIN_REG_SIZE-1))-1)
+
+
+#define BIT_DIVIDER_MIPS 1043
+static int bits_mips[8] = { 277, 249, 290, 267, 229, 341, 212, 241};
+
+struct pushpull {
+	unsigned char *buf;
+	unsigned int buflen;
+	unsigned int ofs;
+	unsigned int reserve;
+};
+
+struct rubin_state {
+	unsigned long p;
+	unsigned long q;
+	unsigned long rec_q;
+	long bit_number;
+	struct pushpull pp;
+	int bit_divider;
+	int bits[8];
+};
+
+static inline void init_pushpull(struct pushpull *pp, char *buf,
+				 unsigned buflen, unsigned ofs,
+				 unsigned reserve)
+{
+	pp->buf = buf;
+	pp->buflen = buflen;
+	pp->ofs = ofs;
+	pp->reserve = reserve;
+}
+
+static inline int pushbit(struct pushpull *pp, int bit, int use_reserved)
+{
+	if (pp->ofs >= pp->buflen - (use_reserved?0:pp->reserve))
+		return -ENOSPC;
+
+	if (bit)
+		pp->buf[pp->ofs >> 3] |= (1<<(7-(pp->ofs & 7)));
+	else
+		pp->buf[pp->ofs >> 3] &= ~(1<<(7-(pp->ofs & 7)));
+
+	pp->ofs++;
+
+	return 0;
+}
+
+static inline int pushedbits(struct pushpull *pp)
+{
+	return pp->ofs;
+}
+
+static inline int pullbit(struct pushpull *pp)
+{
+	int bit;
+
+	bit = (pp->buf[pp->ofs >> 3] >> (7-(pp->ofs & 7))) & 1;
+
+	pp->ofs++;
+	return bit;
+}
+
+static inline int pulledbits(struct pushpull *pp)
+{
+	return pp->ofs;
+}
+
+
+static void init_rubin(struct rubin_state *rs, int div, int *bits)
+{
+	int c;
+
+	rs->q = 0;
+	rs->p = (long) (2 * UPPER_BIT_RUBIN);
+	rs->bit_number = (long) 0;
+	rs->bit_divider = div;
+
+	for (c=0; c<8; c++)
+		rs->bits[c] = bits[c];
+}
+
+
+static int encode(struct rubin_state *rs, long A, long B, int symbol)
+{
+
+	long i0, i1;
+	int ret;
+
+	while ((rs->q >= UPPER_BIT_RUBIN) ||
+	       ((rs->p + rs->q) <= UPPER_BIT_RUBIN)) {
+		rs->bit_number++;
+
+		ret = pushbit(&rs->pp, (rs->q & UPPER_BIT_RUBIN) ? 1 : 0, 0);
+		if (ret)
+			return ret;
+		rs->q &= LOWER_BITS_RUBIN;
+		rs->q <<= 1;
+		rs->p <<= 1;
+	}
+	i0 = A * rs->p / (A + B);
+	if (i0 <= 0)
+		i0 = 1;
+
+	if (i0 >= rs->p)
+		i0 = rs->p - 1;
+
+	i1 = rs->p - i0;
+
+	if (symbol == 0)
+		rs->p = i0;
+	else {
+		rs->p = i1;
+		rs->q += i0;
+	}
+	return 0;
+}
+
+
+static void end_rubin(struct rubin_state *rs)
+{
+
+	int i;
+
+	for (i = 0; i < RUBIN_REG_SIZE; i++) {
+		pushbit(&rs->pp, (UPPER_BIT_RUBIN & rs->q) ? 1 : 0, 1);
+		rs->q &= LOWER_BITS_RUBIN;
+		rs->q <<= 1;
+	}
+}
+
+
+static void init_decode(struct rubin_state *rs, int div, int *bits)
+{
+	init_rubin(rs, div, bits);
+
+	/* behalve lower */
+	rs->rec_q = 0;
+
+	for (rs->bit_number = 0; rs->bit_number++ < RUBIN_REG_SIZE;
+	     rs->rec_q = rs->rec_q * 2 + (long) (pullbit(&rs->pp)))
+		;
+}
+
+static void __do_decode(struct rubin_state *rs, unsigned long p,
+			unsigned long q)
+{
+	register unsigned long lower_bits_rubin = LOWER_BITS_RUBIN;
+	unsigned long rec_q;
+	int c, bits = 0;
+
+	/*
+	 * First, work out how many bits we need from the input stream.
+	 * Note that we have already done the initial check on this
+	 * loop prior to calling this function.
+	 */
+	do {
+		bits++;
+		q &= lower_bits_rubin;
+		q <<= 1;
+		p <<= 1;
+	} while ((q >= UPPER_BIT_RUBIN) || ((p + q) <= UPPER_BIT_RUBIN));
+
+	rs->p = p;
+	rs->q = q;
+
+	rs->bit_number += bits;
+
+	/*
+	 * Now get the bits.  We really want this to be "get n bits".
+	 */
+	rec_q = rs->rec_q;
+	do {
+		c = pullbit(&rs->pp);
+		rec_q &= lower_bits_rubin;
+		rec_q <<= 1;
+		rec_q += c;
+	} while (--bits);
+	rs->rec_q = rec_q;
+}
+
+static int decode(struct rubin_state *rs, long A, long B)
+{
+	unsigned long p = rs->p, q = rs->q;
+	long i0, threshold;
+	int symbol;
+
+	if (q >= UPPER_BIT_RUBIN || ((p + q) <= UPPER_BIT_RUBIN))
+		__do_decode(rs, p, q);
+
+	i0 = A * rs->p / (A + B);
+	if (i0 <= 0)
+		i0 = 1;
+
+	if (i0 >= rs->p)
+		i0 = rs->p - 1;
+
+	threshold = rs->q + i0;
+	symbol = rs->rec_q >= threshold;
+	if (rs->rec_q >= threshold) {
+		rs->q += i0;
+		i0 = rs->p - i0;
+	}
+
+	rs->p = i0;
+
+	return symbol;
+}
+
+
+
+static int out_byte(struct rubin_state *rs, unsigned char byte)
+{
+	int i, ret;
+	struct rubin_state rs_copy;
+	rs_copy = *rs;
+
+	for (i=0; i<8; i++) {
+		ret = encode(rs, rs->bit_divider-rs->bits[i],
+			     rs->bits[i], byte & 1);
+		if (ret) {
+			/* Failed. Restore old state */
+			*rs = rs_copy;
+			return ret;
+		}
+		byte >>= 1 ;
+	}
+	return 0;
+}
+
+static int in_byte(struct rubin_state *rs)
+{
+	int i, result = 0, bit_divider = rs->bit_divider;
+
+	for (i = 0; i < 8; i++)
+		result |= decode(rs, bit_divider - rs->bits[i],
+				 rs->bits[i]) << i;
+
+	return result;
+}
+
+
+
+static int rubin_do_compress(int bit_divider, int *bits, unsigned char *data_in,
+			     unsigned char *cpage_out, uint32_t *sourcelen,
+			     uint32_t *dstlen)
+	{
+	int outpos = 0;
+	int pos=0;
+	struct rubin_state rs;
+
+	init_pushpull(&rs.pp, cpage_out, *dstlen * 8, 0, 32);
+
+	init_rubin(&rs, bit_divider, bits);
+
+	while (pos < (*sourcelen) && !out_byte(&rs, data_in[pos]))
+		pos++;
+
+	end_rubin(&rs);
+
+	if (outpos > pos) {
+		/* We failed */
+		return -1;
+	}
+
+	/* Tell the caller how much we managed to compress,
+	 * and how much space it took */
+
+	outpos = (pushedbits(&rs.pp)+7)/8;
+
+	if (outpos >= pos)
+		return -1; /* We didn't actually compress */
+	*sourcelen = pos;
+	*dstlen = outpos;
+	return 0;
+}
+#if 0
+/* _compress returns the compressed size, -1 if bigger */
+int jffs2_rubinmips_compress(unsigned char *data_in, unsigned char *cpage_out,
+		   uint32_t *sourcelen, uint32_t *dstlen)
+{
+	return rubin_do_compress(BIT_DIVIDER_MIPS, bits_mips, data_in,
+				 cpage_out, sourcelen, dstlen);
+}
+#endif
+static int jffs2_dynrubin_compress(unsigned char *data_in,
+				   unsigned char *cpage_out,
+				   uint32_t *sourcelen, uint32_t *dstlen)
+{
+	int bits[8];
+	unsigned char histo[256];
+	int i;
+	int ret;
+	uint32_t mysrclen, mydstlen;
+
+	mysrclen = *sourcelen;
+	mydstlen = *dstlen - 8;
+
+	if (*dstlen <= 12)
+		return -1;
+
+	memset(histo, 0, 256);
+	for (i=0; i<mysrclen; i++)
+		histo[data_in[i]]++;
+	memset(bits, 0, sizeof(int)*8);
+	for (i=0; i<256; i++) {
+		if (i&128)
+			bits[7] += histo[i];
+		if (i&64)
+			bits[6] += histo[i];
+		if (i&32)
+			bits[5] += histo[i];
+		if (i&16)
+			bits[4] += histo[i];
+		if (i&8)
+			bits[3] += histo[i];
+		if (i&4)
+			bits[2] += histo[i];
+		if (i&2)
+			bits[1] += histo[i];
+		if (i&1)
+			bits[0] += histo[i];
+	}
+
+	for (i=0; i<8; i++) {
+		bits[i] = (bits[i] * 256) / mysrclen;
+		if (!bits[i]) bits[i] = 1;
+		if (bits[i] > 255) bits[i] = 255;
+		cpage_out[i] = bits[i];
+	}
+
+	ret = rubin_do_compress(256, bits, data_in, cpage_out+8, &mysrclen,
+				&mydstlen);
+	if (ret)
+		return ret;
+
+	/* Add back the 8 bytes we took for the probabilities */
+	mydstlen += 8;
+
+	if (mysrclen <= mydstlen) {
+		/* We compressed */
+		return -1;
+	}
+
+	*sourcelen = mysrclen;
+	*dstlen = mydstlen;
+	return 0;
+}
+
+static void rubin_do_decompress(int bit_divider, int *bits,
+				unsigned char *cdata_in, 
+				unsigned char *page_out, uint32_t srclen,
+				uint32_t destlen)
+{
+	int outpos = 0;
+	struct rubin_state rs;
+
+	init_pushpull(&rs.pp, cdata_in, srclen, 0, 0);
+	init_decode(&rs, bit_divider, bits);
+
+	while (outpos < destlen)
+		page_out[outpos++] = in_byte(&rs);
+}
+
+
+static int jffs2_rubinmips_decompress(unsigned char *data_in,
+				      unsigned char *cpage_out,
+				      uint32_t sourcelen, uint32_t dstlen)
+{
+	rubin_do_decompress(BIT_DIVIDER_MIPS, bits_mips, data_in,
+			    cpage_out, sourcelen, dstlen);
+	return 0;
+}
+
+static int jffs2_dynrubin_decompress(unsigned char *data_in,
+				     unsigned char *cpage_out,
+				     uint32_t sourcelen, uint32_t dstlen)
+{
+	int bits[8];
+	int c;
+
+	for (c=0; c<8; c++)
+		bits[c] = data_in[c];
+
+	rubin_do_decompress(256, bits, data_in+8, cpage_out, sourcelen-8,
+			    dstlen);
+	return 0;
+}
+
+static struct jffs2_compressor jffs2_rubinmips_comp = {
+	.priority = JFFS2_RUBINMIPS_PRIORITY,
+	.name = "rubinmips",
+	.compr = JFFS2_COMPR_DYNRUBIN,
+	.compress = NULL, /*&jffs2_rubinmips_compress,*/
+	.decompress = &jffs2_rubinmips_decompress,
+#ifdef JFFS2_RUBINMIPS_DISABLED
+	.disabled = 1,
+#else
+	.disabled = 0,
+#endif
+};
+
+int jffs2_rubinmips_init(void)
+{
+	return jffs2_register_compressor(&jffs2_rubinmips_comp);
+}
+
+void jffs2_rubinmips_exit(void)
+{
+	jffs2_unregister_compressor(&jffs2_rubinmips_comp);
+}
+
+static struct jffs2_compressor jffs2_dynrubin_comp = {
+	.priority = JFFS2_DYNRUBIN_PRIORITY,
+	.name = "dynrubin",
+	.compr = JFFS2_COMPR_RUBINMIPS,
+	.compress = jffs2_dynrubin_compress,
+	.decompress = &jffs2_dynrubin_decompress,
+#ifdef JFFS2_DYNRUBIN_DISABLED
+	.disabled = 1,
+#else
+	.disabled = 0,
+#endif
+};
+
+int jffs2_dynrubin_init(void)
+{
+	return jffs2_register_compressor(&jffs2_dynrubin_comp);
+}
+
+void jffs2_dynrubin_exit(void)
+{
+	jffs2_unregister_compressor(&jffs2_dynrubin_comp);
+}
Index: cpukit/libfs/src/jffs2/src/compr_zlib.c
===================================================================
--- cpukit/libfs/src/jffs2/src/compr_zlib.c	(revision 0c0f128e11007cb0d9ebed8b27dbb7cdde18450d)
+++ cpukit/libfs/src/jffs2/src/compr_zlib.c	(revision 0c0f128e11007cb0d9ebed8b27dbb7cdde18450d)
@@ -0,0 +1,221 @@
+/*
+ * JFFS2 -- Journalling Flash File System, Version 2.
+ *
+ * Copyright Â© 2001-2007 Red Hat, Inc.
+ * Copyright Â© 2004-2010 David Woodhouse <dwmw2@infradead.org>
+ *
+ * Created by David Woodhouse <dwmw2@infradead.org>
+ *
+ * For licensing information, see the file 'LICENCE' in this directory.
+ *
+ */
+
+#if !defined(__KERNEL__) && !defined(__ECOS)
+#error "The userspace support got too messy and was removed. Update your mkfs.jffs2"
+#endif
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/kernel.h>
+#include <linux/zlib.h>
+#include <linux/zutil.h>
+#include "nodelist.h"
+#include "compr.h"
+
+	/* 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
+		avail_in == 0 and avail_out set to the remaining 12
+		bytes for it to clean up.
+	   Q: Is 12 bytes sufficient?
+	*/
+#define STREAM_END_SPACE 12
+
+static DEFINE_MUTEX(deflate_mutex);
+static DEFINE_MUTEX(inflate_mutex);
+static z_stream inf_strm, def_strm;
+
+#ifdef __KERNEL__ /* Linux-only */
+#include <linux/vmalloc.h>
+#include <linux/init.h>
+#include <linux/mutex.h>
+
+static int __init alloc_workspaces(void)
+{
+	def_strm.workspace = vmalloc(zlib_deflate_workspacesize(MAX_WBITS,
+							MAX_MEM_LEVEL));
+	if (!def_strm.workspace)
+		return -ENOMEM;
+
+	jffs2_dbg(1, "Allocated %d bytes for deflate workspace\n",
+		  zlib_deflate_workspacesize(MAX_WBITS, MAX_MEM_LEVEL));
+	inf_strm.workspace = vmalloc(zlib_inflate_workspacesize());
+	if (!inf_strm.workspace) {
+		vfree(def_strm.workspace);
+		return -ENOMEM;
+	}
+	jffs2_dbg(1, "Allocated %d bytes for inflate workspace\n",
+		  zlib_inflate_workspacesize());
+	return 0;
+}
+
+static void free_workspaces(void)
+{
+	vfree(def_strm.workspace);
+	vfree(inf_strm.workspace);
+}
+#else
+#define alloc_workspaces() (0)
+#define free_workspaces() do { } while(0)
+#endif /* __KERNEL__ */
+
+static int jffs2_zlib_compress(unsigned char *data_in,
+			       unsigned char *cpage_out,
+			       uint32_t *sourcelen, uint32_t *dstlen)
+{
+	int ret;
+
+	if (*dstlen <= STREAM_END_SPACE)
+		return -1;
+
+	mutex_lock(&deflate_mutex);
+
+	if (Z_OK != zlib_deflateInit(&def_strm, 3)) {
+		pr_warn("deflateInit failed\n");
+		mutex_unlock(&deflate_mutex);
+		return -1;
+	}
+
+	def_strm.next_in = data_in;
+	def_strm.total_in = 0;
+
+	def_strm.next_out = cpage_out;
+	def_strm.total_out = 0;
+
+	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);
+		jffs2_dbg(1, "calling deflate with avail_in %d, avail_out %d\n",
+			  def_strm.avail_in, def_strm.avail_out);
+		ret = zlib_deflate(&def_strm, Z_PARTIAL_FLUSH);
+		jffs2_dbg(1, "deflate returned with avail_in %d, avail_out %d, total_in %ld, total_out %ld\n",
+			  def_strm.avail_in, def_strm.avail_out,
+			  def_strm.total_in, def_strm.total_out);
+		if (ret != Z_OK) {
+			jffs2_dbg(1, "deflate in loop returned %d\n", ret);
+			zlib_deflateEnd(&def_strm);
+			mutex_unlock(&deflate_mutex);
+			return -1;
+		}
+	}
+	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) {
+		jffs2_dbg(1, "final deflate returned %d\n", ret);
+		ret = -1;
+		goto out;
+	}
+
+	if (def_strm.total_out >= def_strm.total_in) {
+		jffs2_dbg(1, "zlib compressed %ld bytes into %ld; failing\n",
+			  def_strm.total_in, def_strm.total_out);
+		ret = -1;
+		goto out;
+	}
+
+	jffs2_dbg(1, "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:
+	mutex_unlock(&deflate_mutex);
+	return ret;
+}
+
+static int jffs2_zlib_decompress(unsigned char *data_in,
+				 unsigned char *cpage_out,
+				 uint32_t srclen, uint32_t destlen)
+{
+	int ret;
+	int wbits = MAX_WBITS;
+
+	mutex_lock(&inflate_mutex);
+
+	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;
+
+	/* 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)) {
+
+		jffs2_dbg(2, "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 */
+		jffs2_dbg(1, "inflate not skipping adler32\n");
+	}
+
+
+	if (Z_OK != zlib_inflateInit2(&inf_strm, wbits)) {
+		pr_warn("inflateInit failed\n");
+		mutex_unlock(&inflate_mutex);
+		return 1;
+	}
+
+	while((ret = zlib_inflate(&inf_strm, Z_FINISH)) == Z_OK)
+		;
+	if (ret != Z_STREAM_END) {
+		pr_notice("inflate returned %d\n", ret);
+	}
+	zlib_inflateEnd(&inf_strm);
+	mutex_unlock(&inflate_mutex);
+	return 0;
+}
+
+static struct jffs2_compressor jffs2_zlib_comp = {
+    .priority = JFFS2_ZLIB_PRIORITY,
+    .name = "zlib",
+    .compr = JFFS2_COMPR_ZLIB,
+    .compress = &jffs2_zlib_compress,
+    .decompress = &jffs2_zlib_decompress,
+#ifdef JFFS2_ZLIB_DISABLED
+    .disabled = 1,
+#else
+    .disabled = 0,
+#endif
+};
+
+int __init jffs2_zlib_init(void)
+{
+    int ret;
+
+    ret = alloc_workspaces();
+    if (ret)
+	    return ret;
+
+    ret = jffs2_register_compressor(&jffs2_zlib_comp);
+    if (ret)
+	    free_workspaces();
+
+    return ret;
+}
+
+void jffs2_zlib_exit(void)
+{
+    jffs2_unregister_compressor(&jffs2_zlib_comp);
+    free_workspaces();
+}
Index: cpukit/libfs/src/jffs2/src/debug.c
===================================================================
--- cpukit/libfs/src/jffs2/src/debug.c	(revision 0c0f128e11007cb0d9ebed8b27dbb7cdde18450d)
+++ cpukit/libfs/src/jffs2/src/debug.c	(revision 0c0f128e11007cb0d9ebed8b27dbb7cdde18450d)
@@ -0,0 +1,866 @@
+/*
+ * JFFS2 -- Journalling Flash File System, Version 2.
+ *
+ * Copyright Â© 2001-2007 Red Hat, Inc.
+ * Copyright Â© 2004-2010 David Woodhouse <dwmw2@infradead.org>
+ *
+ * Created by David Woodhouse <dwmw2@infradead.org>
+ *
+ * For licensing information, see the file 'LICENCE' in this directory.
+ *
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/pagemap.h>
+#include <linux/crc32.h>
+#include <linux/jffs2.h>
+#include <linux/mtd/mtd.h>
+#include <linux/slab.h>
+#include "nodelist.h"
+#include "debug.h"
+
+#ifdef JFFS2_DBG_SANITY_CHECKS
+
+void
+__jffs2_dbg_acct_sanity_check_nolock(struct jffs2_sb_info *c,
+				     struct jffs2_eraseblock *jeb)
+{
+	if (unlikely(jeb && jeb->used_size + jeb->dirty_size +
+			jeb->free_size + jeb->wasted_size +
+			jeb->unchecked_size != c->sector_size)) {
+		JFFS2_ERROR("eeep, space accounting for block at 0x%08x is screwed.\n", jeb->offset);
+		JFFS2_ERROR("free %#08x + dirty %#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 (unlikely(c->used_size + c->dirty_size + c->free_size + c->erasing_size + c->bad_size
+				+ c->wasted_size + c->unchecked_size != c->flash_size)) {
+		JFFS2_ERROR("eeep, space accounting superblock info is screwed.\n");
+		JFFS2_ERROR("free %#08x + dirty %#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();
+	}
+}
+
+void
+__jffs2_dbg_acct_sanity_check(struct jffs2_sb_info *c,
+			      struct jffs2_eraseblock *jeb)
+{
+	spin_lock(&c->erase_completion_lock);
+	jffs2_dbg_acct_sanity_check_nolock(c, jeb);
+	spin_unlock(&c->erase_completion_lock);
+}
+
+#endif /* JFFS2_DBG_SANITY_CHECKS */
+
+#ifdef JFFS2_DBG_PARANOIA_CHECKS
+/*
+ * Check the fragtree.
+ */
+void
+__jffs2_dbg_fragtree_paranoia_check(struct jffs2_inode_info *f)
+{
+	mutex_lock(&f->sem);
+	__jffs2_dbg_fragtree_paranoia_check_nolock(f);
+	mutex_unlock(&f->sem);
+}
+
+void
+__jffs2_dbg_fragtree_paranoia_check_nolock(struct jffs2_inode_info *f)
+{
+	struct jffs2_node_frag *frag;
+	int bitched = 0;
+
+	for (frag = frag_first(&f->fragtree); frag; frag = frag_next(frag)) {
+		struct jffs2_full_dnode *fn = frag->node;
+
+		if (!fn || !fn->raw)
+			continue;
+
+		if (ref_flags(fn->raw) == REF_PRISTINE) {
+			if (fn->frags > 1) {
+				JFFS2_ERROR("REF_PRISTINE node at 0x%08x had %d frags. Tell dwmw2.\n",
+					ref_offset(fn->raw), fn->frags);
+				bitched = 1;
+			}
+
+			/* 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 && frag_prev(frag)->node) {
+				JFFS2_ERROR("REF_PRISTINE node at 0x%08x had a previous non-hole frag in the same page. Tell dwmw2.\n",
+					ref_offset(fn->raw));
+				bitched = 1;
+			}
+
+			if ((frag->ofs+frag->size) & (PAGE_CACHE_SIZE-1) && frag_next(frag)
+					&& frag_next(frag)->size < PAGE_CACHE_SIZE && frag_next(frag)->node) {
+				JFFS2_ERROR("REF_PRISTINE node at 0x%08x (%08x-%08x) had a following non-hole frag in the same page. Tell dwmw2.\n",
+				       ref_offset(fn->raw), frag->ofs, frag->ofs+frag->size);
+				bitched = 1;
+			}
+		}
+	}
+
+	if (bitched) {
+		JFFS2_ERROR("fragtree is corrupted.\n");
+		__jffs2_dbg_dump_fragtree_nolock(f);
+		BUG();
+	}
+}
+
+/*
+ * Check if the flash contains all 0xFF before we start writing.
+ */
+void
+__jffs2_dbg_prewrite_paranoia_check(struct jffs2_sb_info *c,
+				    uint32_t ofs, int len)
+{
+	size_t retlen;
+	int ret, i;
+	unsigned char *buf;
+
+	buf = kmalloc(len, GFP_KERNEL);
+	if (!buf)
+		return;
+
+	ret = jffs2_flash_read(c, ofs, len, &retlen, buf);
+	if (ret || (retlen != len)) {
+		JFFS2_WARNING("read %d bytes failed or short. ret %d, retlen %zd.\n",
+				len, ret, retlen);
+		kfree(buf);
+		return;
+	}
+
+	ret = 0;
+	for (i = 0; i < len; i++)
+		if (buf[i] != 0xff)
+			ret = 1;
+
+	if (ret) {
+		JFFS2_ERROR("argh, about to write node to %#08x on flash, but there are data already there. The first corrupted byte is at %#08x offset.\n",
+			ofs, ofs + i);
+		__jffs2_dbg_dump_buffer(buf, len, ofs);
+		kfree(buf);
+		BUG();
+	}
+
+	kfree(buf);
+}
+
+void __jffs2_dbg_superblock_counts(struct jffs2_sb_info *c)
+{
+	struct jffs2_eraseblock *jeb;
+	uint32_t free = 0, dirty = 0, used = 0, wasted = 0,
+		erasing = 0, bad = 0, unchecked = 0;
+	int nr_counted = 0;
+	int dump = 0;
+
+	if (c->gcblock) {
+		nr_counted++;
+		free += c->gcblock->free_size;
+		dirty += c->gcblock->dirty_size;
+		used += c->gcblock->used_size;
+		wasted += c->gcblock->wasted_size;
+		unchecked += c->gcblock->unchecked_size;
+	}
+	if (c->nextblock) {
+		nr_counted++;
+		free += c->nextblock->free_size;
+		dirty += c->nextblock->dirty_size;
+		used += c->nextblock->used_size;
+		wasted += c->nextblock->wasted_size;
+		unchecked += c->nextblock->unchecked_size;
+	}
+	list_for_each_entry(jeb, &c->clean_list, list) {
+		nr_counted++;
+		free += jeb->free_size;
+		dirty += jeb->dirty_size;
+		used += jeb->used_size;
+		wasted += jeb->wasted_size;
+		unchecked += jeb->unchecked_size;
+	}
+	list_for_each_entry(jeb, &c->very_dirty_list, list) {
+		nr_counted++;
+		free += jeb->free_size;
+		dirty += jeb->dirty_size;
+		used += jeb->used_size;
+		wasted += jeb->wasted_size;
+		unchecked += jeb->unchecked_size;
+	}
+	list_for_each_entry(jeb, &c->dirty_list, list) {
+		nr_counted++;
+		free += jeb->free_size;
+		dirty += jeb->dirty_size;
+		used += jeb->used_size;
+		wasted += jeb->wasted_size;
+		unchecked += jeb->unchecked_size;
+	}
+	list_for_each_entry(jeb, &c->erasable_list, list) {
+		nr_counted++;
+		free += jeb->free_size;
+		dirty += jeb->dirty_size;
+		used += jeb->used_size;
+		wasted += jeb->wasted_size;
+		unchecked += jeb->unchecked_size;
+	}
+	list_for_each_entry(jeb, &c->erasable_pending_wbuf_list, list) {
+		nr_counted++;
+		free += jeb->free_size;
+		dirty += jeb->dirty_size;
+		used += jeb->used_size;
+		wasted += jeb->wasted_size;
+		unchecked += jeb->unchecked_size;
+	}
+	list_for_each_entry(jeb, &c->erase_pending_list, list) {
+		nr_counted++;
+		free += jeb->free_size;
+		dirty += jeb->dirty_size;
+		used += jeb->used_size;
+		wasted += jeb->wasted_size;
+		unchecked += jeb->unchecked_size;
+	}
+	list_for_each_entry(jeb, &c->free_list, list) {
+		nr_counted++;
+		free += jeb->free_size;
+		dirty += jeb->dirty_size;
+		used += jeb->used_size;
+		wasted += jeb->wasted_size;
+		unchecked += jeb->unchecked_size;
+	}
+	list_for_each_entry(jeb, &c->bad_used_list, list) {
+		nr_counted++;
+		free += jeb->free_size;
+		dirty += jeb->dirty_size;
+		used += jeb->used_size;
+		wasted += jeb->wasted_size;
+		unchecked += jeb->unchecked_size;
+	}
+
+	list_for_each_entry(jeb, &c->erasing_list, list) {
+		nr_counted++;
+		erasing += c->sector_size;
+	}
+	list_for_each_entry(jeb, &c->erase_checking_list, list) {
+		nr_counted++;
+		erasing += c->sector_size;
+	}
+	list_for_each_entry(jeb, &c->erase_complete_list, list) {
+		nr_counted++;
+		erasing += c->sector_size;
+	}
+	list_for_each_entry(jeb, &c->bad_list, list) {
+		nr_counted++;
+		bad += c->sector_size;
+	}
+
+#define check(sz)							\
+do {									\
+	if (sz != c->sz##_size) {					\
+		pr_warn("%s_size mismatch counted 0x%x, c->%s_size 0x%x\n", \
+			#sz, sz, #sz, c->sz##_size);			\
+		dump = 1;						\
+	}								\
+} while (0)
+
+	check(free);
+	check(dirty);
+	check(used);
+	check(wasted);
+	check(unchecked);
+	check(bad);
+	check(erasing);
+
+#undef check
+
+	if (nr_counted != c->nr_blocks) {
+		pr_warn("%s counted only 0x%x blocks of 0x%x. Where are the others?\n",
+			__func__, nr_counted, c->nr_blocks);
+		dump = 1;
+	}
+
+	if (dump) {
+		__jffs2_dbg_dump_block_lists_nolock(c);
+		BUG();
+	}
+}
+
+/*
+ * Check the space accounting and node_ref list correctness for the JFFS2 erasable block 'jeb'.
+ */
+void
+__jffs2_dbg_acct_paranoia_check(struct jffs2_sb_info *c,
+				struct jffs2_eraseblock *jeb)
+{
+	spin_lock(&c->erase_completion_lock);
+	__jffs2_dbg_acct_paranoia_check_nolock(c, jeb);
+	spin_unlock(&c->erase_completion_lock);
+}
+
+void
+__jffs2_dbg_acct_paranoia_check_nolock(struct jffs2_sb_info *c,
+				       struct jffs2_eraseblock *jeb)
+{
+	uint32_t my_used_size = 0;
+	uint32_t my_unchecked_size = 0;
+	uint32_t my_dirty_size = 0;
+	struct jffs2_raw_node_ref *ref2 = jeb->first_node;
+
+	while (ref2) {
+		uint32_t totlen = ref_totlen(c, jeb, ref2);
+
+		if (ref_offset(ref2) < jeb->offset ||
+				ref_offset(ref2) > jeb->offset + c->sector_size) {
+			JFFS2_ERROR("node_ref %#08x shouldn't be in block at %#08x.\n",
+				ref_offset(ref2), jeb->offset);
+			goto error;
+
+		}
+		if (ref_flags(ref2) == REF_UNCHECKED)
+			my_unchecked_size += totlen;
+		else if (!ref_obsolete(ref2))
+			my_used_size += totlen;
+		else
+			my_dirty_size += totlen;
+
+		if ((!ref_next(ref2)) != (ref2 == jeb->last_node)) {
+			JFFS2_ERROR("node_ref for node at %#08x (mem %p) has next at %#08x (mem %p), last_node is at %#08x (mem %p).\n",
+				    ref_offset(ref2), ref2, ref_offset(ref_next(ref2)), ref_next(ref2),
+				    ref_offset(jeb->last_node), jeb->last_node);
+			goto error;
+		}
+		ref2 = ref_next(ref2);
+	}
+
+	if (my_used_size != jeb->used_size) {
+		JFFS2_ERROR("Calculated used size %#08x != stored used size %#08x.\n",
+			my_used_size, jeb->used_size);
+		goto error;
+	}
+
+	if (my_unchecked_size != jeb->unchecked_size) {
+		JFFS2_ERROR("Calculated unchecked size %#08x != stored unchecked size %#08x.\n",
+			my_unchecked_size, jeb->unchecked_size);
+		goto error;
+	}
+
+#if 0
+	/* This should work when we implement ref->__totlen elemination */
+	if (my_dirty_size != jeb->dirty_size + jeb->wasted_size) {
+		JFFS2_ERROR("Calculated dirty+wasted size %#08x != stored dirty + wasted size %#08x\n",
+			my_dirty_size, jeb->dirty_size + jeb->wasted_size);
+		goto error;
+	}
+
+	if (jeb->free_size == 0
+		&& my_used_size + my_unchecked_size + my_dirty_size != c->sector_size) {
+		JFFS2_ERROR("The sum of all nodes in block (%#x) != size of block (%#x)\n",
+			my_used_size + my_unchecked_size + my_dirty_size,
+			c->sector_size);
+		goto error;
+	}
+#endif
+
+	if (!(c->flags & (JFFS2_SB_FLAG_BUILDING|JFFS2_SB_FLAG_SCANNING)))
+		__jffs2_dbg_superblock_counts(c);
+
+	return;
+
+error:
+	__jffs2_dbg_dump_node_refs_nolock(c, jeb);
+	__jffs2_dbg_dump_jeb_nolock(jeb);
+	__jffs2_dbg_dump_block_lists_nolock(c);
+	BUG();
+
+}
+#endif /* JFFS2_DBG_PARANOIA_CHECKS */
+
+#if defined(JFFS2_DBG_DUMPS) || defined(JFFS2_DBG_PARANOIA_CHECKS)
+/*
+ * Dump the node_refs of the 'jeb' JFFS2 eraseblock.
+ */
+void
+__jffs2_dbg_dump_node_refs(struct jffs2_sb_info *c,
+			   struct jffs2_eraseblock *jeb)
+{
+	spin_lock(&c->erase_completion_lock);
+	__jffs2_dbg_dump_node_refs_nolock(c, jeb);
+	spin_unlock(&c->erase_completion_lock);
+}
+
+void
+__jffs2_dbg_dump_node_refs_nolock(struct jffs2_sb_info *c,
+				  struct jffs2_eraseblock *jeb)
+{
+	struct jffs2_raw_node_ref *ref;
+	int i = 0;
+
+	printk(JFFS2_DBG_MSG_PREFIX " Dump node_refs of the eraseblock %#08x\n", jeb->offset);
+	if (!jeb->first_node) {
+		printk(JFFS2_DBG_MSG_PREFIX " no nodes in the eraseblock %#08x\n", jeb->offset);
+		return;
+	}
+
+	printk(JFFS2_DBG);
+	for (ref = jeb->first_node; ; ref = ref_next(ref)) {
+		printk("%#08x", ref_offset(ref));
+#ifdef TEST_TOTLEN
+		printk("(%x)", ref->__totlen);
+#endif
+		if (ref_next(ref))
+			printk("->");
+		else
+			break;
+		if (++i == 4) {
+			i = 0;
+			printk("\n" JFFS2_DBG);
+		}
+	}
+	printk("\n");
+}
+
+/*
+ * Dump an eraseblock's space accounting.
+ */
+void
+__jffs2_dbg_dump_jeb(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb)
+{
+	spin_lock(&c->erase_completion_lock);
+	__jffs2_dbg_dump_jeb_nolock(jeb);
+	spin_unlock(&c->erase_completion_lock);
+}
+
+void
+__jffs2_dbg_dump_jeb_nolock(struct jffs2_eraseblock *jeb)
+{
+	if (!jeb)
+		return;
+
+	printk(JFFS2_DBG_MSG_PREFIX " dump space accounting for the eraseblock at %#08x:\n",
+			jeb->offset);
+
+	printk(JFFS2_DBG "used_size: %#08x\n",		jeb->used_size);
+	printk(JFFS2_DBG "dirty_size: %#08x\n",		jeb->dirty_size);
+	printk(JFFS2_DBG "wasted_size: %#08x\n",	jeb->wasted_size);
+	printk(JFFS2_DBG "unchecked_size: %#08x\n",	jeb->unchecked_size);
+	printk(JFFS2_DBG "free_size: %#08x\n",		jeb->free_size);
+}
+
+void
+__jffs2_dbg_dump_block_lists(struct jffs2_sb_info *c)
+{
+	spin_lock(&c->erase_completion_lock);
+	__jffs2_dbg_dump_block_lists_nolock(c);
+	spin_unlock(&c->erase_completion_lock);
+}
+
+void
+__jffs2_dbg_dump_block_lists_nolock(struct jffs2_sb_info *c)
+{
+	printk(JFFS2_DBG_MSG_PREFIX " dump JFFS2 blocks lists:\n");
+
+	printk(JFFS2_DBG "flash_size: %#08x\n",		c->flash_size);
+	printk(JFFS2_DBG "used_size: %#08x\n",		c->used_size);
+	printk(JFFS2_DBG "dirty_size: %#08x\n",		c->dirty_size);
+	printk(JFFS2_DBG "wasted_size: %#08x\n",	c->wasted_size);
+	printk(JFFS2_DBG "unchecked_size: %#08x\n",	c->unchecked_size);
+	printk(JFFS2_DBG "free_size: %#08x\n",		c->free_size);
+	printk(JFFS2_DBG "erasing_size: %#08x\n",	c->erasing_size);
+	printk(JFFS2_DBG "bad_size: %#08x\n",		c->bad_size);
+	printk(JFFS2_DBG "sector_size: %#08x\n",	c->sector_size);
+	printk(JFFS2_DBG "jffs2_reserved_blocks size: %#08x\n",
+				c->sector_size * c->resv_blocks_write);
+
+	if (c->nextblock)
+		printk(JFFS2_DBG "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(JFFS2_DBG "nextblock: NULL\n");
+
+	if (c->gcblock)
+		printk(JFFS2_DBG "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(JFFS2_DBG "gcblock: NULL\n");
+
+	if (list_empty(&c->clean_list)) {
+		printk(JFFS2_DBG "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;
+			if (!(jeb->used_size == 0 && jeb->dirty_size == 0 && jeb->wasted_size == 0)) {
+				printk(JFFS2_DBG "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 (JFFS2_DBG "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(JFFS2_DBG "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;
+			if (!(jeb->used_size == 0 && jeb->dirty_size == 0 && jeb->wasted_size == 0)) {
+				printk(JFFS2_DBG "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 (JFFS2_DBG "Contains %d blocks with total dirty size %u, average dirty size: %u\n",
+			numblocks, dirty, dirty / numblocks);
+	}
+
+	if (list_empty(&c->dirty_list)) {
+		printk(JFFS2_DBG "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;
+			if (!(jeb->used_size == 0 && jeb->dirty_size == 0 && jeb->wasted_size == 0)) {
+				printk(JFFS2_DBG "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 (JFFS2_DBG "contains %d blocks with total dirty size %u, average dirty size: %u\n",
+			numblocks, dirty, dirty / numblocks);
+	}
+
+	if (list_empty(&c->erasable_list)) {
+		printk(JFFS2_DBG "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);
+
+			if (!(jeb->used_size == 0 && jeb->dirty_size == 0 && jeb->wasted_size == 0)) {
+				printk(JFFS2_DBG "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(JFFS2_DBG "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);
+
+			if (!(jeb->used_size == 0 && jeb->dirty_size == 0 && jeb->wasted_size == 0)) {
+				printk(JFFS2_DBG "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_checking_list)) {
+		printk(JFFS2_DBG "erase_checking_list: empty\n");
+	} else {
+		struct list_head *this;
+
+		list_for_each(this, &c->erase_checking_list) {
+			struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list);
+
+			if (!(jeb->used_size == 0 && jeb->dirty_size == 0 && jeb->wasted_size == 0)) {
+				printk(JFFS2_DBG "erase_checking_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(JFFS2_DBG "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);
+
+			if (!(jeb->used_size == 0 && jeb->dirty_size == 0 && jeb->wasted_size == 0)) {
+				printk(JFFS2_DBG "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(JFFS2_DBG "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);
+
+			if (!(jeb->used_size == 0 && jeb->dirty_size == 0 && jeb->wasted_size == 0)) {
+				printk(JFFS2_DBG "erasable_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(JFFS2_DBG "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);
+
+			if (!(jeb->used_size == 0 && jeb->dirty_size == 0 && jeb->wasted_size == 0)) {
+				printk(JFFS2_DBG "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(JFFS2_DBG "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);
+
+			if (!(jeb->used_size == 0 && jeb->dirty_size == 0 && jeb->wasted_size == 0)) {
+				printk(JFFS2_DBG "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(JFFS2_DBG "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);
+
+			if (!(jeb->used_size == 0 && jeb->dirty_size == 0 && jeb->wasted_size == 0)) {
+				printk(JFFS2_DBG "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);
+			}
+		}
+	}
+}
+
+void
+__jffs2_dbg_dump_fragtree(struct jffs2_inode_info *f)
+{
+	mutex_lock(&f->sem);
+	jffs2_dbg_dump_fragtree_nolock(f);
+	mutex_unlock(&f->sem);
+}
+
+void
+__jffs2_dbg_dump_fragtree_nolock(struct jffs2_inode_info *f)
+{
+	struct jffs2_node_frag *this = frag_first(&f->fragtree);
+	uint32_t lastofs = 0;
+	int buggy = 0;
+
+	printk(JFFS2_DBG_MSG_PREFIX " dump fragtree of ino #%u\n", f->inocache->ino);
+	while(this) {
+		if (this->node)
+			printk(JFFS2_DBG "frag %#04x-%#04x: %#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(JFFS2_DBG "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(JFFS2_DBG "metadata at 0x%08x\n", ref_offset(f->metadata->raw));
+
+	if (buggy) {
+		JFFS2_ERROR("frag tree got a hole in it.\n");
+		BUG();
+	}
+}
+
+#define JFFS2_BUFDUMP_BYTES_PER_LINE	32
+void
+__jffs2_dbg_dump_buffer(unsigned char *buf, int len, uint32_t offs)
+{
+	int skip;
+	int i;
+
+	printk(JFFS2_DBG_MSG_PREFIX " dump from offset %#08x to offset %#08x (%x bytes).\n",
+		offs, offs + len, len);
+	i = skip = offs % JFFS2_BUFDUMP_BYTES_PER_LINE;
+	offs = offs & ~(JFFS2_BUFDUMP_BYTES_PER_LINE - 1);
+
+	if (skip != 0)
+		printk(JFFS2_DBG "%#08x: ", offs);
+
+	while (skip--)
+		printk("   ");
+
+	while (i < len) {
+		if ((i % JFFS2_BUFDUMP_BYTES_PER_LINE) == 0 && i != len -1) {
+			if (i != 0)
+				printk("\n");
+			offs += JFFS2_BUFDUMP_BYTES_PER_LINE;
+			printk(JFFS2_DBG "%0#8x: ", offs);
+		}
+
+		printk("%02x ", buf[i]);
+
+		i += 1;
+	}
+
+	printk("\n");
+}
+
+/*
+ * Dump a JFFS2 node.
+ */
+void
+__jffs2_dbg_dump_node(struct jffs2_sb_info *c, uint32_t ofs)
+{
+	union jffs2_node_union node;
+	int len = sizeof(union jffs2_node_union);
+	size_t retlen;
+	uint32_t crc;
+	int ret;
+
+	printk(JFFS2_DBG_MSG_PREFIX " dump node at offset %#08x.\n", ofs);
+
+	ret = jffs2_flash_read(c, ofs, len, &retlen, (unsigned char *)&node);
+	if (ret || (retlen != len)) {
+		JFFS2_ERROR("read %d bytes failed or short. ret %d, retlen %zd.\n",
+			len, ret, retlen);
+		return;
+	}
+
+	printk(JFFS2_DBG "magic:\t%#04x\n", je16_to_cpu(node.u.magic));
+	printk(JFFS2_DBG "nodetype:\t%#04x\n", je16_to_cpu(node.u.nodetype));
+	printk(JFFS2_DBG "totlen:\t%#08x\n", je32_to_cpu(node.u.totlen));
+	printk(JFFS2_DBG "hdr_crc:\t%#08x\n", je32_to_cpu(node.u.hdr_crc));
+
+	crc = crc32(0, &node.u, sizeof(node.u) - 4);
+	if (crc != je32_to_cpu(node.u.hdr_crc)) {
+		JFFS2_ERROR("wrong common header CRC.\n");
+		return;
+	}
+
+	if (je16_to_cpu(node.u.magic) != JFFS2_MAGIC_BITMASK &&
+		je16_to_cpu(node.u.magic) != JFFS2_OLD_MAGIC_BITMASK)
+	{
+		JFFS2_ERROR("wrong node magic: %#04x instead of %#04x.\n",
+			je16_to_cpu(node.u.magic), JFFS2_MAGIC_BITMASK);
+		return;
+	}
+
+	switch(je16_to_cpu(node.u.nodetype)) {
+
+	case JFFS2_NODETYPE_INODE:
+
+		printk(JFFS2_DBG "the node is inode node\n");
+		printk(JFFS2_DBG "ino:\t%#08x\n", je32_to_cpu(node.i.ino));
+		printk(JFFS2_DBG "version:\t%#08x\n", je32_to_cpu(node.i.version));
+		printk(JFFS2_DBG "mode:\t%#08x\n", node.i.mode.m);
+		printk(JFFS2_DBG "uid:\t%#04x\n", je16_to_cpu(node.i.uid));
+		printk(JFFS2_DBG "gid:\t%#04x\n", je16_to_cpu(node.i.gid));
+		printk(JFFS2_DBG "isize:\t%#08x\n", je32_to_cpu(node.i.isize));
+		printk(JFFS2_DBG "atime:\t%#08x\n", je32_to_cpu(node.i.atime));
+		printk(JFFS2_DBG "mtime:\t%#08x\n", je32_to_cpu(node.i.mtime));
+		printk(JFFS2_DBG "ctime:\t%#08x\n", je32_to_cpu(node.i.ctime));
+		printk(JFFS2_DBG "offset:\t%#08x\n", je32_to_cpu(node.i.offset));
+		printk(JFFS2_DBG "csize:\t%#08x\n", je32_to_cpu(node.i.csize));
+		printk(JFFS2_DBG "dsize:\t%#08x\n", je32_to_cpu(node.i.dsize));
+		printk(JFFS2_DBG "compr:\t%#02x\n", node.i.compr);
+		printk(JFFS2_DBG "usercompr:\t%#02x\n", node.i.usercompr);
+		printk(JFFS2_DBG "flags:\t%#04x\n", je16_to_cpu(node.i.flags));
+		printk(JFFS2_DBG "data_crc:\t%#08x\n", je32_to_cpu(node.i.data_crc));
+		printk(JFFS2_DBG "node_crc:\t%#08x\n", je32_to_cpu(node.i.node_crc));
+
+		crc = crc32(0, &node.i, sizeof(node.i) - 8);
+		if (crc != je32_to_cpu(node.i.node_crc)) {
+			JFFS2_ERROR("wrong node header CRC.\n");
+			return;
+		}
+		break;
+
+	case JFFS2_NODETYPE_DIRENT:
+
+		printk(JFFS2_DBG "the node is dirent node\n");
+		printk(JFFS2_DBG "pino:\t%#08x\n", je32_to_cpu(node.d.pino));
+		printk(JFFS2_DBG "version:\t%#08x\n", je32_to_cpu(node.d.version));
+		printk(JFFS2_DBG "ino:\t%#08x\n", je32_to_cpu(node.d.ino));
+		printk(JFFS2_DBG "mctime:\t%#08x\n", je32_to_cpu(node.d.mctime));
+		printk(JFFS2_DBG "nsize:\t%#02x\n", node.d.nsize);
+		printk(JFFS2_DBG "type:\t%#02x\n", node.d.type);
+		printk(JFFS2_DBG "node_crc:\t%#08x\n", je32_to_cpu(node.d.node_crc));
+		printk(JFFS2_DBG "name_crc:\t%#08x\n", je32_to_cpu(node.d.name_crc));
+
+		node.d.name[node.d.nsize] = '\0';
+		printk(JFFS2_DBG "name:\t\"%s\"\n", node.d.name);
+
+		crc = crc32(0, &node.d, sizeof(node.d) - 8);
+		if (crc != je32_to_cpu(node.d.node_crc)) {
+			JFFS2_ERROR("wrong node header CRC.\n");
+			return;
+		}
+		break;
+
+	default:
+		printk(JFFS2_DBG "node type is unknown\n");
+		break;
+	}
+}
+#endif /* JFFS2_DBG_DUMPS || JFFS2_DBG_PARANOIA_CHECKS */
Index: cpukit/libfs/src/jffs2/src/debug.h
===================================================================
--- cpukit/libfs/src/jffs2/src/debug.h	(revision 0c0f128e11007cb0d9ebed8b27dbb7cdde18450d)
+++ cpukit/libfs/src/jffs2/src/debug.h	(revision 0c0f128e11007cb0d9ebed8b27dbb7cdde18450d)
@@ -0,0 +1,275 @@
+/*
+ * JFFS2 -- Journalling Flash File System, Version 2.
+ *
+ * Copyright Â© 2001-2007 Red Hat, Inc.
+ * Copyright Â© 2004-2010 David Woodhouse <dwmw2@infradead.org>
+ *
+ * Created by David Woodhouse <dwmw2@infradead.org>
+ *
+ * For licensing information, see the file 'LICENCE' in this directory.
+ *
+ */
+
+#ifndef _JFFS2_DEBUG_H_
+#define _JFFS2_DEBUG_H_
+
+#include <linux/sched.h>
+
+#ifndef CONFIG_JFFS2_FS_DEBUG
+#define CONFIG_JFFS2_FS_DEBUG 0
+#endif
+
+#if CONFIG_JFFS2_FS_DEBUG > 0
+/* Enable "paranoia" checks and dumps */
+#define JFFS2_DBG_PARANOIA_CHECKS
+#define JFFS2_DBG_DUMPS
+
+/*
+ * By defining/undefining the below macros one may select debugging messages
+ * fro specific JFFS2 subsystems.
+ */
+#define JFFS2_DBG_READINODE_MESSAGES
+#define JFFS2_DBG_FRAGTREE_MESSAGES
+#define JFFS2_DBG_DENTLIST_MESSAGES
+#define JFFS2_DBG_NODEREF_MESSAGES
+#define JFFS2_DBG_INOCACHE_MESSAGES
+#define JFFS2_DBG_SUMMARY_MESSAGES
+#define JFFS2_DBG_FSBUILD_MESSAGES
+#endif
+
+#if CONFIG_JFFS2_FS_DEBUG > 1
+#define JFFS2_DBG_FRAGTREE2_MESSAGES
+#define JFFS2_DBG_READINODE2_MESSAGES
+#define JFFS2_DBG_MEMALLOC_MESSAGES
+#endif
+
+/* Sanity checks are supposed to be light-weight and enabled by default */
+#define JFFS2_DBG_SANITY_CHECKS
+
+/*
+ * Dx() are mainly used for debugging messages, they must go away and be
+ * superseded by nicer dbg_xxx() macros...
+ */
+#if CONFIG_JFFS2_FS_DEBUG > 0
+#define DEBUG
+#define D1(x) x
+#else
+#define D1(x)
+#endif
+
+#if CONFIG_JFFS2_FS_DEBUG > 1
+#define D2(x) x
+#else
+#define D2(x)
+#endif
+
+#define jffs2_dbg(level, fmt, ...)		\
+do {						\
+	if (CONFIG_JFFS2_FS_DEBUG >= level)	\
+		pr_debug(fmt, ##__VA_ARGS__);	\
+} while (0)
+
+/* The prefixes of JFFS2 messages */
+#define JFFS2_DBG		KERN_DEBUG
+#define JFFS2_DBG_PREFIX	"[JFFS2 DBG]"
+#define JFFS2_DBG_MSG_PREFIX	JFFS2_DBG JFFS2_DBG_PREFIX
+
+/* JFFS2 message macros */
+#define JFFS2_ERROR(fmt, ...)					\
+	pr_err("error: (%d) %s: " fmt,				\
+	       task_pid_nr(current), __func__, ##__VA_ARGS__)
+
+#define JFFS2_WARNING(fmt, ...)						\
+	pr_warn("warning: (%d) %s: " fmt,				\
+		task_pid_nr(current), __func__, ##__VA_ARGS__)
+
+#define JFFS2_NOTICE(fmt, ...)						\
+	pr_notice("notice: (%d) %s: " fmt,				\
+		  task_pid_nr(current), __func__, ##__VA_ARGS__)
+
+#define JFFS2_DEBUG(fmt, ...)						\
+	printk(KERN_DEBUG "[JFFS2 DBG] (%d) %s: " fmt,			\
+	       task_pid_nr(current), __func__, ##__VA_ARGS__)
+
+/*
+ * We split our debugging messages on several parts, depending on the JFFS2
+ * subsystem the message belongs to.
+ */
+/* Read inode debugging messages */
+#ifdef JFFS2_DBG_READINODE_MESSAGES
+#define dbg_readinode(fmt, ...)	JFFS2_DEBUG(fmt, ##__VA_ARGS__)
+#else
+#define dbg_readinode(fmt, ...)
+#endif
+#ifdef JFFS2_DBG_READINODE2_MESSAGES
+#define dbg_readinode2(fmt, ...)	JFFS2_DEBUG(fmt, ##__VA_ARGS__)
+#else
+#define dbg_readinode2(fmt, ...)
+#endif
+
+/* Fragtree build debugging messages */
+#ifdef JFFS2_DBG_FRAGTREE_MESSAGES
+#define dbg_fragtree(fmt, ...)	JFFS2_DEBUG(fmt, ##__VA_ARGS__)
+#else
+#define dbg_fragtree(fmt, ...)
+#endif
+#ifdef JFFS2_DBG_FRAGTREE2_MESSAGES
+#define dbg_fragtree2(fmt, ...)	JFFS2_DEBUG(fmt, ##__VA_ARGS__)
+#else
+#define dbg_fragtree2(fmt, ...)
+#endif
+
+/* Directory entry list manilulation debugging messages */
+#ifdef JFFS2_DBG_DENTLIST_MESSAGES
+#define dbg_dentlist(fmt, ...)	JFFS2_DEBUG(fmt, ##__VA_ARGS__)
+#else
+#define dbg_dentlist(fmt, ...)
+#endif
+
+/* Print the messages about manipulating node_refs */
+#ifdef JFFS2_DBG_NODEREF_MESSAGES
+#define dbg_noderef(fmt, ...)	JFFS2_DEBUG(fmt, ##__VA_ARGS__)
+#else
+#define dbg_noderef(fmt, ...)
+#endif
+
+/* Manipulations with the list of inodes (JFFS2 inocache) */
+#ifdef JFFS2_DBG_INOCACHE_MESSAGES
+#define dbg_inocache(fmt, ...)	JFFS2_DEBUG(fmt, ##__VA_ARGS__)
+#else
+#define dbg_inocache(fmt, ...)
+#endif
+
+/* Summary debugging messages */
+#ifdef JFFS2_DBG_SUMMARY_MESSAGES
+#define dbg_summary(fmt, ...)	JFFS2_DEBUG(fmt, ##__VA_ARGS__)
+#else
+#define dbg_summary(fmt, ...)
+#endif
+
+/* File system build messages */
+#ifdef JFFS2_DBG_FSBUILD_MESSAGES
+#define dbg_fsbuild(fmt, ...)	JFFS2_DEBUG(fmt, ##__VA_ARGS__)
+#else
+#define dbg_fsbuild(fmt, ...)
+#endif
+
+/* Watch the object allocations */
+#ifdef JFFS2_DBG_MEMALLOC_MESSAGES
+#define dbg_memalloc(fmt, ...)	JFFS2_DEBUG(fmt, ##__VA_ARGS__)
+#else
+#define dbg_memalloc(fmt, ...)
+#endif
+
+/* Watch the XATTR subsystem */
+#ifdef JFFS2_DBG_XATTR_MESSAGES
+#define dbg_xattr(fmt, ...)  JFFS2_DEBUG(fmt, ##__VA_ARGS__)
+#else
+#define dbg_xattr(fmt, ...)
+#endif 
+
+/* "Sanity" checks */
+void
+__jffs2_dbg_acct_sanity_check_nolock(struct jffs2_sb_info *c,
+				     struct jffs2_eraseblock *jeb);
+void
+__jffs2_dbg_acct_sanity_check(struct jffs2_sb_info *c,
+			      struct jffs2_eraseblock *jeb);
+
+/* "Paranoia" checks */
+void
+__jffs2_dbg_fragtree_paranoia_check(struct jffs2_inode_info *f);
+void
+__jffs2_dbg_fragtree_paranoia_check_nolock(struct jffs2_inode_info *f);
+void
+__jffs2_dbg_acct_paranoia_check(struct jffs2_sb_info *c,
+			   	struct jffs2_eraseblock *jeb);
+void
+__jffs2_dbg_acct_paranoia_check_nolock(struct jffs2_sb_info *c,
+				       struct jffs2_eraseblock *jeb);
+void
+__jffs2_dbg_prewrite_paranoia_check(struct jffs2_sb_info *c,
+				    uint32_t ofs, int len);
+
+/* "Dump" functions */
+void
+__jffs2_dbg_dump_jeb(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb);
+void
+__jffs2_dbg_dump_jeb_nolock(struct jffs2_eraseblock *jeb);
+void
+__jffs2_dbg_dump_block_lists(struct jffs2_sb_info *c);
+void
+__jffs2_dbg_dump_block_lists_nolock(struct jffs2_sb_info *c);
+void
+__jffs2_dbg_dump_node_refs(struct jffs2_sb_info *c,
+		 	   struct jffs2_eraseblock *jeb);
+void
+__jffs2_dbg_dump_node_refs_nolock(struct jffs2_sb_info *c,
+				  struct jffs2_eraseblock *jeb);
+void
+__jffs2_dbg_dump_fragtree(struct jffs2_inode_info *f);
+void
+__jffs2_dbg_dump_fragtree_nolock(struct jffs2_inode_info *f);
+void
+__jffs2_dbg_dump_buffer(unsigned char *buf, int len, uint32_t offs);
+void
+__jffs2_dbg_dump_node(struct jffs2_sb_info *c, uint32_t ofs);
+
+#ifdef JFFS2_DBG_PARANOIA_CHECKS
+#define jffs2_dbg_fragtree_paranoia_check(f)			\
+	__jffs2_dbg_fragtree_paranoia_check(f)
+#define jffs2_dbg_fragtree_paranoia_check_nolock(f)		\
+	__jffs2_dbg_fragtree_paranoia_check_nolock(f)
+#define jffs2_dbg_acct_paranoia_check(c, jeb)			\
+	__jffs2_dbg_acct_paranoia_check(c,jeb)
+#define jffs2_dbg_acct_paranoia_check_nolock(c, jeb)		\
+	__jffs2_dbg_acct_paranoia_check_nolock(c,jeb)
+#define jffs2_dbg_prewrite_paranoia_check(c, ofs, len)		\
+	__jffs2_dbg_prewrite_paranoia_check(c, ofs, len)
+#else
+#define jffs2_dbg_fragtree_paranoia_check(f)
+#define jffs2_dbg_fragtree_paranoia_check_nolock(f)
+#define jffs2_dbg_acct_paranoia_check(c, jeb)
+#define jffs2_dbg_acct_paranoia_check_nolock(c, jeb)
+#define jffs2_dbg_prewrite_paranoia_check(c, ofs, len)
+#endif /* !JFFS2_PARANOIA_CHECKS */
+
+#ifdef JFFS2_DBG_DUMPS
+#define jffs2_dbg_dump_jeb(c, jeb)				\
+	__jffs2_dbg_dump_jeb(c, jeb);
+#define jffs2_dbg_dump_jeb_nolock(jeb)				\
+	__jffs2_dbg_dump_jeb_nolock(jeb);
+#define jffs2_dbg_dump_block_lists(c)				\
+	__jffs2_dbg_dump_block_lists(c)
+#define jffs2_dbg_dump_block_lists_nolock(c)			\
+	__jffs2_dbg_dump_block_lists_nolock(c)
+#define jffs2_dbg_dump_fragtree(f)				\
+	__jffs2_dbg_dump_fragtree(f);
+#define jffs2_dbg_dump_fragtree_nolock(f)			\
+	__jffs2_dbg_dump_fragtree_nolock(f);
+#define jffs2_dbg_dump_buffer(buf, len, offs)			\
+	__jffs2_dbg_dump_buffer(*buf, len, offs);
+#define jffs2_dbg_dump_node(c, ofs)				\
+	__jffs2_dbg_dump_node(c, ofs);
+#else
+#define jffs2_dbg_dump_jeb(c, jeb)
+#define jffs2_dbg_dump_jeb_nolock(jeb)
+#define jffs2_dbg_dump_block_lists(c)
+#define jffs2_dbg_dump_block_lists_nolock(c)
+#define jffs2_dbg_dump_fragtree(f)
+#define jffs2_dbg_dump_fragtree_nolock(f)
+#define jffs2_dbg_dump_buffer(buf, len, offs)
+#define jffs2_dbg_dump_node(c, ofs)
+#endif /* !JFFS2_DBG_DUMPS */
+
+#ifdef JFFS2_DBG_SANITY_CHECKS
+#define jffs2_dbg_acct_sanity_check(c, jeb)			\
+	__jffs2_dbg_acct_sanity_check(c, jeb)
+#define jffs2_dbg_acct_sanity_check_nolock(c, jeb)		\
+	__jffs2_dbg_acct_sanity_check_nolock(c, jeb)
+#else
+#define jffs2_dbg_acct_sanity_check(c, jeb)
+#define jffs2_dbg_acct_sanity_check_nolock(c, jeb)
+#endif /* !JFFS2_DBG_SANITY_CHECKS */
+
+#endif /* _JFFS2_DEBUG_H_ */
Index: cpukit/libfs/src/jffs2/src/erase.c
===================================================================
--- cpukit/libfs/src/jffs2/src/erase.c	(revision 0c0f128e11007cb0d9ebed8b27dbb7cdde18450d)
+++ cpukit/libfs/src/jffs2/src/erase.c	(revision 0c0f128e11007cb0d9ebed8b27dbb7cdde18450d)
@@ -0,0 +1,515 @@
+/*
+ * JFFS2 -- Journalling Flash File System, Version 2.
+ *
+ * Copyright Â© 2001-2007 Red Hat, Inc.
+ * Copyright Â© 2004-2010 David Woodhouse <dwmw2@infradead.org>
+ *
+ * Created by David Woodhouse <dwmw2@infradead.org>
+ *
+ * For licensing information, see the file 'LICENCE' in this directory.
+ *
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/mtd/mtd.h>
+#include <linux/compiler.h>
+#include <linux/crc32.h>
+#include <linux/sched.h>
+#include <linux/pagemap.h>
+#include "nodelist.h"
+
+struct erase_priv_struct {
+	struct jffs2_eraseblock *jeb;
+	struct jffs2_sb_info *c;
+};
+
+#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, uint32_t bad_offset);
+static void jffs2_erase_succeeded(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb);
+static void jffs2_mark_erased_block(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb);
+
+static void jffs2_erase_block(struct jffs2_sb_info *c,
+			      struct jffs2_eraseblock *jeb)
+{
+	int ret;
+	uint32_t bad_offset;
+#ifdef __ECOS
+       ret = jffs2_flash_erase(c, jeb);
+       if (!ret) {
+	       jffs2_erase_succeeded(c, jeb);
+	       return;
+       }
+       bad_offset = jeb->offset;
+#else /* Linux */
+	struct erase_info *instr;
+
+	jffs2_dbg(1, "%s(): erase block %#08x (range %#08x-%#08x)\n",
+		  __func__,
+		  jeb->offset, jeb->offset, jeb->offset + c->sector_size);
+	instr = kmalloc(sizeof(struct erase_info) + sizeof(struct erase_priv_struct), GFP_KERNEL);
+	if (!instr) {
+		pr_warn("kmalloc for struct erase_info in jffs2_erase_block failed. Refiling block for later\n");
+		mutex_lock(&c->erase_free_sem);
+		spin_lock(&c->erase_completion_lock);
+		list_move(&jeb->list, &c->erase_pending_list);
+		c->erasing_size -= c->sector_size;
+		c->dirty_size += c->sector_size;
+		jeb->dirty_size = c->sector_size;
+		spin_unlock(&c->erase_completion_lock);
+		mutex_unlock(&c->erase_free_sem);
+		return;
+	}
+
+	memset(instr, 0, sizeof(*instr));
+
+	instr->mtd = c->mtd;
+	instr->addr = jeb->offset;
+	instr->len = c->sector_size;
+	instr->callback = jffs2_erase_callback;
+	instr->priv = (unsigned long)(&instr[1]);
+
+	((struct erase_priv_struct *)instr->priv)->jeb = jeb;
+	((struct erase_priv_struct *)instr->priv)->c = c;
+
+	ret = mtd_erase(c->mtd, instr);
+	if (!ret)
+		return;
+
+	bad_offset = instr->fail_addr;
+	kfree(instr);
+#endif /* __ECOS */
+
+	if (ret == -ENOMEM || ret == -EAGAIN) {
+		/* Erase failed immediately. Refile it on the list */
+		jffs2_dbg(1, "Erase at 0x%08x failed: %d. Refiling on erase_pending_list\n",
+			  jeb->offset, ret);
+		mutex_lock(&c->erase_free_sem);
+		spin_lock(&c->erase_completion_lock);
+		list_move(&jeb->list, &c->erase_pending_list);
+		c->erasing_size -= c->sector_size;
+		c->dirty_size += c->sector_size;
+		jeb->dirty_size = c->sector_size;
+		spin_unlock(&c->erase_completion_lock);
+		mutex_unlock(&c->erase_free_sem);
+		return;
+	}
+
+	if (ret == -EROFS)
+		pr_warn("Erase at 0x%08x failed immediately: -EROFS. Is the sector locked?\n",
+			jeb->offset);
+	else
+		pr_warn("Erase at 0x%08x failed immediately: errno %d\n",
+			jeb->offset, ret);
+
+	jffs2_erase_failed(c, jeb, bad_offset);
+}
+
+int jffs2_erase_pending_blocks(struct jffs2_sb_info *c, int count)
+{
+	struct jffs2_eraseblock *jeb;
+	int work_done = 0;
+
+	mutex_lock(&c->erase_free_sem);
+
+	spin_lock(&c->erase_completion_lock);
+
+	while (!list_empty(&c->erase_complete_list) ||
+	       !list_empty(&c->erase_pending_list)) {
+
+		if (!list_empty(&c->erase_complete_list)) {
+			jeb = list_entry(c->erase_complete_list.next, struct jffs2_eraseblock, list);
+			list_move(&jeb->list, &c->erase_checking_list);
+			spin_unlock(&c->erase_completion_lock);
+			mutex_unlock(&c->erase_free_sem);
+			jffs2_mark_erased_block(c, jeb);
+
+			work_done++;
+			if (!--count) {
+				jffs2_dbg(1, "Count reached. jffs2_erase_pending_blocks leaving\n");
+				goto done;
+			}
+
+		} else if (!list_empty(&c->erase_pending_list)) {
+			jeb = list_entry(c->erase_pending_list.next, struct jffs2_eraseblock, list);
+			jffs2_dbg(1, "Starting erase of pending block 0x%08x\n",
+				  jeb->offset);
+			list_del(&jeb->list);
+			c->erasing_size += c->sector_size;
+			c->wasted_size -= jeb->wasted_size;
+			c->free_size -= jeb->free_size;
+			c->used_size -= jeb->used_size;
+			c->dirty_size -= jeb->dirty_size;
+			jeb->wasted_size = jeb->used_size = jeb->dirty_size = jeb->free_size = 0;
+			jffs2_free_jeb_node_refs(c, jeb);
+			list_add(&jeb->list, &c->erasing_list);
+			spin_unlock(&c->erase_completion_lock);
+			mutex_unlock(&c->erase_free_sem);
+
+			jffs2_erase_block(c, jeb);
+
+		} else {
+			BUG();
+		}
+
+		/* Be nice */
+		cond_resched();
+		mutex_lock(&c->erase_free_sem);
+		spin_lock(&c->erase_completion_lock);
+	}
+
+	spin_unlock(&c->erase_completion_lock);
+	mutex_unlock(&c->erase_free_sem);
+ done:
+	jffs2_dbg(1, "jffs2_erase_pending_blocks completed\n");
+	return work_done;
+}
+
+static void jffs2_erase_succeeded(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb)
+{
+	jffs2_dbg(1, "Erase completed successfully at 0x%08x\n", jeb->offset);
+	mutex_lock(&c->erase_free_sem);
+	spin_lock(&c->erase_completion_lock);
+	list_move_tail(&jeb->list, &c->erase_complete_list);
+	/* Wake the GC thread to mark them clean */
+	jffs2_garbage_collect_trigger(c);
+	spin_unlock(&c->erase_completion_lock);
+	mutex_unlock(&c->erase_free_sem);
+	wake_up(&c->erase_wait);
+}
+
+static void jffs2_erase_failed(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, uint32_t bad_offset)
+{
+	/* For NAND, if the failure did not occur at the device level for a
+	   specific physical page, don't bother updating the bad block table. */
+	if (jffs2_cleanmarker_oob(c) && (bad_offset != (uint32_t)MTD_FAIL_ADDR_UNKNOWN)) {
+		/* We had a device-level failure to erase.  Let's see if we've
+		   failed too many times. */
+		if (!jffs2_write_nand_badblock(c, jeb, bad_offset)) {
+			/* We'd like to give this block another try. */
+			mutex_lock(&c->erase_free_sem);
+			spin_lock(&c->erase_completion_lock);
+			list_move(&jeb->list, &c->erase_pending_list);
+			c->erasing_size -= c->sector_size;
+			c->dirty_size += c->sector_size;
+			jeb->dirty_size = c->sector_size;
+			spin_unlock(&c->erase_completion_lock);
+			mutex_unlock(&c->erase_free_sem);
+			return;
+		}
+	}
+
+	mutex_lock(&c->erase_free_sem);
+	spin_lock(&c->erase_completion_lock);
+	c->erasing_size -= c->sector_size;
+	c->bad_size += c->sector_size;
+	list_move(&jeb->list, &c->bad_list);
+	c->nr_erasing_blocks--;
+	spin_unlock(&c->erase_completion_lock);
+	mutex_unlock(&c->erase_free_sem);
+	wake_up(&c->erase_wait);
+}
+
+#ifndef __ECOS
+static void jffs2_erase_callback(struct erase_info *instr)
+{
+	struct erase_priv_struct *priv = (void *)instr->priv;
+
+	if(instr->state != MTD_ERASE_DONE) {
+		pr_warn("Erase at 0x%08llx finished, but state != MTD_ERASE_DONE. State is 0x%x instead.\n",
+			(unsigned long long)instr->addr, instr->state);
+		jffs2_erase_failed(priv->c, priv->jeb, instr->fail_addr);
+	} else {
+		jffs2_erase_succeeded(priv->c, priv->jeb);
+	}
+	kfree(instr);
+}
+#endif /* !__ECOS */
+
+/* Hmmm. Maybe we should accept the extra space it takes and make
+   this a standard doubly-linked list? */
+static inline void jffs2_remove_node_refs_from_ino_list(struct jffs2_sb_info *c,
+			struct jffs2_raw_node_ref *ref, struct jffs2_eraseblock *jeb)
+{
+	struct jffs2_inode_cache *ic = NULL;
+	struct jffs2_raw_node_ref **prev;
+
+	prev = &ref->next_in_ino;
+
+	/* Walk the inode's list once, removing any nodes from this eraseblock */
+	while (1) {
+		if (!(*prev)->next_in_ino) {
+			/* We're looking at the jffs2_inode_cache, which is
+			   at the end of the linked list. Stash it and continue
+			   from the beginning of the list */
+			ic = (struct jffs2_inode_cache *)(*prev);
+			prev = &ic->nodes;
+			continue;
+		}
+
+		if (SECTOR_ADDR((*prev)->flash_offset) == jeb->offset) {
+			/* It's in the block we're erasing */
+			struct jffs2_raw_node_ref *this;
+
+			this = *prev;
+			*prev = this->next_in_ino;
+			this->next_in_ino = NULL;
+
+			if (this == ref)
+				break;
+
+			continue;
+		}
+		/* Not to be deleted. Skip */
+		prev = &((*prev)->next_in_ino);
+	}
+
+	/* PARANOIA */
+	if (!ic) {
+		JFFS2_WARNING("inode_cache/xattr_datum/xattr_ref"
+			      " not found in remove_node_refs()!!\n");
+		return;
+	}
+
+	jffs2_dbg(1, "Removed nodes in range 0x%08x-0x%08x from ino #%u\n",
+		  jeb->offset, jeb->offset + c->sector_size, ic->ino);
+
+	D2({
+		int i=0;
+		struct jffs2_raw_node_ref *this;
+		printk(KERN_DEBUG "After remove_node_refs_from_ino_list: \n");
+
+		this = ic->nodes;
+
+		printk(KERN_DEBUG);
+		while(this) {
+			pr_cont("0x%08x(%d)->",
+			       ref_offset(this), ref_flags(this));
+			if (++i == 5) {
+				printk(KERN_DEBUG);
+				i=0;
+			}
+			this = this->next_in_ino;
+		}
+		pr_cont("\n");
+	});
+
+	switch (ic->class) {
+#ifdef CONFIG_JFFS2_FS_XATTR
+		case RAWNODE_CLASS_XATTR_DATUM:
+			jffs2_release_xattr_datum(c, (struct jffs2_xattr_datum *)ic);
+			break;
+		case RAWNODE_CLASS_XATTR_REF:
+			jffs2_release_xattr_ref(c, (struct jffs2_xattr_ref *)ic);
+			break;
+#endif
+		default:
+			if (ic->nodes == (void *)ic && ic->pino_nlink == 0)
+				jffs2_del_ino_cache(c, ic);
+	}
+}
+
+void jffs2_free_jeb_node_refs(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb)
+{
+	struct jffs2_raw_node_ref *block, *ref;
+	jffs2_dbg(1, "Freeing all node refs for eraseblock offset 0x%08x\n",
+		  jeb->offset);
+
+	block = ref = jeb->first_node;
+
+	while (ref) {
+		if (ref->flash_offset == REF_LINK_NODE) {
+			ref = ref->next_in_ino;
+			jffs2_free_refblock(block);
+			block = ref;
+			continue;
+		}
+		if (ref->flash_offset != REF_EMPTY_NODE && ref->next_in_ino)
+			jffs2_remove_node_refs_from_ino_list(c, ref, jeb);
+		/* else it was a non-inode node or already removed, so don't bother */
+
+		ref++;
+	}
+	jeb->first_node = jeb->last_node = NULL;
+}
+
+static int jffs2_block_check_erase(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, uint32_t *bad_offset)
+{
+	void *ebuf;
+	uint32_t ofs;
+	size_t retlen;
+	int ret;
+	unsigned long *wordebuf;
+
+	ret = mtd_point(c->mtd, jeb->offset, c->sector_size, &retlen,
+			&ebuf, NULL);
+	if (ret != -EOPNOTSUPP) {
+		if (ret) {
+			jffs2_dbg(1, "MTD point failed %d\n", ret);
+			goto do_flash_read;
+		}
+		if (retlen < c->sector_size) {
+			/* Don't muck about if it won't let us point to the whole erase sector */
+			jffs2_dbg(1, "MTD point returned len too short: 0x%zx\n",
+				  retlen);
+			mtd_unpoint(c->mtd, jeb->offset, retlen);
+			goto do_flash_read;
+		}
+		wordebuf = ebuf-sizeof(*wordebuf);
+		retlen /= sizeof(*wordebuf);
+		do {
+		   if (*++wordebuf != ~0)
+			   break;
+		} while(--retlen);
+		mtd_unpoint(c->mtd, jeb->offset, c->sector_size);
+		if (retlen) {
+			pr_warn("Newly-erased block contained word 0x%lx at offset 0x%08tx\n",
+				*wordebuf,
+				jeb->offset +
+				c->sector_size-retlen * sizeof(*wordebuf));
+			return -EIO;
+		}
+		return 0;
+	}
+ do_flash_read:
+	ebuf = kmalloc(PAGE_SIZE, GFP_KERNEL);
+	if (!ebuf) {
+		pr_warn("Failed to allocate page buffer for verifying erase at 0x%08x. Refiling\n",
+			jeb->offset);
+		return -EAGAIN;
+	}
+
+	jffs2_dbg(1, "Verifying erase at 0x%08x\n", jeb->offset);
+
+	for (ofs = jeb->offset; ofs < jeb->offset + c->sector_size; ) {
+		uint32_t readlen = min((uint32_t)PAGE_SIZE, jeb->offset + c->sector_size - ofs);
+		int i;
+
+		*bad_offset = ofs;
+
+		ret = mtd_read(c->mtd, ofs, readlen, &retlen, ebuf);
+		if (ret) {
+			pr_warn("Read of newly-erased block at 0x%08x failed: %d. Putting on bad_list\n",
+				ofs, ret);
+			ret = -EIO;
+			goto fail;
+		}
+		if (retlen != readlen) {
+			pr_warn("Short read from newly-erased block at 0x%08x. Wanted %d, got %zd\n",
+				ofs, readlen, retlen);
+			ret = -EIO;
+			goto fail;
+		}
+		for (i=0; i<readlen; i += sizeof(unsigned long)) {
+			/* It's OK. We know it's properly aligned */
+			unsigned long *datum = ebuf + i;
+			if (*datum + 1) {
+				*bad_offset += i;
+				pr_warn("Newly-erased block contained word 0x%lx at offset 0x%08x\n",
+					*datum, *bad_offset);
+				ret = -EIO;
+				goto fail;
+			}
+		}
+		ofs += readlen;
+		cond_resched();
+	}
+	ret = 0;
+fail:
+	kfree(ebuf);
+	return ret;
+}
+
+static void jffs2_mark_erased_block(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb)
+{
+	size_t retlen;
+	int ret;
+	uint32_t uninitialized_var(bad_offset);
+
+	switch (jffs2_block_check_erase(c, jeb, &bad_offset)) {
+	case -EAGAIN:	goto refile;
+	case -EIO:	goto filebad;
+	}
+
+	/* Write the erase complete marker */
+	jffs2_dbg(1, "Writing erased marker to block at 0x%08x\n", jeb->offset);
+	bad_offset = jeb->offset;
+
+	/* Cleanmarker in oob area or no cleanmarker at all ? */
+	if (jffs2_cleanmarker_oob(c) || c->cleanmarker_size == 0) {
+
+		if (jffs2_cleanmarker_oob(c)) {
+			if (jffs2_write_nand_cleanmarker(c, jeb))
+				goto filebad;
+		}
+	} else {
+
+		struct kvec vecs[1];
+		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)
+		};
+
+		jffs2_prealloc_raw_node_refs(c, jeb, 1);
+
+		marker.hdr_crc = cpu_to_je32(crc32(0, &marker, sizeof(struct jffs2_unknown_node)-4));
+
+		vecs[0].iov_base = (unsigned char *) &marker;
+		vecs[0].iov_len = sizeof(marker);
+		ret = jffs2_flash_direct_writev(c, vecs, 1, jeb->offset, &retlen);
+
+		if (ret || retlen != sizeof(marker)) {
+			if (ret)
+				pr_warn("Write clean marker to block at 0x%08x failed: %d\n",
+				       jeb->offset, ret);
+			else
+				pr_warn("Short write to newly-erased block at 0x%08x: Wanted %zd, got %zd\n",
+				       jeb->offset, sizeof(marker), retlen);
+
+			goto filebad;
+		}
+	}
+	/* Everything else got zeroed before the erase */
+	jeb->free_size = c->sector_size;
+
+	mutex_lock(&c->erase_free_sem);
+	spin_lock(&c->erase_completion_lock);
+
+	c->erasing_size -= c->sector_size;
+	c->free_size += c->sector_size;
+
+	/* Account for cleanmarker now, if it's in-band */
+	if (c->cleanmarker_size && !jffs2_cleanmarker_oob(c))
+		jffs2_link_node_ref(c, jeb, jeb->offset | REF_NORMAL, c->cleanmarker_size, NULL);
+
+	list_move_tail(&jeb->list, &c->free_list);
+	c->nr_erasing_blocks--;
+	c->nr_free_blocks++;
+
+	jffs2_dbg_acct_sanity_check_nolock(c, jeb);
+	jffs2_dbg_acct_paranoia_check_nolock(c, jeb);
+
+	spin_unlock(&c->erase_completion_lock);
+	mutex_unlock(&c->erase_free_sem);
+	wake_up(&c->erase_wait);
+	return;
+
+filebad:
+	jffs2_erase_failed(c, jeb, bad_offset);
+	return;
+
+refile:
+	/* Stick it back on the list from whence it came and come back later */
+	mutex_lock(&c->erase_free_sem);
+	spin_lock(&c->erase_completion_lock);
+	jffs2_garbage_collect_trigger(c);
+	list_move(&jeb->list, &c->erase_complete_list);
+	spin_unlock(&c->erase_completion_lock);
+	mutex_unlock(&c->erase_free_sem);
+	return;
+}
Index: cpukit/libfs/src/jffs2/src/gc.c
===================================================================
--- cpukit/libfs/src/jffs2/src/gc.c	(revision 0c0f128e11007cb0d9ebed8b27dbb7cdde18450d)
+++ cpukit/libfs/src/jffs2/src/gc.c	(revision 0c0f128e11007cb0d9ebed8b27dbb7cdde18450d)
@@ -0,0 +1,1378 @@
+/*
+ * JFFS2 -- Journalling Flash File System, Version 2.
+ *
+ * Copyright Â© 2001-2007 Red Hat, Inc.
+ * Copyright Â© 2004-2010 David Woodhouse <dwmw2@infradead.org>
+ *
+ * Created by David Woodhouse <dwmw2@infradead.org>
+ *
+ * For licensing information, see the file 'LICENCE' in this directory.
+ *
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/kernel.h>
+#include <linux/mtd/mtd.h>
+#include <linux/slab.h>
+#include <linux/pagemap.h>
+#include <linux/crc32.h>
+#include <linux/compiler.h>
+#include <linux/stat.h>
+#include "nodelist.h"
+#include "compr.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,
+					struct jffs2_inode_info *f, struct jffs2_full_dirent *fd);
+static int jffs2_garbage_collect_deletion_dirent(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb,
+					struct jffs2_inode_info *f, struct jffs2_full_dirent *fd);
+static int jffs2_garbage_collect_hole(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_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_info *f);
+
+/* Called with erase_completion_lock held */
+static struct jffs2_eraseblock *jffs2_find_gc_block(struct jffs2_sb_info *c)
+{
+	struct jffs2_eraseblock *ret;
+	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. */
+again:
+	if (!list_empty(&c->bad_used_list) && c->nr_free_blocks > c->resv_blocks_gcbad) {
+		jffs2_dbg(1, "Picking block from bad_used_list to GC next\n");
+		nextlist = &c->bad_used_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. */
+		jffs2_dbg(1, "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 */
+		jffs2_dbg(1, "Picking block from very_dirty_list to GC next\n");
+		nextlist = &c->very_dirty_list;
+	} else if (n < 126 && !list_empty(&c->dirty_list)) {
+		jffs2_dbg(1, "Picking block from dirty_list to GC next\n");
+		nextlist = &c->dirty_list;
+	} else if (!list_empty(&c->clean_list)) {
+		jffs2_dbg(1, "Picking block from clean_list to GC next\n");
+		nextlist = &c->clean_list;
+	} else if (!list_empty(&c->dirty_list)) {
+		jffs2_dbg(1, "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)) {
+		jffs2_dbg(1, "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)) {
+		jffs2_dbg(1, "Picking block from erasable_list to GC next (clean_list and {very_,}dirty_list were empty)\n");
+
+		nextlist = &c->erasable_list;
+	} else if (!list_empty(&c->erasable_pending_wbuf_list)) {
+		/* There are blocks are wating for the wbuf sync */
+		jffs2_dbg(1, "Synching wbuf in order to reuse erasable_pending_wbuf_list blocks\n");
+		spin_unlock(&c->erase_completion_lock);
+		jffs2_flush_wbuf_pad(c);
+		spin_lock(&c->erase_completion_lock);
+		goto again;
+	} else {
+		/* Eep. All were empty */
+		jffs2_dbg(1, "No clean, dirty _or_ erasable blocks to GC from! Where are they all?\n");
+		return NULL;
+	}
+
+	ret = list_entry(nextlist->next, struct jffs2_eraseblock, list);
+	list_del(&ret->list);
+	c->gcblock = ret;
+	ret->gc_node = ret->first_node;
+	if (!ret->gc_node) {
+		pr_warn("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) {
+		jffs2_dbg(1, "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;
+	}
+
+	return ret;
+}
+
+/* jffs2_garbage_collect_pass
+ * Make a single attempt to progress GC. Move one node, and possibly
+ * start erasing one eraseblock.
+ */
+int jffs2_garbage_collect_pass(struct jffs2_sb_info *c)
+{
+	struct jffs2_inode_info *f;
+	struct jffs2_inode_cache *ic;
+	struct jffs2_eraseblock *jeb;
+	struct jffs2_raw_node_ref *raw;
+	uint32_t gcblock_dirty;
+	int ret = 0, inum, nlink;
+	int xattr = 0;
+
+	if (mutex_lock_interruptible(&c->alloc_sem))
+		return -EINTR;
+
+	for (;;) {
+		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 && xattr) {
+			pr_crit("Checked all inodes but still 0x%x bytes of unchecked space?\n",
+				c->unchecked_size);
+			jffs2_dbg_dump_block_lists_nolock(c);
+			spin_unlock(&c->erase_completion_lock);
+			mutex_unlock(&c->alloc_sem);
+			return -ENOSPC;
+		}
+
+		spin_unlock(&c->erase_completion_lock);
+
+		if (!xattr)
+			xattr = jffs2_verify_xattr(c);
+
+		spin_lock(&c->inocache_lock);
+
+		ic = jffs2_get_ino_cache(c, c->checked_ino++);
+
+		if (!ic) {
+			spin_unlock(&c->inocache_lock);
+			continue;
+		}
+
+		if (!ic->pino_nlink) {
+			jffs2_dbg(1, "Skipping check of ino #%d with nlink/pino zero\n",
+				  ic->ino);
+			spin_unlock(&c->inocache_lock);
+			jffs2_xattr_delete_inode(c, ic);
+			continue;
+		}
+		switch(ic->state) {
+		case INO_STATE_CHECKEDABSENT:
+		case INO_STATE_PRESENT:
+			jffs2_dbg(1, "Skipping ino #%u already checked\n",
+				  ic->ino);
+			spin_unlock(&c->inocache_lock);
+			continue;
+
+		case INO_STATE_GC:
+		case INO_STATE_CHECKING:
+			pr_warn("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 */
+			jffs2_dbg(1, "Waiting for ino #%u to finish reading\n",
+				  ic->ino);
+			/* We need to come back again for the _same_ inode. We've
+			 made no progress in this case, but that should be OK */
+			c->checked_ino--;
+
+			mutex_unlock(&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);
+
+		jffs2_dbg(1, "%s(): triggering inode scan of ino#%u\n",
+			  __func__, ic->ino);
+
+		ret = jffs2_do_crccheck_inode(c, ic);
+		if (ret)
+			pr_warn("Returned error for crccheck of ino #%u. Expect badness...\n",
+				ic->ino);
+
+		jffs2_set_inocache_state(c, ic, INO_STATE_CHECKEDABSENT);
+		mutex_unlock(&c->alloc_sem);
+		return ret;
+	}
+
+	/* If there are any blocks which need erasing, erase them now */
+	if (!list_empty(&c->erase_complete_list) ||
+	    !list_empty(&c->erase_pending_list)) {
+		spin_unlock(&c->erase_completion_lock);
+		mutex_unlock(&c->alloc_sem);
+		jffs2_dbg(1, "%s(): erasing pending blocks\n", __func__);
+		if (jffs2_erase_pending_blocks(c, 1))
+			return 0;
+
+		jffs2_dbg(1, "No progress from erasing block; doing GC anyway\n");
+		mutex_lock(&c->alloc_sem);
+		spin_lock(&c->erase_completion_lock);
+	}
+
+	/* First, work out which block we're garbage-collecting */
+	jeb = c->gcblock;
+
+	if (!jeb)
+		jeb = jffs2_find_gc_block(c);
+
+	if (!jeb) {
+		/* Couldn't find a free block. But maybe we can just erase one and make 'progress'? */
+		if (c->nr_erasing_blocks) {
+			spin_unlock(&c->erase_completion_lock);
+			mutex_unlock(&c->alloc_sem);
+			return -EAGAIN;
+		}
+		jffs2_dbg(1, "Couldn't find erase block to garbage collect!\n");
+		spin_unlock(&c->erase_completion_lock);
+		mutex_unlock(&c->alloc_sem);
+		return -EIO;
+	}
+
+	jffs2_dbg(1, "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) {
+		mutex_unlock(&c->alloc_sem);
+		goto eraseit;
+	}
+
+	raw = jeb->gc_node;
+	gcblock_dirty = jeb->dirty_size;
+
+	while(ref_obsolete(raw)) {
+		jffs2_dbg(1, "Node at 0x%08x is obsolete... skipping\n",
+			  ref_offset(raw));
+		raw = ref_next(raw);
+		if (unlikely(!raw)) {
+			pr_warn("eep. End of raw list while still supposedly nodes to GC\n");
+			pr_warn("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);
+			jeb->gc_node = raw;
+			spin_unlock(&c->erase_completion_lock);
+			mutex_unlock(&c->alloc_sem);
+			BUG();
+		}
+	}
+	jeb->gc_node = raw;
+
+	jffs2_dbg(1, "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(&c->erase_completion_lock);
+		if (ref_flags(raw) == REF_PRISTINE) {
+			/* It's an unknown node with JFFS2_FEATURE_RWCOMPAT_COPY */
+			jffs2_garbage_collect_pristine(c, NULL, raw);
+		} else {
+			/* Just mark it obsolete */
+			jffs2_mark_node_obsolete(c, raw);
+		}
+		mutex_unlock(&c->alloc_sem);
+		goto eraseit_lock;
+	}
+
+	ic = jffs2_raw_ref_to_ic(raw);
+
+#ifdef CONFIG_JFFS2_FS_XATTR
+	/* When 'ic' refers xattr_datum/xattr_ref, this node is GCed as xattr.
+	 * We can decide whether this node is inode or xattr by ic->class.     */
+	if (ic->class == RAWNODE_CLASS_XATTR_DATUM
+	    || ic->class == RAWNODE_CLASS_XATTR_REF) {
+		spin_unlock(&c->erase_completion_lock);
+
+		if (ic->class == RAWNODE_CLASS_XATTR_DATUM) {
+			ret = jffs2_garbage_collect_xattr_datum(c, (struct jffs2_xattr_datum *)ic, raw);
+		} else {
+			ret = jffs2_garbage_collect_xattr_ref(c, (struct jffs2_xattr_ref *)ic, raw);
+		}
+		goto test_gcnode;
+	}
+#endif
+
+	/* We need to hold the inocache. Either the erase_completion_lock or
+	   the inocache_lock are sufficient; we trade down since the inocache_lock
+	   causes less contention. */
+	spin_lock(&c->inocache_lock);
+
+	spin_unlock(&c->erase_completion_lock);
+
+	jffs2_dbg(1, "%s(): collecting from block @0x%08x. Node @0x%08x(%d), ino #%u\n",
+		  __func__, jeb->offset, ref_offset(raw), ref_flags(raw),
+		  ic->ino);
+
+	/* 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.
+	*/
+
+	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 {
+			jffs2_dbg(1, "Ino #%u is absent but node not REF_PRISTINE. Reading.\n",
+				  ic->ino);
+		}
+		break;
+
+	case INO_STATE_PRESENT:
+		/* It's in-core. GC must iget() it. */
+		break;
+
+	case INO_STATE_UNCHECKED:
+	case INO_STATE_CHECKING:
+	case INO_STATE_GC:
+		/* Should never happen. We should have finished checking
+		   by the time we actually start doing any GC, and since
+		   we're holding the alloc_sem, no other garbage collection
+		   can happen.
+		*/
+		pr_crit("Inode #%u already in state %d in jffs2_garbage_collect_pass()!\n",
+			ic->ino, ic->state);
+		mutex_unlock(&c->alloc_sem);
+		spin_unlock(&c->inocache_lock);
+		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. */
+
+		mutex_unlock(&c->alloc_sem);
+		jffs2_dbg(1, "%s(): waiting for ino #%u in state %d\n",
+			  __func__, ic->ino, 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.
+
+		   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;
+	}
+
+	/* 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) {
+		spin_unlock(&c->inocache_lock);
+
+		ret = jffs2_garbage_collect_pristine(c, ic, raw);
+
+		spin_lock(&c->inocache_lock);
+		ic->state = INO_STATE_CHECKEDABSENT;
+		wake_up(&c->inocache_wq);
+
+		if (ret != -EBADFD) {
+			spin_unlock(&c->inocache_lock);
+			goto test_gcnode;
+		}
+
+		/* Fall through if it wanted us to, with inocache_lock held */
+	}
+
+	/* Prevent the fairly unlikely race where the gcblock is
+	   entirely obsoleted by the final close of a file which had
+	   the only valid nodes in the block, followed by erasure,
+	   followed by freeing of the ic because the erased block(s)
+	   held _all_ the nodes of that inode.... never been seen but
+	   it's vaguely possible. */
+
+	inum = ic->ino;
+	nlink = ic->pino_nlink;
+	spin_unlock(&c->inocache_lock);
+
+	f = jffs2_gc_fetch_inode(c, inum, !nlink);
+	if (IS_ERR(f)) {
+		ret = PTR_ERR(f);
+		goto release_sem;
+	}
+	if (!f) {
+		ret = 0;
+		goto release_sem;
+	}
+
+	ret = jffs2_garbage_collect_live(c, jeb, raw, f);
+
+	jffs2_gc_release_inode(c, f);
+
+ test_gcnode:
+	if (jeb->dirty_size == gcblock_dirty && !ref_obsolete(jeb->gc_node)) {
+		/* Eep. This really should never happen. GC is broken */
+		pr_err("Error garbage collecting node at %08x!\n",
+		       ref_offset(jeb->gc_node));
+		ret = -ENOSPC;
+	}
+ release_sem:
+	mutex_unlock(&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) {
+		jffs2_dbg(1, "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_garbage_collect_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_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;
+	int ret = 0;
+
+	mutex_lock(&f->sem);
+
+	/* Now we have the lock for this inode. Check that it's still the one at the head
+	   of the list. */
+
+	spin_lock(&c->erase_completion_lock);
+
+	if (c->gcblock != jeb) {
+		spin_unlock(&c->erase_completion_lock);
+		jffs2_dbg(1, "GC block is no longer gcblock. Restart\n");
+		goto upnout;
+	}
+	if (ref_obsolete(raw)) {
+		spin_unlock(&c->erase_completion_lock);
+		jffs2_dbg(1, "node to be GC'd was obsoleted in the meantime.\n");
+		/* They'll call again */
+		goto upnout;
+	}
+	spin_unlock(&c->erase_completion_lock);
+
+	/* OK. Looks safe. And nobody can get us now because we have the semaphore. Move the block */
+	if (f->metadata && f->metadata->raw == raw) {
+		fn = f->metadata;
+		ret = jffs2_garbage_collect_metadata(c, jeb, f, fn);
+		goto upnout;
+	}
+
+	/* 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 (!nrfrags++)
+				start = frag->ofs;
+			if (nrfrags == frag->node->frags)
+				break; /* We've found them all */
+		}
+	}
+	if (fn) {
+		if (ref_flags(raw) == REF_PRISTINE) {
+			ret = jffs2_garbage_collect_pristine(c, f->inocache, raw);
+			if (!ret) {
+				/* Urgh. Return it sensibly. */
+				frag->node->raw = f->inocache->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. */
+			ret = jffs2_garbage_collect_hole(c, jeb, f, fn, start, end);
+		} else {
+			/* It could still be a hole. But we GC the page this way anyway */
+			ret = jffs2_garbage_collect_dnode(c, jeb, f, fn, start, end);
+		}
+		goto upnout;
+	}
+
+	/* Wasn't a dnode. Try dirent */
+	for (fd = f->dents; fd; fd=fd->next) {
+		if (fd->raw == raw)
+			break;
+	}
+
+	if (fd && fd->ino) {
+		ret = jffs2_garbage_collect_dirent(c, jeb, f, fd);
+	} else if (fd) {
+		ret = jffs2_garbage_collect_deletion_dirent(c, jeb, f, fd);
+	} else {
+		pr_warn("Raw node at 0x%08x wasn't in node lists for ino #%u\n",
+			ref_offset(raw), f->inocache->ino);
+		if (ref_obsolete(raw)) {
+			pr_warn("But it's obsolete so we don't mind too much\n");
+		} else {
+			jffs2_dbg_dump_node(c, ref_offset(raw));
+			BUG();
+		}
+	}
+ upnout:
+	mutex_unlock(&f->sem);
+
+	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;
+	size_t retlen;
+	int ret;
+	uint32_t phys_ofs, alloclen;
+	uint32_t crc, rawlen;
+	int retried = 0;
+
+	jffs2_dbg(1, "Going to GC REF_PRISTINE node at 0x%08x\n",
+		  ref_offset(raw));
+
+	alloclen = rawlen = ref_totlen(c, c->gcblock, 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. */
+	if (ic && alloclen > sizeof(struct jffs2_raw_inode) + JFFS2_MIN_DATA_LEN)
+		alloclen = sizeof(struct jffs2_raw_inode) + JFFS2_MIN_DATA_LEN;
+
+	ret = jffs2_reserve_space_gc(c, alloclen, &alloclen, rawlen);
+	/* 'rawlen' is not the exact summary size; it is only an upper estimation */
+
+	if (ret)
+		return ret;
+
+	if (alloclen < rawlen) {
+		/* Doesn't fit untouched. We'll go the old route and split it */
+		return -EBADFD;
+	}
+
+	node = kmalloc(rawlen, GFP_KERNEL);
+	if (!node)
+		return -ENOMEM;
+
+	ret = jffs2_flash_read(c, ref_offset(raw), rawlen, &retlen, (char *)node);
+	if (!ret && retlen != rawlen)
+		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) {
+		pr_warn("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) {
+			pr_warn("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) {
+				pr_warn("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;
+
+	case JFFS2_NODETYPE_DIRENT:
+		crc = crc32(0, node, sizeof(node->d)-8);
+		if (je32_to_cpu(node->d.node_crc) != crc) {
+			pr_warn("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 (strnlen(node->d.name, node->d.nsize) != node->d.nsize) {
+			pr_warn("Name in dirent node at 0x%08x contains zeroes\n",
+				ref_offset(raw));
+			goto bail;
+		}
+
+		if (node->d.nsize) {
+			crc = crc32(0, node->d.name, node->d.nsize);
+			if (je32_to_cpu(node->d.name_crc) != crc) {
+				pr_warn("Name 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.name_crc), crc);
+				goto bail;
+			}
+		}
+		break;
+	default:
+		/* If it's inode-less, we don't _know_ what it is. Just copy it intact */
+		if (ic) {
+			pr_warn("Unknown node type for REF_PRISTINE node at 0x%08x: 0x%04x\n",
+				ref_offset(raw), je16_to_cpu(node->u.nodetype));
+			goto bail;
+		}
+	}
+
+	/* OK, all the CRCs are good; this node can just be copied as-is. */
+ retry:
+	phys_ofs = write_ofs(c);
+
+	ret = jffs2_flash_write(c, phys_ofs, rawlen, &retlen, (char *)node);
+
+	if (ret || (retlen != rawlen)) {
+		pr_notice("Write of %d bytes at 0x%08x failed. returned %d, retlen %zd\n",
+			  rawlen, phys_ofs, ret, retlen);
+		if (retlen) {
+			jffs2_add_physical_node_ref(c, phys_ofs | REF_OBSOLETE, rawlen, NULL);
+		} else {
+			pr_notice("Not marking the space at 0x%08x as dirty because the flash driver returned retlen zero\n",
+				  phys_ofs);
+		}
+		if (!retried) {
+			/* Try to reallocate space and retry */
+			uint32_t dummy;
+			struct jffs2_eraseblock *jeb = &c->blocks[phys_ofs / c->sector_size];
+
+			retried = 1;
+
+			jffs2_dbg(1, "Retrying failed write of REF_PRISTINE node.\n");
+
+			jffs2_dbg_acct_sanity_check(c,jeb);
+			jffs2_dbg_acct_paranoia_check(c, jeb);
+
+			ret = jffs2_reserve_space_gc(c, rawlen, &dummy, rawlen);
+						/* this is not the exact summary size of it,
+							it is only an upper estimation */
+
+			if (!ret) {
+				jffs2_dbg(1, "Allocated space at 0x%08x to retry failed write.\n",
+					  phys_ofs);
+
+				jffs2_dbg_acct_sanity_check(c,jeb);
+				jffs2_dbg_acct_paranoia_check(c, jeb);
+
+				goto retry;
+			}
+			jffs2_dbg(1, "Failed to allocate space to retry failed write: %d!\n",
+				  ret);
+		}
+
+		if (!ret)
+			ret = -EIO;
+		goto out_node;
+	}
+	jffs2_add_physical_node_ref(c, phys_ofs | REF_PRISTINE, rawlen, ic);
+
+	jffs2_mark_node_obsolete(c, raw);
+	jffs2_dbg(1, "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,
+					struct jffs2_inode_info *f, struct jffs2_full_dnode *fn)
+{
+	struct jffs2_full_dnode *new_fn;
+	struct jffs2_raw_inode ri;
+	struct jffs2_node_frag *last_frag;
+	union jffs2_device_node dev;
+	char *mdata = NULL;
+	int mdatalen = 0;
+	uint32_t alloclen, ilen;
+	int ret;
+
+	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 */
+		mdatalen = jffs2_encode_dev(&dev, JFFS2_F_I_RDEV(f));
+		mdata = (char *)&dev;
+		jffs2_dbg(1, "%s(): Writing %d bytes of kdev_t\n",
+			  __func__, mdatalen);
+	} else if (S_ISLNK(JFFS2_F_I_MODE(f))) {
+		mdatalen = fn->size;
+		mdata = kmalloc(fn->size, GFP_KERNEL);
+		if (!mdata) {
+			pr_warn("kmalloc of mdata failed in jffs2_garbage_collect_metadata()\n");
+			return -ENOMEM;
+		}
+		ret = jffs2_read_dnode(c, f, fn, mdata, 0, mdatalen);
+		if (ret) {
+			pr_warn("read of old metadata failed in jffs2_garbage_collect_metadata(): %d\n",
+				ret);
+			kfree(mdata);
+			return ret;
+		}
+		jffs2_dbg(1, "%s(): Writing %d bites of symlink target\n",
+			  __func__, mdatalen);
+
+	}
+
+	ret = jffs2_reserve_space_gc(c, sizeof(ri) + mdatalen, &alloclen,
+				JFFS2_SUMMARY_INODE_SIZE);
+	if (ret) {
+		pr_warn("jffs2_reserve_space_gc of %zd bytes for garbage_collect_metadata failed: %d\n",
+			sizeof(ri) + mdatalen, ret);
+		goto out;
+	}
+
+	last_frag = frag_last(&f->fragtree);
+	if (last_frag)
+		/* Fetch the inode length from the fragtree rather then
+		 * from i_size since i_size may have not been updated yet */
+		ilen = last_frag->ofs + last_frag->size;
+	else
+		ilen = JFFS2_F_I_SIZE(f);
+
+	memset(&ri, 0, sizeof(ri));
+	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 = 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(ilen);
+	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 = 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, ALLOC_GC);
+
+	if (IS_ERR(new_fn)) {
+		pr_warn("Error writing new dnode: %ld\n", PTR_ERR(new_fn));
+		ret = PTR_ERR(new_fn);
+		goto out;
+	}
+	jffs2_mark_node_obsolete(c, fn->raw);
+	jffs2_free_full_dnode(fn);
+	f->metadata = new_fn;
+ out:
+	if (S_ISLNK(JFFS2_F_I_MODE(f)))
+		kfree(mdata);
+	return ret;
+}
+
+static int jffs2_garbage_collect_dirent(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb,
+					struct jffs2_inode_info *f, struct jffs2_full_dirent *fd)
+{
+	struct jffs2_full_dirent *new_fd;
+	struct jffs2_raw_dirent rd;
+	uint32_t alloclen;
+	int ret;
+
+	rd.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK);
+	rd.nodetype = cpu_to_je16(JFFS2_NODETYPE_DIRENT);
+	rd.nsize = strlen(fd->name);
+	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 = cpu_to_je32(f->inocache->ino);
+	rd.version = cpu_to_je32(++f->highest_version);
+	rd.ino = cpu_to_je32(fd->ino);
+	/* If the times on this inode were set by explicit utime() they can be different,
+	   so refrain from splatting them. */
+	if (JFFS2_F_I_MTIME(f) == JFFS2_F_I_CTIME(f))
+		rd.mctime = cpu_to_je32(JFFS2_F_I_MTIME(f));
+	else
+		rd.mctime = cpu_to_je32(0);
+	rd.type = fd->type;
+	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, &alloclen,
+				JFFS2_SUMMARY_DIRENT_SIZE(rd.nsize));
+	if (ret) {
+		pr_warn("jffs2_reserve_space_gc of %zd bytes for garbage_collect_dirent failed: %d\n",
+			sizeof(rd)+rd.nsize, ret);
+		return ret;
+	}
+	new_fd = jffs2_write_dirent(c, f, &rd, fd->name, rd.nsize, ALLOC_GC);
+
+	if (IS_ERR(new_fd)) {
+		pr_warn("jffs2_write_dirent in garbage_collect_dirent failed: %ld\n",
+			PTR_ERR(new_fd));
+		return PTR_ERR(new_fd);
+	}
+	jffs2_add_fd_to_list(c, new_fd, &f->dents);
+	return 0;
+}
+
+static int jffs2_garbage_collect_deletion_dirent(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb,
+					struct jffs2_inode_info *f, struct jffs2_full_dirent *fd)
+{
+	struct jffs2_full_dirent **fdp = &f->dents;
+	int found = 0;
+
+	/* On a medium where we can't actually mark nodes obsolete
+	   pernamently, such as NAND flash, we need to work out
+	   whether this deletion dirent is still needed to actively
+	   delete a 'real' dirent with the same name that's still
+	   somewhere else on the flash. */
+	if (!jffs2_can_mark_obsolete(c)) {
+		struct jffs2_raw_dirent *rd;
+		struct jffs2_raw_node_ref *raw;
+		int ret;
+		size_t retlen;
+		int name_len = strlen(fd->name);
+		uint32_t name_crc = crc32(0, fd->name, name_len);
+		uint32_t rawlen = ref_totlen(c, jeb, fd->raw);
+
+		rd = kmalloc(rawlen, GFP_KERNEL);
+		if (!rd)
+			return -ENOMEM;
+
+		/* 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... */
+		mutex_lock(&c->erase_free_sem);
+
+		for (raw = f->inocache->nodes; raw != (void *)f->inocache; raw = raw->next_in_ino) {
+
+			cond_resched();
+
+			/* We only care about obsolete ones */
+			if (!(ref_obsolete(raw)))
+				continue;
+
+			/* Any dirent with the same name is going to have the same length... */
+			if (ref_totlen(c, NULL, raw) != rawlen)
+				continue;
+
+			/* Doesn't matter if there's one in the same erase block. We're going to
+			   delete it too at the same time. */
+			if (SECTOR_ADDR(raw->flash_offset) == SECTOR_ADDR(fd->raw->flash_offset))
+				continue;
+
+			jffs2_dbg(1, "Check potential deletion dirent at %08x\n",
+				  ref_offset(raw));
+
+			/* This is an obsolete node belonging to the same directory, and it's of the right
+			   length. We need to take a closer look...*/
+			ret = jffs2_flash_read(c, ref_offset(raw), rawlen, &retlen, (char *)rd);
+			if (ret) {
+				pr_warn("%s(): Read error (%d) reading obsolete node at %08x\n",
+					__func__, ret, ref_offset(raw));
+				/* If we can't read it, we don't need to continue to obsolete it. Continue */
+				continue;
+			}
+			if (retlen != rawlen) {
+				pr_warn("%s(): Short read (%zd not %u) reading header from obsolete node at %08x\n",
+					__func__, retlen, rawlen,
+					ref_offset(raw));
+				continue;
+			}
+
+			if (je16_to_cpu(rd->nodetype) != JFFS2_NODETYPE_DIRENT)
+				continue;
+
+			/* If the name CRC doesn't match, skip */
+			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 || !je32_to_cpu(rd->ino))
+				continue;
+
+			/* OK, check the actual name now */
+			if (memcmp(rd->name, fd->name, name_len))
+				continue;
+
+			/* OK. The name really does match. There really is still an older node on
+			   the flash which our deletion dirent obsoletes. So we have to write out
+			   a new deletion dirent to replace it */
+			mutex_unlock(&c->erase_free_sem);
+
+			jffs2_dbg(1, "Deletion dirent at %08x still obsoletes real dirent \"%s\" at %08x for ino #%u\n",
+				  ref_offset(fd->raw), fd->name,
+				  ref_offset(raw), je32_to_cpu(rd->ino));
+			kfree(rd);
+
+			return jffs2_garbage_collect_dirent(c, jeb, f, fd);
+		}
+
+		mutex_unlock(&c->erase_free_sem);
+		kfree(rd);
+	}
+
+	/* FIXME: If we're deleting a dirent which contains the current mtime and ctime,
+	   we should update the metadata node with those times accordingly */
+
+	/* No need for it any more. Just mark it obsolete and remove it from the list */
+	while (*fdp) {
+		if ((*fdp) == fd) {
+			found = 1;
+			*fdp = fd->next;
+			break;
+		}
+		fdp = &(*fdp)->next;
+	}
+	if (!found) {
+		pr_warn("Deletion dirent \"%s\" not found in list for ino #%u\n",
+			fd->name, f->inocache->ino);
+	}
+	jffs2_mark_node_obsolete(c, fd->raw);
+	jffs2_free_full_dirent(fd);
+	return 0;
+}
+
+static int jffs2_garbage_collect_hole(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)
+{
+	struct jffs2_raw_inode ri;
+	struct jffs2_node_frag *frag;
+	struct jffs2_full_dnode *new_fn;
+	uint32_t alloclen, ilen;
+	int ret;
+
+	jffs2_dbg(1, "Writing replacement hole node for ino #%u from offset 0x%x to 0x%x\n",
+		  f->inocache->ino, start, end);
+
+	memset(&ri, 0, sizeof(ri));
+
+	if(fn->frags > 1) {
+		size_t readlen;
+		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, ref_offset(fn->raw), sizeof(ri), &readlen, (char *)&ri);
+		if (readlen != sizeof(ri) || ret) {
+			pr_warn("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 (je16_to_cpu(ri.nodetype) != JFFS2_NODETYPE_INODE) {
+			pr_warn("%s(): Node at 0x%08x had node type 0x%04x instead of JFFS2_NODETYPE_INODE(0x%04x)\n",
+				__func__, ref_offset(fn->raw),
+				je16_to_cpu(ri.nodetype), JFFS2_NODETYPE_INODE);
+			return -EIO;
+		}
+		if (je32_to_cpu(ri.totlen) != sizeof(ri)) {
+			pr_warn("%s(): Node at 0x%08x had totlen 0x%x instead of expected 0x%zx\n",
+				__func__, ref_offset(fn->raw),
+				je32_to_cpu(ri.totlen), sizeof(ri));
+			return -EIO;
+		}
+		crc = crc32(0, &ri, sizeof(ri)-8);
+		if (crc != je32_to_cpu(ri.node_crc)) {
+			pr_warn("%s: Node at 0x%08x had CRC 0x%08x which doesn't match calculated CRC 0x%08x\n",
+				__func__, 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 */
+			pr_warn("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) {
+			pr_warn("%s(): Node 0x%08x wasn't a hole node!\n",
+				__func__, ref_offset(fn->raw));
+			pr_warn("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 = 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 = 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;
+	}
+
+	frag = frag_last(&f->fragtree);
+	if (frag)
+		/* Fetch the inode length from the fragtree rather then
+		 * from i_size since i_size may have not been updated yet */
+		ilen = frag->ofs + frag->size;
+	else
+		ilen = JFFS2_F_I_SIZE(f);
+
+	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(ilen);
+	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), &alloclen,
+				     JFFS2_SUMMARY_INODE_SIZE);
+	if (ret) {
+		pr_warn("jffs2_reserve_space_gc of %zd bytes for garbage_collect_hole failed: %d\n",
+			sizeof(ri), ret);
+		return ret;
+	}
+	new_fn = jffs2_write_dnode(c, f, &ri, NULL, 0, ALLOC_GC);
+
+	if (IS_ERR(new_fn)) {
+		pr_warn("Error writing new hole node: %ld\n", PTR_ERR(new_fn));
+		return PTR_ERR(new_fn);
+	}
+	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;
+	}
+
+	/*
+	 * 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)) {
+			pr_warn("%s(): Replacing fn with %d frag(s) but new ver %d != highest_version %d of ino #%d\n",
+				__func__, fn->frags, je32_to_cpu(ri.version),
+				f->highest_version, je32_to_cpu(ri.ino));
+	});
+
+	/* This is a partially-overlapped hole node. Mark it REF_NORMAL not REF_PRISTINE */
+	mark_ref_normal(new_fn->raw);
+
+	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) {
+			frag->node = new_fn;
+			new_fn->frags++;
+			fn->frags--;
+		}
+	}
+	if (fn->frags) {
+		pr_warn("%s(): Old node still has frags!\n", __func__);
+		BUG();
+	}
+	if (!new_fn->frags) {
+		pr_warn("%s(): New node has no frags!\n", __func__);
+		BUG();
+	}
+
+	jffs2_mark_node_obsolete(c, fn->raw);
+	jffs2_free_full_dnode(fn);
+
+	return 0;
+}
+
+static int jffs2_garbage_collect_dnode(struct jffs2_sb_info *c, struct jffs2_eraseblock *orig_jeb,
+				       struct jffs2_inode_info *f, struct jffs2_full_dnode *fn,
+				       uint32_t start, uint32_t end)
+{
+	struct jffs2_full_dnode *new_fn;
+	struct jffs2_raw_inode ri;
+	uint32_t alloclen, offset, orig_end, orig_start;
+	int ret = 0;
+	unsigned char *comprbuf = NULL, *writebuf;
+	unsigned long pg;
+	unsigned char *pg_ptr;
+
+	memset(&ri, 0, sizeof(ri));
+
+	jffs2_dbg(1, "Writing replacement dnode for ino #%u from offset 0x%x to 0x%x\n",
+		  f->inocache->ino, start, end);
+
+	orig_end = end;
+	orig_start = start;
+
+	if (c->nr_free_blocks + c->nr_erasing_blocks > c->resv_blocks_gcmerge) {
+		/* Attempt to do some merging. But only expand to cover logically
+		   adjacent frags if the block containing them is already considered
+		   to be dirty. Otherwise we end up with GC just going round in
+		   circles dirtying the nodes it already wrote out, especially
+		   on NAND where we have small eraseblocks and hence a much higher
+		   chance of nodes having to be split to cross boundaries. */
+
+		struct jffs2_node_frag *frag;
+		uint32_t min, max;
+
+		min = start & ~(PAGE_CACHE_SIZE-1);
+		max = min + PAGE_CACHE_SIZE;
+
+		frag = jffs2_lookup_node_frag(&f->fragtree, start);
+
+		/* BUG_ON(!frag) but that'll happen anyway... */
+
+		BUG_ON(frag->ofs != start);
+
+		/* First grow down... */
+		while((frag = frag_prev(frag)) && frag->ofs >= min) {
+
+			/* If the previous frag doesn't even reach the beginning, there's
+			   excessive fragmentation. Just merge. */
+			if (frag->ofs > min) {
+				jffs2_dbg(1, "Expanding down to cover partial frag (0x%x-0x%x)\n",
+					  frag->ofs, frag->ofs+frag->size);
+				start = frag->ofs;
+				continue;
+			}
+			/* OK. This frag holds the first byte of the page. */
+			if (!frag->node || !frag->node->raw) {
+				jffs2_dbg(1, "First frag in page is hole (0x%x-0x%x). Not expanding down.\n",
+					  frag->ofs, frag->ofs+frag->size);
+				break;
+			} else {
+
+				/* OK, it's a frag which extends to the beginning of the page. Does it live
+				   in a block which is still considered clean? If so, don't obsolete it.
+				   If not, cover it anyway. */
+
+				struct jffs2_raw_node_ref *raw = frag->node->raw;
+				struct jffs2_eraseblock *jeb;
+
+				jeb = &c->blocks[raw->flash_offset / c->sector_size];
+
+				if (jeb == c->gcblock) {
+					jffs2_dbg(1, "Expanding down to cover frag (0x%x-0x%x) in gcblock at %08x\n",
+						  frag->ofs,
+						  frag->ofs + frag->size,
+						  ref_offset(raw));
+					start = frag->ofs;
+					break;
+				}
+				if (!ISDIRTY(jeb->dirty_size + jeb->wasted_size)) {
+					jffs2_dbg(1, "Not expanding down to cover frag (0x%x-0x%x) in clean block %08x\n",
+						  frag->ofs,
+						  frag->ofs + frag->size,
+						  jeb->offset);
+					break;
+				}
+
+				jffs2_dbg(1, "Expanding down to cover frag (0x%x-0x%x) in dirty block %08x\n",
+					  frag->ofs,
+					  frag->ofs + frag->size,
+					  jeb->offset);
+				start = frag->ofs;
+				break;
+			}
+		}
+
+		/* ... then up */
+
+		/* Find last frag which is actually part of the node we're to GC. */
+		frag = jffs2_lookup_node_frag(&f->fragtree, end-1);
+
+		while((frag = frag_next(frag)) && frag->ofs+frag->size <= max) {
+
+			/* If the previous frag doesn't even reach the beginning, there's lots
+			   of fragmentation. Just merge. */
+			if (frag->ofs+frag->size < max) {
+				jffs2_dbg(1, "Expanding up to cover partial frag (0x%x-0x%x)\n",
+					  frag->ofs, frag->ofs+frag->size);
+				end = frag->ofs + frag->size;
+				continue;
+			}
+
+			if (!frag->node || !frag->node->raw) {
+				jffs2_dbg(1, "Last frag in page is hole (0x%x-0x%x). Not expanding up.\n",
+					  frag->ofs, frag->ofs+frag->size);
+				break;
+			} else {
+
+				/* OK, it's a frag which extends to the beginning of the page. Does it live
+				   in a block which is still considered clean? If so, don't obsolete it.
+				   If not, cover it anyway. */
+
+				struct jffs2_raw_node_ref *raw = frag->node->raw;
+				struct jffs2_eraseblock *jeb;
+
+				jeb = &c->blocks[raw->flash_offset / c->sector_size];
+
+				if (jeb == c->gcblock) {
+					jffs2_dbg(1, "Expanding up to cover frag (0x%x-0x%x) in gcblock at %08x\n",
+						  frag->ofs,
+						  frag->ofs + frag->size,
+						  ref_offset(raw));
+					end = frag->ofs + frag->size;
+					break;
+				}
+				if (!ISDIRTY(jeb->dirty_size + jeb->wasted_size)) {
+					jffs2_dbg(1, "Not expanding up to cover frag (0x%x-0x%x) in clean block %08x\n",
+						  frag->ofs,
+						  frag->ofs + frag->size,
+						  jeb->offset);
+					break;
+				}
+
+				jffs2_dbg(1, "Expanding up to cover frag (0x%x-0x%x) in dirty block %08x\n",
+					  frag->ofs,
+					  frag->ofs + frag->size,
+					  jeb->offset);
+				end = frag->ofs + frag->size;
+				break;
+			}
+		}
+		jffs2_dbg(1, "Expanded dnode to write from (0x%x-0x%x) to (0x%x-0x%x)\n",
+			  orig_start, orig_end, start, end);
+
+		D1(BUG_ON(end > frag_last(&f->fragtree)->ofs + frag_last(&f->fragtree)->size));
+		BUG_ON(end < orig_end);
+		BUG_ON(start > orig_start);
+	}
+
+	/* First, use readpage() to read the appropriate page into the page cache */
+	/* Q: What happens if we actually try to GC the _same_ page for which commit_write()
+	 *    triggered garbage collection in the first place?
+	 * A: I _think_ it's OK. read_cache_page shouldn't deadlock, we'll write out the
+	 *    page OK. We'll actually write it out again in commit_write, which is a little
+	 *    suboptimal, but at least we're correct.
+	 */
+	pg_ptr = jffs2_gc_fetch_page(c, f, start, &pg);
+
+	if (IS_ERR(pg_ptr)) {
+		pr_warn("read_cache_page() returned error: %ld\n",
+			PTR_ERR(pg_ptr));
+		return PTR_ERR(pg_ptr);
+	}
+
+	offset = start;
+	while(offset < orig_end) {
+		uint32_t datalen;
+		uint32_t cdatalen;
+		uint16_t comprtype = JFFS2_COMPR_NONE;
+
+		ret = jffs2_reserve_space_gc(c, sizeof(ri) + JFFS2_MIN_DATA_LEN,
+					&alloclen, JFFS2_SUMMARY_INODE_SIZE);
+
+		if (ret) {
+			pr_warn("jffs2_reserve_space_gc of %zd bytes for garbage_collect_dnode failed: %d\n",
+				sizeof(ri) + JFFS2_MIN_DATA_LEN, ret);
+			break;
+		}
+		cdatalen = min_t(uint32_t, alloclen - sizeof(ri), end - offset);
+		datalen = end - offset;
+
+		writebuf = pg_ptr + (offset & (PAGE_CACHE_SIZE -1));
+
+		comprtype = jffs2_compress(c, f, writebuf, &comprbuf, &datalen, &cdatalen);
+
+		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 = 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 & 0xff;
+		ri.usercompr = (comprtype >> 8) & 0xff;
+		ri.node_crc = cpu_to_je32(crc32(0, &ri, sizeof(ri)-8));
+		ri.data_crc = cpu_to_je32(crc32(0, comprbuf, cdatalen));
+
+		new_fn = jffs2_write_dnode(c, f, &ri, comprbuf, cdatalen, ALLOC_GC);
+
+		jffs2_free_comprbuf(comprbuf, writebuf);
+
+		if (IS_ERR(new_fn)) {
+			pr_warn("Error writing new dnode: %ld\n",
+				PTR_ERR(new_fn));
+			ret = PTR_ERR(new_fn);
+			break;
+		}
+		ret = jffs2_add_full_dnode_to_inode(c, f, new_fn);
+		offset += datalen;
+		if (f->metadata) {
+			jffs2_mark_node_obsolete(c, f->metadata->raw);
+			jffs2_free_full_dnode(f->metadata);
+			f->metadata = NULL;
+		}
+	}
+
+	jffs2_gc_release_page(c, pg_ptr, &pg);
+	return ret;
+}
Index: cpukit/libfs/src/jffs2/src/jffs2_fs_i.h
===================================================================
--- cpukit/libfs/src/jffs2/src/jffs2_fs_i.h	(revision 0c0f128e11007cb0d9ebed8b27dbb7cdde18450d)
+++ cpukit/libfs/src/jffs2/src/jffs2_fs_i.h	(revision 0c0f128e11007cb0d9ebed8b27dbb7cdde18450d)
@@ -0,0 +1,56 @@
+/*
+ * JFFS2 -- Journalling Flash File System, Version 2.
+ *
+ * Copyright Â© 2001-2007 Red Hat, Inc.
+ * Copyright Â© 2004-2010 David Woodhouse <dwmw2@infradead.org>
+ *
+ * Created by David Woodhouse <dwmw2@infradead.org>
+ *
+ * For licensing information, see the file 'LICENCE' in this directory.
+ *
+ */
+
+#ifndef _JFFS2_FS_I
+#define _JFFS2_FS_I
+
+#include <linux/rbtree.h>
+#include <linux/posix_acl.h>
+#include <linux/mutex.h>
+
+struct jffs2_inode_info {
+	/* We need an internal mutex similar to inode->i_mutex.
+	   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_mutex
+	   for the inode(s) which are already locked */
+	struct mutex 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;
+
+	/* The target path if this is the inode of a symlink */
+	unsigned char *target;
+
+	/* 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;
+	struct inode vfs_inode;
+};
+
+#endif /* _JFFS2_FS_I */
Index: cpukit/libfs/src/jffs2/src/jffs2_fs_sb.h
===================================================================
--- cpukit/libfs/src/jffs2/src/jffs2_fs_sb.h	(revision 0c0f128e11007cb0d9ebed8b27dbb7cdde18450d)
+++ cpukit/libfs/src/jffs2/src/jffs2_fs_sb.h	(revision 0c0f128e11007cb0d9ebed8b27dbb7cdde18450d)
@@ -0,0 +1,164 @@
+/*
+ * JFFS2 -- Journalling Flash File System, Version 2.
+ *
+ * Copyright Â© 2001-2007 Red Hat, Inc.
+ * Copyright Â© 2004-2010 David Woodhouse <dwmw2@infradead.org>
+ *
+ * Created by David Woodhouse <dwmw2@infradead.org>
+ *
+ * For licensing information, see the file 'LICENCE' in this directory.
+ *
+ */
+
+#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 <linux/mutex.h>
+#include <linux/timer.h>
+#include <linux/wait.h>
+#include <linux/list.h>
+#include <linux/rwsem.h>
+
+#define JFFS2_SB_FLAG_RO 1
+#define JFFS2_SB_FLAG_SCANNING 2 /* Flash scanning is in progress */
+#define JFFS2_SB_FLAG_BUILDING 4 /* File system building is in progress */
+
+struct jffs2_inodirty;
+
+struct jffs2_mount_opts {
+	bool override_compr;
+	unsigned int compr;
+
+	/* The size of the reserved pool. The reserved pool is the JFFS2 flash
+	 * space which may only be used by root cannot be used by the other
+	 * users. This is implemented simply by means of not allowing the
+	 * latter users to write to the file system if the amount if the
+	 * available space is less then 'rp_size'. */
+	unsigned int rp_size;
+};
+
+/* 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 completion gc_thread_start; /* GC thread start completion */
+	struct completion gc_thread_exit; /* GC thread exit completion port */
+
+	struct mutex 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;
+
+	/* Number of free blocks there must be before we... */
+	uint8_t resv_blocks_write;	/* ... allow a normal filesystem write */
+	uint8_t resv_blocks_deletion;	/* ... allow a normal filesystem deletion */
+	uint8_t resv_blocks_gctrigger;	/* ... wake up the GC thread */
+	uint8_t resv_blocks_gcbad;	/* ... pick a block from the bad_list to GC */
+	uint8_t resv_blocks_gcmerge;	/* ... merge pages when garbage collecting */
+	/* Number of 'very dirty' blocks before we trigger immediate GC */
+	uint8_t vdirty_blocks_gctrigger;
+
+	uint32_t nospc_dirty_size;
+
+	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_checking_list;	/* Blocks which are being checked and marked */
+	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;
+	int inocache_hashsize;
+	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 mutex erase_free_sem;
+
+	uint32_t wbuf_pagesize; /* 0 for NOR and other flashes with no wbuf */
+
+#ifdef CONFIG_JFFS2_FS_WBUF_VERIFY
+	unsigned char *wbuf_verify; /* read-back buffer for verification */
+#endif
+#ifdef CONFIG_JFFS2_FS_WRITEBUFFER
+	unsigned char *wbuf; /* Write-behind buffer for NAND flash */
+	uint32_t wbuf_ofs;
+	uint32_t wbuf_len;
+	struct jffs2_inodirty *wbuf_inodes;
+	struct rw_semaphore wbuf_sem;	/* Protects the write buffer */
+
+	struct delayed_work wbuf_dwork; /* write-buffer write-out work */
+	int wbuf_queued;                /* non-zero delayed work is queued */
+	spinlock_t wbuf_dwork_lock;     /* protects wbuf_dwork and and wbuf_queued */
+
+	unsigned char *oobbuf;
+	int oobavail; /* How many bytes are available for JFFS2 in OOB */
+#endif
+
+	struct jffs2_summary *summary;		/* Summary information */
+	struct jffs2_mount_opts mount_opts;
+
+#ifdef CONFIG_JFFS2_FS_XATTR
+#define XATTRINDEX_HASHSIZE	(57)
+	uint32_t highest_xid;
+	uint32_t highest_xseqno;
+	struct list_head xattrindex[XATTRINDEX_HASHSIZE];
+	struct list_head xattr_unchecked;
+	struct list_head xattr_dead_list;
+	struct jffs2_xattr_ref *xref_dead_list;
+	struct jffs2_xattr_ref *xref_temp;
+	struct rw_semaphore xattr_sem;
+	uint32_t xdatum_mem_usage;
+	uint32_t xdatum_mem_threshold;
+#endif
+	/* OS-private pointer for getting back to master superblock info */
+	void *os_priv;
+};
+
+#endif /* _JFFS2_FS_SB */
Index: cpukit/libfs/src/jffs2/src/nodelist.c
===================================================================
--- cpukit/libfs/src/jffs2/src/nodelist.c	(revision 0c0f128e11007cb0d9ebed8b27dbb7cdde18450d)
+++ cpukit/libfs/src/jffs2/src/nodelist.c	(revision 0c0f128e11007cb0d9ebed8b27dbb7cdde18450d)
@@ -0,0 +1,779 @@
+/*
+ * JFFS2 -- Journalling Flash File System, Version 2.
+ *
+ * Copyright Â© 2001-2007 Red Hat, Inc.
+ *
+ * Created by David Woodhouse <dwmw2@infradead.org>
+ *
+ * For licensing information, see the file 'LICENCE' in this directory.
+ *
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/fs.h>
+#include <linux/mtd/mtd.h>
+#include <linux/rbtree.h>
+#include <linux/crc32.h>
+#include <linux/pagemap.h>
+#include "nodelist.h"
+
+static void jffs2_obsolete_node_frag(struct jffs2_sb_info *c,
+				     struct jffs2_node_frag *this);
+
+void jffs2_add_fd_to_list(struct jffs2_sb_info *c, struct jffs2_full_dirent *new, struct jffs2_full_dirent **list)
+{
+	struct jffs2_full_dirent **prev = list;
+
+	dbg_dentlist("add dirent \"%s\", ino #%u\n", new->name, new->ino);
+
+	while ((*prev) && (*prev)->nhash <= new->nhash) {
+		if ((*prev)->nhash == new->nhash && !strcmp((*prev)->name, new->name)) {
+			/* Duplicate. Free one */
+			if (new->version < (*prev)->version) {
+				dbg_dentlist("Eep! Marking new dirent node obsolete, old is \"%s\", ino #%u\n",
+					(*prev)->name, (*prev)->ino);
+				jffs2_mark_node_obsolete(c, new->raw);
+				jffs2_free_full_dirent(new);
+			} else {
+				dbg_dentlist("marking old dirent \"%s\", ino #%u obsolete\n",
+					(*prev)->name, (*prev)->ino);
+				new->next = (*prev)->next;
+				/* It may have been a 'placeholder' deletion dirent, 
+				   if jffs2_can_mark_obsolete() (see jffs2_do_unlink()) */
+				if ((*prev)->raw)
+					jffs2_mark_node_obsolete(c, ((*prev)->raw));
+				jffs2_free_full_dirent(*prev);
+				*prev = new;
+			}
+			return;
+		}
+		prev = &((*prev)->next);
+	}
+	new->next = *prev;
+	*prev = new;
+}
+
+uint32_t jffs2_truncate_fragtree(struct jffs2_sb_info *c, struct rb_root *list, uint32_t size)
+{
+	struct jffs2_node_frag *frag = jffs2_lookup_node_frag(list, size);
+
+	dbg_fragtree("truncating fragtree to 0x%08x bytes\n", size);
+
+	/* We know frag->ofs <= size. That's what lookup does for us */
+	if (frag && frag->ofs != size) {
+		if (frag->ofs+frag->size > size) {
+			frag->size = size - frag->ofs;
+		}
+		frag = frag_next(frag);
+	}
+	while (frag && frag->ofs >= size) {
+		struct jffs2_node_frag *next = frag_next(frag);
+
+		frag_erase(frag, list);
+		jffs2_obsolete_node_frag(c, frag);
+		frag = next;
+	}
+
+	if (size == 0)
+		return 0;
+
+	frag = frag_last(list);
+
+	/* Sanity check for truncation to longer than we started with... */
+	if (!frag)
+		return 0;
+	if (frag->ofs + frag->size < size)
+		return frag->ofs + frag->size;
+
+	/* If the last fragment starts at the RAM page boundary, it is
+	 * REF_PRISTINE irrespective of its size. */
+	if (frag->node && (frag->ofs & (PAGE_CACHE_SIZE - 1)) == 0) {
+		dbg_fragtree2("marking the last fragment 0x%08x-0x%08x REF_PRISTINE.\n",
+			frag->ofs, frag->ofs + frag->size);
+		frag->node->raw->flash_offset = ref_offset(frag->node->raw) | REF_PRISTINE;
+	}
+	return size;
+}
+
+static void jffs2_obsolete_node_frag(struct jffs2_sb_info *c,
+				     struct jffs2_node_frag *this)
+{
+	if (this->node) {
+		this->node->frags--;
+		if (!this->node->frags) {
+			/* The node has no valid frags left. It's totally obsoleted */
+			dbg_fragtree2("marking old node @0x%08x (0x%04x-0x%04x) obsolete\n",
+				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 {
+			dbg_fragtree2("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);
+}
+
+static 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;
+
+	dbg_fragtree2("insert frag (0x%04x-0x%04x)\n", newfrag->ofs, newfrag->ofs + newfrag->size);
+
+	while (*link) {
+		parent = *link;
+		base = rb_entry(parent, struct jffs2_node_frag, rb);
+
+		if (newfrag->ofs > base->ofs)
+			link = &base->rb.rb_right;
+		else if (newfrag->ofs < base->ofs)
+			link = &base->rb.rb_left;
+		else {
+			JFFS2_ERROR("duplicate frag at %08x (%p,%p)\n", newfrag->ofs, newfrag, base);
+			BUG();
+		}
+	}
+
+	rb_link_node(&newfrag->rb, &base->rb, link);
+}
+
+/*
+ * Allocate and initializes a new fragment.
+ */
+static struct jffs2_node_frag * new_fragment(struct jffs2_full_dnode *fn, uint32_t ofs, uint32_t size)
+{
+	struct jffs2_node_frag *newfrag;
+
+	newfrag = jffs2_alloc_node_frag();
+	if (likely(newfrag)) {
+		newfrag->ofs = ofs;
+		newfrag->size = size;
+		newfrag->node = fn;
+	} else {
+		JFFS2_ERROR("cannot allocate a jffs2_node_frag object\n");
+	}
+
+	return newfrag;
+}
+
+/*
+ * Called when there is no overlapping fragment exist. Inserts a hole before the new
+ * fragment and inserts the new fragment to the fragtree.
+ */
+static int no_overlapping_node(struct jffs2_sb_info *c, struct rb_root *root,
+		 	       struct jffs2_node_frag *newfrag,
+			       struct jffs2_node_frag *this, uint32_t lastend)
+{
+	if (lastend < newfrag->node->ofs) {
+		/* put a hole in before the new fragment */
+		struct jffs2_node_frag *holefrag;
+
+		holefrag= new_fragment(NULL, lastend, newfrag->node->ofs - lastend);
+		if (unlikely(!holefrag)) {
+			jffs2_free_node_frag(newfrag);
+			return -ENOMEM;
+		}
+
+		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 */
+			dbg_fragtree2("add hole frag %#04x-%#04x on the right of the new frag.\n",
+				holefrag->ofs, holefrag->ofs + holefrag->size);
+			rb_link_node(&holefrag->rb, &this->rb, &this->rb.rb_right);
+		} else {
+			dbg_fragtree2("Add hole frag %#04x-%#04x to the root of the tree.\n",
+				holefrag->ofs, holefrag->ofs + holefrag->size);
+			rb_link_node(&holefrag->rb, NULL, &root->rb_node);
+		}
+		rb_insert_color(&holefrag->rb, root);
+		this = holefrag;
+	}
+
+	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 new fragment */
+		dbg_fragtree2("add the new node at the right\n");
+		rb_link_node(&newfrag->rb, &this->rb, &this->rb.rb_right);
+	} else {
+		dbg_fragtree2("insert the new node at the root of the tree\n");
+		rb_link_node(&newfrag->rb, NULL, &root->rb_node);
+	}
+	rb_insert_color(&newfrag->rb, root);
+
+	return 0;
+}
+
+/* Doesn't set inode->i_size */
+static int jffs2_add_frag_to_fragtree(struct jffs2_sb_info *c, struct rb_root *root, struct jffs2_node_frag *newfrag)
+{
+	struct jffs2_node_frag *this;
+	uint32_t lastend;
+
+	/* Skip all the nodes which are completed before this one starts */
+	this = jffs2_lookup_node_frag(root, newfrag->node->ofs);
+
+	if (this) {
+		dbg_fragtree2("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 {
+		dbg_fragtree2("lookup gave no frag\n");
+		lastend = 0;
+	}
+
+	/* See if we ran off the end of the fragtree */
+	if (lastend <= newfrag->ofs) {
+		/* We did */
+
+		/* 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 && (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);
+		}
+
+		return no_overlapping_node(c, root, newfrag, this, lastend);
+	}
+
+	if (this->node)
+		dbg_fragtree2("dealing with frag %u-%u, phys %#08x(%d).\n",
+		this->ofs, this->ofs + this->size,
+		ref_offset(this->node->raw), ref_flags(this->node->raw));
+	else
+		dbg_fragtree2("dealing with hole frag %u-%u.\n",
+		this->ofs, this->ofs + this->size);
+
+	/* 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 (newfrag->ofs > this->ofs) {
+		/* This node isn't completely obsoleted. The start of it remains valid */
+
+		/* 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 */
+			struct jffs2_node_frag *newfrag2;
+
+			if (this->node)
+				dbg_fragtree2("split old frag 0x%04x-0x%04x, phys 0x%08x\n",
+					this->ofs, this->ofs+this->size, ref_offset(this->node->raw));
+			else
+				dbg_fragtree2("split old hole frag 0x%04x-0x%04x\n",
+					this->ofs, this->ofs+this->size);
+
+			/* New second frag pointing to this's node */
+			newfrag2 = new_fragment(this->node, newfrag->ofs + newfrag->size,
+						this->ofs + this->size - newfrag->ofs - newfrag->size);
+			if (unlikely(!newfrag2))
+				return -ENOMEM;
+			if (this->node)
+				this->node->frags++;
+
+			/* 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, root);
+
+			jffs2_fragtree_insert(newfrag2, newfrag);
+			rb_insert_color(&newfrag2->rb, root);
+
+			return 0;
+		}
+		/* New node just reduces 'this' frag in size, doesn't split it */
+		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, root);
+	} else {
+		/* New frag starts at the same point as 'this' used to. Replace
+		   it in the tree without doing a delete and insertion */
+		dbg_fragtree2("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, root);
+
+		if (newfrag->ofs + newfrag->size >= this->ofs+this->size) {
+			dbg_fragtree2("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, root);
+			return 0;
+		}
+	}
+	/* 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 = frag_next(newfrag)) && newfrag->ofs + newfrag->size >= this->ofs + this->size) {
+		/* 'this' frag is obsoleted completely. */
+		dbg_fragtree2("obsoleting node frag %p (%x-%x) and removing from tree\n",
+			this, this->ofs, this->ofs+this->size);
+		rb_erase(&this->rb, root);
+		jffs2_obsolete_node_frag(c, this);
+	}
+	/* Now we're pointing at the first frag which isn't totally obsoleted by
+	   the new frag */
+
+	if (!this || newfrag->ofs + newfrag->size == this->ofs)
+		return 0;
+
+	/* 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;
+}
+
+/*
+ * Given an inode, probably with existing tree of fragments, add the new node
+ * to the fragment tree.
+ */
+int jffs2_add_full_dnode_to_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, struct jffs2_full_dnode *fn)
+{
+	int ret;
+	struct jffs2_node_frag *newfrag;
+
+	if (unlikely(!fn->size))
+		return 0;
+
+	newfrag = new_fragment(fn, fn->ofs, fn->size);
+	if (unlikely(!newfrag))
+		return -ENOMEM;
+	newfrag->node->frags = 1;
+
+	dbg_fragtree("adding node %#04x-%#04x @0x%08x on flash, newfrag *%p\n",
+		  fn->ofs, fn->ofs+fn->size, ref_offset(fn->raw), newfrag);
+
+	ret = jffs2_add_frag_to_fragtree(c, &f->fragtree, newfrag);
+	if (unlikely(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);
+		}
+	}
+	jffs2_dbg_fragtree_paranoia_check_nolock(f);
+
+	return 0;
+}
+
+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, uint32_t ino)
+{
+	struct jffs2_inode_cache *ret;
+
+	ret = c->inocache_list[ino % c->inocache_hashsize];
+	while (ret && ret->ino < ino) {
+		ret = ret->next;
+	}
+
+	if (ret && ret->ino != ino)
+		ret = NULL;
+
+	return ret;
+}
+
+void jffs2_add_ino_cache (struct jffs2_sb_info *c, struct jffs2_inode_cache *new)
+{
+	struct jffs2_inode_cache **prev;
+
+	spin_lock(&c->inocache_lock);
+	if (!new->ino)
+		new->ino = ++c->highest_ino;
+
+	dbg_inocache("add %p (ino #%u)\n", new, new->ino);
+
+	prev = &c->inocache_list[new->ino % c->inocache_hashsize];
+
+	while ((*prev) && (*prev)->ino < new->ino) {
+		prev = &(*prev)->next;
+	}
+	new->next = *prev;
+	*prev = new;
+
+	spin_unlock(&c->inocache_lock);
+}
+
+void jffs2_del_ino_cache(struct jffs2_sb_info *c, struct jffs2_inode_cache *old)
+{
+	struct jffs2_inode_cache **prev;
+
+#ifdef CONFIG_JFFS2_FS_XATTR
+	BUG_ON(old->xref);
+#endif
+	dbg_inocache("del %p (ino #%u)\n", old, old->ino);
+	spin_lock(&c->inocache_lock);
+
+	prev = &c->inocache_list[old->ino % c->inocache_hashsize];
+
+	while ((*prev) && (*prev)->ino < old->ino) {
+		prev = &(*prev)->next;
+	}
+	if ((*prev) == old) {
+		*prev = old->next;
+	}
+
+	/* Free it now unless it's in READING or CLEARING state, which
+	   are the transitions upon read_inode() and clear_inode(). The
+	   rest of the time we know nobody else is looking at it, and
+	   if it's held by read_inode() or clear_inode() they'll free it
+	   for themselves. */
+	if (old->state != INO_STATE_READING && old->state != INO_STATE_CLEARING)
+		jffs2_free_inode_cache(old);
+
+	spin_unlock(&c->inocache_lock);
+}
+
+void jffs2_free_ino_caches(struct jffs2_sb_info *c)
+{
+	int i;
+	struct jffs2_inode_cache *this, *next;
+
+	for (i=0; i < c->inocache_hashsize; i++) {
+		this = c->inocache_list[i];
+		while (this) {
+			next = this->next;
+			jffs2_xattr_free_inode(c, this);
+			jffs2_free_inode_cache(this);
+			this = next;
+		}
+		c->inocache_list[i] = NULL;
+	}
+}
+
+void jffs2_free_raw_node_refs(struct jffs2_sb_info *c)
+{
+	int i;
+	struct jffs2_raw_node_ref *this, *next;
+
+	for (i=0; i<c->nr_blocks; i++) {
+		this = c->blocks[i].first_node;
+		while (this) {
+			if (this[REFS_PER_BLOCK].flash_offset == REF_LINK_NODE)
+				next = this[REFS_PER_BLOCK].next_in_ino;
+			else
+				next = NULL;
+
+			jffs2_free_refblock(this);
+			this = next;
+		}
+		c->blocks[i].first_node = c->blocks[i].last_node = NULL;
+	}
+}
+
+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;
+
+	dbg_fragtree2("root %p, offset %d\n", fragtree, offset);
+
+	next = fragtree->rb_node;
+
+	while(next) {
+		frag = rb_entry(next, struct jffs2_node_frag, rb);
+
+		if (frag->ofs + frag->size <= offset) {
+			/* 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) {
+			next = frag->rb.rb_left;
+		} else {
+			return frag;
+		}
+	}
+
+	/* Exact match not found. Go back up looking at each parent,
+	   and return the closest smaller one */
+
+	if (prev)
+		dbg_fragtree2("no match. Returning frag %#04x-%#04x, closest previous\n",
+			  prev->ofs, prev->ofs+prev->size);
+	else
+		dbg_fragtree2("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;
+
+	dbg_fragtree("killing\n");
+
+	frag = (rb_entry(root->rb_node, struct jffs2_node_frag, rb));
+	while(frag) {
+		if (frag->rb.rb_left) {
+			frag = frag_left(frag);
+			continue;
+		}
+		if (frag->rb.rb_right) {
+			frag = frag_right(frag);
+			continue;
+		}
+
+		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;
+
+		cond_resched();
+	}
+}
+
+struct jffs2_raw_node_ref *jffs2_link_node_ref(struct jffs2_sb_info *c,
+					       struct jffs2_eraseblock *jeb,
+					       uint32_t ofs, uint32_t len,
+					       struct jffs2_inode_cache *ic)
+{
+	struct jffs2_raw_node_ref *ref;
+
+	BUG_ON(!jeb->allocated_refs);
+	jeb->allocated_refs--;
+
+	ref = jeb->last_node;
+
+	dbg_noderef("Last node at %p is (%08x,%p)\n", ref, ref->flash_offset,
+		    ref->next_in_ino);
+
+	while (ref->flash_offset != REF_EMPTY_NODE) {
+		if (ref->flash_offset == REF_LINK_NODE)
+			ref = ref->next_in_ino;
+		else
+			ref++;
+	}
+
+	dbg_noderef("New ref is %p (%08x becomes %08x,%p) len 0x%x\n", ref, 
+		    ref->flash_offset, ofs, ref->next_in_ino, len);
+
+	ref->flash_offset = ofs;
+
+	if (!jeb->first_node) {
+		jeb->first_node = ref;
+		BUG_ON(ref_offset(ref) != jeb->offset);
+	} else if (unlikely(ref_offset(ref) != jeb->offset + c->sector_size - jeb->free_size)) {
+		uint32_t last_len = ref_totlen(c, jeb, jeb->last_node);
+
+		JFFS2_ERROR("Adding new ref %p at (0x%08x-0x%08x) not immediately after previous (0x%08x-0x%08x)\n",
+			    ref, ref_offset(ref), ref_offset(ref)+len,
+			    ref_offset(jeb->last_node), 
+			    ref_offset(jeb->last_node)+last_len);
+		BUG();
+	}
+	jeb->last_node = ref;
+
+	if (ic) {
+		ref->next_in_ino = ic->nodes;
+		ic->nodes = ref;
+	} else {
+		ref->next_in_ino = NULL;
+	}
+
+	switch(ref_flags(ref)) {
+	case REF_UNCHECKED:
+		c->unchecked_size += len;
+		jeb->unchecked_size += len;
+		break;
+
+	case REF_NORMAL:
+	case REF_PRISTINE:
+		c->used_size += len;
+		jeb->used_size += len;
+		break;
+
+	case REF_OBSOLETE:
+		c->dirty_size += len;
+		jeb->dirty_size += len;
+		break;
+	}
+	c->free_size -= len;
+	jeb->free_size -= len;
+
+#ifdef TEST_TOTLEN
+	/* Set (and test) __totlen field... for now */
+	ref->__totlen = len;
+	ref_totlen(c, jeb, ref);
+#endif
+	return ref;
+}
+
+/* No locking, no reservation of 'ref'. Do not use on a live file system */
+int jffs2_scan_dirty_space(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb,
+			   uint32_t size)
+{
+	if (!size)
+		return 0;
+	if (unlikely(size > jeb->free_size)) {
+		pr_crit("Dirty space 0x%x larger then free_size 0x%x (wasted 0x%x)\n",
+			size, jeb->free_size, jeb->wasted_size);
+		BUG();
+	}
+	/* REF_EMPTY_NODE is !obsolete, so that works OK */
+	if (jeb->last_node && ref_obsolete(jeb->last_node)) {
+#ifdef TEST_TOTLEN
+		jeb->last_node->__totlen += size;
+#endif
+		c->dirty_size += size;
+		c->free_size -= size;
+		jeb->dirty_size += size;
+		jeb->free_size -= size;
+	} else {
+		uint32_t ofs = jeb->offset + c->sector_size - jeb->free_size;
+		ofs |= REF_OBSOLETE;
+
+		jffs2_link_node_ref(c, jeb, ofs, size, NULL);
+	}
+
+	return 0;
+}
+
+/* Calculate totlen from surrounding nodes or eraseblock */
+static inline uint32_t __ref_totlen(struct jffs2_sb_info *c,
+				    struct jffs2_eraseblock *jeb,
+				    struct jffs2_raw_node_ref *ref)
+{
+	uint32_t ref_end;
+	struct jffs2_raw_node_ref *next_ref = ref_next(ref);
+
+	if (next_ref)
+		ref_end = ref_offset(next_ref);
+	else {
+		if (!jeb)
+			jeb = &c->blocks[ref->flash_offset / c->sector_size];
+
+		/* Last node in block. Use free_space */
+		if (unlikely(ref != jeb->last_node)) {
+			pr_crit("ref %p @0x%08x is not jeb->last_node (%p @0x%08x)\n",
+				ref, ref_offset(ref), jeb->last_node,
+				jeb->last_node ?
+				ref_offset(jeb->last_node) : 0);
+			BUG();
+		}
+		ref_end = jeb->offset + c->sector_size - jeb->free_size;
+	}
+	return ref_end - ref_offset(ref);
+}
+
+uint32_t __jffs2_ref_totlen(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb,
+			    struct jffs2_raw_node_ref *ref)
+{
+	uint32_t ret;
+
+	ret = __ref_totlen(c, jeb, ref);
+
+#ifdef TEST_TOTLEN
+	if (unlikely(ret != ref->__totlen)) {
+		if (!jeb)
+			jeb = &c->blocks[ref->flash_offset / c->sector_size];
+
+		pr_crit("Totlen for ref at %p (0x%08x-0x%08x) miscalculated as 0x%x instead of %x\n",
+			ref, ref_offset(ref), ref_offset(ref) + ref->__totlen,
+			ret, ref->__totlen);
+		if (ref_next(ref)) {
+			pr_crit("next %p (0x%08x-0x%08x)\n",
+				ref_next(ref), ref_offset(ref_next(ref)),
+				ref_offset(ref_next(ref)) + ref->__totlen);
+		} else 
+			pr_crit("No next ref. jeb->last_node is %p\n",
+				jeb->last_node);
+
+		pr_crit("jeb->wasted_size %x, dirty_size %x, used_size %x, free_size %x\n",
+			jeb->wasted_size, jeb->dirty_size, jeb->used_size,
+			jeb->free_size);
+
+#if defined(JFFS2_DBG_DUMPS) || defined(JFFS2_DBG_PARANOIA_CHECKS)
+		__jffs2_dbg_dump_node_refs_nolock(c, jeb);
+#endif
+
+		WARN_ON(1);
+
+		ret = ref->__totlen;
+	}
+#endif /* TEST_TOTLEN */
+	return ret;
+}
Index: cpukit/libfs/src/jffs2/src/nodelist.h
===================================================================
--- cpukit/libfs/src/jffs2/src/nodelist.h	(revision 0c0f128e11007cb0d9ebed8b27dbb7cdde18450d)
+++ cpukit/libfs/src/jffs2/src/nodelist.h	(revision 0c0f128e11007cb0d9ebed8b27dbb7cdde18450d)
@@ -0,0 +1,480 @@
+/*
+ * JFFS2 -- Journalling Flash File System, Version 2.
+ *
+ * Copyright Â© 2001-2007 Red Hat, Inc.
+ *
+ * Created by David Woodhouse <dwmw2@infradead.org>
+ *
+ * For licensing information, see the file 'LICENCE' in this directory.
+ *
+ */
+
+#ifndef __JFFS2_NODELIST_H__
+#define __JFFS2_NODELIST_H__
+
+#include <linux/fs.h>
+#include <linux/types.h>
+#include <linux/jffs2.h>
+#include "jffs2_fs_sb.h"
+#include "jffs2_fs_i.h"
+#include "xattr.h"
+#include "acl.h"
+#include "summary.h"
+
+#ifdef __ECOS
+#include "os-ecos.h"
+#else
+#include "os-linux.h"
+#endif
+
+#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 constant_cpu_to_je16(x) ((jint16_t){x})
+#define constant_cpu_to_je32(x) ((jint32_t){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 constant_cpu_to_je16(x) ((jint16_t){__constant_cpu_to_be16(x)})
+#define constant_cpu_to_je32(x) ((jint32_t){__constant_cpu_to_be32(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 constant_cpu_to_je16(x) ((jint16_t){__constant_cpu_to_le16(x)})
+#define constant_cpu_to_je32(x) ((jint32_t){__constant_cpu_to_le32(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
+
+/* The minimal node header size */
+#define JFFS2_MIN_NODE_HEADER sizeof(struct jffs2_raw_dirent)
+
+/*
+  This is all we need to keep in-core for each raw node during normal
+  operation. As and when we do read_inode on a particular inode, we can
+  scan the nodes which are listed for it and build up a proper map of
+  which nodes are currently valid. JFFSv1 always used to keep that whole
+  map in core for each inode.
+*/
+struct jffs2_raw_node_ref
+{
+	struct jffs2_raw_node_ref *next_in_ino; /* Points to the next raw_node_ref
+		for this object. If this _is_ the last, it points to the inode_cache,
+		xattr_ref or xattr_datum instead. The common part of those structures
+		has NULL in the first word. See jffs2_raw_ref_to_ic() below */
+	uint32_t flash_offset;
+#undef TEST_TOTLEN
+#ifdef TEST_TOTLEN
+	uint32_t __totlen; /* This may die; use ref_totlen(c, jeb, ) below */
+#endif
+};
+
+#define REF_LINK_NODE ((int32_t)-1)
+#define REF_EMPTY_NODE ((int32_t)-2)
+
+/* Use blocks of about 256 bytes */
+#define REFS_PER_BLOCK ((255/sizeof(struct jffs2_raw_node_ref))-1)
+
+static inline struct jffs2_raw_node_ref *ref_next(struct jffs2_raw_node_ref *ref)
+{
+	ref++;
+
+	/* Link to another block of refs */
+	if (ref->flash_offset == REF_LINK_NODE) {
+		ref = ref->next_in_ino;
+		if (!ref)
+			return ref;
+	}
+
+	/* End of chain */
+	if (ref->flash_offset == REF_EMPTY_NODE)
+		return NULL;
+
+	return ref;
+}
+
+static inline struct jffs2_inode_cache *jffs2_raw_ref_to_ic(struct jffs2_raw_node_ref *raw)
+{
+	while(raw->next_in_ino)
+		raw = raw->next_in_ino;
+
+	/* NB. This can be a jffs2_xattr_datum or jffs2_xattr_ref and
+	   not actually a jffs2_inode_cache. Check ->class */
+	return ((struct jffs2_inode_cache *)raw);
+}
+
+	/* 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, which indicate the node's status; see below: */
+#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)
+
+/* Dirent nodes should be REF_PRISTINE only if they are not a deletion
+   dirent. Deletion dirents should be REF_NORMAL so that GC gets to
+   throw them away when appropriate */
+#define dirent_node_state(rd)	( (je32_to_cpu((rd)->ino)?REF_PRISTINE:REF_NORMAL) )
+
+/* NB: REF_PRISTINE for an inode-less node (ref->next_in_ino == NULL) indicates
+   it is an unknown node of type JFFS2_NODETYPE_RWCOMPAT_COPY, so it'll get
+   copied. If you need to do anything different to GC inode-less nodes, then
+   you need to modify gc.c accordingly. */
+
+/* For each inode in the filesystem, we need to keep a record of
+   nlink, because it would be a PITA to scan the whole directory tree
+   at read_inode() time to calculate it, and to keep sufficient information
+   in the raw_node_ref (basically both parent and child inode number for
+   dirent nodes) would take more space than this does. We also keep
+   a pointer to the first physical node which is part of this inode, too.
+*/
+struct jffs2_inode_cache {
+	/* First part of structure is shared with other objects which
+	   can terminate the raw node refs' next_in_ino list -- which
+	   currently struct jffs2_xattr_datum and struct jffs2_xattr_ref. */
+
+	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_raw_node_ref *nodes;
+	uint8_t class;	/* It's used for identification */
+
+	/* end of shared structure */
+
+	uint8_t flags;
+	uint16_t state;
+	uint32_t ino;
+	struct jffs2_inode_cache *next;
+#ifdef CONFIG_JFFS2_FS_XATTR
+	struct jffs2_xattr_ref *xref;
+#endif
+	uint32_t pino_nlink;	/* Directories store parent inode
+				   here; other inodes store nlink.
+				   Zero always means that it's
+				   completely unlinked. */
+};
+
+/* 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 INO_STATE_CLEARING	6	/* In clear_inode() */
+
+#define INO_FLAGS_XATTR_CHECKED	0x01	/* has no duplicate xattr_ref */
+
+#define RAWNODE_CLASS_INODE_CACHE	0
+#define RAWNODE_CLASS_XATTR_DATUM	1
+#define RAWNODE_CLASS_XATTR_REF		2
+
+#define INOCACHE_HASHSIZE_MIN 128
+#define INOCACHE_HASHSIZE_MAX 1024
+
+#define write_ofs(c) ((c)->nextblock->offset + (c)->sector_size - (c)->nextblock->free_size)
+
+/*
+  Larger representation of a raw node, kept in-core only when the
+  struct inode for this particular ino is instantiated.
+*/
+
+struct jffs2_full_dnode
+{
+	struct jffs2_raw_node_ref *raw;
+	uint32_t ofs; /* The offset to which the data of this node belongs */
+	uint32_t size;
+	uint32_t frags; /* Number of fragments which currently refer
+			to this node. When this reaches zero,
+			the node is obsolete.  */
+};
+
+/*
+   Even larger representation of a raw node, kept in-core only while
+   we're actually building up the original map of which nodes go where,
+   in read_inode()
+*/
+struct jffs2_tmp_dnode_info
+{
+	struct rb_node rb;
+	struct jffs2_full_dnode *fn;
+	uint32_t version;
+	uint32_t data_crc;
+	uint32_t partial_crc;
+	uint16_t csize;
+	uint16_t overlapped;
+};
+
+/* Temporary data structure used during readinode. */
+struct jffs2_readinode_info
+{
+	struct rb_root tn_root;
+	struct jffs2_tmp_dnode_info *mdata_tn;
+	uint32_t highest_version;
+	uint32_t latest_mctime;
+	uint32_t mctime_ver;
+	struct jffs2_full_dirent *fds;
+	struct jffs2_raw_node_ref *latest_ref;
+};
+
+struct jffs2_full_dirent
+{
+	struct jffs2_raw_node_ref *raw;
+	struct jffs2_full_dirent *next;
+	uint32_t version;
+	uint32_t ino; /* == zero for unlink */
+	unsigned int nhash;
+	unsigned char type;
+	unsigned char name[0];
+};
+
+/*
+  Fragments - used to build a map of which raw node to obtain
+  data from for each part of the ino
+*/
+struct jffs2_node_frag
+{
+	struct rb_node rb;
+	struct jffs2_full_dnode *node; /* NULL for holes */
+	uint32_t size;
+	uint32_t ofs; /* The offset to which this fragment belongs */
+};
+
+struct jffs2_eraseblock
+{
+	struct list_head list;
+	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 */
+	uint32_t allocated_refs;
+	struct jffs2_raw_node_ref *first_node;
+	struct jffs2_raw_node_ref *last_node;
+
+	struct jffs2_raw_node_ref *gc_node;	/* Next node to be garbage collected */
+};
+
+static inline int jffs2_blocks_use_vmalloc(struct jffs2_sb_info *c)
+{
+	return ((c->flash_size / c->sector_size) * sizeof (struct jffs2_eraseblock)) > (128 * 1024);
+}
+
+#define ref_totlen(a, b, c) __jffs2_ref_totlen((a), (b), (c))
+
+#define ALLOC_NORMAL	0	/* Normal allocation */
+#define ALLOC_DELETION	1	/* Deletion node. Best to allow it */
+#define ALLOC_GC	2	/* Space requested for GC. Give it or die */
+#define ALLOC_NORETRY	3	/* For jffs2_write_dnode: On failure, return -EAGAIN instead of retrying */
+
+/* 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_encode_dev(union jffs2_device_node *jdev, dev_t rdev)
+{
+	if (old_valid_dev(rdev)) {
+		jdev->old_id = cpu_to_je16(old_encode_dev(rdev));
+		return sizeof(jdev->old_id);
+	} else {
+		jdev->new_id = cpu_to_je32(new_encode_dev(rdev));
+		return sizeof(jdev->new_id);
+	}
+}
+
+static inline struct jffs2_node_frag *frag_first(struct rb_root *root)
+{
+	struct rb_node *node = rb_first(root);
+
+	if (!node)
+		return NULL;
+
+	return rb_entry(node, struct jffs2_node_frag, rb);
+}
+
+static inline struct jffs2_node_frag *frag_last(struct rb_root *root)
+{
+	struct rb_node *node = rb_last(root);
+
+	if (!node)
+		return NULL;
+
+	return rb_entry(node, struct jffs2_node_frag, rb);
+}
+
+#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);
+
+#define tn_next(tn) rb_entry(rb_next(&(tn)->rb), struct jffs2_tmp_dnode_info, rb)
+#define tn_prev(tn) rb_entry(rb_prev(&(tn)->rb), struct jffs2_tmp_dnode_info, rb)
+#define tn_parent(tn) rb_entry(rb_parent(&(tn)->rb), struct jffs2_tmp_dnode_info, rb)
+#define tn_left(tn) rb_entry((tn)->rb.rb_left, struct jffs2_tmp_dnode_info, rb)
+#define tn_right(tn) rb_entry((tn)->rb.rb_right, struct jffs2_tmp_dnode_info, rb)
+#define tn_erase(tn, list) rb_erase(&tn->rb, list);
+#define tn_last(list) rb_entry(rb_last(list), struct jffs2_tmp_dnode_info, rb)
+#define tn_first(list) rb_entry(rb_first(list), struct jffs2_tmp_dnode_info, rb)
+
+/* nodelist.c */
+void jffs2_add_fd_to_list(struct jffs2_sb_info *c, struct jffs2_full_dirent *new, struct jffs2_full_dirent **list);
+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, uint32_t 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);
+int jffs2_add_full_dnode_to_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, struct jffs2_full_dnode *fn);
+uint32_t jffs2_truncate_fragtree (struct jffs2_sb_info *c, struct rb_root *list, uint32_t size);
+struct jffs2_raw_node_ref *jffs2_link_node_ref(struct jffs2_sb_info *c,
+					       struct jffs2_eraseblock *jeb,
+					       uint32_t ofs, uint32_t len,
+					       struct jffs2_inode_cache *ic);
+extern uint32_t __jffs2_ref_totlen(struct jffs2_sb_info *c,
+				   struct jffs2_eraseblock *jeb,
+				   struct jffs2_raw_node_ref *ref);
+
+/* nodemgmt.c */
+int jffs2_thread_should_wake(struct jffs2_sb_info *c);
+int jffs2_reserve_space(struct jffs2_sb_info *c, uint32_t minsize,
+			uint32_t *len, int prio, uint32_t sumsize);
+int jffs2_reserve_space_gc(struct jffs2_sb_info *c, uint32_t minsize,
+			uint32_t *len, uint32_t sumsize);
+struct jffs2_raw_node_ref *jffs2_add_physical_node_ref(struct jffs2_sb_info *c, 
+						       uint32_t ofs, uint32_t len,
+						       struct jffs2_inode_cache *ic);
+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);
+
+/* write.c */
+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, int alloc_mode);
+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, int alloc_mode);
+int jffs2_write_inode_range(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
+			    struct jffs2_raw_inode *ri, unsigned char *buf,
+			    uint32_t offset, uint32_t writelen, uint32_t *retlen);
+int jffs2_do_create(struct jffs2_sb_info *c, struct jffs2_inode_info *dir_f, struct jffs2_inode_info *f,
+		    struct jffs2_raw_inode *ri, const struct qstr *qstr);
+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, uint32_t time);
+int jffs2_do_link(struct jffs2_sb_info *c, struct jffs2_inode_info *dir_f, uint32_t ino,
+		   uint8_t type, const char *name, int namelen, uint32_t time);
+
+
+/* readinode.c */
+int jffs2_do_read_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
+			uint32_t ino, struct jffs2_raw_inode *latest_node);
+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 */
+int jffs2_create_slab_caches(void);
+void jffs2_destroy_slab_caches(void);
+
+struct jffs2_full_dirent *jffs2_alloc_full_dirent(int namesize);
+void jffs2_free_full_dirent(struct jffs2_full_dirent *);
+struct jffs2_full_dnode *jffs2_alloc_full_dnode(void);
+void jffs2_free_full_dnode(struct jffs2_full_dnode *);
+struct jffs2_raw_dirent *jffs2_alloc_raw_dirent(void);
+void jffs2_free_raw_dirent(struct jffs2_raw_dirent *);
+struct jffs2_raw_inode *jffs2_alloc_raw_inode(void);
+void jffs2_free_raw_inode(struct jffs2_raw_inode *);
+struct jffs2_tmp_dnode_info *jffs2_alloc_tmp_dnode_info(void);
+void jffs2_free_tmp_dnode_info(struct jffs2_tmp_dnode_info *);
+int jffs2_prealloc_raw_node_refs(struct jffs2_sb_info *c,
+				 struct jffs2_eraseblock *jeb, int nr);
+void jffs2_free_refblock(struct jffs2_raw_node_ref *);
+struct jffs2_node_frag *jffs2_alloc_node_frag(void);
+void jffs2_free_node_frag(struct jffs2_node_frag *);
+struct jffs2_inode_cache *jffs2_alloc_inode_cache(void);
+void jffs2_free_inode_cache(struct jffs2_inode_cache *);
+#ifdef CONFIG_JFFS2_FS_XATTR
+struct jffs2_xattr_datum *jffs2_alloc_xattr_datum(void);
+void jffs2_free_xattr_datum(struct jffs2_xattr_datum *);
+struct jffs2_xattr_ref *jffs2_alloc_xattr_ref(void);
+void jffs2_free_xattr_ref(struct jffs2_xattr_ref *);
+#endif
+
+/* gc.c */
+int jffs2_garbage_collect_pass(struct jffs2_sb_info *c);
+
+/* read.c */
+int jffs2_read_dnode(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
+		     struct jffs2_full_dnode *fd, unsigned char *buf,
+		     int ofs, int len);
+int jffs2_read_inode_range(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
+			   unsigned char *buf, uint32_t offset, uint32_t len);
+char *jffs2_getlink(struct jffs2_sb_info *c, struct jffs2_inode_info *f);
+
+/* scan.c */
+int jffs2_scan_medium(struct jffs2_sb_info *c);
+void jffs2_rotate_lists(struct jffs2_sb_info *c);
+struct jffs2_inode_cache *jffs2_scan_make_ino_cache(struct jffs2_sb_info *c, uint32_t ino);
+int jffs2_scan_classify_jeb(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb);
+int jffs2_scan_dirty_space(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, uint32_t size);
+
+/* build.c */
+int jffs2_do_mount_fs(struct jffs2_sb_info *c);
+
+/* erase.c */
+int jffs2_erase_pending_blocks(struct jffs2_sb_info *c, int count);
+void jffs2_free_jeb_node_refs(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb);
+
+#ifdef CONFIG_JFFS2_FS_WRITEBUFFER
+/* wbuf.c */
+int jffs2_flush_wbuf_gc(struct jffs2_sb_info *c, uint32_t ino);
+int jffs2_flush_wbuf_pad(struct jffs2_sb_info *c);
+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);
+#endif
+
+#include "debug.h"
+
+#endif /* __JFFS2_NODELIST_H__ */
Index: cpukit/libfs/src/jffs2/src/nodemgmt.c
===================================================================
--- cpukit/libfs/src/jffs2/src/nodemgmt.c	(revision 0c0f128e11007cb0d9ebed8b27dbb7cdde18450d)
+++ cpukit/libfs/src/jffs2/src/nodemgmt.c	(revision 0c0f128e11007cb0d9ebed8b27dbb7cdde18450d)
@@ -0,0 +1,877 @@
+/*
+ * JFFS2 -- Journalling Flash File System, Version 2.
+ *
+ * Copyright Â© 2001-2007 Red Hat, Inc.
+ *
+ * Created by David Woodhouse <dwmw2@infradead.org>
+ *
+ * For licensing information, see the file 'LICENCE' in this directory.
+ *
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/kernel.h>
+#include <linux/mtd/mtd.h>
+#include <linux/compiler.h>
+#include <linux/sched.h> /* For cond_resched() */
+#include "nodelist.h"
+#include "debug.h"
+
+/*
+ * Check whether the user is allowed to write.
+ */
+static int jffs2_rp_can_write(struct jffs2_sb_info *c)
+{
+	uint32_t avail;
+	struct jffs2_mount_opts *opts = &c->mount_opts;
+
+	avail = c->dirty_size + c->free_size + c->unchecked_size +
+		c->erasing_size - c->resv_blocks_write * c->sector_size
+		- c->nospc_dirty_size;
+
+	if (avail < 2 * opts->rp_size)
+		jffs2_dbg(1, "rpsize %u, dirty_size %u, free_size %u, "
+			  "erasing_size %u, unchecked_size %u, "
+			  "nr_erasing_blocks %u, avail %u, resrv %u\n",
+			  opts->rp_size, c->dirty_size, c->free_size,
+			  c->erasing_size, c->unchecked_size,
+			  c->nr_erasing_blocks, avail, c->nospc_dirty_size);
+
+	if (avail > opts->rp_size)
+		return 1;
+
+	/* Always allow root */
+	if (capable(CAP_SYS_RESOURCE))
+		return 1;
+
+	jffs2_dbg(1, "forbid writing\n");
+	return 0;
+}
+
+/**
+ *	jffs2_reserve_space - request physical space to write nodes to flash
+ *	@c: superblock info
+ *	@minsize: Minimum acceptable size of allocation
+ *	@len: Returned value of allocation length
+ *	@prio: Allocation type - ALLOC_{NORMAL,DELETION}
+ *
+ *	Requests a block of physical space on the flash. Returns zero for success
+ *	and puts 'len' into the appropriate place, or returns -ENOSPC or other 
+ *	error if appropriate. Doesn't return len since that's 
+ *
+ *	If it returns zero, jffs2_reserve_space() also downs the per-filesystem
+ *	allocation semaphore, to prevent more than one allocation from being
+ *	active at any time. The semaphore is later released by jffs2_commit_allocation()
+ *
+ *	jffs2_reserve_space() may trigger garbage collection in order to make room
+ *	for the requested allocation.
+ */
+
+static int jffs2_do_reserve_space(struct jffs2_sb_info *c,  uint32_t minsize,
+				  uint32_t *len, uint32_t sumsize);
+
+int jffs2_reserve_space(struct jffs2_sb_info *c, uint32_t minsize,
+			uint32_t *len, int prio, uint32_t sumsize)
+{
+	int ret = -EAGAIN;
+	int blocksneeded = c->resv_blocks_write;
+	/* align it */
+	minsize = PAD(minsize);
+
+	jffs2_dbg(1, "%s(): Requested 0x%x bytes\n", __func__, minsize);
+	mutex_lock(&c->alloc_sem);
+
+	jffs2_dbg(1, "%s(): alloc sem got\n", __func__);
+
+	spin_lock(&c->erase_completion_lock);
+
+	/*
+	 * Check if the free space is greater then size of the reserved pool.
+	 * If not, only allow root to proceed with writing.
+	 */
+	if (prio != ALLOC_DELETION && !jffs2_rp_can_write(c)) {
+		ret = -ENOSPC;
+		goto out;
+	}
+
+	/* this needs a little more thought (true <tglx> :)) */
+	while(ret == -EAGAIN) {
+		while(c->nr_free_blocks + c->nr_erasing_blocks < blocksneeded) {
+			uint32_t dirty, avail;
+
+			/* calculate real dirty size
+			 * dirty_size contains blocks on erase_pending_list
+			 * those blocks are counted in c->nr_erasing_blocks.
+			 * If one block is actually erased, it is not longer counted as dirty_space
+			 * but it is counted in c->nr_erasing_blocks, so we add it and subtract it
+			 * with c->nr_erasing_blocks * c->sector_size again.
+			 * Blocks on erasable_list are counted as dirty_size, but not in c->nr_erasing_blocks
+			 * This helps us to force gc and pick eventually a clean block to spread the load.
+			 * We add unchecked_size here, as we hopefully will find some space to use.
+			 * This will affect the sum only once, as gc first finishes checking
+			 * of nodes.
+			 */
+			dirty = c->dirty_size + c->erasing_size - c->nr_erasing_blocks * c->sector_size + c->unchecked_size;
+			if (dirty < c->nospc_dirty_size) {
+				if (prio == ALLOC_DELETION && c->nr_free_blocks + c->nr_erasing_blocks >= c->resv_blocks_deletion) {
+					jffs2_dbg(1, "%s(): Low on dirty space to GC, but it's a deletion. Allowing...\n",
+						  __func__);
+					break;
+				}
+				jffs2_dbg(1, "dirty size 0x%08x + unchecked_size 0x%08x < nospc_dirty_size 0x%08x, returning -ENOSPC\n",
+					  dirty, c->unchecked_size,
+					  c->sector_size);
+
+				spin_unlock(&c->erase_completion_lock);
+				mutex_unlock(&c->alloc_sem);
+				return -ENOSPC;
+			}
+
+			/* Calc possibly available space. Possibly available means that we
+			 * don't know, if unchecked size contains obsoleted nodes, which could give us some
+			 * more usable space. This will affect the sum only once, as gc first finishes checking
+			 * of nodes.
+			 + Return -ENOSPC, if the maximum possibly available space is less or equal than
+			 * blocksneeded * sector_size.
+			 * This blocks endless gc looping on a filesystem, which is nearly full, even if
+			 * the check above passes.
+			 */
+			avail = c->free_size + c->dirty_size + c->erasing_size + c->unchecked_size;
+			if ( (avail / c->sector_size) <= blocksneeded) {
+				if (prio == ALLOC_DELETION && c->nr_free_blocks + c->nr_erasing_blocks >= c->resv_blocks_deletion) {
+					jffs2_dbg(1, "%s(): Low on possibly available space, but it's a deletion. Allowing...\n",
+						  __func__);
+					break;
+				}
+
+				jffs2_dbg(1, "max. available size 0x%08x  < blocksneeded * sector_size 0x%08x, returning -ENOSPC\n",
+					  avail, blocksneeded * c->sector_size);
+				spin_unlock(&c->erase_completion_lock);
+				mutex_unlock(&c->alloc_sem);
+				return -ENOSPC;
+			}
+
+			mutex_unlock(&c->alloc_sem);
+
+			jffs2_dbg(1, "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 == -EAGAIN) {
+				spin_lock(&c->erase_completion_lock);
+				if (c->nr_erasing_blocks &&
+				    list_empty(&c->erase_pending_list) &&
+				    list_empty(&c->erase_complete_list)) {
+					DECLARE_WAITQUEUE(wait, current);
+					set_current_state(TASK_UNINTERRUPTIBLE);
+					add_wait_queue(&c->erase_wait, &wait);
+					jffs2_dbg(1, "%s waiting for erase to complete\n",
+						  __func__);
+					spin_unlock(&c->erase_completion_lock);
+
+					schedule();
+				} else
+					spin_unlock(&c->erase_completion_lock);
+			} else if (ret)
+				return ret;
+
+			cond_resched();
+
+			if (signal_pending(current))
+				return -EINTR;
+
+			mutex_lock(&c->alloc_sem);
+			spin_lock(&c->erase_completion_lock);
+		}
+
+		ret = jffs2_do_reserve_space(c, minsize, len, sumsize);
+		if (ret) {
+			jffs2_dbg(1, "%s(): ret is %d\n", __func__, ret);
+		}
+	}
+
+out:
+	spin_unlock(&c->erase_completion_lock);
+	if (!ret)
+		ret = jffs2_prealloc_raw_node_refs(c, c->nextblock, 1);
+	if (ret)
+		mutex_unlock(&c->alloc_sem);
+	return ret;
+}
+
+int jffs2_reserve_space_gc(struct jffs2_sb_info *c, uint32_t minsize,
+			   uint32_t *len, uint32_t sumsize)
+{
+	int ret = -EAGAIN;
+	minsize = PAD(minsize);
+
+	jffs2_dbg(1, "%s(): Requested 0x%x bytes\n", __func__, minsize);
+
+	spin_lock(&c->erase_completion_lock);
+	while(ret == -EAGAIN) {
+		ret = jffs2_do_reserve_space(c, minsize, len, sumsize);
+		if (ret) {
+			jffs2_dbg(1, "%s(): looping, ret is %d\n",
+				  __func__, ret);
+		}
+	}
+	spin_unlock(&c->erase_completion_lock);
+	if (!ret)
+		ret = jffs2_prealloc_raw_node_refs(c, c->nextblock, 1);
+
+	return ret;
+}
+
+
+/* Classify nextblock (clean, dirty of verydirty) and force to select an other one */
+
+static void jffs2_close_nextblock(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb)
+{
+
+	if (c->nextblock == NULL) {
+		jffs2_dbg(1, "%s(): Erase block at 0x%08x has already been placed in a list\n",
+			  __func__, jeb->offset);
+		return;
+	}
+	/* 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)) {
+			jffs2_dbg(1, "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 {
+			jffs2_dbg(1, "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 {
+		jffs2_dbg(1, "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->clean_list);
+	}
+	c->nextblock = NULL;
+
+}
+
+/* Select a new jeb for nextblock */
+
+static int jffs2_find_nextblock(struct jffs2_sb_info *c)
+{
+	struct list_head *next;
+
+	/* Take the next block off the 'free' list */
+
+	if (list_empty(&c->free_list)) {
+
+		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_move_tail(&ejeb->list, &c->erase_pending_list);
+			c->nr_erasing_blocks++;
+			jffs2_garbage_collect_trigger(c);
+			jffs2_dbg(1, "%s(): Triggering erase of erasable block at 0x%08x\n",
+				  __func__, ejeb->offset);
+		}
+
+		if (!c->nr_erasing_blocks &&
+			!list_empty(&c->erasable_pending_wbuf_list)) {
+			jffs2_dbg(1, "%s(): Flushing write buffer\n",
+				  __func__);
+			/* c->nextblock is NULL, no update to c->nextblock allowed */
+			spin_unlock(&c->erase_completion_lock);
+			jffs2_flush_wbuf_pad(c);
+			spin_lock(&c->erase_completion_lock);
+			/* Have another go. It'll be on the erasable_list now */
+			return -EAGAIN;
+		}
+
+		if (!c->nr_erasing_blocks) {
+			/* Ouch. We're in GC, or we wouldn't have got here.
+			   And there's no space left. At all. */
+			pr_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;
+		}
+
+		spin_unlock(&c->erase_completion_lock);
+		/* Don't wait for it; just erase one right now */
+		jffs2_erase_pending_blocks(c, 1);
+		spin_lock(&c->erase_completion_lock);
+
+		/* An erase may have failed, decreasing the
+		   amount of free space available. So we must
+		   restart from the beginning */
+		return -EAGAIN;
+	}
+
+	next = c->free_list.next;
+	list_del(next);
+	c->nextblock = list_entry(next, struct jffs2_eraseblock, list);
+	c->nr_free_blocks--;
+
+	jffs2_sum_reset_collected(c->summary); /* reset collected summary */
+
+#ifdef CONFIG_JFFS2_FS_WRITEBUFFER
+	/* adjust write buffer offset, else we get a non contiguous write bug */
+	if (!(c->wbuf_ofs % c->sector_size) && !c->wbuf_len)
+		c->wbuf_ofs = 0xffffffff;
+#endif
+
+	jffs2_dbg(1, "%s(): new nextblock = 0x%08x\n",
+		  __func__, c->nextblock->offset);
+
+	return 0;
+}
+
+/* Called with alloc sem _and_ erase_completion_lock */
+static int jffs2_do_reserve_space(struct jffs2_sb_info *c, uint32_t minsize,
+				  uint32_t *len, uint32_t sumsize)
+{
+	struct jffs2_eraseblock *jeb = c->nextblock;
+	uint32_t reserved_size;				/* for summary information at the end of the jeb */
+	int ret;
+
+ restart:
+	reserved_size = 0;
+
+	if (jffs2_sum_active() && (sumsize != JFFS2_SUMMARY_NOSUM_SIZE)) {
+							/* NOSUM_SIZE means not to generate summary */
+
+		if (jeb) {
+			reserved_size = PAD(sumsize + c->summary->sum_size + JFFS2_SUMMARY_FRAME_SIZE);
+			dbg_summary("minsize=%d , jeb->free=%d ,"
+						"summary->size=%d , sumsize=%d\n",
+						minsize, jeb->free_size,
+						c->summary->sum_size, sumsize);
+		}
+
+		/* Is there enough space for writing out the current node, or we have to
+		   write out summary information now, close this jeb and select new nextblock? */
+		if (jeb && (PAD(minsize) + PAD(c->summary->sum_size + sumsize +
+					JFFS2_SUMMARY_FRAME_SIZE) > jeb->free_size)) {
+
+			/* Has summary been disabled for this jeb? */
+			if (jffs2_sum_is_disabled(c->summary)) {
+				sumsize = JFFS2_SUMMARY_NOSUM_SIZE;
+				goto restart;
+			}
+
+			/* Writing out the collected summary information */
+			dbg_summary("generating summary for 0x%08x.\n", jeb->offset);
+			ret = jffs2_sum_write_sumnode(c);
+
+			if (ret)
+				return ret;
+
+			if (jffs2_sum_is_disabled(c->summary)) {
+				/* jffs2_write_sumnode() couldn't write out the summary information
+				   diabling summary for this jeb and free the collected information
+				 */
+				sumsize = JFFS2_SUMMARY_NOSUM_SIZE;
+				goto restart;
+			}
+
+			jffs2_close_nextblock(c, jeb);
+			jeb = NULL;
+			/* keep always valid value in reserved_size */
+			reserved_size = PAD(sumsize + c->summary->sum_size + JFFS2_SUMMARY_FRAME_SIZE);
+		}
+	} else {
+		if (jeb && minsize > jeb->free_size) {
+			uint32_t waste;
+
+			/* Skip the end of this block and file it as having some dirty space */
+			/* If there's a pending write to it, flush now */
+
+			if (jffs2_wbuf_dirty(c)) {
+				spin_unlock(&c->erase_completion_lock);
+				jffs2_dbg(1, "%s(): Flushing write buffer\n",
+					  __func__);
+				jffs2_flush_wbuf_pad(c);
+				spin_lock(&c->erase_completion_lock);
+				jeb = c->nextblock;
+				goto restart;
+			}
+
+			spin_unlock(&c->erase_completion_lock);
+
+			ret = jffs2_prealloc_raw_node_refs(c, jeb, 1);
+
+			/* Just lock it again and continue. Nothing much can change because
+			   we hold c->alloc_sem anyway. In fact, it's not entirely clear why
+			   we hold c->erase_completion_lock in the majority of this function...
+			   but that's a question for another (more caffeine-rich) day. */
+			spin_lock(&c->erase_completion_lock);
+
+			if (ret)
+				return ret;
+
+			waste = jeb->free_size;
+			jffs2_link_node_ref(c, jeb,
+					    (jeb->offset + c->sector_size - waste) | REF_OBSOLETE,
+					    waste, NULL);
+			/* FIXME: that made it count as dirty. Convert to wasted */
+			jeb->dirty_size -= waste;
+			c->dirty_size -= waste;
+			jeb->wasted_size += waste;
+			c->wasted_size += waste;
+
+			jffs2_close_nextblock(c, jeb);
+			jeb = NULL;
+		}
+	}
+
+	if (!jeb) {
+
+		ret = jffs2_find_nextblock(c);
+		if (ret)
+			return ret;
+
+		jeb = c->nextblock;
+
+		if (jeb->free_size != c->sector_size - c->cleanmarker_size) {
+			pr_warn("Eep. Block 0x%08x taken from free_list had free_size of 0x%08x!!\n",
+				jeb->offset, jeb->free_size);
+			goto restart;
+		}
+	}
+	/* OK, jeb (==c->nextblock) is now pointing at a block which definitely has
+	   enough space */
+	*len = jeb->free_size - reserved_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);
+	}
+
+	jffs2_dbg(1, "%s(): Giving 0x%x bytes at 0x%x\n",
+		  __func__,
+		  *len, jeb->offset + (c->sector_size - jeb->free_size));
+	return 0;
+}
+
+/**
+ *	jffs2_add_physical_node_ref - add a physical node reference to the list
+ *	@c: superblock info
+ *	@new: new node reference to add
+ *	@len: length of this physical node
+ *
+ *	Should only be used to report nodes for which space has been allocated
+ *	by jffs2_reserve_space.
+ *
+ *	Must be called with the alloc_sem held.
+ */
+
+struct jffs2_raw_node_ref *jffs2_add_physical_node_ref(struct jffs2_sb_info *c,
+						       uint32_t ofs, uint32_t len,
+						       struct jffs2_inode_cache *ic)
+{
+	struct jffs2_eraseblock *jeb;
+	struct jffs2_raw_node_ref *new;
+
+	jeb = &c->blocks[ofs / c->sector_size];
+
+	jffs2_dbg(1, "%s(): Node at 0x%x(%d), size 0x%x\n",
+		  __func__, ofs & ~3, ofs & 3, len);
+#if 1
+	/* Allow non-obsolete nodes only to be added at the end of c->nextblock, 
+	   if c->nextblock is set. Note that wbuf.c will file obsolete nodes
+	   even after refiling c->nextblock */
+	if ((c->nextblock || ((ofs & 3) != REF_OBSOLETE))
+	    && (jeb != c->nextblock || (ofs & ~3) != jeb->offset + (c->sector_size - jeb->free_size))) {
+		pr_warn("argh. node added in wrong place at 0x%08x(%d)\n",
+			ofs & ~3, ofs & 3);
+		if (c->nextblock)
+			pr_warn("nextblock 0x%08x", c->nextblock->offset);
+		else
+			pr_warn("No nextblock");
+		pr_cont(", expected at %08x\n",
+			jeb->offset + (c->sector_size - jeb->free_size));
+		return ERR_PTR(-EINVAL);
+	}
+#endif
+	spin_lock(&c->erase_completion_lock);
+
+	new = jffs2_link_node_ref(c, jeb, ofs, len, ic);
+
+	if (!jeb->free_size && !jeb->dirty_size && !ISDIRTY(jeb->wasted_size)) {
+		/* If it lives on the dirty_list, jffs2_reserve_space will put it there */
+		jffs2_dbg(1, "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 (jffs2_wbuf_dirty(c)) {
+			/* Flush the last write in the block if it's outstanding */
+			spin_unlock(&c->erase_completion_lock);
+			jffs2_flush_wbuf_pad(c);
+			spin_lock(&c->erase_completion_lock);
+		}
+
+		list_add_tail(&jeb->list, &c->clean_list);
+		c->nextblock = NULL;
+	}
+	jffs2_dbg_acct_sanity_check_nolock(c,jeb);
+	jffs2_dbg_acct_paranoia_check_nolock(c, jeb);
+
+	spin_unlock(&c->erase_completion_lock);
+
+	return new;
+}
+
+
+void jffs2_complete_reservation(struct jffs2_sb_info *c)
+{
+	jffs2_dbg(1, "jffs2_complete_reservation()\n");
+	spin_lock(&c->erase_completion_lock);
+	jffs2_garbage_collect_trigger(c);
+	spin_unlock(&c->erase_completion_lock);
+	mutex_unlock(&c->alloc_sem);
+}
+
+static inline int on_list(struct list_head *obj, struct list_head *head)
+{
+	struct list_head *this;
+
+	list_for_each(this, head) {
+		if (this == obj) {
+			jffs2_dbg(1, "%p is on list at %p\n", obj, head);
+			return 1;
+
+		}
+	}
+	return 0;
+}
+
+void jffs2_mark_node_obsolete(struct jffs2_sb_info *c, struct jffs2_raw_node_ref *ref)
+{
+	struct jffs2_eraseblock *jeb;
+	int blocknr;
+	struct jffs2_unknown_node n;
+	int ret, addedsize;
+	size_t retlen;
+	uint32_t freed_len;
+
+	if(unlikely(!ref)) {
+		pr_notice("EEEEEK. jffs2_mark_node_obsolete called with NULL node\n");
+		return;
+	}
+	if (ref_obsolete(ref)) {
+		jffs2_dbg(1, "%s(): called with already obsolete node at 0x%08x\n",
+			  __func__, ref_offset(ref));
+		return;
+	}
+	blocknr = ref->flash_offset / c->sector_size;
+	if (blocknr >= c->nr_blocks) {
+		pr_notice("raw node at 0x%08x is off the end of device!\n",
+			  ref->flash_offset);
+		BUG();
+	}
+	jeb = &c->blocks[blocknr];
+
+	if (jffs2_can_mark_obsolete(c) && !jffs2_is_readonly(c) &&
+	    !(c->flags & (JFFS2_SB_FLAG_SCANNING | JFFS2_SB_FLAG_BUILDING))) {
+		/* Hm. This may confuse static lock analysis. If any of the above
+		   three conditions is false, we're going to return from this
+		   function without actually obliterating any nodes or freeing
+		   any jffs2_raw_node_refs. So we don't need to stop erases from
+		   happening, or protect against people holding an obsolete
+		   jffs2_raw_node_ref without the erase_completion_lock. */
+		mutex_lock(&c->erase_free_sem);
+	}
+
+	spin_lock(&c->erase_completion_lock);
+
+	freed_len = ref_totlen(c, jeb, ref);
+
+	if (ref_flags(ref) == REF_UNCHECKED) {
+		D1(if (unlikely(jeb->unchecked_size < freed_len)) {
+				pr_notice("raw unchecked node of size 0x%08x freed from erase block %d at 0x%08x, but unchecked_size was already 0x%08x\n",
+					  freed_len, blocknr,
+					  ref->flash_offset, jeb->used_size);
+			BUG();
+		})
+			jffs2_dbg(1, "Obsoleting previously unchecked node at 0x%08x of len %x\n",
+				  ref_offset(ref), freed_len);
+		jeb->unchecked_size -= freed_len;
+		c->unchecked_size -= freed_len;
+	} else {
+		D1(if (unlikely(jeb->used_size < freed_len)) {
+				pr_notice("raw node of size 0x%08x freed from erase block %d at 0x%08x, but used_size was already 0x%08x\n",
+					  freed_len, blocknr,
+					  ref->flash_offset, jeb->used_size);
+			BUG();
+		})
+			jffs2_dbg(1, "Obsoleting node at 0x%08x of len %#x: ",
+				  ref_offset(ref), freed_len);
+		jeb->used_size -= freed_len;
+		c->used_size -= freed_len;
+	}
+
+	// Take care, that wasted size is taken into concern
+	if ((jeb->dirty_size || ISDIRTY(jeb->wasted_size + freed_len)) && jeb != c->nextblock) {
+		jffs2_dbg(1, "Dirtying\n");
+		addedsize = freed_len;
+		jeb->dirty_size += freed_len;
+		c->dirty_size += freed_len;
+
+		/* Convert wasted space to dirty, if not a bad block */
+		if (jeb->wasted_size) {
+			if (on_list(&jeb->list, &c->bad_used_list)) {
+				jffs2_dbg(1, "Leaving block at %08x on the bad_used_list\n",
+					  jeb->offset);
+				addedsize = 0; /* To fool the refiling code later */
+			} else {
+				jffs2_dbg(1, "Converting %d bytes of wasted space to dirty in block at %08x\n",
+					  jeb->wasted_size, jeb->offset);
+				addedsize += 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;
+			}
+		}
+	} else {
+		jffs2_dbg(1, "Wasting\n");
+		addedsize = 0;
+		jeb->wasted_size += freed_len;
+		c->wasted_size += freed_len;
+	}
+	ref->flash_offset = ref_offset(ref) | REF_OBSOLETE;
+
+	jffs2_dbg_acct_sanity_check_nolock(c, jeb);
+	jffs2_dbg_acct_paranoia_check_nolock(c, jeb);
+
+	if (c->flags & JFFS2_SB_FLAG_SCANNING) {
+		/* Flash scanning is 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);
+		/* We didn't lock the erase_free_sem */
+		return;
+	}
+
+	if (jeb == c->nextblock) {
+		jffs2_dbg(2, "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) {
+			jffs2_dbg(1, "gcblock at 0x%08x completely dirtied. Clearing gcblock...\n",
+				  jeb->offset);
+			c->gcblock = NULL;
+		} else {
+			jffs2_dbg(1, "Eraseblock at 0x%08x completely dirtied. Removing from (dirty?) list...\n",
+				  jeb->offset);
+			list_del(&jeb->list);
+		}
+		if (jffs2_wbuf_dirty(c)) {
+			jffs2_dbg(1, "...and adding to erasable_pending_wbuf_list\n");
+			list_add_tail(&jeb->list, &c->erasable_pending_wbuf_list);
+		} else {
+			if (jiffies & 127) {
+				/* Most of the time, we just erase it immediately. Otherwise we
+				   spend ages scanning it on mount, etc. */
+				jffs2_dbg(1, "...and adding to erase_pending_list\n");
+				list_add_tail(&jeb->list, &c->erase_pending_list);
+				c->nr_erasing_blocks++;
+				jffs2_garbage_collect_trigger(c);
+			} else {
+				/* Sometimes, however, we leave it elsewhere so it doesn't get
+				   immediately reused, and we spread the load a bit. */
+				jffs2_dbg(1, "...and adding to erasable_list\n");
+				list_add_tail(&jeb->list, &c->erasable_list);
+			}
+		}
+		jffs2_dbg(1, "Done OK\n");
+	} else if (jeb == c->gcblock) {
+		jffs2_dbg(2, "Not moving gcblock 0x%08x to dirty_list\n",
+			  jeb->offset);
+	} else if (ISDIRTY(jeb->dirty_size) && !ISDIRTY(jeb->dirty_size - addedsize)) {
+		jffs2_dbg(1, "Eraseblock at 0x%08x is freshly dirtied. Removing from clean list...\n",
+			  jeb->offset);
+		list_del(&jeb->list);
+		jffs2_dbg(1, "...and adding to dirty_list\n");
+		list_add_tail(&jeb->list, &c->dirty_list);
+	} else if (VERYDIRTY(c, jeb->dirty_size) &&
+		   !VERYDIRTY(c, jeb->dirty_size - addedsize)) {
+		jffs2_dbg(1, "Eraseblock at 0x%08x is now very dirty. Removing from dirty list...\n",
+			  jeb->offset);
+		list_del(&jeb->list);
+		jffs2_dbg(1, "...and adding to very_dirty_list\n");
+		list_add_tail(&jeb->list, &c->very_dirty_list);
+	} else {
+		jffs2_dbg(1, "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) || jffs2_is_readonly(c) ||
+		(c->flags & JFFS2_SB_FLAG_BUILDING)) {
+		/* We didn't lock the erase_free_sem */
+		return;
+	}
+
+	/* The erase_free_sem is locked, and has been since before we marked the node obsolete
+	   and potentially put its eraseblock onto the erase_pending_list. Thus, we know that
+	   the block hasn't _already_ been erased, and that 'ref' itself hasn't been freed yet
+	   by jffs2_free_jeb_node_refs() in erase.c. Which is nice. */
+
+	jffs2_dbg(1, "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) {
+		pr_warn("Read error reading from obsoleted node at 0x%08x: %d\n",
+			ref_offset(ref), ret);
+		goto out_erase_sem;
+	}
+	if (retlen != sizeof(n)) {
+		pr_warn("Short read from obsoleted node at 0x%08x: %zd\n",
+			ref_offset(ref), retlen);
+		goto out_erase_sem;
+	}
+	if (PAD(je32_to_cpu(n.totlen)) != PAD(freed_len)) {
+		pr_warn("Node totlen on flash (0x%08x) != totlen from node ref (0x%08x)\n",
+			je32_to_cpu(n.totlen), freed_len);
+		goto out_erase_sem;
+	}
+	if (!(je16_to_cpu(n.nodetype) & JFFS2_NODE_ACCURATE)) {
+		jffs2_dbg(1, "Node at 0x%08x was already marked obsolete (nodetype 0x%04x)\n",
+			  ref_offset(ref), je16_to_cpu(n.nodetype));
+		goto out_erase_sem;
+	}
+	/* 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) {
+		pr_warn("Write error in obliterating obsoleted node at 0x%08x: %d\n",
+			ref_offset(ref), ret);
+		goto out_erase_sem;
+	}
+	if (retlen != sizeof(n)) {
+		pr_warn("Short write in obliterating obsoleted node at 0x%08x: %zd\n",
+			ref_offset(ref), retlen);
+		goto out_erase_sem;
+	}
+
+	/* Nodes which have been marked obsolete no longer need to be
+	   associated with any inode. Remove them from the per-inode list.
+
+	   Note we can't do this for NAND at the moment because we need
+	   obsolete dirent nodes to stay on the lists, because of the
+	   horridness in jffs2_garbage_collect_deletion_dirent(). Also
+	   because we delete the inocache, and on NAND we need that to
+	   stay around until all the nodes are actually erased, in order
+	   to stop us from giving the same inode number to another newly
+	   created inode. */
+	if (ref->next_in_ino) {
+		struct jffs2_inode_cache *ic;
+		struct jffs2_raw_node_ref **p;
+
+		spin_lock(&c->erase_completion_lock);
+
+		ic = jffs2_raw_ref_to_ic(ref);
+		for (p = &ic->nodes; (*p) != ref; p = &((*p)->next_in_ino))
+			;
+
+		*p = ref->next_in_ino;
+		ref->next_in_ino = NULL;
+
+		switch (ic->class) {
+#ifdef CONFIG_JFFS2_FS_XATTR
+			case RAWNODE_CLASS_XATTR_DATUM:
+				jffs2_release_xattr_datum(c, (struct jffs2_xattr_datum *)ic);
+				break;
+			case RAWNODE_CLASS_XATTR_REF:
+				jffs2_release_xattr_ref(c, (struct jffs2_xattr_ref *)ic);
+				break;
+#endif
+			default:
+				if (ic->nodes == (void *)ic && ic->pino_nlink == 0)
+					jffs2_del_ino_cache(c, ic);
+				break;
+		}
+		spin_unlock(&c->erase_completion_lock);
+	}
+
+ out_erase_sem:
+	mutex_unlock(&c->erase_free_sem);
+}
+
+int jffs2_thread_should_wake(struct jffs2_sb_info *c)
+{
+	int ret = 0;
+	uint32_t dirty;
+	int nr_very_dirty = 0;
+	struct jffs2_eraseblock *jeb;
+
+	if (!list_empty(&c->erase_complete_list) ||
+	    !list_empty(&c->erase_pending_list))
+		return 1;
+
+	if (c->unchecked_size) {
+		jffs2_dbg(1, "jffs2_thread_should_wake(): unchecked_size %d, checked_ino #%d\n",
+			  c->unchecked_size, c->checked_ino);
+		return 1;
+	}
+
+	/* dirty_size contains blocks on erase_pending_list
+	 * those blocks are counted in c->nr_erasing_blocks.
+	 * If one block is actually erased, it is not longer counted as dirty_space
+	 * but it is counted in c->nr_erasing_blocks, so we add it and subtract it
+	 * with c->nr_erasing_blocks * c->sector_size again.
+	 * Blocks on erasable_list are counted as dirty_size, but not in c->nr_erasing_blocks
+	 * This helps us to force gc and pick eventually a clean block to spread the load.
+	 */
+	dirty = c->dirty_size + c->erasing_size - c->nr_erasing_blocks * c->sector_size;
+
+	if (c->nr_free_blocks + c->nr_erasing_blocks < c->resv_blocks_gctrigger &&
+			(dirty > c->nospc_dirty_size))
+		ret = 1;
+
+	list_for_each_entry(jeb, &c->very_dirty_list, list) {
+		nr_very_dirty++;
+		if (nr_very_dirty == c->vdirty_blocks_gctrigger) {
+			ret = 1;
+			/* In debug mode, actually go through and count them all */
+			D1(continue);
+			break;
+		}
+	}
+
+	jffs2_dbg(1, "%s(): nr_free_blocks %d, nr_erasing_blocks %d, dirty_size 0x%x, vdirty_blocks %d: %s\n",
+		  __func__, c->nr_free_blocks, c->nr_erasing_blocks,
+		  c->dirty_size, nr_very_dirty, ret ? "yes" : "no");
+
+	return ret;
+}
Index: cpukit/libfs/src/jffs2/src/read.c
===================================================================
--- cpukit/libfs/src/jffs2/src/read.c	(revision 0c0f128e11007cb0d9ebed8b27dbb7cdde18450d)
+++ cpukit/libfs/src/jffs2/src/read.c	(revision 0c0f128e11007cb0d9ebed8b27dbb7cdde18450d)
@@ -0,0 +1,228 @@
+/*
+ * JFFS2 -- Journalling Flash File System, Version 2.
+ *
+ * Copyright Â© 2001-2007 Red Hat, Inc.
+ *
+ * Created by David Woodhouse <dwmw2@infradead.org>
+ *
+ * For licensing information, see the file 'LICENCE' in this directory.
+ *
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/crc32.h>
+#include <linux/pagemap.h>
+#include <linux/mtd/mtd.h>
+#include <linux/compiler.h>
+#include "nodelist.h"
+#include "compr.h"
+
+int jffs2_read_dnode(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
+		     struct jffs2_full_dnode *fd, unsigned char *buf,
+		     int ofs, int len)
+{
+	struct jffs2_raw_inode *ri;
+	size_t readlen;
+	uint32_t crc;
+	unsigned char *decomprbuf = NULL;
+	unsigned char *readbuf = NULL;
+	int ret = 0;
+
+	ri = jffs2_alloc_raw_inode();
+	if (!ri)
+		return -ENOMEM;
+
+	ret = jffs2_flash_read(c, ref_offset(fd->raw), sizeof(*ri), &readlen, (char *)ri);
+	if (ret) {
+		jffs2_free_raw_inode(ri);
+		pr_warn("Error reading node from 0x%08x: %d\n",
+			ref_offset(fd->raw), ret);
+		return ret;
+	}
+	if (readlen != sizeof(*ri)) {
+		jffs2_free_raw_inode(ri);
+		pr_warn("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);
+
+	jffs2_dbg(1, "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)) {
+		pr_warn("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 > je32_to_cpu(ri->dsize)) {
+			pr_warn("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;
+	});
+
+
+	if (ri->compr == JFFS2_COMPR_ZERO) {
+		memset(buf, 0, len);
+		goto out_ri;
+	}
+
+	/* Cases:
+	   Reading whole node and it's uncompressed - read directly to buffer provided, check CRC.
+	   Reading whole node and it's compressed - read into comprbuf, check CRC and decompress to buffer provided
+	   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 == je32_to_cpu(ri->dsize)) {
+		readbuf = buf;
+	} else {
+		readbuf = kmalloc(je32_to_cpu(ri->csize), GFP_KERNEL);
+		if (!readbuf) {
+			ret = -ENOMEM;
+			goto out_ri;
+		}
+	}
+	if (ri->compr != JFFS2_COMPR_NONE) {
+		if (len < je32_to_cpu(ri->dsize)) {
+			decomprbuf = kmalloc(je32_to_cpu(ri->dsize), GFP_KERNEL);
+			if (!decomprbuf) {
+				ret = -ENOMEM;
+				goto out_readbuf;
+			}
+		} else {
+			decomprbuf = buf;
+		}
+	} else {
+		decomprbuf = readbuf;
+	}
+
+	jffs2_dbg(2, "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 != je32_to_cpu(ri->csize))
+		ret = -EIO;
+	if (ret)
+		goto out_decomprbuf;
+
+	crc = crc32(0, readbuf, je32_to_cpu(ri->csize));
+	if (crc != je32_to_cpu(ri->data_crc)) {
+		pr_warn("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;
+	}
+	jffs2_dbg(2, "Data CRC matches calculated CRC %08x\n", crc);
+	if (ri->compr != JFFS2_COMPR_NONE) {
+		jffs2_dbg(2, "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(c, f, ri->compr | (ri->usercompr << 8), readbuf, decomprbuf, je32_to_cpu(ri->csize), je32_to_cpu(ri->dsize));
+		if (ret) {
+			pr_warn("Error: jffs2_decompress returned %d\n", ret);
+			goto out_decomprbuf;
+		}
+	}
+
+	if (len < je32_to_cpu(ri->dsize)) {
+		memcpy(buf, decomprbuf+ofs, len);
+	}
+ out_decomprbuf:
+	if(decomprbuf != buf && decomprbuf != readbuf)
+		kfree(decomprbuf);
+ out_readbuf:
+	if(readbuf != buf)
+		kfree(readbuf);
+ out_ri:
+	jffs2_free_raw_inode(ri);
+
+	return ret;
+}
+
+int jffs2_read_inode_range(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
+			   unsigned char *buf, uint32_t offset, uint32_t len)
+{
+	uint32_t end = offset + len;
+	struct jffs2_node_frag *frag;
+	int ret;
+
+	jffs2_dbg(1, "%s(): ino #%u, range 0x%08x-0x%08x\n",
+		  __func__, f->inocache->ino, offset, offset + len);
+
+	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
+	 * (or perhaps is before it, if we've been asked to read off the
+	 * end of the file). */
+	while(offset < end) {
+		jffs2_dbg(2, "%s(): offset %d, end %d\n",
+			  __func__, offset, end);
+		if (unlikely(!frag || frag->ofs > offset ||
+			     frag->ofs + frag->size <= offset)) {
+			uint32_t holesize = end - offset;
+			if (frag && frag->ofs > offset) {
+				jffs2_dbg(1, "Eep. Hole in ino #%u fraglist. frag->ofs = 0x%08x, offset = 0x%08x\n",
+					  f->inocache->ino, frag->ofs, offset);
+				holesize = min(holesize, frag->ofs - offset);
+			}
+			jffs2_dbg(1, "Filling non-frag hole from %d-%d\n",
+				  offset, offset + holesize);
+			memset(buf, 0, holesize);
+			buf += holesize;
+			offset += holesize;
+			continue;
+		} else if (unlikely(!frag->node)) {
+			uint32_t holeend = min(end, frag->ofs + frag->size);
+			jffs2_dbg(1, "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);
+			continue;
+		} else {
+			uint32_t readlen;
+			uint32_t fragofs; /* offset within the frag to start reading */
+
+			fragofs = offset - frag->ofs;
+			readlen = min(frag->size - fragofs, end - offset);
+			jffs2_dbg(1, "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, f, frag->node, buf, fragofs + frag->ofs - frag->node->ofs, readlen);
+			jffs2_dbg(2, "node read done\n");
+			if (ret) {
+				jffs2_dbg(1, "%s(): error %d\n",
+					  __func__, ret);
+				memset(buf, 0, readlen);
+				return ret;
+			}
+			buf += readlen;
+			offset += readlen;
+			frag = frag_next(frag);
+			jffs2_dbg(2, "node read was OK. Looping\n");
+		}
+	}
+	return 0;
+}
+
Index: cpukit/libfs/src/jffs2/src/readinode.c
===================================================================
--- cpukit/libfs/src/jffs2/src/readinode.c	(revision 0c0f128e11007cb0d9ebed8b27dbb7cdde18450d)
+++ cpukit/libfs/src/jffs2/src/readinode.c	(revision 0c0f128e11007cb0d9ebed8b27dbb7cdde18450d)
@@ -0,0 +1,1471 @@
+/*
+ * JFFS2 -- Journalling Flash File System, Version 2.
+ *
+ * Copyright Â© 2001-2007 Red Hat, Inc.
+ *
+ * Created by David Woodhouse <dwmw2@infradead.org>
+ *
+ * For licensing information, see the file 'LICENCE' in this directory.
+ *
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/fs.h>
+#include <linux/crc32.h>
+#include <linux/pagemap.h>
+#include <linux/mtd/mtd.h>
+#include <linux/compiler.h>
+#include "nodelist.h"
+
+/*
+ * Check the data CRC of the node.
+ *
+ * Returns: 0 if the data CRC is correct;
+ * 	    1 - if incorrect;
+ *	    error code if an error occurred.
+ */
+static int check_node_data(struct jffs2_sb_info *c, struct jffs2_tmp_dnode_info *tn)
+{
+	struct jffs2_raw_node_ref *ref = tn->fn->raw;
+	int err = 0, pointed = 0;
+	struct jffs2_eraseblock *jeb;
+	unsigned char *buffer;
+	uint32_t crc, ofs, len;
+	size_t retlen;
+
+	BUG_ON(tn->csize == 0);
+
+	/* Calculate how many bytes were already checked */
+	ofs = ref_offset(ref) + sizeof(struct jffs2_raw_inode);
+	len = tn->csize;
+
+	if (jffs2_is_writebuffered(c)) {
+		int adj = ofs % c->wbuf_pagesize;
+		if (likely(adj))
+			adj = c->wbuf_pagesize - adj;
+
+		if (adj >= tn->csize) {
+			dbg_readinode("no need to check node at %#08x, data length %u, data starts at %#08x - it has already been checked.\n",
+				      ref_offset(ref), tn->csize, ofs);
+			goto adj_acc;
+		}
+
+		ofs += adj;
+		len -= adj;
+	}
+
+	dbg_readinode("check node at %#08x, data length %u, partial CRC %#08x, correct CRC %#08x, data starts at %#08x, start checking from %#08x - %u bytes.\n",
+		ref_offset(ref), tn->csize, tn->partial_crc, tn->data_crc, ofs - len, ofs, len);
+
+#ifndef __ECOS
+	/* TODO: instead, incapsulate point() stuff to jffs2_flash_read(),
+	 * adding and jffs2_flash_read_end() interface. */
+	err = mtd_point(c->mtd, ofs, len, &retlen, (void **)&buffer, NULL);
+	if (!err && retlen < len) {
+		JFFS2_WARNING("MTD point returned len too short: %zu instead of %u.\n", retlen, tn->csize);
+		mtd_unpoint(c->mtd, ofs, retlen);
+	} else if (err) {
+		if (err != -EOPNOTSUPP)
+			JFFS2_WARNING("MTD point failed: error code %d.\n", err);
+	} else
+		pointed = 1; /* succefully pointed to device */
+#endif
+
+	if (!pointed) {
+		buffer = kmalloc(len, GFP_KERNEL);
+		if (unlikely(!buffer))
+			return -ENOMEM;
+
+		/* TODO: this is very frequent pattern, make it a separate
+		 * routine */
+		err = jffs2_flash_read(c, ofs, len, &retlen, buffer);
+		if (err) {
+			JFFS2_ERROR("can not read %d bytes from 0x%08x, error code: %d.\n", len, ofs, err);
+			goto free_out;
+		}
+
+		if (retlen != len) {
+			JFFS2_ERROR("short read at %#08x: %zd instead of %d.\n", ofs, retlen, len);
+			err = -EIO;
+			goto free_out;
+		}
+	}
+
+	/* Continue calculating CRC */
+	crc = crc32(tn->partial_crc, buffer, len);
+	if(!pointed)
+		kfree(buffer);
+#ifndef __ECOS
+	else
+		mtd_unpoint(c->mtd, ofs, len);
+#endif
+
+	if (crc != tn->data_crc) {
+		JFFS2_NOTICE("wrong data CRC in data node at 0x%08x: read %#08x, calculated %#08x.\n",
+			     ref_offset(ref), tn->data_crc, crc);
+		return 1;
+	}
+
+adj_acc:
+	jeb = &c->blocks[ref->flash_offset / c->sector_size];
+	len = ref_totlen(c, jeb, ref);
+	/* If it should be REF_NORMAL, it'll get marked as such when
+	   we build the fragtree, shortly. No need to worry about GC
+	   moving it while it's marked REF_PRISTINE -- GC won't happen
+	   till we've finished checking every inode anyway. */
+	ref->flash_offset |= REF_PRISTINE;
+	/*
+	 * Mark the node as having been checked and fix the
+	 * accounting accordingly.
+	 */
+	spin_lock(&c->erase_completion_lock);
+	jeb->used_size += len;
+	jeb->unchecked_size -= len;
+	c->used_size += len;
+	c->unchecked_size -= len;
+	jffs2_dbg_acct_paranoia_check_nolock(c, jeb);
+	spin_unlock(&c->erase_completion_lock);
+
+	return 0;
+
+free_out:
+	if(!pointed)
+		kfree(buffer);
+#ifndef __ECOS
+	else
+		mtd_unpoint(c->mtd, ofs, len);
+#endif
+	return err;
+}
+
+/*
+ * Helper function for jffs2_add_older_frag_to_fragtree().
+ *
+ * Checks the node if we are in the checking stage.
+ */
+static int check_tn_node(struct jffs2_sb_info *c, struct jffs2_tmp_dnode_info *tn)
+{
+	int ret;
+
+	BUG_ON(ref_obsolete(tn->fn->raw));
+
+	/* We only check the data CRC of unchecked nodes */
+	if (ref_flags(tn->fn->raw) != REF_UNCHECKED)
+		return 0;
+
+	dbg_readinode("check node %#04x-%#04x, phys offs %#08x\n",
+		      tn->fn->ofs, tn->fn->ofs + tn->fn->size, ref_offset(tn->fn->raw));
+
+	ret = check_node_data(c, tn);
+	if (unlikely(ret < 0)) {
+		JFFS2_ERROR("check_node_data() returned error: %d.\n",
+			ret);
+	} else if (unlikely(ret > 0)) {
+		dbg_readinode("CRC error, mark it obsolete.\n");
+		jffs2_mark_node_obsolete(c, tn->fn->raw);
+	}
+
+	return ret;
+}
+
+static struct jffs2_tmp_dnode_info *jffs2_lookup_tn(struct rb_root *tn_root, uint32_t offset)
+{
+	struct rb_node *next;
+	struct jffs2_tmp_dnode_info *tn = NULL;
+
+	dbg_readinode("root %p, offset %d\n", tn_root, offset);
+
+	next = tn_root->rb_node;
+
+	while (next) {
+		tn = rb_entry(next, struct jffs2_tmp_dnode_info, rb);
+
+		if (tn->fn->ofs < offset)
+			next = tn->rb.rb_right;
+		else if (tn->fn->ofs >= offset)
+			next = tn->rb.rb_left;
+		else
+			break;
+	}
+
+	return tn;
+}
+
+
+static void jffs2_kill_tn(struct jffs2_sb_info *c, struct jffs2_tmp_dnode_info *tn)
+{
+	jffs2_mark_node_obsolete(c, tn->fn->raw);
+	jffs2_free_full_dnode(tn->fn);
+	jffs2_free_tmp_dnode_info(tn);
+}
+/*
+ * This function is used when we read an inode. Data nodes arrive in
+ * arbitrary order -- they may be older or newer than the nodes which
+ * are already in the tree. Where overlaps occur, the older node can
+ * be discarded as long as the newer passes the CRC check. We don't
+ * bother to keep track of holes in this rbtree, and neither do we deal
+ * with frags -- we can have multiple entries starting at the same
+ * offset, and the one with the smallest length will come first in the
+ * ordering.
+ *
+ * Returns 0 if the node was handled (including marking it obsolete)
+ *	 < 0 an if error occurred
+ */
+static int jffs2_add_tn_to_tree(struct jffs2_sb_info *c,
+				struct jffs2_readinode_info *rii,
+				struct jffs2_tmp_dnode_info *tn)
+{
+	uint32_t fn_end = tn->fn->ofs + tn->fn->size;
+	struct jffs2_tmp_dnode_info *this, *ptn;
+
+	dbg_readinode("insert fragment %#04x-%#04x, ver %u at %08x\n", tn->fn->ofs, fn_end, tn->version, ref_offset(tn->fn->raw));
+
+	/* If a node has zero dsize, we only have to keep if it if it might be the
+	   node with highest version -- i.e. the one which will end up as f->metadata.
+	   Note that such nodes won't be REF_UNCHECKED since there are no data to
+	   check anyway. */
+	if (!tn->fn->size) {
+		if (rii->mdata_tn) {
+			if (rii->mdata_tn->version < tn->version) {
+				/* We had a candidate mdata node already */
+				dbg_readinode("kill old mdata with ver %d\n", rii->mdata_tn->version);
+				jffs2_kill_tn(c, rii->mdata_tn);
+			} else {
+				dbg_readinode("kill new mdata with ver %d (older than existing %d\n",
+					      tn->version, rii->mdata_tn->version);
+				jffs2_kill_tn(c, tn);
+				return 0;
+			}
+		}
+		rii->mdata_tn = tn;
+		dbg_readinode("keep new mdata with ver %d\n", tn->version);
+		return 0;
+	}
+
+	/* Find the earliest node which _may_ be relevant to this one */
+	this = jffs2_lookup_tn(&rii->tn_root, tn->fn->ofs);
+	if (this) {
+		/* If the node is coincident with another at a lower address,
+		   back up until the other node is found. It may be relevant */
+		while (this->overlapped) {
+			ptn = tn_prev(this);
+			if (!ptn) {
+				/*
+				 * We killed a node which set the overlapped
+				 * flags during the scan. Fix it up.
+				 */
+				this->overlapped = 0;
+				break;
+			}
+			this = ptn;
+		}
+		dbg_readinode("'this' found %#04x-%#04x (%s)\n", this->fn->ofs, this->fn->ofs + this->fn->size, this->fn ? "data" : "hole");
+	}
+
+	while (this) {
+		if (this->fn->ofs > fn_end)
+			break;
+		dbg_readinode("Ponder this ver %d, 0x%x-0x%x\n",
+			      this->version, this->fn->ofs, this->fn->size);
+
+		if (this->version == tn->version) {
+			/* Version number collision means REF_PRISTINE GC. Accept either of them
+			   as long as the CRC is correct. Check the one we have already...  */
+			if (!check_tn_node(c, this)) {
+				/* The one we already had was OK. Keep it and throw away the new one */
+				dbg_readinode("Like old node. Throw away new\n");
+				jffs2_kill_tn(c, tn);
+				return 0;
+			} else {
+				/* Who cares if the new one is good; keep it for now anyway. */
+				dbg_readinode("Like new node. Throw away old\n");
+				rb_replace_node(&this->rb, &tn->rb, &rii->tn_root);
+				jffs2_kill_tn(c, this);
+				/* Same overlapping from in front and behind */
+				return 0;
+			}
+		}
+		if (this->version < tn->version &&
+		    this->fn->ofs >= tn->fn->ofs &&
+		    this->fn->ofs + this->fn->size <= fn_end) {
+			/* New node entirely overlaps 'this' */
+			if (check_tn_node(c, tn)) {
+				dbg_readinode("new node bad CRC\n");
+				jffs2_kill_tn(c, tn);
+				return 0;
+			}
+			/* ... and is good. Kill 'this' and any subsequent nodes which are also overlapped */
+			while (this && this->fn->ofs + this->fn->size <= fn_end) {
+				struct jffs2_tmp_dnode_info *next = tn_next(this);
+				if (this->version < tn->version) {
+					tn_erase(this, &rii->tn_root);
+					dbg_readinode("Kill overlapped ver %d, 0x%x-0x%x\n",
+						      this->version, this->fn->ofs,
+						      this->fn->ofs+this->fn->size);
+					jffs2_kill_tn(c, this);
+				}
+				this = next;
+			}
+			dbg_readinode("Done killing overlapped nodes\n");
+			continue;
+		}
+		if (this->version > tn->version &&
+		    this->fn->ofs <= tn->fn->ofs &&
+		    this->fn->ofs+this->fn->size >= fn_end) {
+			/* New node entirely overlapped by 'this' */
+			if (!check_tn_node(c, this)) {
+				dbg_readinode("Good CRC on old node. Kill new\n");
+				jffs2_kill_tn(c, tn);
+				return 0;
+			}
+			/* ... but 'this' was bad. Replace it... */
+			dbg_readinode("Bad CRC on old overlapping node. Kill it\n");
+			tn_erase(this, &rii->tn_root);
+			jffs2_kill_tn(c, this);
+			break;
+		}
+
+		this = tn_next(this);
+	}
+
+	/* We neither completely obsoleted nor were completely
+	   obsoleted by an earlier node. Insert into the tree */
+	{
+		struct rb_node *parent;
+		struct rb_node **link = &rii->tn_root.rb_node;
+		struct jffs2_tmp_dnode_info *insert_point = NULL;
+
+		while (*link) {
+			parent = *link;
+			insert_point = rb_entry(parent, struct jffs2_tmp_dnode_info, rb);
+			if (tn->fn->ofs > insert_point->fn->ofs)
+				link = &insert_point->rb.rb_right;
+			else if (tn->fn->ofs < insert_point->fn->ofs ||
+				 tn->fn->size < insert_point->fn->size)
+				link = &insert_point->rb.rb_left;
+			else
+				link = &insert_point->rb.rb_right;
+		}
+		rb_link_node(&tn->rb, &insert_point->rb, link);
+		rb_insert_color(&tn->rb, &rii->tn_root);
+	}
+
+	/* If there's anything behind that overlaps us, note it */
+	this = tn_prev(tn);
+	if (this) {
+		while (1) {
+			if (this->fn->ofs + this->fn->size > tn->fn->ofs) {
+				dbg_readinode("Node is overlapped by %p (v %d, 0x%x-0x%x)\n",
+					      this, this->version, this->fn->ofs,
+					      this->fn->ofs+this->fn->size);
+				tn->overlapped = 1;
+				break;
+			}
+			if (!this->overlapped)
+				break;
+
+			ptn = tn_prev(this);
+			if (!ptn) {
+				/*
+				 * We killed a node which set the overlapped
+				 * flags during the scan. Fix it up.
+				 */
+				this->overlapped = 0;
+				break;
+			}
+			this = ptn;
+		}
+	}
+
+	/* If the new node overlaps anything ahead, note it */
+	this = tn_next(tn);
+	while (this && this->fn->ofs < fn_end) {
+		this->overlapped = 1;
+		dbg_readinode("Node ver %d, 0x%x-0x%x is overlapped\n",
+			      this->version, this->fn->ofs,
+			      this->fn->ofs+this->fn->size);
+		this = tn_next(this);
+	}
+	return 0;
+}
+
+/* Trivial function to remove the last node in the tree. Which by definition
+   has no right-hand child â so can be removed just by making its left-hand
+   child (if any) take its place under its parent. Since this is only done
+   when we're consuming the whole tree, there's no need to use rb_erase()
+   and let it worry about adjusting colours and balancing the tree. That
+   would just be a waste of time. */
+static void eat_last(struct rb_root *root, struct rb_node *node)
+{
+	struct rb_node *parent = rb_parent(node);
+	struct rb_node **link;
+
+	/* LAST! */
+	BUG_ON(node->rb_right);
+
+	if (!parent)
+		link = &root->rb_node;
+	else if (node == parent->rb_left)
+		link = &parent->rb_left;
+	else
+		link = &parent->rb_right;
+
+	*link = node->rb_left;
+	if (node->rb_left)
+		node->rb_left->__rb_parent_color = node->__rb_parent_color;
+}
+
+/* We put the version tree in reverse order, so we can use the same eat_last()
+   function that we use to consume the tmpnode tree (tn_root). */
+static void ver_insert(struct rb_root *ver_root, struct jffs2_tmp_dnode_info *tn)
+{
+	struct rb_node **link = &ver_root->rb_node;
+	struct rb_node *parent = NULL;
+	struct jffs2_tmp_dnode_info *this_tn;
+
+	while (*link) {
+		parent = *link;
+		this_tn = rb_entry(parent, struct jffs2_tmp_dnode_info, rb);
+
+		if (tn->version > this_tn->version)
+			link = &parent->rb_left;
+		else
+			link = &parent->rb_right;
+	}
+	dbg_readinode("Link new node at %p (root is %p)\n", link, ver_root);
+	rb_link_node(&tn->rb, parent, link);
+	rb_insert_color(&tn->rb, ver_root);
+}
+
+/* Build final, normal fragtree from tn tree. It doesn't matter which order
+   we add nodes to the real fragtree, as long as they don't overlap. And
+   having thrown away the majority of overlapped nodes as we went, there
+   really shouldn't be many sets of nodes which do overlap. If we start at
+   the end, we can use the overlap markers -- we can just eat nodes which
+   aren't overlapped, and when we encounter nodes which _do_ overlap we
+   sort them all into a temporary tree in version order before replaying them. */
+static int jffs2_build_inode_fragtree(struct jffs2_sb_info *c,
+				      struct jffs2_inode_info *f,
+				      struct jffs2_readinode_info *rii)
+{
+	struct jffs2_tmp_dnode_info *pen, *last, *this;
+	struct rb_root ver_root = RB_ROOT;
+	uint32_t high_ver = 0;
+
+	if (rii->mdata_tn) {
+		dbg_readinode("potential mdata is ver %d at %p\n", rii->mdata_tn->version, rii->mdata_tn);
+		high_ver = rii->mdata_tn->version;
+		rii->latest_ref = rii->mdata_tn->fn->raw;
+	}
+#ifdef JFFS2_DBG_READINODE_MESSAGES
+	this = tn_last(&rii->tn_root);
+	while (this) {
+		dbg_readinode("tn %p ver %d range 0x%x-0x%x ov %d\n", this, this->version, this->fn->ofs,
+			      this->fn->ofs+this->fn->size, this->overlapped);
+		this = tn_prev(this);
+	}
+#endif
+	pen = tn_last(&rii->tn_root);
+	while ((last = pen)) {
+		pen = tn_prev(last);
+
+		eat_last(&rii->tn_root, &last->rb);
+		ver_insert(&ver_root, last);
+
+		if (unlikely(last->overlapped)) {
+			if (pen)
+				continue;
+			/*
+			 * We killed a node which set the overlapped
+			 * flags during the scan. Fix it up.
+			 */
+			last->overlapped = 0;
+		}
+
+		/* Now we have a bunch of nodes in reverse version
+		   order, in the tree at ver_root. Most of the time,
+		   there'll actually be only one node in the 'tree',
+		   in fact. */
+		this = tn_last(&ver_root);
+
+		while (this) {
+			struct jffs2_tmp_dnode_info *vers_next;
+			int ret;
+			vers_next = tn_prev(this);
+			eat_last(&ver_root, &this->rb);
+			if (check_tn_node(c, this)) {
+				dbg_readinode("node ver %d, 0x%x-0x%x failed CRC\n",
+					     this->version, this->fn->ofs,
+					     this->fn->ofs+this->fn->size);
+				jffs2_kill_tn(c, this);
+			} else {
+				if (this->version > high_ver) {
+					/* Note that this is different from the other
+					   highest_version, because this one is only
+					   counting _valid_ nodes which could give the
+					   latest inode metadata */
+					high_ver = this->version;
+					rii->latest_ref = this->fn->raw;
+				}
+				dbg_readinode("Add %p (v %d, 0x%x-0x%x, ov %d) to fragtree\n",
+					     this, this->version, this->fn->ofs,
+					     this->fn->ofs+this->fn->size, this->overlapped);
+
+				ret = jffs2_add_full_dnode_to_inode(c, f, this->fn);
+				if (ret) {
+					/* Free the nodes in vers_root; let the caller
+					   deal with the rest */
+					JFFS2_ERROR("Add node to tree failed %d\n", ret);
+					while (1) {
+						vers_next = tn_prev(this);
+						if (check_tn_node(c, this))
+							jffs2_mark_node_obsolete(c, this->fn->raw);
+						jffs2_free_full_dnode(this->fn);
+						jffs2_free_tmp_dnode_info(this);
+						this = vers_next;
+						if (!this)
+							break;
+						eat_last(&ver_root, &vers_next->rb);
+					}
+					return ret;
+				}
+				jffs2_free_tmp_dnode_info(this);
+			}
+			this = vers_next;
+		}
+	}
+	return 0;
+}
+
+static void jffs2_free_tmp_dnode_info_list(struct rb_root *list)
+{
+	struct rb_node *this;
+	struct jffs2_tmp_dnode_info *tn;
+
+	this = list->rb_node;
+
+	/* Now at bottom of tree */
+	while (this) {
+		if (this->rb_left)
+			this = this->rb_left;
+		else if (this->rb_right)
+			this = this->rb_right;
+		else {
+			tn = rb_entry(this, struct jffs2_tmp_dnode_info, rb);
+			jffs2_free_full_dnode(tn->fn);
+			jffs2_free_tmp_dnode_info(tn);
+
+			this = rb_parent(this);
+			if (!this)
+				break;
+
+			if (this->rb_left == &tn->rb)
+				this->rb_left = NULL;
+			else if (this->rb_right == &tn->rb)
+				this->rb_right = NULL;
+			else BUG();
+		}
+	}
+	*list = RB_ROOT;
+}
+
+static void jffs2_free_full_dirent_list(struct jffs2_full_dirent *fd)
+{
+	struct jffs2_full_dirent *next;
+
+	while (fd) {
+		next = fd->next;
+		jffs2_free_full_dirent(fd);
+		fd = next;
+	}
+}
+
+/* Returns first valid node after 'ref'. May return 'ref' */
+static struct jffs2_raw_node_ref *jffs2_first_valid_node(struct jffs2_raw_node_ref *ref)
+{
+	while (ref && ref->next_in_ino) {
+		if (!ref_obsolete(ref))
+			return ref;
+		dbg_noderef("node at 0x%08x is obsoleted. Ignoring.\n", ref_offset(ref));
+		ref = ref->next_in_ino;
+	}
+	return NULL;
+}
+
+/*
+ * Helper function for jffs2_get_inode_nodes().
+ * It is called every time an directory entry node is found.
+ *
+ * Returns: 0 on success;
+ * 	    negative error code on failure.
+ */
+static inline int read_direntry(struct jffs2_sb_info *c, struct jffs2_raw_node_ref *ref,
+				struct jffs2_raw_dirent *rd, size_t read,
+				struct jffs2_readinode_info *rii)
+{
+	struct jffs2_full_dirent *fd;
+	uint32_t crc;
+
+	/* Obsoleted. This cannot happen, surely? dwmw2 20020308 */
+	BUG_ON(ref_obsolete(ref));
+
+	crc = crc32(0, rd, sizeof(*rd) - 8);
+	if (unlikely(crc != je32_to_cpu(rd->node_crc))) {
+		JFFS2_NOTICE("header CRC failed on dirent node at %#08x: read %#08x, calculated %#08x\n",
+			     ref_offset(ref), je32_to_cpu(rd->node_crc), crc);
+		jffs2_mark_node_obsolete(c, ref);
+		return 0;
+	}
+
+	/* If we've never checked the CRCs on this node, check them now */
+	if (ref_flags(ref) == REF_UNCHECKED) {
+		struct jffs2_eraseblock *jeb;
+		int len;
+
+		/* Sanity check */
+		if (unlikely(PAD((rd->nsize + sizeof(*rd))) != PAD(je32_to_cpu(rd->totlen)))) {
+			JFFS2_ERROR("illegal nsize in node at %#08x: nsize %#02x, totlen %#04x\n",
+				    ref_offset(ref), rd->nsize, je32_to_cpu(rd->totlen));
+			jffs2_mark_node_obsolete(c, ref);
+			return 0;
+		}
+
+		jeb = &c->blocks[ref->flash_offset / c->sector_size];
+		len = ref_totlen(c, jeb, ref);
+
+		spin_lock(&c->erase_completion_lock);
+		jeb->used_size += len;
+		jeb->unchecked_size -= len;
+		c->used_size += len;
+		c->unchecked_size -= len;
+		ref->flash_offset = ref_offset(ref) | dirent_node_state(rd);
+		spin_unlock(&c->erase_completion_lock);
+	}
+
+	fd = jffs2_alloc_full_dirent(rd->nsize + 1);
+	if (unlikely(!fd))
+		return -ENOMEM;
+
+	fd->raw = ref;
+	fd->version = je32_to_cpu(rd->version);
+	fd->ino = je32_to_cpu(rd->ino);
+	fd->type = rd->type;
+
+	if (fd->version > rii->highest_version)
+		rii->highest_version = fd->version;
+
+	/* Pick out the mctime of the latest dirent */
+	if(fd->version > rii->mctime_ver && je32_to_cpu(rd->mctime)) {
+		rii->mctime_ver = fd->version;
+		rii->latest_mctime = je32_to_cpu(rd->mctime);
+	}
+
+	/*
+	 * Copy as much of the name as possible from the raw
+	 * dirent we've already read from the flash.
+	 */
+	if (read > sizeof(*rd))
+		memcpy(&fd->name[0], &rd->name[0],
+		       min_t(uint32_t, rd->nsize, (read - sizeof(*rd)) ));
+
+	/* Do we need to copy any more of the name directly from the flash? */
+	if (rd->nsize + sizeof(*rd) > read) {
+		/* FIXME: point() */
+		int err;
+		int already = read - sizeof(*rd);
+
+		err = jffs2_flash_read(c, (ref_offset(ref)) + read,
+				rd->nsize - already, &read, &fd->name[already]);
+		if (unlikely(read != rd->nsize - already) && likely(!err))
+			return -EIO;
+
+		if (unlikely(err)) {
+			JFFS2_ERROR("read remainder of name: error %d\n", err);
+			jffs2_free_full_dirent(fd);
+			return -EIO;
+		}
+	}
+
+	fd->nhash = full_name_hash(fd->name, rd->nsize);
+	fd->next = NULL;
+	fd->name[rd->nsize] = '\0';
+
+	/*
+	 * Wheee. We now have a complete jffs2_full_dirent structure, with
+	 * the name in it and everything. Link it into the list
+	 */
+	jffs2_add_fd_to_list(c, fd, &rii->fds);
+
+	return 0;
+}
+
+/*
+ * Helper function for jffs2_get_inode_nodes().
+ * It is called every time an inode node is found.
+ *
+ * Returns: 0 on success (possibly after marking a bad node obsolete);
+ * 	    negative error code on failure.
+ */
+static inline int read_dnode(struct jffs2_sb_info *c, struct jffs2_raw_node_ref *ref,
+			     struct jffs2_raw_inode *rd, int rdlen,
+			     struct jffs2_readinode_info *rii)
+{
+	struct jffs2_tmp_dnode_info *tn;
+	uint32_t len, csize;
+	int ret = 0;
+	uint32_t crc;
+
+	/* Obsoleted. This cannot happen, surely? dwmw2 20020308 */
+	BUG_ON(ref_obsolete(ref));
+
+	crc = crc32(0, rd, sizeof(*rd) - 8);
+	if (unlikely(crc != je32_to_cpu(rd->node_crc))) {
+		JFFS2_NOTICE("node CRC failed on dnode at %#08x: read %#08x, calculated %#08x\n",
+			     ref_offset(ref), je32_to_cpu(rd->node_crc), crc);
+		jffs2_mark_node_obsolete(c, ref);
+		return 0;
+	}
+
+	tn = jffs2_alloc_tmp_dnode_info();
+	if (!tn) {
+		JFFS2_ERROR("failed to allocate tn (%zu bytes).\n", sizeof(*tn));
+		return -ENOMEM;
+	}
+
+	tn->partial_crc = 0;
+	csize = je32_to_cpu(rd->csize);
+
+	/* If we've never checked the CRCs on this node, check them now */
+	if (ref_flags(ref) == REF_UNCHECKED) {
+
+		/* Sanity checks */
+		if (unlikely(je32_to_cpu(rd->offset) > je32_to_cpu(rd->isize)) ||
+		    unlikely(PAD(je32_to_cpu(rd->csize) + sizeof(*rd)) != PAD(je32_to_cpu(rd->totlen)))) {
+			JFFS2_WARNING("inode node header CRC is corrupted at %#08x\n", ref_offset(ref));
+			jffs2_dbg_dump_node(c, ref_offset(ref));
+			jffs2_mark_node_obsolete(c, ref);
+			goto free_out;
+		}
+
+		if (jffs2_is_writebuffered(c) && csize != 0) {
+			/* At this point we are supposed to check the data CRC
+			 * of our unchecked node. But thus far, we do not
+			 * know whether the node is valid or obsolete. To
+			 * figure this out, we need to walk all the nodes of
+			 * the inode and build the inode fragtree. We don't
+			 * want to spend time checking data of nodes which may
+			 * later be found to be obsolete. So we put off the full
+			 * data CRC checking until we have read all the inode
+			 * nodes and have started building the fragtree.
+			 *
+			 * The fragtree is being built starting with nodes
+			 * having the highest version number, so we'll be able
+			 * to detect whether a node is valid (i.e., it is not
+			 * overlapped by a node with higher version) or not.
+			 * And we'll be able to check only those nodes, which
+			 * are not obsolete.
+			 *
+			 * Of course, this optimization only makes sense in case
+			 * of NAND flashes (or other flashes with
+			 * !jffs2_can_mark_obsolete()), since on NOR flashes
+			 * nodes are marked obsolete physically.
+			 *
+			 * Since NAND flashes (or other flashes with
+			 * jffs2_is_writebuffered(c)) are anyway read by
+			 * fractions of c->wbuf_pagesize, and we have just read
+			 * the node header, it is likely that the starting part
+			 * of the node data is also read when we read the
+			 * header. So we don't mind to check the CRC of the
+			 * starting part of the data of the node now, and check
+			 * the second part later (in jffs2_check_node_data()).
+			 * Of course, we will not need to re-read and re-check
+			 * the NAND page which we have just read. This is why we
+			 * read the whole NAND page at jffs2_get_inode_nodes(),
+			 * while we needed only the node header.
+			 */
+			unsigned char *buf;
+
+			/* 'buf' will point to the start of data */
+			buf = (unsigned char *)rd + sizeof(*rd);
+			/* len will be the read data length */
+			len = min_t(uint32_t, rdlen - sizeof(*rd), csize);
+			tn->partial_crc = crc32(0, buf, len);
+
+			dbg_readinode("Calculates CRC (%#08x) for %d bytes, csize %d\n", tn->partial_crc, len, csize);
+
+			/* If we actually calculated the whole data CRC
+			 * and it is wrong, drop the node. */
+			if (len >= csize && unlikely(tn->partial_crc != je32_to_cpu(rd->data_crc))) {
+				JFFS2_NOTICE("wrong data CRC in data node at 0x%08x: read %#08x, calculated %#08x.\n",
+					ref_offset(ref), tn->partial_crc, je32_to_cpu(rd->data_crc));
+				jffs2_mark_node_obsolete(c, ref);
+				goto free_out;
+			}
+
+		} else if (csize == 0) {
+			/*
+			 * We checked the header CRC. If the node has no data, adjust
+			 * the space accounting now. For other nodes this will be done
+			 * later either when the node is marked obsolete or when its
+			 * data is checked.
+			 */
+			struct jffs2_eraseblock *jeb;
+
+			dbg_readinode("the node has no data.\n");
+			jeb = &c->blocks[ref->flash_offset / c->sector_size];
+			len = ref_totlen(c, jeb, ref);
+
+			spin_lock(&c->erase_completion_lock);
+			jeb->used_size += len;
+			jeb->unchecked_size -= len;
+			c->used_size += len;
+			c->unchecked_size -= len;
+			ref->flash_offset = ref_offset(ref) | REF_NORMAL;
+			spin_unlock(&c->erase_completion_lock);
+		}
+	}
+
+	tn->fn = jffs2_alloc_full_dnode();
+	if (!tn->fn) {
+		JFFS2_ERROR("alloc fn failed\n");
+		ret = -ENOMEM;
+		goto free_out;
+	}
+
+	tn->version = je32_to_cpu(rd->version);
+	tn->fn->ofs = je32_to_cpu(rd->offset);
+	tn->data_crc = je32_to_cpu(rd->data_crc);
+	tn->csize = csize;
+	tn->fn->raw = ref;
+	tn->overlapped = 0;
+
+	if (tn->version > rii->highest_version)
+		rii->highest_version = tn->version;
+
+	/* There was a bug where we wrote hole nodes out with
+	   csize/dsize swapped. Deal with it */
+	if (rd->compr == JFFS2_COMPR_ZERO && !je32_to_cpu(rd->dsize) && csize)
+		tn->fn->size = csize;
+	else // normal case...
+		tn->fn->size = je32_to_cpu(rd->dsize);
+
+	dbg_readinode2("dnode @%08x: ver %u, offset %#04x, dsize %#04x, csize %#04x\n",
+		       ref_offset(ref), je32_to_cpu(rd->version),
+		       je32_to_cpu(rd->offset), je32_to_cpu(rd->dsize), csize);
+
+	ret = jffs2_add_tn_to_tree(c, rii, tn);
+
+	if (ret) {
+		jffs2_free_full_dnode(tn->fn);
+	free_out:
+		jffs2_free_tmp_dnode_info(tn);
+		return ret;
+	}
+#ifdef JFFS2_DBG_READINODE2_MESSAGES
+	dbg_readinode2("After adding ver %d:\n", je32_to_cpu(rd->version));
+	tn = tn_first(&rii->tn_root);
+	while (tn) {
+		dbg_readinode2("%p: v %d r 0x%x-0x%x ov %d\n",
+			       tn, tn->version, tn->fn->ofs,
+			       tn->fn->ofs+tn->fn->size, tn->overlapped);
+		tn = tn_next(tn);
+	}
+#endif
+	return 0;
+}
+
+/*
+ * Helper function for jffs2_get_inode_nodes().
+ * It is called every time an unknown node is found.
+ *
+ * Returns: 0 on success;
+ * 	    negative error code on failure.
+ */
+static inline int read_unknown(struct jffs2_sb_info *c, struct jffs2_raw_node_ref *ref, struct jffs2_unknown_node *un)
+{
+	/* We don't mark unknown nodes as REF_UNCHECKED */
+	if (ref_flags(ref) == REF_UNCHECKED) {
+		JFFS2_ERROR("REF_UNCHECKED but unknown node at %#08x\n",
+			    ref_offset(ref));
+		JFFS2_ERROR("Node is {%04x,%04x,%08x,%08x}. Please report this error.\n",
+			    je16_to_cpu(un->magic), je16_to_cpu(un->nodetype),
+			    je32_to_cpu(un->totlen), je32_to_cpu(un->hdr_crc));
+		jffs2_mark_node_obsolete(c, ref);
+		return 0;
+	}
+
+	un->nodetype = cpu_to_je16(JFFS2_NODE_ACCURATE | je16_to_cpu(un->nodetype));
+
+	switch(je16_to_cpu(un->nodetype) & JFFS2_COMPAT_MASK) {
+
+	case JFFS2_FEATURE_INCOMPAT:
+		JFFS2_ERROR("unknown INCOMPAT nodetype %#04X at %#08x\n",
+			    je16_to_cpu(un->nodetype), ref_offset(ref));
+		/* EEP */
+		BUG();
+		break;
+
+	case JFFS2_FEATURE_ROCOMPAT:
+		JFFS2_ERROR("unknown ROCOMPAT nodetype %#04X at %#08x\n",
+			    je16_to_cpu(un->nodetype), ref_offset(ref));
+		BUG_ON(!(c->flags & JFFS2_SB_FLAG_RO));
+		break;
+
+	case JFFS2_FEATURE_RWCOMPAT_COPY:
+		JFFS2_NOTICE("unknown RWCOMPAT_COPY nodetype %#04X at %#08x\n",
+			     je16_to_cpu(un->nodetype), ref_offset(ref));
+		break;
+
+	case JFFS2_FEATURE_RWCOMPAT_DELETE:
+		JFFS2_NOTICE("unknown RWCOMPAT_DELETE nodetype %#04X at %#08x\n",
+			     je16_to_cpu(un->nodetype), ref_offset(ref));
+		jffs2_mark_node_obsolete(c, ref);
+		return 0;
+	}
+
+	return 0;
+}
+
+/*
+ * Helper function for jffs2_get_inode_nodes().
+ * The function detects whether more data should be read and reads it if yes.
+ *
+ * Returns: 0 on success;
+ * 	    negative error code on failure.
+ */
+static int read_more(struct jffs2_sb_info *c, struct jffs2_raw_node_ref *ref,
+		     int needed_len, int *rdlen, unsigned char *buf)
+{
+	int err, to_read = needed_len - *rdlen;
+	size_t retlen;
+	uint32_t offs;
+
+	if (jffs2_is_writebuffered(c)) {
+		int rem = to_read % c->wbuf_pagesize;
+
+		if (rem)
+			to_read += c->wbuf_pagesize - rem;
+	}
+
+	/* We need to read more data */
+	offs = ref_offset(ref) + *rdlen;
+
+	dbg_readinode("read more %d bytes\n", to_read);
+
+	err = jffs2_flash_read(c, offs, to_read, &retlen, buf + *rdlen);
+	if (err) {
+		JFFS2_ERROR("can not read %d bytes from 0x%08x, "
+			"error code: %d.\n", to_read, offs, err);
+		return err;
+	}
+
+	if (retlen < to_read) {
+		JFFS2_ERROR("short read at %#08x: %zu instead of %d.\n",
+				offs, retlen, to_read);
+		return -EIO;
+	}
+
+	*rdlen += to_read;
+	return 0;
+}
+
+/* Get tmp_dnode_info and full_dirent for all non-obsolete nodes associated
+   with this ino. Perform a preliminary ordering on data nodes, throwing away
+   those which are completely obsoleted by newer ones. The naÃ¯ve approach we
+   use to take of just returning them _all_ in version order will cause us to
+   run out of memory in certain degenerate cases. */
+static int jffs2_get_inode_nodes(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
+				 struct jffs2_readinode_info *rii)
+{
+	struct jffs2_raw_node_ref *ref, *valid_ref;
+	unsigned char *buf = NULL;
+	union jffs2_node_union *node;
+	size_t retlen;
+	int len, err;
+
+	rii->mctime_ver = 0;
+
+	dbg_readinode("ino #%u\n", f->inocache->ino);
+
+	/* FIXME: in case of NOR and available ->point() this
+	 * needs to be fixed. */
+	len = sizeof(union jffs2_node_union) + c->wbuf_pagesize;
+	buf = kmalloc(len, GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+
+	spin_lock(&c->erase_completion_lock);
+	valid_ref = jffs2_first_valid_node(f->inocache->nodes);
+	if (!valid_ref && f->inocache->ino != 1)
+		JFFS2_WARNING("Eep. No valid nodes for ino #%u.\n", f->inocache->ino);
+	while (valid_ref) {
+		/* 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. So if we mark 'ref' obsolete while we're
+		   not holding the lock, it can go away immediately. For that reason,
+		   we find the next valid node first, before processing 'ref'.
+		*/
+		ref = valid_ref;
+		valid_ref = jffs2_first_valid_node(ref->next_in_ino);
+		spin_unlock(&c->erase_completion_lock);
+
+		cond_resched();
+
+		/*
+		 * At this point we don't know the type of the node we're going
+		 * to read, so we do not know the size of its header. In order
+		 * to minimize the amount of flash IO we assume the header is
+		 * of size = JFFS2_MIN_NODE_HEADER.
+		 */
+		len = JFFS2_MIN_NODE_HEADER;
+		if (jffs2_is_writebuffered(c)) {
+			int end, rem;
+
+			/*
+			 * We are about to read JFFS2_MIN_NODE_HEADER bytes,
+			 * but this flash has some minimal I/O unit. It is
+			 * possible that we'll need to read more soon, so read
+			 * up to the next min. I/O unit, in order not to
+			 * re-read the same min. I/O unit twice.
+			 */
+			end = ref_offset(ref) + len;
+			rem = end % c->wbuf_pagesize;
+			if (rem)
+				end += c->wbuf_pagesize - rem;
+			len = end - ref_offset(ref);
+		}
+
+		dbg_readinode("read %d bytes at %#08x(%d).\n", len, ref_offset(ref), ref_flags(ref));
+
+		/* FIXME: point() */
+		err = jffs2_flash_read(c, ref_offset(ref), len, &retlen, buf);
+		if (err) {
+			JFFS2_ERROR("can not read %d bytes from 0x%08x, error code: %d.\n", len, ref_offset(ref), err);
+			goto free_out;
+		}
+
+		if (retlen < len) {
+			JFFS2_ERROR("short read at %#08x: %zu instead of %d.\n", ref_offset(ref), retlen, len);
+			err = -EIO;
+			goto free_out;
+		}
+
+		node = (union jffs2_node_union *)buf;
+
+		/* No need to mask in the valid bit; it shouldn't be invalid */
+		if (je32_to_cpu(node->u.hdr_crc) != crc32(0, node, sizeof(node->u)-4)) {
+			JFFS2_NOTICE("Node header CRC failed at %#08x. {%04x,%04x,%08x,%08x}\n",
+				     ref_offset(ref), 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_dbg_dump_node(c, ref_offset(ref));
+			jffs2_mark_node_obsolete(c, ref);
+			goto cont;
+		}
+		if (je16_to_cpu(node->u.magic) != JFFS2_MAGIC_BITMASK) {
+			/* Not a JFFS2 node, whinge and move on */
+			JFFS2_NOTICE("Wrong magic bitmask 0x%04x in node header at %#08x.\n",
+				     je16_to_cpu(node->u.magic), ref_offset(ref));
+			jffs2_mark_node_obsolete(c, ref);
+			goto cont;
+		}
+
+		switch (je16_to_cpu(node->u.nodetype)) {
+
+		case JFFS2_NODETYPE_DIRENT:
+
+			if (JFFS2_MIN_NODE_HEADER < sizeof(struct jffs2_raw_dirent) &&
+			    len < sizeof(struct jffs2_raw_dirent)) {
+				err = read_more(c, ref, sizeof(struct jffs2_raw_dirent), &len, buf);
+				if (unlikely(err))
+					goto free_out;
+			}
+
+			err = read_direntry(c, ref, &node->d, retlen, rii);
+			if (unlikely(err))
+				goto free_out;
+
+			break;
+
+		case JFFS2_NODETYPE_INODE:
+
+			if (JFFS2_MIN_NODE_HEADER < sizeof(struct jffs2_raw_inode) &&
+			    len < sizeof(struct jffs2_raw_inode)) {
+				err = read_more(c, ref, sizeof(struct jffs2_raw_inode), &len, buf);
+				if (unlikely(err))
+					goto free_out;
+			}
+
+			err = read_dnode(c, ref, &node->i, len, rii);
+			if (unlikely(err))
+				goto free_out;
+
+			break;
+
+		default:
+			if (JFFS2_MIN_NODE_HEADER < sizeof(struct jffs2_unknown_node) &&
+			    len < sizeof(struct jffs2_unknown_node)) {
+				err = read_more(c, ref, sizeof(struct jffs2_unknown_node), &len, buf);
+				if (unlikely(err))
+					goto free_out;
+			}
+
+			err = read_unknown(c, ref, &node->u);
+			if (unlikely(err))
+				goto free_out;
+
+		}
+	cont:
+		spin_lock(&c->erase_completion_lock);
+	}
+
+	spin_unlock(&c->erase_completion_lock);
+	kfree(buf);
+
+	f->highest_version = rii->highest_version;
+
+	dbg_readinode("nodes of inode #%u were read, the highest version is %u, latest_mctime %u, mctime_ver %u.\n",
+		      f->inocache->ino, rii->highest_version, rii->latest_mctime,
+		      rii->mctime_ver);
+	return 0;
+
+ free_out:
+	jffs2_free_tmp_dnode_info_list(&rii->tn_root);
+	jffs2_free_full_dirent_list(rii->fds);
+	rii->fds = NULL;
+	kfree(buf);
+	return err;
+}
+
+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_readinode_info rii;
+	uint32_t crc, new_size;
+	size_t retlen;
+	int ret;
+
+	dbg_readinode("ino #%u pino/nlink is %d\n", f->inocache->ino,
+		      f->inocache->pino_nlink);
+
+	memset(&rii, 0, sizeof(rii));
+
+	/* Grab all nodes relevant to this ino */
+	ret = jffs2_get_inode_nodes(c, f, &rii);
+
+	if (ret) {
+		JFFS2_ERROR("cannot read nodes for ino %u, returned error is %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;
+	}
+
+	ret = jffs2_build_inode_fragtree(c, f, &rii);
+	if (ret) {
+		JFFS2_ERROR("Failed to build final fragtree for inode #%u: error %d\n",
+			    f->inocache->ino, ret);
+		if (f->inocache->state == INO_STATE_READING)
+			jffs2_set_inocache_state(c, f->inocache, INO_STATE_CHECKEDABSENT);
+		jffs2_free_tmp_dnode_info_list(&rii.tn_root);
+		/* FIXME: We could at least crc-check them all */
+		if (rii.mdata_tn) {
+			jffs2_free_full_dnode(rii.mdata_tn->fn);
+			jffs2_free_tmp_dnode_info(rii.mdata_tn);
+			rii.mdata_tn = NULL;
+		}
+		return ret;
+	}
+
+	if (rii.mdata_tn) {
+		if (rii.mdata_tn->fn->raw == rii.latest_ref) {
+			f->metadata = rii.mdata_tn->fn;
+			jffs2_free_tmp_dnode_info(rii.mdata_tn);
+		} else {
+			jffs2_kill_tn(c, rii.mdata_tn);
+		}
+		rii.mdata_tn = NULL;
+	}
+
+	f->dents = rii.fds;
+
+	jffs2_dbg_fragtree_paranoia_check_nolock(f);
+
+	if (unlikely(!rii.latest_ref)) {
+		/* No data nodes for this inode. */
+		if (f->inocache->ino != 1) {
+			JFFS2_WARNING("no data nodes found for ino #%u\n", f->inocache->ino);
+			if (!rii.fds) {
+				if (f->inocache->state == INO_STATE_READING)
+					jffs2_set_inocache_state(c, f->inocache, INO_STATE_CHECKEDABSENT);
+				return -EIO;
+			}
+			JFFS2_NOTICE("but it has children so we fake some modes for it\n");
+		}
+		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, ref_offset(rii.latest_ref), sizeof(*latest_node), &retlen, (void *)latest_node);
+	if (ret || retlen != sizeof(*latest_node)) {
+		JFFS2_ERROR("failed to read from flash: error %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. */
+		mutex_unlock(&f->sem);
+		jffs2_do_clear_inode(c, f);
+		return ret?ret:-EIO;
+	}
+
+	crc = crc32(0, latest_node, sizeof(*latest_node)-8);
+	if (crc != je32_to_cpu(latest_node->node_crc)) {
+		JFFS2_ERROR("CRC failed for read_inode of inode %u at physical location 0x%x\n",
+			f->inocache->ino, ref_offset(rii.latest_ref));
+		mutex_unlock(&f->sem);
+		jffs2_do_clear_inode(c, f);
+		return -EIO;
+	}
+
+	switch(jemode_to_cpu(latest_node->mode) & S_IFMT) {
+	case S_IFDIR:
+		if (rii.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(rii.latest_mctime);
+		}
+		break;
+
+
+	case S_IFREG:
+		/* If it was a regular file, truncate it to the latest node's isize */
+		new_size = jffs2_truncate_fragtree(c, &f->fragtree, je32_to_cpu(latest_node->isize));
+		if (new_size != je32_to_cpu(latest_node->isize)) {
+			JFFS2_WARNING("Truncating ino #%u to %d bytes failed because it only had %d bytes to start with!\n",
+				      f->inocache->ino, je32_to_cpu(latest_node->isize), new_size);
+			latest_node->isize = cpu_to_je32(new_size);
+		}
+		break;
+
+	case S_IFLNK:
+		/* Hack to work around broken isize in old symlink code.
+		   Remove this when dwmw2 comes to his senses and stops
+		   symlinks from being an entirely gratuitous special
+		   case. */
+		if (!je32_to_cpu(latest_node->isize))
+			latest_node->isize = latest_node->dsize;
+
+		if (f->inocache->state != INO_STATE_CHECKING) {
+			/* Symlink's inode data is the target path. Read it and
+			 * keep in RAM to facilitate quick follow symlink
+			 * operation. */
+			uint32_t csize = je32_to_cpu(latest_node->csize);
+			if (csize > JFFS2_MAX_NAME_LEN) {
+				mutex_unlock(&f->sem);
+				jffs2_do_clear_inode(c, f);
+				return -ENAMETOOLONG;
+			}
+			f->target = kmalloc(csize + 1, GFP_KERNEL);
+			if (!f->target) {
+				JFFS2_ERROR("can't allocate %u bytes of memory for the symlink target path cache\n", csize);
+				mutex_unlock(&f->sem);
+				jffs2_do_clear_inode(c, f);
+				return -ENOMEM;
+			}
+
+			ret = jffs2_flash_read(c, ref_offset(rii.latest_ref) + sizeof(*latest_node),
+					       csize, &retlen, (char *)f->target);
+
+			if (ret || retlen != csize) {
+				if (retlen != csize)
+					ret = -EIO;
+				kfree(f->target);
+				f->target = NULL;
+				mutex_unlock(&f->sem);
+				jffs2_do_clear_inode(c, f);
+				return ret;
+			}
+
+			f->target[csize] = '\0';
+			dbg_readinode("symlink's target '%s' cached\n", f->target);
+		}
+
+		/* fall through... */
+
+	case S_IFBLK:
+	case S_IFCHR:
+		/* Certain inode types should have only one data node, and it's
+		   kept as the metadata node */
+		if (f->metadata) {
+			JFFS2_ERROR("Argh. Special inode #%u with mode 0%o had metadata node\n",
+			       f->inocache->ino, jemode_to_cpu(latest_node->mode));
+			mutex_unlock(&f->sem);
+			jffs2_do_clear_inode(c, f);
+			return -EIO;
+		}
+		if (!frag_first(&f->fragtree)) {
+			JFFS2_ERROR("Argh. Special inode #%u with mode 0%o has no fragments\n",
+			       f->inocache->ino, jemode_to_cpu(latest_node->mode));
+			mutex_unlock(&f->sem);
+			jffs2_do_clear_inode(c, f);
+			return -EIO;
+		}
+		/* ASSERT: f->fraglist != NULL */
+		if (frag_next(frag_first(&f->fragtree))) {
+			JFFS2_ERROR("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 */
+			mutex_unlock(&f->sem);
+			jffs2_do_clear_inode(c, f);
+			return -EIO;
+		}
+		/* OK. We're happy */
+		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;
+}
+
+/* Scan the list of all nodes present for this ino, build map of versions, etc. */
+int jffs2_do_read_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
+			uint32_t ino, struct jffs2_raw_inode *latest_node)
+{
+	dbg_readinode("read inode #%u\n", ino);
+
+ retry_inocache:
+	spin_lock(&c->inocache_lock);
+	f->inocache = jffs2_get_ino_cache(c, ino);
+
+	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. */
+			dbg_readinode("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. */
+			JFFS2_ERROR("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();
+		if (!f->inocache) {
+			JFFS2_ERROR("cannot allocate inocache for root inode\n");
+			return -ENOMEM;
+		}
+		dbg_readinode("creating inocache for root inode\n");
+		memset(f->inocache, 0, sizeof(struct jffs2_inode_cache));
+		f->inocache->ino = f->inocache->pino_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) {
+		JFFS2_ERROR("requestied to read an nonexistent ino %u\n", ino);
+		return -ENOENT;
+	}
+
+	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 = kzalloc(sizeof(*f), GFP_KERNEL);
+	int ret;
+
+	if (!f)
+		return -ENOMEM;
+
+	mutex_init(&f->sem);
+	mutex_lock(&f->sem);
+	f->inocache = ic;
+
+	ret = jffs2_do_read_inode_internal(c, f, &n);
+	if (!ret) {
+		mutex_unlock(&f->sem);
+		jffs2_do_clear_inode(c, f);
+	}
+	jffs2_xattr_do_crccheck_inode(c, ic);
+	kfree (f);
+	return ret;
+}
+
+void jffs2_do_clear_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f)
+{
+	struct jffs2_full_dirent *fd, *fds;
+	int deleted;
+
+	jffs2_xattr_delete_inode(c, f->inocache);
+	mutex_lock(&f->sem);
+	deleted = f->inocache && !f->inocache->pino_nlink;
+
+	if (f->inocache && f->inocache->state != INO_STATE_CHECKING)
+		jffs2_set_inocache_state(c, f->inocache, INO_STATE_CLEARING);
+
+	if (f->metadata) {
+		if (deleted)
+			jffs2_mark_node_obsolete(c, f->metadata->raw);
+		jffs2_free_full_dnode(f->metadata);
+	}
+
+	jffs2_kill_fragtree(&f->fragtree, deleted?c:NULL);
+
+	if (f->target) {
+		kfree(f->target);
+		f->target = NULL;
+	}
+
+	fds = f->dents;
+	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);
+		if (f->inocache->nodes == (void *)f->inocache)
+			jffs2_del_ino_cache(c, f->inocache);
+	}
+
+	mutex_unlock(&f->sem);
+}
Index: cpukit/libfs/src/jffs2/src/scan.c
===================================================================
--- cpukit/libfs/src/jffs2/src/scan.c	(revision 0c0f128e11007cb0d9ebed8b27dbb7cdde18450d)
+++ cpukit/libfs/src/jffs2/src/scan.c	(revision 0c0f128e11007cb0d9ebed8b27dbb7cdde18450d)
@@ -0,0 +1,1171 @@
+/*
+ * JFFS2 -- Journalling Flash File System, Version 2.
+ *
+ * Copyright Â© 2001-2007 Red Hat, Inc.
+ *
+ * Created by David Woodhouse <dwmw2@infradead.org>
+ *
+ * For licensing information, see the file 'LICENCE' in this directory.
+ *
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/mtd/mtd.h>
+#include <linux/pagemap.h>
+#include <linux/crc32.h>
+#include <linux/compiler.h>
+#include "nodelist.h"
+#include "summary.h"
+#include "debug.h"
+
+#define DEFAULT_EMPTY_SCAN_SIZE 256
+
+#define noisy_printk(noise, fmt, ...)					\
+do {									\
+	if (*(noise)) {							\
+		pr_notice(fmt, ##__VA_ARGS__);				\
+		(*(noise))--;						\
+		if (!(*(noise)))					\
+			pr_notice("Further such events for this erase block will not be printed\n"); \
+	}								\
+} while (0)
+
+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, struct jffs2_summary *s);
+
+/* 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_inode_node(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb,
+				 struct jffs2_raw_inode *ri, uint32_t ofs, struct jffs2_summary *s);
+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_summary *s);
+
+static inline int min_free(struct jffs2_sb_info *c)
+{
+	uint32_t min = 2 * sizeof(struct jffs2_raw_inode);
+#ifdef CONFIG_JFFS2_FS_WRITEBUFFER
+	if (!jffs2_can_mark_obsolete(c) && min < c->wbuf_pagesize)
+		return c->wbuf_pagesize;
+#endif
+	return min;
+
+}
+
+static inline uint32_t EMPTY_SCAN_SIZE(uint32_t sector_size) {
+	if (sector_size < DEFAULT_EMPTY_SCAN_SIZE)
+		return sector_size;
+	else
+		return DEFAULT_EMPTY_SCAN_SIZE;
+}
+
+static int file_dirty(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb)
+{
+	int ret;
+
+	if ((ret = jffs2_prealloc_raw_node_refs(c, jeb, 1)))
+		return ret;
+	if ((ret = jffs2_scan_dirty_space(c, jeb, jeb->free_size)))
+		return ret;
+	/* Turned wasted size into dirty, since we apparently 
+	   think it's recoverable now. */
+	jeb->dirty_size += jeb->wasted_size;
+	c->dirty_size += jeb->wasted_size;
+	c->wasted_size -= jeb->wasted_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);
+	}
+	return 0;
+}
+
+int jffs2_scan_medium(struct jffs2_sb_info *c)
+{
+	int i, ret;
+	uint32_t empty_blocks = 0, bad_blocks = 0;
+	unsigned char *flashbuf = NULL;
+	uint32_t buf_size = 0;
+	struct jffs2_summary *s = NULL; /* summary info collected by the scan process */
+#ifndef __ECOS
+	size_t pointlen, try_size;
+
+	ret = mtd_point(c->mtd, 0, c->mtd->size, &pointlen,
+			(void **)&flashbuf, NULL);
+	if (!ret && pointlen < c->mtd->size) {
+		/* Don't muck about if it won't let us point to the whole flash */
+		jffs2_dbg(1, "MTD point returned len too short: 0x%zx\n",
+			  pointlen);
+		mtd_unpoint(c->mtd, 0, pointlen);
+		flashbuf = NULL;
+	}
+	if (ret && ret != -EOPNOTSUPP)
+		jffs2_dbg(1, "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))
+			try_size = c->sector_size;
+		else
+			try_size = PAGE_SIZE;
+
+		jffs2_dbg(1, "Trying to allocate readbuf of %zu "
+			  "bytes\n", try_size);
+
+		flashbuf = mtd_kmalloc_up_to(c->mtd, &try_size);
+		if (!flashbuf)
+			return -ENOMEM;
+
+		jffs2_dbg(1, "Allocated readbuf of %zu bytes\n",
+			  try_size);
+
+		buf_size = (uint32_t)try_size;
+	}
+
+	if (jffs2_sum_active()) {
+		s = kzalloc(sizeof(struct jffs2_summary), GFP_KERNEL);
+		if (!s) {
+			JFFS2_WARNING("Can't allocate memory for summary\n");
+			ret = -ENOMEM;
+			goto out;
+		}
+	}
+
+	for (i=0; i<c->nr_blocks; i++) {
+		struct jffs2_eraseblock *jeb = &c->blocks[i];
+
+		cond_resched();
+
+		/* reset summary info for next eraseblock scan */
+		jffs2_sum_reset_collected(s);
+
+		ret = jffs2_scan_eraseblock(c, jeb, buf_size?flashbuf:(flashbuf+jeb->offset),
+						buf_size, s);
+
+		if (ret < 0)
+			goto out;
+
+		jffs2_dbg_acct_paranoia_check_nolock(c, jeb);
+
+		/* Now decide which list to put it on */
+		switch(ret) {
+		case BLK_STATE_ALLFF:
+			/*
+			 * Empty block.   Since we can't be sure it
+			 * was entirely erased, we just queue it for erase
+			 * again.  It will be marked as such when the erase
+			 * is complete.  Meanwhile we still count it as empty
+			 * for later checks.
+			 */
+			empty_blocks++;
+			list_add(&jeb->list, &c->erase_pending_list);
+			c->nr_erasing_blocks++;
+			break;
+
+		case BLK_STATE_CLEANMARKER:
+			/* Only a CLEANMARKER node is valid */
+			if (!jeb->dirty_size) {
+				/* It's actually free */
+				list_add(&jeb->list, &c->free_list);
+				c->nr_free_blocks++;
+			} else {
+				/* Dirt */
+				jffs2_dbg(1, "Adding all-dirty block at 0x%08x to erase_pending_list\n",
+					  jeb->offset);
+				list_add(&jeb->list, &c->erase_pending_list);
+				c->nr_erasing_blocks++;
+			}
+			break;
+
+		case BLK_STATE_CLEAN:
+			/* Full (or almost full) of clean data. Clean list */
+			list_add(&jeb->list, &c->clean_list);
+			break;
+
+		case BLK_STATE_PARTDIRTY:
+			/* Some data, but not full. Dirty list. */
+			/* We want to remember the block with most free space
+			and stick it in the 'nextblock' position to start writing to it. */
+			if (jeb->free_size > min_free(c) &&
+					(!c->nextblock || c->nextblock->free_size < jeb->free_size)) {
+				/* Better candidate for the next writes to go to */
+				if (c->nextblock) {
+					ret = file_dirty(c, c->nextblock);
+					if (ret)
+						goto out;
+					/* deleting summary information of the old nextblock */
+					jffs2_sum_reset_collected(c->summary);
+				}
+				/* update collected summary information for the current nextblock */
+				jffs2_sum_move_collected(c, s);
+				jffs2_dbg(1, "%s(): new nextblock = 0x%08x\n",
+					  __func__, jeb->offset);
+				c->nextblock = jeb;
+			} else {
+				ret = file_dirty(c, jeb);
+				if (ret)
+					goto out;
+			}
+			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 */
+			jffs2_dbg(1, "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:
+			jffs2_dbg(1, "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:
+			pr_warn("%s(): unknown block state\n", __func__);
+			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;
+	}
+#ifdef CONFIG_JFFS2_FS_WRITEBUFFER
+	if (!jffs2_can_mark_obsolete(c) && c->wbuf_pagesize && c->nextblock && (c->nextblock->free_size % c->wbuf_pagesize)) {
+		/* 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;
+
+		jffs2_dbg(1, "%s(): Skipping %d bytes in nextblock to ensure page alignment\n",
+			  __func__, skip);
+		jffs2_prealloc_raw_node_refs(c, c->nextblock, 1);
+		jffs2_scan_dirty_space(c, c->nextblock, skip);
+	}
+#endif
+	if (c->nr_erasing_blocks) {
+		if ( !c->used_size && ((c->nr_free_blocks+empty_blocks+bad_blocks)!= c->nr_blocks || bad_blocks == c->nr_blocks) ) {
+			pr_notice("Cowardly refusing to erase blocks on filesystem with no valid JFFS2 nodes\n");
+			pr_notice("empty_blocks %d, bad_blocks %d, c->nr_blocks %d\n",
+				  empty_blocks, bad_blocks, c->nr_blocks);
+			ret = -EIO;
+			goto out;
+		}
+		spin_lock(&c->erase_completion_lock);
+		jffs2_garbage_collect_trigger(c);
+		spin_unlock(&c->erase_completion_lock);
+	}
+	ret = 0;
+ out:
+	if (buf_size)
+		kfree(flashbuf);
+#ifndef __ECOS
+	else
+		mtd_unpoint(c->mtd, 0, c->mtd->size);
+#endif
+	kfree(s);
+	return ret;
+}
+
+static int jffs2_fill_scan_buf(struct jffs2_sb_info *c, void *buf,
+			       uint32_t ofs, uint32_t len)
+{
+	int ret;
+	size_t retlen;
+
+	ret = jffs2_flash_read(c, ofs, len, &retlen, buf);
+	if (ret) {
+		jffs2_dbg(1, "mtd->read(0x%x bytes from 0x%x) returned %d\n",
+			  len, ofs, ret);
+		return ret;
+	}
+	if (retlen < len) {
+		jffs2_dbg(1, "Read at 0x%x gave only 0x%zx bytes\n",
+			  ofs, retlen);
+		return -EIO;
+	}
+	return 0;
+}
+
+int jffs2_scan_classify_jeb(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb)
+{
+	if ((jeb->used_size + jeb->unchecked_size) == PAD(c->cleanmarker_size) && !jeb->dirty_size
+	    && (!jeb->first_node || !ref_next(jeb->first_node)) )
+		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;
+}
+
+#ifdef CONFIG_JFFS2_FS_XATTR
+static int jffs2_scan_xattr_node(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb,
+				 struct jffs2_raw_xattr *rx, uint32_t ofs,
+				 struct jffs2_summary *s)
+{
+	struct jffs2_xattr_datum *xd;
+	uint32_t xid, version, totlen, crc;
+	int err;
+
+	crc = crc32(0, rx, sizeof(struct jffs2_raw_xattr) - 4);
+	if (crc != je32_to_cpu(rx->node_crc)) {
+		JFFS2_WARNING("node CRC failed at %#08x, read=%#08x, calc=%#08x\n",
+			      ofs, je32_to_cpu(rx->node_crc), crc);
+		if ((err = jffs2_scan_dirty_space(c, jeb, je32_to_cpu(rx->totlen))))
+			return err;
+		return 0;
+	}
+
+	xid = je32_to_cpu(rx->xid);
+	version = je32_to_cpu(rx->version);
+
+	totlen = PAD(sizeof(struct jffs2_raw_xattr)
+			+ rx->name_len + 1 + je16_to_cpu(rx->value_len));
+	if (totlen != je32_to_cpu(rx->totlen)) {
+		JFFS2_WARNING("node length mismatch at %#08x, read=%u, calc=%u\n",
+			      ofs, je32_to_cpu(rx->totlen), totlen);
+		if ((err = jffs2_scan_dirty_space(c, jeb, je32_to_cpu(rx->totlen))))
+			return err;
+		return 0;
+	}
+
+	xd = jffs2_setup_xattr_datum(c, xid, version);
+	if (IS_ERR(xd))
+		return PTR_ERR(xd);
+
+	if (xd->version > version) {
+		struct jffs2_raw_node_ref *raw
+			= jffs2_link_node_ref(c, jeb, ofs | REF_PRISTINE, totlen, NULL);
+		raw->next_in_ino = xd->node->next_in_ino;
+		xd->node->next_in_ino = raw;
+	} else {
+		xd->version = version;
+		xd->xprefix = rx->xprefix;
+		xd->name_len = rx->name_len;
+		xd->value_len = je16_to_cpu(rx->value_len);
+		xd->data_crc = je32_to_cpu(rx->data_crc);
+
+		jffs2_link_node_ref(c, jeb, ofs | REF_PRISTINE, totlen, (void *)xd);
+	}
+
+	if (jffs2_sum_active())
+		jffs2_sum_add_xattr_mem(s, rx, ofs - jeb->offset);
+	dbg_xattr("scanning xdatum at %#08x (xid=%u, version=%u)\n",
+		  ofs, xd->xid, xd->version);
+	return 0;
+}
+
+static int jffs2_scan_xref_node(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb,
+				struct jffs2_raw_xref *rr, uint32_t ofs,
+				struct jffs2_summary *s)
+{
+	struct jffs2_xattr_ref *ref;
+	uint32_t crc;
+	int err;
+
+	crc = crc32(0, rr, sizeof(*rr) - 4);
+	if (crc != je32_to_cpu(rr->node_crc)) {
+		JFFS2_WARNING("node CRC failed at %#08x, read=%#08x, calc=%#08x\n",
+			      ofs, je32_to_cpu(rr->node_crc), crc);
+		if ((err = jffs2_scan_dirty_space(c, jeb, PAD(je32_to_cpu(rr->totlen)))))
+			return err;
+		return 0;
+	}
+
+	if (PAD(sizeof(struct jffs2_raw_xref)) != je32_to_cpu(rr->totlen)) {
+		JFFS2_WARNING("node length mismatch at %#08x, read=%u, calc=%zd\n",
+			      ofs, je32_to_cpu(rr->totlen),
+			      PAD(sizeof(struct jffs2_raw_xref)));
+		if ((err = jffs2_scan_dirty_space(c, jeb, je32_to_cpu(rr->totlen))))
+			return err;
+		return 0;
+	}
+
+	ref = jffs2_alloc_xattr_ref();
+	if (!ref)
+		return -ENOMEM;
+
+	/* BEFORE jffs2_build_xattr_subsystem() called, 
+	 * and AFTER xattr_ref is marked as a dead xref,
+	 * ref->xid is used to store 32bit xid, xd is not used
+	 * ref->ino is used to store 32bit inode-number, ic is not used
+	 * Thoes variables are declared as union, thus using those
+	 * are exclusive. In a similar way, ref->next is temporarily
+	 * used to chain all xattr_ref object. It's re-chained to
+	 * jffs2_inode_cache in jffs2_build_xattr_subsystem() correctly.
+	 */
+	ref->ino = je32_to_cpu(rr->ino);
+	ref->xid = je32_to_cpu(rr->xid);
+	ref->xseqno = je32_to_cpu(rr->xseqno);
+	if (ref->xseqno > c->highest_xseqno)
+		c->highest_xseqno = (ref->xseqno & ~XREF_DELETE_MARKER);
+	ref->next = c->xref_temp;
+	c->xref_temp = ref;
+
+	jffs2_link_node_ref(c, jeb, ofs | REF_PRISTINE, PAD(je32_to_cpu(rr->totlen)), (void *)ref);
+
+	if (jffs2_sum_active())
+		jffs2_sum_add_xref_mem(s, rr, ofs - jeb->offset);
+	dbg_xattr("scan xref at %#08x (xid=%u, ino=%u)\n",
+		  ofs, ref->xid, ref->ino);
+	return 0;
+}
+#endif
+
+/* Called with 'buf_size == 0' if buf is in fact a pointer _directly_ into
+   the flash, XIP-style */
+static int jffs2_scan_eraseblock (struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb,
+				  unsigned char *buf, uint32_t buf_size, struct jffs2_summary *s) {
+	struct jffs2_unknown_node *node;
+	struct jffs2_unknown_node crcnode;
+	uint32_t ofs, prevofs, max_ofs;
+	uint32_t hdr_crc, buf_ofs, buf_len;
+	int err;
+	int noise = 0;
+
+
+#ifdef CONFIG_JFFS2_FS_WRITEBUFFER
+	int cleanmarkerfound = 0;
+#endif
+
+	ofs = jeb->offset;
+	prevofs = jeb->offset - 1;
+
+	jffs2_dbg(1, "%s(): Scanning block at 0x%x\n", __func__, ofs);
+
+#ifdef CONFIG_JFFS2_FS_WRITEBUFFER
+	if (jffs2_cleanmarker_oob(c)) {
+		int ret;
+
+		if (mtd_block_isbad(c->mtd, jeb->offset))
+			return BLK_STATE_BADBLOCK;
+
+		ret = jffs2_check_nand_cleanmarker(c, jeb);
+		jffs2_dbg(2, "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;
+		default: 	return ret;
+		}
+	}
+#endif
+
+	if (jffs2_sum_active()) {
+		struct jffs2_sum_marker *sm;
+		void *sumptr = NULL;
+		uint32_t sumlen;
+	      
+		if (!buf_size) {
+			/* XIP case. Just look, point at the summary if it's there */
+			sm = (void *)buf + c->sector_size - sizeof(*sm);
+			if (je32_to_cpu(sm->magic) == JFFS2_SUM_MAGIC) {
+				sumptr = buf + je32_to_cpu(sm->offset);
+				sumlen = c->sector_size - je32_to_cpu(sm->offset);
+			}
+		} else {
+			/* If NAND flash, read a whole page of it. Else just the end */
+			if (c->wbuf_pagesize)
+				buf_len = c->wbuf_pagesize;
+			else
+				buf_len = sizeof(*sm);
+
+			/* Read as much as we want into the _end_ of the preallocated buffer */
+			err = jffs2_fill_scan_buf(c, buf + buf_size - buf_len, 
+						  jeb->offset + c->sector_size - buf_len,
+						  buf_len);				
+			if (err)
+				return err;
+
+			sm = (void *)buf + buf_size - sizeof(*sm);
+			if (je32_to_cpu(sm->magic) == JFFS2_SUM_MAGIC) {
+				sumlen = c->sector_size - je32_to_cpu(sm->offset);
+				sumptr = buf + buf_size - sumlen;
+
+				/* Now, make sure the summary itself is available */
+				if (sumlen > buf_size) {
+					/* Need to kmalloc for this. */
+					sumptr = kmalloc(sumlen, GFP_KERNEL);
+					if (!sumptr)
+						return -ENOMEM;
+					memcpy(sumptr + sumlen - buf_len, buf + buf_size - buf_len, buf_len);
+				}
+				if (buf_len < sumlen) {
+					/* Need to read more so that the entire summary node is present */
+					err = jffs2_fill_scan_buf(c, sumptr, 
+								  jeb->offset + c->sector_size - sumlen,
+								  sumlen - buf_len);				
+					if (err)
+						return err;
+				}
+			}
+
+		}
+
+		if (sumptr) {
+			err = jffs2_sum_scan_sumnode(c, jeb, sumptr, sumlen, &pseudo_random);
+
+			if (buf_size && sumlen > buf_size)
+				kfree(sumptr);
+			/* If it returns with a real error, bail. 
+			   If it returns positive, that's a block classification
+			   (i.e. BLK_STATE_xxx) so return that too.
+			   If it returns zero, fall through to full scan. */
+			if (err)
+				return err;
+		}
+	}
+
+	buf_ofs = jeb->offset;
+
+	if (!buf_size) {
+		/* This is the XIP case -- we're reading _directly_ from the flash chip */
+		buf_len = c->sector_size;
+	} else {
+		buf_len = EMPTY_SCAN_SIZE(c->sector_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;
+	max_ofs = EMPTY_SCAN_SIZE(c->sector_size);
+	/* Scan only EMPTY_SCAN_SIZE of 0xFF before declaring it's empty */
+	while(ofs < max_ofs && *(uint32_t *)(&buf[ofs]) == 0xFFFFFFFF)
+		ofs += 4;
+
+	if (ofs == max_ofs) {
+#ifdef CONFIG_JFFS2_FS_WRITEBUFFER
+		if (jffs2_cleanmarker_oob(c)) {
+			/* scan oob, take care of cleanmarker */
+			int ret = jffs2_check_oob_empty(c, jeb, cleanmarkerfound);
+			jffs2_dbg(2, "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;
+			default: 	return ret;
+			}
+		}
+#endif
+		jffs2_dbg(1, "Block at 0x%08x is empty (erased)\n",
+			  jeb->offset);
+		if (c->cleanmarker_size == 0)
+			return BLK_STATE_CLEANMARKER;	/* don't bother with re-erase */
+		else
+			return BLK_STATE_ALLFF;	/* OK to erase if all blocks are like this */
+	}
+	if (ofs) {
+		jffs2_dbg(1, "Free space at %08x ends at %08x\n", jeb->offset,
+			  jeb->offset + ofs);
+		if ((err = jffs2_prealloc_raw_node_refs(c, jeb, 1)))
+			return err;
+		if ((err = jffs2_scan_dirty_space(c, jeb, ofs)))
+			return err;
+	}
+
+	/* Now ofs is a complete physical flash offset as it always was... */
+	ofs += jeb->offset;
+
+	noise = 10;
+
+	dbg_summary("no summary found in jeb 0x%08x. Apply original scan.\n",jeb->offset);
+
+scan_more:
+	while(ofs < jeb->offset + c->sector_size) {
+
+		jffs2_dbg_acct_paranoia_check_nolock(c, jeb);
+
+		/* Make sure there are node refs available for use */
+		err = jffs2_prealloc_raw_node_refs(c, jeb, 2);
+		if (err)
+			return err;
+
+		cond_resched();
+
+		if (ofs & 3) {
+			pr_warn("Eep. ofs 0x%08x not word-aligned!\n", ofs);
+			ofs = PAD(ofs);
+			continue;
+		}
+		if (ofs == prevofs) {
+			pr_warn("ofs 0x%08x has already been seen. Skipping\n",
+				ofs);
+			if ((err = jffs2_scan_dirty_space(c, jeb, 4)))
+				return err;
+			ofs += 4;
+			continue;
+		}
+		prevofs = ofs;
+
+		if (jeb->offset + c->sector_size < ofs + sizeof(*node)) {
+			jffs2_dbg(1, "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));
+			if ((err = jffs2_scan_dirty_space(c, jeb, (jeb->offset + c->sector_size)-ofs)))
+				return err;
+			break;
+		}
+
+		if (buf_ofs + buf_len < ofs + sizeof(*node)) {
+			buf_len = min_t(uint32_t, buf_size, jeb->offset + c->sector_size - ofs);
+			jffs2_dbg(1, "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;
+		}
+
+		node = (struct jffs2_unknown_node *)&buf[ofs-buf_ofs];
+
+		if (*(uint32_t *)(&buf[ofs-buf_ofs]) == 0xffffffff) {
+			uint32_t inbuf_ofs;
+			uint32_t empty_start, scan_end;
+
+			empty_start = ofs;
+			ofs += 4;
+			scan_end = min_t(uint32_t, EMPTY_SCAN_SIZE(c->sector_size)/8, buf_len);
+
+			jffs2_dbg(1, "Found empty flash at 0x%08x\n", ofs);
+		more_empty:
+			inbuf_ofs = ofs - buf_ofs;
+			while (inbuf_ofs < scan_end) {
+				if (unlikely(*(uint32_t *)(&buf[inbuf_ofs]) != 0xffffffff)) {
+					pr_warn("Empty flash at 0x%08x ends at 0x%08x\n",
+						empty_start, ofs);
+					if ((err = jffs2_scan_dirty_space(c, jeb, ofs-empty_start)))
+						return err;
+					goto scan_more;
+				}
+
+				inbuf_ofs+=4;
+				ofs += 4;
+			}
+			/* Ran off end. */
+			jffs2_dbg(1, "Empty flash to end of buffer at 0x%08x\n",
+				  ofs);
+
+			/* If we're only checking the beginning of a block with a cleanmarker,
+			   bail now */
+			if (buf_ofs == jeb->offset && jeb->used_size == PAD(c->cleanmarker_size) &&
+			    c->cleanmarker_size && !jeb->dirty_size && !ref_next(jeb->first_node)) {
+				jffs2_dbg(1, "%d bytes at start of block seems clean... assuming all clean\n",
+					  EMPTY_SCAN_SIZE(c->sector_size));
+				return BLK_STATE_CLEANMARKER;
+			}
+			if (!buf_size && (scan_end != buf_len)) {/* XIP/point case */
+				scan_end = buf_len;
+				goto more_empty;
+			}
+			
+			/* See how much more there is to read in this eraseblock... */
+			buf_len = min_t(uint32_t, buf_size, jeb->offset + c->sector_size - ofs);
+			if (!buf_len) {
+				/* No more to read. Break out of main loop without marking
+				   this range of empty space as dirty (because it's not) */
+				jffs2_dbg(1, "Empty flash at %08x runs to end of block. Treating as free_space\n",
+					  empty_start);
+				break;
+			}
+			/* point never reaches here */
+			scan_end = buf_len;
+			jffs2_dbg(1, "Reading another 0x%x at 0x%08x\n",
+				  buf_len, ofs);
+			err = jffs2_fill_scan_buf(c, buf, ofs, buf_len);
+			if (err)
+				return err;
+			buf_ofs = ofs;
+			goto more_empty;
+		}
+
+		if (ofs == jeb->offset && je16_to_cpu(node->magic) == KSAMTIB_CIGAM_2SFFJ) {
+			pr_warn("Magic bitmask is backwards at offset 0x%08x. Wrong endian filesystem?\n",
+				ofs);
+			if ((err = jffs2_scan_dirty_space(c, jeb, 4)))
+				return err;
+			ofs += 4;
+			continue;
+		}
+		if (je16_to_cpu(node->magic) == JFFS2_DIRTY_BITMASK) {
+			jffs2_dbg(1, "Dirty bitmask at 0x%08x\n", ofs);
+			if ((err = jffs2_scan_dirty_space(c, jeb, 4)))
+				return err;
+			ofs += 4;
+			continue;
+		}
+		if (je16_to_cpu(node->magic) == JFFS2_OLD_MAGIC_BITMASK) {
+			pr_warn("Old JFFS2 bitmask found at 0x%08x\n", ofs);
+			pr_warn("You cannot use older JFFS2 filesystems with newer kernels\n");
+			if ((err = jffs2_scan_dirty_space(c, jeb, 4)))
+				return err;
+			ofs += 4;
+			continue;
+		}
+		if (je16_to_cpu(node->magic) != JFFS2_MAGIC_BITMASK) {
+			/* OK. We're out of possibilities. Whinge and move on */
+			noisy_printk(&noise, "%s(): Magic bitmask 0x%04x not found at 0x%08x: 0x%04x instead\n",
+				     __func__,
+				     JFFS2_MAGIC_BITMASK, ofs,
+				     je16_to_cpu(node->magic));
+			if ((err = jffs2_scan_dirty_space(c, jeb, 4)))
+				return err;
+			ofs += 4;
+			continue;
+		}
+		/* We seem to have a node of sorts. Check the 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, "%s(): Node at 0x%08x {0x%04x, 0x%04x, 0x%08x) has invalid CRC 0x%08x (calculated 0x%08x)\n",
+				     __func__,
+				     ofs, je16_to_cpu(node->magic),
+				     je16_to_cpu(node->nodetype),
+				     je32_to_cpu(node->totlen),
+				     je32_to_cpu(node->hdr_crc),
+				     hdr_crc);
+			if ((err = jffs2_scan_dirty_space(c, jeb, 4)))
+				return err;
+			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. */
+			pr_warn("Node at 0x%08x with length 0x%08x would run over the end of the erase block\n",
+				ofs, je32_to_cpu(node->totlen));
+			pr_warn("Perhaps the file system was created with the wrong erase size?\n");
+			if ((err = jffs2_scan_dirty_space(c, jeb, 4)))
+				return err;
+			ofs += 4;
+			continue;
+		}
+
+		if (!(je16_to_cpu(node->nodetype) & JFFS2_NODE_ACCURATE)) {
+			/* Wheee. This is an obsoleted node */
+			jffs2_dbg(2, "Node at 0x%08x is obsolete. Skipping\n",
+				  ofs);
+			if ((err = jffs2_scan_dirty_space(c, jeb, PAD(je32_to_cpu(node->totlen)))))
+				return err;
+			ofs += PAD(je32_to_cpu(node->totlen));
+			continue;
+		}
+
+		switch(je16_to_cpu(node->nodetype)) {
+		case JFFS2_NODETYPE_INODE:
+			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);
+				jffs2_dbg(1, "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, s);
+			if (err) return err;
+			ofs += PAD(je32_to_cpu(node->totlen));
+			break;
+
+		case JFFS2_NODETYPE_DIRENT:
+			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);
+				jffs2_dbg(1, "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, s);
+			if (err) return err;
+			ofs += PAD(je32_to_cpu(node->totlen));
+			break;
+
+#ifdef CONFIG_JFFS2_FS_XATTR
+		case JFFS2_NODETYPE_XATTR:
+			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);
+				jffs2_dbg(1, "Fewer than %d bytes (xattr 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_xattr_node(c, jeb, (void *)node, ofs, s);
+			if (err)
+				return err;
+			ofs += PAD(je32_to_cpu(node->totlen));
+			break;
+		case JFFS2_NODETYPE_XREF:
+			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);
+				jffs2_dbg(1, "Fewer than %d bytes (xref 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_xref_node(c, jeb, (void *)node, ofs, s);
+			if (err)
+				return err;
+			ofs += PAD(je32_to_cpu(node->totlen));
+			break;
+#endif	/* CONFIG_JFFS2_FS_XATTR */
+
+		case JFFS2_NODETYPE_CLEANMARKER:
+			jffs2_dbg(1, "CLEANMARKER node found at 0x%08x\n", ofs);
+			if (je32_to_cpu(node->totlen) != c->cleanmarker_size) {
+				pr_notice("CLEANMARKER node found at 0x%08x has totlen 0x%x != normal 0x%x\n",
+					  ofs, je32_to_cpu(node->totlen),
+					  c->cleanmarker_size);
+				if ((err = jffs2_scan_dirty_space(c, jeb, PAD(sizeof(struct jffs2_unknown_node)))))
+					return err;
+				ofs += PAD(sizeof(struct jffs2_unknown_node));
+			} else if (jeb->first_node) {
+				pr_notice("CLEANMARKER node found at 0x%08x, not first node in block (0x%08x)\n",
+					  ofs, jeb->offset);
+				if ((err = jffs2_scan_dirty_space(c, jeb, PAD(sizeof(struct jffs2_unknown_node)))))
+					return err;
+				ofs += PAD(sizeof(struct jffs2_unknown_node));
+			} else {
+				jffs2_link_node_ref(c, jeb, ofs | REF_NORMAL, c->cleanmarker_size, NULL);
+
+				ofs += PAD(c->cleanmarker_size);
+			}
+			break;
+
+		case JFFS2_NODETYPE_PADDING:
+			if (jffs2_sum_active())
+				jffs2_sum_add_padding_mem(s, je32_to_cpu(node->totlen));
+			if ((err = jffs2_scan_dirty_space(c, jeb, PAD(je32_to_cpu(node->totlen)))))
+				return err;
+			ofs += PAD(je32_to_cpu(node->totlen));
+			break;
+
+		default:
+			switch (je16_to_cpu(node->nodetype) & JFFS2_COMPAT_MASK) {
+			case JFFS2_FEATURE_ROCOMPAT:
+				pr_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;
+				if ((err = jffs2_scan_dirty_space(c, jeb, PAD(je32_to_cpu(node->totlen)))))
+					return err;
+				ofs += PAD(je32_to_cpu(node->totlen));
+				break;
+
+			case JFFS2_FEATURE_INCOMPAT:
+				pr_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:
+				jffs2_dbg(1, "Unknown but compatible feature node (0x%04x) found at offset 0x%08x\n",
+					  je16_to_cpu(node->nodetype), ofs);
+				if ((err = jffs2_scan_dirty_space(c, jeb, PAD(je32_to_cpu(node->totlen)))))
+					return err;
+				ofs += PAD(je32_to_cpu(node->totlen));
+				break;
+
+			case JFFS2_FEATURE_RWCOMPAT_COPY: {
+				jffs2_dbg(1, "Unknown but compatible feature node (0x%04x) found at offset 0x%08x\n",
+					  je16_to_cpu(node->nodetype), ofs);
+
+				jffs2_link_node_ref(c, jeb, ofs | REF_PRISTINE, PAD(je32_to_cpu(node->totlen)), NULL);
+
+				/* We can't summarise nodes we don't grok */
+				jffs2_sum_disable_collecting(s);
+				ofs += PAD(je32_to_cpu(node->totlen));
+				break;
+				}
+			}
+		}
+	}
+
+	if (jffs2_sum_active()) {
+		if (PAD(s->sum_size + JFFS2_SUMMARY_FRAME_SIZE) > jeb->free_size) {
+			dbg_summary("There is not enough space for "
+				"summary information, disabling for this jeb!\n");
+			jffs2_sum_disable_collecting(s);
+		}
+	}
+
+	jffs2_dbg(1, "Block at 0x%08x: free 0x%08x, dirty 0x%08x, unchecked 0x%08x, used 0x%08x, wasted 0x%08x\n",
+		  jeb->offset, jeb->free_size, jeb->dirty_size,
+		  jeb->unchecked_size, jeb->used_size, jeb->wasted_size);
+	
+	/* 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;
+	}
+
+	return jffs2_scan_classify_jeb(c, jeb);
+}
+
+struct jffs2_inode_cache *jffs2_scan_make_ino_cache(struct jffs2_sb_info *c, uint32_t ino)
+{
+	struct jffs2_inode_cache *ic;
+
+	ic = jffs2_get_ino_cache(c, ino);
+	if (ic)
+		return ic;
+
+	if (ino > c->highest_ino)
+		c->highest_ino = ino;
+
+	ic = jffs2_alloc_inode_cache();
+	if (!ic) {
+		pr_notice("%s(): allocation of inode cache failed\n", __func__);
+		return NULL;
+	}
+	memset(ic, 0, sizeof(*ic));
+
+	ic->ino = ino;
+	ic->nodes = (void *)ic;
+	jffs2_add_ino_cache(c, ic);
+	if (ino == 1)
+		ic->pino_nlink = 1;
+	return ic;
+}
+
+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_summary *s)
+{
+	struct jffs2_inode_cache *ic;
+	uint32_t crc, ino = je32_to_cpu(ri->ino);
+
+	jffs2_dbg(1, "%s(): Node at 0x%08x\n", __func__, ofs);
+
+	/* 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. */
+
+	/* Check the node CRC in any case. */
+	crc = crc32(0, ri, sizeof(*ri)-8);
+	if (crc != je32_to_cpu(ri->node_crc)) {
+		pr_notice("%s(): CRC failed on node at 0x%08x: Read 0x%08x, calculated 0x%08x\n",
+			  __func__, 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.
+		 */
+		return jffs2_scan_dirty_space(c, jeb,
+					      PAD(je32_to_cpu(ri->totlen)));
+	}
+
+	ic = jffs2_get_ino_cache(c, ino);
+	if (!ic) {
+		ic = jffs2_scan_make_ino_cache(c, ino);
+		if (!ic)
+			return -ENOMEM;
+	}
+
+	/* Wheee. It worked */
+	jffs2_link_node_ref(c, jeb, ofs | REF_UNCHECKED, PAD(je32_to_cpu(ri->totlen)), ic);
+
+	jffs2_dbg(1, "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);
+
+	if (jffs2_sum_active()) {
+		jffs2_sum_add_inode_mem(s, ri, ofs - jeb->offset);
+	}
+
+	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_summary *s)
+{
+	struct jffs2_full_dirent *fd;
+	struct jffs2_inode_cache *ic;
+	uint32_t checkedlen;
+	uint32_t crc;
+	int err;
+
+	jffs2_dbg(1, "%s(): Node at 0x%08x\n", __func__, 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)) {
+		pr_notice("%s(): Node CRC failed on node at 0x%08x: Read 0x%08x, calculated 0x%08x\n",
+			  __func__, 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. */
+		if ((err = jffs2_scan_dirty_space(c, jeb, PAD(je32_to_cpu(rd->totlen)))))
+			return err;
+		return 0;
+	}
+
+	pseudo_random += je32_to_cpu(rd->version);
+
+	/* Should never happen. Did. (OLPC trac #4184)*/
+	checkedlen = strnlen(rd->name, rd->nsize);
+	if (checkedlen < rd->nsize) {
+		pr_err("Dirent at %08x has zeroes in name. Truncating to %d chars\n",
+		       ofs, checkedlen);
+	}
+	fd = jffs2_alloc_full_dirent(checkedlen+1);
+	if (!fd) {
+		return -ENOMEM;
+	}
+	memcpy(&fd->name, rd->name, checkedlen);
+	fd->name[checkedlen] = 0;
+
+	crc = crc32(0, fd->name, rd->nsize);
+	if (crc != je32_to_cpu(rd->name_crc)) {
+		pr_notice("%s(): Name CRC failed on node at 0x%08x: Read 0x%08x, calculated 0x%08x\n",
+			  __func__, ofs, je32_to_cpu(rd->name_crc), crc);
+		jffs2_dbg(1, "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. */
+		if ((err = jffs2_scan_dirty_space(c, jeb, PAD(je32_to_cpu(rd->totlen)))))
+			return err;
+		return 0;
+	}
+	ic = jffs2_scan_make_ino_cache(c, je32_to_cpu(rd->pino));
+	if (!ic) {
+		jffs2_free_full_dirent(fd);
+		return -ENOMEM;
+	}
+
+	fd->raw = jffs2_link_node_ref(c, jeb, ofs | dirent_node_state(rd),
+				      PAD(je32_to_cpu(rd->totlen)), ic);
+
+	fd->next = NULL;
+	fd->version = je32_to_cpu(rd->version);
+	fd->ino = je32_to_cpu(rd->ino);
+	fd->nhash = full_name_hash(fd->name, checkedlen);
+	fd->type = rd->type;
+	jffs2_add_fd_to_list(c, fd, &ic->scan_dents);
+
+	if (jffs2_sum_active()) {
+		jffs2_sum_add_dirent_mem(s, rd, ofs - jeb->offset);
+	}
+
+	return 0;
+}
+
+static int count_list(struct list_head *l)
+{
+	uint32_t count = 0;
+	struct list_head *tmp;
+
+	list_for_each(tmp, l) {
+		count++;
+	}
+	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);
+}
+
+void jffs2_rotate_lists(struct jffs2_sb_info *c)
+{
+	uint32_t x;
+	uint32_t rotateby;
+
+	x = count_list(&c->clean_list);
+	if (x) {
+		rotateby = pseudo_random % x;
+		rotate_list((&c->clean_list), rotateby);
+	}
+
+	x = count_list(&c->very_dirty_list);
+	if (x) {
+		rotateby = pseudo_random % x;
+		rotate_list((&c->very_dirty_list), rotateby);
+	}
+
+	x = count_list(&c->dirty_list);
+	if (x) {
+		rotateby = pseudo_random % x;
+		rotate_list((&c->dirty_list), rotateby);
+	}
+
+	x = count_list(&c->erasable_list);
+	if (x) {
+		rotateby = pseudo_random % x;
+		rotate_list((&c->erasable_list), rotateby);
+	}
+
+	if (c->nr_erasing_blocks) {
+		rotateby = pseudo_random % c->nr_erasing_blocks;
+		rotate_list((&c->erase_pending_list), rotateby);
+	}
+
+	if (c->nr_free_blocks) {
+		rotateby = pseudo_random % c->nr_free_blocks;
+		rotate_list((&c->free_list), rotateby);
+	}
+}
Index: cpukit/libfs/src/jffs2/src/summary.h
===================================================================
--- cpukit/libfs/src/jffs2/src/summary.h	(revision 0c0f128e11007cb0d9ebed8b27dbb7cdde18450d)
+++ cpukit/libfs/src/jffs2/src/summary.h	(revision 0c0f128e11007cb0d9ebed8b27dbb7cdde18450d)
@@ -0,0 +1,213 @@
+/*
+ * JFFS2 -- Journalling Flash File System, Version 2.
+ *
+ * Copyright Â© 2004  Ferenc Havasi <havasi@inf.u-szeged.hu>,
+ *		     Zoltan Sogor <weth@inf.u-szeged.hu>,
+ *		     Patrik Kluba <pajko@halom.u-szeged.hu>,
+ *		     University of Szeged, Hungary
+ *
+ * For licensing information, see the file 'LICENCE' in this directory.
+ *
+ */
+
+#ifndef JFFS2_SUMMARY_H
+#define JFFS2_SUMMARY_H
+
+/* Limit summary size to 64KiB so that we can kmalloc it. If the summary
+   is larger than that, we have to just ditch it and avoid using summary
+   for the eraseblock in question... and it probably doesn't hurt us much
+   anyway. */
+#define MAX_SUMMARY_SIZE 65536
+
+#include <linux/uio.h>
+#include <linux/jffs2.h>
+
+#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
+
+#define JFFS2_SUMMARY_NOSUM_SIZE 0xffffffff
+#define JFFS2_SUMMARY_INODE_SIZE (sizeof(struct jffs2_sum_inode_flash))
+#define JFFS2_SUMMARY_DIRENT_SIZE(x) (sizeof(struct jffs2_sum_dirent_flash) + (x))
+#define JFFS2_SUMMARY_XATTR_SIZE (sizeof(struct jffs2_sum_xattr_flash))
+#define JFFS2_SUMMARY_XREF_SIZE (sizeof(struct jffs2_sum_xref_flash))
+
+/* Summary structures used on flash */
+
+struct jffs2_sum_unknown_flash
+{
+	jint16_t nodetype;	/* node type */
+};
+
+struct jffs2_sum_inode_flash
+{
+	jint16_t nodetype;	/* node type */
+	jint32_t inode;		/* inode number */
+	jint32_t version;	/* inode version */
+	jint32_t offset;	/* offset on jeb */
+	jint32_t totlen; 	/* record length */
+} __attribute__((packed));
+
+struct jffs2_sum_dirent_flash
+{
+	jint16_t nodetype;	/* == JFFS_NODETYPE_DIRENT */
+	jint32_t totlen;	/* record length */
+	jint32_t offset;	/* offset on jeb */
+	jint32_t pino;		/* parent inode */
+	jint32_t version;	/* dirent version */
+	jint32_t ino; 		/* == zero for unlink */
+	uint8_t nsize;		/* dirent name size */
+	uint8_t type;		/* dirent type */
+	uint8_t name[0];	/* dirent name */
+} __attribute__((packed));
+
+struct jffs2_sum_xattr_flash
+{
+	jint16_t nodetype;	/* == JFFS2_NODETYPE_XATR */
+	jint32_t xid;		/* xattr identifier */
+	jint32_t version;	/* version number */
+	jint32_t offset;	/* offset on jeb */
+	jint32_t totlen;	/* node length */
+} __attribute__((packed));
+
+struct jffs2_sum_xref_flash
+{
+	jint16_t nodetype;	/* == JFFS2_NODETYPE_XREF */
+	jint32_t offset;	/* offset on jeb */
+} __attribute__((packed));
+
+union jffs2_sum_flash
+{
+	struct jffs2_sum_unknown_flash u;
+	struct jffs2_sum_inode_flash i;
+	struct jffs2_sum_dirent_flash d;
+	struct jffs2_sum_xattr_flash x;
+	struct jffs2_sum_xref_flash r;
+};
+
+/* Summary structures used in the memory */
+
+struct jffs2_sum_unknown_mem
+{
+	union jffs2_sum_mem *next;
+	jint16_t nodetype;	/* node type */
+};
+
+struct jffs2_sum_inode_mem
+{
+	union jffs2_sum_mem *next;
+	jint16_t nodetype;	/* node type */
+	jint32_t inode;		/* inode number */
+	jint32_t version;	/* inode version */
+	jint32_t offset;	/* offset on jeb */
+	jint32_t totlen; 	/* record length */
+} __attribute__((packed));
+
+struct jffs2_sum_dirent_mem
+{
+	union jffs2_sum_mem *next;
+	jint16_t nodetype;	/* == JFFS_NODETYPE_DIRENT */
+	jint32_t totlen;	/* record length */
+	jint32_t offset;	/* ofset on jeb */
+	jint32_t pino;		/* parent inode */
+	jint32_t version;	/* dirent version */
+	jint32_t ino; 		/* == zero for unlink */
+	uint8_t nsize;		/* dirent name size */
+	uint8_t type;		/* dirent type */
+	uint8_t name[0];	/* dirent name */
+} __attribute__((packed));
+
+struct jffs2_sum_xattr_mem
+{
+	union jffs2_sum_mem *next;
+	jint16_t nodetype;
+	jint32_t xid;
+	jint32_t version;
+	jint32_t offset;
+	jint32_t totlen;
+} __attribute__((packed));
+
+struct jffs2_sum_xref_mem
+{
+	union jffs2_sum_mem *next;
+	jint16_t nodetype;
+	jint32_t offset;
+} __attribute__((packed));
+
+union jffs2_sum_mem
+{
+	struct jffs2_sum_unknown_mem u;
+	struct jffs2_sum_inode_mem i;
+	struct jffs2_sum_dirent_mem d;
+	struct jffs2_sum_xattr_mem x;
+	struct jffs2_sum_xref_mem r;
+};
+
+/* Summary related information stored in superblock */
+
+struct jffs2_summary
+{
+	uint32_t sum_size;      /* collected summary information for nextblock */
+	uint32_t sum_num;
+	uint32_t sum_padded;
+	union jffs2_sum_mem *sum_list_head;
+	union jffs2_sum_mem *sum_list_tail;
+
+	jint32_t *sum_buf;	/* buffer for writing out summary */
+};
+
+/* Summary marker is stored at the end of every sumarized erase block */
+
+struct jffs2_sum_marker
+{
+	jint32_t offset;	/* offset of the summary node in the jeb */
+	jint32_t magic; 	/* == JFFS2_SUM_MAGIC */
+};
+
+#define JFFS2_SUMMARY_FRAME_SIZE (sizeof(struct jffs2_raw_summary) + sizeof(struct jffs2_sum_marker))
+
+#ifdef CONFIG_JFFS2_SUMMARY	/* SUMMARY SUPPORT ENABLED */
+
+#define jffs2_sum_active() (1)
+int jffs2_sum_init(struct jffs2_sb_info *c);
+void jffs2_sum_exit(struct jffs2_sb_info *c);
+void jffs2_sum_disable_collecting(struct jffs2_summary *s);
+int jffs2_sum_is_disabled(struct jffs2_summary *s);
+void jffs2_sum_reset_collected(struct jffs2_summary *s);
+void jffs2_sum_move_collected(struct jffs2_sb_info *c, struct jffs2_summary *s);
+int jffs2_sum_add_kvec(struct jffs2_sb_info *c, const struct kvec *invecs,
+			unsigned long count,  uint32_t to);
+int jffs2_sum_write_sumnode(struct jffs2_sb_info *c);
+int jffs2_sum_add_padding_mem(struct jffs2_summary *s, uint32_t size);
+int jffs2_sum_add_inode_mem(struct jffs2_summary *s, struct jffs2_raw_inode *ri, uint32_t ofs);
+int jffs2_sum_add_dirent_mem(struct jffs2_summary *s, struct jffs2_raw_dirent *rd, uint32_t ofs);
+int jffs2_sum_add_xattr_mem(struct jffs2_summary *s, struct jffs2_raw_xattr *rx, uint32_t ofs);
+int jffs2_sum_add_xref_mem(struct jffs2_summary *s, struct jffs2_raw_xref *rr, uint32_t ofs);
+int jffs2_sum_scan_sumnode(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb,
+			   struct jffs2_raw_summary *summary, uint32_t sumlen,
+			   uint32_t *pseudo_random);
+
+#else				/* SUMMARY DISABLED */
+
+#define jffs2_sum_active() (0)
+#define jffs2_sum_init(a) (0)
+#define jffs2_sum_exit(a)
+#define jffs2_sum_disable_collecting(a)
+#define jffs2_sum_is_disabled(a) (0)
+#define jffs2_sum_reset_collected(a)
+#define jffs2_sum_add_kvec(a,b,c,d) (0)
+#define jffs2_sum_move_collected(a,b)
+#define jffs2_sum_write_sumnode(a) (0)
+#define jffs2_sum_add_padding_mem(a,b)
+#define jffs2_sum_add_inode_mem(a,b,c)
+#define jffs2_sum_add_dirent_mem(a,b,c)
+#define jffs2_sum_add_xattr_mem(a,b,c)
+#define jffs2_sum_add_xref_mem(a,b,c)
+#define jffs2_sum_scan_sumnode(a,b,c,d,e) (0)
+
+#endif /* CONFIG_JFFS2_SUMMARY */
+
+#endif /* JFFS2_SUMMARY_H */
Index: cpukit/libfs/src/jffs2/src/write.c
===================================================================
--- cpukit/libfs/src/jffs2/src/write.c	(revision 0c0f128e11007cb0d9ebed8b27dbb7cdde18450d)
+++ cpukit/libfs/src/jffs2/src/write.c	(revision 0c0f128e11007cb0d9ebed8b27dbb7cdde18450d)
@@ -0,0 +1,722 @@
+/*
+ * JFFS2 -- Journalling Flash File System, Version 2.
+ *
+ * Copyright Â© 2001-2007 Red Hat, Inc.
+ *
+ * Created by David Woodhouse <dwmw2@infradead.org>
+ *
+ * For licensing information, see the file 'LICENCE' in this directory.
+ *
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/kernel.h>
+#include <linux/fs.h>
+#include <linux/crc32.h>
+#include <linux/pagemap.h>
+#include <linux/mtd/mtd.h>
+#include "nodelist.h"
+#include "compr.h"
+
+
+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_inode_cache *ic;
+
+	ic = jffs2_alloc_inode_cache();
+	if (!ic) {
+		return -ENOMEM;
+	}
+
+	memset(ic, 0, sizeof(*ic));
+
+	f->inocache = ic;
+	f->inocache->pino_nlink = 1; /* Will be overwritten shortly for directories */
+	f->inocache->nodes = (struct jffs2_raw_node_ref *)f->inocache;
+	f->inocache->state = INO_STATE_PRESENT;
+
+	jffs2_add_ino_cache(c, f->inocache);
+	jffs2_dbg(1, "%s(): Assigned ino# %d\n", __func__, f->inocache->ino);
+	ri->ino = cpu_to_je32(f->inocache->ino);
+
+	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_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 */
+
+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, int alloc_mode)
+
+{
+	struct jffs2_full_dnode *fn;
+	size_t retlen;
+	uint32_t flash_ofs;
+	struct kvec vecs[2];
+	int ret;
+	int retried = 0;
+	unsigned long cnt = 2;
+
+	D1(if(je32_to_cpu(ri->hdr_crc) != crc32(0, ri, sizeof(struct jffs2_unknown_node)-4)) {
+		pr_crit("Eep. CRC not correct in jffs2_write_dnode()\n");
+		BUG();
+	}
+	   );
+	vecs[0].iov_base = ri;
+	vecs[0].iov_len = sizeof(*ri);
+	vecs[1].iov_base = (unsigned char *)data;
+	vecs[1].iov_len = datalen;
+
+	if (je32_to_cpu(ri->totlen) != sizeof(*ri) + datalen) {
+		pr_warn("%s(): ri->totlen (0x%08x) != sizeof(*ri) (0x%08zx) + datalen (0x%08x)\n",
+			__func__, je32_to_cpu(ri->totlen),
+			sizeof(*ri), datalen);
+	}
+
+	fn = jffs2_alloc_full_dnode();
+	if (!fn)
+		return ERR_PTR(-ENOMEM);
+
+	/* check number of valid vecs */
+	if (!datalen || !data)
+		cnt = 1;
+ retry:
+	flash_ofs = write_ofs(c);
+
+	jffs2_dbg_prewrite_paranoia_check(c, flash_ofs, vecs[0].iov_len + vecs[1].iov_len);
+
+	if ((alloc_mode!=ALLOC_GC) && (je32_to_cpu(ri->version) < f->highest_version)) {
+		BUG_ON(!retried);
+		jffs2_dbg(1, "%s(): dnode_version %d, highest version %d -> updating dnode\n",
+			  __func__,
+			  je32_to_cpu(ri->version), f->highest_version);
+		ri->version = cpu_to_je32(++f->highest_version);
+		ri->node_crc = cpu_to_je32(crc32(0, ri, sizeof(*ri)-8));
+	}
+
+	ret = jffs2_flash_writev(c, vecs, cnt, flash_ofs, &retlen,
+				 (alloc_mode==ALLOC_GC)?0:f->inocache->ino);
+
+	if (ret || (retlen != sizeof(*ri) + datalen)) {
+		pr_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) {
+			/* Don't change raw->size to match retlen. We may have
+			   written the node header already, and only the data will
+			   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, flash_ofs | REF_OBSOLETE, PAD(sizeof(*ri)+datalen), NULL);
+		} else {
+			pr_notice("Not marking the space at 0x%08x as dirty because the flash driver returned retlen zero\n",
+				  flash_ofs);
+		}
+		if (!retried && alloc_mode != ALLOC_NORETRY) {
+			/* Try to reallocate space and retry */
+			uint32_t dummy;
+			struct jffs2_eraseblock *jeb = &c->blocks[flash_ofs / c->sector_size];
+
+			retried = 1;
+
+			jffs2_dbg(1, "Retrying failed write.\n");
+
+			jffs2_dbg_acct_sanity_check(c,jeb);
+			jffs2_dbg_acct_paranoia_check(c, jeb);
+
+			if (alloc_mode == ALLOC_GC) {
+				ret = jffs2_reserve_space_gc(c, sizeof(*ri) + datalen, &dummy,
+							     JFFS2_SUMMARY_INODE_SIZE);
+			} else {
+				/* Locking pain */
+				mutex_unlock(&f->sem);
+				jffs2_complete_reservation(c);
+
+				ret = jffs2_reserve_space(c, sizeof(*ri) + datalen, &dummy,
+							  alloc_mode, JFFS2_SUMMARY_INODE_SIZE);
+				mutex_lock(&f->sem);
+			}
+
+			if (!ret) {
+				flash_ofs = write_ofs(c);
+				jffs2_dbg(1, "Allocated space at 0x%08x to retry failed write.\n",
+					  flash_ofs);
+
+				jffs2_dbg_acct_sanity_check(c,jeb);
+				jffs2_dbg_acct_paranoia_check(c, jeb);
+
+				goto retry;
+			}
+			jffs2_dbg(1, "Failed to allocate space to retry failed write: %d!\n",
+				  ret);
+		}
+		/* Release the full_dnode which is now useless, and return */
+		jffs2_free_full_dnode(fn);
+		return ERR_PTR(ret?ret:-EIO);
+	}
+	/* Mark the space used */
+	/* 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)))) {
+		flash_ofs |= REF_PRISTINE;
+	} else {
+		flash_ofs |= REF_NORMAL;
+	}
+	fn->raw = jffs2_add_physical_node_ref(c, flash_ofs, PAD(sizeof(*ri)+datalen), f->inocache);
+	if (IS_ERR(fn->raw)) {
+		void *hold_err = fn->raw;
+		/* Release the full_dnode which is now useless, and return */
+		jffs2_free_full_dnode(fn);
+		return ERR_CAST(hold_err);
+	}
+	fn->ofs = je32_to_cpu(ri->offset);
+	fn->size = je32_to_cpu(ri->dsize);
+	fn->frags = 0;
+
+	jffs2_dbg(1, "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 & ~3, flash_ofs & 3, 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 (retried) {
+		jffs2_dbg_acct_sanity_check(c,NULL);
+	}
+
+	return fn;
+}
+
+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, int alloc_mode)
+{
+	struct jffs2_full_dirent *fd;
+	size_t retlen;
+	struct kvec vecs[2];
+	uint32_t flash_ofs;
+	int retried = 0;
+	int ret;
+
+	jffs2_dbg(1, "%s(ino #%u, name at *0x%p \"%s\"->ino #%u, name_crc 0x%08x)\n",
+		  __func__,
+		  je32_to_cpu(rd->pino), name, name, je32_to_cpu(rd->ino),
+		  je32_to_cpu(rd->name_crc));
+
+	D1(if(je32_to_cpu(rd->hdr_crc) != crc32(0, rd, sizeof(struct jffs2_unknown_node)-4)) {
+		pr_crit("Eep. CRC not correct in jffs2_write_dirent()\n");
+		BUG();
+	   });
+
+	if (strnlen(name, namelen) != namelen) {
+		/* This should never happen, but seems to have done on at least one
+		   occasion: https://dev.laptop.org/ticket/4184 */
+		pr_crit("Error in jffs2_write_dirent() -- name contains zero bytes!\n");
+		pr_crit("Directory inode #%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));
+		WARN_ON(1);
+		return ERR_PTR(-EIO);
+	}
+
+	vecs[0].iov_base = rd;
+	vecs[0].iov_len = sizeof(*rd);
+	vecs[1].iov_base = (unsigned char *)name;
+	vecs[1].iov_len = namelen;
+
+	fd = jffs2_alloc_full_dirent(namelen+1);
+	if (!fd)
+		return ERR_PTR(-ENOMEM);
+
+	fd->version = je32_to_cpu(rd->version);
+	fd->ino = je32_to_cpu(rd->ino);
+	fd->nhash = full_name_hash(name, namelen);
+	fd->type = rd->type;
+	memcpy(fd->name, name, namelen);
+	fd->name[namelen]=0;
+
+ retry:
+	flash_ofs = write_ofs(c);
+
+	jffs2_dbg_prewrite_paranoia_check(c, flash_ofs, vecs[0].iov_len + vecs[1].iov_len);
+
+	if ((alloc_mode!=ALLOC_GC) && (je32_to_cpu(rd->version) < f->highest_version)) {
+		BUG_ON(!retried);
+		jffs2_dbg(1, "%s(): dirent_version %d, highest version %d -> updating dirent\n",
+			  __func__,
+			  je32_to_cpu(rd->version), f->highest_version);
+		rd->version = cpu_to_je32(++f->highest_version);
+		fd->version = je32_to_cpu(rd->version);
+		rd->node_crc = cpu_to_je32(crc32(0, rd, sizeof(*rd)-8));
+	}
+
+	ret = jffs2_flash_writev(c, vecs, 2, flash_ofs, &retlen,
+				 (alloc_mode==ALLOC_GC)?0:je32_to_cpu(rd->pino));
+	if (ret || (retlen != sizeof(*rd) + namelen)) {
+		pr_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, flash_ofs | REF_OBSOLETE, PAD(sizeof(*rd)+namelen), NULL);
+		} else {
+			pr_notice("Not marking the space at 0x%08x as dirty because the flash driver returned retlen zero\n",
+				  flash_ofs);
+		}
+		if (!retried) {
+			/* Try to reallocate space and retry */
+			uint32_t dummy;
+			struct jffs2_eraseblock *jeb = &c->blocks[flash_ofs / c->sector_size];
+
+			retried = 1;
+
+			jffs2_dbg(1, "Retrying failed write.\n");
+
+			jffs2_dbg_acct_sanity_check(c,jeb);
+			jffs2_dbg_acct_paranoia_check(c, jeb);
+
+			if (alloc_mode == ALLOC_GC) {
+				ret = jffs2_reserve_space_gc(c, sizeof(*rd) + namelen, &dummy,
+							     JFFS2_SUMMARY_DIRENT_SIZE(namelen));
+			} else {
+				/* Locking pain */
+				mutex_unlock(&f->sem);
+				jffs2_complete_reservation(c);
+
+				ret = jffs2_reserve_space(c, sizeof(*rd) + namelen, &dummy,
+							  alloc_mode, JFFS2_SUMMARY_DIRENT_SIZE(namelen));
+				mutex_lock(&f->sem);
+			}
+
+			if (!ret) {
+				flash_ofs = write_ofs(c);
+				jffs2_dbg(1, "Allocated space at 0x%08x to retry failed write\n",
+					  flash_ofs);
+				jffs2_dbg_acct_sanity_check(c,jeb);
+				jffs2_dbg_acct_paranoia_check(c, jeb);
+				goto retry;
+			}
+			jffs2_dbg(1, "Failed to allocate space to retry failed write: %d!\n",
+				  ret);
+		}
+		/* Release the full_dnode which is now useless, and return */
+		jffs2_free_full_dirent(fd);
+		return ERR_PTR(ret?ret:-EIO);
+	}
+	/* Mark the space used */
+	fd->raw = jffs2_add_physical_node_ref(c, flash_ofs | dirent_node_state(rd),
+					      PAD(sizeof(*rd)+namelen), f->inocache);
+	if (IS_ERR(fd->raw)) {
+		void *hold_err = fd->raw;
+		/* Release the full_dirent which is now useless, and return */
+		jffs2_free_full_dirent(fd);
+		return ERR_CAST(hold_err);
+	}
+
+	if (retried) {
+		jffs2_dbg_acct_sanity_check(c,NULL);
+	}
+
+	return fd;
+}
+
+/* The OS-specific code fills in the metadata in the jffs2_raw_inode for us, so that
+   we don't have to go digging in struct inode or its equivalent. It should set:
+   mode, uid, gid, (starting)isize, atime, ctime, mtime */
+int jffs2_write_inode_range(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
+			    struct jffs2_raw_inode *ri, unsigned char *buf,
+			    uint32_t offset, uint32_t writelen, uint32_t *retlen)
+{
+	int ret = 0;
+	uint32_t writtenlen = 0;
+
+	jffs2_dbg(1, "%s(): Ino #%u, ofs 0x%x, len 0x%x\n",
+		  __func__, f->inocache->ino, offset, writelen);
+
+	while(writelen) {
+		struct jffs2_full_dnode *fn;
+		unsigned char *comprbuf = NULL;
+		uint16_t comprtype = JFFS2_COMPR_NONE;
+		uint32_t alloclen;
+		uint32_t datalen, cdatalen;
+		int retried = 0;
+
+	retry:
+		jffs2_dbg(2, "jffs2_commit_write() loop: 0x%x to write to 0x%x\n",
+			  writelen, offset);
+
+		ret = jffs2_reserve_space(c, sizeof(*ri) + JFFS2_MIN_DATA_LEN,
+					&alloclen, ALLOC_NORMAL, JFFS2_SUMMARY_INODE_SIZE);
+		if (ret) {
+			jffs2_dbg(1, "jffs2_reserve_space returned %d\n", ret);
+			break;
+		}
+		mutex_lock(&f->sem);
+		datalen = min_t(uint32_t, writelen, PAGE_CACHE_SIZE - (offset & (PAGE_CACHE_SIZE-1)));
+		cdatalen = min_t(uint32_t, alloclen - sizeof(*ri), datalen);
+
+		comprtype = jffs2_compress(c, f, buf, &comprbuf, &datalen, &cdatalen);
+
+		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 = 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 & 0xff;
+		ri->usercompr = (comprtype >> 8 ) & 0xff;
+		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, ALLOC_NORETRY);
+
+		jffs2_free_comprbuf(comprbuf, buf);
+
+		if (IS_ERR(fn)) {
+			ret = PTR_ERR(fn);
+			mutex_unlock(&f->sem);
+			jffs2_complete_reservation(c);
+			if (!retried) {
+				/* Write error to be retried */
+				retried = 1;
+				jffs2_dbg(1, "Retrying node write in jffs2_write_inode_range()\n");
+				goto retry;
+			}
+			break;
+		}
+		ret = jffs2_add_full_dnode_to_inode(c, f, fn);
+		if (f->metadata) {
+			jffs2_mark_node_obsolete(c, f->metadata->raw);
+			jffs2_free_full_dnode(f->metadata);
+			f->metadata = NULL;
+		}
+		if (ret) {
+			/* Eep */
+			jffs2_dbg(1, "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);
+
+			mutex_unlock(&f->sem);
+			jffs2_complete_reservation(c);
+			break;
+		}
+		mutex_unlock(&f->sem);
+		jffs2_complete_reservation(c);
+		if (!datalen) {
+			pr_warn("Eep. We didn't actually write any data in jffs2_write_inode_range()\n");
+			ret = -EIO;
+			break;
+		}
+		jffs2_dbg(1, "increasing writtenlen by %d\n", datalen);
+		writtenlen += datalen;
+		offset += datalen;
+		writelen -= datalen;
+		buf += datalen;
+	}
+	*retlen = writtenlen;
+	return ret;
+}
+
+int jffs2_do_create(struct jffs2_sb_info *c, struct jffs2_inode_info *dir_f,
+		    struct jffs2_inode_info *f, struct jffs2_raw_inode *ri,
+		    const struct qstr *qstr)
+{
+	struct jffs2_raw_dirent *rd;
+	struct jffs2_full_dnode *fn;
+	struct jffs2_full_dirent *fd;
+	uint32_t alloclen;
+	int ret;
+
+	/* Try to reserve enough space for both node and dirent.
+	 * Just the node will do for now, though
+	 */
+	ret = jffs2_reserve_space(c, sizeof(*ri), &alloclen, ALLOC_NORMAL,
+				JFFS2_SUMMARY_INODE_SIZE);
+	jffs2_dbg(1, "%s(): reserved 0x%x bytes\n", __func__, alloclen);
+	if (ret)
+		return ret;
+
+	mutex_lock(&f->sem);
+
+	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, ALLOC_NORMAL);
+
+	jffs2_dbg(1, "jffs2_do_create created file with mode 0x%x\n",
+		  jemode_to_cpu(ri->mode));
+
+	if (IS_ERR(fn)) {
+		jffs2_dbg(1, "jffs2_write_dnode() failed\n");
+		/* Eeek. Wave bye bye */
+		mutex_unlock(&f->sem);
+		jffs2_complete_reservation(c);
+		return PTR_ERR(fn);
+	}
+	/* No data here. Only a metadata node, which will be
+	   obsoleted by the first data write
+	*/
+	f->metadata = fn;
+
+	mutex_unlock(&f->sem);
+	jffs2_complete_reservation(c);
+
+	ret = jffs2_init_security(&f->vfs_inode, &dir_f->vfs_inode, qstr);
+	if (ret)
+		return ret;
+	ret = jffs2_init_acl_post(&f->vfs_inode);
+	if (ret)
+		return ret;
+
+	ret = jffs2_reserve_space(c, sizeof(*rd)+qstr->len, &alloclen,
+				ALLOC_NORMAL, JFFS2_SUMMARY_DIRENT_SIZE(qstr->len));
+
+	if (ret) {
+		/* Eep. */
+		jffs2_dbg(1, "jffs2_reserve_space() for dirent failed\n");
+		return ret;
+	}
+
+	rd = jffs2_alloc_raw_dirent();
+	if (!rd) {
+		/* Argh. Now we treat it like a normal delete */
+		jffs2_complete_reservation(c);
+		return -ENOMEM;
+	}
+
+	mutex_lock(&dir_f->sem);
+
+	rd->magic = cpu_to_je16(JFFS2_MAGIC_BITMASK);
+	rd->nodetype = cpu_to_je16(JFFS2_NODETYPE_DIRENT);
+	rd->totlen = cpu_to_je32(sizeof(*rd) + qstr->len);
+	rd->hdr_crc = cpu_to_je32(crc32(0, rd, sizeof(struct jffs2_unknown_node)-4));
+
+	rd->pino = cpu_to_je32(dir_f->inocache->ino);
+	rd->version = cpu_to_je32(++dir_f->highest_version);
+	rd->ino = ri->ino;
+	rd->mctime = ri->ctime;
+	rd->nsize = qstr->len;
+	rd->type = DT_REG;
+	rd->node_crc = cpu_to_je32(crc32(0, rd, sizeof(*rd)-8));
+	rd->name_crc = cpu_to_je32(crc32(0, qstr->name, qstr->len));
+
+	fd = jffs2_write_dirent(c, dir_f, rd, qstr->name, qstr->len, ALLOC_NORMAL);
+
+	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);
+		mutex_unlock(&dir_f->sem);
+		return PTR_ERR(fd);
+	}
+
+	/* Link the fd into the inode's list, obsoleting an old
+	   one if necessary. */
+	jffs2_add_fd_to_list(c, fd, &dir_f->dents);
+
+	jffs2_complete_reservation(c);
+	mutex_unlock(&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,
+		    uint32_t time)
+{
+	struct jffs2_raw_dirent *rd;
+	struct jffs2_full_dirent *fd;
+	uint32_t alloclen;
+	int ret;
+
+	if (!jffs2_can_mark_obsolete(c)) {
+		/* We can't mark stuff obsolete on the medium. We need to write a deletion dirent */
+
+		rd = jffs2_alloc_raw_dirent();
+		if (!rd)
+			return -ENOMEM;
+
+		ret = jffs2_reserve_space(c, sizeof(*rd)+namelen, &alloclen,
+					ALLOC_DELETION, JFFS2_SUMMARY_DIRENT_SIZE(namelen));
+		if (ret) {
+			jffs2_free_raw_dirent(rd);
+			return ret;
+		}
+
+		mutex_lock(&dir_f->sem);
+
+		/* Build a deletion node */
+		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 = 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(time);
+		rd->nsize = namelen;
+		rd->type = DT_UNKNOWN;
+		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, ALLOC_DELETION);
+
+		jffs2_free_raw_dirent(rd);
+
+		if (IS_ERR(fd)) {
+			jffs2_complete_reservation(c);
+			mutex_unlock(&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);
+		mutex_unlock(&dir_f->sem);
+	} else {
+		uint32_t nhash = full_name_hash(name, namelen);
+
+		fd = dir_f->dents;
+		/* We don't actually want to reserve any space, but we do
+		   want to be holding the alloc_sem when we write to flash */
+		mutex_lock(&c->alloc_sem);
+		mutex_lock(&dir_f->sem);
+
+		for (fd = dir_f->dents; fd; fd = fd->next) {
+			if (fd->nhash == nhash &&
+			    !memcmp(fd->name, name, namelen) &&
+			    !fd->name[namelen]) {
+
+				jffs2_dbg(1, "Marking old dirent node (ino #%u) @%08x obsolete\n",
+					  fd->ino, ref_offset(fd->raw));
+				jffs2_mark_node_obsolete(c, fd->raw);
+				/* We don't want to remove it from the list immediately,
+				   because that screws up getdents()/seek() semantics even
+				   more than they're screwed already. Turn it into a
+				   node-less deletion dirent instead -- a placeholder */
+				fd->raw = NULL;
+				fd->ino = 0;
+				break;
+			}
+		}
+		mutex_unlock(&dir_f->sem);
+	}
+
+	/* 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) {
+
+		mutex_lock(&dead_f->sem);
+
+		if (S_ISDIR(OFNI_EDONI_2SFFJ(dead_f)->i_mode)) {
+			while (dead_f->dents) {
+				/* There can be only deleted ones */
+				fd = dead_f->dents;
+
+				dead_f->dents = fd->next;
+
+				if (fd->ino) {
+					pr_warn("Deleting inode #%u with active dentry \"%s\"->ino #%u\n",
+						dead_f->inocache->ino,
+						fd->name, fd->ino);
+				} else {
+					jffs2_dbg(1, "Removing deletion dirent for \"%s\" from dir ino #%u\n",
+						  fd->name,
+						  dead_f->inocache->ino);
+				}
+				if (fd->raw)
+					jffs2_mark_node_obsolete(c, fd->raw);
+				jffs2_free_full_dirent(fd);
+			}
+			dead_f->inocache->pino_nlink = 0;
+		} else
+			dead_f->inocache->pino_nlink--;
+		/* NB: Caller must set inode nlink if appropriate */
+		mutex_unlock(&dead_f->sem);
+	}
+
+	jffs2_complete_reservation(c);
+
+	return 0;
+}
+
+
+int jffs2_do_link (struct jffs2_sb_info *c, struct jffs2_inode_info *dir_f, uint32_t ino, uint8_t type, const char *name, int namelen, uint32_t time)
+{
+	struct jffs2_raw_dirent *rd;
+	struct jffs2_full_dirent *fd;
+	uint32_t alloclen;
+	int ret;
+
+	rd = jffs2_alloc_raw_dirent();
+	if (!rd)
+		return -ENOMEM;
+
+	ret = jffs2_reserve_space(c, sizeof(*rd)+namelen, &alloclen,
+				ALLOC_NORMAL, JFFS2_SUMMARY_DIRENT_SIZE(namelen));
+	if (ret) {
+		jffs2_free_raw_dirent(rd);
+		return ret;
+	}
+
+	mutex_lock(&dir_f->sem);
+
+	/* Build a deletion node */
+	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 = 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(time);
+	rd->nsize = namelen;
+
+	rd->type = type;
+
+	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, ALLOC_NORMAL);
+
+	jffs2_free_raw_dirent(rd);
+
+	if (IS_ERR(fd)) {
+		jffs2_complete_reservation(c);
+		mutex_unlock(&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);
+	mutex_unlock(&dir_f->sem);
+
+	return 0;
+}
Index: cpukit/libfs/src/jffs2/src/xattr.h
===================================================================
--- cpukit/libfs/src/jffs2/src/xattr.h	(revision 0c0f128e11007cb0d9ebed8b27dbb7cdde18450d)
+++ cpukit/libfs/src/jffs2/src/xattr.h	(revision 0c0f128e11007cb0d9ebed8b27dbb7cdde18450d)
@@ -0,0 +1,133 @@
+/*
+ * JFFS2 -- Journalling Flash File System, Version 2.
+ *
+ * Copyright Â© 2006  NEC Corporation
+ *
+ * Created by KaiGai Kohei <kaigai@ak.jp.nec.com>
+ *
+ * For licensing information, see the file 'LICENCE' in this directory.
+ *
+ */
+
+#ifndef _JFFS2_FS_XATTR_H_
+#define _JFFS2_FS_XATTR_H_
+
+#include <linux/xattr.h>
+#include <linux/list.h>
+
+#define JFFS2_XFLAGS_HOT	(0x01)	/* This datum is HOT */
+#define JFFS2_XFLAGS_BIND	(0x02)	/* This datum is not reclaimed */
+#define JFFS2_XFLAGS_DEAD	(0x40)	/* This datum is already dead */
+#define JFFS2_XFLAGS_INVALID	(0x80)	/* This datum contains crc error */
+
+struct jffs2_xattr_datum
+{
+	void *always_null;
+	struct jffs2_raw_node_ref *node;
+	uint8_t class;
+	uint8_t flags;
+	uint16_t xprefix;		/* see JFFS2_XATTR_PREFIX_* */
+
+	struct list_head xindex;	/* chained from c->xattrindex[n] */
+	atomic_t refcnt;		/* # of xattr_ref refers this */
+	uint32_t xid;
+	uint32_t version;
+
+	uint32_t data_crc;
+	uint32_t hashkey;
+	char *xname;		/* XATTR name without prefix */
+	uint32_t name_len;	/* length of xname */
+	char *xvalue;		/* XATTR value */
+	uint32_t value_len;	/* length of xvalue */
+};
+
+struct jffs2_inode_cache;
+struct jffs2_xattr_ref
+{
+	void *always_null;
+	struct jffs2_raw_node_ref *node;
+	uint8_t class;
+	uint8_t flags;		/* Currently unused */
+	u16 unused;
+
+	uint32_t xseqno;
+	union {
+		struct jffs2_inode_cache *ic;	/* reference to jffs2_inode_cache */
+		uint32_t ino;			/* only used in scanning/building  */
+	};
+	union {
+		struct jffs2_xattr_datum *xd;	/* reference to jffs2_xattr_datum */
+		uint32_t xid;			/* only used in sccanning/building */
+	};
+	struct jffs2_xattr_ref *next;		/* chained from ic->xref_list */
+};
+
+#define XREF_DELETE_MARKER	(0x00000001)
+static inline int is_xattr_ref_dead(struct jffs2_xattr_ref *ref)
+{
+	return ((ref->xseqno & XREF_DELETE_MARKER) != 0);
+}
+
+#ifdef CONFIG_JFFS2_FS_XATTR
+
+extern void jffs2_init_xattr_subsystem(struct jffs2_sb_info *c);
+extern void jffs2_build_xattr_subsystem(struct jffs2_sb_info *c);
+extern void jffs2_clear_xattr_subsystem(struct jffs2_sb_info *c);
+
+extern struct jffs2_xattr_datum *jffs2_setup_xattr_datum(struct jffs2_sb_info *c,
+							 uint32_t xid, uint32_t version);
+
+extern void jffs2_xattr_do_crccheck_inode(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic);
+extern void jffs2_xattr_delete_inode(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic);
+extern void jffs2_xattr_free_inode(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic);
+
+extern int jffs2_garbage_collect_xattr_datum(struct jffs2_sb_info *c, struct jffs2_xattr_datum *xd,
+					     struct jffs2_raw_node_ref *raw);
+extern int jffs2_garbage_collect_xattr_ref(struct jffs2_sb_info *c, struct jffs2_xattr_ref *ref,
+					   struct jffs2_raw_node_ref *raw);
+extern int jffs2_verify_xattr(struct jffs2_sb_info *c);
+extern void jffs2_release_xattr_datum(struct jffs2_sb_info *c, struct jffs2_xattr_datum *xd);
+extern void jffs2_release_xattr_ref(struct jffs2_sb_info *c, struct jffs2_xattr_ref *ref);
+
+extern int do_jffs2_getxattr(struct inode *inode, int xprefix, const char *xname,
+			     char *buffer, size_t size);
+extern int do_jffs2_setxattr(struct inode *inode, int xprefix, const char *xname,
+			     const char *buffer, size_t size, int flags);
+
+extern const struct xattr_handler *jffs2_xattr_handlers[];
+extern const struct xattr_handler jffs2_user_xattr_handler;
+extern const struct xattr_handler jffs2_trusted_xattr_handler;
+
+extern ssize_t jffs2_listxattr(struct dentry *, char *, size_t);
+#define jffs2_getxattr		generic_getxattr
+#define jffs2_setxattr		generic_setxattr
+#define jffs2_removexattr	generic_removexattr
+
+#else
+
+#define jffs2_init_xattr_subsystem(c)
+#define jffs2_build_xattr_subsystem(c)
+#define jffs2_clear_xattr_subsystem(c)
+
+#define jffs2_xattr_do_crccheck_inode(c, ic)
+#define jffs2_xattr_delete_inode(c, ic)
+#define jffs2_xattr_free_inode(c, ic)
+#define jffs2_verify_xattr(c)			(1)
+
+#define jffs2_xattr_handlers	NULL
+#define jffs2_listxattr		NULL
+#define jffs2_getxattr		NULL
+#define jffs2_setxattr		NULL
+#define jffs2_removexattr	NULL
+
+#endif /* CONFIG_JFFS2_FS_XATTR */
+
+#ifdef CONFIG_JFFS2_FS_SECURITY
+extern int jffs2_init_security(struct inode *inode, struct inode *dir,
+			       const struct qstr *qstr);
+extern const struct xattr_handler jffs2_security_xattr_handler;
+#else
+#define jffs2_init_security(inode,dir,qstr)	(0)
+#endif /* CONFIG_JFFS2_FS_SECURITY */
+
+#endif /* _JFFS2_FS_XATTR_H_ */
