blob: a12f27f363573fbffda3fc1ee2b594f703898009 [file] [log] [blame]
/******************************************************************************
*(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;
}