Beispiel #1
0
static unsigned int ParseAttributes(unsigned char *buff, unsigned int offset, ssize_t len)
{
    unsigned char tag;
    unsigned int name_length, value_length;

    while (offset < len) {
        tag = buff[offset];
        if (TAG_TYPE(tag) == TAG_TYPE_DELIMITER) {
                
            offset++;
            if (tag == TAG_END_OF_ATTRIBUTES) {
                /*
                 * No more attributes.
                 */
                break;
            }
        } else {
            /*
             * Value tag - get the name length.
             */
            name_length = ntohs(*((unsigned short *)(buff + offset + 1)));
                
            /*
             * OK, get the value length.
             */
            value_length = ntohs(*((unsigned short *)(buff + offset + 1 + 2 + name_length)));

                
            switch (TAG_TYPE(tag)) {
            case TAG_TYPE_INTEGER:
                break;
                    
            case TAG_TYPE_OCTETSTRING:
                break;
                    
            case TAG_TYPE_CHARSTRING:
                break;
            }
            offset += 1 + 2 + name_length + 2 + value_length;
        }
    }
    if (offset >= len) {
        LogPrintf(LV_ERROR, "Buffer to small");
    }

    return offset;
}
Beispiel #2
0
/** 
 * \brief calculate data length of a file block
 * \param[in] dev uffs device
 * \param[in] bc block info
 */
int uffs_GetBlockFileDataLength(uffs_Device *dev, uffs_BlockInfo *bc, u8 type)
{
	u16 page_id;
	u16 i;
	uffs_Tags *tag;
	int size = 0;
	u16 page;
	u16 lastPage = dev->attr->pages_per_block - 1;

	uffs_BlockInfoLoad(dev, bc, lastPage);
	tag = GET_TAG(bc, lastPage);

	if (TAG_IS_GOOD(tag) && TAG_PAGE_ID(tag) == lastPage) {
		// First try the last page.
		// if it's the full loaded file/data block, then we have a quick path.
		if (type == UFFS_TYPE_FILE) {
			size = dev->com.pg_data_size * (dev->attr->pages_per_block - 2) + TAG_DATA_LEN(tag);
			return size;
		}
		if (type == UFFS_TYPE_DATA) {
			size = dev->com.pg_data_size * (dev->attr->pages_per_block - 1) + TAG_DATA_LEN(tag);
			return size;
		}
	}

	// ok, it's not the full loaded file/data block,
	// need to read all spares....
	uffs_BlockInfoLoad(dev, bc, UFFS_ALL_PAGES);
	tag = GET_TAG(bc, 0);
	if (uffs_Assert(TAG_IS_GOOD(tag), "block %d page 0 does not have good tag ?", bc->block)) {
		if (TAG_TYPE(tag) == UFFS_TYPE_FILE) {
			page_id = 1;	//In file header block, file data page_id from 1
			i = 1;			//search from page 1
		}
		else {
			page_id = 0;	//in normal file data block, page_id from 0
			i = 0;			//in normal file data block, search from page 0
		}
		for (; i < dev->attr->pages_per_block; i++) {
			tag = GET_TAG(bc, i);
			if (TAG_IS_GOOD(tag)) {
				if (page_id == TAG_PAGE_ID(tag)) {
					page = uffs_FindBestPageInBlock(dev, bc, i);
					if (uffs_Assert(page != UFFS_INVALID_PAGE, "got an invalid page ?")) {
						size += TAG_DATA_LEN(GET_TAG(bc, page));
						page_id++;
					}
				}
			}
		}
	}

	return size;
}
Beispiel #3
0
/** 
 * \brief calculate data length of a file block
 * \param[in] dev uffs device
 * \param[in] bc block info
 */
int uffs_GetBlockFileDataLength(uffs_Device *dev, uffs_BlockInfo *bc, u8 type)
{
	u16 page_id;
	u16 i;
	uffs_Tags *tag;
	int size = 0;
	u16 page;
	u16 lastPage = dev->attr->pages_per_block - 1;

	// TODO: Need to speed up this procedure!
	// First try the last page. will hit it
	// if it's the full loaded file/data block.
	uffs_BlockInfoLoad(dev, bc, lastPage);
	tag = GET_TAG(bc, lastPage);

	if (type == UFFS_TYPE_FILE) {
		if(TAG_PAGE_ID(tag) == (lastPage - 1) &&
			TAG_DATA_LEN(tag) == dev->com.pg_data_size) {
			size = dev->com.pg_data_size * (dev->attr->pages_per_block - 1);
			return size;
		}
	}
	if (type == UFFS_TYPE_DATA) {
		if(TAG_PAGE_ID(tag) == lastPage &&
			TAG_DATA_LEN(tag) == dev->com.pg_data_size) {
			size = dev->com.pg_data_size * dev->attr->pages_per_block;
			return size;
		}
	}

	// ok, it's not the full loaded file/data block,
	// need to read all spares....
	uffs_BlockInfoLoad(dev, bc, UFFS_ALL_PAGES);
	tag = GET_TAG(bc, 0);
	if (TAG_TYPE(tag) == UFFS_TYPE_FILE) {
		page_id = 1; //In file header block, file data page_id from 1
		i = 1;		//search from page 1
	}
	else {
		page_id = 0;	//in normal file data block, page_id from 0
		i = 0;		//in normal file data block, search from page 0
	}
	for (; i < dev->attr->pages_per_block; i++) {
		tag = GET_TAG(bc, i);
		if (page_id == TAG_PAGE_ID(tag)) {
			page = uffs_FindBestPageInBlock(dev, bc, i);
			size += TAG_DATA_LEN(GET_TAG(bc, page));
			page_id++;
		}
	}

	return size;
}
Beispiel #4
0
static int
parse_attributes(tvbuff_t *tvb, int offset, proto_tree *tree)
{
    guint8       tag;
    const gchar *tag_desc;
    int          name_length, value_length;
    proto_tree  *as_tree      = tree;
    proto_item  *tas          = NULL;
    int          start_offset = offset;
    proto_tree  *attr_tree    = tree;

    while (tvb_offset_exists(tvb, offset)) {
        tag = tvb_get_guint8(tvb, offset);
        tag_desc = val_to_str(tag, tag_vals, "Reserved (0x%02x)");
        if (TAG_TYPE(tag) == TAG_TYPE_DELIMITER) {
            /*
             * If we had an attribute sequence we were
             * working on, we're done with it; set its
             * length to the length of all the stuff
             * we've done so far.
             */
            if (tas != NULL)
                proto_item_set_len(tas, offset - start_offset);

            /*
             * This tag starts a new attribute sequence;
             * create a new tree under this tag when we see
             * a non-delimiter tag, under which to put
             * those attributes.
             */
            as_tree   = NULL;
            attr_tree = tree;

            /*
             * Remember the offset at which this attribute
             * sequence started, so we can use it to compute
             * its length when it's finished.
             */
            start_offset = offset;

            /*
             * Now create a new item for this tag.
             * XXX - should use proto_tree_add_subtree
             */
            tas = proto_tree_add_text(tree, tvb, offset, 1, "%s", tag_desc);
            offset += 1;
            if (tag == TAG_END_OF_ATTRIBUTES) {
                /*
                 * No more attributes.
                 */
                break;
            }
        } else {
            /*
             * Value tag - get the name length.
             */
            name_length = tvb_get_ntohs(tvb, offset + 1);

            /*
             * OK, get the value length.
             */
            value_length = tvb_get_ntohs(tvb, offset + 1 + 2 + name_length);

            /*
             * OK, does the value run past the end of the
             * frame?
             */
            if (as_tree == NULL) {
                /*
                 * OK, there's an attribute to hang
                 * under a delimiter tag, but we don't
                 * have a tree for that tag yet; create
                 * a tree.
                 */
                as_tree = proto_item_add_subtree(tas,
                                                 ett_ipp_as);
                attr_tree = as_tree;
            }

            switch (TAG_TYPE(tag)) {

            case TAG_TYPE_INTEGER:
                if (name_length != 0) {
                    /*
                     * This is an attribute, not
                     * an additional value, so
                     * start a tree for it.
                     */
                    attr_tree = add_integer_tree(as_tree,
                                                 tvb, offset, name_length,
                                                 value_length, tag);
                }
                add_integer_value(tag_desc, attr_tree, tvb,
                                  offset, name_length, value_length, tag);
                break;

            case TAG_TYPE_OCTETSTRING:
                if (name_length != 0) {
                    /*
                     * This is an attribute, not
                     * an additional value, so
                     * start a tree for it.
                     */
                    attr_tree = add_octetstring_tree(as_tree,
                                                     tvb, offset, name_length,
                                                     value_length);
                }
                add_octetstring_value(tag_desc, attr_tree, tvb,
                                      offset, name_length, value_length);
                break;

            case TAG_TYPE_CHARSTRING:
                if (name_length != 0) {
                    /*
                     * This is an attribute, not
                     * an additional value, so
                     * start a tree for it.
                     */
                    attr_tree = add_charstring_tree(as_tree,
                                                    tvb, offset, name_length,
                                                    value_length);
                }
                add_charstring_value(tag_desc, attr_tree, tvb,
                                     offset, name_length, value_length);
                break;
            }
            offset += 1 + 2 + name_length + 2 + value_length;
        }
    }

    return offset;
}
Beispiel #5
0
/* usage: t_pgrw
 *
 * This test case test page read/write
 */
static int cmd_TestPageReadWrite(int argc, char *argv[])
{
	TreeNode *node = NULL;
	uffs_Device *dev;
	uffs_Tags local_tag;
	uffs_Tags *tag = &local_tag;
	int ret;
	u16 block;
	u16 page;
	uffs_Buf *buf = NULL;

	u32 i;
	int rc = -1;

	dev = uffs_GetDeviceFromMountPoint("/");
	if (!dev)
		goto ext;

	buf = uffs_BufClone(dev, NULL);
	if (!buf)
		goto ext;

	node = uffs_TreeGetErasedNode(dev);
	if (!node) {
		MSGLN("no free block ?");
		goto ext;
	}

	for (i = 0; i < dev->com.pg_data_size; i++) {
		buf->data[i] = i & 0xFF;
	}

	block = node->u.list.block;
	page = 1;

	TAG_DIRTY_BIT(tag) = TAG_DIRTY;
	TAG_VALID_BIT(tag) = TAG_VALID;
	TAG_DATA_LEN(tag) = dev->com.pg_data_size;
	TAG_TYPE(tag) = UFFS_TYPE_DATA;
	TAG_PAGE_ID(tag) = 3;
	TAG_PARENT(tag) = 100;
	TAG_SERIAL(tag) = 10;
	TAG_BLOCK_TS(tag) = 1;
	SEAL_TAG(tag);

	ret = uffs_FlashWritePageCombine(dev, block, page, buf, tag);
	if (UFFS_FLASH_HAVE_ERR(ret)) {
		MSGLN("Write page error: %d", ret);
		goto ext;
	}

	ret = uffs_FlashReadPage(dev, block, page, buf, U_FALSE);
	if (UFFS_FLASH_HAVE_ERR(ret)) {
		MSGLN("Read page error: %d", ret);
		goto ext;
	}

	for (i = 0; i < dev->com.pg_data_size; i++) {
		if (buf->data[i] != (i & 0xFF)) {
			MSGLN("Data verify fail at: %d", i);
			goto ext;
		}
	}

	ret = uffs_FlashReadPageTag(dev, block, page, tag);
	if (UFFS_FLASH_HAVE_ERR(ret)) {
		MSGLN("Read tag (page spare) error: %d", ret);
		goto ext;
	}
	
	// verify tag:
	if (!TAG_IS_SEALED(tag)) {
		MSGLN("not sealed ? Tag verify fail!");
		goto ext;
	}

	if (!TAG_IS_DIRTY(tag)) {
		MSGLN("not dirty ? Tag verify fail!");
		goto ext;
	}

	if (!TAG_IS_VALID(tag)) {
		MSGLN("not valid ? Tag verify fail!");
		goto ext;
	}

	if (TAG_DATA_LEN(tag) != dev->com.pg_data_size ||
		TAG_TYPE(tag) != UFFS_TYPE_DATA ||
		TAG_PAGE_ID(tag) != 3 ||
		TAG_PARENT(tag) != 100 ||
		TAG_SERIAL(tag) != 10 ||
		TAG_BLOCK_TS(tag) != 1) {

		MSGLN("Tag verify fail!");
		goto ext;
	}

	MSGLN("Page read/write test succ.");
	rc = 0;

ext:
	if (node) {
		uffs_TreeEraseNode(dev, node);
		uffs_TreeInsertToErasedListTail(dev, node);
	}

	if (dev)
		uffs_PutDevice(dev);

	if (buf)
		uffs_BufFreeClone(dev, buf);

	return rc;
}
Beispiel #6
0
/* usage: t_pgrw
 *
 * This test case test page read/write
 */
static BOOL cmdTestPageReadWrite(const char *tail)
{
	TreeNode *node;
	uffs_Device *dev;
	uffs_Tags local_tag;
	uffs_Tags *tag = &local_tag;
	int ret;
	u16 block;
	u16 page;
	uffs_Buf *buf;

	u32 i;

	dev = uffs_GetDeviceFromMountPoint("/");
	if (!dev)
		goto ext;

	buf = uffs_BufClone(dev, NULL);
	if (!buf)
		goto ext;

	node = uffs_TreeGetErasedNode(dev);
	if (!node) {
		MSGLN("no free block ?");
		goto ext;
	}

	for (i = 0; i < dev->com.pg_data_size; i++) {
		buf->data[i] = i & 0xFF;
	}

	block = node->u.list.block;
	page = 1;

	TAG_DATA_LEN(tag) = dev->com.pg_data_size;
	TAG_TYPE(tag) = UFFS_TYPE_DATA;
	TAG_PAGE_ID(tag) = 3;
	TAG_PARENT(tag) = 100;
	TAG_SERIAL(tag) = 10;
	TAG_BLOCK_TS(tag) = 1;

	ret = uffs_FlashWritePageCombine(dev, block, page, buf, tag);
	if (UFFS_FLASH_HAVE_ERR(ret)) {
		MSGLN("Write page error: %d", ret);
		goto ext;
	}

	ret = uffs_FlashReadPage(dev, block, page, buf);
	if (UFFS_FLASH_HAVE_ERR(ret)) {
		MSGLN("Read page error: %d", ret);
		goto ext;
	}

	for (i = 0; i < dev->com.pg_data_size; i++) {
		if (buf->data[i] != (i & 0xFF)) {
			MSGLN("Data verify fail at: %d", i);
			goto ext;
		}
	}

	ret = uffs_FlashReadPageTag(dev, block, page, tag);
	if (UFFS_FLASH_HAVE_ERR(ret)) {
		MSGLN("Read tag (page spare) error: %d", ret);
		goto ext;
	}
	
	// verify tag:
	if (!TAG_IS_DIRTY(tag)) {
		MSGLN("not dirty ? Tag verify fail!");
		goto ext;
	}

	if (!TAG_IS_VALID(tag)) {
		MSGLN("not valid ? Tag verify fail!");
		goto ext;
	}

	if (TAG_DATA_LEN(tag) != dev->com.pg_data_size ||
		TAG_TYPE(tag) != UFFS_TYPE_DATA ||
		TAG_PAGE_ID(tag) != 3 ||
		TAG_PARENT(tag) != 100 ||
		TAG_SERIAL(tag) != 10 ||
		TAG_BLOCK_TS(tag) != 1) {

		MSGLN("Tag verify fail!");
		goto ext;
	}

	MSGLN("Page read/write test succ.");

ext:
	if (node) {
		uffs_FlashEraseBlock(dev, node->u.list.block);
		if (HAVE_BADBLOCK(dev))
			uffs_BadBlockProcess(dev, node);
		else
			uffs_InsertToErasedListHead(dev, node);
	}

	if (dev)
		uffs_PutDevice(dev);

	if (buf)
		uffs_BufFreeClone(dev, buf);

	return TRUE;
}
Beispiel #7
0
static URET _BuildValidTreeNode(uffs_Device *dev,
								TreeNode *node,		//!< empty node
								uffs_BlockInfo *bc,
								struct BlockTypeStatSt *st)
{
	uffs_Tags *tag;
	TreeNode *node_alt;
	u16 block, parent, serial, block_alt, block_save;
	uffs_BlockInfo *bc_alt;
	u8 type;
	int page;
	UBOOL needToInsertToTree = U_FALSE;
	uffs_Buf *buf = NULL;
	uffs_FileInfo *info;
	u16 data_sum = 0;

	// check the first page on the block ...
	uffs_BlockInfoLoad(dev, bc, 0);

	tag = GET_TAG(bc, 0);  //get first page's tag

	if (!TAG_IS_DIRTY(tag)) {
		// should never go here ... unless mark dirty page failed ?
		uffs_Perror(UFFS_MSG_NORMAL,
					"First page is clean in a non-erased block ?");
		return U_FAIL;
	}

	if (!TAG_IS_VALID(tag)) {
		//first page is invalid ? should be erased now!
		uffs_Perror(UFFS_MSG_NORMAL,
					"first page in block %d is invalid, will be erased now!",
					bc->block);
		goto process_invalid_block;		
	}

	block = bc->block;
	parent = TAG_PARENT(tag);
	serial = TAG_SERIAL(tag);
	type = TAG_TYPE(tag);

	// check if there is an 'alternative block' 
	// (node which has the same serial number) in tree ?
	node_alt = uffs_FindFromTree(dev, type, parent, serial); 

	if (node_alt != NULL) {
		//find a alternate node ! need to check the timestamp !

		block_alt = _GetBlockFromNode(type, node_alt);

		uffs_Perror(UFFS_MSG_NORMAL,
					"Process unclean block (%d vs %d)", block, block_alt);

		if (block_alt == INVALID_UFFS_SERIAL) {
			uffs_Perror(UFFS_MSG_SERIOUS, "invalid block ?");
			return U_FAIL;
		}
		
		bc_alt = uffs_BlockInfoGet(dev, block_alt);
		if (bc_alt == NULL) {
			uffs_Perror(UFFS_MSG_SERIOUS, "can't get block info ");
			return U_FAIL;
		}
		uffs_BlockInfoLoad(dev, bc_alt, 0);
		if (uffs_IsSrcNewerThanObj (
				TAG_BLOCK_TS(tag),
				TAG_BLOCK_TS(GET_TAG(bc_alt, 0))) == U_TRUE) {

			//the node is newer than node_alt, so keep node_alt, and erase node
			uffs_FlashEraseBlock(dev, block);
			node->u.list.block = block;
			if (HAVE_BADBLOCK(dev))
				uffs_BadBlockProcess(dev, node);
			else
				uffs_TreeInsertToErasedListTail(dev, node);

			uffs_BlockInfoPut(dev, bc_alt);  //put back bc_alt before we return.
			return U_SUCC;
		}
		else {
			//the node is older than node_alt, so keep node, and erase node_alt
			//we use node as erased node to insert to erased list

			block_save = _GetBlockFromNode(type, node_alt);
			uffs_FlashEraseBlock(dev, block_save);
			node->u.list.block = block_save;
			if (HAVE_BADBLOCK(dev))
				uffs_BadBlockProcess(dev, node);
			else
				uffs_TreeInsertToErasedListTail(dev, node);

			//put back bc_alt because we don't need it anymore.
			uffs_BlockInfoPut(dev, bc_alt);
			
			//use node_alt to store new informations in following
			node = node_alt;

			needToInsertToTree = U_FALSE;
		}
	}
	else {
		needToInsertToTree = U_TRUE;
	}

	if (type == UFFS_TYPE_DIR || type == UFFS_TYPE_FILE) {
		buf = uffs_BufClone(dev, NULL);
		if (buf == NULL)
			return U_FAIL;
		uffs_BlockInfoLoad(dev, bc, UFFS_ALL_PAGES);
		page = uffs_FindPageInBlockWithPageId(dev, bc, 0);
		if (page == UFFS_INVALID_PAGE) {
			uffs_BufFreeClone(dev, buf);
			uffs_Perror(UFFS_MSG_SERIOUS,
				"Can't find any valid page for page_id=0 ? invalid block !"
				"this might be caused by the tag layout change.\n");
			goto process_invalid_block;
		}
		page = uffs_FindBestPageInBlock(dev, bc, page);
		uffs_FlashReadPage(dev, block, page, buf, U_FALSE);
		info = (uffs_FileInfo *) (buf->data);
		data_sum = uffs_MakeSum16(info->name, info->name_len);
		uffs_BufFreeClone(dev, buf);
	}

	switch (type) {
	case UFFS_TYPE_DIR:
		node->u.dir.block = bc->block;
		node->u.dir.checksum = data_sum;
		node->u.dir.parent = TAG_PARENT(tag);
		node->u.dir.serial = TAG_SERIAL(tag);
		st->dir++;
		break;
	case UFFS_TYPE_FILE:
		node->u.file.block = bc->block;
		node->u.file.checksum = data_sum;
		node->u.file.parent = TAG_PARENT(tag);
		node->u.file.serial = TAG_SERIAL(tag);
		node->u.file.len = uffs_GetBlockFileDataLength(dev, bc, UFFS_TYPE_FILE);  
		st->file++;
		break;
	case UFFS_TYPE_DATA:
		node->u.data.block = bc->block;
		node->u.data.parent = TAG_PARENT(tag);
		node->u.data.serial = TAG_SERIAL(tag);
		node->u.data.len = uffs_GetBlockFileDataLength(dev, bc, UFFS_TYPE_DATA); 
		st->data++;
		break;
	}

	if (needToInsertToTree == U_TRUE) {
		uffs_InsertNodeToTree(dev, type, node);
	}

	return U_SUCC;

process_invalid_block:
	/* erase the invalid block */
	uffs_FlashEraseBlock(dev, bc->block);

	node->u.list.block = bc->block;

	if (HAVE_BADBLOCK(dev))
		uffs_BadBlockProcess(dev, node);
	else
		uffs_TreeInsertToErasedListTail(dev, node);

	return U_SUCC;
}
/** 
 * \brief recover bad block
 * \param[in] dev uffs device
 */
void uffs_BadBlockRecover(uffs_Device *dev)
{
	TreeNode *good, *bad;
	uffs_Buf *buf;
	u16 i;
	u16 page;
	uffs_BlockInfo *bc = NULL;
	uffs_Tags *tag;
	uffs_Tags newTag;
	UBOOL succRecov;
	UBOOL goodBlockIsDirty = U_FALSE;
	int ret;
	int region;
	u8 type;
	
	if (dev->bad.block == UFFS_INVALID_BLOCK)
		return;

	// pick up an erased good block
	good = uffs_TreeGetErasedNode(dev);
	if (good == NULL) {
		uffs_Perror(UFFS_ERR_SERIOUS, "no free block to replace bad block!");
		return;
	}

	//recover block
	bc = uffs_BlockInfoGet(dev, dev->bad.block);
	
	if (bc == NULL) {
		uffs_Perror(UFFS_ERR_SERIOUS, "can't get bad block info");
		return;
	}

	succRecov = U_TRUE;
	for (i = 0; i < dev->attr->pages_per_block; i++) {
		page = uffs_FindPageInBlockWithPageId(dev, bc, i);
		if(page == UFFS_INVALID_PAGE) {
			break;  //end of last valid page, normal break
		}
		page = uffs_FindBestPageInBlock(dev, bc, page);
		tag = GET_TAG(bc, page);
		buf = uffs_BufClone(dev, NULL);
		if (buf == NULL) {	
			uffs_Perror(UFFS_ERR_SERIOUS, "Can't clone a new buf!");
			succRecov = U_FALSE;
			break;
		}
		//NOTE: since this is a bad block, we can't guarantee the data is ECC ok, so just load data even ECC is not OK.
		ret = uffs_LoadPhyDataToBufEccUnCare(dev, buf, bc->block, page);
		if (ret == U_FAIL) {
			uffs_Perror(UFFS_ERR_SERIOUS, "I/O error ?");
			uffs_BufFreeClone(dev, buf);
			succRecov = U_FALSE;
			break;
		}
		buf->data_len = TAG_DATA_LEN(tag);
		if (buf->data_len > dev->com.pg_data_size) {
			uffs_Perror(UFFS_ERR_NOISY, "data length over flow!!!");
			buf->data_len = dev->com.pg_data_size;
		}

		buf->parent = TAG_PARENT(tag);
		buf->serial = TAG_SERIAL(tag);
		buf->type = TAG_TYPE(tag);
		buf->page_id = TAG_PAGE_ID(tag);
		
		newTag = *tag;
		TAG_BLOCK_TS(&newTag) = uffs_GetNextBlockTimeStamp(TAG_BLOCK_TS(tag));

		ret = uffs_FlashWritePageCombine(dev, good->u.list.block, i, buf, &newTag);

		goodBlockIsDirty = U_TRUE;
		uffs_BufFreeClone(dev, buf);

		if (ret == UFFS_FLASH_IO_ERR) {
			uffs_Perror(UFFS_ERR_NORMAL, "I/O error ?");
			succRecov = U_FALSE;
			break;
		}
	}


	if (succRecov == U_TRUE) {
		//successful recover bad block, so need to mark bad block, and replace with good one

		region = SEARCH_REGION_DIR|SEARCH_REGION_FILE|SEARCH_REGION_DATA;
		bad = uffs_TreeFindNodeByBlock(dev, dev->bad.block, &region);
		if (bad != NULL) {
			switch (region) {
			case SEARCH_REGION_DIR:
				bad->u.dir.block = good->u.list.block;
				type = UFFS_TYPE_DIR;
				break;
			case SEARCH_REGION_FILE:
				bad->u.file.block = good->u.list.block;
				type = UFFS_TYPE_FILE;
				break;
			case SEARCH_REGION_DATA:
				bad->u.data.block = good->u.list.block;
				type = UFFS_TYPE_DATA;
			}
			
			//from now, the 'bad' is actually good block :)))
			uffs_Perror(UFFS_ERR_NOISY, "new bad block %d found, and replaced by %d!", dev->bad.block, good->u.list.block);
			uffs_BlockInfoExpire(dev, bc, UFFS_ALL_PAGES);
			//we reuse the 'good' node as bad block node, and process the bad block.
			good->u.list.block = dev->bad.block;
			uffs_BadBlockProcess(dev, good);
		}
		else {
			uffs_Perror(UFFS_ERR_SERIOUS, "can't find the reported bad block(%d) in the tree???", dev->bad.block);
			if (goodBlockIsDirty == U_TRUE)
				dev->ops->EraseBlock(dev, good->u.list.block);
			uffs_TreeInsertToErasedListTail(dev, good);
		}
	}
	else {
		if (goodBlockIsDirty == U_TRUE)
			dev->ops->EraseBlock(dev, good->u.list.block);
		uffs_TreeInsertToErasedListTail(dev, good); //put back to erased list
	}

	uffs_BlockInfoPut(dev, bc);

}