/** * Close an openned object. * * \param[in] obj object to be closed * \return U_SUCC or U_FAIL (error code in obj->err). */ URET uffs_CloseObject(uffs_Object *obj) { #ifdef CONFIG_CHANGE_MODIFY_TIME uffs_Device *dev; uffs_Buf *buf; uffs_FileInfo fi; #endif if(obj->dev == NULL || obj->open_succ != U_TRUE) { obj->err = UEBADF; goto ext; } uffs_ObjectDevLock(obj); if (obj->oflag & (UO_WRONLY|UO_RDWR|UO_APPEND|UO_CREATE|UO_TRUNC)) { #ifdef CONFIG_CHANGE_MODIFY_TIME dev = obj->dev; if (obj->node) { //need to change the last modify time stamp if (obj->type == UFFS_TYPE_DIR) buf = uffs_BufGetEx(dev, UFFS_TYPE_DIR, obj->node, 0, obj->oflag); else buf = uffs_BufGetEx(dev, UFFS_TYPE_FILE, obj->node, 0, obj->oflag); if(buf == NULL) { uffs_Perror(UFFS_MSG_SERIOUS, "can't get file header"); do_FlushObject(obj); uffs_ObjectDevUnLock(obj); goto ext; } uffs_BufRead(dev, buf, &fi, 0, sizeof(uffs_FileInfo)); fi.last_modify = uffs_GetCurDateTime(); uffs_BufWrite(dev, buf, &fi, 0, sizeof(uffs_FileInfo)); uffs_BufPut(dev, buf); } #endif do_FlushObject(obj); } uffs_ObjectDevUnLock(obj); ext: do_ReleaseObjectResource(obj); return (obj->err == UENOERR ? U_SUCC : U_FAIL); }
static int do_WriteNewBlock(uffs_Object *obj, const void *data, u32 len, u16 parent, u16 serial) { uffs_Device *dev = obj->dev; u16 page_id; int wroteSize = 0; int size; uffs_Buf *buf; URET ret; for (page_id = 0; page_id < dev->attr->pages_per_block; page_id++) { size = (len - wroteSize) > dev->com.pg_data_size ? dev->com.pg_data_size : len - wroteSize; if (size <= 0) break; buf = uffs_BufNew(dev, UFFS_TYPE_DATA, parent, serial, page_id); if (buf == NULL) { uffs_Perror(UFFS_MSG_SERIOUS, "can't create a new page ?"); break; } // Note: if data == NULL, we will fill '\0' ret = uffs_BufWrite(dev, buf, data == NULL ? NULL : (u8 *)data + wroteSize, 0, size); uffs_BufPut(dev, buf); if (ret != U_SUCC) { uffs_Perror(UFFS_MSG_SERIOUS, "write data fail!"); break; } wroteSize += size; obj->node->u.file.len += size; } return wroteSize; }
static int do_WriteInternalBlock(uffs_Object *obj, TreeNode *node, u16 fdn, const void *data, u32 len, u32 blockOfs) { uffs_Device *dev = obj->dev; u16 maxPageID; u16 page_id; u32 size; u32 pageOfs; u32 wroteSize = 0; URET ret; uffs_Buf *buf; u32 block_start; u8 type; u16 parent, serial; block_start = GetStartOfDataBlock(obj, fdn); if (fdn == 0) { type = UFFS_TYPE_FILE; parent = node->u.file.parent; serial = node->u.file.serial; } else { type = UFFS_TYPE_DATA; parent = node->u.data.parent; serial = fdn; } if (fdn == 0) maxPageID = obj->head_pages; else maxPageID = dev->attr->pages_per_block - 1; while (wroteSize < len) { page_id = blockOfs / dev->com.pg_data_size; if (fdn == 0) page_id++; //in file header, page_id start from 1, not 0. if (page_id > maxPageID) break; pageOfs = blockOfs % dev->com.pg_data_size; size = (len - wroteSize + pageOfs) > dev->com.pg_data_size ? (dev->com.pg_data_size - pageOfs) : (len - wroteSize); if ((obj->node->u.file.len % dev->com.pg_data_size) == 0 && (blockOfs + block_start) == obj->node->u.file.len) { buf = uffs_BufNew(dev, type, parent, serial, page_id); if(buf == NULL) { uffs_Perror(UFFS_MSG_SERIOUS, "can create a new buf!"); break; } } else { buf = uffs_BufGetEx(dev, type, node, page_id, obj->oflag); if (buf == NULL) { uffs_Perror(UFFS_MSG_SERIOUS, "can't get buffer ?"); break; } } // Note: if data == NULL, then we will fill '\0' ret = uffs_BufWrite(dev, buf, data == NULL ? NULL : (u8 *)data + wroteSize, pageOfs, size); uffs_BufPut(dev, buf); if (ret == U_FAIL) { uffs_Perror(UFFS_MSG_SERIOUS, "write inter data fail!"); break; } wroteSize += size; blockOfs += size; if (block_start + blockOfs > obj->node->u.file.len) obj->node->u.file.len = block_start + blockOfs; } return wroteSize; }
/** * Create an object under the given dir. * * \param[in|out] obj to be created, obj is returned from uffs_GetObject() * \param[in] dev uffs device * \param[in] dir object parent dir serial NO. * \param[in] name point to the object name * \param[in] name_len object name length * \param[in] oflag open flag. UO_DIR should be passed for an dir object. * * \return U_SUCC or U_FAIL (error code in obj->err). */ URET uffs_CreateObjectEx(uffs_Object *obj, uffs_Device *dev, int dir, const char *name, int name_len, int oflag) { uffs_Buf *buf = NULL; uffs_FileInfo fi; TreeNode *node; obj->dev = dev; obj->parent = dir; obj->type = (oflag & UO_DIR ? UFFS_TYPE_DIR : UFFS_TYPE_FILE); obj->name = name; obj->name_len = name_len; if (obj->type == UFFS_TYPE_DIR) { if (name[obj->name_len - 1] == '/') // get rid of ending '/' for dir obj->name_len--; } else { if (name[obj->name_len - 1] == '/') { // file name can't end with '/' obj->err = UENOENT; goto ext; } } if (obj->name_len == 0) { // empty name ? obj->err = UENOENT; goto ext; } obj->sum = uffs_MakeSum16(obj->name, obj->name_len); uffs_ObjectDevLock(obj); if (obj->type == UFFS_TYPE_DIR) { //find out whether have file with the same name node = uffs_TreeFindFileNodeByName(obj->dev, obj->name, obj->name_len, obj->sum, obj->parent); if (node != NULL) { obj->err = UEEXIST; // we can't create a dir has the // same name with exist file. goto ext_1; } obj->node = uffs_TreeFindDirNodeByName(obj->dev, obj->name, obj->name_len, obj->sum, obj->parent); if (obj->node != NULL) { obj->err = UEEXIST; // we can't create a dir already exist. goto ext_1; } } else { //find out whether have dir with the same name node = uffs_TreeFindDirNodeByName(obj->dev, obj->name, obj->name_len, obj->sum, obj->parent); if (node != NULL) { obj->err = UEEXIST; goto ext_1; } obj->node = uffs_TreeFindFileNodeByName(obj->dev, obj->name, obj->name_len, obj->sum, obj->parent); if (obj->node) { /* file already exist, truncate it to zero length */ obj->serial = GET_OBJ_NODE_SERIAL(obj); obj->open_succ = U_TRUE; // set open_succ to U_TRUE before // call do_TruncateObject() if (do_TruncateObject(obj, 0, eDRY_RUN) == U_SUCC) do_TruncateObject(obj, 0, eREAL_RUN); goto ext_1; } } /* dir|file does not exist, create a new one */ obj->serial = uffs_FindFreeFsnSerial(obj->dev); if (obj->serial == INVALID_UFFS_SERIAL) { uffs_Perror(UFFS_MSG_SERIOUS, "No free serial num!"); obj->err = UENOMEM; goto ext_1; } if (obj->dev->tree.erased_count < obj->dev->cfg.reserved_free_blocks) { uffs_Perror(UFFS_MSG_NOISY, "insufficient block in create obj"); obj->err = UENOMEM; goto ext_1; } buf = uffs_BufNew(obj->dev, obj->type, obj->parent, obj->serial, 0); if (buf == NULL) { uffs_Perror(UFFS_MSG_SERIOUS, "Can't create new buffer when create obj!"); goto ext_1; } memset(&fi, 0, sizeof(uffs_FileInfo)); fi.name_len = obj->name_len < sizeof(fi.name) ? obj->name_len : sizeof(fi.name) - 1; memcpy(fi.name, obj->name, fi.name_len); fi.name[fi.name_len] = '\0'; fi.access = 0; fi.attr |= FILE_ATTR_WRITE; if (obj->type == UFFS_TYPE_DIR) fi.attr |= FILE_ATTR_DIR; fi.create_time = fi.last_modify = uffs_GetCurDateTime(); uffs_BufWrite(obj->dev, buf, &fi, 0, sizeof(uffs_FileInfo)); uffs_BufPut(obj->dev, buf); // flush buffer immediately, // so that the new node will be inserted into the tree uffs_BufFlushGroup(obj->dev, obj->parent, obj->serial); // update obj->node: after buf flushed, // the NEW node can be found in the tree if (obj->type == UFFS_TYPE_DIR) obj->node = uffs_TreeFindDirNode(obj->dev, obj->serial); else obj->node = uffs_TreeFindFileNode(obj->dev, obj->serial); if (obj->node == NULL) { uffs_Perror(UFFS_MSG_NOISY, "Can't find the node in the tree ?"); obj->err = UEIOERR; goto ext_1; } if (obj->type == UFFS_TYPE_FILE) obj->node->u.file.len = 0; //init the length to 0 if (HAVE_BADBLOCK(obj->dev)) uffs_BadBlockRecover(obj->dev); obj->open_succ = U_TRUE; ext_1: uffs_ObjectDevUnLock(obj); ext: return (obj->err == UENOERR ? U_SUCC : U_FAIL); }
/** * Remove object under a new parent, change object name. * * \param[in|out] obj * \param[in] new_parent new parent's serial number * \param[in] new_name new name of the object. * if new_name == NULL, keep the old name. * \param[in] name_len new name length. * * \return U_SUCC or U_FAIL (obj->err for the reason) */ URET uffs_MoveObjectEx(uffs_Object *obj, int new_parent, const char *new_name, int name_len) { uffs_Buf *buf; uffs_FileInfo fi; uffs_Device *dev = obj->dev; TreeNode *node = obj->node; if (dev == NULL || node == NULL || obj->open_succ != U_TRUE) { obj->err = UEBADF; goto ext; } uffs_ObjectDevLock(obj); obj->parent = new_parent; if (name_len > 0) { buf = uffs_BufGetEx(dev, obj->type, node, 0, obj->oflag); if (buf == NULL) { uffs_Perror(UFFS_MSG_SERIOUS, "can't get buf when rename!"); obj->err = UEIOERR; goto ext_1; } memcpy(&fi, buf->data, sizeof(uffs_FileInfo)); if (new_name[name_len-1] == '/') name_len--; memcpy(fi.name, new_name, name_len); fi.name[name_len] = 0; fi.name_len = name_len; fi.last_modify = uffs_GetCurDateTime(); buf->parent = new_parent; // !! need to manually change the 'parent' !! uffs_BufWrite(dev, buf, &fi, 0, sizeof(uffs_FileInfo)); uffs_BufPut(dev, buf); // !! force a block recover so that all old tag will be expired !! // This is important so we only need to check // the first spare when mount UFFS :) uffs_BufFlushGroupEx(dev, obj->parent, obj->serial, U_TRUE); obj->name = new_name; obj->name_len = name_len; obj->sum = uffs_MakeSum16(fi.name, fi.name_len); } //update the check sum and new parent of tree node if (obj->type == UFFS_TYPE_DIR) { obj->node->u.dir.checksum = obj->sum; obj->node->u.dir.parent = new_parent; } else { obj->node->u.file.checksum = obj->sum; obj->node->u.file.parent = new_parent; } ext_1: uffs_ObjectDevUnLock(obj); ext: return (obj->err == UENOERR ? U_SUCC : U_FAIL); }
// // 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); }
static URET do_TruncateInternalWithBlockRecover(uffs_Object *obj, u16 fdn, u32 remain, UBOOL dry_run) { 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; u16 parent, serial; int slot; 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; } else { node = uffs_TreeFindDataNode(dev, fnode->u.file.serial, fdn); if (node == NULL) { obj->err = UEIOERR; uffs_Perror(UFFS_ERR_SERIOUS, "can't find data node when truncate obj"); goto ext; } 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 (dry_run == U_TRUE) { // 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 for (page_id = (fdn == 0 ? 1 : 0); page_id <= max_page_id; page_id++) { if (block_start + (page_id + 1) * dev->com.pg_data_size >= remain) break; } if (page_id > max_page_id) { obj->err = UEUNKNOWN; uffs_Perror(UFFS_ERR_SERIOUS, "Overflow"); 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); if (buf == NULL) { obj->err = UENOMEM; uffs_Perror(UFFS_ERR_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) buf->data_len = 0; else { remain = (remain % dev->com.pg_data_size); buf->data_len = (remain == 0 ? dev->com.pg_data_size : 0); } 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); ext: return (obj->err == UENOERR ? U_SUCC : U_FAIL); }