/* calculate file length, etc */ static URET _BuildTreeStepThree(uffs_Device *dev) { int i; u16 x; TreeNode *work; TreeNode *node; struct uffs_TreeSt *tree; uffs_Pool *pool; u16 blockSave; TreeNode *cache = NULL; u16 cacheSerial = INVALID_UFFS_SERIAL; tree = &(dev->tree); pool = TPOOL(dev); uffs_Perror(UFFS_MSG_NOISY, "build tree step three"); for (i = 0; i < DATA_NODE_ENTRY_LEN; i++) { x = tree->data_entry[i]; while (x != EMPTY_NODE) { work = FROM_IDX(x, pool); if (work->u.data.parent == cacheSerial) { node = cache; } else { node = uffs_TreeFindFileNode(dev, work->u.data.parent); cache = node; cacheSerial = work->u.data.parent; } if (node == NULL) { x = work->hash_next; //this data block does not belong to any file ? //should be erased. uffs_Perror(UFFS_MSG_NORMAL, "find a orphan data block:%d, " "parent:%d, serial:%d, will be erased!", work->u.data.block, work->u.data.parent, work->u.data.serial); uffs_BreakFromEntry(dev, UFFS_TYPE_DATA, work); blockSave = work->u.data.block; work->u.list.block = blockSave; uffs_FlashEraseBlock(dev, blockSave); if (HAVE_BADBLOCK(dev)) uffs_BadBlockProcess(dev, work); else uffs_TreeInsertToErasedListTail(dev, work); } else { node->u.file.len += work->u.data.len; x = work->hash_next; } } } return U_SUCC; }
TreeNode * uffs_TreeGetErasedNode(uffs_Device *dev) { TreeNode *node = uffs_TreeGetErasedNodeNoCheck(dev); u16 block; if (node && node->u.list.u.need_check) { block = node->u.list.block; if (uffs_FlashCheckErasedBlock(dev, block) != U_SUCC) { // Hmm, this block is not fully erased ? erase it immediately. uffs_FlashEraseBlock(dev, block); node->u.list.u.need_check = 0; } } return node; }
/** * \brief delete uffs object * * \param[in] name full name of object * \param[out] err return error code * * \return U_SUCC if object is deleted successfully. * return U_FAIL if error happen, error code is set to *err. */ URET uffs_DeleteObject(const char * name, int *err) { uffs_Object *obj, *work; TreeNode *node, *d_node; uffs_Device *dev = NULL; u16 block; u16 serial, parent, last_serial; UBOOL bad = U_FALSE; URET ret = U_FAIL; obj = uffs_GetObject(); if (obj == NULL) { if (err) *err = UEMFILE; goto ext_unlock; } if (uffs_OpenObject(obj, name, UO_RDWR|UO_DIR) == U_FAIL) { if (uffs_OpenObject(obj, name, UO_RDWR) == U_FAIL) { if (err) *err = UENOENT; goto ext_unlock; } } dev = obj->dev; // working throught object pool see if the object is opened ... uffs_ObjectDevLock(obj); work = NULL; while ((work = (uffs_Object *)uffs_PoolFindNextAllocated(&_object_pool, work)) != NULL) { if (work != obj && work->dev && work->dev == obj->dev && work->node && work->node == obj->node) { // this object is opened, can't delete it. if (err) *err = UEACCES; goto ext_lock; } } if (obj->type == UFFS_TYPE_DIR) { // if the dir is not empty, can't delete it. node = uffs_TreeFindDirNodeWithParent(dev, obj->serial); if (node != NULL) { if (err) *err = UEACCES; goto ext_lock; //have sub dirs ? } node = uffs_TreeFindFileNodeWithParent(dev, obj->serial); if (node != NULL) { if (err) *err = UEACCES; goto ext_lock; //have sub files ? } } // before erase the block, we need to take care of the buffer ... uffs_BufFlushAll(dev); if (_CheckObjBufRef(obj) > 0) { if (err) *err = UEACCES; goto ext_lock; } node = obj->node; // ok, now we are safe to erase DIR/FILE block :-) block = GET_BLOCK_FROM_NODE(obj); parent = obj->serial; last_serial = (obj->type == UFFS_TYPE_FILE && node->u.file.len > 0 ? GetFdnByOfs(obj, node->u.file.len - 1) : 0); uffs_BreakFromEntry(dev, obj->type, node); uffs_FlashEraseBlock(dev, block); node->u.list.block = block; node->u.list.u.serial = obj->serial; // From now on, the object is gone physically, // but we need to 'suspend' this node so that no one will re-use // the serial number during deleting the reset part of object. if (HAVE_BADBLOCK(dev)) { uffs_BadBlockProcessSuspend(dev, node); bad = U_TRUE; // will be put into 'bad' list later } else { uffs_TreeSuspendAdd(dev, node); bad = U_FALSE; // will be put into erased list later } // now erase DATA blocks if (obj->type == UFFS_TYPE_FILE && last_serial > 0) { for (serial = 1; serial <= last_serial; serial++) { uffs_ObjectDevUnLock(obj); ; // yield CPU to improve responsive when deleting large file. uffs_ObjectDevLock(obj); d_node = uffs_TreeFindDataNode(dev, parent, serial); if (uffs_Assert(d_node != NULL, "Can't find DATA node parent = %d, serial = %d\n", parent, serial)) { uffs_BreakFromEntry(dev, UFFS_TYPE_DATA, d_node); block = d_node->u.data.block; uffs_FlashEraseBlock(dev, block); d_node->u.list.block = block; if (HAVE_BADBLOCK(dev)) uffs_BadBlockProcess(dev, d_node); else uffs_TreeInsertToErasedListTail(dev, d_node); } } } // now process the suspend node uffs_TreeRemoveSuspendNode(dev, node); if (bad) uffs_TreeInsertToBadBlockList(dev, node); else uffs_TreeInsertToErasedListTail(dev, node); ret = U_SUCC; ext_lock: uffs_ObjectDevUnLock(obj); ext_unlock: do_ReleaseObjectResource(obj); uffs_PutObject(obj); return ret; }
/** truncate obj without lock device */ static URET do_TruncateObject(uffs_Object *obj, u32 remain, RunOptionE run_opt) { uffs_Device *dev = obj->dev; TreeNode *fnode = obj->node; u16 fdn; u32 flen; u32 block_start; TreeNode *node; uffs_BlockInfo *bc; uffs_Buf *buf; u16 page; int pos; pos = obj->pos; // save current file position if (obj->dev == NULL || obj->open_succ == U_FALSE || fnode == NULL) { obj->err = UEBADF; goto ext; } /* can't truncate a dir */ /* TODO: delete files under dir ? */ if (obj->type == UFFS_TYPE_DIR) { obj->err = UEEXIST; goto ext; } if (remain >= fnode->u.file.len) { goto ext; //!< nothing to do ... } flen = fnode->u.file.len; if (flen < remain) { // file is shorter than 'reamin', fill the gap with '\0' if (run_opt == eREAL_RUN) { obj->pos = flen; // move file pointer to the end if (do_WriteObject(obj, NULL, remain - flen) > 0) { // fill '\0' ... uffs_Perror(UFFS_MSG_SERIOUS, "Write object not finished. expect %d but only %d wrote.", remain - flen, fnode->u.file.len - flen); obj->err = UEIOERR; // likely be an I/O error. } flen = obj->node->u.file.len; } } else { while (flen > remain) { fdn = GetFdnByOfs(obj, flen - 1); //uffs_BufFlushGroup(dev, obj->serial, fdn); //!< flush the buffer block_start = GetStartOfDataBlock(obj, fdn); if (remain <= block_start && fdn > 0) { node = uffs_TreeFindDataNode(dev, obj->serial, fdn); if (node == NULL) { uffs_Perror(UFFS_MSG_SERIOUS, "can't find data node when trancate obj."); obj->err = UEIOERR; goto ext; } bc = uffs_BlockInfoGet(dev, node->u.data.block); if (bc == NULL) { uffs_Perror(UFFS_MSG_SERIOUS, "can't get block info when trancate obj."); obj->err = UEIOERR; goto ext; } for (page = 0; page < dev->attr->pages_per_block; page++) { buf = uffs_BufFind(dev, fnode->u.file.serial, fdn, page); if (buf) { //!< ok, the buffer was loaded before ... if (uffs_BufIsFree(buf) == U_FALSE) { uffs_BlockInfoPut(dev, bc); goto ext; //!< and someone is still holding the buffer, // can't truncate it !!! } else if (run_opt == eREAL_RUN) uffs_BufMarkEmpty(dev, buf); //!< discard the buffer } } if (run_opt == eREAL_RUN) { uffs_BreakFromEntry(dev, UFFS_TYPE_DATA, node); uffs_FlashEraseBlock(dev, bc->block); node->u.list.block = bc->block; if (HAVE_BADBLOCK(dev)) uffs_BadBlockProcess(dev, node); else uffs_TreeInsertToErasedListTail(dev, node); fnode->u.file.len = block_start; } flen = block_start; uffs_BlockInfoPut(dev, bc); } else { if (do_TruncateInternalWithBlockRecover(obj, fdn, remain, run_opt) == U_SUCC) { if (run_opt == eREAL_RUN) fnode->u.file.len = remain; flen = remain; } } } } if (HAVE_BADBLOCK(dev)) uffs_BadBlockRecover(dev); ext: obj->pos = pos; // keep file pointer offset not changed. uffs_Assert(fnode == obj->node, "obj->node change!\n"); return (obj->err == UENOERR ? U_SUCC : U_FAIL); }
/** * \brief delete uffs object * * \param[in] name full name of object * \param[out] err return error code * * \return U_SUCC if object is deleted successfully. * return U_FAIL if error happen, error code is set to *err. */ URET uffs_DeleteObject(const char * name, int *err) { uffs_Object *obj; TreeNode *node; uffs_Device *dev; u16 block; uffs_Buf *buf; URET ret = U_FAIL; obj = uffs_GetObject(); if (obj == NULL) { if (err) *err = UEMFILE; goto err1; } if (uffs_OpenObject(obj, name, UO_RDWR|UO_DIR) == U_FAIL) { if (uffs_OpenObject(obj, name, UO_RDWR) == U_FAIL) { if (err) *err = UENOENT; goto err1; } } uffs_TruncateObject(obj, 0); uffs_ObjectDevLock(obj); dev = obj->dev; if (obj->type == UFFS_TYPE_DIR) { // if the dir is not empty, can't delete it. node = uffs_TreeFindDirNodeWithParent(dev, obj->serial); if (node != NULL) { if (err) *err = UEACCES; goto err; //have sub dirs ? } node = uffs_TreeFindFileNodeWithParent(dev, obj->serial); if (node != NULL) { if (err) *err = UEACCES; goto err; //have sub files ? } } block = GET_BLOCK_FROM_NODE(obj); node = obj->node; // before erase the block, we need to take care of the buffer ... uffs_BufFlushAll(dev); if (HAVE_BADBLOCK(dev)) uffs_BadBlockRecover(dev); buf = uffs_BufFind(dev, obj->parent, obj->serial, 0); if (buf) { //need to expire this buffer ... if (buf->ref_count != 0) { //there is other obj for this file still in use ? uffs_Perror(UFFS_ERR_NORMAL, "Try to delete object but still have buf referenced."); if (err) *err = UEACCES; goto err; } buf->mark = UFFS_BUF_EMPTY; //!< make this buffer expired. } //TODO: need to take care of other obj->node ? uffs_BreakFromEntry(dev, obj->type, node); uffs_FlashEraseBlock(dev, block); node->u.list.block = block; if (HAVE_BADBLOCK(dev)) uffs_BadBlockProcess(dev, node); else uffs_TreeInsertToErasedListTail(dev, node); ret = U_SUCC; err: uffs_ObjectDevUnLock(obj); err1: do_ReleaseObjectResource(obj); uffs_PutObject(obj); return ret; }
/** truncate obj without lock device */ static URET do_TruncateObject(uffs_Object *obj, u32 remain, UBOOL dry_run) { uffs_Device *dev = obj->dev; TreeNode *fnode = obj->node; u16 fdn; u32 flen; u32 block_start; TreeNode *node; uffs_BlockInfo *bc; uffs_Buf *buf; u16 page; if (obj->dev == NULL || obj->open_succ == U_FALSE || fnode == NULL) { obj->err = UEBADF; goto ext; } /* can't truncate a dir */ /* TODO: delete files under dir ? */ if (obj->type == UFFS_TYPE_DIR) { obj->err = UEEXIST; goto ext; } if (remain >= fnode->u.file.len) { goto ext; //!< nothing to do ... } flen = fnode->u.file.len; while (flen > remain) { fdn = GetFdnByOfs(obj, flen - 1); //uffs_BufFlushGroup(dev, obj->serial, fdn); //!< flush the buffer block_start = GetStartOfDataBlock(obj, fdn); if (remain <= block_start && fdn > 0) { node = uffs_TreeFindDataNode(dev, obj->serial, fdn); if (node == NULL) { uffs_Perror(UFFS_ERR_SERIOUS, "can't find data node when trancate obj."); obj->err = UEIOERR; goto ext; } bc = uffs_BlockInfoGet(dev, node->u.data.block); if (bc == NULL) { uffs_Perror(UFFS_ERR_SERIOUS, "can't get block info when trancate obj."); obj->err = UEIOERR; goto ext; } for (page = 0; page < dev->attr->pages_per_block; page++) { buf = uffs_BufFind(dev, fnode->u.file.serial, fdn, page); if (buf) { //!< ok, the buffer was loaded before ... if (uffs_BufIsFree(buf) == U_FALSE) { uffs_BlockInfoPut(dev, bc); goto ext; //!< and someone is still holding the buffer, can't truncate it !!! } else if (dry_run == U_FALSE) uffs_BufMarkEmpty(dev, buf); //!< discard the buffer } } if (dry_run == U_FALSE) { uffs_BlockInfoExpire(dev, bc, UFFS_ALL_PAGES); uffs_BreakFromEntry(dev, UFFS_TYPE_DATA, node); uffs_FlashEraseBlock(dev, bc->block); node->u.list.block = bc->block; if (HAVE_BADBLOCK(dev)) uffs_BadBlockProcess(dev, node); else uffs_TreeInsertToErasedListTail(dev, node); uffs_BlockInfoPut(dev, bc); fnode->u.file.len = block_start; } else { uffs_BlockInfoPut(dev, bc); } flen = block_start; } else { if (do_TruncateInternalWithBlockRecover(obj, fdn, remain, dry_run) == U_SUCC) { if (dry_run == U_FALSE) fnode->u.file.len = remain; flen = remain; } } } if (HAVE_BADBLOCK(dev)) uffs_BadBlockRecover(dev); ext: return (obj->err == UENOERR ? U_SUCC : U_FAIL); }
static void _ForceFormatAndCheckBlock(uffs_Device *dev, int block) { int i, j; uffs_Buf *buf = NULL; UBOOL bad = U_TRUE; URET ret; struct uffs_FlashOpsSt *ops = dev->ops; struct uffs_TagStoreSt ts; u8 *spare = NULL; buf = uffs_BufClone(dev, NULL); if (buf == NULL) { uffs_Perror(UFFS_MSG_SERIOUS, "Alloc page buffer fail ! Format stoped."); goto ext; } spare = (u8 *)uffs_PoolGet(SPOOL(dev)); if (spare == NULL) goto ext; //step 1: Erase, fully fill with 0x0, and check ret = uffs_FlashEraseBlock(dev, block); if (UFFS_FLASH_IS_BAD_BLOCK(ret)) goto bad_out; memset(buf->header, 0, dev->com.pg_size); memset(&ts, 0, sizeof(ts)); memset(spare, 0, dev->attr->spare_size); for (i = 0; i < dev->attr->pages_per_block; i++) { if (ops->WritePageWithLayout) ret = ops->WritePageWithLayout(dev, block, i, buf->header, dev->com.pg_size, NULL, &ts); else ret = ops->WritePage(dev, block, i, buf->header, dev->com.pg_size, spare, dev->attr->spare_size); if (UFFS_FLASH_IS_BAD_BLOCK(ret)) goto bad_out; } for (i = 0; i < dev->attr->pages_per_block; i++) { memset(buf->header, 0xFF, dev->com.pg_size); memset(&ts, 0xFF, sizeof(ts)); memset(spare, 0xFF, dev->attr->spare_size); if (ops->ReadPageWithLayout) { ret = ops->ReadPageWithLayout(dev, block, i, buf->header, dev->com.pg_size, NULL, &ts, NULL); if (UFFS_FLASH_IS_BAD_BLOCK(ret)) goto bad_out; for (j = 0; j < dev->com.pg_size; j++) if (buf->header[j] != 0) goto bad_out; for (j = 0; j < sizeof(ts); j++) if (((u8 *)&ts)[j] != 0) goto bad_out; } else { ret = ops->ReadPage(dev, block, i, buf->header, dev->com.pg_size, NULL, spare, dev->attr->spare_size); if (UFFS_FLASH_IS_BAD_BLOCK(ret)) goto bad_out; for (j = 0; j < dev->com.pg_size; j++) if (buf->header[j] != 0) goto bad_out; for (j = 0; j < dev->attr->spare_size; j++) if (spare[j] != 0) goto bad_out; } } //step 2: Erase, and check ret = uffs_FlashEraseBlock(dev, block); if (UFFS_FLASH_IS_BAD_BLOCK(ret)) goto bad_out; for (i = 0; i < dev->attr->pages_per_block; i++) { memset(buf->header, 0, dev->com.pg_size); memset(&ts, 0, sizeof(ts)); memset(spare, 0, dev->attr->spare_size); if (ops->ReadPageWithLayout) { ret = ops->ReadPageWithLayout(dev, block, i, buf->header, dev->com.pg_size, NULL, &ts, NULL); if (UFFS_FLASH_IS_BAD_BLOCK(ret)) goto bad_out; for (j = 0; j < dev->com.pg_size; j++) if (buf->header[j] != 0xFF) goto bad_out; for (j = 0; j < sizeof(ts); j++) if (((u8 *)&ts)[j] != 0xFF) goto bad_out; } else { ret = ops->ReadPage(dev, block, i, buf->header, dev->com.pg_size, NULL, spare, dev->attr->spare_size); if (UFFS_FLASH_IS_BAD_BLOCK(ret)) goto bad_out; for (j = 0; j < dev->com.pg_size; j++) if (buf->header[j] != 0xFF) goto bad_out; for (j = 0; j < dev->attr->spare_size; j++) if (spare[j] != 0xFF) goto bad_out; } } // format succ bad = U_FALSE; bad_out: if (bad == U_TRUE) uffs_FlashMarkBadBlock(dev, block); ext: if (buf) uffs_BufFreeClone(dev, buf); if (spare) uffs_PoolPut(SPOOL(dev), spare); return; }
URET uffs_FormatDevice(uffs_Device *dev, UBOOL force) { u16 i, slot; URET ret = U_SUCC; if (dev == NULL) return U_FAIL; if (dev->ops == NULL) return U_FAIL; uffs_GlobalFsLockLock(); ret = uffs_BufFlushAll(dev); if (dev->ref_count > 1 && !force) { uffs_Perror(UFFS_MSG_NORMAL, "can't format when dev->ref_count = %d", dev->ref_count); ret = U_FAIL; } if (ret == U_SUCC && force) { uffs_DirEntryBufPutAll(dev); uffs_PutAllObjectBuf(dev); uffs_FdSignatureIncrease(); } if (ret == U_SUCC && uffs_BufIsAllFree(dev) == U_FALSE && !force) { uffs_Perror(UFFS_MSG_NORMAL, "some page still in used!"); ret = U_FAIL; } if (!force) { for (slot = 0; ret == U_SUCC && slot < dev->cfg.dirty_groups; slot++) { if (dev->buf.dirtyGroup[slot].count > 0) { uffs_Perror(UFFS_MSG_SERIOUS, "there still have dirty pages!"); ret = U_FAIL; } } } if (ret == U_SUCC) uffs_BufSetAllEmpty(dev); if (ret == U_SUCC && uffs_BlockInfoIsAllFree(dev) == U_FALSE && !force) { uffs_Perror(UFFS_MSG_NORMAL, "there still have block info cache ? fail to format"); ret = U_FAIL; } if (ret == U_SUCC) uffs_BlockInfoExpireAll(dev); for (i = dev->par.start; ret == U_SUCC && i <= dev->par.end; i++) { if (uffs_FlashIsBadBlock(dev, i) == U_FALSE) { uffs_FlashEraseBlock(dev, i); if (HAVE_BADBLOCK(dev)) uffs_BadBlockProcess(dev, NULL); } else { #ifdef CONFIG_ENABLE_BAD_BLOCK_VERIFY _ForceFormatAndCheckBlock(dev, i); #endif } } if (ret == U_SUCC && uffs_TreeRelease(dev) == U_FAIL) { ret = U_FAIL; } if (ret == U_SUCC && uffs_TreeInit(dev) == U_FAIL) { ret = U_FAIL; } if (ret == U_SUCC && uffs_BuildTree(dev) == U_FAIL) { ret = U_FAIL; } uffs_GlobalFsLockUnlock(); return ret; }
/* 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; }
static URET _BuildTreeStepOne(uffs_Device *dev) { int block_lt; uffs_BlockInfo *bc = NULL; TreeNode *node; struct uffs_TreeSt *tree; uffs_Pool *pool; struct uffs_MiniHeaderSt header; URET ret = U_SUCC; struct BlockTypeStatSt st = {0, 0, 0}; tree = &(dev->tree); pool = TPOOL(dev); tree->bad = NULL; tree->bad_count = 0; tree->erased = NULL; tree->erased_tail = NULL; tree->erased_count = 0; uffs_Perror(UFFS_MSG_NOISY, "build tree step one"); // printf("s:%d e:%d\n", dev->par.start, dev->par.end); for (block_lt = dev->par.start; block_lt <= dev->par.end; block_lt++) { bc = uffs_BlockInfoGet(dev, block_lt); if (bc == NULL) { uffs_Perror(UFFS_MSG_SERIOUS, "step one:fail to get block info"); ret = U_FAIL; break; } node = (TreeNode *)uffs_PoolGet(pool); if (node == NULL) { uffs_Perror(UFFS_MSG_SERIOUS, "insufficient tree node!"); ret = U_FAIL; break; } // Need to check bad block at first ! if (uffs_FlashIsBadBlock(dev, block_lt) == U_TRUE) { node->u.list.block = block_lt; uffs_TreeInsertToBadBlockList(dev, node); uffs_Perror(UFFS_MSG_NORMAL, "found bad block %d", block_lt); } else if (uffs_IsPageErased(dev, bc, 0) == U_TRUE) { //@ read one spare: 0 // page 0 tag shows it's an erased block, we need to check the mini header status to make sure it is clean. if (uffs_LoadMiniHeader(dev, block_lt, 0, &header) == U_FAIL) { uffs_Perror(UFFS_MSG_SERIOUS, "I/O error when reading mini header !" "block %d page %d", block_lt, 0); ret = U_FAIL; break; } if (header.status != 0xFF) { // page 0 tag is clean but page data is dirty ??? // this block should be erased immediately ! uffs_FlashEraseBlock(dev, block_lt); } node->u.list.block = block_lt; if (HAVE_BADBLOCK(dev)) { uffs_Perror(UFFS_MSG_NORMAL, "New bad block (%d) discovered.", block_lt); uffs_BadBlockProcess(dev, node); } else { // page 0 is clean does not means all pages in this block are clean, // need to check this block later before use it. uffs_TreeInsertToErasedListTailEx(dev, node, 1); } } else { // this block have valid data page(s). ret = _ScanAndFixUnCleanPage(dev, bc); if (ret == U_FAIL) break; ret = _BuildValidTreeNode(dev, node, bc, &st); if (ret == U_FAIL) break; } uffs_BlockInfoPut(dev, bc); } //end of for if(ret == U_FAIL) uffs_BlockInfoPut(dev, bc); uffs_Perror(UFFS_MSG_NORMAL, "DIR %d, FILE %d, DATA %d", st.dir, st.file, st.data); return ret; }
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; }