/** * \brief check if there are anyone holding buf of this obj. * If no one holding the buffers, expire the buffer. * \return * 0 : no one holding any buf of this obj * >0 : the ref_count of buf which refer to this obj. */ int _CheckObjBufRef(uffs_Object *obj) { uffs_Device *dev = obj->dev; uffs_Buf *buf; TreeNode *node = obj->node; u16 parent, serial, last_serial; // check the DIR or FILE block for (buf = uffs_BufFind(dev, obj->parent, obj->serial, UFFS_ALL_PAGES); buf != NULL; buf = uffs_BufFindFrom(dev, buf->next, obj->parent, obj->serial, UFFS_ALL_PAGES)) { if (buf->ref_count > 0) { // oops ... uffs_Perror(UFFS_MSG_SERIOUS, "someone still hold buf parent = %d, serial = %d, ref_count", obj->parent, obj->serial, buf->ref_count); return buf->ref_count; } else { buf->mark = UFFS_BUF_EMPTY; } } if (buf == NULL || buf->ref_count == 0) { // check the DATA block if (obj->type == UFFS_TYPE_FILE && node->u.file.len > 0) { parent = obj->serial; last_serial = GetFdnByOfs(obj, node->u.file.len - 1); for (serial = 1; serial <= last_serial; serial++) { for (buf = uffs_BufFind(dev, parent, serial, UFFS_ALL_PAGES); buf != NULL; buf = uffs_BufFindFrom(dev, buf->next, parent, serial, UFFS_ALL_PAGES)) { if (buf->ref_count != 0) { // oops ... uffs_Perror(UFFS_MSG_SERIOUS, "someone still hold buf parent = %d, serial = %d, ref_count", parent, serial, buf->ref_count); return buf->ref_count; } else { buf->mark = UFFS_BUF_EMPTY; } } } } } return 0; }
/** * 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; }
/** * \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); }
/** * 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; }
/** * 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 = obj->node; int remain = len; u16 fdn; u32 write_start; TreeNode *dnode; u32 size; 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_ERR_NOISY, "Can't write to an dir object!"); obj->err = UEACCES; return 0; } if (obj->pos > fnode->u.file.len) { return 0; //can't write file out of range } if (obj->oflag == UO_RDONLY) { obj->err = UEACCES; return 0; } uffs_ObjectDevLock(obj); if (obj->oflag & UO_APPEND) obj->pos = fnode->u.file.len; while (remain > 0) { write_start = obj->pos + len - remain; if (write_start > fnode->u.file.len) { uffs_Perror(UFFS_ERR_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 < MINIMUN_ERASED_BLOCK) { uffs_Perror(UFFS_ERR_NOISY, "insufficient block in write obj, new block"); break; } size = do_WriteNewBlock(obj, (u8 *)data + len - remain, remain, fnode->u.file.serial, fdn); //Flush immediately, so that the new data node will be created and put in the tree. 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_ERR_SERIOUS, "can't find data node in tree ?"); obj->err = UEUNKNOWN; break; } size = do_WriteInternalBlock(obj, dnode, fdn, (u8 *)data + len - remain, remain, write_start - GetStartOfDataBlock(obj, fdn)); #ifdef CONFIG_FLUSH_BUF_AFTER_WRITE uffs_BufFlushGroup(dev, fnode->u.file.serial, fdn); #endif if (size == 0) break; remain -= size; } } obj->pos += (len - remain); if (HAVE_BADBLOCK(dev)) uffs_BadBlockRecover(dev); uffs_ObjectDevUnLock(obj); return len - remain; }
/** 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); }