Example #1
0
/** compare [name] with tree [node] represented object name by loading
	uffs_FileInfo from storage */
UBOOL uffs_TreeCompareFileName(uffs_Device *dev,
							   const char *name, u32 len, u16 sum,
							   TreeNode *node, int type)
{
	UBOOL matched = U_FALSE;
	uffs_FileInfo *fi;
	uffs_Buf *buf;
	u16 data_sum;

	buf = uffs_BufGetEx(dev, type, node, 0, 0);
	if (buf == NULL) {
		uffs_Perror(UFFS_MSG_SERIOUS, "can't get buf !\n ");
		goto ext;
	}
	fi = (uffs_FileInfo *)(buf->data);
	data_sum = uffs_MakeSum16(fi->name, fi->name_len);

	if (data_sum != sum) {
		uffs_Perror(UFFS_MSG_NORMAL,
					"the obj's sum in storage is different with given sum!");
		goto ext;
	}

	if (fi->name_len == len) {
		if(uffs_CompareFileName(fi->name, fi->name_len, name) == U_TRUE) {
			matched = U_TRUE;
		}
	}
ext:
	if (buf)
		uffs_BufPut(dev, buf);

	return matched;
}
Example #2
0
/** 
 * create a new file on a free block
 * \param[in] dev uffs device
 * \param[in] parent parent dir serial num
 * \param[in] serial serial num of this new file
 * \param[in] bc block information
 * \param[in] fi file information
 * \note parent, serial, bc must be provided before,
 *		 and all information in fi should be filled well before.
 */
URET uffs_CreateNewFile(uffs_Device *dev,
						u16 parent, u16 serial,
						uffs_BlockInfo *bc, uffs_FileInfo *fi)
{
	uffs_Tags *tag;
	uffs_Buf *buf;

	uffs_BlockInfoLoad(dev, bc, 0);

	tag = GET_TAG(bc, 0);
	TAG_PARENT(tag) = parent;
	TAG_SERIAL(tag) = serial;
	TAG_DATA_LEN(tag) = sizeof(uffs_FileInfo);

	buf = uffs_BufGet(dev, parent, serial, 0);
	if (buf == NULL) {
		uffs_Perror(UFFS_MSG_SERIOUS, "get buf fail.");
		return U_FAIL;
	}

	memcpy(buf->data, fi, TAG_DATA_LEN(tag));
	buf->data_len = TAG_DATA_LEN(tag);

	return uffs_BufPut(dev, buf);
}
Example #3
0
static URET _LoadObjectInfo(uffs_Device *dev, TreeNode *node, uffs_ObjectInfo *info, int type, int *err)
{
	uffs_Buf *buf;

	buf = uffs_BufGetEx(dev, (u8)type, node, 0);

	if (buf == NULL) {
		if (err)
			*err = UENOMEM;
		return U_FAIL;
	}

	memcpy(&(info->info), buf->data, sizeof(uffs_FileInfo));

	if (type == UFFS_TYPE_DIR) {
		info->len = 0;
		info->serial = node->u.dir.serial;
	}
	else {
		info->len = node->u.file.len;
		info->serial = node->u.file.serial;
	}

	uffs_BufPut(dev, buf);

	return U_SUCC;
}
Example #4
0
File: uffs_fs.c Project: mazj/uffs
/**
 * 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);
}
Example #5
0
File: uffs_fs.c Project: mazj/uffs
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;
}
Example #6
0
File: uffs_fs.c Project: mazj/uffs
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;
}
Example #7
0
File: uffs_fs.c Project: mazj/uffs
/**
 * 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);
}
Example #8
0
File: uffs_fs.c Project: mazj/uffs
/**
 * 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);
}
Example #9
0
File: uffs_fs.c Project: mazj/uffs
//
// 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);
}
Example #10
0
File: uffs_fs.c Project: mazj/uffs
/**
 * 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;
}
Example #11
0
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);
}