1 /******************************************************************************
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 ******************************************************************************/
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
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"
30 #include <persistence_file_cache.h>
33 #include <fcntl.h> // for open flags
38 #include <sys/types.h>
44 // local function prototype
45 int pclFileGetDefaultData(int handle, const char* resource_id, int policy);
49 int pclFileClose(int fd)
51 int rval = EPERS_NOT_INITIALIZED;
53 //DLT_LOG(gDLTContext, DLT_LOG_INFO, DLT_STRING("pclFileClose fd: "), DLT_INT(fd));
55 if(gPclInitialized >= PCLinitialized)
57 if(fd < MaxPersHandle)
59 // check if a backup and checksum file needs to bel deleted
60 if( gFileHandleArray[fd].permission != PersistencePermission_ReadOnly)
63 remove(gFileHandleArray[fd].backupPath); // we don't care about return value
65 // remove checksum file
66 remove(gFileHandleArray[fd].csumPath); // we don't care about return value
69 __sync_fetch_and_sub(&gOpenFdArray[fd], FileClosed); // set closed flag
71 rval = pfcCloseFile(fd);
79 rval = EPERS_MAXHANDLE;
88 int pclFileGetSize(int fd)
90 int size = EPERS_NOT_INITIALIZED;
92 if(gPclInitialized >= PCLinitialized)
96 size = pfcFileGetSize(fd);
99 size = fstat(fd, &buf);
101 //DLT_LOG(gDLTContext, DLT_LOG_INFO, DLT_STRING("pclFileGetSize fd: "), DLT_INT(fd));
114 void* pclFileMapData(void* addr, long size, long offset, int fd)
119 DLT_LOG(gPclDLTContext, DLT_LOG_WARN, DLT_STRING("pclFileMapData not supported when using file cache"));
121 //DLT_LOG(gDLTContext, DLT_LOG_INFO, DLT_STRING("pclFileMapData fd: "), DLT_INT(fd));
123 if(gPclInitialized >= PCLinitialized)
125 if(AccessNoLock != isAccessLocked() ) // check if access to persistent data is locked
127 int mapFlag = PROT_WRITE | PROT_READ;
128 ptr = mmap(addr,size, mapFlag, MAP_SHARED, fd, offset);
132 ptr = EPERS_MAP_LOCKFS;
142 int pclFileOpen(unsigned int ldbid, const char* resource_id, unsigned int user_no, unsigned int seat_no)
144 int handle = EPERS_NOT_INITIALIZED;
146 if(gPclInitialized >= PCLinitialized)
149 PersistenceInfo_s dbContext;
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
156 //DLT_LOG(gDLTContext, DLT_LOG_INFO, DLT_STRING("pclFileOpen: "), DLT_INT(ldbid), DLT_STRING(resource_id) );
158 dbContext.context.ldbid = ldbid;
159 dbContext.context.seat_no = seat_no;
160 dbContext.context.user_no = user_no;
162 // get database context: database path and database key
163 shared_DB = get_db_context(&dbContext, resource_id, ResIsFile, dbKey, dbPath);
165 if(dbContext.configKey.type == PersistenceResourceType_file) // check if the resource is really a file
167 // create backup path
169 char fileSubPath[DbPathMaxLen] = {0};
171 if(dbContext.configKey.policy == PersistencePolicy_wc)
173 length = gCPathPrefixSize;
177 length = gWTPathPrefixSize;
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);
184 if(shared_DB >= 0) // check valid database context
186 int flags = pclGetPosixPermission(dbContext.configKey.permission);
188 // file will be opened writable, so check about data consistency
189 if( (dbContext.configKey.permission != PersistencePermission_ReadOnly)
190 && pclBackupNeeded(dbPath) )
192 if((handle = pclVerifyConsistency(dbPath, backupPath, csumPath, flags)) == -1)
194 DLT_LOG(gPclDLTContext, DLT_LOG_ERROR, DLT_STRING("pclFileOpen: error => file inconsistent, recovery N O T possible!"));
200 DLT_LOG(gPclDLTContext, DLT_LOG_INFO, DLT_STRING("pclFileOpen: No Backup => file is read only OR is in blacklist!"));
205 if(handle > 0) // when the file is open, close it and do a new open unde PFC control
210 handle = pfcOpenFile(dbPath, DontCreateFile);
212 if(handle <= 0) // check if open is needed or already done in verifyConsistency
214 handle = open(dbPath, flags);
218 if(handle == -1 && errno == ENOENT) // file does not exist, create file and folder
221 if((handle = pclCreateFile(dbPath)) == -1)
223 DLT_LOG(gPclDLTContext, DLT_LOG_ERROR, DLT_STRING("pclFileOpen: error => failed to create file: "), DLT_STRING(dbPath));
227 if(pclFileGetDefaultData(handle, resource_id, dbContext.configKey.policy) == -1) // try to get default data
229 DLT_LOG(gPclDLTContext, DLT_LOG_WARN, DLT_STRING("pclFileOpen: no default data available: "), DLT_STRING(resource_id));
234 if(handle < MaxPersHandle && handle > 0 )
236 __sync_fetch_and_add(&gOpenFdArray[handle], FileOpen); // set open flag
238 if(dbContext.configKey.permission != PersistencePermission_ReadOnly)
240 strcpy(gFileHandleArray[handle].backupPath, backupPath);
241 strcpy(gFileHandleArray[handle].csumPath, csumPath);
242 gFileHandleArray[handle].backupCreated = 0;
243 gFileHandleArray[handle].permission = dbContext.configKey.permission;
249 handle = EPERS_MAXHANDLE;
252 else // requested resource is not in the RCT, so create resource as local/cached.
254 // assemble file string for local cached location
255 snprintf(dbPath, DbPathMaxLen, gLocalCacheFilePath, gAppId, user_no, seat_no, resource_id);
256 handle = pclCreateFile(dbPath);
260 if(handle < MaxPersHandle)
262 __sync_fetch_and_add(&gOpenFdArray[handle], FileOpen); // set open flag
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
272 handle = EPERS_MAXHANDLE;
279 handle = EPERS_RESOURCE_NO_FILE;
288 int pclFileReadData(int fd, void * buffer, int buffer_size)
290 int readSize = EPERS_NOT_INITIALIZED;
292 //DLT_LOG(gDLTContext, DLT_LOG_INFO, DLT_STRING("pclFileReadData fd: "), DLT_INT(fd));
293 if(gPclInitialized >= PCLinitialized)
296 readSize = pfcReadFile(fd, buffer, buffer_size);
298 readSize = read(fd, buffer, buffer_size);
306 int pclFileRemove(unsigned int ldbid, const char* resource_id, unsigned int user_no, unsigned int seat_no)
308 int rval = EPERS_NOT_INITIALIZED;
310 //DLT_LOG(gDLTContext, DLT_LOG_INFO, DLT_STRING("pclFileReadData "), DLT_INT(ldbid), DLT_STRING(resource_id));
312 if(gPclInitialized >= PCLinitialized)
314 if(AccessNoLock != isAccessLocked() ) // check if access to persistent data is locked
317 PersistenceInfo_s dbContext;
319 char dbKey[DbKeyMaxLen] = {0}; // database key
320 char dbPath[DbPathMaxLen] = {0}; // database location
322 dbContext.context.ldbid = ldbid;
323 dbContext.context.seat_no = seat_no;
324 dbContext.context.user_no = user_no;
326 // get database context: database path and database key
327 shared_DB = get_db_context(&dbContext, resource_id, ResIsFile, dbKey, dbPath);
329 if( (shared_DB >= 0) // check valid database context
330 && (dbContext.configKey.type == PersistenceResourceType_file) ) // check if type matches
332 rval = remove(dbPath);
335 DLT_LOG(gPclDLTContext, DLT_LOG_ERROR, DLT_STRING("pclFileRemove => remove ERROR"), DLT_STRING(strerror(errno)) );
341 DLT_LOG(gPclDLTContext, DLT_LOG_ERROR, DLT_STRING("pclFileRemove ==> no valid database context or resource not a file"));
355 int pclFileSeek(int fd, long int offset, int whence)
357 int rval = EPERS_NOT_INITIALIZED;
359 //DLT_LOG(gDLTContext, DLT_LOG_INFO, DLT_STRING("pclFileSeek fd:"), DLT_INT(fd));
361 if(gPclInitialized >= PCLinitialized)
363 if(AccessNoLock != isAccessLocked() ) // check if access to persistent data is locked
366 rval = pfcFileSeek(fd, offset, whence);
368 rval = lseek(fd, offset, whence);
382 int pclFileUnmapData(void* address, long size)
384 int rval = EPERS_NOT_INITIALIZED;
386 //DLT_LOG(gDLTContext, DLT_LOG_INFO, DLT_STRING("pclFileUnmapData"));
388 if(gPclInitialized >= PCLinitialized)
390 if(AccessNoLock != isAccessLocked() ) // check if access to persistent data is locked
392 rval = munmap(address, size);
405 int pclFileWriteData(int fd, const void * buffer, int buffer_size)
407 int size = EPERS_NOT_INITIALIZED;
409 //DLT_LOG(gDLTContext, DLT_LOG_INFO, DLT_STRING("pclFileWriteData fd:"), DLT_INT(fd));
411 if(gPclInitialized >= PCLinitialized)
413 if(AccessNoLock != isAccessLocked() ) // check if access to persistent data is locked
415 if(fd < MaxPersHandle)
417 if(gFileHandleArray[fd].permission != PersistencePermission_ReadOnly)
419 // check if a backup file has to be created
420 if(gFileHandleArray[fd].backupCreated == 0)
422 char csumBuf[ChecksumBufSize] = {0};
424 // calculate checksum
425 pclCalcCrc32Csum(fd, csumBuf);
426 // create checksum and backup file
427 pclCreateBackup(gFileHandleArray[fd].backupPath, fd, gFileHandleArray[fd].csumPath, csumBuf);
429 gFileHandleArray[fd].backupCreated = 1;
433 size = pfcWriteFile(fd, buffer, buffer_size);
435 size = write(fd, buffer, buffer_size);
440 size = EPERS_RESOURCE_READ_ONLY;
454 int pclFileCreatePath(unsigned int ldbid, const char* resource_id, unsigned int user_no, unsigned int seat_no, char** path, unsigned int* size)
456 int handle = EPERS_NOT_INITIALIZED;
458 if(gPclInitialized >= PCLinitialized)
461 PersistenceInfo_s dbContext;
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
468 //DLT_LOG(gDLTContext, DLT_LOG_INFO, DLT_STRING("pclFileOpen: "), DLT_INT(ldbid), DLT_STRING(resource_id) );
470 dbContext.context.ldbid = ldbid;
471 dbContext.context.seat_no = seat_no;
472 dbContext.context.user_no = user_no;
474 // get database context: database path and database key
475 shared_DB = get_db_context(&dbContext, resource_id, ResIsFile, dbKey, dbPath);
477 if( dbContext.configKey.type == PersistenceResourceType_file) // check if type matches
479 if(shared_DB >= 0) // check valid database context
481 int flags = pclGetPosixPermission(dbContext.configKey.permission);
483 // file will be opened writable, so check about data consistency
484 if( dbContext.configKey.permission != PersistencePermission_ReadOnly
485 && pclBackupNeeded(dbPath) )
487 snprintf(backupPath, DbPathMaxLen-1, "%s%s", dbPath, gBackupPostfix);
488 snprintf(csumPath, DbPathMaxLen-1, "%s%s", dbPath, gBackupCsPostfix);
490 if((handle = pclVerifyConsistency(dbPath, backupPath, csumPath, flags)) == -1)
492 DLT_LOG(gPclDLTContext, DLT_LOG_ERROR, DLT_STRING("pclFileOpen: error => file inconsistent, recovery N O T possible!"));
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
500 handle = get_persistence_handle_idx();
504 if(handle < MaxPersHandle)
506 __sync_fetch_and_add(&gOpenHandleArray[handle], FileOpen); // set open flag
508 if(dbContext.configKey.permission != PersistencePermission_ReadOnly)
510 strncpy(gOssHandleArray[handle].backupPath, backupPath, DbPathMaxLen);
511 strncpy(gOssHandleArray[handle].csumPath, csumPath, DbPathMaxLen);
513 gOssHandleArray[handle].backupCreated = 0;
514 gOssHandleArray[handle].permission = dbContext.configKey.permission;
517 *size = strlen(dbPath);
518 *path = malloc((*size)+1); // allocate 1 byte for the string termination
520 /* Check if malloc was successful */
523 memcpy(*path, dbPath, (*size));
524 (*path)[(*size)] = '\0'; // terminate string
525 gOssHandleArray[handle].filePath = *path;
527 if(access(*path, F_OK) == -1)
529 // file does not exist, create it.
531 if((handle = pclCreateFile(*path)) == -1)
533 DLT_LOG(gPclDLTContext, DLT_LOG_ERROR, DLT_STRING("pclFileCreatePath: error => failed to create file: "), DLT_STRING(*path));
537 if(pclFileGetDefaultData(handle, resource_id, dbContext.configKey.policy) == -1) // try to get default data
539 DLT_LOG(gPclDLTContext, DLT_LOG_WARN, DLT_STRING("pclFileCreatePath => no default data available: "), DLT_STRING(resource_id));
541 close(handle); // don't need the open file
547 handle = EPERS_DESER_ALLOCMEM;
548 DLT_LOG(gPclDLTContext, DLT_LOG_ERROR,
549 DLT_STRING("pclFileCreatePath: malloc() failed for path:"),
551 DLT_STRING("With the size:"),
557 set_persistence_handle_close_idx(handle);
558 handle = EPERS_MAXHANDLE;
562 else // requested resource is not in the RCT, so create resource as local/cached.
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();
570 if(handle < MaxPersHandle)
572 snprintf(backupPath, DbPathMaxLen, "%s%s", dbPath, gBackupPostfix);
573 snprintf(csumPath, DbPathMaxLen, "%s%s", dbPath, gBackupCsPostfix);
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
583 set_persistence_handle_close_idx(handle);
584 handle = EPERS_MAXHANDLE;
591 handle = EPERS_RESOURCE_NO_FILE;
600 int pclFileReleasePath(int pathHandle)
602 int rval = EPERS_NOT_INITIALIZED;
604 //DLT_LOG(gDLTContext, DLT_LOG_INFO, DLT_STRING("pclFileClose fd: "), DLT_INT(fd));
606 if(gPclInitialized >= PCLinitialized)
608 if(pathHandle < MaxPersHandle)
610 // check if a backup and checksum file needs to bel deleted
611 if( gFileHandleArray[pathHandle].permission != PersistencePermission_ReadOnly)
613 // remove backup file
614 remove(gOssHandleArray[pathHandle].backupPath); // we don't care about return value
616 // remove checksum file
617 remove(gOssHandleArray[pathHandle].csumPath); // we don't care about return value
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;
628 rval = EPERS_MAXHANDLE;
638 int pclFileGetDefaultData(int handle, const char* resource_id, int policy)
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;
646 // create path to default data
647 if(policy == PersistencePolicy_wc)
649 snprintf(pathPrefix, DbPathMaxLen, gLocalCachePath, gAppId);
651 else if(policy == PersistencePolicy_wt)
653 snprintf(pathPrefix, DbPathMaxLen, gLocalWtPath, gAppId);
656 snprintf(defaultPath, DbPathMaxLen, "%s%s/%s", pathPrefix, PERS_ORG_DEFAULT_DATA_FOLDER_NAME_, resource_id);
658 defaultHandle = open(defaultPath, O_RDONLY);
659 if(defaultHandle != -1) // check if default data is available
663 memset(&buf, 0, sizeof(buf));
665 fstat(defaultHandle, &buf);
666 rval = sendfile(handle, defaultHandle, 0, buf.st_size);
669 rval = lseek(handle, 0, SEEK_SET); // set fd back to beginning of the file
673 DLT_LOG(gPclDLTContext, DLT_LOG_WARN, DLT_STRING("pclFileGetDefaultData => failed to copy file "), DLT_STRING(strerror(errno)));
676 close(defaultHandle);
680 rval = -1; // no default data available