Ejemplo n.º 1
0
Archivo: uffs_fs.c Proyecto: mazj/uffs
/**
 * 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;
}
Ejemplo n.º 2
0
Archivo: uffs_fs.c Proyecto: mazj/uffs
static void do_ReleaseObjectResource(uffs_Object *obj)
{
	if (obj) {
		if (obj->dev) {
			if (HAVE_BADBLOCK(obj->dev))
				uffs_BadBlockRecover(obj->dev);
			if (obj->dev_lock_count > 0) {
				uffs_ObjectDevUnLock(obj);
			}
			uffs_PutDevice(obj->dev);
			obj->dev = NULL;
			obj->open_succ = U_FALSE;
		}
	}
}
Ejemplo n.º 3
0
Archivo: uffs_fs.c Proyecto: 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);
}
Ejemplo n.º 4
0
Archivo: uffs_fs.c Proyecto: mazj/uffs
/** 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);

}
Ejemplo n.º 5
0
Archivo: uffs_fs.c Proyecto: 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;
}
Ejemplo n.º 6
0
/**
 * 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;
}
Ejemplo n.º 7
0
/**
 * \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;
	TreeNode *node;
	uffs_Device *dev;
	u16 block;
	uffs_Buf *buf;
	URET ret = U_FAIL;

	obj = uffs_GetObject();
	if (obj == NULL) {
		if (err)
			*err = UEMFILE;
		goto err1;
	}

	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 err1;
		}
	}

	uffs_TruncateObject(obj, 0);

	uffs_ObjectDevLock(obj);
	dev = obj->dev;

	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 err;  //have sub dirs ?
		}

		node = uffs_TreeFindFileNodeWithParent(dev, obj->serial);
		if (node != NULL) 
		{
			if (err)
				*err = UEACCES;
			goto err;  //have sub files ?
		}
	}

	block = GET_BLOCK_FROM_NODE(obj);
	node = obj->node;

	// before erase the block, we need to take care of the buffer ...
	uffs_BufFlushAll(dev);

	if (HAVE_BADBLOCK(dev))
		uffs_BadBlockRecover(dev);

	buf = uffs_BufFind(dev, obj->parent, obj->serial, 0);

	if (buf) {
		//need to expire this buffer ...
		if (buf->ref_count != 0) {
			//there is other obj for this file still in use ?
			uffs_Perror(UFFS_ERR_NORMAL, "Try to delete object but still have buf referenced.");
			if (err)
				*err = UEACCES;
			goto err;
		}

		buf->mark = UFFS_BUF_EMPTY; //!< make this buffer expired.
	}

	//TODO: need to take care of other obj->node ?

	uffs_BreakFromEntry(dev, obj->type, node);
	uffs_FlashEraseBlock(dev, block);
	node->u.list.block = block;
	if (HAVE_BADBLOCK(dev))
		uffs_BadBlockProcess(dev, node);
	else
		uffs_TreeInsertToErasedListTail(dev, node);

	ret = U_SUCC;
err:
	uffs_ObjectDevUnLock(obj);
err1:
	do_ReleaseObjectResource(obj);

	uffs_PutObject(obj);

	return ret;
}
Ejemplo n.º 8
0
/** 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);

}