blob: a12f27f363573fbffda3fc1ee2b594f703898009 [file] [log] [blame]
b.liue9582032025-04-17 19:18:16 +08001/******************************************************************************
2*(C) Copyright 2011 Marvell International Ltd.
3* All Rights Reserved
4******************************************************************************/
5#include <stdlib.h>
6#include <sys/types.h>
7#include <sys/vfs.h>
8#include <sys/stat.h>
9#include <libgen.h>
10#include <dirent.h>
11#include <fnmatch.h>
12#include <errno.h>
13#include <string.h>
14#include <stdlib.h>
15#include <fcntl.h>
16#include <sys/prctl.h>
17#include <sys/time.h>
18#include <sys/signal.h>
19#include <unistd.h>
20#include "NVMFileOps.h"
21#include "NVMServer_defs.h"
22#include "pxa_dbg.h"
23#include "nvm_shared.h"
24#include "nvm_copy.h"
25
26#define NVM_TMP_EXT_LEN 4 /*=strlen(.tmp) but in compile time*/
27
28#define NVM_FILE_NAME_MAX_LENGTH_LONG 128
29
30#define NVM_FLAG_IS_COMM 0x1
31#define NVM_FLAG_IS_DIR 0x2
32#define NVM_FLAG_IS_READONLY 0x4
33
34pthread_mutex_t lock;
35#ifdef NVMS_SYNC_THREAD_ENA
36pthread_mutex_t syncLock;
37pthread_t syncTaskRef;
38sem_t syncSem;
39#endif
40char rootDir[80];
41
42//#undef DBGMSG
43//#define DBGMSG(...) /* */
44
45#ifndef BIONIC
46/* There is well known problem with huge file operation time
47 * occurred on low-memory.
48 * The problem may occur on BIONIC also but is critical on
49 * non-BIONIC low-memory devices using JFFS file system.
50 * Hige latency could cause failure. Let's detect TimeOut and
51 * print message into kmsg-buffer; in case of N timeouts
52 * try to call panic.
53 * Mostly critical are sync/fflush commands...
54 */
55#define NVMS_GUARD_TIMER
56#endif
57
58#ifndef NVMS_GUARD_TIMER
59static void NVMS_GuardTimerInit(void) {}
60static void NVMS_GuardTimerStart(int nn) {}
61static void NVMS_GuardTimerStop(void) {}
62#else
63static int NVMS_GuardErrMsg_fd = -1;
64static int NVMS_Guard;
65static int NVMS_GuardOpTrack;
66
67#define NVMS_GUARD_SEC 4
68#define NVMS_GUARD_NUM_TO_PANIC 5 /* equal 5*4=16sec */
69/* Enable NVMS_GUARD_PANIC only if NVMS knows to stop guard-timer or
70 * abort file-operation (and stop-timer) on cp-assert
71 * CP-logging is extreemly long on JFFS, guard may be started just before
72 * cp-assert and could abort (by panic) the logging.
73*/
74//#define NVMS_GUARD_PANIC
75
76
77static void NVMS_GuardTimerStart(int nn);
78
79void lockFile(void) { pthread_mutex_lock(&lock); }
80void unlockFile(void) { pthread_mutex_unlock(&lock); }
81
82FileItem* getItemByFd(int fd, List list)
83{
84 FileItem* Item;
85 Iterator it;
86 unsigned int cnt=0;
87
88 for (it = Begin(list); it != End(list); it = GetNext(it))
89 {
90 Item = it->data;
91 cnt++;
92 if (Item->fd == fd)
93 {
94 DBGMSG("getItemByFd %d=%d\r\n",fd, cnt);
95 return Item;
96 }
97 }
98
99 return NULL;
100}
101
102static void NVMS_GuardTimerHandler(int signum, struct siginfo *pInfo, void *p)
103{
104 (void)signum; // unused
105 (void)pInfo; // unused
106 (void)p; //unused
107#ifdef NVMS_GUARD_PANIC
108 FILE *fd;
109#endif
110 if (!NVMS_Guard)
111 return; //already deleted
112 if (NVMS_GuardErrMsg_fd >= 0) {
113 char buf[80];
114 int len;
115 len = sprintf(buf, "[NVMS] file opTrackNo_%d TimeOut (%d*%dsec)\n",
116 NVMS_GuardOpTrack, NVMS_Guard, NVMS_GUARD_SEC);
117 write(NVMS_GuardErrMsg_fd, buf, len);
118 }
119 if (NVMS_Guard >= NVMS_GUARD_NUM_TO_PANIC) {
120#ifdef NVMS_GUARD_PANIC
121 pxa_dbg("NVMS_GUARD_PANIC!!!\n");
122 fd = fopen("/dev/ramdump_ctl", "w");
123 fprintf(fd, "p_NVMS_GUARD_TIMER") ;
124 fclose(fd);
125#else
126 // do not restart timer; stop printing
127 NVMS_Guard = 0;
128#endif
129 } else {
130 //Re-start the timer
131 NVMS_GuardTimerStart(NVMS_GuardOpTrack);
132 }
133}
134
135static void NVMS_GuardTimerInit(void)
136{
137 /* set up the handler */
138 struct sigaction act;
139 act.sa_sigaction = NVMS_GuardTimerHandler;
140 sigemptyset(&act.sa_mask);
141 act.sa_flags = 0;
142 NVMS_Guard = 0;
143 NVMS_GuardOpTrack = 0;
144 sigaction(SIGALRM, &act, NULL);
145 /* kmsg must be opened on init but not on failure */
146 NVMS_GuardErrMsg_fd = open("/dev/kmsg", O_WRONLY | O_SYNC);
147}
148
149static void NVMS_GuardTimerStart(int nn)
150{
151 int rc;
152 struct itimerval interval_timer;
153 interval_timer.it_interval.tv_usec = 0;
154 interval_timer.it_interval.tv_sec = 0;
155 interval_timer.it_value.tv_usec = 0;
156 interval_timer.it_value.tv_sec = NVMS_GUARD_SEC;
157 rc = setitimer(ITIMER_REAL, &interval_timer, NULL);
158 if (!rc) {
159 NVMS_Guard++;
160 NVMS_GuardOpTrack = nn;
161 }
162}
163
164static void NVMS_GuardTimerStop(void)
165{
166 struct itimerval interval_timer;
167 if (!NVMS_Guard)
168 return; //timer is not running.
169 NVMS_Guard = 0;
170 memset(&interval_timer, 0, sizeof(interval_timer));
171 setitimer(ITIMER_REAL, &interval_timer, NULL);
172}
173#endif//NVMS_GUARD_TIMER
174
175
176static int nvm_dir_fsync(const char* dirName)
177{
178 int ret = -1;
179 if (dirName != NULL)
180 {
181 int fid;
182 FILE* fd;
183 fd = fopen(dirName, "rb");
184 if (fd == NULL)
185 {
186 ERRMSG("ERR: DIR fopen(%s) fail:%s", dirName, strerror(errno));
187 }
188 else
189 {
190 fid = fileno(fd);
191 ret = fsync(fid);
192 if (ret != 0)
193 {
194 ERRMSG("ERR: DIR fsync(%d) fail:%s", fid, strerror(errno));
195 }
196 fclose(fd);
197 }
198 }
199 return(ret);
200}
201
202static int is_tmpfile_required(const char * file_name __attribute__ ((unused)), const char * szAttrib)
203{
204 return ((szAttrib[0] == 'w') || (szAttrib[0] == 'a') ||
205 (szAttrib[1] == '+') || (szAttrib[2] == '+'));
206}
207
208static void removeTmpfile(const char* dirname)
209{
210 DIR* dirstream;
211 struct dirent* ent;
212 char* pos;
213 int len, offsTMP;
214
215 DBGMSG("scan for tmp file");
216 dirstream = opendir(dirname);
217 if (dirstream == NULL)
218 {
219 ERRMSG("failed to open dir %s", dirname);
220 return;
221 }
222
223 while ((ent = readdir(dirstream)))
224 {
225 if (ent->d_type != DT_REG)
226 continue; //not a regular file
227
228 len = strlen(ent->d_name);
229 if (len <= NVM_TMP_EXT_LEN)
230 continue; //name too short to be "*.tmp"
231
232 offsTMP = len - NVM_TMP_EXT_LEN;
233 if (strcmp(ent->d_name + offsTMP, ".tmp"))
234 continue; //not "*.tmp"
235
236 unlink(ent->d_name);
237 DBGMSG("unlink tmp file [%s]", ent->d_name);
238 }
239 /* TMP remove is not critical. Do not flush/sync for it */
240 closedir(dirstream);
241}
242
243#ifdef NVMS_SYNC_THREAD_ENA
244extern "C" void* syncTask(void* param)
245{
246#error NVMS sync thread is obsolet
247 return 0;
248}
249#endif
250
251
252void NVMFileOps(const char* dir, bool async __attribute__ ((unused)))
253{
254 pthread_mutex_init(&lock, NULL);
255 rootDir[0] = '\0';
256 strncat(rootDir, dir, sizeof(rootDir) - 1);
257 chdir(rootDir);
258 removeTmpfile(rootDir);
259 NVMS_GuardTimerInit();
260}
261
262NVM_STATUS_T FileDelete(const char* szFileName)
263{
264 if (unlink(szFileName) == 0)
265 {
266 DBGMSG("unlink(%s) done\n", szFileName);
267 return NVM_STATUS_SUCCESS;
268 }
269 else
270 {
271 ERRMSG("unlink %s failed:%s\n", szFileName, strerror(errno));
272 return NVM_STATUS_FAILURE; //todo how we handle this?- just output error message
273 }
274}
275
276int FileFindEntry(DIR** dirstream, const char* szFileName,
277 NVM_FILE_INFO* pFindResults, unsigned *fStatus)
278{
279 struct dirent* ent;
280 struct stat fs;
281 int ret, file_date, file_time;
282 struct tm gm_time;
283 char *pdir, *pbase, *fileName;
284 char dir[NVM_FILE_NAME_MAX_LENGTH_LONG];
285 char base[NVM_FILE_NAME_MAX_LENGTH_LONG];
286 char path[NVM_FILE_NAME_MAX_LENGTH_LONG];
287 int nameOffs;
288
289 dir[0] = '\0';
290 strncat(dir, szFileName, NVM_FILE_NAME_MAX_LENGTH_LONG - 1);
291 base[0] = '\0';
292 strncat(base, szFileName, NVM_FILE_NAME_MAX_LENGTH_LONG - 1);
293 pdir = dirname(dir);
294 pbase = basename(base);
295
296 if (!*dirstream)
297 {
298 *dirstream = opendir(pdir);
299 if (!*dirstream)
300 {
301 DBGMSG("failed to open dir %s", pdir);
302 return -1;
303 }
304 }
305 snprintf(path, NVM_FILE_NAME_MAX_LENGTH_LONG, "%s/", pdir);
306 nameOffs = strlen(path);
307
308 while ((ent = readdir(*dirstream)))
309 {
310 if (strcmp(ent->d_name, "..") == 0 || strcmp(ent->d_name, ".") == 0)
311 continue;
312
313 //DBGMSG("%s: ent->d_name[%s], ent->d_type[%d]", __FUNCTION__, ent->d_name, ent->d_type);
314 //DBGMSG("%s: path[%s], pdir[%s], pbase[%s]", __FUNCTION__, path, pdir, pbase);
315 //DBGMSG("%s: pFindResults->plr_date[0x%lx]", __FUNCTION__, pFindResults->plr_date);
316 /* current request from cp NVM, ignore cp NVM directory file */
317 if ( (pFindResults->plr_date == 0xCFCFCFCF) && (ent->d_type == DT_DIR))
318 continue;
319
320 /* Found */
321 if (fnmatch(pbase, ent->d_name, 0) == 0)
322 {
323 strncat(path + nameOffs, ent->d_name, NVM_FILE_NAME_MAX_LENGTH_LONG - nameOffs - 1);
324 ret = stat(path, &fs);
325 if (!ret)
326 {
327 memset(pFindResults, 0, sizeof(NVM_FILE_INFO));
328 strncat(pFindResults->file_name, ent->d_name, sizeof(pFindResults->file_name) - 1);
329 break; /* OK, entry is valid; otherwise continue as non-statable entries might exist, e.g. softlink with bad target */
330 }
331 else
332 {
333 ret = errno;
334 ERRMSG("Failed to get file info[%s]: %s\n", ent->d_name, strerror(ret));
335 path[nameOffs] = '\0'; //reset file name
336 }
337 }
338 }
339
340 if (ent)
341 {
342 DBGMSG("readdir() return [%s]\n", ent->d_name);
343 gmtime_r((const time_t *)&(fs.st_mtime), &gm_time);
344 file_date = gm_time.tm_mday << (12 + 4);
345 file_date += (gm_time.tm_mon + 1) << 12;
346 file_date += gm_time.tm_year + 1900;
347 file_time = gm_time.tm_hour << (6 + 6);
348 file_time += gm_time.tm_min << 6;
349 file_time += gm_time.tm_sec;
350 pFindResults->size = fs.st_size;
351 pFindResults->date = file_date;
352 pFindResults->time = file_time;
353 *fStatus = S_ISDIR(fs.st_mode) ? NVM_DIR_MASK | NVM_FILE_MASK
354 : NVM_FILE_MASK;
355 return 0;
356 }
357 return -1;
358}
359
360NVM_STATUS_T FileFindFirst(int ClientID, const char* szFileName,
361 NVM_FILE_INFO* pFindResults, unsigned *fStatus, int *searchHandle, List list)
362{
363 DIR* dirstream = NULL;
364 int ret;
365 FileItem* item;
366
367 DBGMSG("Find First [%s]\n", szFileName);
368
369 ret = FileFindEntry(&dirstream, szFileName, pFindResults, fStatus);
370 if (!ret)
371 {
372 item = (FileItem*)malloc(sizeof(*item));
373 if (!item)
374 {
375 ERRMSG("failed to add file list for FindNext.\n");
376 closedir(dirstream);
377 *searchHandle = -1;
378 return NVM_STATUS_FAILURE;
379 }
380 strcpy(item->fName, szFileName);
381 item->fd = dirfd(dirstream);
382 item->handle = dirstream;
383 item->flag = NVM_FLAG_IS_DIR;
384 if (ClientID == NVM_CLIENT_COMM)
385 item->flag |= NVM_FLAG_IS_COMM;
386
387 lockFile();
388 Append(list, item);
389 unlockFile();
390
391 *searchHandle = item->fd;
392 if (ClientID == NVM_CLIENT_ACAT)
393 usleep(10000); //Prevents CPU starvation caused by ACAT APPS
394 free(item);
395 return NVM_STATUS_SUCCESS;
396 }
397 else
398 {
399 DBGMSG("failed to FileFindFirst a matching file");
400 if (dirstream)
401 closedir(dirstream);
402 *searchHandle = -1;
403 return NVM_STATUS_FAILURE;
404 }
405}
406
407NVM_STATUS_T FileFindNext(NVM_FILE_INFO* pFindResults,
408 unsigned* fStatus, int searchHandle, List list)
409{
410 DIR* dirstream;
411 int ret;
412 char path[NVM_FILE_NAME_MAX_LENGTH_LONG];
413 FileItem* item;
414
415 lockFile();
416 item = getItemByFd(searchHandle, list);
417 if (item == NULL || !item->fName[0] || item->handle == NULL)
418 {
419 unlockFile();
420 ERRMSG("Invalid dir item\n");
421 return NVM_STATUS_FAILURE;
422 }
423 dirstream = (DIR*)item->handle;
424 path[0] = '\0';
425 strncat(path, item->fName, NVM_FILE_NAME_MAX_LENGTH_LONG - 1);
426 DBGMSG("Find Next [%s]\n", item->fName);
427 unlockFile();
428
429 ret = FileFindEntry(&dirstream, path, pFindResults, fStatus);
430 if (!ret)
431 return NVM_STATUS_SUCCESS;
432 else
433 {
434 DBGMSG("failed to FileFindNext a matching file"); //=OK, end of dir-list
435 return NVM_STATUS_FAILURE;
436 }
437}
438
439
440NVM_STATUS_T FileFindClose(int searchHandle, List list)
441{
442 NVM_STATUS_T rc = NVM_STATUS_SUCCESS;
443 FileItem* item;
444 DIR* dirstream;
445
446 lockFile();
447// item = removeItemByFd(searchHandle, list);
448 item = getItemByFd(searchHandle, list);
449// unlockFile();
450 if (item != NULL)
451 {
452 DBGMSG("FileFindClose [%s]", item->fName);
453 dirstream = (DIR*)item->handle;
454 if (dirstream == NULL)
455 rc = NVM_STATUS_FAILURE;
456 else
457 closedir(dirstream);
458 if (!item->fName[0])
459 rc = NVM_STATUS_FAILURE;
460 item = removeItemByFd(searchHandle, list);
461 }
462 else
463 {
464 ERRMSG("cannot find dir handle %d", searchHandle);
465 }
466 unlockFile();
467 return rc;
468}
469
470NVM_STATUS_T GetFileSize(const char* szFileName,
471 unsigned* pdwSizeHigh, unsigned* pdwSizeLow)
472{
473 struct stat fs;
474 int ret;
475
476 ret = stat(szFileName, &fs);
477 if (ret != 0)
478 {
479 DBGMSG("failed to open the file [%s].\n", szFileName);
480 return NVM_STATUS_FAILURE;
481 }
482 *pdwSizeLow = fs.st_size;
483 *pdwSizeHigh = 0;
484 DBGMSG("%s size = %lld\n", szFileName, fs.st_size);
485 return NVM_STATUS_SUCCESS;
486}
487
488
489NVM_STATUS_T FileOpen(int ClientID, const char* szFileName,
490 const char* szAttrib, unsigned* hFile, List list)
491{
492 FILE* fp;
493 int ret;
494 FileItem* item;
495
496 item = (FileItem *)malloc(sizeof(*item));
497 if (!item)
498 {
499 ERRMSG("Cannot add file [%s] with attrib [%s] to file list\n", szFileName, szAttrib);
500 return NVM_STATUS_FAILURE;
501 }
502 item->fName[0] = 0; // no one cares about file name in read only mode
503 item->flag = 0;
504 if (is_tmpfile_required(szFileName, szAttrib))
505 { //open for Write
506 if (strlen(szFileName) > NAME_MAX)
507 {
508 ERRMSG("file [%s] length too long!!!\n", szFileName);
509 return NVM_STATUS_FAILURE;
510 }
511 strcpy(item->fName, szFileName);
512 NVMS_GuardTimerStart(1);
513 fp = FileOpenSafe(szFileName, szAttrib);
514 NVMS_GuardTimerStop();//1
515 }
516 else
517 { //open for Read
518 item->flag |= NVM_FLAG_IS_READONLY;
519 NVMS_GuardTimerStart(2);
520 fp = fopen(szFileName, szAttrib);
521 NVMS_GuardTimerStop();//2
522 }
523
524 if (!fp)
525 {
526 DBGMSG("Cannot open file [%s] with attrib [%s]\n", szFileName, szAttrib);
527 free(item);
528 return NVM_STATUS_FAILURE;
529 }
530
531 DBGMSG("FileOpen(%s with %s) returns %p\n", szFileName, szAttrib, fp);
532 item->fd = fileno(fp);
533 item->handle = fp;
534 if (ClientID == NVM_CLIENT_COMM)
535 item->flag |= NVM_FLAG_IS_COMM;
536 lockFile();
537 Append(list, item);
538 unlockFile();
539 *hFile = item->fd;
540 free(item);
541 return NVM_STATUS_SUCCESS;
542}
543
544NVM_STATUS_T FileWrite(unsigned hFile, const void* pBuffer, unsigned dwBufferLen,
545 short wItemSize, unsigned dwCount, unsigned* pdwActual, List list)
546{
547 UNUSEDPARAM(dwBufferLen)
548 FILE *fp;
549 NVM_STATUS_T ret = NVM_STATUS_SUCCESS;
550 FileItem* item;
551
552 *pdwActual = 0;
553 if ((wItemSize == 0) || (dwCount == 0))
554 return NVM_STATUS_FAILURE;
555
556 lockFile();
557 item = getItemByFd(hFile, list);
558 if (!item)
559 {
560 unlockFile();
561 return NVM_STATUS_FAILURE;
562 }
563 fp = (FILE*)item->handle;
564 unlockFile();
565 DBGMSG("FileWrite(%p, %d * %d)\n", fp, wItemSize, dwCount);
566 NVMS_GuardTimerStart(3);
567 *pdwActual = fwrite(pBuffer, wItemSize, dwCount, fp);
568 NVMS_GuardTimerStop();//3
569 if (ferror(fp))
570 {
571 ERRMSG("FileWrite: File write error %d, %s.[%p].\n", *pdwActual, strerror(errno), fp);
572 clearerr(fp);
573 ret = NVM_STATUS_FAILURE;
574 }
575 DBGMSG("FileWrite(%p, %d * %d) returns %d\n", fp, wItemSize, dwCount, *pdwActual);
576 return ret;
577}
578
579NVM_STATUS_T FileFlush(unsigned hFile, List list)
580{
581 FILE *fp;
582 FileItem* item;
583
584 lockFile();
585 item = getItemByFd(hFile, list);
586 if (!item)
587 {
588 unlockFile();
589 return NVM_STATUS_FAILURE;
590 }
591 fp = (FILE*)item->handle;
592 unlockFile();
593 int ret = fflush(fp);
594 DBGMSG("FileFlush(%p) returns %d\n", fp, ret);
595 return NVM_STATUS_SUCCESS;
596}
597
598NVM_STATUS_T FileSeek(unsigned hFile, long dwOffset, int dwOrigin, List list)
599{
600 FILE *fp;
601 FileItem* item;
602
603 lockFile();
604 item = getItemByFd(hFile, list);
605 if (!item)
606 {
607 unlockFile();
608 return NVM_STATUS_FAILURE;
609 }
610 fp = (FILE*)item->handle;
611 unlockFile();
612 int ret = fseek(fp, dwOffset, dwOrigin);
613 DBGMSG("FileSeek(%p at %d from %d) returns %d\n", fp, dwOffset, dwOrigin, ret);
614 if (ret == 0)
615 return NVM_STATUS_SUCCESS;
616 else
617 return NVM_STATUS_FAILURE;
618}
619
620NVM_STATUS_T FileRead(unsigned hFile, short wItemSize, unsigned dwCount,
621 unsigned* pdwActual, void* pBuffer, List list)
622{
623 FILE *fp;
624 FileItem* item;
625
626 lockFile();
627 item = getItemByFd(hFile, list);
628 if (!item)
629 {
630 unlockFile();
631 return NVM_STATUS_FAILURE;
632 }
633 fp = (FILE*)item->handle;
634 unlockFile();
635 if (feof(fp))
636 {
637 ERRMSG("FileRead: File pointer is at the end of the file!.[%p].\n", fp);
638 }
639 DBGMSG("FileRead(%p)\n", fp);
640
641 NVMS_GuardTimerStart(4);
642 *pdwActual = fread(pBuffer, wItemSize, dwCount, fp);
643 NVMS_GuardTimerStop();//4
644 if (ferror(fp))
645 {
646 ERRMSG("FileRead(%s) error", item->fName);
647 clearerr(fp);
648 }
649
650 DBGMSG("FileRead(%p %d * %d) returns %d\n", fp, wItemSize, dwCount, *pdwActual);
651
652 return NVM_STATUS_SUCCESS;
653}
654
655NVM_STATUS_T FileClose(int ClientID, unsigned hFile, List list)
656{
657 int ret = 0;
658 FileItem* item;
659 FILE* fp;
660
661 lockFile();
662 item = getItemByFd(hFile, list);
663// item = removeItemByFd(hFile, list);
664 if (!item)
665 {
666 unlockFile();
667 ERRMSG("FileClose: cannot find file handler [%u].\n", hFile);
668 return NVM_STATUS_FAILURE;
669 }
670 fp = (FILE*)item->handle;
671 if (item->flag & NVM_FLAG_IS_READONLY)
672 {
673 //No NVMS_GuardTimerStart() required since no problems seen
674 ret = fclose(fp);
675 }
676 else
677 {
678 NVMS_GuardTimerStart(6);
679 ERRMSG("FileClose: fflush fp[0x%lx].\n", fp);
680 fflush(fp);
681 ret = FileCloseSafe(item); //close with rename
682 NVMS_GuardTimerStop();//6
683 }
684 item = removeItemByFd(hFile, list);
685
686 unlockFile();
687 if (ClientID != NVM_CLIENT_ACAT)
688 {
689 if (ret == 0)
690 ret = NVM_STATUS_SUCCESS;
691 else
692 ret = NVM_STATUS_FS_ERROR;
693 }
694 else
695 {
696 usleep(10000); //Prevents CPU starvation caused by ACAT APPS
697 ret = NVM_STATUS_SUCCESS;
698 }
699 DBGMSG("FileClose returns %d\n", ret);
700 return (NVM_STATUS_T)ret;
701}
702
703NVM_STATUS_T FileCloseAll(int ClientID, List list)
704{
705 bool type, client = (NVM_CLIENT_COMM == ClientID);
706 size_t file_num = 0, dir_num = 0, remain;
707 int wr_cnt = 0;
708
709 FileItem* Item = NULL;
710 Iterator it;
711 Iterator it2;
712
713 lockFile();
714
715 for (it = Begin(list); it != End(list);)
716 {
717 Item = it->data;
718 if (Item->flag & NVM_FLAG_IS_DIR)
719 {
720 DBGMSG("close dir %s", Item->fName);
721 closedir((DIR*)Item->handle);
722 ++dir_num;
723 }
724 else
725 {
726 // close but not safe. TMP removed at the end
727 if (!(Item->flag & NVM_FLAG_IS_READONLY))
728 wr_cnt++;
729 fclose((FILE*)Item->handle);
730 ++file_num;
731 }
732 /* get next item before free current item */
733 it2 = GetNext(it);
734 Remove(list, it);
735 it = it2;
736 }
737 remain = GetLength(list);
738
739 unlockFile();
740 DBGMSG("FileCloseAll: closed %zu dirs, %zu files, %zu left", dir_num, file_num, remain);
741 if (wr_cnt)
742 removeTmpfile(rootDir);
743 return NVM_STATUS_SUCCESS;
744}
745
746NVM_STATUS_T FileRename(const char* szOldFileName, const char* szNewFileName)
747{
748 int ret;
749
750 ret = rename(szOldFileName, szNewFileName);
751 DBGMSG("rename(%s --> %s) returns %d\n", szOldFileName, szNewFileName, ret);
752 if (ret == 0)
753 return NVM_STATUS_SUCCESS;
754 else
755 {
756 ERRMSG("rename %s to %s failed\n", szOldFileName, szNewFileName);
757 return NVM_STATUS_FAILURE; //todo how we handle this??- just output error messages now.
758 }
759}
760
761NVM_STATUS_T GetFileStat(const char* szFileName, int* dwStat)
762{
763 struct stat fs;
764 int ret;
765
766 ret = stat(szFileName, &fs);
767 DBGMSG("stat(%s) returns %d\n", szFileName, ret);
768 if (ret == 0)
769 {
770 *dwStat = S_ISDIR(fs.st_mode)
771 ? NVM_DIR_MASK | NVM_FILE_MASK
772 : NVM_FILE_MASK;
773 return NVM_STATUS_SUCCESS;
774 }
775 else
776 return NVM_STATUS_FAILURE;
777}
778
779NVM_STATUS_T FileChangeMode(const char* szFileName, mode_t dwNewMode)
780{
781 int ret;
782
783 ret = chmod(szFileName, dwNewMode);
784 DBGMSG("%chmod(%s, %u) returns %d\n", szFileName, dwNewMode, ret);
785 if (ret == 0)
786 return NVM_STATUS_SUCCESS;
787 else
788 return NVM_STATUS_FAILURE;
789}
790
791NVM_STATUS_T GetAvailSpace(const char* szVol, unsigned* pdwSize)
792{
793 struct statfs fs;
794
795 /*FIXME PC tool sends Unicode16 for szVol, disable szVol param for now */
796 szVol = "/NVM/";
797 if (statfs(szVol, &fs) < 0)
798 {
799 *pdwSize = 0;
800 ERRMSG("failed to get filesystem info for %s", szVol);
801 return NVM_STATUS_FAILURE;
802 }
803
804 *pdwSize = fs.f_bavail * fs.f_bsize;
805 DBGMSG("GetAvailSpace returns %u\n", *pdwSize);
806
807 return NVM_STATUS_SUCCESS;
808}
809
810NVM_STATUS_T GetTotalSpace(unsigned long long* pdwSize)
811{
812 struct statfs fs;
813
814 if (statfs(rootDir, &fs) < 0)
815 {
816 *pdwSize = 0;
817 ERRMSG("failed to get filesystem info for %s", rootDir);
818 return NVM_STATUS_FAILURE;
819 }
820
821 *pdwSize = fs.f_blocks * fs.f_bsize;
822 DBGMSG("GetTotalSpace returns %llu\n", *pdwSize);
823
824 return NVM_STATUS_SUCCESS;
825}
826
827NVM_STATUS_T MkDir(const char* szDirName, mode_t dwMode)
828{
829 int ret;
830
831 ret = mkdir(szDirName, dwMode);
832 DBGMSG("mkdir(%s, %u) returns %d\n", szDirName, dwMode, ret);
833 if (ret == 0)
834 return NVM_STATUS_SUCCESS;
835 else
836 return NVM_STATUS_FAILURE;
837}
838
839NVM_STATUS_T RmDir(const char* szDirName)
840{
841 int ret;
842
843 ret = rmdir(szDirName);
844 DBGMSG("rmdir(%s) returns %d\n", szDirName, ret);
845 if (ret == 0)
846 return NVM_STATUS_SUCCESS;
847 else
848 return NVM_STATUS_FAILURE;
849}
850
851
852/* File opened for Write operation and not closed properly
853* (power reset, CP-assert) may have partial/incorrect data
854* Always write into TMP-copy and move to original only upon fclose.
855* => so the Original could be "not updated" but always last valid!
856* Refer FileOpenSafe, FileCloseSafe, removeTmpfile and FileCloseAll
857*/
858FILE* FileOpenSafe(const char* fName, const char* szAttrib)
859{
860 #define TMP_BUF_SIZE 1024
861 char * tmpfile_name = NULL;
862 FILE * fpTmp, *fpOrig = NULL;
863 char buf[TMP_BUF_SIZE];
864 int bRead, bWrite;
865
866 asprintf(&tmpfile_name, "%s.tmp", fName);
867 if (!tmpfile_name)
868 return NULL; //no memory error !
869
870 /* Try to copy from original to TMP */
871 fpOrig = fopen(fName, "rb");
872 if (fpOrig) {
873 fpTmp = fopen(tmpfile_name, "wb");
874 if (!fpTmp) {
875 fclose(fpOrig);
876 free(tmpfile_name);
877 return NULL; // error !
878 }
879 do {
880 bRead = fread(buf, 1, TMP_BUF_SIZE, fpOrig);
881 if (bRead == 0)
882 break;
883 bWrite = fwrite(buf, 1, bRead, fpTmp);
884 if (bWrite != bRead) {
885 fclose(fpOrig);
886 fclose(fpTmp);
887 free(tmpfile_name);
888 ERRMSG("ERR: fopen: %s \"%s\" COPY-to-TMP failed\n", fName, szAttrib);
889 return NULL;
890 }
891 } while (bRead == TMP_BUF_SIZE);
892 fclose(fpOrig);
893 fclose(fpTmp);
894 }
895
896 fpTmp = fopen(tmpfile_name, szAttrib);
897 free(tmpfile_name);
898 return fpTmp;
899}
900
901int FileCloseSafe(FileItem* item)
902{
903 int ret;
904 char * tmpfile_name = NULL;
905 FILE* fp = (FILE*)item->handle;
906
907 DBGMSG("%s: fp[0x%lx]\n", __FUNCTION__, fp);
908 /* fsync does not flushes meta-data
909 * This is done by nvm_dir_fsync()
910 */
911 ret = fsync(item->fd);
912 if (ret < 0)
913 {
914 ERRMSG("fsync %p failed:%s", item->handle, strerror(errno));
915 }
916 fclose(fp);
917 asprintf(&tmpfile_name, "%s.tmp", item->fName);
918 if (!tmpfile_name) {
919 ERRMSG("ERR: fclose: no memory, [%s] failed", tmpfile_name);
920 return -1;
921 }
922 if (rename(tmpfile_name, item->fName)) {
923 ERRMSG("ERR: fclose: rename %s > %s failed, errno %d, strerr %s\n", tmpfile_name, item->fName, errno, strerror(errno));
924 return -1;
925 }
926 nvm_dir_fsync(rootDir);
927 DBGMSG("%s: rootDir %s\n", __FUNCTION__, rootDir);
928 free(tmpfile_name);
929 return ret;
930}
931
932FileItem* removeItemByFd(int fd, List list)
933{
934 FileItem* Item = NULL;
935 Iterator it;
936
937 for (it = Begin(list); it != End(list); it = GetNext(it))
938 {
939 Item = it->data;
940 if (Item->fd == fd)
941 {
942 Remove(list, it);
943 DBGMSG("removeItemByFd %d\r\n",fd);
944 break;
945 }
946 }
947
948 return Item;
949}
950