/** * \brief Initializes the memory pool. * \param[in] pool memory pool * \param[in] mem pool memory * \param[in] mem_size size of pool memory * \param[in] buf_size size of a single buffer * \param[in] num_bufs number of buffers * \return Returns U_SUCC if successful. */ URET uffs_PoolInit(uffs_Pool *pool, void *mem, u32 mem_size, u32 buf_size, u32 num_bufs) { unsigned int i; uffs_PoolEntry *e1, *e2; uffs_Assert(pool, "pool missing"); uffs_Assert(mem, "pool memory missing"); uffs_Assert(num_bufs > 0, "not enough buffers"); uffs_Assert(buf_size % sizeof(void *) == 0, "buffer size not aligned to pointer size"); uffs_Assert(mem_size == num_bufs * buf_size, "pool memory size is wrong"); pool->mem = (u8 *)mem; pool->buf_size = buf_size; pool->num_bufs = num_bufs; pool->sem = uffs_SemCreate(1); uffs_SemWait(pool->sem); // Initialize the free_list e1 = e2 = pool->free_list = (uffs_PoolEntry *) pool->mem; for (i = 1; i < pool->num_bufs; i++) { e2 = (uffs_PoolEntry *) (pool->mem + i * pool->buf_size); e1->next = e2; e1 = e2; } e2->next = NULL; uffs_SemSignal(pool->sem); return U_SUCC; }
static URET uffs_InitDeviceConfig(uffs_Device *dev) { if (dev->cfg.dirty_groups == 0) dev->cfg.dirty_groups = MAX_DIRTY_BUF_GROUPS; if (!uffs_Assert(dev->cfg.dirty_groups >= 1 && dev->cfg.dirty_groups <= MAX_DIRTY_BUF_GROUPS, "invalid config: dirty_groups = %d\n", dev->cfg.dirty_groups)) return U_FAIL; #if CONFIG_USE_STATIC_MEMORY_ALLOCATOR > 0 dev->cfg.bc_caches = MAX_CACHED_BLOCK_INFO; dev->cfg.page_buffers = MAX_PAGE_BUFFERS; dev->cfg.dirty_pages = MAX_DIRTY_PAGES_IN_A_BLOCK; dev->cfg.reserved_free_blocks = MINIMUN_ERASED_BLOCK; #else if (dev->cfg.bc_caches == 0) dev->cfg.bc_caches = MAX_CACHED_BLOCK_INFO; if (dev->cfg.page_buffers == 0) dev->cfg.page_buffers = MAX_PAGE_BUFFERS; if (dev->cfg.dirty_pages == 0) dev->cfg.dirty_pages = MAX_DIRTY_PAGES_IN_A_BLOCK; if (dev->cfg.reserved_free_blocks == 0) dev->cfg.reserved_free_blocks = MINIMUN_ERASED_BLOCK; if (!uffs_Assert(dev->cfg.page_buffers - CLONE_BUFFERS_THRESHOLD >= 3, "invalid config: page_buffers = %d\n", dev->cfg.page_buffers)) return U_FAIL; #endif return U_SUCC; }
/** * \brief Gets a buffer by index (offset). * This method returns a buffer from the memory pool by index. * \param[in] pool memory pool * \param[in] index index * \return Returns a pointer to the buffer. */ void *uffs_PoolGetBufByIndex(uffs_Pool *pool, u32 index) { uffs_Assert(pool, "pool missing"); uffs_Assert(index < pool->num_bufs, "index out of range"); return (u8 *) pool->mem + index * pool->buf_size; }
/** * \brief Gets the index (offset) of a buffer. * This method returns the index of a buffer from the memory pool. * \param[in] pool memory pool * \param[in] p buffer to get index from * \return Returns the index of the buffer. */ u32 uffs_PoolGetIndex(uffs_Pool *pool, void *p) { uffs_Assert(pool, "pool missing"); uffs_Assert(p >= (void *) pool->mem && p < (void *) (pool->mem + pool->num_bufs * pool->buf_size), "pointer out of range"); return ((u8 *) p - pool->mem) / pool->buf_size; }
/** * \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; }
/** * \brief Gets a buffer by index (offset). * This method returns a buffer from the memory pool by index. * \param[in] pool memory pool * \param[in] index index * \return Returns a pointer to the buffer. */ void *uffs_PoolGetBufByIndex(uffs_Pool *pool, u32 index) { if (!uffs_Assert(pool != NULL, "pool missing") || !uffs_Assert(index < pool->num_bufs, "index(%d) out of range(max %d)", index, pool->num_bufs)) { return NULL; } return (u8 *) pool->mem + index * pool->buf_size; }
/** * \brief Gets the index (offset) of a buffer. * This method returns the index of a buffer from the memory pool. * \param[in] pool memory pool * \param[in] p buffer to get index from * \return Returns the index of the buffer. */ u32 uffs_PoolGetIndex(uffs_Pool *pool, void *p) { if (!uffs_Assert(pool != NULL, "pool missing") || !uffs_Assert(p >= (void *) pool->mem && p < (void *) (pool->mem + pool->num_bufs * pool->buf_size), "pointer out of range")) { uffs_Panic(); } return ((u8 *) p - pool->mem) / pool->buf_size; }
static void drain_sdata(u8 *data, int len) { uffs_Assert(sizeof(g_sdata_buf) - g_sdata_buf_pointer >= len, "BUG: Serial Data Buffer overdrain !!"); if (data) memcpy(data, g_sdata_buf + g_sdata_buf_pointer, len); g_sdata_buf_pointer += len; }
static void feed_sdata(const u8 *data, int len) { uffs_Assert(g_sdata_buf_pointer + len <= sizeof(g_sdata_buf), "BUG: Serial Data Buffer overflow !!"); if (data) memcpy(g_sdata_buf + g_sdata_buf_pointer, data, len); g_sdata_buf_pointer += len; }
/** * write data to obj, from obj->pos * * \param[in] obj file obj * \param[in] data data pointer * \param[in] len length of data to be write * * \return bytes wrote to obj */ int uffs_WriteObject(uffs_Object *obj, const void *data, int len) { uffs_Device *dev = obj->dev; TreeNode *fnode = NULL; int remain; u32 pos; int wrote = 0; if (obj == NULL) return 0; if (obj->dev == NULL || obj->open_succ != U_TRUE) { obj->err = UEBADF; return 0; } if (obj->type == UFFS_TYPE_DIR) { uffs_Perror(UFFS_MSG_NOISY, "Can't write to an dir object!"); obj->err = UEACCES; return 0; } if (obj->oflag == UO_RDONLY) { obj->err = UEACCES; // can't write to 'read only' mode opened file return 0; } fnode = obj->node; uffs_ObjectDevLock(obj); if (obj->oflag & UO_APPEND) obj->pos = fnode->u.file.len; else { if (obj->pos > fnode->u.file.len) { // current pos pass over the end of file, need to fill the gap with '\0' pos = obj->pos; // save desired pos obj->pos = fnode->u.file.len; // filling gap from the end of the file. remain = do_WriteObject(obj, NULL, pos - fnode->u.file.len); // Write filling bytes. Note: the filling data does not count as 'wrote' in this write operation. obj->pos = pos - remain; if (remain > 0) // fail to fill the gap ? stop. goto ext; } } remain = do_WriteObject(obj, data, len); wrote = len - remain; obj->pos += wrote; ext: if (HAVE_BADBLOCK(dev)) uffs_BadBlockRecover(dev); uffs_ObjectDevUnLock(obj); uffs_Assert(fnode == obj->node, "obj->node change!\n"); return wrote; }
static void feed_sdata_constant(u8 val, int num) { if (!uffs_Assert(g_sdata_buf_pointer + num <= sizeof(g_sdata_buf), "BUG: Serial Data Buffer overflow !!")) return; memset(g_sdata_buf + g_sdata_buf_pointer, val, num); g_sdata_buf_pointer += num; }
/** * \brief Releases the memory pool. * \param[in] pool memory pool * \return Returns U_SUCC if successful. */ URET uffs_PoolRelease(uffs_Pool *pool) { uffs_Assert(pool, "pool missing"); uffs_SemDelete(pool->sem); pool->sem = 0; return U_SUCC; }
/** * \brief given a page, search the block to find * a better page with the same page id * * \param[in] dev uffs device * \param[in] bc block info * \param[in] page page number to be compared with * * \return the better page number, could be the same with the given page. * if the given page does not have good tag, return UFFS_INVALID_PAGE. */ u16 uffs_FindBestPageInBlock(uffs_Device *dev, uffs_BlockInfo *bc, u16 page) { int i; uffs_Tags *tag, *tag_old; u16 lastPage = dev->attr->pages_per_block - 1; if (!uffs_Assert(page != UFFS_INVALID_PAGE, "invalid param !")) return page; // just in case ... uffs_BlockInfoLoad(dev, bc, page); // load old page tag_old = GET_TAG(bc, page); if (!uffs_Assert(TAG_IS_GOOD(tag_old), "try to find a invalid page ?")) return UFFS_INVALID_PAGE; if (page == lastPage) // already the last page ? return page; // check for fully loaded block, in which case the given // page id is the best page id uffs_BlockInfoLoad(dev, bc, lastPage); tag = GET_TAG(bc, lastPage); if (TAG_IS_GOOD(tag) && TAG_PAGE_ID(tag) == lastPage) return page; // block not fully loaded, search from bottom to top for (i = lastPage; i > page; i--) { uffs_BlockInfoLoad(dev, bc, i); tag = GET_TAG(bc, i); if (TAG_IS_GOOD(tag) && TAG_PAGE_ID(tag) == TAG_PAGE_ID(tag_old) && TAG_PARENT(tag) == TAG_PARENT(tag_old) && TAG_SERIAL(tag) == TAG_SERIAL(tag_old)) { break; } } return i; }
/** * \brief Get a buffer from the memory pool. * This version is locked and should be used when multiple threads access the * same memory pool. * \param[in] pool memory pool * \return Returns a pointer to the buffer or NULL if none is available. */ void *uffs_PoolGetLocked(uffs_Pool *pool) { uffs_PoolEntry *e; if (!uffs_Assert(pool != NULL, "pool missing")) return NULL; if (!uffs_Assert(pool->sem != OSSEM_NOT_INITED, "pool semaphore not initialized")) return NULL; uffs_SemWait(pool->sem); e = pool->free_list; if (e) pool->free_list = e->next; uffs_SemSignal(pool->sem); return e; }
/** * \brief Puts a buffer back to the memory pool. * This version is locked and should be used when multiple threads access the * same memory pool. * \param[in] pool memory pool * \param[in] p buffer to put back * \return Returns 0 if successful. */ int uffs_PoolPutLocked(uffs_Pool *pool, void *p) { uffs_PoolEntry *e = (uffs_PoolEntry *)p; if (!uffs_Assert(pool != NULL, "pool missing")) return -1; if (!uffs_Assert(pool->sem != OSSEM_NOT_INITED, "pool semaphore not initialized")) return -1; if (e) { uffs_SemWait(pool->sem); e->next = pool->free_list; pool->free_list = e; uffs_SemSignal(pool->sem); return 0; } return -1; }
/** * \brief Initializes the memory pool. * \param[in] pool memory pool * \param[in] mem pool memory * \param[in] mem_size size of pool memory * \param[in] buf_size size of a single buffer * \param[in] num_bufs number of buffers * \param[in] lock create semaphore for locking the memory pool * \return Returns U_SUCC if successful. */ URET uffs_PoolInit(uffs_Pool *pool, void *mem, u32 mem_size, u32 buf_size, u32 num_bufs, UBOOL lock) { unsigned int i; uffs_PoolEntry *e1, *e2; if (!uffs_Assert(pool != NULL, "pool missing") || !uffs_Assert(mem != NULL, "pool memory missing") || !uffs_Assert(num_bufs > 0, "not enough buffers") || !uffs_Assert(buf_size % sizeof(void *) == 0, "buffer size not aligned to pointer size") || !uffs_Assert(mem_size == num_bufs * buf_size, "pool memory size is wrong")) { return U_FAIL; } pool->mem = (u8 *)mem; pool->buf_size = buf_size; pool->num_bufs = num_bufs; pool->sem = OSSEM_NOT_INITED; if (lock) { uffs_SemCreate(&pool->sem); uffs_SemWait(pool->sem); } // Initialize the free_list e1 = e2 = pool->free_list = (uffs_PoolEntry *) pool->mem; for (i = 1; i < pool->num_bufs; i++) { e2 = (uffs_PoolEntry *) (pool->mem + i * pool->buf_size); e1->next = e2; e1 = e2; } e2->next = NULL; if (lock) uffs_SemSignal(pool->sem); return U_SUCC; }
/** * \brief Releases the memory pool. * \param[in] pool memory pool * \return Returns U_SUCC if successful. */ URET uffs_PoolRelease(uffs_Pool *pool) { if (!uffs_Assert(pool != NULL, "pool missing")) return U_FAIL; if (pool->sem != OSSEM_NOT_INITED) { uffs_SemDelete(&pool->sem); pool->sem = OSSEM_NOT_INITED; } return U_SUCC; }
/** * \brief Get a buffer from the memory pool. * \param[in] pool memory pool * \return Returns a pointer to the buffer or NULL if none is available. */ void *uffs_PoolGet(uffs_Pool *pool) { uffs_PoolEntry *e; uffs_Assert(pool, "pool missing"); e = pool->free_list; if (e) pool->free_list = e->next; return e; }
/** * \brief Get a buffer from the memory pool. * \param[in] pool memory pool * \return Returns a pointer to the buffer or NULL if none is available. */ void *uffs_PoolGet(uffs_Pool *pool) { uffs_PoolEntry *e; if (!uffs_Assert(pool != NULL, "pool missing")) return NULL; e = pool->free_list; if (e) pool->free_list = e->next; return e; }
/** * make spare from tag and ecc * * \param[in] dev uffs dev * \param[in] ts uffs tag store, NULL if don't pack tag store * \param[in] ecc ecc of data, NULL if don't pack ecc * \param[out] spare output buffer * \note spare buffer size: dev->mem.spare_data_size, * all unpacked bytes will be inited 0xFF */ void uffs_FlashMakeSpare(uffs_Device *dev, const uffs_TagStore *ts, const u8 *ecc, u8* spare) { u8 *p_ts = (u8 *)ts; int ts_size = TAG_STORE_SIZE; int ecc_size = ECC_SIZE(dev); int n; const u8 *p; if (!uffs_Assert(spare != NULL, "invalid param")) return; memset(spare, 0xFF, dev->mem.spare_data_size); // initialize as 0xFF. SEAL_BYTE(dev, spare) = 0; // set seal byte = 0. // load ecc p = dev->attr->ecc_layout; if (p && ecc) { while (*p != 0xFF && ecc_size > 0) { n = (p[1] > ecc_size ? ecc_size : p[1]); memcpy(spare + p[0], ecc, n); ecc_size -= n; ecc += n; p += 2; } } p = dev->attr->data_layout; while (*p != 0xFF && ts_size > 0) { n = (p[1] > ts_size ? ts_size : p[1]); memcpy(spare + p[0], p_ts, n); ts_size -= n; p_ts += n; p += 2; } uffs_Assert(SEAL_BYTE(dev, spare) == 0, "Make spare fail!"); }
/** * \brief Puts a buffer back to the memory pool. * \param[in] pool memory pool * \param[in] p buffer to put back * \return Returns 0 if successful. */ int uffs_PoolPut(uffs_Pool *pool, void *p) { uffs_PoolEntry *e = (uffs_PoolEntry *)p; uffs_Assert(pool, "pool missing"); if (e) { e->next = pool->free_list; pool->free_list = e; return 0; } return -1; }
/** * \brief Get a buffer from the memory pool. * This version is locked and should be used when multiple threads access the * same memory pool. * \param[in] pool memory pool * \return Returns a pointer to the buffer or NULL if none is available. */ void *uffs_PoolGetLocked(uffs_Pool *pool) { uffs_PoolEntry *e; uffs_Assert(pool, "pool missing"); uffs_SemWait(pool->sem); e = pool->free_list; if (e) pool->free_list = e->next; uffs_SemSignal(pool->sem); return e; }
/** * \brief Puts a buffer back to the memory pool. * This version is locked and should be used when multiple threads access the * same memory pool. * \param[in] pool memory pool * \param[in] p buffer to put back * \return Returns 0 if successful. */ int uffs_PoolPutLocked(uffs_Pool *pool, void *p) { uffs_PoolEntry *e = (uffs_PoolEntry *)p; uffs_Assert(pool, "pool missing"); if (e) { uffs_SemWait(pool->sem); e->next = pool->free_list; pool->free_list = e; uffs_SemSignal(pool->sem); return 0; } return -1; }
static int program_sdata(uffs_Device *dev, int block, int page) { uffs_FileEmu *emu = (uffs_FileEmu *)(dev->attr->_private); int abs_page; struct uffs_StorageAttrSt *attr = dev->attr; u8 ecc_buf[RS_ECC_SIZE]; int writtern; // In the real world, MLC controller will generate RS-ECC code in serial data buffer // and might start auto programing NAND flash. Here, we use software ECC to emulate RS-ECC. memset(ecc_buf, 0xFF, sizeof(ecc_buf)); uffs_EccMake(g_sdata_buf, attr->page_data_size, ecc_buf); feed_sdata(ecc_buf, RS_ECC_SIZE); uffs_Assert(g_sdata_buf_pointer == PAGE_FULL_SIZE, "Serial Data Buffer is not fully filled !!"); abs_page = attr->pages_per_block * block + page; fseek(emu->fp, abs_page * PAGE_FULL_SIZE, SEEK_SET); writtern = fwrite(g_sdata_buf, 1, PAGE_FULL_SIZE, emu->fp); return (writtern == PAGE_FULL_SIZE) ? UFFS_FLASH_NO_ERR : UFFS_FLASH_IO_ERR; }
static URET do_FlushObject(uffs_Object *obj) { uffs_Device *dev; URET ret = U_SUCC; TreeNode *node = NULL; dev = obj->dev; if (obj->node) { node = obj->node; if (obj->type == UFFS_TYPE_DIR) ret = uffs_BufFlushGroup(dev, obj->node->u.dir.parent, obj->node->u.dir.serial); else { ret = ( uffs_BufFlushGroupMatchParent(dev, obj->node->u.file.serial) == U_SUCC && uffs_BufFlushGroup(dev, obj->node->u.file.parent, obj->node->u.file.serial) == U_SUCC ) ? U_SUCC : U_FAIL; } uffs_Assert(node == obj->node, "obj->node change!\n"); } return ret; }
/** * \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; }
/** * read data from obj * * \param[in] obj uffs object * \param[out] data output data buffer * \param[in] len required length of data to be read from object->pos * * \return return bytes of data have been read */ int uffs_ReadObject(uffs_Object *obj, void *data, int len) { uffs_Device *dev = obj->dev; TreeNode *fnode = NULL; u32 remain = len; u16 fdn; u32 read_start; TreeNode *dnode; u32 size; uffs_Buf *buf; u32 blockOfs; u16 page_id; u8 type; u32 pageOfs; if (obj == NULL) return 0; fnode = obj->node; if (obj->dev == NULL || obj->open_succ == U_FALSE) { obj->err = UEBADF; return 0; } if (obj->type == UFFS_TYPE_DIR) { uffs_Perror(UFFS_MSG_NOISY, "Can't read data from a dir object!"); obj->err = UEBADF; return 0; } if (obj->pos > fnode->u.file.len) { return 0; //can't read file out of range } if (obj->oflag & UO_WRONLY) { obj->err = UEACCES; return 0; } uffs_ObjectDevLock(obj); while (remain > 0) { read_start = obj->pos + len - remain; if (read_start >= fnode->u.file.len) { //uffs_Perror(UFFS_MSG_NOISY, "read point out of file ?"); break; } fdn = GetFdnByOfs(obj, read_start); if (fdn == 0) { dnode = obj->node; type = UFFS_TYPE_FILE; } else { type = UFFS_TYPE_DATA; dnode = uffs_TreeFindDataNode(dev, fnode->u.file.serial, fdn); if (dnode == NULL) { uffs_Perror(UFFS_MSG_SERIOUS, "can't get data node in entry!"); obj->err = UEUNKNOWN_ERR; break; } } blockOfs = GetStartOfDataBlock(obj, fdn); page_id = (read_start - blockOfs) / dev->com.pg_data_size; if (fdn == 0) { /** * fdn == 0: this means that the reading is start from the first block, * since the page 0 is for file attr, so we move to the next page ID. */ page_id++; } buf = uffs_BufGetEx(dev, type, dnode, (u16)page_id, obj->oflag); if (buf == NULL) { uffs_Perror(UFFS_MSG_SERIOUS, "can't get buffer when read obj."); obj->err = UEIOERR; break; } pageOfs = read_start % dev->com.pg_data_size; if (pageOfs >= buf->data_len) { //uffs_Perror(UFFS_MSG_NOISY, "read data out of page range ?"); uffs_BufPut(dev, buf); break; } size = (remain + pageOfs > buf->data_len ? buf->data_len - pageOfs : remain); uffs_BufRead(dev, buf, (u8 *)data + len - remain, pageOfs, size); uffs_BufPut(dev, buf); remain -= size; } obj->pos += (len - remain); if (HAVE_BADBLOCK(dev)) uffs_BadBlockRecover(dev); uffs_ObjectDevUnLock(obj); uffs_Assert(fnode == obj->node, "obj->node change!\n"); return len - remain; }
// // To trancate the file, this is the last block to be trancated. // We need to discard one or more pages within this block, hence requires 'block recover'. // static URET do_TruncateInternalWithBlockRecover(uffs_Object *obj, u16 fdn, u32 remain, RunOptionE run_opt) { uffs_Device *dev = obj->dev; TreeNode *fnode = obj->node; u16 page_id, max_page_id; TreeNode *node; uffs_Buf *buf = NULL; u8 type; u32 block_start, block_offset; u16 parent, serial; int slot; uffs_BlockInfo *bc = NULL; int block = -1; if (fdn == 0) { node = fnode; type = UFFS_TYPE_FILE; max_page_id = obj->head_pages; block_start = 0; parent = node->u.file.parent; serial = node->u.file.serial; block = node->u.file.block; } else { node = uffs_TreeFindDataNode(dev, fnode->u.file.serial, fdn); if (node == NULL) { obj->err = UEIOERR; uffs_Perror(UFFS_MSG_SERIOUS, "can't find data node when truncate obj"); goto ext; } block = node->u.data.block; type = UFFS_TYPE_DATA; max_page_id = dev->attr->pages_per_block - 1; block_start = obj->head_pages * dev->com.pg_data_size + (fdn - 1) * dev->com.pg_data_size * dev->attr->pages_per_block; parent = node->u.data.parent; serial = node->u.data.serial; } if (run_opt == eDRY_RUN) { // checking the buffer. this is the main reason why we need the 'dry run' mode. for (page_id = 0; page_id <= max_page_id; page_id++) { buf = uffs_BufFind(dev, parent, serial, page_id); if (buf) { //!< ok, the buffer was loaded before ... if (uffs_BufIsFree(buf) == U_FALSE) { obj->err = UEEXIST; break; //!< and someone is still holding the buffer, // can't truncate it !!! } } } buf = NULL; goto ext; } // find the last page *after* truncate block_offset = remain - block_start; page_id = block_offset / dev->com.pg_data_size; if (fdn == 0) page_id++; if (!uffs_Assert(page_id <= max_page_id, "fdn = %d, block_start = %d, remain = %d\n", fdn, block_start, remain)) { obj->err = UEUNKNOWN_ERR; goto ext; } // flush buffer before performing block recovery uffs_BufFlushGroup(dev, parent, serial); // load the last page buf = uffs_BufGetEx(dev, type, node, page_id, obj->oflag); if (buf == NULL) { obj->err = UENOMEM; uffs_Perror(UFFS_MSG_SERIOUS, "Can't get buf"); goto ext; } uffs_BufWrite(dev, buf, NULL, 0, 0); // just make this buf dirty // lock the group slot = uffs_BufFindGroupSlot(dev, parent, serial); uffs_BufLockGroup(dev, slot); if (remain == 0) // remain == 0: means discard all data in this block. buf->data_len = 0; else { remain = (remain % dev->com.pg_data_size); // remain == 0: means that we need to keep all data in this page. buf->data_len = (remain == 0 ? dev->com.pg_data_size : remain); } /* mark this buf as UFFS_BUF_EXT_MARK_TRUNC_TAIL, when flushing dirty buffers, UFFS will not do page recover for pages after this buf page id (because this file is ended at this page) */ buf->ext_mark |= UFFS_BUF_EXT_MARK_TRUNC_TAIL; uffs_BufPut(dev, buf); // invalidate the rest page buf page_id++; for (; page_id <= max_page_id; page_id++) { buf = uffs_BufFind(dev, parent, serial, page_id); if (buf) uffs_BufMarkEmpty(dev, buf); } // flush dirty buffer immediately, forcing block recovery. uffs_BufFlushGroupEx(dev, parent, serial, U_TRUE); // unlock the group uffs_BufUnLockGroup(dev, slot); // Invalidate block info cache for the 'old' block bc = uffs_BlockInfoGet(dev, block); if (bc) { uffs_BlockInfoExpire(dev, bc, UFFS_ALL_PAGES); uffs_BlockInfoPut(dev, bc); } ext: return (obj->err == UENOERR ? U_SUCC : U_FAIL); }
/** 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); }
/** * write data to obj, return remain data (0 if all data been written). */ static int do_WriteObject(uffs_Object *obj, const void *data, int len) { uffs_Device *dev = obj->dev; TreeNode *fnode = obj->node; int remain = len; u16 fdn; u32 write_start; TreeNode *dnode; u32 size; while (remain > 0) { write_start = obj->pos + len - remain; if (write_start > fnode->u.file.len) { uffs_Perror(UFFS_MSG_SERIOUS, "write point out of file ?"); break; } fdn = GetFdnByOfs(obj, write_start); if (write_start == fnode->u.file.len && fdn > 0 && write_start == GetStartOfDataBlock(obj, fdn)) { if (dev->tree.erased_count < dev->cfg.reserved_free_blocks) { uffs_Perror(UFFS_MSG_NOISY, "insufficient block in write obj, new block"); break; } size = do_WriteNewBlock(obj, data ? (u8 *)data + len - remain : NULL, remain, fnode->u.file.serial, fdn); // // Flush the new block buffers immediately, so that the new data node will be // created and put in the tree. // // But before do that, we need to make sure the previous // data block (if exist) been flushed first. // if (fdn > 1) { uffs_BufFlushGroup(dev, fnode->u.file.serial, fdn - 1); } else { uffs_BufFlushGroup(dev, fnode->u.file.parent, fnode->u.file.serial); } // Now flush the new block. uffs_BufFlushGroup(dev, fnode->u.file.serial, fdn); if (size == 0) break; remain -= size; } else { if(fdn == 0) dnode = obj->node; else dnode = uffs_TreeFindDataNode(dev, fnode->u.file.serial, fdn); if(dnode == NULL) { uffs_Perror(UFFS_MSG_SERIOUS, "can't find data node in tree ?"); obj->err = UEUNKNOWN_ERR; break; } size = do_WriteInternalBlock(obj, dnode, fdn, data ? (u8 *)data + len - remain : NULL, remain, write_start - GetStartOfDataBlock(obj, fdn)); #ifdef CONFIG_FLUSH_BUF_AFTER_WRITE if (fdn == 0) uffs_BufFlushGroup(dev, fnode->u.file.parent, fnode->u.file.serial); else uffs_BufFlushGroup(dev, fnode->u.file.serial, fdn); #endif if (size == 0) break; remain -= size; } } uffs_Assert(fnode == obj->node, "obj->node change!\n"); return remain; }