diff -Narup Mtd-orig/fs/Kconfig mtd/fs/Kconfig --- Mtd-orig/fs/Kconfig 2006-02-03 15:50:38.000000000 +0100 +++ mtd/fs/Kconfig 2006-02-03 15:47:39.000000000 +0100 @@ -77,6 +77,44 @@ config JFFS2_SUMMARY If unsure, say 'N'. +config JFFS2_CS + bool "JFFS2 centralized summary support (EXPERIMENTAL)" + depends on JFFS2_FS && JFFS2_FS_WRITEBUFFER + default n + help + This feature makes it possible to use centralized summary information + for faster filesystem mount. It stores the JFFS2 memory representation + at umount time, so at mount it is enough to read it. + + There is no corresponding user-space tool, so the first mount + will happen using the orinal scanning method - or with + erase block summary. + + If unsure, say 'N'. + +choice + prompt "JFFS2 centralized summary storing mode" if JFFS2_CS + default JFFS2_CS_FEB + depends on JFFS2_CS + help + You can set the storing method of centralized summary. + Don't touch if unsure. + +config JFFS2_CS_FEB + bool "First Erase Block" + help + In this case centralized summary stores the place of the summary + information in the first usable (not bad) erase block. + +config JFFS2_CS_MP_SYSFS + bool "Mount parameter and SYSFS (EXPERIMENTAL)" + help + In this case centralized summary needs the offset as a mount + parameter (cs_offset=... ). The new offset can be read from SysFS + (/sys/module/jffs2/parameters/centsum_offset) after clean umount. + +endchoice + config JFFS2_COMPRESSION_OPTIONS bool "Advanced compression options for JFFS2" depends on JFFS2_FS diff -Narup Mtd-orig/fs/jffs2/Makefile.common mtd/fs/jffs2/Makefile.common --- Mtd-orig/fs/jffs2/Makefile.common 2006-02-03 15:51:19.000000000 +0100 +++ mtd/fs/jffs2/Makefile.common 2006-02-03 15:48:21.000000000 +0100 @@ -16,3 +16,4 @@ jffs2-$(CONFIG_JFFS2_RUBIN) += compr_rub jffs2-$(CONFIG_JFFS2_RTIME) += compr_rtime.o jffs2-$(CONFIG_JFFS2_ZLIB) += compr_zlib.o jffs2-$(CONFIG_JFFS2_SUMMARY) += summary.o +jffs2-$(CONFIG_JFFS2_CS) += cent_sum.o diff -Narup Mtd-orig/fs/jffs2/build.c mtd/fs/jffs2/build.c --- Mtd-orig/fs/jffs2/build.c 2006-02-03 15:51:19.000000000 +0100 +++ mtd/fs/jffs2/build.c 2006-02-03 15:48:22.000000000 +0100 @@ -169,6 +169,28 @@ static int jffs2_build_filesystem(struct ret = 0; + if (jffs2_cs_mode() == JFFS2_CS_MODE_FEB) { + if (c->cs_struct->csblock && !IS_CSBLOCK_VALID && !jffs2_is_readonly(c)) { + if (jffs2_cs_init_cs_block(c)) { + /* TODO: select a new block and retry */ + } + } + else if (c->cs_struct->csblock && !IS_CSBLOCK_VALID && jffs2_is_readonly(c) ) { + JFFS2_WARNING("cs_block is not valid and fs is in RO mode " + "=> disabling centsum !!!\n"); + c->cs_struct->csblock = NULL; + } + else if (c->cs_struct->csblock && IS_CSBLOCK_VALID) { + c->unchecked_size -= c->cs_struct->csblock->unchecked_size; + c->wasted_size -= c->cs_struct->csblock->wasted_size; + c->dirty_size -= c->cs_struct->csblock->dirty_size; + c->used_size -= c->cs_struct->csblock->used_size; + c->free_size -= c->cs_struct->csblock->free_size; + c->used_size += c->sector_size; + list_del(&c->cs_struct->csblock->list); + } + } + exit: if (ret) { for_each_inode(i, c, ic) { @@ -348,20 +370,56 @@ int jffs2_do_mount_fs(struct jffs2_sb_in if (ret) return ret; - if (jffs2_build_filesystem(c)) { - dbg_fsbuild("build_fs failed\n"); - jffs2_free_ino_caches(c); - jffs2_free_raw_node_refs(c); + if (jffs2_cs_active()) { + if (jffs2_cs_build_filesystem(c)) { + jffs2_free_ino_caches(c); + jffs2_free_raw_node_refs(c); + jffs2_cs_reset_sb_info(c); + + if (jffs2_build_filesystem(c)) { + dbg_fsbuild("build_fs failed\n"); + jffs2_free_ino_caches(c); + jffs2_free_raw_node_refs(c); #ifndef __ECOS - if (jffs2_blocks_use_vmalloc(c)) - vfree(c->blocks); - else + if (jffs2_blocks_use_vmalloc(c)) + vfree(c->blocks); + else #endif - kfree(c->blocks); + kfree(c->blocks); - return -EIO; - } + return -EIO; + } + /* write mount log if fs isn't readonly */ + } else if (!jffs2_is_readonly(c)) { + dbg_cs("Writing mount log (end of mount)\n"); + + if (jffs2_cs_write_mount_log(c)) { + JFFS2_WARNING("write_mount_log returned with error!\n"); + + c->cs_struct->csblock = NULL; + c->cs_struct->cs_flags &= ~JFFS2_CS_CSBLOCK_VALID; // flag valid = 0 + c->cs_struct->csblock_free = 0; + + return -EIO; + } + } + } else { + if (jffs2_build_filesystem(c)) { + dbg_fsbuild("build_fs failed\n"); + jffs2_free_ino_caches(c); + jffs2_free_raw_node_refs(c); +#ifndef __ECOS + if (jffs2_blocks_use_vmalloc(c)) + vfree(c->blocks); + else +#endif + kfree(c->blocks); + + return -EIO; + } + } + jffs2_calc_trigger_levels(c); return 0; diff -Narup Mtd-orig/fs/jffs2/cent_sum.c mtd/fs/jffs2/cent_sum.c --- Mtd-orig/fs/jffs2/cent_sum.c 1970-01-01 01:00:00.000000000 +0100 +++ mtd/fs/jffs2/cent_sum.c 2006-02-03 16:55:52.000000000 +0100 @@ -0,0 +1,2412 @@ +/* + * JFFS2 -- Journalling Flash File System, Version 2. + * + * Copyright (C) 2005 Zoltan Sogor , + * Ferenc Havasi , + * Patrik Kluba , + * University of Szeged, Hungary + * + * Thanks for Peter Grayson + * + * For licensing information, see the file 'LICENCE' in this directory. + * + * $Id$ + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "nodelist.h" + +//#define D1(x) x +//#define D2(x) x + +struct erase_priv_struct { + struct jffs2_eraseblock *jeb; + struct jffs2_sb_info *c; +}; + +static int jffs2_cs_erase_block(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb); +static int jffs2_cs_mark_erased_block(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb); +static int jffs2_cs_write_inocache(struct jffs2_sb_info *c, struct jffs2_cs_inocache *buff, + uint32_t cachelen, uint32_t count, uint32_t offset); +static int jffs2_cs_write_jebinfo(struct jffs2_sb_info *c, struct jffs2_cs_jeb *buff, + uint32_t jebinfolen, uint32_t num, uint32_t offset); +static int jffs2_cs_write_rawref(struct jffs2_sb_info *c, struct jffs2_cs_rawref *buff, + uint32_t rawreflen, uint32_t num, uint32_t offset); +static int jffs2_cs_write_lists(struct jffs2_sb_info *c, jint16_t *buff, + uint32_t listslen, uint32_t offset); +static int jffs2_cs_add_offset(struct jffs2_sb_info *c, uint32_t offset, uint32_t length); +static void jffs2_cs_clean_offset_list(struct jffs2_sb_info *c); +static int jffs2_cs_read_log_jeb(struct jffs2_sb_info *c); +static int jffs2_cs_process_log(struct jffs2_sb_info *c, struct jffs2_raw_cs_log *log); +static int jffs2_cs_process_node(struct jffs2_sb_info *c, union jffs2_node_union *node, uint32_t offset); +static int jffs2_cs_process_inocache_node(struct jffs2_sb_info *c, struct jffs2_raw_cs_inocache *node); +static int jffs2_cs_process_jebinfo_node(struct jffs2_sb_info *c, struct jffs2_raw_cs_jebinfo *node); +static int jffs2_cs_process_rawref_node(struct jffs2_sb_info *c, struct jffs2_raw_cs_rawref *node); +static int jffs2_cs_process_lists_node(struct jffs2_sb_info *c, struct jffs2_raw_cs_lists *node); +static int jffs2_cs_correct_jebinfo(struct jffs2_sb_info *c); +static void *jffs2_cs_alloc_flashbuf(struct jffs2_sb_info *c); +static void jffs2_cs_free_flashbuf(struct jffs2_sb_info *c, void *flashbuf); + +#ifdef CONFIG_JFFS2_CS_FEB +static int jffs2_cs_write_umount_log_jeb(struct jffs2_sb_info *c); +#endif + +#ifdef CONFIG_JFFS2_CS_MP_SYSFS +static int jffs2_cs_parse_mount_params(struct jffs2_sb_info *c, char *options); +static int jffs2_cs_write_umount_log_mntsys(struct jffs2_sb_info *c); +#endif + +void jffs2_free_all_node_refs(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb); + +// move all data from jeb to nextblock, and erase jeb +int jffs2_cs_move_block(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb) +{ + int ret; + + if (c->gcblock) { + dbg_cs("error GC is in use!\n"); + return -1; + } + + c->gcblock = jeb; + list_del(&c->gcblock->list); + + c->gcblock->gc_node = c->gcblock->first_node; + + /* Have we accidentally picked a clean block with wasted space ? */ + if (c->gcblock->wasted_size) { + dbg_cs("Converting wasted_size %08x to dirty_size\n",c->gcblock->wasted_size); + c->gcblock->dirty_size += c->gcblock->wasted_size; + c->wasted_size -= c->gcblock->wasted_size; + c->dirty_size += c->gcblock->wasted_size; + c->gcblock->wasted_size = 0; + } + + if (c->gcblock->gc_node) { + + while(c->gcblock) { + ret = jffs2_garbage_collect_pass(c); + if (ret) { + printk("%s: Error occured during gc!\n", __FUNCTION__); + /* TODO ERROR HANDLING*/ + return -1; + } + } + } else { + JFFS2_WARNING("Eep. c->gcblock->gc_node for block at 0x%08x is NULL\n", c->gcblock->offset); + } + + c->erasing_size += c->sector_size; + c->wasted_size -= jeb->wasted_size; + c->free_size -= jeb->free_size; + c->used_size -= jeb->used_size; + c->dirty_size -= jeb->dirty_size; + jeb->wasted_size = jeb->used_size = jeb->dirty_size = jeb->free_size = 0; + jffs2_free_all_node_refs(c, jeb); + + ret = jffs2_cs_erase_block(c, jeb); + + if (ret) { + c->erasing_size -= c->sector_size; + c->bad_size += c->sector_size; + list_add(&jeb->list, &c->bad_list); + + return -1; + } + + c->erasing_size -= c->sector_size; + + if (jffs2_cs_mark_erased_block(c, jeb)) { + JFFS2_ERROR("Error during cs_mark_erase_block\n"); + // TODO : we should correct sb size info !!! + // maybe : + c->bad_size += c->sector_size; + list_add(&jeb->list, &c->bad_list); + + return -1; + } + + // adding cenztralized summary log block size as used_space + c->used_size += c->sector_size; + + // for debugging + //D1(jffs2_dump_block_lists(c)); + + return 0; +} + + +// init centralized summary structures +int jffs2_cs_init(struct jffs2_sb_info *c, void *data) +{ + + c->cs_struct = kmalloc(sizeof(struct cent_sum), GFP_KERNEL); + if(!c->cs_struct) + return -ENOMEM; + + c->cs_struct->csblock = NULL; + c->cs_struct->csblock_free = 0; + c->cs_struct->cs_log_offset = 0; + c->cs_struct->cs_flags &= ~JFFS2_CS_CSBLOCK_VALID; // flag valid = 0 + c->cs_struct->cs_flags &= ~JFFS2_CS_DURING_MOUNT; // flag during_mount= 0 + c->cs_struct->cs_flags &= ~JFFS2_CS_DURING_UNMOUNT; // flag during_unmount= 0 + c->cs_struct->cs_flags &= ~JFFS2_CS_MOUNT_OFFSET_PARAM; // flag mount_offset_param = 0 + + c->cs_struct->offset_list_tail = NULL; + c->cs_struct->offset_list_head = NULL; + c->cs_struct->offset_list_num = 0; + + if (jffs2_cs_mode() == JFFS2_CS_MODE_MP_SYSFS) { + if(!jffs2_cs_parse_mount_params(c, data)) { + JFFS2_ERROR("Error at mount options parsing!\n"); + return -EINVAL; + } + } + + return 0; +} + +// init centralized summary log block +int jffs2_cs_init_cs_block_jeb(struct jffs2_sb_info *c) +{ + int ret; + + c->cs_struct->cs_flags |= JFFS2_CS_DURING_MOUNT; // flag during_mount = 1 + + ret = jffs2_cs_move_block(c, c->cs_struct->csblock); + + if (!ret) { + c->cs_struct->csblock_free = c->sector_size; + c->cs_struct->cs_flags |= JFFS2_CS_CSBLOCK_VALID; // flag valid = 1; + c->cs_struct->cs_flags &= ~JFFS2_CS_DURING_MOUNT; // flag during_mount = 0 + + if (jffs2_cs_write_mount_log(c)) { + dbg_cs("%s: write_mount_log returned with error\n"); + + // disable cent_sum + c->cs_struct->csblock = NULL; + c->cs_struct->cs_flags &= ~JFFS2_CS_CSBLOCK_VALID; // flag valid = 0 + c->cs_struct->csblock_free = 0; + + return -1; + } + + return 0; + } + + dbg_cs("Can't empty erase block for centralized summary log\n"); + + c->cs_struct->csblock = NULL; + c->cs_struct->cs_flags &= ~JFFS2_CS_CSBLOCK_VALID; // flag valid = 0 + c->cs_struct->cs_flags &= ~JFFS2_CS_DURING_MOUNT; // flag during_mount = 0 + + return -1; +} + + +// erase the jeb +static int jffs2_cs_erase_block(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb) +{ + int ret; + uint32_t bad_offset; + struct erase_info *instr; + + dbg_cs("erase block %#x (range %#x-%#x)\n", jeb->offset, jeb->offset, + jeb->offset + c->sector_size); + instr = kmalloc(sizeof(struct erase_info) + sizeof(struct erase_priv_struct), GFP_KERNEL); + if (!instr) { + JFFS2_ERROR("Can't allocate memory for erase_info!\n"); + return -ENOMEM; + } + + memset(instr, 0, sizeof(*instr)); + + instr->mtd = c->mtd; + instr->addr = jeb->offset; + instr->len = c->sector_size; + instr->callback = NULL; + instr->priv = (unsigned long)(&instr[1]); + instr->fail_addr = 0xffffffff; + + ((struct erase_priv_struct *)instr->priv)->jeb = jeb; + ((struct erase_priv_struct *)instr->priv)->c = c; + + ret = c->mtd->erase(c->mtd, instr); + + if (!ret) { + c->cs_struct->csblock_free = c->sector_size; + return 0; + } + + bad_offset = instr->fail_addr; + kfree(instr); + + if (!c->mtd->block_markbad) + return 1; // What else can we do? + + dbg_cs("Marking bad block at %08x\n", bad_offset); + ret = c->mtd->block_markbad(c->mtd, bad_offset); + + if (ret) { + dbg_cs("Write failed for block at %08x: error %d\n", jeb->offset, ret); + return ret; + } + + return 1; +} + + +// mark block +static int jffs2_cs_mark_erased_block(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb) +{ + unsigned char *ebuf; + size_t retlen; + int ret; + uint32_t bad_offset; + + ebuf = kmalloc(PAGE_SIZE, GFP_KERNEL); + if (!ebuf) { + JFFS2_WARNING ("Failed to allocate page buffer for verifying " + "erase at 0x%08x. Assuming it worked\n", jeb->offset); + } else { + uint32_t ofs = jeb->offset; + + dbg_cs("Verifying erase at 0x%08x\n", jeb->offset); + while(ofs < jeb->offset + c->sector_size) { + uint32_t readlen = min((uint32_t)PAGE_SIZE, jeb->offset + c->sector_size - ofs); + int i; + + bad_offset = ofs; + + ret = jffs2_flash_read(c, ofs, readlen, &retlen, ebuf); + if (ret) { + JFFS2_WARNING("Read of newly-erased block at 0x%08x " + "failed: %d. Putting on bad_list\n", ofs, ret); + goto bad; + } + if (retlen != readlen) { + JFFS2_WARNING("Short read from newly-erased block at" + "0x%08x. Wanted %d, got %zd\n", ofs, readlen, retlen); + goto bad; + } + for (i=0; imtd->block_markbad) + return 1; // What else can we do? + + dbg_cs("Marking bad block at %08x\n", bad_offset); + ret = c->mtd->block_markbad(c->mtd, bad_offset); + + if (ret) { + dbg_cs("Write failed for block at " + "%08x: error %d\n", jeb->offset, ret); + return ret; + } + + return -1; + } + } + ofs += readlen; + } + kfree(ebuf); + } + + bad_offset = jeb->offset; + + /* Write the erase complete marker */ + dbg_cs("Writing erased marker to block at 0x%08x\n", jeb->offset); +//#warning "write_nand_clnmkr -> write_nand_ebh" + if (jffs2_write_nand_ebh(c, jeb)) + goto bad2; + + jeb->first_node = jeb->last_node = NULL; + + jeb->free_size = c->sector_size; + jeb->used_size = 0; + jeb->dirty_size = 0; + jeb->wasted_size = 0; + + return 0; +} + + +// free cs_struct->offset_list +void jffs2_cs_free_offset_list(struct jffs2_sb_info *c) +{ + struct cent_sum_offset *temp; + + while (c->cs_struct->offset_list_head != NULL) { + temp = c->cs_struct->offset_list_head; + c->cs_struct->offset_list_head = temp->next; + kfree(temp); + c->cs_struct->offset_list_num--; + } + + c->cs_struct->offset_list_tail = NULL; + + if (c->cs_struct->offset_list_num) { + dbg_cs("List is empty, but list_num != 0\n"); + BUG(); + } +} + +// free cs_struct +void jffs2_cs_free(struct jffs2_sb_info *c) +{ + if (c->cs_struct->offset_list_head != NULL) { + jffs2_cs_free_offset_list(c); + } + + if (c->cs_struct) + kfree(c->cs_struct); + +} + +// free c->lists (clean, free, dirty, ...) +void jffs2_cs_free_lists(struct jffs2_sb_info *c) +{ + int i; + + for(i=0; inr_blocks; i++) { + list_del_init(&c->blocks[i].list); + } + +} + +static inline struct jffs2_inode_cache * +first_inode_chain(int *i, struct jffs2_sb_info *c) +{ + for (; *i < 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))) + +// count list +static int count_list(struct list_head *l) +{ + uint32_t count = 0; + struct list_head *tmp; + + list_for_each(tmp, l) { + count++; + } + return count; +} + +// count the number of rawref entries +static unsigned int jffs2_cs_count_rawrefs(struct jffs2_sb_info *c) +{ + int i; + unsigned int count = 0; + struct jffs2_inode_cache *ic; + struct jffs2_raw_node_ref *raw; + + for_each_inode(i, c, ic) { + raw = ic->nodes; + while (raw) { + raw = raw->next_in_ino; + count++; + } + } + + return count; +} + +struct jffs2_cent_sum_tmp_raws { + struct jffs2_raw_node_ref *raw; + struct jffs2_raw_node_ref *nii; +}; + +// save rawrefs next_in_ino field +int jffs2_cs_save_rawrefs_nii(struct jffs2_sb_info *c) +{ + unsigned int size, count; + int i; + void *ptr; + struct jffs2_inode_cache *ic; + struct jffs2_raw_node_ref *raw; + struct jffs2_cent_sum_tmp_raws *rn; + + count = jffs2_cs_count_rawrefs(c); + size = sizeof(count) + count * 2 * sizeof(void *); + + c->cs_struct->raw_nii = vmalloc(size); + if (!c->cs_struct->raw_nii) { + JFFS2_WARNING("can't allocate memory!"); + return -ENOMEM; + } + + ptr = c->cs_struct->raw_nii; + + *((int *) ptr) = count; + ptr += sizeof(count); + rn = ptr; + + for_each_inode(i, c, ic) { + raw = ic->nodes; + while (raw) { + rn->raw = raw; + rn->nii = raw->next_in_ino; + rn++; + raw = raw->next_in_ino; + } + } + + return 0; +} + +// restore rawrefs next_in_ino field +int jffs2_cs_restore_rawrefs_nii(struct jffs2_sb_info *c) +{ + unsigned int i, count; + struct jffs2_cent_sum_tmp_raws *rn; + + count = *((int *)c->cs_struct->raw_nii); + rn = c->cs_struct->raw_nii + sizeof(count); + + for (i=0; iraw->next_in_ino = rn->nii; + rn++; + } + + vfree(c->cs_struct->raw_nii); + + return 0; +} + +// dump ino_cache data +static int jffs2_cs_dump_ic(struct jffs2_sb_info *c) +{ + int i, ret; + unsigned int minsize, count = 0; + uint32_t offset, cachesize = 0, avail = 0; + struct jffs2_inode_cache *ic; + struct jffs2_raw_node_ref *raw, *temp; + struct jffs2_cs_inocache *buff = NULL, *ptr; + + minsize = sizeof(struct jffs2_raw_cs_inocache) + JFFS2_MIN_INOCACHE_NUM * JFFS2_INOCACHE_ENTRY_SIZE; + + dbg_cs2("Start cs_dump_inocache\n"); + + ret = jffs2_reserve_space(c, minsize, &offset, &avail, ALLOC_CENT_SUM, 0); + if (ret) { + dbg_cs("reserve_space returned: %d\n", ret); + return ret; + } + + buff = kmalloc(CENT_SUM_BUFFER_SIZE, GFP_KERNEL); + + if (!buff) { + JFFS2_ERROR("Can't allocate memory for cent_cache!!!\n"); + return -ENOMEM; + } + + memset(buff, 0, CENT_SUM_BUFFER_SIZE); + + ptr = buff; + + for_each_inode(i, c, ic) { + + if ((cachesize + JFFS2_INOCACHE_ENTRY_SIZE + sizeof(struct jffs2_raw_cs_inocache) > avail) || + (cachesize + JFFS2_INOCACHE_ENTRY_SIZE + sizeof(struct jffs2_raw_cs_inocache) > CENT_SUM_BUFFER_SIZE)) { + + ret = jffs2_cs_write_inocache(c, buff, cachesize, count, offset); + + up(&c->alloc_sem); // locked by jffs2_reserve_space + + if (ret) { // error during write + dbg_cs("Error during write!\n"); + kfree(buff); + return ret; + } + + // alloc for next inocache node + ret = jffs2_reserve_space(c, minsize, &offset, &avail, ALLOC_CENT_SUM, 0); + if (ret) { + dbg_cs("reserve_space returned: %d\n", ret); + kfree(buff); + return ret; + } + + ptr = buff; + cachesize = 0; + count = 0; // count = cachesize / JFFS2_INOCACHE_ENTRY_SIZE + } + + dbg_cs2("Dumping inocache %d \n", ic->ino); + + ptr->ino = cpu_to_je32(ic->ino); + ptr->nlink = cpu_to_je32(ic->nlink); + + if (ic->state == INO_STATE_UNCHECKED) { + ptr->state = cpu_to_je32(ic->state); + } else { + ptr->state = cpu_to_je32(INO_STATE_CHECKEDABSENT); + } + + raw = ic->nodes; + while (raw) { + temp = raw->next_in_ino; + raw->next_in_ino = (void *)ic->ino; + raw = temp; + } + + cachesize += JFFS2_INOCACHE_ENTRY_SIZE; + count++; + ptr++; + } + + if (cachesize != 0) { + + ret = jffs2_cs_write_inocache(c, buff, cachesize, count, offset); + + up(&c->alloc_sem); // locked by jffs2_reserve_space + + if (ret) { // error during write + dbg_cs("Error during write!\n"); + kfree(buff); + return ret; + } + } else { + dbg_cs2("empty buffer -> up alloc_sem\n"); + up(&c->alloc_sem); // locked by jffs2_reserve_space + } + + kfree(buff); + dbg_cs2("End cs_dump_inocache\n"); + + return 0; +} + +// dump jebinfo +static int jffs2_cs_dump_jebs(struct jffs2_sb_info *c) +{ + int i, ret; + unsigned int minsize, count = 0; + uint32_t offset, jebinfosize = 0, avail = 0; + struct jffs2_cs_jeb *buff = NULL, *ptr; + + minsize = sizeof(struct jffs2_raw_cs_jebinfo) + JFFS2_MIN_JEBINFO_NUM * JFFS2_JEBINFO_ENTRY_SIZE; + + dbg_cs2("Start cs_dump_jebinfo\n"); + + ret = jffs2_reserve_space(c, minsize, &offset, &avail, ALLOC_CENT_SUM, 0); + if (ret) { + dbg_cs("reserve_space returned: %d\n", ret); + return ret; + } + + buff = kmalloc(CENT_SUM_BUFFER_SIZE, GFP_KERNEL); + + if (!buff) { + JFFS2_ERROR("Can't allocate memory for cent_jebinfo!!!\n"); + return -ENOMEM; + } + + memset(buff, 0, CENT_SUM_BUFFER_SIZE); + + ptr = buff; + + for(i=0; inr_blocks; i++) { + + if (&c->blocks[i] == c->cs_struct->csblock) + continue; + + if ((jebinfosize + JFFS2_JEBINFO_ENTRY_SIZE + sizeof(struct jffs2_raw_cs_jebinfo) > avail) || + (jebinfosize + JFFS2_JEBINFO_ENTRY_SIZE + sizeof(struct jffs2_raw_cs_jebinfo) > CENT_SUM_BUFFER_SIZE)) { + + ret = jffs2_cs_write_jebinfo(c, buff, jebinfosize, count, offset); + + up(&c->alloc_sem); // locked by jffs2_reserve_space + + if (ret) { // error during write + dbg_cs("Error during write!\n"); + kfree(buff); + return ret; + } + + // alloc for next inocache node + ret = jffs2_reserve_space(c, minsize, &offset, &avail, ALLOC_CENT_SUM, 0); + if (ret) { + dbg_cs("reserve_space returned: %d\n", ret); + kfree(buff); + return ret; + } + + ptr = buff; + jebinfosize = 0; + count = 0; // count = jebinfosize / JFFS2_JEBINFO_ENTRY_SIZE + } + + ptr->offset = cpu_to_je32(c->blocks[i].offset); + ptr->unchecked_size = cpu_to_je32(c->blocks[i].unchecked_size); + ptr->used_size = cpu_to_je32(c->blocks[i].used_size); + ptr->dirty_size = cpu_to_je32(c->blocks[i].dirty_size); + ptr->wasted_size = cpu_to_je32(c->blocks[i].wasted_size); + + jebinfosize += JFFS2_JEBINFO_ENTRY_SIZE; + count++; + ptr++; + + } + + if (jebinfosize != 0) { + + ret = jffs2_cs_write_jebinfo(c, buff, jebinfosize, count, offset); + + up(&c->alloc_sem); // locked by jffs2_reserve_space + + if (ret) { // error during write + dbg_cs("Error during write!\n"); + kfree(buff); + return ret; + } + } + else { + dbg_cs2("empty buffer -> up alloc_sem\n"); + up(&c->alloc_sem); // locked by jffs2_reserve_space + } + + kfree(buff); + dbg_cs2("End cs_dump_jebinfo\n"); + + return 0; +} + +// dump raw_node_ref information +static int jffs2_cs_dump_rawrefs(struct jffs2_sb_info *c) +{ + int i, ret; + unsigned int minsize, count = 0; + uint32_t offset, rawrefssize = 0, avail = 0; + struct jffs2_cs_rawref *buff = NULL, *ptr; + struct jffs2_raw_node_ref *walk = NULL, *temp_raw = NULL; + + minsize = sizeof(struct jffs2_raw_cs_rawref) + JFFS2_MIN_RAWREF_NUM * JFFS2_RAWREF_ENTRY_SIZE; + + dbg_cs2("Start cs_dump_rawrefs\n"); + + ret = jffs2_reserve_space(c, minsize, &offset, &avail, ALLOC_CENT_SUM, 0); + if (ret) { + dbg_cs("jffs2_reserve_space returned: %d\n" ret); + return ret; + } + + buff = kmalloc(CENT_SUM_BUFFER_SIZE, GFP_KERNEL); + + if (!buff) { + JFFS2_ERROR("Can't allocate memory for cent_rawref!\n"); + return -ENOMEM; + } + + memset(buff, 0, CENT_SUM_BUFFER_SIZE); + + ptr = buff; + + for(i=0; inr_blocks; i++) { + + walk = c->blocks[i].first_node; + + while (walk) { + + if ((rawrefssize + JFFS2_RAWREF_ENTRY_SIZE + sizeof(struct jffs2_raw_cs_rawref) > avail) || + (rawrefssize + JFFS2_RAWREF_ENTRY_SIZE + sizeof(struct jffs2_raw_cs_rawref) > CENT_SUM_BUFFER_SIZE)) { + + ret = jffs2_cs_write_rawref(c, buff, rawrefssize, count, offset); + + up(&c->alloc_sem); // locked by jffs2_reserve_space + + if (ret) { // error during write + dbg_cs("Error during write\n"); + kfree(buff); + return ret; + } + + // alloc for next inocache node + ret = jffs2_reserve_space(c, minsize, &offset, &avail, ALLOC_CENT_SUM, 0); + if (ret) { + dbg_cs("reserve_space returned: %d\n", ret); + kfree(buff); + return ret; + } + + ptr = buff; + rawrefssize = 0; + count = 0; // count = rawrefssize / 20 + } + + temp_raw = walk; + walk = walk->next_phys; + + ptr->flash_offset = cpu_to_je32(temp_raw->flash_offset); + ptr->totlen = cpu_to_je32(temp_raw->__totlen); + ptr->ino = cpu_to_je32(((unsigned int) temp_raw->next_in_ino)); + + rawrefssize += JFFS2_RAWREF_ENTRY_SIZE; + count++; + ptr++; + } + + } + + if (rawrefssize != 0) { + + ret = jffs2_cs_write_rawref(c, buff, rawrefssize, count, offset); + + up(&c->alloc_sem); // locked by jffs2_reserve_space + + if (ret) { // error during write + dbg_cs("Error during write!\n"); + kfree(buff); + return ret; + } + } else { + dbg_cs2("empty buffer -> up alloc_sem\n"); + up(&c->alloc_sem); // locked by jffs2_reserve_space + } + + kfree(buff); + dbg_cs2("End cs_dump_rawrefs\n"); + + return 0; +} + +// dump list +static void jffs2_cs_dump_list(struct jffs2_sb_info *c, jint16_t **ptr, struct list_head *l, int count) +{ + struct list_head *tmp; + **ptr = cpu_to_je16(count); + (*ptr)++; + + if (count) { + list_for_each(tmp, l) { + struct jffs2_eraseblock *jeb = list_entry(tmp, struct jffs2_eraseblock, list); + **ptr = cpu_to_je16(jeb->offset / c->sector_size); + (*ptr)++; + } + } +} + +// dump lists (free, clean , dirty, ...) +static int jffs2_cs_dump_lists(struct jffs2_sb_info *c) +{ + int ret, buff_size, nodesize; + uint32_t offset, avail; + int count[7]; + jint16_t *ptr = NULL, *buff = NULL; + + dbg_cs2("Start cs_dump_lists\n"); + + buff_size = PAD((7 + c->nr_blocks) * 2); + nodesize = sizeof(struct jffs2_raw_cs_lists) + buff_size; + + // TODO + + ret = jffs2_reserve_space(c, nodesize, &offset, &avail, ALLOC_CENT_SUM, 0); + if (ret) { + dbg_cs("reserve_space returned: %d\n", ret); + return ret; + } + + count[0] = count_list(&c->clean_list); + count[1] = count_list(&c->very_dirty_list); + count[2] = count_list(&c->dirty_list); + count[3] = count_list(&c->erasable_list); + count[4] = count_list(&c->free_list); + count[5] = count_list(&c->bad_list); + count[6] = count_list(&c->bad_used_list); + + buff = kmalloc(buff_size, GFP_KERNEL); + if (!buff) { + JFFS2_ERROR("Can't allocate memory for cent_lists!!!\n"); + return -ENOMEM; + } + + memset(buff, 0, buff_size); + + ptr = buff; + + jffs2_cs_dump_list(c, &ptr, &c->clean_list, count[0]); + jffs2_cs_dump_list(c, &ptr, &c->very_dirty_list, count[1]); + jffs2_cs_dump_list(c, &ptr, &c->dirty_list, count[2]); + jffs2_cs_dump_list(c, &ptr, &c->erasable_list, count[3]); + jffs2_cs_dump_list(c, &ptr, &c->free_list, count[4]); + jffs2_cs_dump_list(c, &ptr, &c->bad_list, count[5]); + jffs2_cs_dump_list(c, &ptr, &c->bad_used_list, count[6]); + + ret = jffs2_cs_write_lists(c, buff, nodesize, offset); + + up(&c->alloc_sem); // locked by jffs2_reserve_space + + if (ret) { // error during write + dbg_cs("Error during write\n"); + kfree(buff); + return ret; + } + + kfree(buff); + dbg_cs2("End cs_dump_lists\n"); + + return 0; +} + + +// dump centralized summary data +void jffs2_cs_dump_centsum_data(struct jffs2_sb_info *c) +{ + dbg_cs2("Dumping centsum data\n"); + + c->cs_struct->cs_flags |= JFFS2_CS_DURING_UNMOUNT; // flag during_umount = 1 + + if (jffs2_cs_dump_ic(c)) { + goto out; + } + + if (jffs2_cs_dump_jebs(c)) { + goto out; + } + + if (jffs2_cs_dump_rawrefs(c)) { + goto out; + } + + if (jffs2_cs_dump_lists(c)) { + goto out; + } + + if (jffs2_cs_write_umount_log(c)) { + // TODO find new cs_block + } + +out: + c->cs_struct->cs_flags &= ~JFFS2_CS_DURING_UNMOUNT; // flag during_umount = 0 +} + +// write inocache node to flash +static int jffs2_cs_write_inocache(struct jffs2_sb_info *c, struct jffs2_cs_inocache *buff, + uint32_t cachelen, uint32_t count, uint32_t offset) +{ + int ret, infosize; + size_t retlen; + struct kvec vecs[2]; + struct jffs2_raw_cs_inocache cent_cache; + struct jffs2_eraseblock *jeb; + + dbg_cs("Write inocache node to 0x%x\n", offset); + + infosize = PAD(sizeof(cent_cache) + cachelen); + + cent_cache.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); + cent_cache.nodetype = cpu_to_je16(JFFS2_NODETYPE_CENT_SUM_INOCACHE); + cent_cache.totlen = cpu_to_je32(infosize); + cent_cache.hdr_crc = cpu_to_je32(crc32(0, ¢_cache, sizeof(struct jffs2_unknown_node) - 4)); + cent_cache.cache_num = cpu_to_je32(count); + cent_cache.cache_crc = cpu_to_je32(crc32(0, buff, cachelen)); + cent_cache.node_crc = cpu_to_je32(crc32(0, ¢_cache, sizeof(cent_cache) - 8)); + + vecs[0].iov_base = ¢_cache; + vecs[0].iov_len = sizeof(cent_cache); + vecs[1].iov_base = buff; + vecs[1].iov_len = cachelen; + + jeb = &c->blocks[offset / c->sector_size]; + + ret = jffs2_flash_writev(c, vecs, 2, offset, &retlen, 0); + + if (ret || (retlen != infosize)) { + JFFS2_WARNING("write of %zd bytes at 0x%08x failed. returned %d, retlen %zd\n", + infosize, offset, ret, retlen); + + WASTED_SPACE(infosize); + return -EIO; + } + + DECREASE_FREE_SPACE(infosize); + + ret = jffs2_cs_add_offset(c, offset, infosize); + if (ret) + return ret; + + D1(printk("%s: End write\n", __FUNCTION__)); + + return 0; +} + +// write jebinfo node to flash +static int jffs2_cs_write_jebinfo(struct jffs2_sb_info *c, struct jffs2_cs_jeb *buff, + uint32_t jebinfolen, uint32_t num, uint32_t offset) +{ + int ret, infosize; + size_t retlen; + struct kvec vecs[2]; + struct jffs2_raw_cs_jebinfo cent_jebinfo; + struct jffs2_eraseblock *jeb; + + dbg_cs("Write jebinfo node to 0x%x\n", offset); + + infosize = PAD(sizeof(cent_jebinfo) + jebinfolen); + + cent_jebinfo.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); + cent_jebinfo.nodetype = cpu_to_je16(JFFS2_NODETYPE_CENT_SUM_JEBINFO); + cent_jebinfo.totlen = cpu_to_je32(infosize); + cent_jebinfo.hdr_crc = cpu_to_je32(crc32(0, ¢_jebinfo, sizeof(struct jffs2_unknown_node) - 4)); + cent_jebinfo.jebinfo_num = cpu_to_je32(num); + cent_jebinfo.jebinfo_crc = cpu_to_je32(crc32(0, buff, jebinfolen)); + cent_jebinfo.node_crc = cpu_to_je32(crc32(0, ¢_jebinfo, sizeof(cent_jebinfo) - 8)); + + vecs[0].iov_base = ¢_jebinfo; + vecs[0].iov_len = sizeof(cent_jebinfo); + vecs[1].iov_base = buff; + vecs[1].iov_len = jebinfolen; + + jeb = &c->blocks[offset / c->sector_size]; + + ret = jffs2_flash_writev(c, vecs, 2, offset, &retlen, 0); + + if (ret || (retlen != infosize)) { + JFFS2_WARNING("write of %zd bytes at 0x%08x failed. returned %d, retlen %zd\n", + infosize, offset, ret, retlen); + + WASTED_SPACE(infosize); + return -EIO; + } + + DECREASE_FREE_SPACE(infosize); + + ret = jffs2_cs_add_offset(c, offset, infosize); + if (ret) + return ret; + + dbg_cs("End write\n"); + + return 0; +} + +// write rawref node to flash +static int jffs2_cs_write_rawref(struct jffs2_sb_info *c, struct jffs2_cs_rawref *buff, + uint32_t rawreflen, uint32_t num, uint32_t offset) +{ + int ret, infosize; + size_t retlen; + struct kvec vecs[2]; + struct jffs2_raw_cs_rawref cent_rawref; + struct jffs2_eraseblock *jeb; + + dbg_cs("Write rawref node to 0x%x\n", offset); + + infosize = PAD(sizeof(cent_rawref) + rawreflen); + + cent_rawref.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); + cent_rawref.nodetype = cpu_to_je16(JFFS2_NODETYPE_CENT_SUM_RAWREF); + cent_rawref.totlen = cpu_to_je32(infosize); + cent_rawref.hdr_crc = cpu_to_je32(crc32(0, ¢_rawref, sizeof(struct jffs2_unknown_node) - 4)); + cent_rawref.rawref_num = cpu_to_je32(num); + cent_rawref.rawref_crc = cpu_to_je32(crc32(0, buff, rawreflen)); + cent_rawref.node_crc = cpu_to_je32(crc32(0, ¢_rawref, sizeof(cent_rawref) - 8)); + + vecs[0].iov_base = ¢_rawref; + vecs[0].iov_len = sizeof(cent_rawref); + vecs[1].iov_base = buff; + vecs[1].iov_len = rawreflen; + + jeb = &c->blocks[offset / c->sector_size]; + + ret = jffs2_flash_writev(c, vecs, 2, offset, &retlen, 0); + + if (ret || (retlen != infosize)) { + JFFS2_WARNING("write of %zd bytes at 0x%08x failed. returned %d, retlen %zd\n", + infosize, offset, ret, retlen); + + WASTED_SPACE(infosize); + return -EIO; + } + + DECREASE_FREE_SPACE(infosize); + + ret = jffs2_cs_add_offset(c, offset, infosize); + if (ret) + return ret; + + dbg_cs("End write\n"); + + return 0; +} + +// write lists node to flash +static int jffs2_cs_write_lists(struct jffs2_sb_info *c, jint16_t *buff, + uint32_t listslen, uint32_t offset) +{ + int ret, infosize; + size_t retlen; + struct kvec vecs[2]; + struct jffs2_raw_cs_lists cent_lists; + struct jffs2_eraseblock *jeb; + + dbg_cs("Write lists node to 0x%x\n", offset); + + infosize = PAD(sizeof(cent_lists) + listslen); + + cent_lists.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); + cent_lists.nodetype = cpu_to_je16(JFFS2_NODETYPE_CENT_SUM_LISTS); + cent_lists.totlen = cpu_to_je32(infosize); + cent_lists.hdr_crc = cpu_to_je32(crc32(0, ¢_lists, sizeof(struct jffs2_unknown_node) - 4)); + cent_lists.lists_crc = cpu_to_je32(crc32(0, buff, listslen)); + + vecs[0].iov_base = ¢_lists; + vecs[0].iov_len = sizeof(cent_lists); + vecs[1].iov_base = buff; + vecs[1].iov_len = listslen; + + jeb = &c->blocks[offset / c->sector_size]; + + ret = jffs2_flash_writev(c, vecs, 2, offset, &retlen, 0); + + if (ret || (retlen != infosize)) { + JFFS2_WARNING("write of %zd bytes at 0x%08x failed. returned %d, retlen %zd\n", + infosize, offset, ret, retlen); + + WASTED_SPACE(infosize); + return -EIO; + } + + DECREASE_FREE_SPACE(infosize); + + ret = jffs2_cs_add_offset(c, offset, infosize); + if (ret) + return ret; + + dbg_cs("%s: End write\n"); + + return 0; +} + +// write mount log entry to flash +int jffs2_cs_write_mount_log_jeb(struct jffs2_sb_info *c) +{ + struct jffs2_raw_cs_log cent_log; + struct kvec vecs[1]; + int ret; + size_t retlen; + uint32_t offset; + +retry: + if (c->cs_struct->csblock_free < PAD(sizeof(cent_log))) { + dbg_cs("There is not enough free space for log -> erase it\n"); + // erase_cs_block + if (jffs2_cs_erase_block(c, c->cs_struct->csblock)) { + JFFS2_WARNING("Can't erase cent_sum log block\n"); + // TODO select new block for log, empty it, erase it and write the log + return -1; + } + } + + offset = c->sector_size - c->cs_struct->csblock_free; // padded + + cent_log.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); + cent_log.nodetype = cpu_to_je16(JFFS2_NODETYPE_CENT_SUM_LOG); + cent_log.totlen = cpu_to_je32(PAD(sizeof(cent_log))); + cent_log.hdr_crc = cpu_to_je32(crc32(0, ¢_log, sizeof(struct jffs2_unknown_node) - 4)); + + cent_log.valid = 0; + cent_log.reserved = 0; + cent_log.erase_size = cpu_to_je32(c->sector_size); + cent_log.ofs_num = cpu_to_je16(0); + cent_log.version = cpu_to_je32(JFFS2_CS_VERSION); + + cent_log.ofs_crc = cpu_to_je32(0); + cent_log.node_crc = cpu_to_je32(crc32(0, ¢_log, sizeof(cent_log) - 8)); + + vecs[0].iov_base = ¢_log; + vecs[0].iov_len = PAD(sizeof(cent_log)); + + dbg_cs("Writing mount_log node to offset : %x\n", offset); + + down(&c->alloc_sem); + + ret = jffs2_flash_writev(c, vecs, 1, offset, &retlen, 0); + + up(&c->alloc_sem); + + if (ret || (retlen != PAD(sizeof(cent_log)))) { + JFFS2_WARNING("write of %zd bytes at 0x%08x failed. returned %d, retlen %zd\n", + PAD(sizeof(cent_log)), offset, ret, retlen); + + c->cs_struct->csblock_free -= PAD(sizeof(cent_log)); + goto retry; + } + + c->cs_struct->csblock_free -= PAD(sizeof(cent_log)); + + return 0; +} + + +#ifdef CONFIG_JFFS2_CS_FEB +// write umount log entry to flash +static int jffs2_cs_write_umount_log_jeb(struct jffs2_sb_info *c) +{ + struct jffs2_raw_cs_log cent_log; + struct cent_sum_offset *walk = NULL, *temp = NULL; + struct kvec vecs[2]; + int ret, i, infosize, datasize, failure = 0; + size_t retlen; + uint32_t offset; + jint32_t *buff = NULL, *ptr = NULL; + + infosize = PAD(sizeof(cent_log) + c->cs_struct->offset_list_num * 4); + datasize = infosize - sizeof(cent_log); + +retry: + if (c->cs_struct->csblock_free < infosize) { + dbg_cs("There is not enough free space for log -> erase it\n"); + // erase_cs_block + if (jffs2_cs_erase_block(c, c->cs_struct->csblock)){ + JFFS2_WARNING("Can't erase cent_sum log block\n"); + // TODO select new block for log, empty it, erase it and write the log + return -1; + } + } + + offset = c->sector_size - c->cs_struct->csblock_free; // padded + + if (!failure) { + + buff = kmalloc(datasize, GFP_KERNEL); + + if (!buff) { + JFFS2_WARNING("Can't allocate memory for cent_sum offset buffer!\n"); + return -ENOMEM; + } + + memset(buff, 0, datasize); + + cent_log.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); + cent_log.nodetype = cpu_to_je16(JFFS2_NODETYPE_CENT_SUM_LOG); + cent_log.totlen = cpu_to_je32(infosize); + cent_log.hdr_crc = cpu_to_je32(crc32(0, ¢_log, sizeof(struct jffs2_unknown_node) - 4)); + + cent_log.valid = 1; + cent_log.reserved = 0; + cent_log.erase_size = cpu_to_je32(c->sector_size); + cent_log.ofs_num = cpu_to_je16(c->cs_struct->offset_list_num); + cent_log.version = cpu_to_je32(JFFS2_CS_VERSION); + + ptr = buff; + temp = c->cs_struct->offset_list_head; + + for (i=0; ics_struct->offset_list_num; i++) { + walk = temp->next; + *(ptr++) = cpu_to_je32(temp->offset); + temp = walk; + } + + if (walk) { + JFFS2_ERROR("i == c->cs_struct->offset_list_num, but walk != 0 !(???)\n"); + //BUG(); + } + + cent_log.ofs_crc = cpu_to_je32(crc32(0, buff, datasize)); + cent_log.node_crc = cpu_to_je32(crc32(0, ¢_log, sizeof(cent_log) - 8)); + + vecs[0].iov_base = ¢_log; + vecs[0].iov_len = sizeof(cent_log); + vecs[1].iov_base = buff; + vecs[1].iov_len = datasize; + + } + + dbg_cs("Writing umount node to offset : %x\n", offset); + + down(&c->alloc_sem); + + ret = jffs2_flash_writev(c, vecs, 2, offset, &retlen, 0); + + up(&c->alloc_sem); + + if (ret || (retlen != infosize)) { + JFFS2_WARNING("write of %zd bytes at 0x%08x failed. returned %d, retlen %zd\n", + infosize, offset, ret, retlen); + + c->cs_struct->csblock_free -= infosize; + failure = 1; + goto retry; + } + + kfree(buff); + c->cs_struct->csblock_free -= infosize; + + return 0; + +} +#endif // CONFIG_JFFS2_CS_FEB + +// add a new offset to offset list +static int jffs2_cs_add_offset(struct jffs2_sb_info *c, uint32_t offset, uint32_t length) +{ + struct cent_sum_offset *new; + + new = kmalloc(sizeof(new), GFP_KERNEL); + if (!new) { + JFFS2_WARNING("Can't allocate memory for cent_sum_offset!\n"); + return -ENOMEM; + } + + new->next = NULL; + new->offset = offset; + new->length = length; + + if (c->cs_struct->offset_list_head == NULL) { + c->cs_struct->offset_list_head = new; + c->cs_struct->offset_list_tail = new; + } else { + c->cs_struct->offset_list_tail->next = new; + c->cs_struct->offset_list_tail = new; + } + + c->cs_struct->offset_list_num++; + dbg_cs("Offset list num = %d\n", c->cs_struct->offset_list_num); + + return 0; +} + +// clean up offset_list +static void jffs2_cs_clean_offset_list(struct jffs2_sb_info *c) +{ + struct cent_sum_offset *walk; + + while (c->cs_struct->offset_list_head) { + walk = c->cs_struct->offset_list_head->next; + kfree(c->cs_struct->offset_list_head); + c->cs_struct->offset_list_head = walk; + } + + c->cs_struct->offset_list_tail = NULL; + c->cs_struct->offset_list_num = 0; + +} + +// find csblock, call read_log, handle errors +int jffs2_cs_build_filesystem_jeb(struct jffs2_sb_info *c) +{ + int i, ret, length; + + for (i=0; i < CENT_SUM_RETRY_EMPTY; i++) { +//#warning "jffs2_check_nand_cleanmarker -> jffs2_check_nand_cleanmarker_ebh" + if (jffs2_check_nand_cleanmarker_ebh(c, &c->blocks[i], &length) == 2) { // if (bad_block) + continue; + } + + dbg_cs("csblock is the %d. block\n", i); + c->cs_struct->csblock = &c->blocks[i]; + break; + } + + if (c->cs_struct->csblock) { + + ret = jffs2_cs_read_log_jeb(c); + + switch (ret) { + case 0: // LOG OKK; + c->cs_struct->cs_flags |= JFFS2_CS_CSBLOCK_VALID; // flag valid = 1 + break; + + case 1: // MOUNT LOG found (umount failed) -> normal scan + c->cs_struct->cs_flags |= JFFS2_CS_CSBLOCK_VALID; // flag valid = 1 + return 1; + + case 2: // LOG not found -> empty and erase block, write mount log, normal scan + c->cs_struct->cs_flags &= ~JFFS2_CS_CSBLOCK_VALID; // flag valid = 0 + return 1; + + case 3: // LOG crc check failed => erase block, write mount log -> normal scan + c->cs_struct->cs_flags |= JFFS2_CS_CSBLOCK_VALID; // flag vlaid = 1 + + if (!jffs2_is_readonly(c)) { + if (jffs2_cs_erase_block(c, c->cs_struct->csblock)) { + c->cs_struct->csblock = NULL; // centralized summary disabled + dbg_cs("csblock erase error -> DISABLING cent_sum!\n"); + return 1; // ERROR + } + } + + return 1; + + default: + JFFS2_WARNING("cs_read_log returned error\n"); + c->cs_struct->csblock = NULL; // centralized summary disabled + return 1; + } + } else { + // TODO disable cent_sum + c->cs_struct->csblock = NULL; + return 1; + } + + dbg_cs("End of cs_build_fs\n"); + return 0; +} + +// read csblock, find last log, call process_log, handle errors +static int jffs2_cs_read_log_jeb(struct jffs2_sb_info *c) +{ + int ret; + uint32_t ofs, prev_ofs, crc; + unsigned char *flashbuf = NULL; + struct jffs2_unknown_node *node = NULL; + struct jffs2_raw_cs_log *log = NULL, *temp_log = NULL; + struct jffs2_unknown_node crcnode; + + flashbuf = jffs2_cs_alloc_flashbuf(c); + if (!flashbuf) { + JFFS2_WARNING("Can't allocate memory for flashbuf !\n"); + return -ENOMEM; + } + + ret = jffs2_fill_scan_buf(c, flashbuf, c->cs_struct->csblock->offset, + c->sector_size); + if (ret) { + jffs2_cs_free_flashbuf(c, flashbuf); + return -EIO; + } + + ofs = prev_ofs = 0; + c->cs_struct->csblock_free = c->sector_size; + + while ( *((uint32_t *) (flashbuf + ofs)) != 0xffffffff) { + + node = (struct jffs2_unknown_node *) (flashbuf + ofs); + + if (je16_to_cpu(node->magic) != JFFS2_MAGIC_BITMASK) { + JFFS2_WARNING("Magic bitmask not found!\n"); + + if(je16_to_cpu(node->magic) == KSAMTIB_CIGAM_2SFFJ) { + JFFS2_WARNING("Wrong endian filesystem!\n"); + jffs2_cs_free_flashbuf(c, flashbuf); + return -EIO; + } + // TODO handle problem: may be -EIO + return -EIO; + } + + /* 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; + crc = crc32(0, &crcnode, sizeof(crcnode)-4); + + if (crc != je32_to_cpu(node->hdr_crc)) { + JFFS2_WARNING("Header crc error!\n"); + + if (je16_to_cpu(node->nodetype) == JFFS2_NODETYPE_CENT_SUM_LOG) { + JFFS2_WARNING("LOG node has crc failure => erase block, write mount log\n"); + jffs2_cs_free_flashbuf(c, flashbuf); + return 3; // TODO: DANGEROUS SOLUTION + } + + jffs2_cs_free_flashbuf(c, flashbuf); + return -EIO; + } + + if (je16_to_cpu(node->nodetype) != JFFS2_NODETYPE_CENT_SUM_LOG && + je16_to_cpu(node->nodetype) != JFFS2_NODETYPE_PADDING) { + dbg_cs("Not LOG node found!\n"); + jffs2_cs_free_flashbuf(c, flashbuf); + return 2; + } + + if (je16_to_cpu(node->nodetype) == JFFS2_NODETYPE_CENT_SUM_LOG) + prev_ofs = ofs; + + c->cs_struct->csblock_free -= PAD(je32_to_cpu(node->totlen)); + ofs += PAD(je32_to_cpu(node->totlen)); + + dbg_cs("c->cs_struct->csblock_free = %d\n", c->cs_struct->csblock_free); + + if (ofs >= c->sector_size) { + dbg_cs("Ofs points to the next block => node=last_log\n"); + break; + } + } + + if (ofs == 0) { + dbg_cs("No node found in the begining of the block \n"); + // TODO delete ??? + jffs2_cs_free_flashbuf(c, flashbuf); + return 1; + } + + dbg_cs("LOG found!!!\n"); + + log = (struct jffs2_raw_cs_log *) (flashbuf + prev_ofs); + crc = crc32(0, log, sizeof(struct jffs2_raw_cs_log) - 8); + + /* check node crc */ + if (crc != je32_to_cpu(log->node_crc)) { + JFFS2_WARNING("log->node_crc != calculated node_crc\n"); + jffs2_cs_free_flashbuf(c, flashbuf); + return 3; + } + + /* check erase block size */ + if (je32_to_cpu(log->erase_size) != c->sector_size) { + JFFS2_WARNING("Image erase_size (%d) != jffs2 erase_size (%d) -> normal scan\n", + je32_to_cpu(log->erase_size), c->sector_size); + jffs2_cs_free_flashbuf(c, flashbuf); + return 1; + } + + /* check CS version */ + if (je32_to_cpu(log->version) != JFFS2_CS_VERSION) { + JFFS2_WARNING("Image has CS with version number %d, which is not supported (%d)\n", + je32_to_cpu(log->version), JFFS2_CS_VERSION); + JFFS2_WARNING("Skipping CS information => erase CS block, normal scan\n"); + jffs2_cs_free_flashbuf(c, flashbuf); + return 3; + } + + /* check log type */ + if (!log->valid) { + dbg_cs("Mount log found! -> normal scan\n"); + jffs2_cs_free_flashbuf(c, flashbuf); + return 1; + } + + /* check offset crc */ + crc = crc32(0, log->ofs, je32_to_cpu(log->totlen) - sizeof(struct jffs2_raw_cs_log)); + if (crc != je32_to_cpu(log->ofs_crc)) { + JFFS2_WARNING("log->ofs_crc != calculated ofs_crc\n"); + jffs2_cs_free_flashbuf(c, flashbuf); + return 3; + } + + temp_log = kmalloc(PAD(je32_to_cpu(log->totlen)), GFP_KERNEL); + if (!temp_log) { + JFFS2_WARNING("Can't allocate memory for temp_log!\n"); + jffs2_cs_free_flashbuf(c, flashbuf); + return 1; + } + + memcpy(temp_log, log, PAD(je32_to_cpu(log->totlen))); + jffs2_cs_free_flashbuf(c, flashbuf); + + ret = jffs2_cs_process_log(c, temp_log); + if (ret) { + kfree(temp_log); + return ret; + } + + kfree(temp_log); + return 0; +} + +// process log info +static int jffs2_cs_process_log(struct jffs2_sb_info *c, struct jffs2_raw_cs_log *log) +{ + int i, ret; + int loaded = 0; + uint32_t loaded_offset = 0; + uint32_t offset, orig_offset; + unsigned char *flashbuf = NULL; + union jffs2_node_union *node; + + flashbuf = jffs2_cs_alloc_flashbuf(c); + + if (!flashbuf) { + JFFS2_WARNING("Can't allocate memory for flashbuf !\n"); + return 1; //-ENOMEM; + } + + for(i=0; iofs_num); i++) { + + offset = je32_to_cpu(log->ofs[i]); + orig_offset = offset; + + dbg_cs("Offset(%d) = 0x%x\n", i, offset); + + if (!loaded) { + + ret = jffs2_fill_scan_buf(c, flashbuf, offset & (~(c->sector_size - 1)), c->sector_size); + + if (ret) { + JFFS2_WARNING("fill_scan_buf returned error\n"); + jffs2_cs_free_flashbuf(c, flashbuf); + return 1; //-EIO; + } + + loaded = 1; + loaded_offset = offset & (~(c->sector_size - 1)); + } + else { + if (loaded_offset != (offset & (~(c->sector_size - 1)))) { + + ret = jffs2_fill_scan_buf(c, flashbuf, offset & (~(c->sector_size - 1)), c->sector_size); + + if (ret) { + JFFS2_WARNING("fill_scan_buf returned error\n"); + jffs2_cs_free_flashbuf(c, flashbuf); + return 1; //-EIO; + } + + loaded_offset = offset & (~(c->sector_size - 1)); + } + } + + offset = offset % c->sector_size; + node = (union jffs2_node_union *) (flashbuf + offset); + + ret = jffs2_cs_process_node(c, node, orig_offset); + + if (ret) { + jffs2_cs_free_flashbuf(c, flashbuf); + jffs2_cs_clean_offset_list(c); + return ret; + } + } + + jffs2_cs_free_flashbuf(c, flashbuf); + + if (jffs2_cs_mode() == JFFS2_CS_MODE_MP_SYSFS) { + ret = jffs2_cs_add_offset(c, c->cs_struct->cs_log_offset, je32_to_cpu(log->totlen)); + if (ret) { + return ret; + } + } + + // all data was loaded, correct jeb & sb space counters + jffs2_cs_correct_jebinfo(c); + + return 0; +} + +// process cent_sum node +static int jffs2_cs_process_node(struct jffs2_sb_info *c, union jffs2_node_union *node, uint32_t offset) +{ + uint32_t crc; + + crc = crc32(0, &(node->u), sizeof(struct jffs2_unknown_node) - 4); + if (crc != je32_to_cpu(node->u.hdr_crc)) { + JFFS2_WARNING("Hdr_crc failed on cent_sum (offset) node!\n"); + return 1; + } + + if (jffs2_cs_add_offset(c, offset, je32_to_cpu(node->u.totlen))) { + return 1; + } + + switch (je16_to_cpu(node->u.nodetype)) { + + case JFFS2_NODETYPE_CENT_SUM_INOCACHE: + return jffs2_cs_process_inocache_node(c, &node->ic); + + case JFFS2_NODETYPE_CENT_SUM_JEBINFO: + return jffs2_cs_process_jebinfo_node(c, &node->j); + + case JFFS2_NODETYPE_CENT_SUM_RAWREF: + return jffs2_cs_process_rawref_node(c, &node->r); + + case JFFS2_NODETYPE_CENT_SUM_LISTS: + return jffs2_cs_process_lists_node(c, &node->li); + + default: + JFFS2_WARNING("Unknown nodetype!\n"); + return 1; + } + + return 0; +} + +// process cent_sum inocache node +static int jffs2_cs_process_inocache_node(struct jffs2_sb_info *c, struct jffs2_raw_cs_inocache *node) +{ + int i; + uint32_t crc; + struct jffs2_cs_inocache *ptr = NULL; + struct jffs2_inode_cache *ic; + + crc = crc32(0, node, sizeof(struct jffs2_raw_cs_inocache) - 8); + if (crc != je32_to_cpu(node->node_crc)) { + JFFS2_WARNING("Crc failure on cent_sum inocache: node_crc!\n"); + return 1; + } + + crc = crc32(0, node->cache, je32_to_cpu(node->cache_num) * JFFS2_INOCACHE_ENTRY_SIZE); + if (crc != je32_to_cpu(node->cache_crc)) { + JFFS2_WARNING("Crc failure on cent_sum inocache: cache_crc!\n"); + return 1; + } + + ptr = (struct jffs2_cs_inocache *) node->cache; + + for(i=0; icache_num); i++) { + + ic = jffs2_alloc_inode_cache(); + if (!ic) { + JFFS2_WARNING("allocation of inode cache failed\n"); + return 1; + } + memset(ic, 0, sizeof(*ic)); + + ic->ino = je32_to_cpu(ptr->ino); + ic->nlink = je32_to_cpu(ptr->nlink); + ic->state = je32_to_cpu(ptr->state); + ptr++; + + dbg_cs2("Adding inocache %d\n", ic->ino); + + ic->nodes = (void *)ic; + jffs2_add_ino_cache(c, ic); + + if (ic->ino > c->highest_ino) + c->highest_ino = ic->ino; + + } + + return 0; +} + +// process cent_sum jebinfo node +static int jffs2_cs_process_jebinfo_node(struct jffs2_sb_info *c, struct jffs2_raw_cs_jebinfo *node) +{ + int i, tmp; + uint32_t crc; + struct jffs2_cs_jeb *ptr = NULL; + struct jffs2_eraseblock *jeb; + + crc = crc32(0, node, sizeof(struct jffs2_raw_cs_jebinfo) - 8); + if (crc != je32_to_cpu(node->node_crc)) { + JFFS2_WARNING("Crc failure on cent_sum jebinfo: node_crc!\n"); + return 1; + } + + crc = crc32(0, node->jebinfo, je32_to_cpu(node->jebinfo_num) * JFFS2_JEBINFO_ENTRY_SIZE); + if (crc != je32_to_cpu(node->jebinfo_crc)) { + JFFS2_WARNING("Crc failure on cent_sum jebinfo: jebinfo_crc!\n"); + return 1; + } + + ptr = (struct jffs2_cs_jeb *) node->jebinfo; + + for(i=0; ijebinfo_num); i++) { + + jeb = &c->blocks[je32_to_cpu(ptr->offset) / c->sector_size]; + jeb->unchecked_size = je32_to_cpu(ptr->unchecked_size); + jeb->used_size = je32_to_cpu(ptr->used_size); + jeb->dirty_size = je32_to_cpu(ptr->dirty_size); + jeb->wasted_size = je32_to_cpu(ptr->wasted_size); + ptr++; + + c->unchecked_size += jeb->unchecked_size; + c->used_size += jeb->used_size; + c->dirty_size += jeb->dirty_size; + c->wasted_size += jeb->wasted_size; + + // calculate free size + tmp = jeb->unchecked_size + jeb->used_size + jeb->dirty_size + jeb->wasted_size; + jeb->free_size = c->sector_size - tmp; + + c->free_size -= tmp; + + } + + return 0; +} + +// process cent_sum rawref node +static int jffs2_cs_process_rawref_node(struct jffs2_sb_info *c, struct jffs2_raw_cs_rawref *node) +{ + int i; + uint32_t crc; + uint32_t ino; + struct jffs2_cs_rawref *ptr = NULL; + struct jffs2_eraseblock *jeb; + struct jffs2_raw_node_ref *raw; + struct jffs2_inode_cache *ic; + + crc = crc32(0, node, sizeof(struct jffs2_raw_cs_rawref) - 8); + if (crc != je32_to_cpu(node->node_crc)) { + JFFS2_WARNING("Crc failure on cent_sum rawref: node_crc!\n"); + return 1; + } + + crc = crc32(0, node->rawrefs, je32_to_cpu(node->rawref_num) * JFFS2_RAWREF_ENTRY_SIZE); + if (crc != je32_to_cpu(node->rawref_crc)) { + JFFS2_WARNING("Crc failure on cent_sum rawref: rawref_crc!\n"); + return 1; + } + + ptr = (struct jffs2_cs_rawref *) node->rawrefs; + + for(i=0; irawref_num); i++) { + + raw = jffs2_alloc_raw_node_ref(); + + if (!raw) { + JFFS2_WARNING("allocation of node reference failed\n"); + return 1; + } + + raw->flash_offset = je32_to_cpu(ptr->flash_offset); + raw->__totlen = je32_to_cpu(ptr->totlen); + ino = je32_to_cpu(ptr->ino); + ptr++; + + jeb = &c->blocks[raw->flash_offset / c->sector_size]; + + raw->next_phys = NULL; + + if (!jeb->first_node) + jeb->first_node = raw; + if (jeb->last_node) + jeb->last_node->next_phys = raw; + jeb->last_node = raw; + + if (ino) { + ic = jffs2_get_ino_cache(c, ino); + if (!ic) { + JFFS2_WARNING("Inocache not found for ino %d at offset 0x%x!\n", + ino, raw->flash_offset); + BUG(); + } + + raw->next_in_ino = ic->nodes; + ic->nodes = raw; + } else { + raw->next_in_ino = NULL; + } + } + + return 0; +} + + +// process list entry +static void jffs2_cs_process_list(struct jffs2_sb_info *c, jint16_t **ptr, struct list_head *l) +{ + int count, i; + + count = je16_to_cpu(**ptr); + (*ptr)++; + + if (!count) { + return; + } + + for (i=0; iblocks[je16_to_cpu(**ptr)].list, l); + (*ptr)++; + } + +} + +// process cent_sum lists node +static int jffs2_cs_process_lists_node(struct jffs2_sb_info *c, struct jffs2_raw_cs_lists *node) +{ + uint32_t crc; + jint16_t *ptr; + + crc = crc32(0, node->lists, je32_to_cpu(node->totlen) - sizeof (struct jffs2_raw_cs_lists)); + if (crc != je32_to_cpu(node->lists_crc)) { + JFFS2_WARNING("Crc failure on cent_sum lists: lists_crc!\n"); + return 1; + } + + ptr = node->lists; + + jffs2_cs_process_list(c, &ptr, &c->clean_list); + jffs2_cs_process_list(c, &ptr, &c->very_dirty_list); + jffs2_cs_process_list(c, &ptr, &c->dirty_list); + jffs2_cs_process_list(c, &ptr, &c->erasable_list); + + c->nr_free_blocks = je16_to_cpu(*ptr); + jffs2_cs_process_list(c, &ptr, &c->free_list); + + c->bad_size = je16_to_cpu(*ptr) * c->sector_size; + c->free_size -= c->bad_size; + jffs2_cs_process_list(c, &ptr, &c->bad_list); + + // TODO !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + jffs2_cs_process_list(c, &ptr, &c->bad_used_list); + + return 0; +} + +// correct jeb space information +static int jffs2_cs_correct_jebinfo(struct jffs2_sb_info *c) +{ + struct cent_sum_offset *walk; + struct jffs2_eraseblock *jeb; + + while(c->cs_struct->offset_list_head) { + walk = c->cs_struct->offset_list_head->next; + + jeb = &c->blocks[c->cs_struct->offset_list_head->offset / c->sector_size]; + + WASTED_SPACE(c->cs_struct->offset_list_head->length); + + // correct wasted & dirty size by jeb change, move jeb to an other list if it is nessesery + if (walk) { + if (jeb != &c->blocks[walk->offset / c->sector_size]) { + list_del(&jeb->list); + + c->wasted_size += jeb->free_size; + c->free_size -= jeb->free_size; + jeb->wasted_size += jeb->free_size; + jeb->free_size = 0; + + if (ISDIRTY (jeb->wasted_size + jeb->dirty_size)) { + c->dirty_size += jeb->wasted_size; + c->wasted_size -= jeb->wasted_size; + jeb->dirty_size += jeb->wasted_size; + jeb->wasted_size = 0; + if (VERYDIRTY(c, jeb->dirty_size)) { + dbg_cs("Adding full erase block at 0x%08x to very_dirty_list " + "(free 0x%08x, dirty 0x%08x, used 0x%08x\n", + jeb->offset, jeb->free_size, jeb->dirty_size, + jeb->used_size); + list_add_tail(&jeb->list, &c->very_dirty_list); + } else { + dbg_cs("Adding full erase block at 0x%08x to dirty_list " + "(free 0x%08x, dirty 0x%08x, used 0x%08x\n", + jeb->offset, jeb->free_size, jeb->dirty_size, + jeb->used_size); + list_add_tail(&jeb->list, &c->dirty_list); + } + } else { + dbg_cs("Adding full erase block at 0x%08x to clean_list " + "(free 0x%08x, dirty 0x%08x, used 0x%08x\n", + jeb->offset, jeb->free_size, jeb->dirty_size, + jeb->used_size); + list_add_tail(&jeb->list, &c->clean_list); + } + + } + } else { // this is the last offset + uint32_t padding; + + padding = jeb->free_size % c->wbuf_pagesize; + + if (padding) { + DIRTY_SPACE(padding); + } + + if (!jffs2_sum_active()) { + list_del(&jeb->list); + c->nextblock = jeb; //last used block + } else { + list_del(&jeb->list); + + c->wasted_size += jeb->free_size; + c->free_size -= jeb->free_size; + jeb->wasted_size += jeb->free_size; + jeb->free_size = 0; + + if (ISDIRTY (jeb->wasted_size + jeb->dirty_size)) { + c->dirty_size += jeb->wasted_size; + c->wasted_size -= jeb->wasted_size; + jeb->dirty_size += jeb->wasted_size; + jeb->wasted_size = 0; + if (VERYDIRTY(c, jeb->dirty_size)) { + dbg_cs("Adding full erase block at 0x%08x to very_dirty_list " + "(free 0x%08x, dirty 0x%08x, used 0x%08x\n", + jeb->offset, jeb->free_size, jeb->dirty_size, + jeb->used_size); + list_add_tail(&jeb->list, &c->very_dirty_list); + } else { + dbg_cs("Adding full erase block at 0x%08x to dirty_list " + "(free 0x%08x, dirty 0x%08x, used 0x%08x\n", + jeb->offset, jeb->free_size, jeb->dirty_size, + jeb->used_size); + list_add_tail(&jeb->list, &c->dirty_list); + } + } else { + dbg_cs("Adding full erase block at 0x%08x to clean_list " + "(free 0x%08x, dirty 0x%08x, used 0x%08x\n", + jeb->offset, jeb->free_size, jeb->dirty_size, + jeb->used_size); + list_add_tail(&jeb->list, &c->clean_list); + } + } + } + + kfree(c->cs_struct->offset_list_head); + c->cs_struct->offset_list_head = walk; + c->cs_struct->offset_list_num--; + } + + if (jffs2_cs_mode() == JFFS2_CS_MODE_FEB) { + // correct c->free_size + dbg_cs2("unc : %d, was : %d, dir : %d, use : %d, fre : %d\n", + c->unchecked_size, c->wasted_size, c->dirty_size, c->used_size, c->free_size); + + c->used_size += c->sector_size; + c->free_size -= c->sector_size; + + dbg_cs2("unc : %d, was : %d, dir : %d, use : %d, fre : %d\n", + c->unchecked_size, c->wasted_size, c->dirty_size, c->used_size, c->free_size); + } + + return 0; +} + +// correct jeb space information at remount +int jffs2_cs_correct_jebinfo_remount(struct jffs2_sb_info *c) +{ + struct cent_sum_offset *walk; + struct jffs2_eraseblock *jeb; + + while(c->cs_struct->offset_list_head) { + walk = c->cs_struct->offset_list_head->next; + + jeb = &c->blocks[c->cs_struct->offset_list_head->offset / c->sector_size]; + + INCREASE_WASTED_SPACE(c->cs_struct->offset_list_head->length); + + // correct wasted & dirty size by jeb change, move jeb to an other list if it is nessesery + if (walk) { + if (jeb != &c->blocks[walk->offset / c->sector_size]) { + list_del(&jeb->list); + + c->wasted_size += jeb->free_size; + c->free_size -= jeb->free_size; + jeb->wasted_size += jeb->free_size; + jeb->free_size = 0; + + if (ISDIRTY (jeb->wasted_size + jeb->dirty_size)) { + c->dirty_size += jeb->wasted_size; + c->wasted_size -= jeb->wasted_size; + jeb->dirty_size += jeb->wasted_size; + jeb->wasted_size = 0; + if (VERYDIRTY(c, jeb->dirty_size)) { + dbg_cs("Adding full erase block at 0x%08x to very_dirty_list " + "(free 0x%08x, dirty 0x%08x, used 0x%08x\n", + jeb->offset, jeb->free_size, jeb->dirty_size, + jeb->used_size); + list_add_tail(&jeb->list, &c->very_dirty_list); + } else { + dbg_cs("Adding full erase block at 0x%08x to dirty_list " + "(free 0x%08x, dirty 0x%08x, used 0x%08x\n", + jeb->offset, jeb->free_size, jeb->dirty_size, + jeb->used_size); + list_add_tail(&jeb->list, &c->dirty_list); + } + } else { + dbg_cs("Adding full erase block at 0x%08x to clean_list " + "(free 0x%08x, dirty 0x%08x, used 0x%08x\n", + jeb->offset, jeb->free_size, jeb->dirty_size, + jeb->used_size); + list_add_tail(&jeb->list, &c->clean_list); + } + + } + } else { + // this is the last offset + } + + kfree(c->cs_struct->offset_list_head); + c->cs_struct->offset_list_head = walk; + c->cs_struct->offset_list_num--; + } + + if (c->cs_struct->offset_list_num) { + dbg_cs("List is empty, but list_num != 0\n"); + BUG(); + } + + return 0; +} + +// if we can't load the log => reset sb_info +void jffs2_cs_reset_sb_info(struct jffs2_sb_info *c) +{ + int i; + + c->used_size = 0; + c->dirty_size = 0; + c->wasted_size = 0; + c->free_size = c->flash_size; + c->erasing_size = 0; + c->bad_size = 0; + c->unchecked_size = 0; + + c->nr_free_blocks = 0; + c->nr_erasing_blocks = 0; + + c->nextblock = NULL; + + for (i=0; inr_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; + c->blocks[i].dirty_size = 0; + c->blocks[i].wasted_size = 0; + c->blocks[i].unchecked_size = 0; + c->blocks[i].used_size = 0; + c->blocks[i].first_node = NULL; + c->blocks[i].last_node = NULL; + c->blocks[i].bad_count = 0; + } + + INIT_LIST_HEAD(&c->clean_list); + INIT_LIST_HEAD(&c->very_dirty_list); + INIT_LIST_HEAD(&c->dirty_list); + INIT_LIST_HEAD(&c->erasable_list); + INIT_LIST_HEAD(&c->erasing_list); + INIT_LIST_HEAD(&c->erase_pending_list); + INIT_LIST_HEAD(&c->erasable_pending_wbuf_list); + INIT_LIST_HEAD(&c->erase_complete_list); + INIT_LIST_HEAD(&c->free_list); + INIT_LIST_HEAD(&c->bad_list); + INIT_LIST_HEAD(&c->bad_used_list); + c->highest_ino = 1; +} + +static void* jffs2_cs_alloc_flashbuf(struct jffs2_sb_info *c) +{ + if (c->sector_size > 128*1024) + return vmalloc(c->sector_size); + else + return kmalloc(c->sector_size, GFP_KERNEL); +} + +static void jffs2_cs_free_flashbuf(struct jffs2_sb_info *c, void *flashbuf) +{ + if (c->sector_size > 128*1024) + vfree(flashbuf); + else + kfree(flashbuf); +} + +#ifdef CONFIG_JFFS2_CS_MP_SYSFS +/******************************************************************************* +* MOUNT PARAMETER / SYSFS MODE SPECIFIC FUNTIONS * +*******************************************************************************/ + +enum { + Opt_cs_offset, Opt_err, +}; + +static match_table_t tokens = { + {Opt_cs_offset, "cs_offset=%u"}, + {Opt_err, NULL}, +}; + + +// parse mount parameters for "cs_offset = value" +static int jffs2_cs_parse_mount_params(struct jffs2_sb_info *c, char *options) +{ + char *p; + substring_t args[MAX_OPT_ARGS]; + + if (!options) { + JFFS2_WARNING("no mount parameter found!\n"); + return 1; + } + + while ((p = strsep(&options, ",")) != NULL) { + + int token, n; + if (!*p) + continue; + + token = match_token(p, tokens, args); + + switch (token) { + + case Opt_cs_offset: + if (match_int(&args[0], &n)) { + JFFS2_WARNING("Match_int failure!\n"); + return 0; + } + + if (n < 0) { + JFFS2_WARNING("negative parameter is not allowed!\n"); + return 0; + } + + c->cs_struct->cs_log_offset = n; + c->cs_struct->cs_flags |= JFFS2_CS_MOUNT_OFFSET_PARAM; + dbg_cs("cs_log_offset = %d\n", c->cs_struct->cs_log_offset); + break; + + default: + JFFS2_WARNING("Unrecognized mount option \"%s\" " + "or missing value!\n", p); + return 0; + } + } + + return 1; +} + +// write mount log +int jffs2_cs_write_mount_log_mntsys(struct jffs2_sb_info *c) +{ + return 0; +} + +// write umount log (set centsum_offset /sysfs/) +static int jffs2_cs_write_umount_log_mntsys(struct jffs2_sb_info *c) +{ + struct jffs2_raw_cs_log cent_log; + struct cent_sum_offset *walk = NULL, *temp = NULL; + struct kvec vecs[2]; + struct jffs2_eraseblock *jeb = NULL; + int ret, i, infosize, datasize; + size_t retlen; + uint32_t offset, avail; + jint32_t *buff = NULL, *ptr = NULL; + + infosize = PAD(sizeof(cent_log) + c->cs_struct->offset_list_num * 4); + datasize = infosize - sizeof(cent_log); + + ret = jffs2_reserve_space(c, infosize, &offset, &avail, ALLOC_CENT_SUM, 0); + if (ret) { + dbg_cs("reserve_space returned: %d\n", ret); + return ret; + } + + buff = kmalloc (datasize, GFP_KERNEL); + + if (!buff) { + JFFS2_WARNING("Can't allocate memory for cent_sum offset buffer!\n"); + return -ENOMEM; + } + + memset(buff, 0, datasize); + + cent_log.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); + cent_log.nodetype = cpu_to_je16(JFFS2_NODETYPE_CENT_SUM_LOG); + cent_log.totlen = cpu_to_je32(infosize); + cent_log.hdr_crc = cpu_to_je32(crc32(0, ¢_log, sizeof(struct jffs2_unknown_node) - 4)); + + cent_log.valid = 1; + cent_log.reserved = 0; + cent_log.erase_size = cpu_to_je32(c->sector_size); + cent_log.ofs_num = cpu_to_je16(c->cs_struct->offset_list_num); + cent_log.version = cpu_to_je32(JFFS2_CS_VERSION); + + ptr = buff; + temp = c->cs_struct->offset_list_head; + + for (i=0; ics_struct->offset_list_num; i++) { + walk = temp->next; + *(ptr++) = cpu_to_je32(temp->offset); + temp = walk; + } + + if (walk) { + JFFS2_WARNING("i == c->cs_struct->offset_list_num, but walk != 0 !(???)\n"); + //BUG(); + } + + cent_log.ofs_crc = cpu_to_je32(crc32(0, buff, datasize)); + cent_log.node_crc = cpu_to_je32(crc32(0, ¢_log, sizeof(cent_log) - 8)); + + vecs[0].iov_base = ¢_log; + vecs[0].iov_len = sizeof(cent_log); + vecs[1].iov_base = buff; + vecs[1].iov_len = datasize; + + jeb = &c->blocks[offset / c->sector_size]; + + dbg_cs("Writing umount node to offset : %x\n", offset); + + ret = jffs2_flash_writev(c, vecs, 2, offset, &retlen, 0); + up(&c->alloc_sem); + + if (ret || (retlen != infosize)) { + JFFS2_WARNING("write of %zd bytes at 0x%08x failed. returned %d, retlen %zd\n", + infosize, offset, ret, retlen); + + kfree(buff); + WASTED_SPACE(infosize); + return -EIO; + } + + kfree(buff); + DECREASE_FREE_SPACE(infosize); + centsum_offset = offset; + + ret = jffs2_cs_add_offset(c, offset, infosize); + if (ret) + return ret; + + return 0; +} + + +// read cs log from flash, check it, load it +static int jffs2_cs_read_log_mntsys(struct jffs2_sb_info *c) +{ + int ret, load_size; + uint32_t crc; + unsigned char *flashbuf = NULL; + struct jffs2_unknown_node *node = NULL, crcnode; + struct jffs2_raw_cs_log *log = NULL, *temp_log = NULL; + + flashbuf = kmalloc(c->sector_size, GFP_KERNEL); + + if (!flashbuf) { + JFFS2_WARNING("Can't allocate memory for flashbuf !\n"); + return -ENOMEM; + } + + load_size = c->sector_size - (c->cs_struct->cs_log_offset % c->sector_size); + + ret = jffs2_fill_scan_buf(c, flashbuf, c->cs_struct->cs_log_offset, load_size); + if (ret) { + jffs2_cs_free_flashbuf(c, flashbuf); + return -EIO; + } + + node = (struct jffs2_unknown_node *) flashbuf; + + if (je16_to_cpu(node->magic) != JFFS2_MAGIC_BITMASK) { + JFFS2_WARNING("Magic bitmask not found!\n"); + + if(je16_to_cpu(node->magic) == KSAMTIB_CIGAM_2SFFJ) { + JFFS2_WARNING("Wrong endian filesystem!\n"); + jffs2_cs_free_flashbuf(c, flashbuf); + return -EIO; + } + // TODO handle problem: may be -EIO + return -EIO; + } + + crcnode.magic = node->magic; + crcnode.nodetype = cpu_to_je16(je16_to_cpu(node->nodetype) | JFFS2_NODE_ACCURATE); + crcnode.totlen = node->totlen; + + crc = crc32(0, &crcnode, sizeof(crcnode) - 4); + if (crc != je32_to_cpu(node->hdr_crc)) { + JFFS2_WARNING("Header crc error!\n"); + jffs2_cs_free_flashbuf(c, flashbuf); + return -EIO; + } + + if (je16_to_cpu(node->nodetype) == JFFS2_NODETYPE_CENT_SUM_LOG) { + dbg_cs("LOG found !!!\n"); + log = (struct jffs2_raw_cs_log *) node; + } else { + dbg_cs("not LOG found !!!\n"); + jffs2_cs_free_flashbuf(c, flashbuf); + return 1; + } + + /* check node crc */ + crc = crc32(0, log, sizeof(struct jffs2_raw_cs_log) - 8); + if (crc != je32_to_cpu(log->node_crc)) { + JFFS2_WARNING("log->node_crc != calculated node_crc\n"); + jffs2_cs_free_flashbuf(c, flashbuf); + return 1; + } + + /* check erase block size */ + if (je32_to_cpu(log->erase_size) != c->sector_size) { + JFFS2_WARNING("Image erase_size (%d) != jffs2 erase_size (%d) -> normal scan\n", + je32_to_cpu(log->erase_size), c->sector_size); + jffs2_cs_free_flashbuf(c, flashbuf); + return 1; + } + + /* check CS version */ + if (je32_to_cpu(log->version) != JFFS2_CS_VERSION) { + JFFS2_WARNING("Image has CS with version number %d, which is not supported (%d)\n", + je32_to_cpu(log->version), JFFS2_CS_VERSION); + JFFS2_WARNING("Skipping CS information => normal scan\n"); + jffs2_cs_free_flashbuf(c, flashbuf); + return 1; + } + + /* check CS log type */ + if (!log->valid) { + dbg_cs("Mount log found! (??? impossible in mount param / sysfs mode)-> normal scan\n"); + jffs2_cs_free_flashbuf(c, flashbuf); + return 1; + } + + /* check offsets crc */ + crc = crc32(0, log->ofs, je32_to_cpu(log->totlen) - sizeof(struct jffs2_raw_cs_log)); + if (crc != je32_to_cpu(log->ofs_crc)) { + JFFS2_WARNING("log->ofs_crc != calculated ofs_crc\n"); + jffs2_cs_free_flashbuf(c, flashbuf); + return 1; + } + + temp_log = kmalloc(PAD(je32_to_cpu(log->totlen)), GFP_KERNEL); + if (!temp_log) { + JFFS2_WARNING("Can't allocate memory for temp_log!\n"); + jffs2_cs_free_flashbuf(c, flashbuf); + return 1; //-ENOMEM; + } + + memcpy(temp_log, log, PAD(je32_to_cpu(log->totlen))); + jffs2_cs_free_flashbuf(c, flashbuf); + + ret = jffs2_cs_process_log(c, temp_log); + if (ret) { + kfree(temp_log); + return ret; + } + + kfree(temp_log); + return 0; +} + +// build filesystem +int jffs2_cs_build_filesystem_mntsys(struct jffs2_sb_info *c) +{ + int ret; + + if (!HAS_MOUNT_OFFSET_PARAM) { + dbg_cs("No mount parameter -> normal scan\n"); + return 1; + } + + ret = jffs2_cs_read_log_mntsys(c); + + if (ret) { + dbg_cs("returned with %d\n", ret); + return 1; + } + + return 0; +} +#endif /* CONFIG_JFFS2_CS_MP_SYSFS */ diff -Narup Mtd-orig/fs/jffs2/cent_sum.h mtd/fs/jffs2/cent_sum.h --- Mtd-orig/fs/jffs2/cent_sum.h 1970-01-01 01:00:00.000000000 +0100 +++ mtd/fs/jffs2/cent_sum.h 2006-02-03 17:14:45.000000000 +0100 @@ -0,0 +1,155 @@ +/* + * JFFS2 -- Journalling Flash File System, Version 2. + * + * Copyright (C) 2005 Zoltan Sogor , + * Ferenc Havasi , + * Patrik Kluba , + * University of Szeged, Hungary + * + * For licensing information, see the file 'LICENCE' in this directory. + * + * $Id$ + * + */ + +#ifndef _CENT_SUM_H +#define _CENT_SUM_H + +#include + +#define JFFS2_CS_VERSION 1 +#define CENT_SUM_RETRY_EMPTY 3 +#define CENT_SUM_BUFFER_SIZE 128*1024 //kmalloc can't allocate more than 128k + +#define JFFS2_INOCACHE_ENTRY_SIZE (sizeof(struct jffs2_cs_inocache)) +#define JFFS2_MIN_INOCACHE_NUM 10 +#define JFFS2_JEBINFO_ENTRY_SIZE (sizeof(struct jffs2_cs_jeb)) +#define JFFS2_MIN_JEBINFO_NUM 5 +#define JFFS2_RAWREF_ENTRY_SIZE (sizeof(struct jffs2_cs_rawref)) +#define JFFS2_MIN_RAWREF_NUM 10 + +#define JFFS2_CS_DURING_MOUNT 1 +#define JFFS2_CS_DURING_UNMOUNT 2 +#define JFFS2_CS_CSBLOCK_VALID 4 +#define JFFS2_CS_MOUNT_OFFSET_PARAM 8 // for Mount param / Sysfs support + +#define IS_DURING_MOUNT (c->cs_struct->cs_flags & JFFS2_CS_DURING_MOUNT) +#define IS_DURING_UMOUNT (c->cs_struct->cs_flags & JFFS2_CS_DURING_UNMOUNT) +#define IS_CSBLOCK_VALID (c->cs_struct->cs_flags & JFFS2_CS_CSBLOCK_VALID) +#define HAS_MOUNT_OFFSET_PARAM (c->cs_struct->cs_flags & JFFS2_CS_MOUNT_OFFSET_PARAM) + +#define JFFS2_CS_MODE_NONE 0 // CS is not enabled +#define JFFS2_CS_MODE_FEB 1 // First Erase Block +#define JFFS2_CS_MODE_MP_SYSFS 2 // Mount Parameter / SysFS + +#define DECREASE_FREE_SPACE(x) do { typeof(x) _x = (x); \ + c->free_size -= _x; \ + jeb->free_size -= _x; \ + } while(0) + +#define INCREASE_WASTED_SPACE(x) do { typeof(x) _x = (x); \ + c->wasted_size += _x; \ + jeb->wasted_size += _x; \ + } while(0) + +struct jffs2_eraseblock; + +struct cent_sum_offset { + struct cent_sum_offset *next; + uint32_t offset; + uint32_t length; +}; + +struct cent_sum { + struct jffs2_eraseblock *csblock; + uint32_t csblock_free; + uint32_t cs_log_offset; // for mount param / sysfs support + unsigned char cs_flags; + struct cent_sum_offset *offset_list_tail; + struct cent_sum_offset *offset_list_head; + uint32_t offset_list_num; + void *raw_nii; // stores raw_node_refs and its next_in_ino +}; + +struct jffs2_cs_inocache { + jint32_t ino; + jint32_t nlink; + jint32_t state; +} __attribute__((packed)); + +struct jffs2_cs_jeb { + jint32_t offset; + jint32_t unchecked_size; + jint32_t used_size; + jint32_t dirty_size; + jint32_t wasted_size; +} __attribute__((packed)); + +struct jffs2_cs_rawref { + jint32_t flash_offset; + jint32_t totlen; + jint32_t ino; +} __attribute__((packed)); + +extern unsigned int centsum_offset; // module parameter (sysfs) stores umount log offset for next mount + +#if defined(CONFIG_JFFS2_CS) +#define jffs2_cs_active() (1) + +int jffs2_cs_init(struct jffs2_sb_info *c, void *data); +void jffs2_cs_dump_centsum_data(struct jffs2_sb_info *c); +void jffs2_cs_free(struct jffs2_sb_info *c); +void jffs2_cs_free_lists(struct jffs2_sb_info *c); +void jffs2_cs_reset_sb_info(struct jffs2_sb_info *c); +int jffs2_cs_correct_jebinfo_remount(struct jffs2_sb_info *c); +int jffs2_cs_save_rawrefs_nii(struct jffs2_sb_info *c); +int jffs2_cs_restore_rawrefs_nii(struct jffs2_sb_info *c); + +#if defined(CONFIG_JFFS2_CS_FEB) +// FEB mode specific functions, ... + +#define jffs2_cs_mode() (JFFS2_CS_MODE_FEB) + +int jffs2_cs_init_cs_block_jeb(struct jffs2_sb_info *c); +int jffs2_cs_write_mount_log_jeb(struct jffs2_sb_info *c); +int jffs2_cs_build_filesystem_jeb(struct jffs2_sb_info *c); + +#define jffs2_cs_write_mount_log(c) jffs2_cs_write_mount_log_jeb(c) +#define jffs2_cs_write_umount_log(c) jffs2_cs_write_umount_log_jeb(c) +#define jffs2_cs_build_filesystem(c) jffs2_cs_build_filesystem_jeb(c) +#define jffs2_cs_init_cs_block(c) jffs2_cs_init_cs_block_jeb(c) +#define jffs2_cs_parse_mount_params(a,b) (0) + +#elif defined(CONFIG_JFFS2_CS_MP_SYSFS) +// MP_SYSFS mode specific functions, ... + +#define jffs2_cs_mode() (JFFS2_CS_MODE_MP_SYSFS) + +int jffs2_cs_write_mount_log_mntsys(struct jffs2_sb_info *c); +int jffs2_cs_build_filesystem_mntsys(struct jffs2_sb_info *c); + +#define jffs2_cs_write_mount_log(c) jffs2_cs_write_mount_log_mntsys(c) +#define jffs2_cs_write_umount_log(c) jffs2_cs_write_umount_log_mntsys(c) +#define jffs2_cs_build_filesystem(c) jffs2_cs_build_filesystem_mntsys(c) +#define jffs2_cs_init_cs_block(a) (0) + +#endif // CONFIG_JFFS2_CS_xyz + +#else // CONFIG_JFFS2_CS not defined + +#define jffs2_cs_active() (0) +#define jffs2_cs_mode() (JFFS2_CS_MODE_NONE) +#define jffs2_cs_init(a,b) (0) +#define jffs2_cs_build_filesystem(c) (0) +#define jffs2_cs_reset_sb_info(c) +#define jffs2_cs_write_mount_log(c) (0) +#define jffs2_cs_dump_centsum_data(c) +#define jffs2_cs_save_rawrefs_nii(c) (0) +#define jffs2_cs_restore_rawrefs_nii(c) +#define jffs2_cs_correct_jebinfo_remount(c) +#define jffs2_cs_free(c) +#define jffs2_cs_init_cs_block(c) (0) +#endif + + +#endif // _CENT_SUM_H diff -Narup Mtd-orig/fs/jffs2/debug.h mtd/fs/jffs2/debug.h --- Mtd-orig/fs/jffs2/debug.h 2006-02-03 15:51:22.000000000 +0100 +++ mtd/fs/jffs2/debug.h 2006-02-03 15:48:24.000000000 +0100 @@ -34,11 +34,13 @@ #define JFFS2_DBG_NODEREF_MESSAGES #define JFFS2_DBG_INOCACHE_MESSAGES #define JFFS2_DBG_SUMMARY_MESSAGES +#define JFFS2_DBG_CENT_SUM_MESSAGES #define JFFS2_DBG_FSBUILD_MESSAGES #endif #if CONFIG_JFFS2_FS_DEBUG > 1 #define JFFS2_DBG_FRAGTREE2_MESSAGES +#define JFFS2_DBG_CENT_SUM2_MESSAGES #define JFFS2_DBG_MEMALLOC_MESSAGES #endif @@ -157,6 +159,18 @@ #define dbg_summary(fmt, ...) #endif +/* Centralized Summary debugging messages */ +#ifdef JFFS2_DBG_CENT_SUM_MESSAGES +#define dbg_cs(fmt, ...) JFFS2_DEBUG(fmt, ##__VA_ARGS__) +#else +#define dbg_cs(fmt, ...) +#endif +#ifdef JFFS2_DBG_CENT_SUM2_MESSAGES +#define dbg_cs2(fmt, ...) JFFS2_DEBUG(fmt, ##__VA_ARGS__) +#else +#define dbg_cs2(fmt, ...) +#endif + /* File system build messages */ #ifdef JFFS2_DBG_FSBUILD_MESSAGES #define dbg_fsbuild(fmt, ...) JFFS2_DEBUG(fmt, ##__VA_ARGS__) diff -Narup Mtd-orig/fs/jffs2/erase.c mtd/fs/jffs2/erase.c --- Mtd-orig/fs/jffs2/erase.c 2006-02-03 15:51:23.000000000 +0100 +++ mtd/fs/jffs2/erase.c 2006-02-03 15:48:25.000000000 +0100 @@ -30,7 +30,7 @@ static void jffs2_erase_callback(struct #endif static void jffs2_erase_failed(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, uint32_t bad_offset); static void jffs2_erase_succeeded(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb); -static void jffs2_free_all_node_refs(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb); +void jffs2_free_all_node_refs(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb); static void jffs2_mark_erased_block(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb); static void jffs2_erase_block(struct jffs2_sb_info *c, @@ -284,7 +284,7 @@ static inline void jffs2_remove_node_ref jffs2_del_ino_cache(c, ic); } -static void jffs2_free_all_node_refs(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb) +void jffs2_free_all_node_refs(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb) { struct jffs2_raw_node_ref *ref; D1(printk(KERN_DEBUG "Freeing all node refs for eraseblock offset 0x%08x\n", jeb->offset)); @@ -358,8 +358,8 @@ static void jffs2_mark_erased_block(stru uint32_t bad_offset; switch (jffs2_block_check_erase(c, jeb, &bad_offset)) { - case -EAGAIN: goto refile; - case -EIO: goto filebad; + case -EAGAIN: goto refile; + case -EIO: goto filebad; } /* Write the erase complete marker */ diff -Narup Mtd-orig/fs/jffs2/fs.c mtd/fs/jffs2/fs.c --- Mtd-orig/fs/jffs2/fs.c 2006-02-03 15:51:24.000000000 +0100 +++ mtd/fs/jffs2/fs.c 2006-02-03 15:48:26.000000000 +0100 @@ -340,6 +340,7 @@ void jffs2_dirty_inode(struct inode *ino int jffs2_remount_fs (struct super_block *sb, int *flags, char *data) { + int ret; struct jffs2_sb_info *c = JFFS2_SB_INFO(sb); if (c->flags & JFFS2_SB_FLAG_RO && !(sb->s_flags & MS_RDONLY)) @@ -351,13 +352,33 @@ int jffs2_remount_fs (struct super_block Flush the writebuffer, if neccecary, else we loose it */ if (!(sb->s_flags & MS_RDONLY)) { jffs2_stop_garbage_collect_thread(c); + + if (((jffs2_cs_mode() == JFFS2_CS_MODE_FEB) && c->cs_struct->csblock && (*flags & MS_RDONLY)) || + ((jffs2_cs_mode() == JFFS2_CS_MODE_MP_SYSFS) && (*flags & MS_RDONLY))) { + ret = jffs2_cs_save_rawrefs_nii(c); // save raw->next_in_ino + if (ret) + return ret; + + jffs2_cs_dump_centsum_data(c); + jffs2_cs_correct_jebinfo_remount(c); + + jffs2_cs_restore_rawrefs_nii(c); // load raw->next_in_ino + } + down(&c->alloc_sem); jffs2_flush_wbuf_pad(c); up(&c->alloc_sem); } - if (!(*flags & MS_RDONLY)) + if (!(*flags & MS_RDONLY)) { + if (((jffs2_cs_mode() == JFFS2_CS_MODE_FEB) && c->cs_struct->csblock && (sb->s_flags & MS_RDONLY)) || + ((jffs2_cs_mode() == JFFS2_CS_MODE_MP_SYSFS) && (sb->s_flags & MS_RDONLY))) { + if (jffs2_cs_write_mount_log(c)) { + // error handling ??? + } + } jffs2_start_garbage_collect_thread(c); + } *flags |= MS_NOATIME; @@ -490,6 +511,9 @@ int jffs2_do_fill_super(struct super_blo } memset(c->inocache_list, 0, INOCACHE_HASHSIZE * sizeof(struct jffs2_inode_cache *)); + if ((ret = jffs2_cs_init(c, data))) + goto out_inohash; + if ((ret = jffs2_do_mount_fs(c))) goto out_inohash; diff -Narup Mtd-orig/fs/jffs2/gc.c mtd/fs/jffs2/gc.c --- Mtd-orig/fs/jffs2/gc.c 2006-02-03 15:51:24.000000000 +0100 +++ mtd/fs/jffs2/gc.c 2006-02-03 15:48:26.000000000 +0100 @@ -97,7 +97,8 @@ again: list_del(&ret->list); c->gcblock = ret; ret->gc_node = ret->first_node; - if (!ret->gc_node) { + + if (!jffs2_cs_active() && !ret->gc_node) { printk(KERN_WARNING "Eep. ret->gc_node for block at 0x%08x is NULL\n", ret->offset); BUG(); } @@ -388,12 +389,16 @@ int jffs2_garbage_collect_pass(struct jf eraseit: if (c->gcblock && !c->gcblock->used_size) { - D1(printk(KERN_DEBUG "Block at 0x%08x completely obsoleted by GC. Moving to erase_pending_list\n", c->gcblock->offset)); - /* We're GC'ing an empty block? */ - list_add_tail(&c->gcblock->list, &c->erase_pending_list); - c->gcblock = NULL; - c->nr_erasing_blocks++; - jffs2_erase_pending_trigger(c); + if (jffs2_cs_mode() == JFFS2_CS_MODE_FEB && c->cs_struct->csblock && IS_DURING_MOUNT) { + c->gcblock = NULL; + } else { + D1(printk(KERN_DEBUG "Block at 0x%08x completely obsoleted by GC. Moving to erase_pending_list\n", c->gcblock->offset)); + /* We're GC'ing an empty block? */ + list_add_tail(&c->gcblock->list, &c->erase_pending_list); + c->gcblock = NULL; + c->nr_erasing_blocks++; + jffs2_erase_pending_trigger(c); + } } spin_unlock(&c->erase_completion_lock); diff -Narup Mtd-orig/fs/jffs2/nodelist.h mtd/fs/jffs2/nodelist.h --- Mtd-orig/fs/jffs2/nodelist.h 2006-02-03 15:51:25.000000000 +0100 +++ mtd/fs/jffs2/nodelist.h 2006-02-03 15:48:28.000000000 +0100 @@ -21,6 +21,7 @@ #include #include #include "summary.h" +#include "cent_sum.h" #ifdef __ECOS #include "os-ecos.h" @@ -266,6 +267,7 @@ static inline uint32_t ref_totlen(struct #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 */ +#define ALLOC_CENT_SUM 4 /* For CS */ /* How much dirty space before it goes on the very_dirty_list */ #define VERYDIRTY(c, size) ((size) >= ((c)->sector_size / 2)) diff -Narup Mtd-orig/fs/jffs2/nodemgmt.c mtd/fs/jffs2/nodemgmt.c --- Mtd-orig/fs/jffs2/nodemgmt.c 2006-02-03 15:51:26.000000000 +0100 +++ mtd/fs/jffs2/nodemgmt.c 2006-02-03 15:48:28.000000000 +0100 @@ -59,7 +59,7 @@ int jffs2_reserve_space(struct jffs2_sb_ /* this needs a little more thought (true :)) */ while(ret == -EAGAIN) { - while(c->nr_free_blocks + c->nr_erasing_blocks < blocksneeded) { + while(c->nr_free_blocks + c->nr_erasing_blocks < blocksneeded && prio != ALLOC_CENT_SUM) { int ret; uint32_t dirty, avail; @@ -588,7 +588,9 @@ void jffs2_mark_node_obsolete(struct jff return; } - if (jeb == c->nextblock) { + if (jffs2_cs_mode() == JFFS2_CS_MODE_FEB && c->cs_struct->csblock && IS_DURING_MOUNT) { + //printk("Don't move gcblock/csblock to erase_pending_list\n"); + } else if (jeb == c->nextblock) { D2(printk(KERN_DEBUG "Not moving nextblock 0x%08x to dirty/erase_pending list\n", jeb->offset)); } else if (!jeb->used_size && !jeb->unchecked_size) { if (jeb == c->gcblock) { diff -Narup Mtd-orig/fs/jffs2/scan.c mtd/fs/jffs2/scan.c --- Mtd-orig/fs/jffs2/scan.c 2006-02-03 15:51:28.000000000 +0100 +++ mtd/fs/jffs2/scan.c 2006-02-03 15:48:30.000000000 +0100 @@ -185,10 +185,16 @@ int jffs2_scan_medium(struct jffs2_sb_in /* deleting summary information of the old nextblock */ jffs2_sum_reset_collected(c->summary); } - /* update collected summary infromation for the current nextblock */ - jffs2_sum_move_collected(c, s); - D1(printk(KERN_DEBUG "jffs2_scan_medium(): new nextblock = 0x%08x\n", jeb->offset)); - c->nextblock = jeb; + + if (!(jffs2_cs_mode() == JFFS2_CS_MODE_FEB && c->cs_struct->csblock == jeb)) { + /* update collected summary infromation for the current nextblock */ + jffs2_sum_move_collected(c, s); + D1(printk(KERN_DEBUG "jffs2_scan_medium(): new nextblock = 0x%08x\n", jeb->offset)); + c->nextblock = jeb; + } else { + /*don't let JFFS2 to select the CS block for nextblock */ + c->nextblock = NULL; + } } else { jeb->dirty_size += jeb->free_size + jeb->wasted_size; c->dirty_size += jeb->free_size + jeb->wasted_size; diff -Narup Mtd-orig/fs/jffs2/summary.c mtd/fs/jffs2/summary.c --- Mtd-orig/fs/jffs2/summary.c 2006-02-03 15:51:28.000000000 +0100 +++ mtd/fs/jffs2/summary.c 2006-02-03 15:48:30.000000000 +0100 @@ -331,6 +331,21 @@ int jffs2_sum_add_kvec(struct jffs2_sb_i dbg_summary("node SUMMARY\n"); break; + + case JFFS2_NODETYPE_CENT_SUM_INOCACHE: + case JFFS2_NODETYPE_CENT_SUM_JEBINFO: + case JFFS2_NODETYPE_CENT_SUM_RAWREF: + case JFFS2_NODETYPE_CENT_SUM_LISTS: + dbg_summary("node CENT_SUM data\n"); + c->summary->sum_padded += je32_to_cpu(node->u.totlen); + break; + + case JFFS2_NODETYPE_CENT_SUM_LOG: + dbg_summary("node CENT_SUM log\n"); + if (jffs2_cs_mode() == JFFS2_CS_MODE_MP_SYSFS) + c->summary->sum_padded += je32_to_cpu(node->u.totlen); + break; + default: /* If you implement a new node type you should also implement summary support for it or disable summary. @@ -811,9 +826,10 @@ int jffs2_sum_write_sumnode(struct jffs2 jeb = c->nextblock; - if (!c->summary->sum_num || !c->summary->sum_list_head) { - JFFS2_WARNING("Empty summary info!!!\n"); - BUG(); + if ((!c->summary->sum_num || !c->summary->sum_list_head) && + (!jffs2_cs_active() || !c->summary->sum_padded)) { + JFFS2_WARNING("Empty summary info!!!\n"); + BUG(); } datasize = c->summary->sum_size + sizeof(struct jffs2_sum_marker); @@ -835,28 +851,33 @@ int jffs2_sum_write_sumnode(struct jffs2 if (ret) return 0; /* can't write out summary, block is marked as NOSUM_SIZE */ - /* for ACCT_PARANOIA_CHECK */ - spin_unlock(&c->erase_completion_lock); - summary_ref = jffs2_alloc_raw_node_ref(); - spin_lock(&c->erase_completion_lock); - - if (!summary_ref) { - JFFS2_NOTICE("Failed to allocate node ref for summary\n"); - return -ENOMEM; + if (jffs2_cs_active() && IS_DURING_UMOUNT) { + DECREASE_FREE_SPACE(infosize); } + else { + /* for ACCT_PARANOIA_CHECK */ + spin_unlock(&c->erase_completion_lock); + summary_ref = jffs2_alloc_raw_node_ref(); + spin_lock(&c->erase_completion_lock); + + if (!summary_ref) { + JFFS2_NOTICE("Failed to allocate node ref for summary\n"); + return -ENOMEM; + } - summary_ref->next_in_ino = NULL; - summary_ref->next_phys = NULL; - summary_ref->flash_offset = (jeb->offset + c->sector_size - jeb->free_size) | REF_NORMAL; - summary_ref->__totlen = infosize; + summary_ref->next_in_ino = NULL; + summary_ref->next_phys = NULL; + summary_ref->flash_offset = (jeb->offset + c->sector_size - jeb->free_size) | REF_NORMAL; + summary_ref->__totlen = infosize; + + if (!jeb->first_node) + jeb->first_node = summary_ref; + if (jeb->last_node) + jeb->last_node->next_phys = summary_ref; + jeb->last_node = summary_ref; - if (!jeb->first_node) - jeb->first_node = summary_ref; - if (jeb->last_node) - jeb->last_node->next_phys = summary_ref; - jeb->last_node = summary_ref; - - USED_SPACE(infosize); + USED_SPACE(infosize); + } return 0; } diff -Narup Mtd-orig/fs/jffs2/super-v24.c mtd/fs/jffs2/super-v24.c --- Mtd-orig/fs/jffs2/super-v24.c 2006-02-03 15:51:28.000000000 +0100 +++ mtd/fs/jffs2/super-v24.c 2006-02-03 15:48:31.000000000 +0100 @@ -95,6 +95,14 @@ static void jffs2_put_super (struct supe if (!(sb->s_flags & MS_RDONLY)) jffs2_stop_garbage_collect_thread(c); + + if (((jffs2_cs_mode == JFFS2_CS_MODE_FEB) && c->cs_struct->csblock && !jffs2_is_readonly(c)) || + ((jffs2_cs_mode == JFFS2_CS_MODE_MP_SYSFS) && !jffs2_is_readonly(c))) { + jffs2_cs_dump_centsum_data(c); + } + + jffs2_cs_free(c); + down(&c->alloc_sem); jffs2_flush_wbuf_pad(c); up(&c->alloc_sem); @@ -129,6 +137,9 @@ static int __init init_jffs2_fs(void) #ifdef CONFIG_JFFS2_SUMMARY " (SUMMARY)" #endif +#ifdef CONFIG_JFFS2_CS + " (CENT_SUM)" +#endif " (C) 2001-2003 Red Hat, Inc.\n"); #ifdef JFFS2_OUT_OF_KERNEL diff -Narup Mtd-orig/fs/jffs2/super.c mtd/fs/jffs2/super.c --- Mtd-orig/fs/jffs2/super.c 2006-02-03 15:51:29.000000000 +0100 +++ mtd/fs/jffs2/super.c 2006-02-03 15:48:31.000000000 +0100 @@ -279,6 +279,13 @@ static void jffs2_put_super (struct supe D2(printk(KERN_DEBUG "jffs2: jffs2_put_super()\n")); + if (((jffs2_cs_mode() == JFFS2_CS_MODE_FEB) && c->cs_struct->csblock && !jffs2_is_readonly(c)) || + ((jffs2_cs_mode() == JFFS2_CS_MODE_MP_SYSFS) && !jffs2_is_readonly(c))) { + jffs2_cs_dump_centsum_data(c); + } + + jffs2_cs_free(c); + down(&c->alloc_sem); jffs2_flush_wbuf_pad(c); up(&c->alloc_sem); @@ -327,6 +334,16 @@ static int __init init_jffs2_fs(void) #ifdef CONFIG_JFFS2_SUMMARY " (SUMMARY) " #endif +#ifdef CONFIG_JFFS2_CS + "(CENT_SUM-" +#ifdef CONFIG_JFFS2_CS_FEB + "F" +#endif +#ifdef CONFIG_JFFS2_CS_MP_SYSFS + "M" +#endif + ")" +#endif " (C) 2001-2003 Red Hat, Inc.\n"); jffs2_inode_cachep = kmem_cache_create("jffs2_i", @@ -374,6 +391,10 @@ static void __exit exit_jffs2_fs(void) module_init(init_jffs2_fs); module_exit(exit_jffs2_fs); +unsigned int centsum_offset = 0; +module_param(centsum_offset, uint, 0444); +MODULE_PARM_DESC(centsum_offset, "The offset of the Centralized Summary Log Node"); + MODULE_DESCRIPTION("The Journalling Flash File System, v2"); MODULE_AUTHOR("Red Hat, Inc."); MODULE_LICENSE("GPL"); // Actually dual-licensed, but it doesn't matter for diff -Narup Mtd-orig/fs/jffs2/wbuf.c mtd/fs/jffs2/wbuf.c --- Mtd-orig/fs/jffs2/wbuf.c 2006-02-03 15:51:29.000000000 +0100 +++ mtd/fs/jffs2/wbuf.c 2006-02-03 15:48:32.000000000 +0100 @@ -505,10 +505,15 @@ static int __jffs2_flush_wbuf(struct jff jeb->offset, jeb->free_size); BUG(); } - jeb->free_size -= (c->wbuf_pagesize - c->wbuf_len); - c->free_size -= (c->wbuf_pagesize - c->wbuf_len); - jeb->wasted_size += (c->wbuf_pagesize - c->wbuf_len); - c->wasted_size += (c->wbuf_pagesize - c->wbuf_len); + + if ((jffs2_cs_mode() == JFFS2_CS_MODE_FEB) && (jeb == c->cs_struct->csblock)) { + c->cs_struct->csblock_free -= (c->wbuf_pagesize - c->wbuf_len); + } else { + jeb->free_size -= (c->wbuf_pagesize - c->wbuf_len); + c->free_size -= (c->wbuf_pagesize - c->wbuf_len); + jeb->wasted_size += (c->wbuf_pagesize - c->wbuf_len); + c->wasted_size += (c->wbuf_pagesize - c->wbuf_len); + } } /* Stick any now-obsoleted blocks on the erase_pending_list */ diff -Narup Mtd-orig/include/linux/jffs2.h mtd/include/linux/jffs2.h --- Mtd-orig/include/linux/jffs2.h 2006-02-03 15:51:43.000000000 +0100 +++ mtd/include/linux/jffs2.h 2006-02-03 16:15:29.000000000 +0100 @@ -71,6 +71,12 @@ #define JFFS2_NODETYPE_ERASEBLOCK_HEADER (JFFS2_FEATURE_RWCOMPAT_DELETE | JFFS2_NODE_ACCURATE | 5) #define JFFS2_NODETYPE_SUMMARY (JFFS2_FEATURE_RWCOMPAT_DELETE | JFFS2_NODE_ACCURATE | 6) +#define JFFS2_NODETYPE_CENT_SUM_LOG (JFFS2_FEATURE_RWCOMPAT_DELETE | JFFS2_NODE_ACCURATE | 7) +#define JFFS2_NODETYPE_CENT_SUM_INOCACHE (JFFS2_FEATURE_RWCOMPAT_DELETE | JFFS2_NODE_ACCURATE | 8) +#define JFFS2_NODETYPE_CENT_SUM_JEBINFO (JFFS2_FEATURE_RWCOMPAT_DELETE | JFFS2_NODE_ACCURATE | 9) +#define JFFS2_NODETYPE_CENT_SUM_RAWREF (JFFS2_FEATURE_RWCOMPAT_DELETE | JFFS2_NODE_ACCURATE | 10) +#define JFFS2_NODETYPE_CENT_SUM_LISTS (JFFS2_FEATURE_RWCOMPAT_DELETE | JFFS2_NODE_ACCURATE | 11) + // Maybe later... //#define JFFS2_NODETYPE_CHECKPOINT (JFFS2_FEATURE_RWCOMPAT_DELETE | JFFS2_NODE_ACCURATE | 3) //#define JFFS2_NODETYPE_OPTIONS (JFFS2_FEATURE_RWCOMPAT_COPY | JFFS2_NODE_ACCURATE | 4) @@ -186,6 +192,67 @@ struct jffs2_raw_ebh jint32_t data[0]; } __attribute__((packed)); +struct jffs2_raw_cs_log +{ + jint16_t magic; + jint16_t nodetype; /* = JFFS2_NODETYPE_CENT_SUM_LOG */ + jint32_t totlen; + jint32_t hdr_crc; + uint8_t valid; /* 0 = mount log, 1 = umount log*/ + uint8_t reserved; + jint16_t ofs_num; /* number of jeb offsets */ + jint32_t version; /* CS log version*/ + jint32_t erase_size; /* erase block size */ + jint32_t ofs_crc; /* jebs crc */ + jint32_t node_crc; /* node crc */ + jint32_t ofs[0]; /* jeb offsets, which contains centralized summary information */ +} __attribute__((packed)); + +struct jffs2_raw_cs_inocache +{ + jint16_t magic; + jint16_t nodetype; /* = JFFS2_NODETYPE_CENT_SUM_INOCACHE */ + jint32_t totlen; + jint32_t hdr_crc; + jint32_t cache_num; /* number of cache entries */ + jint32_t cache_crc; /* cache information crc */ + jint32_t node_crc; /* node crc */ + jint32_t cache[0]; /* cache info */ +} __attribute__((packed)); + +struct jffs2_raw_cs_jebinfo +{ + jint16_t magic; + jint16_t nodetype; /* = JFFS2_NODETYPE_CENT_SUM_JEBINFO */ + jint32_t totlen; + jint32_t hdr_crc; + jint32_t jebinfo_num; /* number of jeb entries */ + jint32_t jebinfo_crc; /* jeb crc */ + jint32_t node_crc; /* node crc */ + jint32_t jebinfo[0]; /* jeb info */ +} __attribute__((packed)); + +struct jffs2_raw_cs_rawref +{ + jint16_t magic; + jint16_t nodetype; /* = JFFS2_NODETYPE_CENT_SUM_RAWREF */ + jint32_t totlen; + jint32_t hdr_crc; + jint32_t rawref_num; /* number of jeb entries */ + jint32_t rawref_crc; /* rawrefs crc */ + jint32_t node_crc; /* node crc */ + jint32_t rawrefs[0]; /* rawref info */ +} __attribute__((packed)); + +struct jffs2_raw_cs_lists +{ + jint16_t magic; + jint16_t nodetype; /* = JFFS2_NODETYPE_CENT_SUM_LISTS */ + jint32_t totlen; + jint32_t hdr_crc; + jint32_t lists_crc; /* lists crc */ + jint16_t lists[0]; /* list info */ +} __attribute__((packed)); union jffs2_node_union { @@ -194,6 +261,11 @@ union jffs2_node_union struct jffs2_raw_summary s; struct jffs2_raw_ebh eh; struct jffs2_unknown_node u; + struct jffs2_raw_cs_log l; + struct jffs2_raw_cs_inocache ic; + struct jffs2_raw_cs_jebinfo j; + struct jffs2_raw_cs_rawref r; + struct jffs2_raw_cs_lists li; }; #endif /* __LINUX_JFFS2_H__ */ diff -Narup Mtd-orig/include/linux/jffs2_fs_sb.h mtd/include/linux/jffs2_fs_sb.h --- Mtd-orig/include/linux/jffs2_fs_sb.h 2006-02-03 15:51:43.000000000 +0100 +++ mtd/include/linux/jffs2_fs_sb.h 2006-02-03 15:48:46.000000000 +0100 @@ -114,6 +114,7 @@ struct jffs2_sb_info { #endif struct jffs2_summary *summary; /* Summary information */ + struct cent_sum *cs_struct; /* using for centralized summary */ uint32_t ebh_size; /* This is the space size occupied by eraseblock_header on flash */ diff -Narup Mtd-orig/util/cent_sum.h mtd/util/cent_sum.h --- Mtd-orig/util/cent_sum.h 1970-01-01 01:00:00.000000000 +0100 +++ mtd/util/cent_sum.h 2006-02-03 17:15:06.000000000 +0100 @@ -0,0 +1,89 @@ +/* + * JFFS2 -- Journalling Flash File System, Version 2. + * + * Copyright (C) 2005 Zoltan Sogor , + * Ferenc Havasi , + * Patrik Kluba , + * University of Szeged, Hungary + * + * For licensing information, see the file 'LICENCE' in this directory. + * + * $Id$ + * + */ + +#ifndef _CENT_SUM_H +#define _CENT_SUM_H + +#include + +#define JFFS2_CS_VERSION 1 +#define CENT_SUM_RETRY_EMPTY 3 +#define CENT_SUM_BUFFER_SIZE 128*1024 //kmalloc can't allocate more than 128k + +#define JFFS2_INOCACHE_ENTRY_SIZE (sizeof(struct jffs2_cs_inocache)) +#define JFFS2_MIN_INOCACHE_NUM 10 +#define JFFS2_JEBINFO_ENTRY_SIZE (sizeof(struct jffs2_cs_jeb)) +#define JFFS2_MIN_JEBINFO_NUM 5 +#define JFFS2_RAWREF_ENTRY_SIZE (sizeof(struct jffs2_cs_rawref)) +#define JFFS2_MIN_RAWREF_NUM 10 + +#define JFFS2_CS_DURING_MOUNT 1 +#define JFFS2_CS_DURING_UNMOUNT 2 +#define JFFS2_CS_CSBLOCK_VALID 4 +#define JFFS2_CS_MOUNT_OFFSET_PARAM 8 // for Mount param / Sysfs support + +#define IS_DURING_MOUNT (c->cs_struct->cs_flags & JFFS2_CS_DURING_MOUNT) +#define IS_DURING_UMOUNT (c->cs_struct->cs_flags & JFFS2_CS_DURING_UNMOUNT) +#define IS_CSBLOCK_VALID (c->cs_struct->cs_flags & JFFS2_CS_CSBLOCK_VALID) +#define HAS_MOUNT_OFFSET_PARAM (c->cs_struct->cs_flags & JFFS2_CS_MOUNT_OFFSET_PARAM) + +#define DECREASE_FREE_SPACE(x) do { typeof(x) _x = (x); \ + c->free_size -= _x; \ + jeb->free_size -= _x; \ + } while(0) + +#define INCREASE_WASTED_SPACE(x) do { typeof(x) _x = (x); \ + c->wasted_size += _x; \ + jeb->wasted_size += _x; \ + } while(0) + +struct jffs2_eraseblock; + +struct cent_sum_offset { + struct cent_sum_offset *next; + uint32_t offset; + uint32_t length; +}; + +struct cent_sum { + struct jffs2_eraseblock *csblock; + uint32_t csblock_free; + uint32_t cs_log_offset; // for mount param / sysfs support + unsigned char cs_flags; + struct cent_sum_offset *offset_list_tail; + struct cent_sum_offset *offset_list_head; + uint32_t offset_list_num; +}; + +struct jffs2_cs_inocache { + jint32_t ino; + jint32_t nlink; + jint32_t state; +} __attribute__((packed)); + +struct jffs2_cs_jeb { + jint32_t offset; + jint32_t unchecked_size; + jint32_t used_size; + jint32_t dirty_size; + jint32_t wasted_size; +} __attribute__((packed)); + +struct jffs2_cs_rawref { + jint32_t flash_offset; + jint32_t totlen; + jint32_t ino; +} __attribute__((packed)); + +#endif // _CENT_SUM_H diff -Narup Mtd-orig/util/jffs2dump.c mtd/util/jffs2dump.c --- Mtd-orig/util/jffs2dump.c 2006-02-03 15:50:56.000000000 +0100 +++ mtd/util/jffs2dump.c 2006-02-03 17:04:45.000000000 +0100 @@ -35,6 +35,7 @@ #include #include "crc32.h" #include "summary.h" +#include "cent_sum.h" #define PROGRAM "jffs2dump" #define VERSION "$Revision: 1.12 $" @@ -413,6 +414,236 @@ void do_dumpcontent (void) p += PAD(je32_to_cpu (node->u.totlen)); break; + + case JFFS2_NODETYPE_CENT_SUM_LOG: + printf("%8s CS %s node at 0x%08x, totlen 0x%08x, version %d\n","", node->l.valid ? "UNMNTLOG" : "MNTLOG", + p - data, je32_to_cpu (node->u.totlen), je32_to_cpu(node->l.version)); + + crc = crc32 (0, node, sizeof (struct jffs2_raw_cs_log) - 8); + if (crc != je32_to_cpu (node->l.node_crc)) { + printf ("Wrong node_crc at 0x%08x, 0x%08x instead of 0x%08x\n", p - data, je32_to_cpu (node->l.node_crc), crc); + p += PAD(je32_to_cpu (node->l.totlen)); + dirty += PAD(je32_to_cpu (node->l.totlen));; + continue; + } + + if (node->l.valid) { + crc = crc32(0, p + sizeof (struct jffs2_raw_cs_log), je32_to_cpu (node->l.totlen) - sizeof(struct jffs2_raw_cs_log)); + if (crc != je32_to_cpu(node->l.ofs_crc)) { + printf ("Wrong data_crc at 0x%08x, 0x%08x instead of 0x%08x\n", p - data, je32_to_cpu (node->l.ofs_crc), crc); + p += PAD(je32_to_cpu (node->l.totlen)); + dirty += PAD(je32_to_cpu (node->l.totlen));; + continue; + } + + if (verbose) { + int i; + jint32_t *ptr = NULL; + + ptr = (jint32_t *) node->l.ofs; + for (i = 0; i < je16_to_cpu(node->l.ofs_num); i++) { + printf("%12s CENT_SUM data node at offset 0x%08x\n","", je32_to_cpu (*ptr)); + ptr++; + } + } + } + p += PAD(je32_to_cpu (node->u.totlen)); + break; + + case JFFS2_NODETYPE_CENT_SUM_INOCACHE: + printf("%8s CS INOCACHE node at 0x%08x, totlen 0x%08x\n","",p - data, je32_to_cpu (node->u.totlen)); + + crc = crc32 (0, node, sizeof (struct jffs2_raw_cs_inocache) - 8); + if (crc != je32_to_cpu (node->ic.node_crc)) { + printf ("Wrong node_crc at 0x%08x, 0x%08x instead of 0x%08x\n", p - data, je32_to_cpu (node->ic.node_crc), crc); + p += PAD(je32_to_cpu (node->ic.totlen)); + dirty += PAD(je32_to_cpu (node->ic.totlen));; + continue; + } + + crc = crc32(0, p + sizeof (struct jffs2_raw_cs_inocache), je32_to_cpu (node->ic.cache_num) * JFFS2_INOCACHE_ENTRY_SIZE); + if (crc != je32_to_cpu(node->ic.cache_crc)) { + printf ("Wrong data_crc at 0x%08x, 0x%08x instead of 0x%08x\n", p - data, je32_to_cpu (node->ic.cache_crc), crc); + p += PAD(je32_to_cpu (node->ic.totlen)); + dirty += PAD(je32_to_cpu (node->ic.totlen));; + continue; + } + + if (verbose) { + int i; + struct jffs2_cs_inocache *ptr; + + ptr = (struct jffs2_cs_inocache *) node->ic.cache; + for (i = 0; i < je32_to_cpu(node->ic.cache_num); i++) { + printf("%12s ino #%d, nlink %d, state %s\n", "", + je32_to_cpu(ptr->ino), + je32_to_cpu(ptr->nlink), + (je32_to_cpu(ptr->state) ? "INO_STATE_CHECKEDABSENT" : "INO_STATE_UNCHECKED")); + ptr++; + } + } + + p += PAD(je32_to_cpu (node->u.totlen)); + break; + + case JFFS2_NODETYPE_CENT_SUM_JEBINFO: + printf("%8s CS JEBINFO node at 0x%08x, totlen 0x%08x\n","",p - data, je32_to_cpu (node->u.totlen)); + + crc = crc32 (0, node, sizeof (struct jffs2_raw_cs_jebinfo) - 8); + if (crc != je32_to_cpu (node->j.node_crc)) { + printf ("Wrong node_crc at 0x%08x, 0x%08x instead of 0x%08x\n", p - data, je32_to_cpu (node->j.node_crc), crc); + p += PAD(je32_to_cpu (node->j.totlen)); + dirty += PAD(je32_to_cpu (node->j.totlen));; + continue; + } + + crc = crc32(0, p + sizeof (struct jffs2_raw_cs_jebinfo), je32_to_cpu (node->j.jebinfo_num) * JFFS2_JEBINFO_ENTRY_SIZE); + if (crc != je32_to_cpu(node->j.jebinfo_crc)) { + printf ("Wrong data_crc at 0x%08x, 0x%08x instead of 0x%08x\n", p - data, je32_to_cpu (node->j.jebinfo_crc), crc); + p += PAD(je32_to_cpu (node->j.totlen)); + dirty += PAD(je32_to_cpu (node->j.totlen));; + continue; + } + + if (verbose) { + int i; + struct jffs2_cs_jeb *ptr; + + ptr = (struct jffs2_cs_jeb *) node->j.jebinfo; + for (i = 0; i < je32_to_cpu(node->j.jebinfo_num); i++) { + printf("%12s offset 0x%08x, unchecked 0x%08x, used 0x%08x, dirty 0x%08x, wasted 0x%08x\n", "", + je32_to_cpu(ptr->offset), + je32_to_cpu(ptr->unchecked_size), + je32_to_cpu(ptr->used_size), + je32_to_cpu(ptr->dirty_size), + je32_to_cpu(ptr->wasted_size)); + ptr++; + } + } + + p += PAD(je32_to_cpu (node->u.totlen)); + break; + + case JFFS2_NODETYPE_CENT_SUM_RAWREF: + printf("%8s CS RAWREF node at 0x%08x, totlen 0x%08x\n","",p - data, je32_to_cpu (node->u.totlen)); + + crc = crc32 (0, node, sizeof (struct jffs2_raw_cs_rawref) - 8); + if (crc != je32_to_cpu (node->r.node_crc)) { + printf ("Wrong node_crc at 0x%08x, 0x%08x instead of 0x%08x\n", p - data, je32_to_cpu (node->r.node_crc), crc); + p += PAD(je32_to_cpu (node->r.totlen)); + dirty += PAD(je32_to_cpu (node->r.totlen));; + continue; + } + + crc = crc32(0, p + sizeof (struct jffs2_raw_cs_rawref), je32_to_cpu (node->r.rawref_num) * JFFS2_RAWREF_ENTRY_SIZE); + if (crc != je32_to_cpu(node->r.rawref_crc)) { + printf ("Wrong data_crc at 0x%08x, 0x%08x instead of 0x%08x\n", p - data, je32_to_cpu (node->r.rawref_crc), crc); + p += PAD(je32_to_cpu (node->r.totlen)); + dirty += PAD(je32_to_cpu (node->r.totlen));; + continue; + } + + if (verbose) { + int i; + struct jffs2_cs_rawref *ptr; + + ptr = (struct jffs2_cs_rawref *) node->r.rawrefs; + for (i = 0; i < je32_to_cpu(node->r.rawref_num); i++) { + printf("%12s flash_offset 0x%08x, __totlen 0x%08x, ic #%d\n", "", + je32_to_cpu(ptr->flash_offset), + je32_to_cpu(ptr->totlen), + je32_to_cpu(ptr->ino)); + ptr++; + } + } + + p += PAD(je32_to_cpu (node->u.totlen)); + break; + + case JFFS2_NODETYPE_CENT_SUM_LISTS: + printf("%8s CS LISTS node at 0x%08x, totlen 0x%08x\n","",p - data, je32_to_cpu (node->u.totlen)); + + crc = crc32(0, p + sizeof (struct jffs2_raw_cs_lists), je32_to_cpu (node->li.totlen) - sizeof (struct jffs2_raw_cs_lists)); + if (crc != je32_to_cpu(node->li.lists_crc)) { + printf ("Wrong data_crc at 0x%08x, 0x%08x instead of 0x%08x\n", p - data, je32_to_cpu (node->li.lists_crc), crc); + p += PAD(je32_to_cpu (node->li.totlen)); + dirty += PAD(je32_to_cpu (node->li.totlen));; + continue; + } + + if (verbose) { + int i,num; + jint16_t *ptr; + ptr = node->li.lists; + + num = je16_to_cpu(*ptr); + ptr++; + printf("%12s Clean list count: %d, members: \n%12s","",num,""); + + for(i = 0; i < num; i++) { + printf(" %d",je16_to_cpu(*ptr)); + ptr++; + } + + num = je16_to_cpu(*ptr); + ptr++; + printf("\n%12s Very dirty list count: %d, members: \n%12s","",num,""); + + for(i = 0; i < num; i++) { + printf(" %d",je16_to_cpu(*ptr)); + ptr++; + } + + num = je16_to_cpu(*ptr); + ptr++; + printf("\n%12s Dirty list count: %d, members: \n%12s","",num,""); + + for(i = 0; i < num; i++) { + printf(" %d",je16_to_cpu(*ptr)); + ptr++; + } + num = je16_to_cpu(*ptr); + ptr++; + printf("\n%12s Erasable list count: %d, members: \n%12s","",num,""); + + for(i = 0; i < num; i++) { + printf(" %d",je16_to_cpu(*ptr)); + ptr++; + } + + num = je16_to_cpu(*ptr); + ptr++; + printf("\n%12s Free list count: %d, members: \n%12s","",num,""); + + for(i = 0; i < num; i++) { + printf(" %d",je16_to_cpu(*ptr)); + ptr++; + } + + num = je16_to_cpu(*ptr); + ptr++; + printf("\n%12s Bad list count: %d, members: \n%12s","",num,""); + + for(i = 0; i < num; i++) { + printf(" %d",je16_to_cpu(*ptr)); + ptr++; + } + + num = je16_to_cpu(*ptr); + ptr++; + printf("\n%12s Bad used list count: %d, members: \n%12s","",num,""); + + for(i = 0; i < num; i++) { + printf(" %d",je16_to_cpu(*ptr)); + ptr++; + } + printf("\n"); + } + + p += PAD(je32_to_cpu (node->u.totlen)); + break; + + case 0xffff: p += 4; empty += 4; @@ -434,6 +665,21 @@ void do_dumpcontent (void) printf ("Empty space: %d, dirty space: %d\n", empty, dirty); } +void conv_lists(jint16_t *jptr) { + int i, j, num; + + for (j = 0 ; j < 7; j++) { + num = je16_to_cpu((*jptr)); + *jptr = cnv_e16((*jptr)); + jptr++; + + for (i = 0; i < num; i++) { + *jptr = cnv_e16((*jptr)); + jptr++; + } + } +} + /* * Convert endianess */ @@ -648,6 +894,177 @@ void do_endianconvert (void) p += PAD(je32_to_cpu (node->eh.totlen)); break; + case JFFS2_NODETYPE_CENT_SUM_LOG: { + int i, len; + jint32_t *jptr; + + newnode.l.magic = cnv_e16 (node->l.magic); + newnode.l.nodetype = cnv_e16 (node->l.nodetype); + newnode.l.totlen = cnv_e32 (node->l.totlen); + newnode.l.hdr_crc = cpu_to_e32 (crc32 (0, &newnode, sizeof (struct jffs2_unknown_node) - 4)); + newnode.l.valid = node->l.valid; + newnode.l.reserved = node->l.reserved; + newnode.l.ofs_num = cnv_e16 (node->l.ofs_num); + newnode.l.version = cnv_e32 (node->l.version); + newnode.l.erase_size = cnv_e32 (node->l.erase_size); + newnode.l.node_crc = cpu_to_e32 (crc32 (0, &newnode, sizeof (struct jffs2_raw_cs_log) - 8)); + + len = je32_to_cpu (node->l.totlen) - sizeof (struct jffs2_raw_cs_log); + + if ( node->l.valid == 1) { + + // cent_sum_log header + p += sizeof (struct jffs2_raw_cs_log); + jptr = (jint32_t *) p; + + for (i = 0; i < je16_to_cpu(node->l.ofs_num); i++) { + *jptr = cnv_e32((*jptr)); + jptr++; + } + + // generate new crc on cent_sum log ofs + newnode.l.ofs_crc = cpu_to_e32 ( crc32(0, ((char *) node) + sizeof (struct jffs2_raw_cs_log), len)); + + p += len; + // write out new node header + write(fd, &newnode, sizeof (struct jffs2_raw_cs_log)); + // write out new summary data + write(fd, &node->l.ofs, len); + } else { + + // cent_sum_log header + p += sizeof (struct jffs2_raw_cs_log); + + newnode.l.ofs_crc = cpu_to_je32(0); + + // write out new node header + write(fd, &newnode, sizeof (struct jffs2_raw_cs_log)); + write(fd, p + sizeof(struct jffs2_raw_cs_log), len); + + p += len; + } + + break; + } + + case JFFS2_NODETYPE_CENT_SUM_INOCACHE: { + int i, len; + struct jffs2_cs_inocache *jptr; + + newnode.ic.magic = cnv_e16 (node->l.magic); + newnode.ic.nodetype = cnv_e16 (node->l.nodetype); + newnode.ic.totlen = cnv_e32 (node->l.totlen); + newnode.ic.hdr_crc = cpu_to_e32 (crc32 (0, &newnode, sizeof (struct jffs2_unknown_node) - 4)); + newnode.ic.cache_num = cnv_e32 (node->ic.cache_num); + newnode.ic.node_crc = cpu_to_e32 (crc32 (0, &newnode, sizeof (struct jffs2_raw_cs_inocache) - 8)); + + len = je32_to_cpu(node->ic.totlen) - sizeof (struct jffs2_raw_cs_inocache); + + jptr = (struct jffs2_cs_inocache *) (p + sizeof (struct jffs2_raw_cs_inocache)); + + for (i = 0; i < je32_to_cpu(node->ic.cache_num); i++) { + jptr->ino = cnv_e32(jptr->ino); + jptr->nlink = cnv_e32(jptr->nlink); + jptr->state = cnv_e32(jptr->state); + jptr++; + } + + newnode.ic.cache_crc = cpu_to_e32 (crc32 (0, (char *)node + sizeof (struct jffs2_raw_cs_inocache), len)); + + write(fd, &newnode, sizeof (struct jffs2_raw_cs_inocache)); + write(fd, p + sizeof(struct jffs2_raw_cs_inocache), len); + + p += PAD(je32_to_cpu (node->u.totlen)); + break; + } + + case JFFS2_NODETYPE_CENT_SUM_JEBINFO: { + int i, len; + struct jffs2_cs_jeb *jptr; + + newnode.j.magic = cnv_e16 (node->j.magic); + newnode.j.nodetype = cnv_e16 (node->j.nodetype); + newnode.j.totlen = cnv_e32 (node->j.totlen); + newnode.j.hdr_crc = cpu_to_e32 (crc32 (0, &newnode, sizeof (struct jffs2_unknown_node) - 4)); + newnode.j.jebinfo_num = cnv_e32 (node->j.jebinfo_num); + newnode.j.node_crc = cpu_to_e32 (crc32 (0, &newnode, sizeof (struct jffs2_raw_cs_jebinfo) - 8)); + + len = je32_to_cpu(node->j.totlen) - sizeof (struct jffs2_raw_cs_jebinfo); + + jptr = (struct jffs2_cs_jeb *) (p + sizeof (struct jffs2_raw_cs_jebinfo)); + + for (i = 0; i < je32_to_cpu(node->j.jebinfo_num); i++) { + jptr->offset = cnv_e32(jptr->offset); + jptr->unchecked_size = cnv_e32(jptr->unchecked_size); + jptr->used_size = cnv_e32(jptr->used_size); + jptr->dirty_size = cnv_e32(jptr->dirty_size); + jptr->wasted_size = cnv_e32(jptr->wasted_size); + jptr++; + } + + newnode.j.jebinfo_crc = cpu_to_e32 (crc32 (0, (char *)node + sizeof (struct jffs2_raw_cs_jebinfo), len)); + + write(fd, &newnode, sizeof (struct jffs2_raw_cs_jebinfo)); + write(fd, p + sizeof(struct jffs2_raw_cs_jebinfo), len); + + p += PAD(je32_to_cpu (node->u.totlen)); + break; + } + + case JFFS2_NODETYPE_CENT_SUM_RAWREF: { + int i, len; + struct jffs2_cs_rawref *jptr; + + newnode.r.magic = cnv_e16 (node->r.magic); + newnode.r.nodetype = cnv_e16 (node->r.nodetype); + newnode.r.totlen = cnv_e32 (node->r.totlen); + newnode.r.hdr_crc = cpu_to_e32 (crc32 (0, &newnode, sizeof (struct jffs2_unknown_node) - 4)); + newnode.r.rawref_num = cnv_e32 (node->r.rawref_num); + newnode.r.node_crc = cpu_to_e32 (crc32 (0, &newnode, sizeof (struct jffs2_raw_cs_rawref) - 8)); + + len = je32_to_cpu(node->r.totlen) - sizeof (struct jffs2_raw_cs_rawref); + + jptr = (struct jffs2_cs_rawref *) (p + sizeof (struct jffs2_raw_cs_rawref)); + + for (i = 0; i < je32_to_cpu(node->r.rawref_num); i++) { + jptr->flash_offset = cnv_e32(jptr->flash_offset); + jptr->totlen = cnv_e32(jptr->totlen); + jptr->ino = cnv_e32(jptr->ino); + jptr++; + } + + newnode.r.rawref_crc = cpu_to_e32 (crc32 (0, (char *)node + sizeof (struct jffs2_raw_cs_rawref), len)); + + write(fd, &newnode, sizeof (struct jffs2_raw_cs_rawref)); + write(fd, p + sizeof(struct jffs2_raw_cs_rawref), len); + + p += PAD(je32_to_cpu (node->u.totlen)); + break; + } + + case JFFS2_NODETYPE_CENT_SUM_LISTS: { + jint16_t *jptr; + + newnode.li.magic = cnv_e16 (node->li.magic); + newnode.li.nodetype = cnv_e16 (node->li.nodetype); + newnode.li.totlen = cnv_e32 (node->li.totlen); + newnode.li.hdr_crc = cpu_to_e32 (crc32 (0, &newnode, sizeof (struct jffs2_unknown_node) - 4)); + + len = je32_to_cpu(node->li.totlen) - sizeof (struct jffs2_raw_cs_lists); + + jptr = (jint16_t *) (p + sizeof (struct jffs2_raw_cs_lists)); + + conv_lists(jptr); + + newnode.li.lists_crc = cpu_to_e32 (crc32 (0, (char *)node + sizeof (struct jffs2_raw_cs_lists), len)); + + write(fd, &newnode, sizeof (struct jffs2_raw_cs_lists)); + write(fd, p + sizeof(struct jffs2_raw_cs_lists), len); + + p += PAD(je32_to_cpu (node->u.totlen)); + break; + } + case 0xffff: write (fd, p, 4); p += 4;