/** * \brief process bad block: erase bad block, mark it as 'bad' and put the node to bad block list. * \param[in] dev uffs device * \param[in] node bad block tree node (before the block turn 'bad', it must belong to something ...) */ void uffs_BadBlockProcess(uffs_Device *dev, TreeNode *node) { if (HAVE_BADBLOCK(dev)) { // mark the bad block uffs_FlashMarkBadBlock(dev, dev->bad.block); // and put it into bad block list if (node != NULL) uffs_TreeInsertToBadBlockList(dev, node); //clear bad block mark. dev->bad.block = UFFS_INVALID_BLOCK; } }
/** * \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; }
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; }