| /****************************************************************************** |
| *(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; |
| } |
| |