/*
* JFFS2 -- Journalling Flash File System, Version 2.
*
* Copyright (C) 2001-2003 Red Hat, Inc.
*
* Created by David Woodhouse <dwmw2@infradead.org>
*
* For licensing information, see the file 'LICENCE' in this directory.
*
* $Id: scan.c,v 1.125 2005/09/30 13:59:13 dedekind Exp $
*
*/
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/mtd/mtd.h>
#include <linux/pagemap.h>
#include <linux/crc32.h>
#include <linux/compiler.h>
#include "nodelist.h"
#include "summary.h"
#include "debug.h"
#define DEFAULT_EMPTY_SCAN_SIZE 1024
#define noisy_printk(noise, args...) do { \
if (*(noise)) { \
printk(KERN_NOTICE args); \
(*(noise))--; \
if (!(*(noise))) { \
printk(KERN_NOTICE "Further such events for this erase block will not be printed\n"); \
} \
} \
} while(0)
static uint32_t pseudo_random;
static int jffs2_scan_eraseblock (struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb,
unsigned char *buf, uint32_t buf_size, struct jffs2_summary *s);
/* These helper functions _must_ increase ofs and also do the dirty/used space accounting.
* Returning an error will abort the mount - bad checksums etc. should just mark the space
* as dirty.
*/
static int jffs2_scan_inode_node(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb,
struct jffs2_raw_inode *ri, uint32_t ofs, struct jffs2_summary *s);
static int jffs2_scan_dirent_node(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb,
struct jffs2_raw_dirent *rd, uint32_t ofs, struct jffs2_summary *s);
static inline int min_free(struct jffs2_sb_info *c)
{
uint32_t min = 2 * sizeof(struct jffs2_raw_inode);
#ifdef CONFIG_JFFS2_FS_WRITEBUFFER
if (!jffs2_can_mark_obsolete(c) && min < c->wbuf_pagesize)
return c->wbuf_pagesize;
#endif
return min;
}
static inline uint32_t EMPTY_SCAN_SIZE(uint32_t sector_size) {
if (sector_size < DEFAULT_EMPTY_SCAN_SIZE)
return sector_size;
else
return DEFAULT_EMPTY_SCAN_SIZE;
}
int jffs2_scan_medium(struct jffs2_sb_info *c)
{
int i, ret;
uint32_t empty_blocks = 0, bad_blocks = 0;
unsigned char *flashbuf = NULL;
uint32_t buf_size = 0;
struct jffs2_summary *s = NULL; /* summary info collected by the scan process */
#ifndef __ECOS
size_t pointlen;
if (c->mtd->point) {
ret = c->mtd->point (c->mtd, 0, c->mtd->size, &pointlen, &flashbuf);
if (!ret && pointlen < c->mtd->size) {
/* Don't muck about if it won't let us point to the whole flash */
D1(printk(KERN_DEBUG "MTD point returned len too short: 0x%zx\n", pointlen));
c->mtd->unpoint(c->mtd, flashbuf, 0, c->mtd->size);
flashbuf = NULL;
}
if (ret)
D1(printk(KERN_DEBUG "MTD point failed %d\n", ret));
}
#endif
if (!flashbuf) {
/* For NAND it's quicker to read a whole eraseblock at a time,
apparently */
if (jffs2_cleanmarker_oob(c))
buf_size = c->sector_size;
else
buf_size = PAGE_SIZE;
/* Respect kmalloc limitations */
if (buf_size > 128*1024)
buf_size = 128*1024;
D1(printk(KERN_DEBUG "Allocating readbuf of %d bytes\n", buf_size));
flashbuf = kmalloc(buf_size, GFP_KERNEL);
if (!flashbuf)
return -ENOMEM;
}
if (jffs2_sum_active()) {
s = kmalloc(sizeof(struct jffs2_summary), GFP_KERNEL);
if (!s) {
JFFS2_WARNING("Can't allocate memory for summary\n");
return -ENOMEM;
}
memset(s, 0, sizeof(struct jffs2_summary));
}
for (i=0; i<c->nr_blocks; i++) {
struct jffs2_eraseblock *jeb = &c->blocks[i];
/* reset summary info for next eraseblock scan */
jffs2_sum_reset_collected(s);
ret = jffs2_scan_eraseblock(c, jeb, buf_size?flashbuf:(flashbuf+jeb->offset),
buf_size, s);
if (ret < 0)
goto out;
jffs2_dbg_acct_paranoia_check_nolock(c, jeb);
/* Now decide which list to put it on */
switch(ret) {
case BLK_STATE_ALLFF:
/*
* Empty block. Since we can't be sure it
* was entirely erased, we just queue it for erase
* again. It will be marked as such when the erase
* is complete. Meanwhile we still count it as empty
* for later checks.
*/
empty_blocks++;
list_add(&jeb->list, &c->erase_pending_list);
c->nr_erasing_blocks++;
break;
case BLK_STATE_CLEANMARKER:
/* Only a CLEANMARKER node is valid */
if (!jeb->dirty_size) {
/* It's actually free */
list_add(&jeb->list, &c->free_list);
c->nr_free_blocks++;
} else {
/* Dirt */
D1(printk(KERN_DE