diff options
Diffstat (limited to 'fs/jffs/intrep.c')
-rw-r--r-- | fs/jffs/intrep.c | 3457 |
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 |