summaryrefslogtreecommitdiffstats
path: root/fs
diff options
context:
space:
mode:
authorSteffen Trumtrar <s.trumtrar@pengutronix.de>2020-02-05 10:12:01 +0100
committerSascha Hauer <s.hauer@pengutronix.de>2020-02-10 09:42:43 +0100
commit831b3312e5fe902bacd699c9ff1e81e1c2b79f66 (patch)
treee619e2f21b4c130211c8ca25729fe37253b9ae43 /fs
parent15395b158eb9f4a2b2aeb7bde97d0d2db99badae (diff)
downloadbarebox-831b3312e5fe902bacd699c9ff1e81e1c2b79f66.tar.gz
fs: jffs2: add initial support for reading jffs2
Import the jffs2 filesystem code from Linux v5.5-rc1 and convert it to work with barebox. Writing is *not* supported. Testing was done with a n25q256a-compatible QuadSPI chip on a SoCFPGA-based Socrates board. Testing was done with a combination of: user@somelinuxhost: mkfs.jffs2 --eraseblock=4 -d fs/jffs2/ -o jffs2.img root@target:~ flash_erase -j /dev/mtd5 0 0 root@target:~ dd if=jffs2.img of=/dev/mtd5 barebox@EBV SOCrates:/ mount /dev/mtd0.data (...) mounted /dev/mtd0.data on /mnt/mtd0.data Signed-off-by: Steffen Trumtrar <s.trumtrar@pengutronix.de> Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
Diffstat (limited to 'fs')
-rw-r--r--fs/Kconfig1
-rw-r--r--fs/Makefile1
-rw-r--r--fs/jffs2/Kconfig47
-rw-r--r--fs/jffs2/Makefile9
-rw-r--r--fs/jffs2/acl.h42
-rw-r--r--fs/jffs2/build.c425
-rw-r--r--fs/jffs2/compr.c155
-rw-r--r--fs/jffs2/compr.h112
-rw-r--r--fs/jffs2/compr_lzo.c52
-rw-r--r--fs/jffs2/compr_rtime.c86
-rw-r--r--fs/jffs2/compr_rubin.c270
-rw-r--r--fs/jffs2/compr_zlib.c105
-rw-r--r--fs/jffs2/debug.c868
-rw-r--r--fs/jffs2/debug.h269
-rw-r--r--fs/jffs2/dir.c135
-rw-r--r--fs/jffs2/fs.c483
-rw-r--r--fs/jffs2/jffs2_fs_i.h55
-rw-r--r--fs/jffs2/jffs2_fs_sb.h160
-rw-r--r--fs/jffs2/malloc.c315
-rw-r--r--fs/jffs2/nodelist.c915
-rw-r--r--fs/jffs2/nodelist.h506
-rw-r--r--fs/jffs2/os-linux.h193
-rw-r--r--fs/jffs2/read.c229
-rw-r--r--fs/jffs2/readinode.c1401
-rw-r--r--fs/jffs2/scan.c1157
-rw-r--r--fs/jffs2/summary.h212
-rw-r--r--fs/jffs2/super.c130
-rw-r--r--fs/jffs2/xattr.h126
28 files changed, 8459 insertions, 0 deletions
diff --git a/fs/Kconfig b/fs/Kconfig
index adf281a..e0405de 100644
--- a/fs/Kconfig
+++ b/fs/Kconfig
@@ -71,6 +71,7 @@ config FS_EFIVARFS
This filesystem driver provides access to EFI variables.
source "fs/fat/Kconfig"
+source "fs/jffs2/Kconfig"
source "fs/ubifs/Kconfig"
config FS_BPKFS
diff --git a/fs/Makefile b/fs/Makefile
index 9889a65..f13dc97 100644
--- a/fs/Makefile
+++ b/fs/Makefile
@@ -6,6 +6,7 @@ obj-$(CONFIG_FS_LEGACY) += legacy.o
obj-$(CONFIG_FS_DEVFS) += devfs.o
obj-$(CONFIG_FS_FAT) += fat/
obj-y += fs.o libfs.o
+obj-$(CONFIG_FS_JFFS2) += jffs2/
obj-$(CONFIG_FS_UBIFS) += ubifs/
obj-$(CONFIG_FS_TFTP) += tftp.o
obj-$(CONFIG_FS_OMAP4_USBBOOT) += omap4_usbbootfs.o
diff --git a/fs/jffs2/Kconfig b/fs/jffs2/Kconfig
new file mode 100644
index 0000000..3121d36
--- /dev/null
+++ b/fs/jffs2/Kconfig
@@ -0,0 +1,47 @@
+menuconfig FS_JFFS2
+ bool
+ select CRC32
+ depends on MTD
+ prompt "jffs2 support"
+
+if FS_JFFS2
+
+config FS_JFFS2_FS_DEBUG
+ int "JFFS2 debugging verbosity (0 = quiet, 2 = noisy)"
+ default "0"
+
+config FS_JFFS2_COMPRESSION_OPTIONS
+ bool "Advanced compression options for JFFS2"
+ depends on FS_JFFS2
+ help
+ Enabling this option allows you to explicitly choose which
+ compression modules, if any, are enabled in JFFS2. Removing
+ compressors can mean you cannot read existing file systems,
+ and enabling experimental compressors can mean that you
+ write a file system which cannot be read by a standard kernel.
+
+ If unsure, you should _definitely_ say 'N'.
+
+if FS_JFFS2_COMPRESSION_OPTIONS
+
+config FS_JFFS2_COMPRESSION_ZLIB
+ bool
+ select ZLIB
+ prompt "ZLIB compression support"
+
+config FS_JFFS2_COMPRESSION_LZO
+ bool
+ select LZO_DECOMPRESS
+ prompt "LZO compression support"
+
+config FS_JFFS2_COMPRESSION_RTIME
+ bool
+ prompt "RTIME compression support"
+
+config FS_JFFS2_COMPRESSION_RUBIN
+ bool
+ prompt "Rubin compression support"
+
+endif
+
+endif
diff --git a/fs/jffs2/Makefile b/fs/jffs2/Makefile
new file mode 100644
index 0000000..a4da48d
--- /dev/null
+++ b/fs/jffs2/Makefile
@@ -0,0 +1,9 @@
+obj-y += compr.o dir.o nodelist.o malloc.o
+obj-y += read.o readinode.o scan.o
+obj-y += build.o fs.o
+obj-y += super.o debug.o
+
+obj-$(CONFIG_FS_JFFS2_COMPRESSION_ZLIB) += compr_zlib.o
+obj-$(CONFIG_FS_JFFS2_COMPRESSION_LZO) += compr_lzo.o
+obj-$(CONFIG_FS_JFFS2_COMPRESSION_RTIME) += compr_rtime.o
+obj-$(CONFIG_FS_JFFS2_COMPRESSION_RUBIN) += compr_rubin.o
diff --git a/fs/jffs2/acl.h b/fs/jffs2/acl.h
new file mode 100644
index 0000000..12d0271
--- /dev/null
+++ b/fs/jffs2/acl.h
@@ -0,0 +1,42 @@
+/*
+ * 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;
+ struct jffs2_acl_entry a_entries[];
+};
+
+#ifdef CONFIG_JFFS2_FS_POSIX_ACL
+
+struct posix_acl *jffs2_get_acl(struct inode *inode, int type);
+int jffs2_set_acl(struct inode *inode, struct posix_acl *acl, int type);
+extern int jffs2_init_acl_pre(struct inode *, struct inode *, umode_t *);
+extern int jffs2_init_acl_post(struct inode *);
+
+#else
+
+#define jffs2_get_acl (NULL)
+#define jffs2_set_acl (NULL)
+#define jffs2_init_acl_pre(dir_i,inode,mode) (0)
+#define jffs2_init_acl_post(inode) (0)
+
+#endif /* CONFIG_JFFS2_FS_POSIX_ACL */
diff --git a/fs/jffs2/build.c b/fs/jffs2/build.c
new file mode 100644
index 0000000..f3fc0ad
--- /dev/null
+++ b/fs/jffs2/build.c
@@ -0,0 +1,425 @@
+/*
+ * 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 <common.h>
+#include <linux/kernel.h>
+#include <linux/sched.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,
+ int *dir_hardlinks)
+{
+ 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);
+ /* Clear the ic/raw union so it doesn't cause problems later. */
+ fd->ic = NULL;
+ continue;
+ }
+
+ /* From this point, fd->raw is no longer used so we can set fd->ic */
+ fd->ic = child_ic;
+ child_ic->pino_nlink++;
+ /* If we appear (at this stage) to have hard-linked directories,
+ * set a flag to trigger a scan later */
+ if (fd->type == DT_DIR) {
+ child_ic->flags |= INO_FLAGS_IS_DIR;
+ if (child_ic->pino_nlink > 1)
+ *dir_hardlinks = 1;
+ }
+
+ 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, i, dir_hardlinks = 0;
+ 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, &dir_hardlinks);
+ 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");
+
+ if (dir_hardlinks) {
+ /* If we detected directory hardlinks earlier, *hopefully*
+ * they are gone now because some of the links were from
+ * dead directories which still had some old dirents lying
+ * around and not yet garbage-collected, but which have
+ * been discarded above. So clear the pino_nlink field
+ * in each directory, so that the final scan below can
+ * print appropriate warnings. */
+ for_each_inode(i, c, ic) {
+ if (ic->flags & INO_FLAGS_IS_DIR)
+ ic->pino_nlink = 0;
+ }
+ }
+ 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;
+ /* We do use the pino_nlink field to count nlink of
+ * directories during fs build, so set it to the
+ * parent ino# now. Now that there's hopefully only
+ * one. */
+ if (fd->type == DT_DIR) {
+ if (!fd->ic) {
+ /* We'll have complained about it and marked the coresponding
+ raw node obsolete already. Just skip it. */
+ continue;
+ }
+
+ /* We *have* to have set this in jffs2_build_inode_pass1() */
+ BUG_ON(!(fd->ic->flags & INO_FLAGS_IS_DIR));
+
+ /* We clear ic->pino_nlink ∀ directories' ic *only* if dir_hardlinks
+ * is set. Otherwise, we know this should never trigger anyway, so
+ * we don't do the check. And ic->pino_nlink still contains the nlink
+ * value (which is 1). */
+ if (dir_hardlinks && fd->ic->pino_nlink) {
+ JFFS2_ERROR("child dir \"%s\" (ino #%u) of dir ino #%u is also hard linked from dir ino #%u\n",
+ fd->name, fd->ino, ic->ino, fd->ic->pino_nlink);
+ /* Should we unlink it from its previous parent? */
+ }
+
+ /* For directories, ic->pino_nlink holds that parent inode # */
+ fd->ic->pino_nlink = ic->ino;
+ }
+ 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 */
+ 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:
+ kfree(c->blocks);
+ return ret;
+}
diff --git a/fs/jffs2/compr.c b/fs/jffs2/compr.c
new file mode 100644
index 0000000..1c5a7d9
--- /dev/null
+++ b/fs/jffs2/compr.c
@@ -0,0 +1,155 @@
+/*
+ * 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 <common.h>
+#include "compr.h"
+
+/* Available compressors are on this list */
+static LIST_HEAD(jffs2_compressor_list);
+
+/* Statistics for blocks stored without compression */
+static uint32_t none_stat_decompr_blocks=0;
+
+/*
+ * Return 1 to use this compression
+ */
+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:
+ list_for_each_entry(this, &jffs2_compressor_list, list) {
+ if (comprtype == this->compr) {
+ this->usecount++;
+ ret = this->decompress(cdata_in, data_out, cdatalen, datalen);
+ if (ret) {
+ pr_warn("Decompressor \"%s\" returned %d\n",
+ this->name, ret);
+ }
+ else {
+ this->stat_decompr_blocks++;
+ }
+ this->usecount--;
+ return ret;
+ }
+ }
+ pr_warn("compression type 0x%02x not available\n", comprtype);
+ 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);
+
+ 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);
+ })
+
+
+ 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);
+
+ if (comp->usecount) {
+ 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);
+ })
+ return 0;
+}
+
+int __init jffs2_compressors_init(void)
+{
+/* Registering compressors */
+#ifdef CONFIG_FS_JFFS2_COMPRESSION_ZLIB
+ jffs2_zlib_init();
+#endif
+#ifdef CONFIG_FS_JFFS2_COMPRESSION_LZO
+ jffs2_lzo_init();
+#endif
+#ifdef CONFIG_FS_JFFS2_COMPRESSION_RTIME
+ jffs2_rtime_init();
+#endif
+#ifdef CONFIG_FS_JFFS2_COMPRESSION_RUBIN
+ jffs2_dynrubin_init();
+#endif
+/* Setting default compression mode */
+ return 0;
+}
+
+int jffs2_compressors_exit(void)
+{
+#ifdef CONFIG_FS_JFFS2_COMPRESSION_ZLIB
+ jffs2_zlib_exit();
+#endif
+#ifdef CONFIG_FS_JFFS2_COMPRESSION_LZO
+ jffs2_lzo_exit();
+#endif
+#ifdef CONFIG_FS_JFFS2_COMPRESSION_RTIME
+ jffs2_rtime_exit();
+#endif
+#ifdef CONFIG_FS_JFFS2_COMPRESSION_RUBIN
+ jffs2_dynrubin_exit();
+#endif
+ return 0;
+}
diff --git a/fs/jffs2/compr.h b/fs/jffs2/compr.h
new file mode 100644
index 0000000..2e3c368
--- /dev/null
+++ b/fs/jffs2/compr.h
@@ -0,0 +1,112 @@
+/*
+ * 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 <errno.h>
+#include <fs.h>
+#include <linux/jffs2.h>
+#include <linux/list.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/types.h>
+#include <malloc.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_FS_JFFS2_COMPRESSION_RUBIN
+int jffs2_rubinmips_init(void);
+void jffs2_rubinmips_exit(void);
+int jffs2_dynrubin_init(void);
+void jffs2_dynrubin_exit(void);
+#endif
+#ifdef CONFIG_FS_JFFS2_COMPRESSION_RTIME
+int jffs2_rtime_init(void);
+void jffs2_rtime_exit(void);
+#endif
+#ifdef CONFIG_FS_JFFS2_COMPRESSION_ZLIB
+int jffs2_zlib_init(void);
+void jffs2_zlib_exit(void);
+#endif
+#ifdef CONFIG_FS_JFFS2_COMPRESSION_LZO
+int jffs2_lzo_init(void);
+void jffs2_lzo_exit(void);
+#endif
+#ifdef CONFIG_FS_JFFS2_COMPRESSION_RTIME
+int jffs2_rtime_init(void);
+void jffs2_rtime_exit(void);
+#endif
+#ifdef CONFIG_FS_JFFS2_COMPRESSION_RUBIN
+int jffs2_dynrubin_init(void);
+void jffs2_dynrubin_exit(void);
+#endif
+
+#endif /* __JFFS2_COMPR_H__ */
diff --git a/fs/jffs2/compr_lzo.c b/fs/jffs2/compr_lzo.c
new file mode 100644
index 0000000..1be30dc
--- /dev/null
+++ b/fs/jffs2/compr_lzo.c
@@ -0,0 +1,52 @@
+/*
+ * JFFS2 -- Journalling Flash File System, Version 2.
+ *
+ * Copyright © 2007 Nokia Corporation. All rights reserved.
+ * Copyright © 2004-2010 David Woodhouse <dwmw2@infradead.org>
+ *
+ * Created by Richard Purdie <rpurdie@openedhand.com>
+ *
+ * For licensing information, see the file 'LICENCE' in this directory.
+ *
+ */
+
+#include <common.h>
+#include <lzo.h>
+#include <linux/kernel.h>
+#include "compr.h"
+
+static int jffs2_lzo_decompress(unsigned char *data_in, unsigned char *cpage_out,
+ uint32_t srclen, uint32_t destlen)
+{
+ size_t dl = destlen;
+ int ret;
+
+ ret = lzo1x_decompress_safe(data_in, srclen, cpage_out, &dl);
+
+ if (ret != LZO_E_OK || dl != destlen)
+ return -1;
+
+ return 0;
+}
+
+static struct jffs2_compressor jffs2_lzo_comp = {
+ .priority = JFFS2_LZO_PRIORITY,
+ .name = "lzo",
+ .compr = JFFS2_COMPR_LZO,
+ .decompress = &jffs2_lzo_decompress,
+ .disabled = 0,
+};
+
+int __init jffs2_lzo_init(void)
+{
+ int ret;
+
+ ret = jffs2_register_compressor(&jffs2_lzo_comp);
+
+ return ret;
+}
+
+void jffs2_lzo_exit(void)
+{
+ jffs2_unregister_compressor(&jffs2_lzo_comp);
+}
diff --git a/fs/jffs2/compr_rtime.c b/fs/jffs2/compr_rtime.c
new file mode 100644
index 0000000..d74d7d3
--- /dev/null
+++ b/fs/jffs2/compr_rtime.c
@@ -0,0 +1,86 @@
+/*
+ * 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/string.h>
+#include <linux/jffs2.h>
+#include "compr.h"
+
+static int jffs2_rtime_decompress(unsigned char *data_in,
+ unsigned char *cpage_out,
+ uint32_t srclen, uint32_t destlen)
+{
+ unsigned 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,
+ .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);
+}
diff --git a/fs/jffs2/compr_rubin.c b/fs/jffs2/compr_rubin.c
new file mode 100644
index 0000000..3e4faa7
--- /dev/null
+++ b/fs/jffs2/compr_rubin.c
@@ -0,0 +1,270 @@
+/*
+ * 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 "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 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 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 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 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,
+ .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,
+ .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);
+}
diff --git a/fs/jffs2/compr_zlib.c b/fs/jffs2/compr_zlib.c
new file mode 100644
index 0000000..222e0d6
--- /dev/null
+++ b/fs/jffs2/compr_zlib.c
@@ -0,0 +1,105 @@
+/*
+ * 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 <common.h>
+#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 struct z_stream_s inf_strm;
+
+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;
+
+ 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");
+ 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);
+ return 0;
+}
+
+static struct jffs2_compressor jffs2_zlib_comp = {
+ .priority = JFFS2_ZLIB_PRIORITY,
+ .name = "zlib",
+ .compr = JFFS2_COMPR_ZLIB,
+ .decompress = &jffs2_zlib_decompress,
+#ifdef JFFS2_ZLIB_DISABLED
+ .disabled = 1,
+#else
+ .disabled = 0,
+#endif
+};
+
+int __init jffs2_zlib_init(void)
+{
+ int ret;
+
+ inf_strm.workspace = vmalloc(zlib_inflate_workspacesize());
+ jffs2_dbg(1, "Allocated %d bytes for inflate workspace\n",
+ zlib_inflate_workspacesize());
+
+ ret = jffs2_register_compressor(&jffs2_zlib_comp);
+
+ return ret;
+}
+
+void jffs2_zlib_exit(void)
+{
+ jffs2_unregister_compressor(&jffs2_zlib_comp);
+ vfree(inf_strm.workspace);
+}
diff --git a/fs/jffs2/debug.c b/fs/jffs2/debug.c
new file mode 100644
index 0000000..536a178
--- /dev/null
+++ b/fs/jffs2/debug.c
@@ -0,0 +1,868 @@
+/*
+ * 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 <common.h>
+#include <crc.h>
+#include <fs.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/pagemap.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_SIZE-1) && frag_prev(frag)
+ && frag_prev(frag)->size < PAGE_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_SIZE-1) && frag_next(frag)
+ && frag_next(frag)->size < PAGE_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 */
diff --git a/fs/jffs2/debug.h b/fs/jffs2/debug.h
new file mode 100644
index 0000000..97733ec
--- /dev/null
+++ b/fs/jffs2/debug.h
@@ -0,0 +1,269 @@
+/*
+ * 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_FS_JFFS2_FS_DEBUG
+#define CONFIG_FS_JFFS2_FS_DEBUG 0
+#endif
+
+#if CONFIG_FS_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_FS_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_FS_JFFS2_FS_DEBUG > 0
+#define DEBUG
+#define D1(x) x
+#else
+#define D1(x)
+#endif
+
+#if CONFIG_FS_JFFS2_FS_DEBUG > 1
+#define D2(x) x
+#else
+#define D2(x)
+#endif
+
+#define jffs2_dbg(level, fmt, ...) \
+do { \
+ if (CONFIG_FS_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(fmt, ##__VA_ARGS__)
+
+#define JFFS2_WARNING(fmt, ...) pr_warn(fmt, ##__VA_ARGS__)
+
+#define JFFS2_NOTICE(fmt, ...) pr_notice(fmt, ##__VA_ARGS__)
+
+#define JFFS2_DEBUG(fmt, ...) \
+ printk(KERN_DEBUG "[JFFS2 DBG] %s: " fmt, \
+ __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_ */
diff --git a/fs/jffs2/dir.c b/fs/jffs2/dir.c
new file mode 100644
index 0000000..4d28d2b
--- /dev/null
+++ b/fs/jffs2/dir.c
@@ -0,0 +1,135 @@
+/*
+ * 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 <common.h>
+#include <linux/fs.h>
+#include <linux/hash.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/jffs2.h>
+#include "jffs2_fs_i.h"
+#include "jffs2_fs_sb.h"
+#include <linux/time.h>
+#include "nodelist.h"
+
+static int jffs2_readdir (struct file *, struct dir_context *);
+
+static struct dentry *jffs2_lookup (struct inode *,struct dentry *,
+ unsigned int);
+
+const struct file_operations jffs2_dir_operations =
+{
+ .read = NULL,
+ .iterate = jffs2_readdir,
+};
+
+const struct inode_operations jffs2_dir_inode_operations =
+{
+ .lookup = jffs2_lookup,
+ .create = NULL, /* not present in barebox */
+ .link = NULL, /* not present in barebox */
+ .symlink = NULL, /* not present in barebox */
+ .unlink = NULL, /* not present in barebox */
+ .mkdir = NULL, /* not present in barebox */
+ .rmdir = NULL, /* not present in barebox */
+};
+
+/***********************************************************************/
+
+/* We keep the dirent list sorted in increasing order of name hash,
+ and we use the same hash function as the dentries. Makes this
+ nice and simple
+*/
+static struct dentry *jffs2_lookup(struct inode *dir_i, struct dentry *target,
+ unsigned int flags)
+{
+ struct jffs2_inode_info *dir_f;
+ struct jffs2_full_dirent *fd = NULL, *fd_list;
+ uint32_t ino = 0;
+ struct inode *inode = NULL;
+ unsigned int nhash;
+
+ jffs2_dbg(1, "jffs2_lookup()\n");
+
+ if (target->d_name.len > JFFS2_MAX_NAME_LEN)
+ return ERR_PTR(-ENAMETOOLONG);
+
+ dir_f = JFFS2_INODE_INFO(dir_i);
+
+ /* The 'nhash' on the fd_list is not the same as the dentry hash */
+ nhash = full_name_hash(NULL, target->d_name.name, target->d_name.len);
+
+ mutex_lock(&dir_f->sem);
+
+ /* NB: The 2.2 backport will need to explicitly check for '.' and '..' here */
+ for (fd_list = dir_f->dents; fd_list && fd_list->nhash <= nhash; fd_list = fd_list->next) {
+ if (fd_list->nhash == nhash &&
+ (!fd || fd_list->version > fd->version) &&
+ strlen(fd_list->name) == target->d_name.len &&
+ !strncmp(fd_list->name, target->d_name.name, target->d_name.len)) {
+ fd = fd_list;
+ }
+ }
+ if (fd)
+ ino = fd->ino;
+ mutex_unlock(&dir_f->sem);
+ if (ino) {
+ inode = jffs2_iget(dir_i->i_sb, ino);
+ if (IS_ERR(inode))
+ pr_warn("iget() failed for ino #%u\n", ino);
+ }
+
+ if (IS_ERR(inode))
+ return ERR_CAST(inode);
+ d_add(target, inode);
+ return NULL;
+}
+
+/***********************************************************************/
+
+
+static int jffs2_readdir(struct file *file, struct dir_context *ctx)
+{
+ struct inode *inode = file_inode(file);
+ struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode);
+ struct jffs2_full_dirent *fd;
+ unsigned long curofs = 1;
+
+ jffs2_dbg(1, "jffs2_readdir() for dir_i #%lu\n", inode->i_ino);
+
+ if (!dir_emit_dots(file, ctx))
+ return 0;
+
+ mutex_lock(&f->sem);
+ for (fd = f->dents; fd; fd = fd->next) {
+ curofs++;
+ /* First loop: curofs = 2; pos = 2 */
+ if (curofs < ctx->pos) {
+ jffs2_dbg(2, "Skipping dirent: \"%s\", ino #%u, type %d, because curofs %ld < offset %ld\n",
+ fd->name, fd->ino, fd->type, curofs, (unsigned long)ctx->pos);
+ continue;
+ }
+ if (!fd->ino) {
+ jffs2_dbg(2, "Skipping deletion dirent \"%s\"\n",
+ fd->name);
+ ctx->pos++;
+ continue;
+ }
+ jffs2_dbg(2, "Dirent %ld: \"%s\", ino #%u, type %d\n",
+ (unsigned long)ctx->pos, fd->name, fd->ino, fd->type);
+ if (!dir_emit(ctx, fd->name, strlen(fd->name), fd->ino, fd->type))
+ break;
+ ctx->pos++;
+ }
+ mutex_unlock(&f->sem);
+ return 0;
+}
diff --git a/fs/jffs2/fs.c b/fs/jffs2/fs.c
new file mode 100644
index 0000000..f094291
--- /dev/null
+++ b/fs/jffs2/fs.c
@@ -0,0 +1,483 @@
+/*
+ * 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 <common.h>
+#include <crc.h>
+#include <driver.h>
+#include <init.h>
+#include <fs.h>
+#include <malloc.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/list.h>
+#include <linux/mtd/mtd.h>
+#include <linux/pagemap.h>
+#include <linux/stat.h>
+#include "compr.h"
+#include "nodelist.h"
+#include "os-linux.h"
+
+static int jffs2_flash_setup(struct jffs2_sb_info *c);
+
+/* from include/linux/fs.h */
+static inline void i_uid_write(struct inode *inode, uid_t uid)
+{
+ inode->i_uid = uid;
+}
+
+static inline void i_gid_write(struct inode *inode, gid_t gid)
+{
+ inode->i_gid = gid;
+}
+
+const struct file_operations jffs2_file_operations;
+const struct inode_operations jffs2_file_inode_operations;
+
+static int jffs2_open(struct device_d *dev, FILE *file, const char *filename)
+{
+ struct inode *inode = file->f_inode;
+ struct jffs2_file *jf;
+
+ jf = xzalloc(sizeof(*jf));
+
+ jf->inode = inode;
+ jf->buf = xmalloc(JFFS2_BLOCK_SIZE);
+ jf->offset = -1;
+
+ file->priv = jf;
+
+ return 0;
+}
+
+static int jffs2_close(struct device_d *dev, FILE *f)
+{
+ struct jffs2_file *jf = f->priv;
+
+ free(jf->buf);
+ free(jf);
+
+ return 0;
+}
+
+static int jffs2_get_block(struct jffs2_file *jf, unsigned int pos)
+{
+ struct jffs2_sb_info *c = JFFS2_SB_INFO(jf->inode->i_sb);
+ struct jffs2_inode_info *f = JFFS2_INODE_INFO(jf->inode);
+ int ret;
+
+ if (pos != jf->offset) {
+ ret = jffs2_read_inode_range(c, f, jf->buf, pos,
+ JFFS2_BLOCK_SIZE);
+ if (ret && ret != -ENOENT)
+ return ret;
+ jf->offset = pos;
+ }
+
+ return 0;
+}
+
+static int jffs2_read(struct device_d *_dev, FILE *f, void *buf,
+ size_t insize)
+{
+ struct jffs2_file *jf = f->priv;
+ unsigned int pos = f->pos;
+ unsigned int ofs;
+ unsigned int now;
+ unsigned int size = insize;
+ int ret;
+
+ /* Read till end of current block */
+ ofs = f->pos % JFFS2_BLOCK_SIZE;
+ if (ofs) {
+ ret = jffs2_get_block(jf, pos);
+ if (ret)
+ return ret;
+
+ now = min(size, JFFS2_BLOCK_SIZE - ofs);
+
+ memcpy(buf, jf->buf + ofs, now);
+ size -= now;
+ pos += now;
+ buf += now;
+ }
+
+ /* Do full blocks */
+ while (size >= JFFS2_BLOCK_SIZE) {
+ ret = jffs2_get_block(jf, pos);
+ if (ret)
+ return ret;
+
+ memcpy(buf, jf->buf, JFFS2_BLOCK_SIZE);
+ size -= JFFS2_BLOCK_SIZE;
+ pos += JFFS2_BLOCK_SIZE;
+ buf += JFFS2_BLOCK_SIZE;
+ }
+
+ /* And the rest */
+ if (size) {
+ ret = jffs2_get_block(jf, pos);
+ if (ret)
+ return ret;
+ memcpy(buf, jf->buf, size);
+ }
+
+ return insize;
+
+}
+
+struct inode *jffs2_iget(struct super_block *sb, unsigned long ino)
+{
+ struct jffs2_inode_info *f;
+ struct jffs2_sb_info *c;
+ struct jffs2_raw_inode latest_node;
+ struct inode *inode;
+ int ret;
+
+ jffs2_dbg(1, "%s(): ino == %lu\n", __func__, ino);
+
+ inode = iget_locked(sb, ino);
+ if (!inode)
+ return ERR_PTR(-ENOMEM);
+ if (!(inode->i_state & I_NEW))
+ return inode;
+
+ f = JFFS2_INODE_INFO(inode);
+ c = JFFS2_SB_INFO(inode->i_sb);
+
+ jffs2_init_inode_info(f);
+ mutex_lock(&f->sem);
+
+ ret = jffs2_do_read_inode(c, f, inode->i_ino, &latest_node);
+ if (ret)
+ goto error;
+
+ inode->i_mode = jemode_to_cpu(latest_node.mode);
+ i_uid_write(inode, je16_to_cpu(latest_node.uid));
+ i_gid_write(inode, je16_to_cpu(latest_node.gid));
+ inode->i_size = je32_to_cpu(latest_node.isize);
+ inode->i_atime = ITIME(je32_to_cpu(latest_node.atime));
+ inode->i_mtime = ITIME(je32_to_cpu(latest_node.mtime));
+ inode->i_ctime = ITIME(je32_to_cpu(latest_node.ctime));
+
+ set_nlink(inode, f->inocache->pino_nlink);
+
+ inode->i_blocks = (inode->i_size + 511) >> 9;
+
+ switch (inode->i_mode & S_IFMT) {
+
+ case S_IFLNK:
+ inode->i_op = &simple_symlink_inode_operations;
+ inode->i_link = f->target;
+ break;
+
+ case S_IFDIR:
+ {
+ struct jffs2_full_dirent *fd;
+ set_nlink(inode, 2); /* parent and '.' */
+
+ for (fd=f->dents; fd; fd = fd->next) {
+ if (fd->type == DT_DIR && fd->ino)
+ inc_nlink(inode);
+ }
+ /* Root dir gets i_nlink 3 for some reason */
+ if (inode->i_ino == 1)
+ inc_nlink(inode);
+
+ inode->i_op = &jffs2_dir_inode_operations;
+ inode->i_fop = &jffs2_dir_operations;
+ break;
+ }
+ case S_IFREG:
+ inode->i_op = &jffs2_file_inode_operations;
+ inode->i_fop = &jffs2_file_operations;
+ break;
+
+ case S_IFBLK:
+ case S_IFCHR:
+ case S_IFSOCK:
+ case S_IFIFO:
+ break;
+
+ default:
+ pr_warn("%s(): Bogus i_mode %o for ino %lu\n",
+ __func__, inode->i_mode, (unsigned long)inode->i_ino);
+ }
+
+ mutex_unlock(&f->sem);
+
+ jffs2_dbg(1, "jffs2_read_inode() returning\n");
+ return inode;
+
+error:
+ mutex_unlock(&f->sem);
+ iget_failed(inode);
+ return ERR_PTR(ret);
+}
+
+static int calculate_inocache_hashsize(uint32_t flash_size)
+{
+ /*
+ * Pick a inocache hash size based on the size of the medium.
+ * Count how many megabytes we're dealing with, apply a hashsize twice
+ * that size, but rounding down to the usual big powers of 2. And keep
+ * to sensible bounds.
+ */
+
+ int size_mb = flash_size / 1024 / 1024;
+ int hashsize = (size_mb * 2) & ~0x3f;
+
+ if (hashsize < INOCACHE_HASHSIZE_MIN)
+ return INOCACHE_HASHSIZE_MIN;
+ if (hashsize > INOCACHE_HASHSIZE_MAX)
+ return INOCACHE_HASHSIZE_MAX;
+
+ return hashsize;
+}
+
+static void jffs2_put_super(struct super_block *sb)
+{
+ if (sb->s_fs_info) {
+ struct jffs2_sb_info *ctx = sb->s_fs_info;
+
+ jffs2_free_ino_caches(ctx);
+ jffs2_free_raw_node_refs(ctx);
+ kfree(ctx->blocks);
+ kfree(ctx->inocache_list);
+ jffs2_flash_cleanup(ctx);
+
+ kfree(sb->s_fs_info);
+ }
+}
+
+int jffs2_do_fill_super(struct super_block *sb, int silent)
+{
+ struct jffs2_sb_info *c;
+ struct inode *root_i;
+ int ret;
+ size_t blocks;
+
+ c = JFFS2_SB_INFO(sb);
+
+#ifndef CONFIG_JFFS2_FS_WRITEBUFFER
+ if (c->mtd->type == MTD_NANDFLASH) {
+ pr_err("Cannot operate on NAND flash unless jffs2 NAND support is compiled in");
+ return -EINVAL;
+ }
+ if (c->mtd->type == MTD_DATAFLASH) {
+ pr_err("Cannot operate on DataFlash unless jffs2 DataFlash support is compiled in");
+ return -EINVAL;
+ }
+#endif
+
+ c->flash_size = c->mtd->size;
+ c->sector_size = c->mtd->erasesize;
+ blocks = c->flash_size / c->sector_size;
+
+ /*
+ * Size alignment check
+ */
+ if ((c->sector_size * blocks) != c->flash_size) {
+ c->flash_size = c->sector_size * blocks;
+ pr_info("Flash size not aligned to erasesize, reducing to %dKiB",
+ c->flash_size / 1024);
+ }
+
+ if (c->flash_size < 5*c->sector_size) {
+ pr_err("Too few erase blocks (%d)",
+ c->flash_size / c->sector_size);
+ return -EINVAL;
+ }
+
+ c->cleanmarker_size = sizeof(struct jffs2_unknown_node);
+
+ /* NAND (or other bizarre) flash... do setup accordingly */
+ ret = jffs2_flash_setup(c);
+ if (ret)
+ return ret;
+
+ c->inocache_hashsize = calculate_inocache_hashsize(c->flash_size);
+ c->inocache_list = kcalloc(c->inocache_hashsize, sizeof(struct jffs2_inode_cache *), GFP_KERNEL);
+ if (!c->inocache_list) {
+ ret = -ENOMEM;
+ goto out_wbuf;
+ }
+
+ jffs2_init_xattr_subsystem(c);
+
+ if ((ret = jffs2_do_mount_fs(c)))
+ goto out_inohash;
+
+ jffs2_dbg(1, "%s(): Getting root inode\n", __func__);
+ root_i = jffs2_iget(sb, 1);
+ if (IS_ERR(root_i)) {
+ jffs2_dbg(1, "get root inode failed\n");
+ ret = PTR_ERR(root_i);
+ goto out_root;
+ }
+
+ ret = -ENOMEM;
+
+ jffs2_dbg(1, "%s(): d_make_root()\n", __func__);
+ sb->s_root = d_make_root(root_i);
+ if (!sb->s_root)
+ goto out_root;
+
+ sb->s_maxbytes = 0xFFFFFFFF;
+ sb->s_blocksize = PAGE_SIZE;
+ sb->s_blocksize_bits = PAGE_SHIFT;
+ sb->s_magic = JFFS2_SUPER_MAGIC;
+
+ return 0;
+
+out_root:
+ jffs2_free_ino_caches(c);
+ jffs2_free_raw_node_refs(c);
+out_inohash:
+ kfree(c->inocache_list);
+out_wbuf:
+ jffs2_flash_cleanup(c);
+ return ret;
+}
+
+static int jffs2_flash_setup(struct jffs2_sb_info *c) {
+ int ret = 0;
+
+ if (jffs2_cleanmarker_oob(c)) {
+ /* NAND flash... do setup accordingly */
+ ret = jffs2_nand_flash_setup(c);
+ if (ret)
+ return ret;
+ }
+
+ /* and Dataflash */
+ if (jffs2_dataflash(c)) {
+ ret = jffs2_dataflash_setup(c);
+ if (ret)
+ return ret;
+ }
+
+ /* and an UBI volume */
+ if (jffs2_ubivol(c)) {
+ ret = jffs2_ubivol_setup(c);
+ if (ret)
+ return ret;
+ }
+
+ return ret;
+}
+
+void jffs2_flash_cleanup(struct jffs2_sb_info *c) {
+
+ if (jffs2_cleanmarker_oob(c)) {
+ jffs2_nand_flash_cleanup(c);
+ }
+
+ /* and DataFlash */
+ if (jffs2_dataflash(c)) {
+ jffs2_dataflash_cleanup(c);
+ }
+
+ /* and Intel "Sibley" flash */
+ if (jffs2_nor_wbuf_flash(c)) {
+ jffs2_nor_wbuf_flash_cleanup(c);
+ }
+
+ /* and an UBI volume */
+ if (jffs2_ubivol(c)) {
+ jffs2_ubivol_cleanup(c);
+ }
+}
+
+static int jffs2_probe(struct device_d *dev)
+{
+ struct fs_device_d *fsdev;
+ struct super_block *sb;
+ struct jffs2_sb_info *ctx;
+ int ret;
+
+ fsdev = dev_to_fs_device(dev);
+ sb = &fsdev->sb;
+
+ ret = fsdev_open_cdev(fsdev);
+ if (ret)
+ goto err_out;
+
+ ctx = kzalloc(sizeof(struct jffs2_sb_info), GFP_KERNEL);
+ if (!ctx)
+ return -ENOMEM;
+
+ ctx->mtd = fsdev->cdev->mtd;
+
+ sb->s_fs_info = ctx;
+
+ ret = jffs2_compressors_init();
+ if (ret) {
+ pr_err("error: Failed to initialise compressors\n");
+ goto err_out;
+ }
+
+ ret = jffs2_create_slab_caches();
+ if (ret) {
+ pr_err("error: Failed to initialise slab caches\n");
+ goto err_compressors;
+ }
+
+ if (jffs2_fill_super(fsdev, 0)) {
+ dev_err(dev, "no valid jffs2 found\n");
+ ret = -EINVAL;
+ goto err_slab;
+ }
+
+ return 0;
+
+err_slab:
+ jffs2_destroy_slab_caches();
+err_compressors:
+ jffs2_compressors_exit();
+err_out:
+
+ return ret;
+}
+
+static void jffs2_remove(struct device_d *dev)
+{
+ struct fs_device_d *fsdev;
+ struct super_block *sb;
+
+ fsdev = dev_to_fs_device(dev);
+ sb = &fsdev->sb;
+
+ jffs2_destroy_slab_caches();
+ jffs2_compressors_exit();
+
+ jffs2_put_super(sb);
+}
+
+
+static struct fs_driver_d jffs2_driver = {
+ .open = jffs2_open,
+ .close = jffs2_close,
+ .read = jffs2_read,
+ .type = filetype_jffs2,
+ .flags = 0,
+ .drv = {
+ .probe = jffs2_probe,
+ .remove = jffs2_remove,
+ .name = "jffs2",
+ }
+};
+
+static int jffs2_init(void)
+{
+ return register_fs_driver(&jffs2_driver);
+}
+coredevice_initcall(jffs2_init);
diff --git a/fs/jffs2/jffs2_fs_i.h b/fs/jffs2/jffs2_fs_i.h
new file mode 100644
index 0000000..2d4b030
--- /dev/null
+++ b/fs/jffs2/jffs2_fs_i.h
@@ -0,0 +1,55 @@
+/*
+ * 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/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 */
diff --git a/fs/jffs2/jffs2_fs_sb.h b/fs/jffs2/jffs2_fs_sb.h
new file mode 100644
index 0000000..84d118c
--- /dev/null
+++ b/fs/jffs2/jffs2_fs_sb.h
@@ -0,0 +1,160 @@
+/*
+ * 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 check_ino; /* *NEXT* inode to be checked */
+
+ unsigned int flags;
+
+ struct task_struct *gc_task; /* GC task struct */
+
+ 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 */
+
+ 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 */
diff --git a/fs/jffs2/malloc.c b/fs/jffs2/malloc.c
new file mode 100644
index 0000000..b7afc68
--- /dev/null
+++ b/fs/jffs2/malloc.c
@@ -0,0 +1,315 @@
+/*
+ * 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 <common.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/jffs2.h>
+#include "nodelist.h"
+
+/* These are initialised to NULL in the kernel startup code.
+ If you're porting to other operating systems, beware */
+static struct kmem_cache *full_dnode_slab;
+static struct kmem_cache *raw_dirent_slab;
+static struct kmem_cache *raw_inode_slab;
+static struct kmem_cache *tmp_dnode_info_slab;
+static struct kmem_cache *raw_node_ref_slab;
+static struct kmem_cache *node_frag_slab;
+static struct kmem_cache *inode_cache_slab;
+#ifdef CONFIG_JFFS2_FS_XATTR
+static struct kmem_cache *xattr_datum_cache;
+static struct kmem_cache *xattr_ref_cache;
+#endif
+
+int __init jffs2_create_slab_caches(void)
+{
+ full_dnode_slab = kmem_cache_create("jffs2_full_dnode",
+ sizeof(struct jffs2_full_dnode),
+ 0, 0, NULL);
+ if (!full_dnode_slab)
+ goto err;
+
+ raw_dirent_slab = kmem_cache_create("jffs2_raw_dirent",
+ sizeof(struct jffs2_raw_dirent),
+ 0, SLAB_HWCACHE_ALIGN, NULL);
+ if (!raw_dirent_slab)
+ goto err;
+
+ raw_inode_slab = kmem_cache_create("jffs2_raw_inode",
+ sizeof(struct jffs2_raw_inode),
+ 0, SLAB_HWCACHE_ALIGN, NULL);
+ if (!raw_inode_slab)
+ goto err;
+
+ tmp_dnode_info_slab = kmem_cache_create("jffs2_tmp_dnode",
+ sizeof(struct jffs2_tmp_dnode_info),
+ 0, 0, NULL);
+ if (!tmp_dnode_info_slab)
+ goto err;
+
+ raw_node_ref_slab = kmem_cache_create("jffs2_refblock",
+ sizeof(struct jffs2_raw_node_ref) * (REFS_PER_BLOCK + 1),
+ 0, 0, NULL);
+ if (!raw_node_ref_slab)
+ goto err;
+
+ node_frag_slab = kmem_cache_create("jffs2_node_frag",
+ sizeof(struct jffs2_node_frag),
+ 0, 0, NULL);
+ if (!node_frag_slab)
+ goto err;
+
+ inode_cache_slab = kmem_cache_create("jffs2_inode_cache",
+ sizeof(struct jffs2_inode_cache),
+ 0, 0, NULL);
+ if (!inode_cache_slab)
+ goto err;
+
+#ifdef CONFIG_JFFS2_FS_XATTR
+ xattr_datum_cache = kmem_cache_create("jffs2_xattr_datum",
+ sizeof(struct jffs2_xattr_datum),
+ 0, 0, NULL);
+ if (!xattr_datum_cache)
+ goto err;
+
+ xattr_ref_cache = kmem_cache_create("jffs2_xattr_ref",
+ sizeof(struct jffs2_xattr_ref),
+ 0, 0, NULL);
+ if (!xattr_ref_cache)
+ goto err;
+#endif
+
+ return 0;
+ err:
+ jffs2_destroy_slab_caches();
+ return -ENOMEM;
+}
+
+void jffs2_destroy_slab_caches(void)
+{
+ kmem_cache_destroy(full_dnode_slab);
+ kmem_cache_destroy(raw_dirent_slab);
+ kmem_cache_destroy(raw_inode_slab);
+ kmem_cache_destroy(tmp_dnode_info_slab);
+ kmem_cache_destroy(raw_node_ref_slab);
+ kmem_cache_destroy(node_frag_slab);
+ kmem_cache_destroy(inode_cache_slab);
+#ifdef CONFIG_JFFS2_FS_XATTR
+ kmem_cache_destroy(xattr_datum_cache);
+ kmem_cache_destroy(xattr_ref_cache);
+#endif
+}
+
+struct jffs2_full_dirent *jffs2_alloc_full_dirent(int namesize)
+{
+ struct jffs2_full_dirent *ret;
+ ret = kmalloc(sizeof(struct jffs2_full_dirent) + namesize, GFP_KERNEL);
+ dbg_memalloc("%p\n", ret);
+ return ret;
+}
+
+void jffs2_free_full_dirent(struct jffs2_full_dirent *x)
+{
+ dbg_memalloc("%p\n", x);
+ kfree(x);
+}
+
+struct jffs2_full_dnode *jffs2_alloc_full_dnode(void)
+{
+ struct jffs2_full_dnode *ret;
+ ret = kmem_cache_alloc(full_dnode_slab, GFP_KERNEL);
+ dbg_memalloc("%p\n", ret);
+ return ret;
+}
+
+void jffs2_free_full_dnode(struct jffs2_full_dnode *x)
+{
+ dbg_memalloc("%p\n", x);
+ kmem_cache_free(full_dnode_slab, x);
+}
+
+struct jffs2_raw_dirent *jffs2_alloc_raw_dirent(void)
+{
+ struct jffs2_raw_dirent *ret;
+ ret = kmem_cache_alloc(raw_dirent_slab, GFP_KERNEL);
+ dbg_memalloc("%p\n", ret);
+ return ret;
+}
+
+void jffs2_free_raw_dirent(struct jffs2_raw_dirent *x)
+{
+ dbg_memalloc("%p\n", x);
+ kmem_cache_free(raw_dirent_slab, x);
+}
+
+struct jffs2_raw_inode *jffs2_alloc_raw_inode(void)
+{
+ struct jffs2_raw_inode *ret;
+ ret = kmem_cache_alloc(raw_inode_slab, GFP_KERNEL);
+ dbg_memalloc("%p\n", ret);
+ return ret;
+}
+
+void jffs2_free_raw_inode(struct jffs2_raw_inode *x)
+{
+ dbg_memalloc("%p\n", x);
+ kmem_cache_free(raw_inode_slab, x);
+}
+
+struct jffs2_tmp_dnode_info *jffs2_alloc_tmp_dnode_info(void)
+{
+ struct jffs2_tmp_dnode_info *ret;
+ ret = kmem_cache_alloc(tmp_dnode_info_slab, GFP_KERNEL);
+ dbg_memalloc("%p\n",
+ ret);
+ return ret;
+}
+
+void jffs2_free_tmp_dnode_info(struct jffs2_tmp_dnode_info *x)
+{
+ dbg_memalloc("%p\n", x);
+ kmem_cache_free(tmp_dnode_info_slab, x);
+}
+
+static struct jffs2_raw_node_ref *jffs2_alloc_refblock(void)
+{
+ struct jffs2_raw_node_ref *ret;
+
+ ret = kmem_cache_alloc(raw_node_ref_slab, GFP_KERNEL);
+ if (ret) {
+ int i = 0;
+ for (i=0; i < REFS_PER_BLOCK; i++) {
+ ret[i].flash_offset = REF_EMPTY_NODE;
+ ret[i].next_in_ino = NULL;
+ }
+ ret[i].flash_offset = REF_LINK_NODE;
+ ret[i].next_in_ino = NULL;
+ }
+ return ret;
+}
+
+int jffs2_prealloc_raw_node_refs(struct jffs2_sb_info *c,
+ struct jffs2_eraseblock *jeb, int nr)
+{
+ struct jffs2_raw_node_ref **p, *ref;
+ int i = nr;
+
+ dbg_memalloc("%d\n", nr);
+
+ p = &jeb->last_node;
+ ref = *p;
+
+ dbg_memalloc("Reserving %d refs for block @0x%08x\n", nr, jeb->offset);
+
+ /* If jeb->last_node is really a valid node then skip over it */
+ if (ref && ref->flash_offset != REF_EMPTY_NODE)
+ ref++;
+
+ while (i) {
+ if (!ref) {
+ dbg_memalloc("Allocating new refblock linked from %p\n", p);
+ ref = *p = jffs2_alloc_refblock();
+ if (!ref)
+ return -ENOMEM;
+ }
+ if (ref->flash_offset == REF_LINK_NODE) {
+ p = &ref->next_in_ino;
+ ref = *p;
+ continue;
+ }
+ i--;
+ ref++;
+ }
+ jeb->allocated_refs = nr;
+
+ dbg_memalloc("Reserved %d refs for block @0x%08x, last_node is %p (%08x,%p)\n",
+ nr, jeb->offset, jeb->last_node, jeb->last_node->flash_offset,
+ jeb->last_node->next_in_ino);
+
+ return 0;
+}
+
+void jffs2_free_refblock(struct jffs2_raw_node_ref *x)
+{
+ dbg_memalloc("%p\n", x);
+ kmem_cache_free(raw_node_ref_slab, x);
+}
+
+struct jffs2_node_frag *jffs2_alloc_node_frag(void)
+{
+ struct jffs2_node_frag *ret;
+ ret = kmem_cache_alloc(node_frag_slab, GFP_KERNEL);
+ dbg_memalloc("%p\n", ret);
+ return ret;
+}
+
+void jffs2_free_node_frag(struct jffs2_node_frag *x)
+{
+ dbg_memalloc("%p\n", x);
+ kmem_cache_free(node_frag_slab, x);
+}
+
+struct jffs2_inode_cache *jffs2_alloc_inode_cache(void)
+{
+ struct jffs2_inode_cache *ret;
+ ret = kmem_cache_alloc(inode_cache_slab, GFP_KERNEL);
+ dbg_memalloc("%p\n", ret);
+ return ret;
+}
+
+void jffs2_free_inode_cache(struct jffs2_inode_cache *x)
+{
+ dbg_memalloc("%p\n", x);
+ kmem_cache_free(inode_cache_slab, x);
+}
+
+#ifdef CONFIG_JFFS2_FS_XATTR
+struct jffs2_xattr_datum *jffs2_alloc_xattr_datum(void)
+{
+ struct jffs2_xattr_datum *xd;
+ xd = kmem_cache_zalloc(xattr_datum_cache, GFP_KERNEL);
+ dbg_memalloc("%p\n", xd);
+ if (!xd)
+ return NULL;
+
+ xd->class = RAWNODE_CLASS_XATTR_DATUM;
+ xd->node = (void *)xd;
+ INIT_LIST_HEAD(&xd->xindex);
+ return xd;
+}
+
+void jffs2_free_xattr_datum(struct jffs2_xattr_datum *xd)
+{
+ dbg_memalloc("%p\n", xd);
+ kmem_cache_free(xattr_datum_cache, xd);
+}
+
+struct jffs2_xattr_ref *jffs2_alloc_xattr_ref(void)
+{
+ struct jffs2_xattr_ref *ref;
+ ref = kmem_cache_zalloc(xattr_ref_cache, GFP_KERNEL);
+ dbg_memalloc("%p\n", ref);
+ if (!ref)
+ return NULL;
+
+ ref->class = RAWNODE_CLASS_XATTR_REF;
+ ref->node = (void *)ref;
+ return ref;
+}
+
+void jffs2_free_xattr_ref(struct jffs2_xattr_ref *ref)
+{
+ dbg_memalloc("%p\n", ref);
+ kmem_cache_free(xattr_ref_cache, ref);
+}
+#endif
diff --git a/fs/jffs2/nodelist.c b/fs/jffs2/nodelist.c
new file mode 100644
index 0000000..446407e
--- /dev/null
+++ b/fs/jffs2/nodelist.c
@@ -0,0 +1,915 @@
+/*
+ * 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 <common.h>
+#include <crc.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/fs.h>
+#include <linux/mtd/mtd.h>
+#include <linux/rbtree.h>
+#include <linux/pagemap.h>
+#include "nodelist.h"
+
+static void jffs2_obsolete_node_frag(struct jffs2_sb_info *c,
+ struct jffs2_node_frag *this);
+
+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;
+ int addedsize;
+ 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];
+
+ 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 {
+ jffs2_dbg(1, "...and adding to erase_pending_list\n");
+ list_add_tail(&jeb->list, &c->erase_pending_list);
+ c->nr_erasing_blocks++;
+ }
+ 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);
+
+ return;
+}
+
+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_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_SHIFT == newfrag->ofs >> PAGE_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_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_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;
+ 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, *next;
+
+ dbg_fragtree("killing\n");
+ rbtree_postorder_for_each_entry_safe(frag, next, root, rb) {
+ 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);
+ }
+
+ jffs2_free_node_frag(frag);
+ 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;
+}
diff --git a/fs/jffs2/nodelist.h b/fs/jffs2/nodelist.h
new file mode 100644
index 0000000..f78e176
--- /dev/null
+++ b/fs/jffs2/nodelist.h
@@ -0,0 +1,506 @@
+/*
+ * 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 <common.h>
+#include <linux/fs.h>
+#include <linux/hash.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
+
+#define crc32(seed, data, length) crc32_no_comp(seed, (unsigned char const *)data, length)
+
+/* Hash courtesy of the R5 hash in reiserfs modulo sign bits */
+#define init_name_hash(salt) (unsigned long)(salt)
+
+/* partial hash update function. Assume roughly 4 bits per character */
+static inline unsigned long
+partial_name_hash(unsigned long c, unsigned long prevhash)
+{
+ return (prevhash + (c << 4) + (c >> 4)) * 11;
+}
+
+/*
+ * Finally: cut down the number of bits to a int value (and try to avoid
+ * losing bits). This also has the property (wanted by the dcache)
+ * that the msbits make a good hash table index.
+ */
+static inline unsigned int end_name_hash(unsigned long hash)
+{
+ return hash_long(hash, 32);
+}
+
+/* Return the hash of a string of known length */
+static inline unsigned int full_name_hash(const void *salt, const char *name, unsigned int len)
+{
+ unsigned long hash = init_name_hash(salt);
+ while (len--)
+ hash = partial_name_hash((unsigned char)*name++, hash);
+ return end_name_hash(hash);
+}
+
+/* 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 INO_FLAGS_IS_DIR 0x02 /* is a directory */
+
+#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;
+ uint32_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
+{
+ union {
+ struct jffs2_raw_node_ref *raw;
+ struct jffs2_inode_cache *ic; /* Just during part of build */
+ };
+ 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 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 *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 *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__ */
diff --git a/fs/jffs2/os-linux.h b/fs/jffs2/os-linux.h
new file mode 100644
index 0000000..b86a55e
--- /dev/null
+++ b/fs/jffs2/os-linux.h
@@ -0,0 +1,193 @@
+/*
+ * 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_OS_LINUX_H__
+#define __JFFS2_OS_LINUX_H__
+
+/* JFFS2 uses Linux mode bits natively -- no need for conversion */
+#define os_to_jffs2_mode(x) (x)
+#define jffs2_to_os_mode(x) (x)
+
+struct kstatfs;
+struct kvec;
+struct fs_device_d;
+
+#define JFFS2_BLOCK_SIZE PAGE_SIZE
+
+#define JFFS2_INODE_INFO(i) (container_of(i, struct jffs2_inode_info, vfs_inode))
+#define OFNI_EDONI_2SFFJ(f) (&(f)->vfs_inode)
+#define JFFS2_SB_INFO(sb) (sb->s_fs_info)
+#define OFNI_BS_2SFFJ(c) ((struct super_block *)c->os_priv)
+
+
+#define JFFS2_F_I_SIZE(f) (OFNI_EDONI_2SFFJ(f)->i_size)
+#define JFFS2_F_I_MODE(f) (OFNI_EDONI_2SFFJ(f)->i_mode)
+#define JFFS2_F_I_UID(f) (i_uid_read(OFNI_EDONI_2SFFJ(f)))
+#define JFFS2_F_I_GID(f) (i_gid_read(OFNI_EDONI_2SFFJ(f)))
+#define JFFS2_F_I_RDEV(f) (OFNI_EDONI_2SFFJ(f)->i_rdev)
+
+#define JFFS2_CLAMP_TIME(t) ((uint32_t)clamp_t(time64_t, (t), 0, U32_MAX))
+#define ITIME(sec) ((struct timespec){sec, 0})
+#define JFFS2_NOW() JFFS2_CLAMP_TIME(ktime_get_real_seconds())
+#define I_SEC(tv) JFFS2_CLAMP_TIME((tv).tv_sec)
+#define JFFS2_F_I_CTIME(f) I_SEC(OFNI_EDONI_2SFFJ(f)->i_ctime)
+#define JFFS2_F_I_MTIME(f) I_SEC(OFNI_EDONI_2SFFJ(f)->i_mtime)
+#define JFFS2_F_I_ATIME(f) I_SEC(OFNI_EDONI_2SFFJ(f)->i_atime)
+#define sleep_on_spinunlock(wq, s) \
+ do { \
+ DECLARE_WAITQUEUE(__wait, current); \
+ add_wait_queue((wq), &__wait); \
+ set_current_state(TASK_UNINTERRUPTIBLE); \
+ spin_unlock(s); \
+ schedule(); \
+ remove_wait_queue((wq), &__wait); \
+ } while(0)
+
+static inline void jffs2_init_inode_info(struct jffs2_inode_info *f)
+{
+ f->highest_version = 0;
+ f->fragtree = RB_ROOT;
+ f->metadata = NULL;
+ f->dents = NULL;
+ f->target = NULL;
+ f->flags = 0;
+ f->usercompr = 0;
+}
+
+struct jffs2_file {
+ struct inode *inode;
+ void *buf;
+ unsigned int offset;
+};
+
+#define jffs2_is_readonly(c) (OFNI_BS_2SFFJ(c)->s_flags & SB_RDONLY)
+
+#define SECTOR_ADDR(x) ( (((unsigned long)(x) / c->sector_size) * c->sector_size) )
+#ifndef CONFIG_JFFS2_FS_WRITEBUFFER
+
+
+#ifdef CONFIG_JFFS2_SUMMARY
+#define jffs2_can_mark_obsolete(c) (0)
+#else
+#define jffs2_can_mark_obsolete(c) (1)
+#endif
+
+#define jffs2_is_writebuffered(c) (0)
+#define jffs2_cleanmarker_oob(c) (0)
+#define jffs2_write_nand_cleanmarker(c,jeb) (-EIO)
+
+#define jffs2_flash_write(c, ofs, len, retlen, buf) jffs2_flash_direct_write(c, ofs, len, retlen, buf)
+#define jffs2_flash_read(c, ofs, len, retlen, buf) (mtd_read((c)->mtd, ofs, len, retlen, buf))
+#define jffs2_flush_wbuf_pad(c) ({ do{} while(0); (void)(c), 0; })
+#define jffs2_flush_wbuf_gc(c, i) ({ do{} while(0); (void)(c), (void) i, 0; })
+#define jffs2_write_nand_badblock(c,jeb,bad_offset) (1)
+#define jffs2_nand_flash_setup(c) (0)
+#define jffs2_nand_flash_cleanup(c) do {} while(0)
+#define jffs2_wbuf_dirty(c) (0)
+#define jffs2_flash_writev(a,b,c,d,e,f) jffs2_flash_direct_writev(a,b,c,d,e)
+#define jffs2_wbuf_timeout NULL
+#define jffs2_wbuf_process NULL
+#define jffs2_dataflash(c) (0)
+#define jffs2_dataflash_setup(c) (0)
+#define jffs2_dataflash_cleanup(c) do {} while (0)
+#define jffs2_nor_wbuf_flash(c) (0)
+#define jffs2_nor_wbuf_flash_setup(c) (0)
+#define jffs2_nor_wbuf_flash_cleanup(c) do {} while (0)
+#define jffs2_ubivol(c) (0)
+#define jffs2_ubivol_setup(c) (0)
+#define jffs2_ubivol_cleanup(c) do {} while (0)
+#define jffs2_dirty_trigger(c) do {} while (0)
+
+#else /* NAND and/or ECC'd NOR support present */
+
+#define jffs2_is_writebuffered(c) (c->wbuf != NULL)
+
+#ifdef CONFIG_JFFS2_SUMMARY
+#define jffs2_can_mark_obsolete(c) (0)
+#else
+#define jffs2_can_mark_obsolete(c) (c->mtd->flags & (MTD_BIT_WRITEABLE))
+#endif
+
+#define jffs2_cleanmarker_oob(c) (c->mtd->type == MTD_NANDFLASH)
+
+#define jffs2_wbuf_dirty(c) (!!(c)->wbuf_len)
+
+/* wbuf.c */
+int jffs2_flash_writev(struct jffs2_sb_info *c, const struct kvec *vecs, unsigned long count, loff_t to, size_t *retlen, uint32_t ino);
+int jffs2_flash_write(struct jffs2_sb_info *c, loff_t ofs, size_t len, size_t *retlen, const u_char *buf);
+int jffs2_flash_read(struct jffs2_sb_info *c, loff_t ofs, size_t len, size_t *retlen, u_char *buf);
+int jffs2_check_oob_empty(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb,int mode);
+int jffs2_check_nand_cleanmarker(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb);
+int jffs2_write_nand_cleanmarker(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb);
+int jffs2_write_nand_badblock(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, uint32_t bad_offset);
+void jffs2_wbuf_timeout(unsigned long data);
+void jffs2_wbuf_process(void *data);
+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_nand_flash_setup(struct jffs2_sb_info *c);
+void jffs2_nand_flash_cleanup(struct jffs2_sb_info *c);
+
+#define jffs2_dataflash(c) (c->mtd->type == MTD_DATAFLASH)
+int jffs2_dataflash_setup(struct jffs2_sb_info *c);
+void jffs2_dataflash_cleanup(struct jffs2_sb_info *c);
+#define jffs2_ubivol(c) (c->mtd->type == MTD_UBIVOLUME)
+int jffs2_ubivol_setup(struct jffs2_sb_info *c);
+void jffs2_ubivol_cleanup(struct jffs2_sb_info *c);
+
+#define jffs2_nor_wbuf_flash(c) (c->mtd->type == MTD_NORFLASH && ! (c->mtd->flags & MTD_BIT_WRITEABLE))
+int jffs2_nor_wbuf_flash_setup(struct jffs2_sb_info *c);
+void jffs2_nor_wbuf_flash_cleanup(struct jffs2_sb_info *c);
+void jffs2_dirty_trigger(struct jffs2_sb_info *c);
+
+#endif /* WRITEBUFFER */
+
+/* background.c */
+int jffs2_start_garbage_collect_thread(struct jffs2_sb_info *c);
+void jffs2_stop_garbage_collect_thread(struct jffs2_sb_info *c);
+void jffs2_garbage_collect_trigger(struct jffs2_sb_info *c);
+
+/* dir.c */
+extern const struct file_operations jffs2_dir_operations;
+extern const struct inode_operations jffs2_dir_inode_operations;
+
+/* file.c */
+extern const struct file_operations jffs2_file_operations;
+extern const struct inode_operations jffs2_file_inode_operations;
+extern const struct address_space_operations jffs2_file_address_operations;
+int jffs2_fsync(struct file *, loff_t, loff_t, int);
+
+/* ioctl.c */
+long jffs2_ioctl(struct file *, unsigned int, unsigned long);
+
+/* symlink.c */
+extern const struct inode_operations jffs2_symlink_inode_operations;
+
+/* fs.c */
+struct inode *jffs2_iget(struct super_block *, unsigned long);
+int jffs2_do_fill_super(struct super_block *sb, int);
+void jffs2_gc_release_inode(struct jffs2_sb_info *c,
+ struct jffs2_inode_info *f);
+struct jffs2_inode_info *jffs2_gc_fetch_inode(struct jffs2_sb_info *c,
+ int inum, int unlinked);
+
+void jffs2_flash_cleanup(struct jffs2_sb_info *c);
+
+/* super.c */
+int jffs2_fill_super(struct fs_device_d *fsdev, int silent);
+
+/* writev.c */
+int jffs2_flash_direct_writev(struct jffs2_sb_info *c, const struct kvec *vecs,
+ unsigned long count, loff_t to, size_t *retlen);
+int jffs2_flash_direct_write(struct jffs2_sb_info *c, loff_t ofs, size_t len,
+ size_t *retlen, const u_char *buf);
+
+#endif /* __JFFS2_OS_LINUX_H__ */
+
diff --git a/fs/jffs2/read.c b/fs/jffs2/read.c
new file mode 100644
index 0000000..2848e4d
--- /dev/null
+++ b/fs/jffs2/read.c
@@ -0,0 +1,229 @@
+/*
+ * 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 <common.h>
+#include <crc.h>
+#include <linux/kernel.h>
+#include <linux/slab.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;
+}
diff --git a/fs/jffs2/readinode.c b/fs/jffs2/readinode.c
new file mode 100644
index 0000000..0fe67cb
--- /dev/null
+++ b/fs/jffs2/readinode.c
@@ -0,0 +1,1401 @@
+/*
+ * 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 <common.h>
+#include <crc.h>
+#include <fs.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/pagemap.h>
+#include <linux/mtd/mtd.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);
+
+ 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);
+
+ 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);
+ 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 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 jffs2_tmp_dnode_info *tn, *next;
+
+ rbtree_postorder_for_each_entry_safe(tn, next, list, rb) {
+ jffs2_free_full_dnode(tn->fn);
+ jffs2_free_tmp_dnode_info(tn);
+ }
+
+ *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)) {
+ jffs2_free_full_dirent(fd);
+ JFFS2_ERROR("short read: wanted %d bytes, got %zd\n",
+ rd->nsize - already, read);
+ 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(NULL, 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_IWUSR);
+ 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. */
+ 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));
+ 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)
+ 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);
+ 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;
+ 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));
+ 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));
+ 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 */
+ 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);
+
+ 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:
+ break;
+ 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("requested to read a 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);
+ 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);
+
+ 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);
+}
diff --git a/fs/jffs2/scan.c b/fs/jffs2/scan.c
new file mode 100644
index 0000000..64a5e02
--- /dev/null
+++ b/fs/jffs2/scan.c
@@ -0,0 +1,1157 @@
+/*
+ * 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 <common.h>
+#include <crc.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/mtd/mtd.h>
+#include <linux/pagemap.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 */
+ size_t try_size;
+
+ 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 = kmalloc(try_size, GFP_KERNEL);
+ 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;
+ }
+ }
+ ret = 0;
+ out:
+ if (buf_size)
+ kfree(flashbuf);
+ 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;
+
+ /* sm->offset maybe wrong but MAGIC maybe right */
+ if (sumlen > c->sector_size)
+ goto full_scan;
+
+ /* 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) {
+ if (sumlen > buf_size)
+ kfree(sumptr);
+ 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;
+ }
+ }
+
+full_scan:
+ 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)) {
+ jffs2_dbg(1,"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) {
+ jffs2_dbg(1, "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) {
+ jffs2_dbg(1, "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(NULL, 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);
+ }
+}
diff --git a/fs/jffs2/summary.h b/fs/jffs2/summary.h
new file mode 100644
index 0000000..a4b4747
--- /dev/null
+++ b/fs/jffs2/summary.h
@@ -0,0 +1,212 @@
+/*
+ * 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/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 */
diff --git a/fs/jffs2/super.c b/fs/jffs2/super.c
new file mode 100644
index 0000000..d9bb6d0
--- /dev/null
+++ b/fs/jffs2/super.c
@@ -0,0 +1,130 @@
+/*
+ * 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 <common.h>
+#include <init.h>
+#include <linux/slab.h>
+#include <linux/list.h>
+#include <fs.h>
+#include <linux/err.h>
+#include <linux/mount.h>
+#include <linux/jffs2.h>
+#include <linux/pagemap.h>
+#include <linux/mtd/mtd.h>
+#include <linux/ctype.h>
+#include <linux/namei.h>
+#include "compr.h"
+#include "nodelist.h"
+
+static struct kmem_cache *jffs2_inode_cachep;
+
+/* from include/linux/fs.h */
+static inline void i_uid_write(struct inode *inode, uid_t uid)
+{
+ inode->i_uid = uid;
+}
+
+static inline void i_gid_write(struct inode *inode, gid_t gid)
+{
+ inode->i_gid = gid;
+}
+
+
+static struct inode *jffs2_alloc_inode(struct super_block *sb)
+{
+ struct jffs2_inode_info *f;
+
+ f = kmem_cache_alloc(jffs2_inode_cachep, GFP_KERNEL);
+ if (!f)
+ return NULL;
+ return &f->vfs_inode;
+}
+
+static void jffs2_destroy_inode(struct inode *inode)
+{
+ struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode);
+
+ kfree(f->target);
+ kmem_cache_free(jffs2_inode_cachep, f);
+}
+
+static void jffs2_i_init_once(void *foo)
+{
+}
+
+static const struct super_operations jffs2_super_operations =
+{
+ .alloc_inode = jffs2_alloc_inode,
+ .destroy_inode = jffs2_destroy_inode,
+};
+
+/*
+ * fill in the superblock
+ */
+int jffs2_fill_super(struct fs_device_d *fsdev, int silent)
+{
+ struct super_block *sb = &fsdev->sb;
+ struct jffs2_sb_info *c = sb->s_fs_info;
+
+ c->os_priv = sb;
+
+ /* Initialize JFFS2 superblock locks, the further initialization will
+ * be done later */
+ mutex_init(&c->alloc_sem);
+ mutex_init(&c->erase_free_sem);
+ init_waitqueue_head(&c->erase_wait);
+ init_waitqueue_head(&c->inocache_wq);
+ spin_lock_init(&c->erase_completion_lock);
+ spin_lock_init(&c->inocache_lock);
+
+ sb->s_op = &jffs2_super_operations;
+ sb->s_flags = sb->s_flags | SB_NOATIME;
+ sb->s_xattr = jffs2_xattr_handlers;
+
+ return jffs2_do_fill_super(sb, silent);
+}
+
+static int __init init_jffs2_fs(void)
+{
+ /* Paranoia checks for on-medium structures. If we ask GCC
+ to pack them with __attribute__((packed)) then it _also_
+ assumes that they're not aligned -- so it emits crappy
+ code on some architectures. Ideally we want an attribute
+ which means just 'no padding', without the alignment
+ thing. But GCC doesn't have that -- we have to just
+ hope the structs are the right sizes, instead. */
+ BUILD_BUG_ON(sizeof(struct jffs2_unknown_node) != 12);
+ BUILD_BUG_ON(sizeof(struct jffs2_raw_dirent) != 40);
+ BUILD_BUG_ON(sizeof(struct jffs2_raw_inode) != 68);
+ BUILD_BUG_ON(sizeof(struct jffs2_raw_summary) != 32);
+
+ pr_info("JFFS version 2.2."
+#ifdef CONFIG_JFFS2_FS_WRITEBUFFER
+ " (NAND)"
+#endif
+#ifdef CONFIG_JFFS2_SUMMARY
+ " (SUMMARY) "
+#endif
+ " © 2001-2006 Red Hat, Inc.\n");
+
+ jffs2_inode_cachep = kmem_cache_create("jffs2_i",
+ sizeof(struct jffs2_inode_info),
+ 0, (SLAB_RECLAIM_ACCOUNT|
+ SLAB_MEM_SPREAD|SLAB_ACCOUNT),
+ jffs2_i_init_once);
+ if (!jffs2_inode_cachep) {
+ pr_err("error: Failed to initialise inode cache\n");
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+late_initcall(init_jffs2_fs);
diff --git a/fs/jffs2/xattr.h b/fs/jffs2/xattr.h
new file mode 100644
index 0000000..dced873
--- /dev/null
+++ b/fs/jffs2/xattr.h
@@ -0,0 +1,126 @@
+/*
+ * 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/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] */
+ uint32_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);
+
+#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
+
+#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_ */