File backups on seperate partition; Fixed bug 146; created performance benchmark...
[profile/ivi/persistence-client-library.git] / src / persistence_client_library_backup_filelist.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_backup_filelist.c
13  * @ingroup        Persistence client library
14  * @author         Ingo Huerner
15  * @brief          Implementation of persistence client library backup filelist
16  * @see
17  */
18
19 #include "persistence_client_library_backup_filelist.h"
20 #include "persistence_client_library_handle.h"
21 #include "rbtree.h"
22 #include "../include_protected/crc32.h"
23 #include "../include_protected/persistence_client_library_data_organization.h"
24
25
26 #include <fcntl.h>
27 #include <string.h>
28 #include <sys/mman.h>
29 #include <sys/stat.h>
30 #include <errno.h>
31 #include <unistd.h>
32 #include <stdlib.h>
33 #include <stdio.h>
34
35
36 /// structure definition for a key value item
37 typedef struct _key_value_s
38 {
39    unsigned int key;
40    char*        value;
41 }key_value_s;
42
43
44 void  key_val_rel(void *p);
45
46 void* key_val_dup(void *p);
47
48 int key_val_cmp(const void *p1, const void *p2 );
49
50
51
52 /// the size of the token array
53 enum configConstants
54 {
55    TOKENARRAYSIZE = 255
56 };
57
58
59 const char gCharLookup[] =
60 {
61    0,0,0,0,0,0,0,0,  0,0,0,0,0,0,0,0,  0,0,0,0,0,0,0,0,  0,0,0,0,0,0,0,0,  // from 0x0 (NULL)  to 0x1F (unit seperator)
62    0,1,1,1,1,1,1,1,  1,1,1,1,1,1,1,1,  1,1,1,1,1,1,1,1,  1,1,1,1,1,1,1,1,  // from 020 (space) to 0x2F (?)
63    1,1,1,1,1,1,1,1,  1,1,1,1,1,1,1,1,  1,1,1,1,1,1,1,1,  1,1,1,1,1,1,1,1,  // from 040 (@)     to 0x5F (_)
64    1,1,1,1,1,1,1,1,  1,1,1,1,1,1,1,1,  1,1,1,1,1,1,1,1,  1,1,1,1,1,1,1     // from 060 (')     to 0x7E (~)
65 };
66
67
68 char* gpConfigFileMap = 0;
69 char* gpTokenArray[TOKENARRAYSIZE] = {0};
70 int gTokenCounter = 0;
71 unsigned int gConfigFileSize = 0;
72
73
74 /// the rb tree
75 static jsw_rbtree_t *gRb_tree_bl = NULL;
76
77 void fillCharTokenArray()
78 {
79    unsigned int i=0;
80    int blankCount=0;
81    char* tmpPointer = gpConfigFileMap;
82
83    // set the first pointer to the start of the file
84    gpTokenArray[blankCount] = tmpPointer;
85    blankCount++;
86
87    while(i < gConfigFileSize)
88    {
89       if(   ((unsigned int)*tmpPointer < 127)
90          && ((unsigned int)*tmpPointer >= 0))
91            {
92                    if(1 != gCharLookup[(unsigned int)*tmpPointer])
93                    {
94                            *tmpPointer = 0;
95
96                            // check if we are at the end of the token array
97                            if(blankCount >= TOKENARRAYSIZE)
98                            {
99                                    break;
100                            }
101                            gpTokenArray[blankCount] = tmpPointer+1;
102                            blankCount++;
103                            gTokenCounter++;
104                    }
105            }
106       tmpPointer++;
107            i++;
108    }
109 }
110
111
112 void createAndStoreFileNames()
113 {
114    int i= 0, j =0;
115    char path[128];
116    const char* gFilePostFix                = ".pers";
117    const char* gKeyPathFormat              = "/%s/%s/%s/%s/%s%s";
118    key_value_s* item;
119
120    // creat new tree
121    gRb_tree_bl = jsw_rbnew(key_val_cmp, key_val_dup, key_val_rel);
122
123    if(gRb_tree_bl != NULL)
124    {
125
126       for(i=0; i<128; i++)
127       {
128          // assemble path
129          snprintf(path, 128, gKeyPathFormat, gpTokenArray[j+2],      // storage type
130                                              gpTokenArray[j+3],      // policy id
131                                              gpTokenArray[j+4],      // profileID
132                                              gpTokenArray[j],        // application id
133                                              gpTokenArray[j+1],      // filename
134                                              gFilePostFix);          // file postfix
135
136          // asign key and value to the rbtree item
137          item = malloc(sizeof(key_value_s));
138          if(item != NULL)
139          {
140             item->key = pclCrc32(0, (unsigned char*)path, strlen(path));
141             // we don't need the path name here, we just need to know that this key is available in the tree
142             item->value = "";
143             jsw_rbinsert(gRb_tree_bl, item);
144             free(item);
145          }
146          j+=5;
147          if(gpTokenArray[j] == NULL)
148          {
149             break;
150          }
151       }
152    }
153
154 }
155
156
157 int readBlacklistConfigFile(const char* filename)
158 {
159    int fd = 0,
160        status = 0,
161        rval = 0;
162
163    struct stat buffer;
164
165    if(filename != NULL)
166    {
167
168            memset(&buffer, 0, sizeof(buffer));
169            status = stat(filename, &buffer);
170            if(status != -1)
171            {
172                   gConfigFileSize = buffer.st_size;
173            }
174
175            fd = open(filename, O_RDONLY);
176            if (fd == -1)
177            {
178                   DLT_LOG(gDLTContext, DLT_LOG_WARN, DLT_STRING("configReader::readConfigFile ==> Error file open"), DLT_STRING(filename), DLT_STRING(strerror(errno)) );
179                   return -1;
180            }
181
182            // check for empty file
183            if(gConfigFileSize == 0)
184            {
185                   DLT_LOG(gDLTContext, DLT_LOG_WARN, DLT_STRING("configReader::readConfigFile ==> Error file size is 0:"), DLT_STRING(filename));
186                   close(fd);
187                   return -1;
188            }
189
190            // map the config file into memory
191            gpConfigFileMap = (char*)mmap(0, gConfigFileSize, PROT_WRITE, MAP_PRIVATE, fd, 0);
192
193            if (gpConfigFileMap == MAP_FAILED)
194            {
195                   gpConfigFileMap = 0;
196                   close(fd);
197                   DLT_LOG(gDLTContext, DLT_LOG_ERROR, DLT_STRING("configReader::readConfigFile ==> Error mapping the file:"), DLT_STRING(filename), DLT_STRING(strerror(errno)) );
198
199                   return -1;
200            }
201
202            // reset the token counter
203            gTokenCounter = 0;
204
205            fillCharTokenArray();
206
207            // create filenames and store them in the tree
208            createAndStoreFileNames();
209
210            munmap(gpConfigFileMap, gConfigFileSize);
211
212            close(fd);
213    }
214    else
215    {
216            rval = -1;
217    }
218
219    return rval;
220 }
221
222
223
224 int need_backup_key(unsigned int key)
225 {
226    int rval = 1;
227    key_value_s* item = NULL;
228    key_value_s* foundItem = NULL;
229
230    item = malloc(sizeof(key_value_s));
231    if(item != NULL && gRb_tree_bl != NULL)
232    {
233       item->key = key;
234       foundItem = (key_value_s*)jsw_rbfind(gRb_tree_bl, item);
235       if(foundItem != NULL)
236       {
237          rval = 0;
238       }
239       free(item);
240    }
241    else
242    {
243       if(item!=NULL)
244              free(item);
245
246       rval = -1;
247       DLT_LOG(gDLTContext, DLT_LOG_ERROR, DLT_STRING("need_backup_key ==> item or gRb_tree_bl is NULL"));
248    }
249
250    return rval;
251 }
252
253
254 /// compare function for tree key_value_s item
255 int key_val_cmp(const void *p1, const void *p2 )
256 {
257    int rval = -1;
258    key_value_s* first;
259    key_value_s* second;
260
261    first  = (key_value_s*)p1;
262    second = (key_value_s*)p2;
263
264    if(second->key == first->key)
265    {
266       rval = 0;
267    }
268    else if(second->key < first->key)
269    {
270       rval = -1;
271    }
272    else
273    {
274       rval = 1;
275    }
276
277    return rval;
278  }
279
280 /// duplicate function for key_value_s item
281 void* key_val_dup(void *p)
282 {
283    int value_size = 0;
284    key_value_s* src = NULL;
285    key_value_s* dst = NULL;
286
287    src = (key_value_s*)p;
288    value_size = strlen(src->value)+1;
289
290    // allocate memory for node
291    dst = malloc(sizeof(key_value_s));
292    if(dst != NULL)
293    {
294       // duplicate hash key
295      dst->key = src->key;
296
297      // duplicate value
298      dst->value = malloc(value_size);
299      if(dst->value != NULL)
300         strncpy(dst->value, src->value, value_size);
301    }
302
303
304    return dst;
305 }
306
307 /// release function for key_value_s item
308 void  key_val_rel(void *p )
309 {
310    key_value_s* rel = NULL;
311    rel = (key_value_s*)p;
312
313    if(rel->value != NULL)
314       free(rel->value);
315
316    if(rel != NULL)
317       free(rel);
318 }
319
320
321 int pclCreateFile(const char* path)
322 {
323    const char* delimiters = "/\n";   // search for blank and end of line
324    char* tokenArray[24];
325    char* thePath = (char*)path;
326    int numTokens = 0, i = 0, validPath = 1;
327    int handle = 0;
328
329    tokenArray[numTokens++] = strtok(thePath, delimiters);
330    while(tokenArray[numTokens-1] != NULL )
331    {
332      tokenArray[numTokens] = strtok(NULL, delimiters);
333      if(tokenArray[numTokens] != NULL)
334      {
335         numTokens++;
336         if(numTokens >= 24)
337         {
338            validPath = 0;
339            break;
340         }
341      }
342      else
343      {
344         break;
345      }
346    }
347
348    if(validPath == 1)
349    {
350       char createPath[DbPathMaxLen] = {0};
351       snprintf(createPath, DbPathMaxLen, "/%s",tokenArray[0] );
352       for(i=1; i<numTokens-1; i++)
353       {
354          // create folders
355          strncat(createPath, "/", DbPathMaxLen-1);
356          strncat(createPath, tokenArray[i], DbPathMaxLen-1);
357          mkdir(createPath, 0744);
358       }
359       // finally create the file
360       strncat(createPath, "/", DbPathMaxLen-1);
361       strncat(createPath, tokenArray[i], DbPathMaxLen-1);
362       handle = open(createPath, O_CREAT|O_RDWR |O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH);
363       if(handle != -1)
364       {
365          if(handle < MaxPersHandle)
366          {
367             __sync_fetch_and_add(&gOpenFdArray[handle], FileOpen); // set open flag
368          }
369          else
370          {
371             close(handle);
372             handle = EPERS_MAXHANDLE;
373          }
374       }
375    }
376    else
377    {
378       DLT_LOG(gDLTContext, DLT_LOG_ERROR, DLT_STRING("pclCreateFile ==> no valid path to create: "), DLT_STRING(path) );
379    }
380
381    return handle;
382 }
383
384
385 int pclVerifyConsistency(const char* origPath, const char* backupPath, const char* csumPath, int openFlags)
386 {
387    int handle = 0, readSize = 0;
388    int backupAvail = 0, csumAvail = 0;
389    int fdCsum = 0, fdBackup = 0;
390
391    char origCsumBuf[ChecksumBufSize] = {0};
392    char backCsumBuf[ChecksumBufSize] = {0};
393    char csumBuf[ChecksumBufSize]     = {0};
394
395    // check if we have a backup and checksum file
396    backupAvail = access(backupPath, F_OK);
397    csumAvail   = access(csumPath, F_OK);
398
399    // *************************************************
400    // there is a backup file and a checksum
401    // *************************************************
402    if( (backupAvail == 0) && (csumAvail == 0) )
403    {
404       DLT_LOG(gDLTContext, DLT_LOG_INFO, DLT_STRING("pclVerifyConsistency => there is a backup file AND a checksum"));
405       // calculate checksum form backup file
406       fdBackup = open(backupPath,  O_RDONLY);
407       if(fdBackup != -1)
408       {
409          pclCalcCrc32Csum(fdBackup, backCsumBuf);
410
411          fdCsum = open(csumPath,  O_RDONLY);
412          if(fdCsum != -1)
413          {
414             readSize = read(fdCsum, csumBuf, ChecksumBufSize);
415             if(readSize > 0)
416             {
417                if(strcmp(csumBuf, backCsumBuf)  == 0)
418                {
419                   // checksum matches ==> replace with original file
420                   handle = pclRecoverFromBackup(fdBackup, origPath);
421                }
422                else
423                {
424                   // checksum does not match, check checksum with original file
425                   handle = open(origPath, openFlags);
426                   if(handle != -1)
427                   {
428                      pclCalcCrc32Csum(handle, origCsumBuf);
429                      if(strcmp(csumBuf, origCsumBuf)  != 0)
430                      {
431                         close(handle);
432                         handle = -1;  // error: file corrupt
433                      }
434                      // else case: checksum matches ==> keep original file ==> nothing to do
435
436                   }
437                   else
438                   {
439                      close(handle);
440                      handle = -1;     // error: file corrupt
441                   }
442                }
443             }
444             close(fdCsum);
445          }
446          else
447          {
448             close(fdCsum);
449             handle = -1;     // error: file corrupt
450          }
451       }
452       else
453       {
454          handle = -1;
455       }
456       close(fdBackup);
457    }
458    // *************************************************
459    // there is ONLY a checksum file
460    // *************************************************
461    else if(csumAvail == 0)
462    {
463       DLT_LOG(gDLTContext, DLT_LOG_INFO, DLT_STRING("pclVerifyConsistency => there is ONLY a checksum file"));
464
465       fdCsum = open(csumPath,  O_RDONLY);
466       if(fdCsum != -1)
467       {
468          readSize = read(fdCsum, csumBuf, ChecksumBufSize);
469          if(readSize <= 0)
470          {
471             DLT_LOG(gDLTContext, DLT_LOG_ERROR, DLT_STRING("pclVerifyConsistency => read checksum: invalid readSize"));
472          }
473          close(fdCsum);
474
475          // calculate the checksum form the original file to see if it matches
476          handle = open(origPath, openFlags);
477          if(handle != -1)
478          {
479             pclCalcCrc32Csum(handle, origCsumBuf);
480
481             if(strcmp(csumBuf, origCsumBuf)  != 0)
482             {
483                 close(handle);
484                 handle = -1;  // checksum does NOT match ==> error: file corrupt
485             }
486             // else case: checksum matches ==> keep original file ==> nothing to do
487          }
488          else
489          {
490             close(handle);
491             handle = -1;      // error: file corrupt
492          }
493       }
494       else
495       {
496          close(fdCsum);
497          handle = -1;         // error: file corrupt
498       }
499    }
500    // *************************************************
501    // there is ONLY a backup file
502    // *************************************************
503    else if(backupAvail == 0)
504    {
505       DLT_LOG(gDLTContext, DLT_LOG_INFO, DLT_STRING("pclVerifyConsistency => there is ONLY a backup file"));
506
507       // calculate checksum form backup file
508       fdBackup = open(backupPath,  O_RDONLY);
509       if(fdBackup != -1)
510       {
511          pclCalcCrc32Csum(fdBackup, backCsumBuf);
512          close(fdBackup);
513
514          // calculate the checksum form the original file to see if it matches
515          handle = open(origPath, openFlags);
516          if(handle != -1)
517          {
518             pclCalcCrc32Csum(handle, origCsumBuf);
519
520             if(strcmp(backCsumBuf, origCsumBuf)  != 0)
521             {
522                close(handle);
523                handle = -1;   // checksum does NOT match ==> error: file corrupt
524             }
525             // else case: checksum matches ==> keep original file ==> nothing to do
526
527          }
528          else
529          {
530             close(handle);
531             handle = -1;      // error: file corrupt
532          }
533       }
534       else
535       {
536          close(fdBackup);
537          handle = -1;         // error: file corrupt
538       }
539    }
540    // for else case: nothing to do
541
542
543    // if we are in an inconsistent state: delete file, backup and checksum
544    if(handle == -1)
545    {
546       remove(origPath);
547       remove(backupPath);
548       remove(csumPath);
549    }
550
551    return handle;
552 }
553
554
555
556 int pclRecoverFromBackup(int backupFd, const char* original)
557 {
558    int handle = 0;
559    int readSize = 0;
560    char buffer[RDRWBufferSize];
561
562    handle = open(original, O_TRUNC | O_RDWR);
563    if(handle != -1)
564    {
565       // copy data from one file to another
566       while((readSize = read(backupFd, buffer, RDRWBufferSize)) > 0)
567       {
568          if(write(handle, buffer, readSize) != readSize)
569          {
570             DLT_LOG(gDLTContext, DLT_LOG_ERROR, DLT_STRING("pclRecoverFromBackup => couldn't write whole buffer"));
571             break;
572          }
573       }
574
575    }
576
577    return handle;
578 }
579
580
581
582 int pclCreateBackup(const char* dstPath, int srcfd, const char* csumPath, const char* csumBuf)
583 {
584    int dstFd = 0, csfd = 0;
585    int readSize = -1;
586    char buffer[RDRWBufferSize];
587
588    if(access(dstPath, F_OK) != 0)
589    {
590       char pathToCreate[DbPathMaxLen] = {0};
591       strncpy(pathToCreate, dstPath, DbPathMaxLen);
592       pclCreateFileAndPath(pathToCreate);
593    }
594
595    // create checksum file and and write checksum
596    csfd = open(csumPath, O_CREAT | O_WRONLY | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH);
597    if(csfd != -1)
598    {
599       int csumSize = strlen(csumBuf);
600       if(write(csfd, csumBuf, csumSize) != csumSize)
601       {
602          DLT_LOG(gDLTContext, DLT_LOG_ERROR, DLT_STRING("pclCreateBackup => failed to write checksum to file"));
603       }
604       close(csfd);
605    }
606    else
607    {
608       DLT_LOG(gDLTContext, DLT_LOG_ERROR, DLT_STRING("pclCreateBackup => failed to create checksum file:"), DLT_STRING(strerror(errno)) );
609    }
610
611    // create backup file, user and group has read/write permission, others have read permission
612    dstFd = open(dstPath, O_CREAT | O_WRONLY | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH);
613    if(dstFd != -1)
614    {
615       off_t curPos = 0;
616       // remember the current position
617       curPos = lseek(srcfd, 0, SEEK_CUR);
618
619       // copy data from one file to another
620       while((readSize = read(srcfd, buffer, RDRWBufferSize)) > 0)
621       {
622          if(write(dstFd, buffer, readSize) != readSize)
623          {
624             DLT_LOG(gDLTContext, DLT_LOG_ERROR, DLT_STRING("pclCreateBackup => couldn't write whole buffer"));
625             break;
626          }
627       }
628
629       if(readSize == -1)
630          DLT_LOG(gDLTContext, DLT_LOG_ERROR, DLT_STRING("pcl_create_backup => error copying file"));
631
632       if((readSize = close(dstFd)) == -1)
633          DLT_LOG(gDLTContext, DLT_LOG_ERROR, DLT_STRING("pcl_create_backup => error closing fd"));
634
635       // set back to the position
636       lseek(srcfd, curPos, SEEK_SET);
637    }
638    else
639    {
640       DLT_LOG(gDLTContext, DLT_LOG_ERROR, DLT_STRING("pclCreateBackup => failed to open backup file"),
641                                           DLT_STRING(dstPath), DLT_STRING(strerror(errno)));
642    }
643
644    return readSize;
645 }
646
647
648
649 int pclCalcCrc32Csum(int fd, char crc32sum[])
650 {
651    int rval = 1;
652
653    if(crc32sum != 0)
654    {
655       char* buf;
656       struct stat statBuf;
657
658       fstat(fd, &statBuf);
659       buf = malloc((unsigned int)statBuf.st_size);
660
661       if(buf != 0)
662       {
663          off_t curPos = 0;
664          // remember the current position
665          curPos = lseek(fd, 0, SEEK_CUR);
666
667          if(curPos != 0)
668          {
669             // set to beginning of the file
670             lseek(fd, 0, SEEK_SET);
671          }
672
673          while((rval = read(fd, buf, statBuf.st_size)) > 0)
674          {
675             unsigned int crc = 0;
676             crc = pclCrc32(crc, (unsigned char*)buf, statBuf.st_size);
677             snprintf(crc32sum, ChecksumBufSize-1, "%x", crc);
678          }
679
680          // set back to the position
681          lseek(fd, curPos, SEEK_SET);
682
683          free(buf);
684       }
685    }
686    return rval;
687 }
688
689
690
691 int pclBackupNeeded(const char* path)
692 {
693    return need_backup_key(pclCrc32(0, (const unsigned char*)path, strlen(path)));
694 }
695
696
697
698 int pclCreateFileAndPath(const char* path)
699 {
700    const char* delimiters = "/\n";   // search for blank and end of line
701    char* tokenArray[24];
702    char* thePath = (char*)path;
703    char createPath[DbPathMaxLen] = {0};
704    int numTokens = 0, i = 0, validPath = 1;
705    int rval = -1;
706
707    tokenArray[numTokens++] = strtok(thePath, delimiters);
708    while(tokenArray[numTokens-1] != NULL )
709    {
710      tokenArray[numTokens] = strtok(NULL, delimiters);
711      if(tokenArray[numTokens] != NULL)
712      {
713         numTokens++;
714         if(numTokens >= 24)
715         {
716            validPath = 0;
717            break;
718         }
719      }
720      else
721      {
722         break;
723      }
724    }
725
726    if(validPath == 1)
727    {
728       snprintf(createPath, DbPathMaxLen, "/%s",tokenArray[0] );
729       for(i=1; i<numTokens-1; i++)
730       {
731          // create folders
732          strncat(createPath, "/", DbPathMaxLen-1);
733          strncat(createPath, tokenArray[i], DbPathMaxLen-1);
734          mkdir(createPath, 0744);
735       }
736       // finally create the file
737       strncat(createPath, "/", DbPathMaxLen-1);
738       strncat(createPath, tokenArray[i], DbPathMaxLen-1);
739       rval = open(createPath, O_CREAT|O_RDWR |O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH);
740       close(rval);
741    }
742    else
743    {
744       DLT_LOG(gDLTContext, DLT_LOG_ERROR, DLT_STRING("pclCreateFileAndPath ==> no valid path to create:"), DLT_STRING(path));
745    }
746
747    return rval;
748 }
749
750
751
752 int pclGetPosixPermission(PersistencePermission_e permission)
753 {
754    int posixPerm = 0;
755
756    switch( (int)permission)
757    {
758    case PersistencePermission_ReadWrite:
759       posixPerm = O_RDWR;
760       break;
761    case PersistencePermission_ReadOnly:
762       posixPerm = O_RDONLY;
763       break;
764    case PersistencePermission_WriteOnly:
765       posixPerm = O_WRONLY;
766       break;
767    default:
768       posixPerm = O_RDONLY;
769       break;
770    }
771
772    return posixPerm;
773 }
774
775
776