d24257f5f01d7e0ae5cc767b84e994327d7b42a2
[profile/ivi/persistence-client-library.git] / src / persistence_client_library_file.c
1 /******************************************************************************
2  * Project         Persistency
3  * (c) copyright   2012
4  * Company         XS Embedded GmbH
5  *****************************************************************************/
6 /******************************************************************************
7  * This Source Code Form is subject to the terms of the
8  * Mozilla Public License, v. 2.0. If a  copy of the MPL was not distributed
9  * with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
10 ******************************************************************************/
11  /**
12  * @file           persistence_client_library_file.c
13  * @ingroup        Persistence client library
14  * @author         Ingo Huerner
15  * @brief          Implementation of the persistence client library.
16  *                 Library provides an API to access persistent data
17  * @see            
18  */
19
20 #include "persistence_client_library_file.h"
21 #include "persistence_client_library_backup_filelist.h"
22 #include "persistence_client_library_pas_interface.h"
23 #include "persistence_client_library_handle.h"
24 #include "persistence_client_library_prct_access.h"
25 #include "persistence_client_library_data_organization.h"
26 #include "persistence_client_library_db_access.h"
27 #include "crc32.h"
28
29 #if USE_FILECACHE
30    #include <persistence_file_cache.h>
31 #endif
32
33 #include <fcntl.h>   // for open flags
34 #include <errno.h>
35 #include <string.h>
36 #include <sys/mman.h>
37 #include <sys/stat.h>
38 #include <sys/types.h>
39 #include <unistd.h>
40 #include <stdlib.h>
41
42
43
44 // local function prototype
45 int pclFileGetDefaultData(int handle, const char* resource_id, int policy);
46
47
48
49 int pclFileClose(int fd)
50 {
51    int rval = EPERS_NOT_INITIALIZED;
52
53    //DLT_LOG(gDLTContext, DLT_LOG_INFO, DLT_STRING("pclFileClose fd: "), DLT_INT(fd));
54
55    if(gPclInitialized >= PCLinitialized)
56    {
57       if(fd < MaxPersHandle)
58       {
59          // check if a backup and checksum file needs to bel deleted
60          if( gFileHandleArray[fd].permission != PersistencePermission_ReadOnly)
61          {
62             // remove backup file
63             remove(gFileHandleArray[fd].backupPath);  // we don't care about return value
64
65             // remove checksum file
66             remove(gFileHandleArray[fd].csumPath);    // we don't care about return value
67
68          }
69          __sync_fetch_and_sub(&gOpenFdArray[fd], FileClosed);   // set closed flag
70 #if USE_FILECACHE
71          rval = pfcCloseFile(fd);
72 #else
73          rval = close(fd);
74 #endif
75
76       }
77       else
78       {
79           rval = EPERS_MAXHANDLE;
80       }
81    }
82
83    return rval;
84 }
85
86
87
88 int pclFileGetSize(int fd)
89 {
90    int size = EPERS_NOT_INITIALIZED;
91
92    if(gPclInitialized >= PCLinitialized)
93    {
94
95 #if USE_FILECACHE
96       size = pfcFileGetSize(fd);
97 #else
98       struct stat buf;
99       size = fstat(fd, &buf);
100
101       //DLT_LOG(gDLTContext, DLT_LOG_INFO, DLT_STRING("pclFileGetSize fd: "), DLT_INT(fd));
102
103       if(size != -1)
104       {
105          size = buf.st_size;
106       }
107 #endif
108    }
109    return size;
110 }
111
112
113
114 void* pclFileMapData(void* addr, long size, long offset, int fd)
115 {
116    void* ptr = 0;
117
118 #if USE_FILECACHE
119    DLT_LOG(gPclDLTContext, DLT_LOG_WARN, DLT_STRING("pclFileMapData not supported when using file cache"));
120 #else
121    //DLT_LOG(gDLTContext, DLT_LOG_INFO, DLT_STRING("pclFileMapData fd: "), DLT_INT(fd));
122
123    if(gPclInitialized >= PCLinitialized)
124    {
125       if(AccessNoLock != isAccessLocked() ) // check if access to persistent data is locked
126       {
127          int mapFlag = PROT_WRITE | PROT_READ;
128          ptr = mmap(addr,size, mapFlag, MAP_SHARED, fd, offset);
129       }
130       else
131       {
132          ptr = EPERS_MAP_LOCKFS;
133       }
134    }
135 #endif
136
137    return ptr;
138 }
139
140
141
142 int pclFileOpen(unsigned int ldbid, const char* resource_id, unsigned int user_no, unsigned int seat_no)
143 {
144    int handle = EPERS_NOT_INITIALIZED;
145
146    if(gPclInitialized >= PCLinitialized)
147    {
148       int shared_DB = 0;
149       PersistenceInfo_s dbContext;
150
151       char dbKey[DbKeyMaxLen]      = {0};    // database key
152       char dbPath[DbPathMaxLen]    = {0};    // database location
153       char backupPath[DbPathMaxLen] = {0};    // backup file
154       char csumPath[DbPathMaxLen]  = {0};    // checksum file
155
156       //DLT_LOG(gDLTContext, DLT_LOG_INFO, DLT_STRING("pclFileOpen: "), DLT_INT(ldbid), DLT_STRING(resource_id) );
157
158       dbContext.context.ldbid   = ldbid;
159       dbContext.context.seat_no = seat_no;
160       dbContext.context.user_no = user_no;
161
162       // get database context: database path and database key
163       shared_DB = get_db_context(&dbContext, resource_id, ResIsFile, dbKey, dbPath);
164
165       if(dbContext.configKey.type == PersistenceResourceType_file)   // check if the resource is really a file
166       {
167          // create backup path
168          int length = 0;
169          char fileSubPath[DbPathMaxLen] = {0};
170
171          if(dbContext.configKey.policy ==  PersistencePolicy_wc)
172          {
173             length = gCPathPrefixSize;
174          }
175          else
176          {
177             length = gWTPathPrefixSize;
178          }
179
180          strncpy(fileSubPath, dbPath+length, DbPathMaxLen);
181          snprintf(backupPath, DbPathMaxLen-1, "%s%s%s", gBackupPrefix, fileSubPath, gBackupPostfix);
182          snprintf(csumPath,   DbPathMaxLen-1, "%s%s%s", gBackupPrefix, fileSubPath, gBackupCsPostfix);
183
184          if(shared_DB >= 0)                                          // check valid database context
185          {
186             int flags = pclGetPosixPermission(dbContext.configKey.permission);
187
188             // file will be opened writable, so check about data consistency
189             if( (dbContext.configKey.permission != PersistencePermission_ReadOnly)
190                && pclBackupNeeded(dbPath) )
191             {
192                if((handle = pclVerifyConsistency(dbPath, backupPath, csumPath, flags)) == -1)
193                {
194                   DLT_LOG(gPclDLTContext, DLT_LOG_ERROR, DLT_STRING("pclFileOpen: error => file inconsistent, recovery  N O T  possible!"));
195                   return -1;
196                }
197             }
198             else
199             {
200                DLT_LOG(gPclDLTContext, DLT_LOG_INFO, DLT_STRING("pclFileOpen: No Backup => file is read only OR is in blacklist!"));
201             }
202
203 #if USE_FILECACHE
204
205             if(handle > 0)   // when the file is open, close it and do a new open unde PFC control
206             {
207                close(handle);
208             }
209
210             handle = pfcOpenFile(dbPath, DontCreateFile);
211 #else
212             if(handle <= 0)   // check if open is needed or already done in verifyConsistency
213             {
214                handle = open(dbPath, flags);
215             }
216 #endif
217
218             if(handle == -1 && errno == ENOENT) // file does not exist, create file and folder
219             {
220
221                if((handle = pclCreateFile(dbPath)) == -1)
222                {
223                   DLT_LOG(gPclDLTContext, DLT_LOG_ERROR, DLT_STRING("pclFileOpen: error => failed to create file: "), DLT_STRING(dbPath));
224                }
225                else
226                {
227                 if(pclFileGetDefaultData(handle, resource_id, dbContext.configKey.policy) == -1)        // try to get default data
228                 {
229                         DLT_LOG(gPclDLTContext, DLT_LOG_WARN, DLT_STRING("pclFileOpen: no default data available: "), DLT_STRING(resource_id));
230                 }
231                }
232             }
233
234             if(handle < MaxPersHandle && handle > 0 )
235             {
236                __sync_fetch_and_add(&gOpenFdArray[handle], FileOpen); // set open flag
237
238                if(dbContext.configKey.permission != PersistencePermission_ReadOnly)
239                {
240                   strcpy(gFileHandleArray[handle].backupPath, backupPath);
241                   strcpy(gFileHandleArray[handle].csumPath,   csumPath);
242                   gFileHandleArray[handle].backupCreated = 0;
243                   gFileHandleArray[handle].permission = dbContext.configKey.permission;
244                }
245             }
246             else
247             {
248                close(handle);
249                handle = EPERS_MAXHANDLE;
250             }
251          }
252          else  // requested resource is not in the RCT, so create resource as local/cached.
253          {
254             // assemble file string for local cached location
255             snprintf(dbPath, DbPathMaxLen, gLocalCacheFilePath, gAppId, user_no, seat_no, resource_id);
256             handle = pclCreateFile(dbPath);
257
258             if(handle != -1)
259             {
260                         if(handle < MaxPersHandle)
261                         {
262                            __sync_fetch_and_add(&gOpenFdArray[handle], FileOpen); // set open flag
263
264                            strcpy(gFileHandleArray[handle].backupPath, backupPath);
265                            strcpy(gFileHandleArray[handle].csumPath,   csumPath);
266                            gFileHandleArray[handle].backupCreated = 0;
267                            gFileHandleArray[handle].permission = PersistencePermission_ReadWrite;  // make it writable
268                         }
269                         else
270                         {
271                            close(handle);
272                            handle = EPERS_MAXHANDLE;
273                         }
274             }
275          }
276       }
277       else
278       {
279          handle = EPERS_RESOURCE_NO_FILE;
280       }
281    }
282
283    return handle;
284 }
285
286
287
288 int pclFileReadData(int fd, void * buffer, int buffer_size)
289 {
290    int readSize = EPERS_NOT_INITIALIZED;
291
292    //DLT_LOG(gDLTContext, DLT_LOG_INFO, DLT_STRING("pclFileReadData fd: "), DLT_INT(fd));
293    if(gPclInitialized >= PCLinitialized)
294    {
295 #if USE_FILECACHE
296       readSize = pfcReadFile(fd, buffer, buffer_size);
297 #else
298       readSize = read(fd, buffer, buffer_size);
299 #endif
300    }
301    return readSize;
302 }
303
304
305
306 int pclFileRemove(unsigned int ldbid, const char* resource_id, unsigned int user_no, unsigned int seat_no)
307 {
308    int rval = EPERS_NOT_INITIALIZED;
309
310    //DLT_LOG(gDLTContext, DLT_LOG_INFO, DLT_STRING("pclFileReadData "), DLT_INT(ldbid), DLT_STRING(resource_id));
311
312    if(gPclInitialized >= PCLinitialized)
313    {
314       if(AccessNoLock != isAccessLocked() ) // check if access to persistent data is locked
315       {
316          int shared_DB = 0;
317          PersistenceInfo_s dbContext;
318
319          char dbKey[DbKeyMaxLen]   = {0};      // database key
320          char dbPath[DbPathMaxLen] = {0};    // database location
321
322          dbContext.context.ldbid   = ldbid;
323          dbContext.context.seat_no = seat_no;
324          dbContext.context.user_no = user_no;
325
326          // get database context: database path and database key
327          shared_DB = get_db_context(&dbContext, resource_id, ResIsFile, dbKey, dbPath);
328
329          if(   (shared_DB >= 0)                                               // check valid database context
330             && (dbContext.configKey.type == PersistenceResourceType_file) )   // check if type matches
331          {
332             rval = remove(dbPath);
333             if(rval == -1)
334             {
335                DLT_LOG(gPclDLTContext, DLT_LOG_ERROR, DLT_STRING("pclFileRemove => remove ERROR"), DLT_STRING(strerror(errno)) );
336             }
337          }
338          else
339          {
340             rval = shared_DB;
341             DLT_LOG(gPclDLTContext, DLT_LOG_ERROR, DLT_STRING("pclFileRemove ==> no valid database context or resource not a file"));
342          }
343       }
344       else
345       {
346          rval = EPERS_LOCKFS;
347       }
348    }
349
350    return rval;
351 }
352
353
354
355 int pclFileSeek(int fd, long int offset, int whence)
356 {
357    int rval = EPERS_NOT_INITIALIZED;
358
359    //DLT_LOG(gDLTContext, DLT_LOG_INFO, DLT_STRING("pclFileSeek fd:"), DLT_INT(fd));
360
361    if(gPclInitialized >= PCLinitialized)
362    {
363       if(AccessNoLock != isAccessLocked() ) // check if access to persistent data is locked
364       {
365 #if USE_FILECACHE
366          rval = pfcFileSeek(fd, offset, whence);
367 #else
368          rval = lseek(fd, offset, whence);
369 #endif
370       }
371       else
372       {
373          rval = EPERS_LOCKFS;
374       }
375    }
376
377    return rval;
378 }
379
380
381
382 int pclFileUnmapData(void* address, long size)
383 {
384    int rval = EPERS_NOT_INITIALIZED;
385
386    //DLT_LOG(gDLTContext, DLT_LOG_INFO, DLT_STRING("pclFileUnmapData"));
387
388    if(gPclInitialized >= PCLinitialized)
389    {
390       if(AccessNoLock != isAccessLocked() ) // check if access to persistent data is locked
391       {
392          rval =  munmap(address, size);
393       }
394       else
395       {
396          rval = EPERS_LOCKFS;
397       }
398    }
399
400    return rval;
401 }
402
403
404
405 int pclFileWriteData(int fd, const void * buffer, int buffer_size)
406 {
407    int size = EPERS_NOT_INITIALIZED;
408
409    //DLT_LOG(gDLTContext, DLT_LOG_INFO, DLT_STRING("pclFileWriteData fd:"), DLT_INT(fd));
410
411    if(gPclInitialized >= PCLinitialized)
412    {
413       if(AccessNoLock != isAccessLocked() ) // check if access to persistent data is locked
414       {
415          if(fd < MaxPersHandle)
416          {
417             if(gFileHandleArray[fd].permission != PersistencePermission_ReadOnly)
418             {
419                // check if a backup file has to be created
420                if(gFileHandleArray[fd].backupCreated == 0)
421                {
422                   char csumBuf[ChecksumBufSize] = {0};
423
424                   // calculate checksum
425                   pclCalcCrc32Csum(fd, csumBuf);
426                   // create checksum and backup file
427                   pclCreateBackup(gFileHandleArray[fd].backupPath, fd, gFileHandleArray[fd].csumPath, csumBuf);
428
429                   gFileHandleArray[fd].backupCreated = 1;
430                }
431
432 #if USE_FILECACHE
433                size = pfcWriteFile(fd, buffer, buffer_size);
434 #else
435                size = write(fd, buffer, buffer_size);
436 #endif
437             }
438             else
439             {
440                size = EPERS_RESOURCE_READ_ONLY;
441             }
442          }
443       }
444       else
445       {
446          size = EPERS_LOCKFS;
447       }
448    }
449
450    return size;
451 }
452
453
454 int pclFileCreatePath(unsigned int ldbid, const char* resource_id, unsigned int user_no, unsigned int seat_no, char** path, unsigned int* size)
455 {
456    int handle = EPERS_NOT_INITIALIZED;
457
458    if(gPclInitialized >= PCLinitialized)
459    {
460       int shared_DB = 0;
461       PersistenceInfo_s dbContext;
462
463       char dbKey[DbKeyMaxLen]      = {0};    // database key
464       char dbPath[DbPathMaxLen]    = {0};    // database location
465       char backupPath[DbPathMaxLen] = {0};    // backup file
466       char csumPath[DbPathMaxLen]  = {0};    // checksum file
467
468       //DLT_LOG(gDLTContext, DLT_LOG_INFO, DLT_STRING("pclFileOpen: "), DLT_INT(ldbid), DLT_STRING(resource_id) );
469
470       dbContext.context.ldbid   = ldbid;
471       dbContext.context.seat_no = seat_no;
472       dbContext.context.user_no = user_no;
473
474       // get database context: database path and database key
475       shared_DB = get_db_context(&dbContext, resource_id, ResIsFile, dbKey, dbPath);
476
477       if( dbContext.configKey.type == PersistenceResourceType_file)     // check if type matches
478       {
479          if(shared_DB >= 0)                                             // check valid database context
480          {
481             int flags = pclGetPosixPermission(dbContext.configKey.permission);
482
483             // file will be opened writable, so check about data consistency
484             if(   dbContext.configKey.permission != PersistencePermission_ReadOnly
485                && pclBackupNeeded(dbPath) )
486             {
487                snprintf(backupPath, DbPathMaxLen-1, "%s%s", dbPath, gBackupPostfix);
488                snprintf(csumPath,   DbPathMaxLen-1, "%s%s", dbPath, gBackupCsPostfix);
489
490                if((handle = pclVerifyConsistency(dbPath, backupPath, csumPath, flags)) == -1)
491                {
492                   DLT_LOG(gPclDLTContext, DLT_LOG_ERROR, DLT_STRING("pclFileOpen: error => file inconsistent, recovery  N O T  possible!"));
493                   return -1;
494                }
495                // we don't need the file handle here
496                // the application calling this function must use the POSIX open() function to get an file descriptor
497                close(handle);
498             }
499
500             handle = get_persistence_handle_idx();
501
502             if(handle != -1)
503             {
504                if(handle < MaxPersHandle)
505                {
506                   __sync_fetch_and_add(&gOpenHandleArray[handle], FileOpen); // set open flag
507
508                   if(dbContext.configKey.permission != PersistencePermission_ReadOnly)
509                   {
510                      strncpy(gOssHandleArray[handle].backupPath, backupPath, DbPathMaxLen);
511                      strncpy(gOssHandleArray[handle].csumPath,   csumPath, DbPathMaxLen);
512
513                      gOssHandleArray[handle].backupCreated = 0;
514                      gOssHandleArray[handle].permission = dbContext.configKey.permission;
515                   }
516
517                   *size = strlen(dbPath);
518                   *path = malloc((*size)+1);       // allocate 1 byte for the string termination
519
520                   /* Check if malloc was successful */
521                   if(NULL != (*path))
522                   {
523                                                         memcpy(*path, dbPath, (*size));
524                                                         (*path)[(*size)] = '\0';         // terminate string
525                                                         gOssHandleArray[handle].filePath = *path;
526
527                      if(access(*path, F_OK) == -1)
528                      {
529                                                                 // file does not exist, create it.
530                                                                 int handle = 0;
531                                                                 if((handle = pclCreateFile(*path)) == -1)
532                                                                 {
533                                                                         DLT_LOG(gPclDLTContext, DLT_LOG_ERROR, DLT_STRING("pclFileCreatePath: error => failed to create file: "), DLT_STRING(*path));
534                                                                 }
535                                                                 else
536                                                                 {
537                                                                         if(pclFileGetDefaultData(handle, resource_id, dbContext.configKey.policy) == -1)        // try to get default data
538                                                                         {
539                                                                                 DLT_LOG(gPclDLTContext, DLT_LOG_WARN, DLT_STRING("pclFileCreatePath => no default data available: "), DLT_STRING(resource_id));
540                                                                         }
541                                                                         close(handle);    // don't need the open file
542                                                                 }
543                      }
544                   }
545                                                 else
546                   {
547                      handle = EPERS_DESER_ALLOCMEM;
548                      DLT_LOG(gPclDLTContext, DLT_LOG_ERROR,
549                                           DLT_STRING("pclFileCreatePath: malloc() failed for path:"),
550                                           DLT_STRING(dbPath),
551                                           DLT_STRING("With the size:"),
552                                               DLT_UINT(*size));
553                   }
554                }
555                else
556                                         {
557                                                 set_persistence_handle_close_idx(handle);
558                                                 handle = EPERS_MAXHANDLE;
559                                         }
560             }
561          }
562          else  // requested resource is not in the RCT, so create resource as local/cached.
563          {
564             // assemble file string for local cached location
565             snprintf(dbPath, DbPathMaxLen, gLocalCacheFilePath, gAppId, user_no, seat_no, resource_id);
566             handle = get_persistence_handle_idx();
567
568             if(handle != -1)
569             {
570                if(handle < MaxPersHandle)
571                {
572                   snprintf(backupPath, DbPathMaxLen, "%s%s", dbPath, gBackupPostfix);
573                   snprintf(csumPath,   DbPathMaxLen, "%s%s", dbPath, gBackupCsPostfix);
574
575                   __sync_fetch_and_add(&gOpenHandleArray[handle], FileOpen); // set open flag
576                   strncpy(gOssHandleArray[handle].backupPath, backupPath, DbPathMaxLen);
577                   strncpy(gOssHandleArray[handle].csumPath,   csumPath, DbPathMaxLen);
578                   gOssHandleArray[handle].backupCreated = 0;
579                   gOssHandleArray[handle].permission = PersistencePermission_ReadWrite;  // make it writable
580                }
581                else
582                {
583                   set_persistence_handle_close_idx(handle);
584                   handle = EPERS_MAXHANDLE;
585                }
586             }
587          }
588       }
589       else
590       {
591          handle = EPERS_RESOURCE_NO_FILE;
592       }
593    }
594
595    return handle;
596 }
597
598
599
600 int pclFileReleasePath(int pathHandle)
601 {
602    int rval = EPERS_NOT_INITIALIZED;
603
604    //DLT_LOG(gDLTContext, DLT_LOG_INFO, DLT_STRING("pclFileClose fd: "), DLT_INT(fd));
605
606    if(gPclInitialized >= PCLinitialized)
607    {
608       if(pathHandle < MaxPersHandle)
609       {
610          // check if a backup and checksum file needs to bel deleted
611          if( gFileHandleArray[pathHandle].permission != PersistencePermission_ReadOnly)
612          {
613             // remove backup file
614             remove(gOssHandleArray[pathHandle].backupPath);  // we don't care about return value
615
616             // remove checksum file
617             remove(gOssHandleArray[pathHandle].csumPath);    // we don't care about return value
618
619          }
620          free(gOssHandleArray[pathHandle].filePath);
621          __sync_fetch_and_sub(&gOpenHandleArray[pathHandle], FileClosed);   // set closed flag
622          set_persistence_handle_close_idx(pathHandle);
623          gOssHandleArray[pathHandle].filePath = NULL;
624          rval = 1;
625       }
626       else
627       {
628         rval = EPERS_MAXHANDLE;
629       }
630    }
631
632    return rval;
633 }
634
635
636
637
638 int pclFileGetDefaultData(int handle, const char* resource_id, int policy)
639 {
640         // check if there is default data available
641         char pathPrefix[DbPathMaxLen]  = { [0 ... DbPathMaxLen-1] = 0};
642         char defaultPath[DbPathMaxLen] = { [0 ... DbPathMaxLen-1] = 0};
643         int defaultHandle = -1;
644         int rval = 0;
645
646         // create path to default data
647         if(policy == PersistencePolicy_wc)
648         {
649                 snprintf(pathPrefix, DbPathMaxLen, gLocalCachePath, gAppId);
650         }
651         else if(policy == PersistencePolicy_wt)
652         {
653                 snprintf(pathPrefix, DbPathMaxLen, gLocalWtPath, gAppId);
654         }
655
656         snprintf(defaultPath, DbPathMaxLen, "%s%s/%s", pathPrefix, PERS_ORG_DEFAULT_DATA_FOLDER_NAME_, resource_id);
657
658         defaultHandle = open(defaultPath, O_RDONLY);
659         if(defaultHandle != -1) // check if default data is available
660         {
661                 // copy default data
662                 struct stat buf;
663                 memset(&buf, 0, sizeof(buf));
664
665                 fstat(defaultHandle, &buf);
666                 rval = sendfile(handle, defaultHandle, 0, buf.st_size);
667                 if(rval != -1)
668                 {
669                         rval = lseek(handle, 0, SEEK_SET); // set fd back to beginning of the file
670                 }
671                 else
672                 {
673                         DLT_LOG(gPclDLTContext, DLT_LOG_WARN, DLT_STRING("pclFileGetDefaultData => failed to copy file "), DLT_STRING(strerror(errno)));
674                 }
675
676                 close(defaultHandle);
677         }
678         else
679         {
680                 rval = -1; // no default data available
681         }
682 }// getDefault
683
684