aboutsummaryrefslogtreecommitdiff
path: root/fs/jffs/intrep.c
diff options
context:
space:
mode:
Diffstat (limited to 'fs/jffs/intrep.c')
-rw-r--r--fs/jffs/intrep.c3457
1 files changed, 3457 insertions, 0 deletions
diff --git a/fs/jffs/intrep.c b/fs/jffs/intrep.c
new file mode 100644
index 00000000000..8cc6893fc56
--- /dev/null
+++ b/fs/jffs/intrep.c
@@ -0,0 +1,3457 @@
+/*
+ * JFFS -- Journaling Flash File System, Linux implementation.
+ *
+ * Copyright (C) 1999, 2000 Axis Communications, Inc.
+ *
+ * Created by Finn Hakansson <finn@axis.com>.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * $Id: intrep.c,v 1.102 2001/09/23 23:28:36 dwmw2 Exp $
+ *
+ * Ported to Linux 2.3.x and MTD:
+ * Copyright (C) 2000 Alexander Larsson (alex@cendio.se), Cendio Systems AB
+ *
+ */
+
+/* This file contains the code for the internal structure of the
+ Journaling Flash File System, JFFS. */
+
+/*
+ * Todo list:
+ *
+ * memcpy_to_flash() and memcpy_from_flash() functions.
+ *
+ * Implementation of hard links.
+ *
+ * Organize the source code in a better way. Against the VFS we could
+ * have jffs_ext.c, and against the block device jffs_int.c.
+ * A better file-internal organization too.
+ *
+ * A better checksum algorithm.
+ *
+ * Consider endianness stuff. ntohl() etc.
+ *
+ * Are we handling the atime, mtime, ctime members of the inode right?
+ *
+ * Remove some duplicated code. Take a look at jffs_write_node() and
+ * jffs_rewrite_data() for instance.
+ *
+ * Implement more meaning of the nlink member in various data structures.
+ * nlink could be used in conjunction with hard links for instance.
+ *
+ * Better memory management. Allocate data structures in larger chunks
+ * if possible.
+ *
+ * If too much meta data is stored, a garbage collect should be issued.
+ * We have experienced problems with too much meta data with for instance
+ * log files.
+ *
+ * Improve the calls to jffs_ioctl(). We would like to retrieve more
+ * information to be able to debug (or to supervise) JFFS during run-time.
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/slab.h>
+#include <linux/jffs.h>
+#include <linux/fs.h>
+#include <linux/stat.h>
+#include <linux/pagemap.h>
+#include <asm/semaphore.h>
+#include <asm/byteorder.h>
+#include <linux/smp_lock.h>
+#include <linux/time.h>
+#include <linux/ctype.h>
+
+#include "intrep.h"
+#include "jffs_fm.h"
+
+long no_jffs_node = 0;
+static long no_jffs_file = 0;
+#if defined(JFFS_MEMORY_DEBUG) && JFFS_MEMORY_DEBUG
+long no_jffs_control = 0;
+long no_jffs_raw_inode = 0;
+long no_jffs_node_ref = 0;
+long no_jffs_fm = 0;
+long no_jffs_fmcontrol = 0;
+long no_hash = 0;
+long no_name = 0;
+#endif
+
+static int jffs_scan_flash(struct jffs_control *c);
+static int jffs_update_file(struct jffs_file *f, struct jffs_node *node);
+static int jffs_build_file(struct jffs_file *f);
+static int jffs_free_file(struct jffs_file *f);
+static int jffs_free_node_list(struct jffs_file *f);
+static int jffs_garbage_collect_now(struct jffs_control *c);
+static int jffs_insert_file_into_hash(struct jffs_file *f);
+static int jffs_remove_redundant_nodes(struct jffs_file *f);
+
+/* Is there enough space on the flash? */
+static inline int JFFS_ENOUGH_SPACE(struct jffs_control *c, __u32 space)
+{
+ struct jffs_fmcontrol *fmc = c->fmc;
+
+ while (1) {
+ if ((fmc->flash_size - (fmc->used_size + fmc->dirty_size))
+ >= fmc->min_free_size + space) {
+ return 1;
+ }
+ if (fmc->dirty_size < fmc->sector_size)
+ return 0;
+
+ if (jffs_garbage_collect_now(c)) {
+ D1(printk("JFFS_ENOUGH_SPACE: jffs_garbage_collect_now() failed.\n"));
+ return 0;
+ }
+ }
+}
+
+#if CONFIG_JFFS_FS_VERBOSE > 0
+static __u8
+flash_read_u8(struct mtd_info *mtd, loff_t from)
+{
+ size_t retlen;
+ __u8 ret;
+ int res;
+
+ res = MTD_READ(mtd, from, 1, &retlen, &ret);
+ if (retlen != 1) {
+ printk("Didn't read a byte in flash_read_u8(). Returned %d\n", res);
+ return 0;
+ }
+
+ return ret;
+}
+
+static void
+jffs_hexdump(struct mtd_info *mtd, loff_t pos, int size)
+{
+ char line[16];
+ int j = 0;
+
+ while (size > 0) {
+ int i;
+
+ printk("%ld:", (long) pos);
+ for (j = 0; j < 16; j++) {
+ line[j] = flash_read_u8(mtd, pos++);
+ }
+ for (i = 0; i < j; i++) {
+ if (!(i & 1)) {
+ printk(" %.2x", line[i] & 0xff);
+ }
+ else {
+ printk("%.2x", line[i] & 0xff);
+ }
+ }
+
+ /* Print empty space */
+ for (; i < 16; i++) {
+ if (!(i & 1)) {
+ printk(" ");
+ }
+ else {
+ printk(" ");
+ }
+ }
+ printk(" ");
+
+ for (i = 0; i < j; i++) {
+ if (isgraph(line[i])) {
+ printk("%c", line[i]);
+ }
+ else {
+ printk(".");
+ }
+ }
+ printk("\n");
+ size -= 16;
+ }
+}
+
+#endif
+
+#define flash_safe_acquire(arg)
+#define flash_safe_release(arg)
+
+
+static int
+flash_safe_read(struct mtd_info *mtd, loff_t from,
+ u_char *buf, size_t count)
+{
+ size_t retlen;
+ int res;
+
+ D3(printk(KERN_NOTICE "flash_safe_read(%p, %08x, %p, %08x)\n",
+ mtd, (unsigned int) from, buf, count));
+
+ res = MTD_READ(mtd, from, count, &retlen, buf);
+ if (retlen != count) {
+ panic("Didn't read all bytes in flash_safe_read(). Returned %d\n", res);
+ }
+ return res?res:retlen;
+}
+
+
+static __u32
+flash_read_u32(struct mtd_info *mtd, loff_t from)
+{
+ size_t retlen;
+ __u32 ret;
+ int res;
+
+ res = MTD_READ(mtd, from, 4, &retlen, (unsigned char *)&ret);
+ if (retlen != 4) {
+ printk("Didn't read all bytes in flash_read_u32(). Returned %d\n", res);
+ return 0;
+ }
+
+ return ret;
+}
+
+
+static int
+flash_safe_write(struct mtd_info *mtd, loff_t to,
+ const u_char *buf, size_t count)
+{
+ size_t retlen;
+ int res;
+
+ D3(printk(KERN_NOTICE "flash_safe_write(%p, %08x, %p, %08x)\n",
+ mtd, (unsigned int) to, buf, count));
+
+ res = MTD_WRITE(mtd, to, count, &retlen, buf);
+ if (retlen != count) {
+ printk("Didn't write all bytes in flash_safe_write(). Returned %d\n", res);
+ }
+ return res?res:retlen;
+}
+
+
+static int
+flash_safe_writev(struct mtd_info *mtd, const struct kvec *vecs,
+ unsigned long iovec_cnt, loff_t to)
+{
+ size_t retlen, retlen_a;
+ int i;
+ int res;
+
+ D3(printk(KERN_NOTICE "flash_safe_writev(%p, %08x, %p)\n",
+ mtd, (unsigned int) to, vecs));
+
+ if (mtd->writev) {
+ res = MTD_WRITEV(mtd, vecs, iovec_cnt, to, &retlen);
+ return res ? res : retlen;
+ }
+ /* Not implemented writev. Repeatedly use write - on the not so
+ unreasonable assumption that the mtd driver doesn't care how
+ many write cycles we use. */
+ res=0;
+ retlen=0;
+
+ for (i=0; !res && i<iovec_cnt; i++) {
+ res = MTD_WRITE(mtd, to, vecs[i].iov_len, &retlen_a, vecs[i].iov_base);
+ if (retlen_a != vecs[i].iov_len) {
+ printk("Didn't write all bytes in flash_safe_writev(). Returned %d\n", res);
+ if (i != iovec_cnt-1)
+ return -EIO;
+ }
+ /* If res is non-zero, retlen_a is undefined, but we don't
+ care because in that case it's not going to be
+ returned anyway.
+ */
+ to += retlen_a;
+ retlen += retlen_a;
+ }
+ return res?res:retlen;
+}
+
+
+static int
+flash_memset(struct mtd_info *mtd, loff_t to,
+ const u_char c, size_t size)
+{
+ static unsigned char pattern[64];
+ int i;
+
+ /* fill up pattern */
+
+ for(i = 0; i < 64; i++)
+ pattern[i] = c;
+
+ /* write as many 64-byte chunks as we can */
+
+ while (size >= 64) {
+ flash_safe_write(mtd, to, pattern, 64);
+ size -= 64;
+ to += 64;
+ }
+
+ /* and the rest */
+
+ if(size)
+ flash_safe_write(mtd, to, pattern, size);
+
+ return size;
+}
+
+
+static void
+intrep_erase_callback(struct erase_info *done)
+{
+ wait_queue_head_t *wait_q;
+
+ wait_q = (wait_queue_head_t *)done->priv;
+
+ wake_up(wait_q);
+}
+
+
+static int
+flash_erase_region(struct mtd_info *mtd, loff_t start,
+ size_t size)
+{
+ struct erase_info *erase;
+ DECLARE_WAITQUEUE(wait, current);
+ wait_queue_head_t wait_q;
+
+ erase = kmalloc(sizeof(struct erase_info), GFP_KERNEL);
+ if (!erase)
+ return -ENOMEM;
+
+ init_waitqueue_head(&wait_q);
+
+ erase->mtd = mtd;
+ erase->callback = intrep_erase_callback;
+ erase->addr = start;
+ erase->len = size;
+ erase->priv = (u_long)&wait_q;
+
+ /* FIXME: Use TASK_INTERRUPTIBLE and deal with being interrupted */
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ add_wait_queue(&wait_q, &wait);
+
+ if (MTD_ERASE(mtd, erase) < 0) {
+ set_current_state(TASK_RUNNING);
+ remove_wait_queue(&wait_q, &wait);
+ kfree(erase);
+
+ printk(KERN_WARNING "flash: erase of region [0x%lx, 0x%lx] "
+ "totally failed\n", (long)start, (long)start + size);
+
+ return -1;
+ }
+
+ schedule(); /* Wait for flash to finish. */
+ remove_wait_queue(&wait_q, &wait);
+
+ kfree(erase);
+
+ return 0;
+}
+
+/* This routine calculates checksums in JFFS. */
+static __u32
+jffs_checksum(const void *data, int size)
+{
+ __u32 sum = 0;
+ __u8 *ptr = (__u8 *)data;
+ while (size-- > 0) {
+ sum += *ptr++;
+ }
+ D3(printk(", result: 0x%08x\n", sum));
+ return sum;
+}
+
+
+static int
+jffs_checksum_flash(struct mtd_info *mtd, loff_t start, int size, __u32 *result)
+{
+ __u32 sum = 0;
+ loff_t ptr = start;
+ __u8 *read_buf;
+ int i, length;
+
+ /* Allocate read buffer */
+ read_buf = (__u8 *) kmalloc (sizeof(__u8) * 4096, GFP_KERNEL);
+ if (!read_buf) {
+ printk(KERN_NOTICE "kmalloc failed in jffs_checksum_flash()\n");
+ return -ENOMEM;
+ }
+ /* Loop until checksum done */
+ while (size) {
+ /* Get amount of data to read */
+ if (size < 4096)
+ length = size;
+ else
+ length = 4096;
+
+ /* Perform flash read */
+ D3(printk(KERN_NOTICE "jffs_checksum_flash\n"));
+ flash_safe_read(mtd, ptr, &read_buf[0], length);
+
+ /* Compute checksum */
+ for (i=0; i < length ; i++)
+ sum += read_buf[i];
+
+ /* Update pointer and size */
+ size -= length;
+ ptr += length;
+ }
+
+ /* Free read buffer */
+ kfree (read_buf);
+
+ /* Return result */
+ D3(printk("checksum result: 0x%08x\n", sum));
+ *result = sum;
+ return 0;
+}
+
+static __inline__ void jffs_fm_write_lock(struct jffs_fmcontrol *fmc)
+{
+ // down(&fmc->wlock);
+}
+
+static __inline__ void jffs_fm_write_unlock(struct jffs_fmcontrol *fmc)
+{
+ // up(&fmc->wlock);
+}
+
+
+/* Create and initialize a new struct jffs_file. */
+static struct jffs_file *
+jffs_create_file(struct jffs_control *c,
+ const struct jffs_raw_inode *raw_inode)
+{
+ struct jffs_file *f;
+
+ if (!(f = (struct jffs_file *)kmalloc(sizeof(struct jffs_file),
+ GFP_KERNEL))) {
+ D(printk("jffs_create_file(): Failed!\n"));
+ return NULL;
+ }
+ no_jffs_file++;
+ memset(f, 0, sizeof(struct jffs_file));
+ f->ino = raw_inode->ino;
+ f->pino = raw_inode->pino;
+ f->nlink = raw_inode->nlink;
+ f->deleted = raw_inode->deleted;
+ f->c = c;
+
+ return f;
+}
+
+
+/* Build a control block for the file system. */
+static struct jffs_control *
+jffs_create_control(struct super_block *sb)
+{
+ struct jffs_control *c;
+ register int s = sizeof(struct jffs_control);
+ int i;
+ D(char *t = 0);
+
+ D2(printk("jffs_create_control()\n"));
+
+ if (!(c = (struct jffs_control *)kmalloc(s, GFP_KERNEL))) {
+ goto fail_control;
+ }
+ DJM(no_jffs_control++);
+ c->root = NULL;
+ c->gc_task = NULL;
+ c->hash_len = JFFS_HASH_SIZE;
+ s = sizeof(struct list_head) * c->hash_len;
+ if (!(c->hash = (struct list_head *)kmalloc(s, GFP_KERNEL))) {
+ goto fail_hash;
+ }
+ DJM(no_hash++);
+ for (i = 0; i < c->hash_len; i++)
+ INIT_LIST_HEAD(&c->hash[i]);
+ if (!(c->fmc = jffs_build_begin(c, MINOR(sb->s_dev)))) {
+ goto fail_fminit;
+ }
+ c->next_ino = JFFS_MIN_INO + 1;
+ c->delete_list = (struct jffs_delete_list *) 0;
+ return c;
+
+fail_fminit:
+ D(t = "c->fmc");
+fail_hash:
+ kfree(c);
+ DJM(no_jffs_control--);
+ D(t = t ? t : "c->hash");
+fail_control:
+ D(t = t ? t : "control");
+ D(printk("jffs_create_control(): Allocation failed: (%s)\n", t));
+ return (struct jffs_control *)0;
+}
+
+
+/* Clean up all data structures associated with the file system. */
+void
+jffs_cleanup_control(struct jffs_control *c)
+{
+ D2(printk("jffs_cleanup_control()\n"));
+
+ if (!c) {
+ D(printk("jffs_cleanup_control(): c == NULL !!!\n"));
+ return;
+ }
+
+ while (c->delete_list) {
+ struct jffs_delete_list *delete_list_element;
+ delete_list_element = c->delete_list;
+ c->delete_list = c->delete_list->next;
+ kfree(delete_list_element);
+ }
+
+ /* Free all files and nodes. */
+ if (c->hash) {
+ jffs_foreach_file(c, jffs_free_node_list);
+ jffs_foreach_file(c, jffs_free_file);
+ kfree(c->hash);
+ DJM(no_hash--);
+ }
+ jffs_cleanup_fmcontrol(c->fmc);
+ kfree(c);
+ DJM(no_jffs_control--);
+ D3(printk("jffs_cleanup_control(): Leaving...\n"));
+}
+
+
+/* This function adds a virtual root node to the in-RAM representation.
+ Called by jffs_build_fs(). */
+static int
+jffs_add_virtual_root(struct jffs_control *c)
+{
+ struct jffs_file *root;
+ struct jffs_node *node;
+
+ D2(printk("jffs_add_virtual_root(): "
+ "Creating a virtual root directory.\n"));
+
+ if (!(root = (struct jffs_file *)kmalloc(sizeof(struct jffs_file),
+ GFP_KERNEL))) {
+ return -ENOMEM;
+ }
+ no_jffs_file++;
+ if (!(node = jffs_alloc_node())) {
+ kfree(root);
+ no_jffs_file--;
+ return -ENOMEM;
+ }
+ DJM(no_jffs_node++);
+ memset(node, 0, sizeof(struct jffs_node));
+ node->ino = JFFS_MIN_INO;
+ memset(root, 0, sizeof(struct jffs_file));
+ root->ino = JFFS_MIN_INO;
+ root->mode = S_IFDIR | S_IRWXU | S_IRGRP
+ | S_IXGRP | S_IROTH | S_IXOTH;
+ root->atime = root->mtime = root->ctime = get_seconds();
+ root->nlink = 1;
+ root->c = c;
+ root->version_head = root->version_tail = node;
+ jffs_insert_file_into_hash(root);
+ return 0;
+}
+
+
+/* This is where the file system is built and initialized. */
+int
+jffs_build_fs(struct super_block *sb)
+{
+ struct jffs_control *c;
+ int err = 0;
+
+ D2(printk("jffs_build_fs()\n"));
+
+ if (!(c = jffs_create_control(sb))) {
+ return -ENOMEM;
+ }
+ c->building_fs = 1;
+ c->sb = sb;
+ if ((err = jffs_scan_flash(c)) < 0) {
+ if(err == -EAGAIN){
+ /* scan_flash() wants us to try once more. A flipping
+ bits sector was detect in the middle of the scan flash.
+ Clean up old allocated memory before going in.
+ */
+ D1(printk("jffs_build_fs: Cleaning up all control structures,"
+ " reallocating them and trying mount again.\n"));
+ jffs_cleanup_control(c);
+ if (!(c = jffs_create_control(sb))) {
+ return -ENOMEM;
+ }
+ c->building_fs = 1;
+ c->sb = sb;
+
+ if ((err = jffs_scan_flash(c)) < 0) {
+ goto jffs_build_fs_fail;
+ }
+ }else{
+ goto jffs_build_fs_fail;
+ }
+ }
+
+ /* Add a virtual root node if no one exists. */
+ if (!jffs_find_file(c, JFFS_MIN_INO)) {
+ if ((err = jffs_add_virtual_root(c)) < 0) {
+ goto jffs_build_fs_fail;
+ }
+ }
+
+ while (c->delete_list) {
+ struct jffs_file *f;
+ struct jffs_delete_list *delete_list_element;
+
+ if ((f = jffs_find_file(c, c->delete_list->ino))) {
+ f->deleted = 1;
+ }
+ delete_list_element = c->delete_list;
+ c->delete_list = c->delete_list->next;
+ kfree(delete_list_element);
+ }
+
+ /* Remove deleted nodes. */
+ if ((err = jffs_foreach_file(c, jffs_possibly_delete_file)) < 0) {
+ printk(KERN_ERR "JFFS: Failed to remove deleted nodes.\n");
+ goto jffs_build_fs_fail;
+ }
+ /* Remove redundant nodes. (We are not interested in the
+ return value in this case.) */
+ jffs_foreach_file(c, jffs_remove_redundant_nodes);
+ /* Try to build a tree from all the nodes. */
+ if ((err = jffs_foreach_file(c, jffs_insert_file_into_tree)) < 0) {
+ printk("JFFS: Failed to build tree.\n");
+ goto jffs_build_fs_fail;
+ }
+ /* Compute the sizes of all files in the filesystem. Adjust if
+ necessary. */
+ if ((err = jffs_foreach_file(c, jffs_build_file)) < 0) {
+ printk("JFFS: Failed to build file system.\n");
+ goto jffs_build_fs_fail;
+ }
+ sb->s_fs_info = (void *)c;
+ c->building_fs = 0;
+
+ D1(jffs_print_hash_table(c));
+ D1(jffs_print_tree(c->root, 0));
+
+ return 0;
+
+jffs_build_fs_fail:
+ jffs_cleanup_control(c);
+ return err;
+} /* jffs_build_fs() */
+
+
+/*
+ This checks for sectors that were being erased in their previous
+ lifetimes and for some reason or the other (power fail etc.),
+ the erase cycles never completed.
+ As the flash array would have reverted back to read status,
+ these sectors are detected by the symptom of the "flipping bits",
+ i.e. bits being read back differently from the same location in
+ flash if read multiple times.
+ The only solution to this is to re-erase the entire
+ sector.
+ Unfortunately detecting "flipping bits" is not a simple exercise
+ as a bit may be read back at 1 or 0 depending on the alignment
+ of the stars in the universe.
+ The level of confidence is in direct proportion to the number of
+ scans done. By power fail testing I (Vipin) have been able to
+ proove that reading twice is not enough.
+ Maybe 4 times? Change NUM_REREADS to a higher number if you want
+ a (even) higher degree of confidence in your mount process.
+ A higher number would of course slow down your mount.
+*/
+static int check_partly_erased_sectors(struct jffs_fmcontrol *fmc){
+
+#define NUM_REREADS 4 /* see note above */
+#define READ_AHEAD_BYTES 4096 /* must be a multiple of 4,
+ usually set to kernel page size */
+
+ __u8 *read_buf1;
+ __u8 *read_buf2;
+
+ int err = 0;
+ int retlen;
+ int i;
+ int cnt;
+ __u32 offset;
+ loff_t pos = 0;
+ loff_t end = fmc->flash_size;
+
+
+ /* Allocate read buffers */
+ read_buf1 = (__u8 *) kmalloc (sizeof(__u8) * READ_AHEAD_BYTES, GFP_KERNEL);
+ if (!read_buf1)
+ return -ENOMEM;
+
+ read_buf2 = (__u8 *) kmalloc (sizeof(__u8) * READ_AHEAD_BYTES, GFP_KERNEL);
+ if (!read_buf2) {
+ kfree(read_buf1);
+ return -ENOMEM;
+ }
+
+ CHECK_NEXT:
+ while(pos < end){
+
+ D1(printk("check_partly_erased_sector():checking sector which contains"
+ " offset 0x%x for flipping bits..\n", (__u32)pos));
+
+ retlen = flash_safe_read(fmc->mtd, pos,
+ &read_buf1[0], READ_AHEAD_BYTES);
+ retlen &= ~3;
+
+ for(cnt = 0; cnt < NUM_REREADS; cnt++){
+ (void)flash_safe_read(fmc->mtd, pos,
+ &read_buf2[0], READ_AHEAD_BYTES);
+
+ for (i=0 ; i < retlen ; i+=4) {
+ /* buffers MUST match, double word for word! */
+ if(*((__u32 *) &read_buf1[i]) !=
+ *((__u32 *) &read_buf2[i])
+ ){
+ /* flipping bits detected, time to erase sector */
+ /* This will help us log some statistics etc. */
+ D1(printk("Flipping bits detected in re-read round:%i of %i\n",
+ cnt, NUM_REREADS));
+ D1(printk("check_partly_erased_sectors:flipping bits detected"
+ " @offset:0x%x(0x%x!=0x%x)\n",
+ (__u32)pos+i, *((__u32 *) &read_buf1[i]),
+ *((__u32 *) &read_buf2[i])));
+
+ /* calculate start of present sector */
+ offset = (((__u32)pos+i)/(__u32)fmc->sector_size) * (__u32)fmc->sector_size;
+
+ D1(printk("check_partly_erased_sector():erasing sector starting 0x%x.\n",
+ offset));
+
+ if (flash_erase_region(fmc->mtd,
+ offset, fmc->sector_size) < 0) {
+ printk(KERN_ERR "JFFS: Erase of flash failed. "
+ "offset = %u, erase_size = %d\n",
+ offset , fmc->sector_size);
+
+ err = -EIO;
+ goto returnBack;
+
+ }else{
+ D1(printk("JFFS: Erase of flash sector @0x%x successful.\n",
+ offset));
+ /* skip ahead to the next sector */
+ pos = (((__u32)pos+i)/(__u32)fmc->sector_size) * (__u32)fmc->sector_size;
+ pos += fmc->sector_size;
+ goto CHECK_NEXT;
+ }
+ }
+ }
+ }
+ pos += READ_AHEAD_BYTES;
+ }
+
+ returnBack:
+ kfree(read_buf1);
+ kfree(read_buf2);
+
+ D2(printk("check_partly_erased_sector():Done checking all sectors till offset 0x%x for flipping bits.\n",
+ (__u32)pos));
+
+ return err;
+
+}/* end check_partly_erased_sectors() */
+
+
+
+/* Scan the whole flash memory in order to find all nodes in the
+ file systems. */
+static int
+jffs_scan_flash(struct jffs_control *c)
+{
+ char name[JFFS_MAX_NAME_LEN + 2];
+ struct jffs_raw_inode raw_inode;
+ struct jffs_node *node = NULL;
+ struct jffs_fmcontrol *fmc = c->fmc;
+ __u32 checksum;
+ __u8 tmp_accurate;
+ __u16 tmp_chksum;
+ __u32 deleted_file;
+ loff_t pos = 0;
+ loff_t start;
+ loff_t test_start;
+ loff_t end = fmc->flash_size;
+ __u8 *read_buf;
+ int i, len, retlen;
+ __u32 offset;
+
+ __u32 free_chunk_size1;
+ __u32 free_chunk_size2;
+
+
+#define NUMFREEALLOWED 2 /* 2 chunks of at least erase size space allowed */
+ int num_free_space = 0; /* Flag err if more than TWO
+ free blocks found. This is NOT allowed
+ by the current jffs design.
+ */
+ int num_free_spc_not_accp = 0; /* For debugging purposed keep count
+ of how much free space was rejected and
+ marked dirty
+ */
+
+ D1(printk("jffs_scan_flash(): start pos = 0x%lx, end = 0x%lx\n",
+ (long)pos, (long)end));
+
+ flash_safe_acquire(fmc->mtd);
+
+ /*
+ check and make sure that any sector does not suffer
+ from the "partly erased, bit flipping syndrome" (TM Vipin :)
+ If so, offending sectors will be erased.
+ */
+ if(check_partly_erased_sectors(fmc) < 0){
+
+ flash_safe_release(fmc->mtd);
+ return -EIO; /* bad, bad, bad error. Cannot continue.*/
+ }
+
+ /* Allocate read buffer */
+ read_buf = (__u8 *) kmalloc (sizeof(__u8) * 4096, GFP_KERNEL);
+ if (!read_buf) {
+ flash_safe_release(fmc->mtd);
+ return -ENOMEM;
+ }
+
+ /* Start the scan. */
+ while (pos < end) {
+ deleted_file = 0;
+
+ /* Remember the position from where we started this scan. */
+ start = pos;
+
+ switch (flash_read_u32(fmc->mtd, pos)) {
+ case JFFS_EMPTY_BITMASK:
+ /* We have found 0xffffffff at this position. We have to
+ scan the rest of the flash till the end or till
+ something else than 0xffffffff is found.
+ Keep going till we do not find JFFS_EMPTY_BITMASK
+ anymore */
+
+ D1(printk("jffs_scan_flash(): 0xffffffff at pos 0x%lx.\n",
+ (long)pos));
+
+ while(pos < end){
+
+ len = end - pos < 4096 ? end - pos : 4096;
+
+ retlen = flash_safe_read(fmc->mtd, pos,
+ &read_buf[0], len);
+
+ retlen &= ~3;
+
+ for (i=0 ; i < retlen ; i+=4, pos += 4) {
+ if(*((__u32 *) &read_buf[i]) !=
+ JFFS_EMPTY_BITMASK)
+ break;
+ }
+ if (i == retlen)
+ continue;
+ else
+ break;
+ }
+
+ D1(printk("jffs_scan_flash():0xffffffff ended at pos 0x%lx.\n",
+ (long)pos));
+
+ /* If some free space ends in the middle of a sector,
+ treat it as dirty rather than clean.
+ This is to handle the case where one thread
+ allocated space for a node, but didn't get to
+ actually _write_ it before power was lost, leaving
+ a gap in the log. Shifting all node writes into
+ a single kernel thread will fix the original problem.
+ */
+ if ((__u32) pos % fmc->sector_size) {
+ /* If there was free space in previous
+ sectors, don't mark that dirty too -
+ only from the beginning of this sector
+ (or from start)
+ */
+
+ test_start = pos & ~(fmc->sector_size-1); /* end of last sector */
+
+ if (start < test_start) {
+
+ /* free space started in the previous sector! */
+
+ if((num_free_space < NUMFREEALLOWED) &&
+ ((unsigned int)(test_start - start) >= fmc->sector_size)){
+
+ /*
+ Count it in if we are still under NUMFREEALLOWED *and* it is
+ at least 1 erase sector in length. This will keep us from
+ picking any little ole' space as "free".
+ */
+
+ D1(printk("Reducing end of free space to 0x%x from 0x%x\n",
+ (unsigned int)test_start, (unsigned int)pos));
+
+ D1(printk("Free space accepted: Starting 0x%x for 0x%x bytes\n",
+ (unsigned int) start,
+ (unsigned int)(test_start - start)));
+
+ /* below, space from "start" to "pos" will be marked dirty. */
+ start = test_start;
+
+ /* Being in here means that we have found at least an entire
+ erase sector size of free space ending on a sector boundary.
+ Keep track of free spaces accepted.
+ */
+ num_free_space++;
+ }else{
+ num_free_spc_not_accp++;
+ D1(printk("Free space (#%i) found but *Not* accepted: Starting"
+ " 0x%x for 0x%x bytes\n",
+ num_free_spc_not_accp, (unsigned int)start,
+ (unsigned int)((unsigned int)(pos & ~(fmc->sector_size-1)) - (unsigned int)start)));
+
+ }
+
+ }
+ if((((__u32)(pos - start)) != 0)){
+
+ D1(printk("Dirty space: Starting 0x%x for 0x%x bytes\n",
+ (unsigned int) start, (unsigned int) (pos - start)));
+ jffs_fmalloced(fmc, (__u32) start,
+ (__u32) (pos - start), NULL);
+ }else{
+ /* "Flipping bits" detected. This means that our scan for them
+ did not catch this offset. See check_partly_erased_sectors() for
+ more info.
+ */
+
+ D1(printk("jffs_scan_flash():wants to allocate dirty flash "
+ "space for 0 bytes.\n"));
+ D1(printk("jffs_scan_flash(): Flipping bits! We will free "
+ "all allocated memory, erase this sector and remount\n"));
+
+ /* calculate start of present sector */
+ offset = (((__u32)pos)/(__u32)fmc->sector_size) * (__u32)fmc->sector_size;
+
+ D1(printk("jffs_scan_flash():erasing sector starting 0x%x.\n",
+ offset));
+
+ if (flash_erase_region(fmc->mtd,
+ offset, fmc->sector_size) < 0) {
+ printk(KERN_ERR "JFFS: Erase of flash failed. "
+ "offset = %u, erase_size = %d\n",
+ offset , fmc->sector_size);
+
+ flash_safe_release(fmc->mtd);
+ kfree (read_buf);
+ return -1; /* bad, bad, bad! */
+
+ }
+ flash_safe_release(fmc->mtd);
+ kfree (read_buf);
+
+ return -EAGAIN; /* erased offending sector. Try mount one more time please. */
+ }
+ }else{
+ /* Being in here means that we have found free space that ends on an erase sector
+ boundary.
+ Count it in if we are still under NUMFREEALLOWED *and* it is at least 1 erase
+ sector in length. This will keep us from picking any little ole' space as "free".
+ */
+ if((num_free_space < NUMFREEALLOWED) &&
+ ((unsigned int)(pos - start) >= fmc->sector_size)){
+ /* We really don't do anything to mark space as free, except *not*
+ mark it dirty and just advance the "pos" location pointer.
+ It will automatically be picked up as free space.
+ */
+ num_free_space++;
+ D1(printk("Free space accepted: Starting 0x%x for 0x%x bytes\n",
+ (unsigned int) start, (unsigned int) (pos - start)));
+ }else{
+ num_free_spc_not_accp++;
+ D1(printk("Free space (#%i) found but *Not* accepted: Starting "
+ "0x%x for 0x%x bytes\n", num_free_spc_not_accp,
+ (unsigned int) start,
+ (unsigned int) (pos - start)));
+
+ /* Mark this space as dirty. We already have our free space. */
+ D1(printk("Dirty space: Starting 0x%x for 0x%x bytes\n",
+ (unsigned int) start, (unsigned int) (pos - start)));
+ jffs_fmalloced(fmc, (__u32) start,
+ (__u32) (pos - start), NULL);
+ }
+
+ }
+ if(num_free_space > NUMFREEALLOWED){
+ printk(KERN_WARNING "jffs_scan_flash(): Found free space "
+ "number %i. Only %i free space is allowed.\n",
+ num_free_space, NUMFREEALLOWED);
+ }
+ continue;
+
+ case JFFS_DIRTY_BITMASK:
+ /* We have found 0x00000000 at this position. Scan as far
+ as possible to find out how much is dirty. */
+ D1(printk("jffs_scan_flash(): 0x00000000 at pos 0x%lx.\n",
+ (long)pos));
+ for (; pos < end
+ && JFFS_DIRTY_BITMASK == flash_read_u32(fmc->mtd, pos);
+ pos += 4);
+ D1(printk("jffs_scan_flash(): 0x00 ended at "
+ "pos 0x%lx.\n", (long)pos));
+ jffs_fmalloced(fmc, (__u32) start,
+ (__u32) (pos - start), NULL);
+ continue;
+
+ case JFFS_MAGIC_BITMASK:
+ /* We have probably found a new raw inode. */
+ break;
+
+ default:
+ bad_inode:
+ /* We're f*cked. This is not solved yet. We have
+ to scan for the magic pattern. */
+ D1(printk("*************** Dirty flash memory or "
+ "bad inode: "
+ "hexdump(pos = 0x%lx, len = 128):\n",
+ (long)pos));
+ D1(jffs_hexdump(fmc->mtd, pos, 128));
+
+ for (pos += 4; pos < end; pos += 4) {
+ switch (flash_read_u32(fmc->mtd, pos)) {
+ case JFFS_MAGIC_BITMASK:
+ case JFFS_EMPTY_BITMASK:
+ /* handle these in the main switch() loop */
+ goto cont_scan;
+
+ default:
+ break;
+ }
+ }
+
+ cont_scan:
+ /* First, mark as dirty the region
+ which really does contain crap. */
+ jffs_fmalloced(fmc, (__u32) start,
+ (__u32) (pos - start),
+ NULL);
+
+ continue;
+ }/* switch */
+
+ /* We have found the beginning of an inode. Create a
+ node for it unless there already is one available. */
+ if (!node) {
+ if (!(node = jffs_alloc_node())) {
+ /* Free read buffer */
+ kfree (read_buf);
+
+ /* Release the flash device */
+ flash_safe_release(fmc->mtd);
+
+ return -ENOMEM;
+ }
+ DJM(no_jffs_node++);
+ }
+
+ /* Read the next raw inode. */
+
+ flash_safe_read(fmc->mtd, pos, (u_char *) &raw_inode,
+ sizeof(struct jffs_raw_inode));
+
+ /* When we compute the checksum for the inode, we never
+ count the 'accurate' or the 'checksum' fields. */
+ tmp_accurate = raw_inode.accurate;
+ tmp_chksum = raw_inode.chksum;
+ raw_inode.accurate = 0;
+ raw_inode.chksum = 0;
+ checksum = jffs_checksum(&raw_inode,
+ sizeof(struct jffs_raw_inode));
+ raw_inode.accurate = tmp_accurate;
+ raw_inode.chksum = tmp_chksum;
+
+ D3(printk("*** We have found this raw inode at pos 0x%lx "
+ "on the flash:\n", (long)pos));
+ D3(jffs_print_raw_inode(&raw_inode));
+
+ if (checksum != raw_inode.chksum) {
+ D1(printk("jffs_scan_flash(): Bad checksum: "
+ "checksum = %u, "
+ "raw_inode.chksum = %u\n",
+ checksum, raw_inode.chksum));
+ pos += sizeof(struct jffs_raw_inode);
+ jffs_fmalloced(fmc, (__u32) start,
+ (__u32) (pos - start), NULL);
+ /* Reuse this unused struct jffs_node. */
+ continue;
+ }
+
+ /* Check the raw inode read so far. Start with the
+ maximum length of the filename. */
+ if (raw_inode.nsize > JFFS_MAX_NAME_LEN) {
+ printk(KERN_WARNING "jffs_scan_flash: Found a "
+ "JFFS node with name too large\n");
+ goto bad_inode;
+ }
+
+ if (raw_inode.rename && raw_inode.dsize != sizeof(__u32)) {
+ printk(KERN_WARNING "jffs_scan_flash: Found a "
+ "rename node with dsize %u.\n",
+ raw_inode.dsize);
+ jffs_print_raw_inode(&raw_inode);
+ goto bad_inode;
+ }
+
+ /* The node's data segment should not exceed a
+ certain length. */
+ if (raw_inode.dsize > fmc->max_chunk_size) {
+ printk(KERN_WARNING "jffs_scan_flash: Found a "
+ "JFFS node with dsize (0x%x) > max_chunk_size (0x%x)\n",
+ raw_inode.dsize, fmc->max_chunk_size);
+ goto bad_inode;
+ }
+
+ pos += sizeof(struct jffs_raw_inode);
+
+ /* This shouldn't be necessary because a node that
+ violates the flash boundaries shouldn't be written
+ in the first place. */
+ if (pos >= end) {
+ goto check_node;
+ }
+
+ /* Read the name. */
+ *name = 0;
+ if (raw_inode.nsize) {
+ flash_safe_read(fmc->mtd, pos, name, raw_inode.nsize);
+ name[raw_inode.nsize] = '\0';
+ pos += raw_inode.nsize
+ + JFFS_GET_PAD_BYTES(raw_inode.nsize);
+ D3(printk("name == \"%s\"\n", name));
+ checksum = jffs_checksum(name, raw_inode.nsize);
+ if (checksum != raw_inode.nchksum) {
+ D1(printk("jffs_scan_flash(): Bad checksum: "
+ "checksum = %u, "
+ "raw_inode.nchksum = %u\n",
+ checksum, raw_inode.nchksum));
+ jffs_fmalloced(fmc, (__u32) start,
+ (__u32) (pos - start), NULL);
+ /* Reuse this unused struct jffs_node. */
+ continue;
+ }
+ if (pos >= end) {
+ goto check_node;
+ }
+ }
+
+ /* Read the data, if it exists, in order to be sure it
+ matches the checksum. */
+ if (raw_inode.dsize) {
+ if (raw_inode.rename) {
+ deleted_file = flash_read_u32(fmc->mtd, pos);
+ }
+ if (jffs_checksum_flash(fmc->mtd, pos, raw_inode.dsize, &checksum)) {
+ printk("jffs_checksum_flash() failed to calculate a checksum\n");
+ jffs_fmalloced(fmc, (__u32) start,
+ (__u32) (pos - start), NULL);
+ /* Reuse this unused struct jffs_node. */
+ continue;
+ }
+ pos += raw_inode.dsize
+ + JFFS_GET_PAD_BYTES(raw_inode.dsize);
+
+ if (checksum != raw_inode.dchksum) {
+ D1(printk("jffs_scan_flash(): Bad checksum: "
+ "checksum = %u, "
+ "raw_inode.dchksum = %u\n",
+ checksum, raw_inode.dchksum));
+ jffs_fmalloced(fmc, (__u32) start,
+ (__u32) (pos - start), NULL);
+ /* Reuse this unused struct jffs_node. */
+ continue;
+ }
+ }
+
+ check_node:
+
+ /* Remember the highest inode number in the whole file
+ system. This