ASR_BASE

Change-Id: Icf3719cc0afe3eeb3edc7fa80a2eb5199ca9dda1
diff --git a/marvell/lte-telephony/apps/NVM_Proxy/source/NVMFileOps.c b/marvell/lte-telephony/apps/NVM_Proxy/source/NVMFileOps.c
new file mode 100644
index 0000000..a12f27f
--- /dev/null
+++ b/marvell/lte-telephony/apps/NVM_Proxy/source/NVMFileOps.c
@@ -0,0 +1,950 @@
+/******************************************************************************
+*(C) Copyright 2011 Marvell International Ltd.
+* All Rights Reserved
+******************************************************************************/
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/vfs.h>
+#include <sys/stat.h>
+#include <libgen.h>
+#include <dirent.h>
+#include <fnmatch.h>
+#include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <sys/prctl.h>
+#include <sys/time.h>
+#include <sys/signal.h>
+#include <unistd.h>
+#include "NVMFileOps.h"
+#include "NVMServer_defs.h"
+#include "pxa_dbg.h"
+#include "nvm_shared.h"
+#include "nvm_copy.h"
+
+#define NVM_TMP_EXT_LEN  4 /*=strlen(.tmp) but in compile time*/
+
+#define NVM_FILE_NAME_MAX_LENGTH_LONG  128
+
+#define NVM_FLAG_IS_COMM 0x1
+#define NVM_FLAG_IS_DIR 0x2
+#define NVM_FLAG_IS_READONLY 0x4
+
+pthread_mutex_t lock;
+#ifdef NVMS_SYNC_THREAD_ENA
+pthread_mutex_t syncLock;
+pthread_t syncTaskRef;
+sem_t syncSem;
+#endif
+char rootDir[80];
+
+//#undef DBGMSG
+//#define DBGMSG(...) /* */
+
+#ifndef BIONIC
+/* There is well known problem with huge file operation time
+ * occurred on low-memory.
+ * The problem may occur on BIONIC also but is critical on
+ * non-BIONIC low-memory devices using JFFS file system.
+ * Hige latency could cause failure. Let's detect TimeOut and
+ * print message into kmsg-buffer; in case of N timeouts
+ * try to call panic.
+ * Mostly critical are sync/fflush commands...
+ */
+#define NVMS_GUARD_TIMER
+#endif
+
+#ifndef NVMS_GUARD_TIMER
+static void NVMS_GuardTimerInit(void)	{}
+static void NVMS_GuardTimerStart(int nn)	{}
+static void NVMS_GuardTimerStop(void)	{}
+#else
+static int NVMS_GuardErrMsg_fd = -1;
+static int NVMS_Guard;
+static int NVMS_GuardOpTrack;
+
+#define NVMS_GUARD_SEC           4
+#define NVMS_GUARD_NUM_TO_PANIC  5 /* equal 5*4=16sec */
+/* Enable NVMS_GUARD_PANIC only if NVMS knows to stop guard-timer or
+ * abort file-operation (and stop-timer) on cp-assert
+ * CP-logging is extreemly long on JFFS, guard may be started just before
+ * cp-assert and could abort (by panic) the logging.
+*/
+//#define NVMS_GUARD_PANIC
+
+
+static void NVMS_GuardTimerStart(int nn);
+
+void lockFile(void) { pthread_mutex_lock(&lock); }
+void unlockFile(void) { pthread_mutex_unlock(&lock); }
+
+FileItem* getItemByFd(int fd, List list)
+{
+	FileItem* Item;
+	Iterator it;
+	unsigned int cnt=0;
+
+	for (it = Begin(list); it != End(list); it = GetNext(it))
+	{
+		Item = it->data;
+		cnt++;
+		if (Item->fd == fd)
+		{
+			DBGMSG("getItemByFd %d=%d\r\n",fd, cnt);
+			return Item;
+		}
+	}
+
+	return NULL;
+}
+
+static void NVMS_GuardTimerHandler(int signum, struct siginfo *pInfo, void *p)
+{
+	(void)signum;	// unused
+	(void)pInfo;	// unused
+	(void)p;	//unused
+#ifdef NVMS_GUARD_PANIC
+	FILE *fd;
+#endif
+	if (!NVMS_Guard)
+		return; //already deleted
+	if (NVMS_GuardErrMsg_fd >= 0) {
+		char buf[80];
+		int len;
+		len = sprintf(buf, "[NVMS] file opTrackNo_%d TimeOut (%d*%dsec)\n",
+			NVMS_GuardOpTrack, NVMS_Guard, NVMS_GUARD_SEC);
+		write(NVMS_GuardErrMsg_fd, buf, len);
+	}
+	if (NVMS_Guard >= NVMS_GUARD_NUM_TO_PANIC) {
+#ifdef NVMS_GUARD_PANIC
+        pxa_dbg("NVMS_GUARD_PANIC!!!\n");
+		fd = fopen("/dev/ramdump_ctl", "w");
+		fprintf(fd, "p_NVMS_GUARD_TIMER") ;
+		fclose(fd);
+#else
+		// do not restart timer; stop printing
+		NVMS_Guard = 0;
+#endif
+	} else {
+		//Re-start the timer
+		NVMS_GuardTimerStart(NVMS_GuardOpTrack);
+	}
+}
+
+static void NVMS_GuardTimerInit(void)
+{
+	/* set up the handler */
+	struct sigaction act;
+	act.sa_sigaction = NVMS_GuardTimerHandler;
+	sigemptyset(&act.sa_mask);
+	act.sa_flags = 0;
+	NVMS_Guard = 0;
+	NVMS_GuardOpTrack = 0;
+	sigaction(SIGALRM, &act, NULL);
+	/* kmsg must be opened on init but not on failure */
+	NVMS_GuardErrMsg_fd = open("/dev/kmsg", O_WRONLY | O_SYNC);
+}
+
+static void NVMS_GuardTimerStart(int nn)
+{
+	int rc;
+	struct itimerval interval_timer;
+	interval_timer.it_interval.tv_usec = 0;
+	interval_timer.it_interval.tv_sec = 0;
+	interval_timer.it_value.tv_usec = 0;
+	interval_timer.it_value.tv_sec = NVMS_GUARD_SEC;
+	rc = setitimer(ITIMER_REAL, &interval_timer, NULL);
+	if (!rc) {
+		NVMS_Guard++;
+		NVMS_GuardOpTrack = nn;
+	}
+}
+
+static void NVMS_GuardTimerStop(void)
+{
+	struct itimerval interval_timer;
+	if (!NVMS_Guard)
+		return;		//timer is not running.
+	NVMS_Guard = 0;
+	memset(&interval_timer, 0, sizeof(interval_timer));
+	setitimer(ITIMER_REAL, &interval_timer, NULL);
+}
+#endif//NVMS_GUARD_TIMER
+
+
+static int nvm_dir_fsync(const char* dirName)
+{
+	int ret = -1;
+	if (dirName != NULL)
+	{
+		int fid;
+		FILE*  fd;
+		fd = fopen(dirName, "rb");
+		if (fd == NULL)
+		{
+			ERRMSG("ERR: DIR fopen(%s) fail:%s", dirName, strerror(errno));
+		}
+		else
+		{
+			fid = fileno(fd);
+			ret = fsync(fid);
+			if (ret != 0)
+			{
+				ERRMSG("ERR: DIR fsync(%d) fail:%s", fid, strerror(errno));
+			}
+			fclose(fd);
+		}
+	}
+	return(ret);
+}
+
+static int is_tmpfile_required(const char * file_name __attribute__ ((unused)), const char * szAttrib)
+{
+	return ((szAttrib[0] == 'w') || (szAttrib[0] == 'a') ||
+		(szAttrib[1] == '+') || (szAttrib[2] == '+'));
+}
+
+static void removeTmpfile(const char* dirname)
+{
+	DIR* dirstream;
+	struct dirent* ent;
+	char* pos;
+	int len, offsTMP;
+
+	DBGMSG("scan for tmp file");
+	dirstream = opendir(dirname);
+	if (dirstream == NULL)
+	{
+		ERRMSG("failed to open dir %s", dirname);
+		return;
+	}
+
+	while ((ent = readdir(dirstream)))
+	{
+		if (ent->d_type != DT_REG)
+			continue; //not a regular file
+
+		len = strlen(ent->d_name);
+		if (len <= NVM_TMP_EXT_LEN)
+			continue; //name too short to be "*.tmp"
+
+		offsTMP = len - NVM_TMP_EXT_LEN;
+		if (strcmp(ent->d_name + offsTMP, ".tmp"))
+			continue; //not "*.tmp"
+
+		unlink(ent->d_name);
+		DBGMSG("unlink tmp file [%s]", ent->d_name);
+	}
+	/* TMP remove is not critical. Do not flush/sync for it */
+	closedir(dirstream);
+}
+
+#ifdef NVMS_SYNC_THREAD_ENA
+extern "C" void* syncTask(void* param)
+{
+#error NVMS sync thread is obsolet
+	return 0;
+}
+#endif
+
+
+void NVMFileOps(const char* dir, bool async __attribute__ ((unused)))
+{
+	pthread_mutex_init(&lock, NULL);
+	rootDir[0] = '\0';
+	strncat(rootDir, dir, sizeof(rootDir) - 1);
+	chdir(rootDir);
+	removeTmpfile(rootDir);
+	NVMS_GuardTimerInit();
+}
+
+NVM_STATUS_T FileDelete(const char* szFileName)
+{
+	if (unlink(szFileName) == 0)
+	{
+		DBGMSG("unlink(%s) done\n", szFileName);
+		return NVM_STATUS_SUCCESS;
+	}
+	else
+	{
+		ERRMSG("unlink %s failed:%s\n", szFileName, strerror(errno));
+		return NVM_STATUS_FAILURE; //todo how we handle this?- just output error message
+	}
+}
+
+int FileFindEntry(DIR** dirstream, const char* szFileName,
+	NVM_FILE_INFO* pFindResults, unsigned *fStatus)
+{
+	struct dirent* ent;
+	struct stat fs;
+	int ret, file_date, file_time;
+	struct tm gm_time;
+	char *pdir, *pbase, *fileName;
+	char dir[NVM_FILE_NAME_MAX_LENGTH_LONG];
+	char base[NVM_FILE_NAME_MAX_LENGTH_LONG];
+	char path[NVM_FILE_NAME_MAX_LENGTH_LONG];
+	int  nameOffs;
+
+	dir[0] = '\0';
+	strncat(dir, szFileName, NVM_FILE_NAME_MAX_LENGTH_LONG - 1);
+	base[0] = '\0';
+	strncat(base, szFileName, NVM_FILE_NAME_MAX_LENGTH_LONG - 1);
+	pdir = dirname(dir);
+	pbase = basename(base);
+
+	if (!*dirstream)
+	{
+		*dirstream = opendir(pdir);
+		if (!*dirstream)
+		{
+			DBGMSG("failed to open dir %s", pdir);
+			return -1;
+		}
+	}
+	snprintf(path, NVM_FILE_NAME_MAX_LENGTH_LONG, "%s/", pdir);
+	nameOffs = strlen(path);
+
+	while ((ent = readdir(*dirstream)))
+	{
+		if (strcmp(ent->d_name, "..") == 0 || strcmp(ent->d_name, ".") == 0)
+			continue;
+
+        //DBGMSG("%s: ent->d_name[%s], ent->d_type[%d]", __FUNCTION__, ent->d_name, ent->d_type);
+        //DBGMSG("%s: path[%s], pdir[%s], pbase[%s]", __FUNCTION__, path, pdir, pbase);
+        //DBGMSG("%s: pFindResults->plr_date[0x%lx]", __FUNCTION__, pFindResults->plr_date);
+        /* current request from cp NVM, ignore cp NVM directory file */
+        if ( (pFindResults->plr_date == 0xCFCFCFCF) && (ent->d_type == DT_DIR))
+                continue;
+
+		/* Found */
+		if (fnmatch(pbase, ent->d_name, 0) == 0)
+		{
+			strncat(path + nameOffs, ent->d_name, NVM_FILE_NAME_MAX_LENGTH_LONG - nameOffs - 1);
+			ret = stat(path, &fs);
+			if (!ret)
+			{
+				memset(pFindResults, 0, sizeof(NVM_FILE_INFO));
+				strncat(pFindResults->file_name, ent->d_name, sizeof(pFindResults->file_name) - 1);
+				break; /* OK, entry is valid; otherwise continue as non-statable entries might exist, e.g. softlink with bad target */
+			}
+			else
+			{
+				ret = errno;
+				ERRMSG("Failed to get file info[%s]: %s\n", ent->d_name, strerror(ret));
+				path[nameOffs] = '\0';   //reset file name
+			}
+		}
+	}
+
+	if (ent)
+	{
+		DBGMSG("readdir() return [%s]\n", ent->d_name);
+		gmtime_r((const time_t *)&(fs.st_mtime), &gm_time);
+		file_date = gm_time.tm_mday << (12 + 4);
+		file_date += (gm_time.tm_mon + 1) << 12;
+		file_date += gm_time.tm_year + 1900;
+		file_time = gm_time.tm_hour << (6 + 6);
+		file_time += gm_time.tm_min << 6;
+		file_time += gm_time.tm_sec;
+		pFindResults->size = fs.st_size;
+		pFindResults->date = file_date;
+		pFindResults->time = file_time;
+		*fStatus = S_ISDIR(fs.st_mode) ? NVM_DIR_MASK | NVM_FILE_MASK
+			: NVM_FILE_MASK;
+		return 0;
+	}
+	return -1;
+}
+
+NVM_STATUS_T FileFindFirst(int ClientID, const char* szFileName,
+	NVM_FILE_INFO* pFindResults, unsigned *fStatus, int *searchHandle, List list)
+{
+	DIR* dirstream = NULL;
+	int ret;
+	FileItem* item;
+
+	DBGMSG("Find First [%s]\n", szFileName);
+
+	ret = FileFindEntry(&dirstream, szFileName, pFindResults, fStatus);
+	if (!ret)
+	{
+		item = (FileItem*)malloc(sizeof(*item));
+		if (!item)
+		{
+			ERRMSG("failed to add file list for FindNext.\n");
+			closedir(dirstream);
+			*searchHandle = -1;
+			return NVM_STATUS_FAILURE;
+		}
+		strcpy(item->fName, szFileName);
+		item->fd = dirfd(dirstream);
+		item->handle = dirstream;
+		item->flag = NVM_FLAG_IS_DIR;
+		if (ClientID == NVM_CLIENT_COMM)
+			item->flag |= NVM_FLAG_IS_COMM;
+
+		lockFile();
+		Append(list, item);
+		unlockFile();
+
+		*searchHandle = item->fd;
+		if (ClientID == NVM_CLIENT_ACAT)
+			usleep(10000); //Prevents CPU starvation caused by ACAT APPS
+		free(item);
+		return NVM_STATUS_SUCCESS;
+	}
+	else
+	{
+		DBGMSG("failed to FileFindFirst a matching file");
+		if (dirstream)
+			closedir(dirstream);
+		*searchHandle = -1;
+		return NVM_STATUS_FAILURE;
+	}
+}
+
+NVM_STATUS_T FileFindNext(NVM_FILE_INFO* pFindResults,
+	unsigned* fStatus, int searchHandle, List list)
+{
+	DIR* dirstream;
+	int ret;
+	char path[NVM_FILE_NAME_MAX_LENGTH_LONG];
+	FileItem* item;
+
+	lockFile();
+	item = getItemByFd(searchHandle, list);
+	if (item == NULL || !item->fName[0] || item->handle == NULL)
+	{
+		unlockFile();
+		ERRMSG("Invalid dir item\n");
+		return NVM_STATUS_FAILURE;
+	}
+	dirstream = (DIR*)item->handle;
+	path[0] = '\0';
+	strncat(path, item->fName, NVM_FILE_NAME_MAX_LENGTH_LONG - 1);
+	DBGMSG("Find Next [%s]\n", item->fName);
+	unlockFile();
+
+	ret = FileFindEntry(&dirstream, path, pFindResults, fStatus);
+	if (!ret)
+		return NVM_STATUS_SUCCESS;
+	else
+	{
+		DBGMSG("failed to FileFindNext a matching file"); //=OK, end of dir-list
+		return NVM_STATUS_FAILURE;
+	}
+}
+
+
+NVM_STATUS_T FileFindClose(int searchHandle, List list)
+{
+	NVM_STATUS_T rc = NVM_STATUS_SUCCESS;
+	FileItem* item;
+	DIR* dirstream;
+
+	lockFile();
+//	item = removeItemByFd(searchHandle, list);
+	item = getItemByFd(searchHandle, list);
+//	unlockFile();
+	if (item != NULL)
+	{
+		DBGMSG("FileFindClose [%s]", item->fName);
+		dirstream = (DIR*)item->handle;
+		if (dirstream == NULL)
+			rc = NVM_STATUS_FAILURE;
+		else
+			closedir(dirstream);
+		if (!item->fName[0])
+			rc = NVM_STATUS_FAILURE;
+		item = removeItemByFd(searchHandle, list);
+	}
+	else
+	{
+		ERRMSG("cannot find dir handle %d", searchHandle);
+	}
+    unlockFile();
+	return rc;
+}
+
+NVM_STATUS_T GetFileSize(const char* szFileName,
+	unsigned* pdwSizeHigh, unsigned* pdwSizeLow)
+{
+	struct stat fs;
+	int ret;
+
+	ret = stat(szFileName, &fs);
+	if (ret != 0)
+	{
+		DBGMSG("failed to open the file [%s].\n", szFileName);
+		return NVM_STATUS_FAILURE;
+	}
+	*pdwSizeLow = fs.st_size;
+	*pdwSizeHigh = 0;
+	DBGMSG("%s size = %lld\n", szFileName, fs.st_size);
+	return NVM_STATUS_SUCCESS;
+}
+
+
+NVM_STATUS_T FileOpen(int ClientID, const char* szFileName,
+	const char* szAttrib, unsigned* hFile, List list)
+{
+	FILE* fp;
+	int ret;
+	FileItem* item;
+
+	item = (FileItem *)malloc(sizeof(*item));
+	if (!item)
+	{
+		ERRMSG("Cannot add file [%s] with attrib [%s] to file list\n", szFileName, szAttrib);
+		return NVM_STATUS_FAILURE;
+	}
+	item->fName[0] = 0; // no one cares about file name in read only mode
+	item->flag = 0;
+	if (is_tmpfile_required(szFileName, szAttrib))
+	{ //open for Write
+        if (strlen(szFileName) > NAME_MAX)
+        {
+            ERRMSG("file [%s] length too long!!!\n", szFileName);
+            return NVM_STATUS_FAILURE;
+        }
+		strcpy(item->fName, szFileName);
+		NVMS_GuardTimerStart(1);
+		fp = FileOpenSafe(szFileName, szAttrib);
+		NVMS_GuardTimerStop();//1
+	}
+	else
+	{ //open for Read
+		item->flag |= NVM_FLAG_IS_READONLY;
+		NVMS_GuardTimerStart(2);
+		fp = fopen(szFileName, szAttrib);
+		NVMS_GuardTimerStop();//2
+	}
+
+	if (!fp)
+	{
+		DBGMSG("Cannot open file [%s] with attrib [%s]\n", szFileName, szAttrib);
+		free(item);
+		return NVM_STATUS_FAILURE;
+	}
+
+	DBGMSG("FileOpen(%s with %s) returns %p\n", szFileName, szAttrib, fp);
+	item->fd = fileno(fp);
+	item->handle = fp;
+	if (ClientID == NVM_CLIENT_COMM)
+		item->flag |= NVM_FLAG_IS_COMM;
+	lockFile();
+	Append(list, item);
+	unlockFile();
+	*hFile = item->fd;
+	free(item);
+	return NVM_STATUS_SUCCESS;
+}
+
+NVM_STATUS_T FileWrite(unsigned hFile, const void* pBuffer, unsigned dwBufferLen,
+	short wItemSize, unsigned dwCount, unsigned* pdwActual, List list)
+{
+	UNUSEDPARAM(dwBufferLen)
+	FILE *fp;
+	NVM_STATUS_T ret = NVM_STATUS_SUCCESS;
+	FileItem* item;
+
+	*pdwActual = 0;
+	if ((wItemSize == 0) || (dwCount == 0))
+		return NVM_STATUS_FAILURE;
+
+	lockFile();
+	item = getItemByFd(hFile, list);
+	if (!item)
+	{
+		unlockFile();
+		return NVM_STATUS_FAILURE;
+	}
+	fp = (FILE*)item->handle;
+	unlockFile();
+	DBGMSG("FileWrite(%p, %d * %d)\n", fp, wItemSize, dwCount);
+	NVMS_GuardTimerStart(3);
+	*pdwActual = fwrite(pBuffer, wItemSize, dwCount, fp);
+	NVMS_GuardTimerStop();//3
+	if (ferror(fp))
+	{
+		ERRMSG("FileWrite: File write error %d, %s.[%p].\n", *pdwActual, strerror(errno), fp);
+		clearerr(fp);
+		ret = NVM_STATUS_FAILURE;
+	}
+	DBGMSG("FileWrite(%p, %d * %d) returns %d\n", fp, wItemSize, dwCount, *pdwActual);
+	return ret;
+}
+
+NVM_STATUS_T FileFlush(unsigned hFile, List list)
+{
+	FILE *fp;
+	FileItem* item;
+
+	lockFile();
+	item = getItemByFd(hFile, list);
+	if (!item)
+	{
+		unlockFile();
+		return NVM_STATUS_FAILURE;
+	}
+	fp = (FILE*)item->handle;
+	unlockFile();
+	int ret = fflush(fp);
+	DBGMSG("FileFlush(%p) returns %d\n", fp, ret);
+	return NVM_STATUS_SUCCESS;
+}
+
+NVM_STATUS_T FileSeek(unsigned hFile, long dwOffset, int dwOrigin, List list)
+{
+	FILE *fp;
+	FileItem* item;
+
+	lockFile();
+	item = getItemByFd(hFile, list);
+	if (!item)
+	{
+		unlockFile();
+		return NVM_STATUS_FAILURE;
+	}
+	fp = (FILE*)item->handle;
+	unlockFile();
+	int ret = fseek(fp, dwOffset, dwOrigin);
+	DBGMSG("FileSeek(%p at %d from %d) returns %d\n", fp, dwOffset, dwOrigin, ret);
+	if (ret == 0)
+		return NVM_STATUS_SUCCESS;
+	else
+		return NVM_STATUS_FAILURE;
+}
+
+NVM_STATUS_T FileRead(unsigned hFile, short wItemSize, unsigned dwCount,
+	unsigned* pdwActual, void* pBuffer, List list)
+{
+	FILE *fp;
+	FileItem* item;
+
+	lockFile();
+	item = getItemByFd(hFile, list);
+	if (!item)
+	{
+		unlockFile();
+		return NVM_STATUS_FAILURE;
+	}
+	fp = (FILE*)item->handle;
+	unlockFile();
+	if (feof(fp))
+	{
+		ERRMSG("FileRead: File pointer is at the end of the file!.[%p].\n", fp);
+    }
+    DBGMSG("FileRead(%p)\n", fp);
+
+	NVMS_GuardTimerStart(4);
+	*pdwActual = fread(pBuffer, wItemSize, dwCount, fp);
+	NVMS_GuardTimerStop();//4
+	if (ferror(fp))
+	{
+		ERRMSG("FileRead(%s) error", item->fName);
+		clearerr(fp);
+	}
+
+	DBGMSG("FileRead(%p %d * %d) returns %d\n", fp, wItemSize, dwCount, *pdwActual);
+
+	return NVM_STATUS_SUCCESS;
+}
+
+NVM_STATUS_T FileClose(int ClientID, unsigned hFile, List list)
+{
+	int ret = 0;
+	FileItem* item;
+	FILE* fp;
+
+	lockFile();
+	item = getItemByFd(hFile, list);
+//	item = removeItemByFd(hFile, list);
+	if (!item)
+	{
+		unlockFile();
+		ERRMSG("FileClose: cannot find file handler [%u].\n", hFile);
+		return NVM_STATUS_FAILURE;
+	}
+	fp = (FILE*)item->handle;
+	if (item->flag & NVM_FLAG_IS_READONLY)
+	{
+		//No NVMS_GuardTimerStart() required since no problems seen
+		ret = fclose(fp);
+	}
+	else
+	{
+		NVMS_GuardTimerStart(6);
+        ERRMSG("FileClose: fflush fp[0x%lx].\n", fp);
+		fflush(fp);
+		ret = FileCloseSafe(item); //close with rename
+		NVMS_GuardTimerStop();//6
+	}
+	item = removeItemByFd(hFile, list);
+	
+	unlockFile();
+	if (ClientID != NVM_CLIENT_ACAT)
+	{
+		if (ret == 0)
+			ret = NVM_STATUS_SUCCESS;
+		else
+			ret = NVM_STATUS_FS_ERROR;
+	}
+	else
+	{
+		usleep(10000); //Prevents CPU starvation caused by ACAT APPS
+		ret = NVM_STATUS_SUCCESS;
+	}
+	DBGMSG("FileClose returns %d\n", ret);
+	return (NVM_STATUS_T)ret;
+}
+
+NVM_STATUS_T FileCloseAll(int ClientID, List list)
+{
+	bool type, client = (NVM_CLIENT_COMM == ClientID);
+	size_t file_num = 0, dir_num = 0, remain;
+	int wr_cnt = 0;
+
+	FileItem* Item = NULL;
+	Iterator it;
+	Iterator it2;
+
+	lockFile();
+
+	for (it = Begin(list); it != End(list);)
+	{
+		Item = it->data;
+		if (Item->flag & NVM_FLAG_IS_DIR)
+		{
+			DBGMSG("close dir %s", Item->fName);
+			closedir((DIR*)Item->handle);
+			++dir_num;
+		}
+		else
+		{
+			// close but not safe. TMP removed at the end
+			if (!(Item->flag & NVM_FLAG_IS_READONLY))
+				wr_cnt++;
+			fclose((FILE*)Item->handle);
+			++file_num;
+		}
+		/* get next item before free current item */
+		it2 = GetNext(it);
+		Remove(list, it);
+		it = it2;
+	}
+	remain = GetLength(list);
+
+	unlockFile();
+	DBGMSG("FileCloseAll: closed %zu dirs, %zu files, %zu left", dir_num, file_num, remain);
+	if (wr_cnt)
+		removeTmpfile(rootDir);
+	return NVM_STATUS_SUCCESS;
+}
+
+NVM_STATUS_T FileRename(const char* szOldFileName, const char* szNewFileName)
+{
+	int ret;
+
+	ret = rename(szOldFileName, szNewFileName);
+	DBGMSG("rename(%s --> %s) returns %d\n", szOldFileName, szNewFileName, ret);
+	if (ret == 0)
+		return NVM_STATUS_SUCCESS;
+	else
+	{
+		ERRMSG("rename %s to %s failed\n", szOldFileName, szNewFileName);
+		return NVM_STATUS_FAILURE; //todo how we handle this??- just output error messages now.
+	}
+}
+
+NVM_STATUS_T GetFileStat(const char* szFileName, int* dwStat)
+{
+	struct stat fs;
+	int ret;
+
+	ret = stat(szFileName, &fs);
+	DBGMSG("stat(%s) returns %d\n", szFileName, ret);
+	if (ret == 0)
+	{
+		*dwStat = S_ISDIR(fs.st_mode)
+			? NVM_DIR_MASK | NVM_FILE_MASK
+			: NVM_FILE_MASK;
+		return NVM_STATUS_SUCCESS;
+	}
+	else
+		return NVM_STATUS_FAILURE;
+}
+
+NVM_STATUS_T FileChangeMode(const char* szFileName, mode_t dwNewMode)
+{
+	int ret;
+
+	ret = chmod(szFileName, dwNewMode);
+	DBGMSG("%chmod(%s, %u) returns %d\n", szFileName, dwNewMode, ret);
+	if (ret == 0)
+		return NVM_STATUS_SUCCESS;
+	else
+		return NVM_STATUS_FAILURE;
+}
+
+NVM_STATUS_T GetAvailSpace(const char* szVol, unsigned* pdwSize)
+{
+	struct statfs fs;
+
+	/*FIXME PC tool sends Unicode16 for szVol, disable szVol param for now */
+    szVol = "/NVM/";
+	if (statfs(szVol, &fs) < 0)
+	{
+		*pdwSize = 0;
+		ERRMSG("failed to get filesystem info for %s", szVol);
+		return NVM_STATUS_FAILURE;
+	}
+
+	*pdwSize = fs.f_bavail * fs.f_bsize;
+	DBGMSG("GetAvailSpace returns %u\n", *pdwSize);
+
+	return NVM_STATUS_SUCCESS;
+}
+
+NVM_STATUS_T GetTotalSpace(unsigned long long* pdwSize)
+{
+	struct statfs fs;
+
+	if (statfs(rootDir, &fs) < 0)
+	{
+		*pdwSize = 0;
+		ERRMSG("failed to get filesystem info for %s", rootDir);
+		return NVM_STATUS_FAILURE;
+	}
+
+	*pdwSize = fs.f_blocks * fs.f_bsize;
+	DBGMSG("GetTotalSpace returns %llu\n", *pdwSize);
+
+	return NVM_STATUS_SUCCESS;
+}
+
+NVM_STATUS_T MkDir(const char* szDirName, mode_t dwMode)
+{
+	int ret;
+
+	ret = mkdir(szDirName, dwMode);
+	DBGMSG("mkdir(%s, %u) returns %d\n", szDirName, dwMode, ret);
+	if (ret == 0)
+		return NVM_STATUS_SUCCESS;
+	else
+		return NVM_STATUS_FAILURE;
+}
+
+NVM_STATUS_T RmDir(const char* szDirName)
+{
+	int ret;
+
+	ret = rmdir(szDirName);
+	DBGMSG("rmdir(%s) returns %d\n", szDirName, ret);
+	if (ret == 0)
+		return NVM_STATUS_SUCCESS;
+	else
+		return NVM_STATUS_FAILURE;
+}
+
+
+/* File opened for Write operation and not closed properly
+* (power reset, CP-assert) may have partial/incorrect data
+* Always write into TMP-copy and move to original only upon fclose.
+*  => so the Original could be "not updated" but always last valid!
+* Refer FileOpenSafe, FileCloseSafe, removeTmpfile and FileCloseAll
+*/
+FILE*  FileOpenSafe(const char* fName, const char* szAttrib)
+{
+	#define TMP_BUF_SIZE   1024
+	char * tmpfile_name = NULL;
+	FILE * fpTmp, *fpOrig = NULL;
+	char buf[TMP_BUF_SIZE];
+	int bRead, bWrite;
+
+	asprintf(&tmpfile_name, "%s.tmp", fName);
+	if (!tmpfile_name)
+		return NULL; //no memory error !
+
+	/* Try to copy from original to TMP */
+	fpOrig = fopen(fName, "rb");
+	if (fpOrig) {
+		fpTmp = fopen(tmpfile_name, "wb");
+		if (!fpTmp) {
+			fclose(fpOrig);
+            free(tmpfile_name);
+			return NULL; // error !
+		}
+		do {
+			bRead = fread(buf, 1, TMP_BUF_SIZE, fpOrig);
+			if (bRead == 0)
+				break;
+			bWrite = fwrite(buf, 1, bRead, fpTmp);
+			if (bWrite != bRead) {
+				fclose(fpOrig);
+				fclose(fpTmp);
+                free(tmpfile_name);
+				ERRMSG("ERR: fopen: %s \"%s\" COPY-to-TMP failed\n", fName, szAttrib);
+				return NULL;
+			}
+		} while (bRead == TMP_BUF_SIZE);
+		fclose(fpOrig);
+		fclose(fpTmp);
+	}
+
+	fpTmp = fopen(tmpfile_name, szAttrib);
+    free(tmpfile_name);
+	return fpTmp;
+}
+
+int FileCloseSafe(FileItem* item)
+{
+	int ret;
+	char * tmpfile_name = NULL;
+	FILE* fp = (FILE*)item->handle;
+
+    DBGMSG("%s: fp[0x%lx]\n", __FUNCTION__, fp);
+	/* fsync does not flushes meta-data
+	 * This is done by nvm_dir_fsync()
+	 */
+	ret = fsync(item->fd);
+	if (ret < 0)
+	{
+		ERRMSG("fsync %p failed:%s", item->handle, strerror(errno));
+	}
+	fclose(fp);
+	asprintf(&tmpfile_name, "%s.tmp", item->fName);
+	if (!tmpfile_name) {
+		ERRMSG("ERR: fclose: no memory, [%s] failed", tmpfile_name);
+		return -1;
+	}
+	if (rename(tmpfile_name, item->fName)) {
+		ERRMSG("ERR: fclose: rename %s > %s failed, errno %d, strerr %s\n", tmpfile_name, item->fName, errno, strerror(errno));
+        return -1;
+	}
+	nvm_dir_fsync(rootDir);
+    DBGMSG("%s: rootDir %s\n", __FUNCTION__, rootDir);
+    free(tmpfile_name);
+	return ret;
+}
+
+FileItem* removeItemByFd(int fd, List list)
+{
+	FileItem* Item = NULL;
+	Iterator it;
+
+	for (it = Begin(list); it != End(list); it = GetNext(it))
+	{
+		Item = it->data;
+		if (Item->fd == fd)
+		{
+			Remove(list, it);
+            DBGMSG("removeItemByFd %d\r\n",fd);
+			break;
+		}
+	}
+
+	return Item;
+}
+