a9f8a683735feee301db617b2b94a4be04950dfd
[profile/ivi/eet.git] / src / lib / eet_lib.c
1 #ifdef HAVE_CONFIG_H
2 # include <config.h>
3 #endif /* ifdef HAVE_CONFIG_H */
4
5 #ifdef HAVE_ALLOCA_H
6 # include <alloca.h>
7 #elif defined __GNUC__
8 # define alloca __builtin_alloca
9 #elif defined _AIX
10 # define alloca __alloca
11 #elif defined _MSC_VER
12 # include <malloc.h>
13 # define alloca _alloca
14 #else /* ifdef HAVE_ALLOCA_H */
15 # include <stddef.h>
16 # ifdef  __cplusplus
17 extern "C"
18 # endif /* ifdef  __cplusplus */
19 void *    alloca (size_t);
20 #endif /* ifdef HAVE_ALLOCA_H */
21
22 #ifdef _WIN32
23 # include <winsock2.h>
24 #endif /* ifdef _WIN32 */
25
26 #include <stdio.h>
27 #include <errno.h>
28 #include <sys/types.h>
29 #include <sys/stat.h>
30 #include <sys/mman.h>
31 #include <time.h>
32 #include <string.h>
33 #include <fnmatch.h>
34 #include <fcntl.h>
35 #include <zlib.h>
36
37 #ifndef _MSC_VER
38 # include <unistd.h>
39 #endif /* ifndef _MSC_VER */
40
41 #ifdef HAVE_NETINET_IN_H
42 # include <netinet/in.h>
43 #endif /* ifdef HAVE_NETINET_IN_H */
44
45 #ifdef HAVE_EVIL
46 # include <Evil.h>
47 #endif /* ifdef HAVE_EVIL */
48
49 #ifdef HAVE_GNUTLS
50 # include <gnutls/gnutls.h>
51 # include <gcrypt.h>
52 #endif /* ifdef HAVE_GNUTLS */
53
54 #ifdef HAVE_OPENSSL
55 # include <openssl/err.h>
56 # include <openssl/evp.h>
57 #endif /* ifdef HAVE_OPENSSL */
58
59 #ifdef EFL_HAVE_POSIX_THREADS
60 # include <pthread.h>
61 # ifdef HAVE_GNUTLS
62 GCRY_THREAD_OPTION_PTHREAD_IMPL;
63 # endif /* ifdef HAVE_GNUTLS */
64 #endif /* ifdef EFL_HAVE_POSIX_THREADS */
65
66 #include <Eina.h>
67
68 #include "Eet.h"
69 #include "Eet_private.h"
70
71 static Eet_Version _version = { VMAJ, VMIN, VMIC, VREV };
72 EAPI Eet_Version *eet_version = &_version;
73
74 #ifdef HAVE_REALPATH
75 # undef HAVE_REALPATH
76 #endif /* ifdef HAVE_REALPATH */
77
78 #define EET_MAGIC_FILE        0x1ee7ff00
79 #define EET_MAGIC_FILE_HEADER 0x1ee7ff01
80
81 #define EET_MAGIC_FILE2       0x1ee70f42
82
83 typedef struct _Eet_File_Header      Eet_File_Header;
84 typedef struct _Eet_File_Node        Eet_File_Node;
85 typedef struct _Eet_File_Directory   Eet_File_Directory;
86
87 struct _Eet_File
88 {
89    char                *path;
90    FILE                *readfp;
91    Eet_File_Header     *header;
92    Eet_Dictionary      *ed;
93    Eet_Key             *key;
94    const unsigned char *data;
95    const void          *x509_der;
96    const void          *signature;
97    void                *sha1;
98
99    Eet_File_Mode        mode;
100
101    int                  magic;
102    int                  references;
103
104    int                  data_size;
105    int                  x509_length;
106    unsigned int         signature_length;
107    int                  sha1_length;
108
109    time_t               mtime;
110
111 #ifdef EFL_HAVE_THREADS
112 # ifdef EFL_HAVE_POSIX_THREADS
113    pthread_mutex_t      file_lock;
114 # else /* ifdef EFL_HAVE_POSIX_THREADS */
115    HANDLE               file_lock;
116 # endif /* ifdef EFL_HAVE_POSIX_THREADS */
117 #endif /* ifdef EFL_HAVE_THREADS */
118
119    unsigned char        writes_pending : 1;
120    unsigned char        delete_me_now : 1;
121 };
122
123 struct _Eet_File_Header
124 {
125    int                 magic;
126    Eet_File_Directory *directory;
127 };
128
129 struct _Eet_File_Directory
130 {
131    int             size;
132    Eet_File_Node **nodes;
133 };
134
135 struct _Eet_File_Node
136 {
137    char          *name;
138    void          *data;
139    Eet_File_Node *next;  /* FIXME: make buckets linked lists */
140
141    int            offset;
142    int            dictionary_offset;
143    int            name_offset;
144
145    int            name_size;
146    int            size;
147    int            data_size;
148
149    unsigned char  free_name : 1;
150    unsigned char  compression : 1;
151    unsigned char  ciphered : 1;
152    unsigned char  alias : 1;
153 };
154
155 #if 0
156 /* Version 2 */
157 /* NB: all int's are stored in network byte order on disk */
158 /* file format: */
159 int magic; /* magic number ie 0x1ee7ff00 */
160 int num_directory_entries; /* number of directory entries to follow */
161 int bytes_directory_entries; /* bytes of directory entries to follow */
162 struct
163 {
164    int  offset; /* bytes offset into file for data chunk */
165    int  flags; /* flags - for now 0 = uncompressed and clear, 1 = compressed and clear, 2 = uncompressed and ciphered, 3 = compressed and ciphered */
166    int  size; /* size of the data chunk */
167    int  data_size; /* size of the (uncompressed) data chunk */
168    int  name_size; /* length in bytes of the name field */
169    char name[name_size]; /* name string (variable length) and \0 terminated */
170 } directory[num_directory_entries];
171 /* and now startes the data stream... */
172 #endif /* if 0 */
173
174 #if 0
175 /* Version 3 */
176 /* NB: all int's are stored in network byte order on disk */
177 /* file format: */
178 int magic; /* magic number ie 0x1ee70f42 */
179 int num_directory_entries; /* number of directory entries to follow */
180 int num_dictionary_entries; /* number of dictionary entries to follow */
181 struct
182 {
183    int data_offset; /* bytes offset into file for data chunk */
184    int size; /* size of the data chunk */
185    int data_size; /* size of the (uncompressed) data chunk */
186    int name_offset; /* bytes offset into file for name string */
187    int name_size; /* length in bytes of the name field */
188    int flags; /* bit flags - for now:
189                  bit 0 => compresion on/off
190                  bit 1 => ciphered on/off
191                  bit 2 => alias
192                */
193 } directory[num_directory_entries];
194 struct
195 {
196    int hash;
197    int offset;
198    int size;
199    int prev;
200    int next;
201 } dictionary[num_dictionary_entries];
202 /* now start the string stream. */
203 /* and right after them the data stream. */
204 int magic_sign; /* Optional, only if the eet file is signed. */
205 int signature_length; /* Signature length. */
206 int x509_length; /* Public certificate that signed the file. */
207 char signature[signature_length]; /* The signature. */
208 char x509[x509_length]; /* The public certificate. */
209 #endif /* if 0 */
210
211 #define EET_FILE2_HEADER_COUNT           3
212 #define EET_FILE2_DIRECTORY_ENTRY_COUNT  6
213 #define EET_FILE2_DICTIONARY_ENTRY_COUNT 5
214
215 #define EET_FILE2_HEADER_SIZE            (sizeof(int) *\
216                                           EET_FILE2_HEADER_COUNT)
217 #define EET_FILE2_DIRECTORY_ENTRY_SIZE   (sizeof(int) *\
218                                           EET_FILE2_DIRECTORY_ENTRY_COUNT)
219 #define EET_FILE2_DICTIONARY_ENTRY_SIZE  (sizeof(int) *\
220                                           EET_FILE2_DICTIONARY_ENTRY_COUNT)
221
222 /* prototypes of internal calls */
223 static Eet_File *         eet_cache_find(const char *path,
224                                          Eet_File  **cache,
225                                          int         cache_num);
226 static void               eet_cache_add(Eet_File   *ef,
227                                         Eet_File ***cache,
228                                         int        *cache_num,
229                                         int        *cache_alloc);
230 static void               eet_cache_del(Eet_File   *ef,
231                                         Eet_File ***cache,
232                                         int        *cache_num,
233                                         int        *cache_alloc);
234 static int                eet_string_match(const char *s1, const char *s2);
235 #if 0 /* Unused */
236 static Eet_Error          eet_flush(Eet_File *ef);
237 #endif /* if 0 */
238 static Eet_Error          eet_flush2(Eet_File *ef);
239 static Eet_File_Node *    find_node_by_name(Eet_File *ef, const char *name);
240 static int                read_data_from_disk(Eet_File      *ef,
241                                               Eet_File_Node *efn,
242                                               void          *buf,
243                                               int            len);
244
245 static Eet_Error          eet_internal_close(Eet_File *ef, Eina_Bool locked);
246
247 #ifdef EFL_HAVE_THREADS
248
249 # ifdef EFL_HAVE_POSIX_THREADS
250
251 static pthread_mutex_t eet_cache_lock = PTHREAD_MUTEX_INITIALIZER;
252
253 #  define LOCK_CACHE   pthread_mutex_lock(&eet_cache_lock)
254 #  define UNLOCK_CACHE pthread_mutex_unlock(&eet_cache_lock)
255
256 #  define INIT_FILE(File)    pthread_mutex_init(&File->file_lock, NULL)
257 #  define LOCK_FILE(File)    pthread_mutex_lock(&File->file_lock)
258 #  define UNLOCK_FILE(File)  pthread_mutex_unlock(&File->file_lock)
259 #  define DESTROY_FILE(File) pthread_mutex_destroy(&File->file_lock)
260
261 # else /* EFL_HAVE_WIN32_THREADS */
262
263 static HANDLE eet_cache_lock = NULL;
264
265 #  define LOCK_CACHE   WaitForSingleObject(eet_cache_lock, INFINITE)
266 #  define UNLOCK_CACHE ReleaseMutex(eet_cache_lock)
267
268 #  define INIT_FILE(File)    File->file_lock = CreateMutex(NULL, FALSE, NULL)
269 #  define LOCK_FILE(File)    WaitForSingleObject(File->file_lock, INFINITE)
270 #  define UNLOCK_FILE(File)  ReleaseMutex(File->file_lock)
271 #  define DESTROY_FILE(File) CloseHandle(File->file_lock)
272
273 # endif /* EFL_HAVE_WIN32_THREADS */
274
275 #else /* ifdef EFL_HAVE_THREADS */
276
277 # define LOCK_CACHE   do {} while (0)
278 # define UNLOCK_CACHE do {} while (0)
279
280 # define INIT_FILE(File)    do {} while (0)
281 # define LOCK_FILE(File)    do {} while (0)
282 # define UNLOCK_FILE(File)  do {} while (0)
283 # define DESTROY_FILE(File) do {} while (0)
284
285 #endif /* EFL_HAVE_THREADS */
286
287 /* cache. i don't expect this to ever be large, so arrays will do */
288 static int eet_writers_num = 0;
289 static int eet_writers_alloc = 0;
290 static Eet_File **eet_writers = NULL;
291 static int eet_readers_num = 0;
292 static int eet_readers_alloc = 0;
293 static Eet_File **eet_readers = NULL;
294 static int eet_init_count = 0;
295
296 /* log domain variable */
297 int _eet_log_dom_global = -1;
298
299 /* Check to see its' an eet file pointer */
300 static inline int
301 eet_check_pointer(const Eet_File *ef)
302 {
303    if ((!ef) || (ef->magic != EET_MAGIC_FILE))
304       return 1;
305
306    return 0;
307 } /* eet_check_pointer */
308
309 static inline int
310 eet_check_header(const Eet_File *ef)
311 {
312    if (!ef->header)
313       return 1;
314
315    if (!ef->header->directory)
316       return 1;
317
318    return 0;
319 } /* eet_check_header */
320
321 static inline int
322 eet_test_close(int       test,
323                Eet_File *ef)
324 {
325    if (test)
326      {
327         ef->delete_me_now = 1;
328         eet_internal_close(ef, EINA_TRUE);
329      }
330
331    return test;
332 } /* eet_test_close */
333
334 /* find an eet file in the currently in use cache */
335 static Eet_File *
336 eet_cache_find(const char *path,
337                Eet_File  **cache,
338                int         cache_num)
339 {
340    int i;
341
342    /* walk list */
343    for (i = 0; i < cache_num; i++)
344      {
345         /* if matches real path - return it */
346         if (eet_string_match(cache[i]->path, path))
347            if (!cache[i]->delete_me_now)
348               return cache[i];
349
350      }
351
352    /* not found */
353    return NULL;
354 } /* eet_cache_find */
355
356 /* add to end of cache */
357 /* this should only be called when the cache lock is already held */
358 static void
359 eet_cache_add(Eet_File   *ef,
360               Eet_File ***cache,
361               int        *cache_num,
362               int        *cache_alloc)
363 {
364    Eet_File **new_cache;
365    int new_cache_num;
366    int new_cache_alloc;
367
368    new_cache_num = *cache_num;
369    if (new_cache_num >= 64) /* avoid fd overruns - limit to 128 (most recent) in the cache */
370      {
371         Eet_File *del_ef = NULL;
372         int i;
373
374         new_cache = *cache;
375         for (i = 0; i < new_cache_num; i++)
376           {
377              if (new_cache[i]->references == 0)
378                {
379                   del_ef = new_cache[i];
380                   break;
381                }
382           }
383
384         if (del_ef)
385           {
386              del_ef->delete_me_now = 1;
387              eet_internal_close(del_ef, EINA_TRUE);
388           }
389      }
390
391    new_cache = *cache;
392    new_cache_num = *cache_num;
393    new_cache_alloc = *cache_alloc;
394    new_cache_num++;
395    if (new_cache_num > new_cache_alloc)
396      {
397         new_cache_alloc += 16;
398         new_cache = realloc(new_cache, new_cache_alloc * sizeof(Eet_File *));
399         if (!new_cache)
400           {
401              CRIT("BAD ERROR! Eet realloc of cache list failed. Abort");
402              abort();
403           }
404      }
405
406    new_cache[new_cache_num - 1] = ef;
407    *cache = new_cache;
408    *cache_num = new_cache_num;
409    *cache_alloc = new_cache_alloc;
410 } /* eet_cache_add */
411
412 /* delete from cache */
413 /* this should only be called when the cache lock is already held */
414 static void
415 eet_cache_del(Eet_File   *ef,
416               Eet_File ***cache,
417               int        *cache_num,
418               int        *cache_alloc)
419 {
420    Eet_File **new_cache;
421    int new_cache_num, new_cache_alloc;
422    int i, j;
423
424    new_cache = *cache;
425    new_cache_num = *cache_num;
426    new_cache_alloc = *cache_alloc;
427    if (new_cache_num <= 0)
428       return;
429
430    for (i = 0; i < new_cache_num; i++)
431      {
432         if (new_cache[i] == ef)
433            break;
434      }
435
436    if (i >= new_cache_num)
437       return;
438
439    new_cache_num--;
440    for (j = i; j < new_cache_num; j++)
441       new_cache[j] = new_cache[j + 1];
442
443    if (new_cache_num <= (new_cache_alloc - 16))
444      {
445         new_cache_alloc -= 16;
446         if (new_cache_num > 0)
447           {
448              new_cache = realloc(new_cache, new_cache_alloc * sizeof(Eet_File *));
449              if (!new_cache)
450                {
451                   CRIT("BAD ERROR! Eet realloc of cache list failed. Abort");
452                   abort();
453                }
454           }
455         else
456           {
457              free(new_cache);
458              new_cache = NULL;
459           }
460      }
461
462    *cache = new_cache;
463    *cache_num = new_cache_num;
464    *cache_alloc = new_cache_alloc;
465 } /* eet_cache_del */
466
467 /* internal string match. null friendly, catches same ptr */
468 static int
469 eet_string_match(const char *s1,
470                  const char *s2)
471 {
472    /* both null- no match */
473    if ((!s1) || (!s2))
474       return 0;
475
476    if (s1 == s2)
477       return 1;
478
479    return (!strcmp(s1, s2));
480 } /* eet_string_match */
481
482 /* flush out writes to a v2 eet file */
483 static Eet_Error
484 eet_flush2(Eet_File *ef)
485 {
486    Eet_File_Node *efn;
487    FILE *fp;
488    Eet_Error error = EET_ERROR_NONE;
489    int head[EET_FILE2_HEADER_COUNT];
490    int num_directory_entries = 0;
491    int num_dictionary_entries = 0;
492    int bytes_directory_entries = 0;
493    int bytes_dictionary_entries = 0;
494    int bytes_strings = 0;
495    int data_offset = 0;
496    int strings_offset = 0;
497    int num;
498    int i;
499    int j;
500
501    if (eet_check_pointer(ef))
502       return EET_ERROR_BAD_OBJECT;
503
504    if (eet_check_header(ef))
505       return EET_ERROR_EMPTY;
506
507    if (!ef->writes_pending)
508       return EET_ERROR_NONE;
509
510    if ((ef->mode == EET_FILE_MODE_READ_WRITE)
511        || (ef->mode == EET_FILE_MODE_WRITE))
512      {
513         int fd;
514
515         /* opening for write - delete old copy of file right away */
516         unlink(ef->path);
517         fd = open(ef->path, O_CREAT | O_TRUNC | O_RDWR, S_IRUSR | S_IWUSR);
518         fp = fdopen(fd, "wb");
519         if (!fp)
520            return EET_ERROR_NOT_WRITABLE;
521
522         fcntl(fileno(fp), F_SETFD, FD_CLOEXEC);
523      }
524    else
525       return EET_ERROR_NOT_WRITABLE;
526
527    /* calculate string base offset and data base offset */
528    num = (1 << ef->header->directory->size);
529    for (i = 0; i < num; ++i)
530      {
531         for (efn = ef->header->directory->nodes[i]; efn; efn = efn->next)
532           {
533              num_directory_entries++;
534              bytes_strings += strlen(efn->name) + 1;
535           }
536      }
537    if (ef->ed)
538      {
539         num_dictionary_entries = ef->ed->count;
540
541         for (i = 0; i < num_dictionary_entries; ++i)
542            bytes_strings += ef->ed->all[i].len;
543      }
544
545    /* calculate section bytes size */
546    bytes_directory_entries = EET_FILE2_DIRECTORY_ENTRY_SIZE *
547       num_directory_entries + EET_FILE2_HEADER_SIZE;
548    bytes_dictionary_entries = EET_FILE2_DICTIONARY_ENTRY_SIZE *
549       num_dictionary_entries;
550
551    /* calculate per entry offset */
552    strings_offset = bytes_directory_entries + bytes_dictionary_entries;
553    data_offset = bytes_directory_entries + bytes_dictionary_entries +
554       bytes_strings;
555
556    for (i = 0; i < num; ++i)
557      {
558         for (efn = ef->header->directory->nodes[i]; efn; efn = efn->next)
559           {
560              efn->offset = data_offset;
561              data_offset += efn->size;
562
563              efn->name_offset = strings_offset;
564              strings_offset += efn->name_size;
565           }
566      }
567
568    /* calculate dictionary strings offset */
569    if (ef->ed)
570       ef->ed->offset = strings_offset;
571
572    /* go thru and write the header */
573    head[0] = (int)htonl((unsigned int)EET_MAGIC_FILE2);
574    head[1] = (int)htonl((unsigned int)num_directory_entries);
575    head[2] = (int)htonl((unsigned int)num_dictionary_entries);
576
577    fseek(fp, 0, SEEK_SET);
578    if (fwrite(head, sizeof (head), 1, fp) != 1)
579       goto write_error;
580
581    /* write directories entry */
582    for (i = 0; i < num; i++)
583      {
584         for (efn = ef->header->directory->nodes[i]; efn; efn = efn->next)
585           {
586              unsigned int flag;
587              int ibuf[EET_FILE2_DIRECTORY_ENTRY_COUNT];
588
589              flag = (efn->alias << 2) | (efn->ciphered << 1) | efn->compression;
590
591              ibuf[0] = (int)htonl((unsigned int)efn->offset);
592              ibuf[1] = (int)htonl((unsigned int)efn->size);
593              ibuf[2] = (int)htonl((unsigned int)efn->data_size);
594              ibuf[3] = (int)htonl((unsigned int)efn->name_offset);
595              ibuf[4] = (int)htonl((unsigned int)efn->name_size);
596              ibuf[5] = (int)htonl((unsigned int)flag);
597
598              if (fwrite(ibuf, sizeof(ibuf), 1, fp) != 1)
599                 goto write_error;
600           }
601      }
602
603    /* write dictionnary */
604    if (ef->ed)
605      {
606         int offset = strings_offset;
607
608         for (j = 0; j < ef->ed->count; ++j)
609           {
610              int sbuf[EET_FILE2_DICTIONARY_ENTRY_COUNT];
611
612              sbuf[0] = (int)htonl((unsigned int)ef->ed->all[j].hash);
613              sbuf[1] = (int)htonl((unsigned int)offset);
614              sbuf[2] = (int)htonl((unsigned int)ef->ed->all[j].len);
615              sbuf[3] = (int)htonl((unsigned int)ef->ed->all[j].prev);
616              sbuf[4] = (int)htonl((unsigned int)ef->ed->all[j].next);
617
618              offset += ef->ed->all[j].len;
619
620              if (fwrite(sbuf, sizeof (sbuf), 1, fp) != 1)
621                 goto write_error;
622           }
623      }
624
625    /* write directories name */
626    for (i = 0; i < num; i++)
627      {
628         for (efn = ef->header->directory->nodes[i]; efn; efn = efn->next)
629           {
630              if (fwrite(efn->name, efn->name_size, 1, fp) != 1)
631                 goto write_error;
632           }
633      }
634
635    /* write strings */
636    if (ef->ed)
637       for (j = 0; j < ef->ed->count; ++j)
638         {
639            if (ef->ed->all[j].str)
640              {
641                 if (fwrite(ef->ed->all[j].str, ef->ed->all[j].len, 1, fp) != 1)
642                    goto write_error;
643              }
644            else if (fwrite(ef->ed->all[j].mmap, ef->ed->all[j].len, 1, fp) != 1)
645               goto write_error;
646         }
647
648    /* write data */
649    for (i = 0; i < num; i++)
650      {
651         for (efn = ef->header->directory->nodes[i]; efn; efn = efn->next)
652           {
653              if (fwrite(efn->data, efn->size, 1, fp) != 1)
654                 goto write_error;
655           }
656      }
657
658    /* flush all write to the file. */
659    fflush(fp);
660 // this is going to really cause trouble. if ANYTHING this needs to go into a
661 // thread spawned off - but even then...
662 // in this case... ext4 is "wrong". (yes we can jump up and down and point posix
663 // manual pages at eachother, but ext4 broke behavior that has been in place
664 // for decades and that 1000's of apps rely on daily - that is that one operation
665 // to disk is committed to disk BEFORE following operations, so the fs retains
666 // a consistent state
667 //   fsync(fileno(fp));
668
669    /* append signature if required */
670    if (ef->key)
671      {
672         error = eet_identity_sign(fp, ef->key);
673         if (error != EET_ERROR_NONE)
674            goto sign_error;
675      }
676
677    /* no more writes pending */
678    ef->writes_pending = 0;
679
680    fclose(fp);
681
682    return EET_ERROR_NONE;
683
684 write_error:
685    if (ferror(fp))
686      {
687         switch (errno)
688           {
689            case EFBIG: error = EET_ERROR_WRITE_ERROR_FILE_TOO_BIG; break;
690
691            case EIO: error = EET_ERROR_WRITE_ERROR_IO_ERROR; break;
692
693            case ENOSPC: error = EET_ERROR_WRITE_ERROR_OUT_OF_SPACE; break;
694
695            case EPIPE: error = EET_ERROR_WRITE_ERROR_FILE_CLOSED; break;
696
697            default: error = EET_ERROR_WRITE_ERROR; break;
698           } /* switch */
699      }
700
701 sign_error:
702    fclose(fp);
703    return error;
704 } /* eet_flush2 */
705
706 EAPI int
707 eet_init(void)
708 {
709    if (++eet_init_count != 1)
710       return eet_init_count;
711
712    if (!eina_init())
713      {
714         fprintf(stderr, "Eet: Eina init failed");
715         return --eet_init_count;
716      }
717
718    _eet_log_dom_global = eina_log_domain_register("eet", EET_DEFAULT_LOG_COLOR);
719    if (_eet_log_dom_global < 0)
720      {
721         EINA_LOG_ERR("Eet Can not create a general log domain.");
722         goto shutdown_eina;
723      }
724
725    if (!eet_node_init())
726      {
727         EINA_LOG_ERR("Eet: Eet_Node mempool creation failed");
728         goto unregister_log_domain;
729      }
730
731 #ifdef HAVE_GNUTLS
732    /* Before the library can be used, it must initialize itself if needed. */
733    if (gcry_control(GCRYCTL_ANY_INITIALIZATION_P) == 0)
734      {
735         gcry_check_version(NULL);
736         /* Disable warning messages about problems with the secure memory subsystem.
737            This command should be run right after gcry_check_version. */
738         if (gcry_control(GCRYCTL_DISABLE_SECMEM_WARN))
739            goto shutdown_eet;  /* This command is used to allocate a pool of secure memory and thus
740                                   enabling the use of secure memory. It also drops all extra privileges the
741                                   process has (i.e. if it is run as setuid (root)). If the argument nbytes
742                                   is 0, secure memory will be disabled. The minimum amount of secure memory
743                                   allocated is currently 16384 bytes; you may thus use a value of 1 to
744                                   request that default size. */
745
746         if (gcry_control(GCRYCTL_INIT_SECMEM, 16384, 0))
747            WRN(
748               "BIG FAT WARNING: I AM UNABLE TO REQUEST SECMEM, Cryptographic operation are at risk !");
749      }
750
751 # ifdef EFL_HAVE_POSIX_THREADS
752    if (gcry_control(GCRYCTL_SET_THREAD_CBS, &gcry_threads_pthread))
753       WRN(
754          "YOU ARE USING PTHREADS, BUT I CANNOT INITIALIZE THREADSAFE GCRYPT OPERATIONS!");
755
756 # endif /* ifdef EFL_HAVE_POSIX_THREADS */
757    if (gnutls_global_init())
758       goto shutdown_eet;
759
760 #endif /* ifdef HAVE_GNUTLS */
761 #ifdef HAVE_OPENSSL
762    ERR_load_crypto_strings();
763    OpenSSL_add_all_algorithms();
764 #endif /* ifdef HAVE_OPENSSL */
765
766    return eet_init_count;
767
768 #ifdef HAVE_GNUTLS
769 shutdown_eet:
770 #endif   
771    eet_node_shutdown();
772 unregister_log_domain:
773    eina_log_domain_unregister(_eet_log_dom_global);
774    _eet_log_dom_global = -1;
775 shutdown_eina:
776    eina_shutdown();
777    return --eet_init_count;
778 } /* eet_init */
779
780 EAPI int
781 eet_shutdown(void)
782 {
783    if (--eet_init_count != 0)
784       return eet_init_count;
785
786    eet_clearcache();
787    eet_node_shutdown();
788 #ifdef HAVE_GNUTLS
789    gnutls_global_deinit();
790 #endif /* ifdef HAVE_GNUTLS */
791 #ifdef HAVE_OPENSSL
792    EVP_cleanup();
793    ERR_free_strings();
794 #endif /* ifdef HAVE_OPENSSL */
795    eina_log_domain_unregister(_eet_log_dom_global);
796    _eet_log_dom_global = -1;
797    eina_shutdown();
798
799    return eet_init_count;
800 } /* eet_shutdown */
801
802 EAPI Eet_Error
803 eet_sync(Eet_File *ef)
804 {
805    Eet_Error ret;
806
807    if (eet_check_pointer(ef))
808       return EET_ERROR_BAD_OBJECT;
809
810    if ((ef->mode != EET_FILE_MODE_WRITE) &&
811        (ef->mode != EET_FILE_MODE_READ_WRITE))
812       return EET_ERROR_NOT_WRITABLE;
813
814    if (!ef->writes_pending)
815       return EET_ERROR_NONE;
816
817    LOCK_FILE(ef);
818
819    ret = eet_flush2(ef);
820
821    UNLOCK_FILE(ef);
822    return ret;
823 } /* eet_sync */
824
825 EAPI void
826 eet_clearcache(void)
827 {
828    int num = 0;
829    int i;
830
831    /*
832     * We need to compute the list of eet file to close separately from the cache,
833     * due to eet_close removing them from the cache after each call.
834     */
835    LOCK_CACHE;
836    for (i = 0; i < eet_writers_num; i++)
837      {
838         if (eet_writers[i]->references <= 0)
839            num++;
840      }
841
842    for (i = 0; i < eet_readers_num; i++)
843      {
844         if (eet_readers[i]->references <= 0)
845            num++;
846      }
847
848    if (num > 0)
849      {
850         Eet_File **closelist = NULL;
851
852         closelist = alloca(num * sizeof(Eet_File *));
853         num = 0;
854         for (i = 0; i < eet_writers_num; i++)
855           {
856              if (eet_writers[i]->references <= 0)
857                {
858                   closelist[num] = eet_writers[i];
859                   eet_writers[i]->delete_me_now = 1;
860                   num++;
861                }
862           }
863
864         for (i = 0; i < eet_readers_num; i++)
865           {
866              if (eet_readers[i]->references <= 0)
867                {
868                   closelist[num] = eet_readers[i];
869                   eet_readers[i]->delete_me_now = 1;
870                   num++;
871                }
872           }
873
874         for (i = 0; i < num; i++)
875           {
876              eet_internal_close(closelist[i], EINA_TRUE);
877           }
878      }
879
880    UNLOCK_CACHE;
881 } /* eet_clearcache */
882
883 /* FIXME: MMAP race condition in READ_WRITE_MODE */
884 static Eet_File *
885 eet_internal_read2(Eet_File *ef)
886 {
887    const int *data = (const int *)ef->data;
888    const char *start = (const char *)ef->data;
889    int idx = 0;
890    int num_directory_entries;
891    int bytes_directory_entries;
892    int num_dictionary_entries;
893    int bytes_dictionary_entries;
894    int signature_base_offset;
895    int i;
896
897    idx += sizeof(int);
898    if (eet_test_close((int)ntohl(*data) != EET_MAGIC_FILE2, ef))
899       return NULL;
900
901    data++;
902
903 #define GET_INT(Value, Pointer, Index)\
904    {\
905       Value = ntohl(*Pointer);\
906       Pointer++;\
907       Index += sizeof(int);\
908    }
909
910    /* get entries count and byte count */
911    GET_INT(num_directory_entries,  data, idx);
912    /* get dictionary count and byte count */
913    GET_INT(num_dictionary_entries, data, idx);
914
915    bytes_directory_entries = EET_FILE2_DIRECTORY_ENTRY_SIZE *
916       num_directory_entries + EET_FILE2_HEADER_SIZE;
917    bytes_dictionary_entries = EET_FILE2_DICTIONARY_ENTRY_SIZE *
918       num_dictionary_entries;
919
920    /* we can't have <= 0 values here - invalid */
921    if (eet_test_close((num_directory_entries <= 0), ef))
922       return NULL;
923
924    /* we can't have more bytes directory and bytes in dictionaries than the size of the file */
925    if (eet_test_close((bytes_directory_entries + bytes_dictionary_entries) >
926                       ef->data_size, ef))
927       return NULL;
928
929    /* allocate header */
930    ef->header = calloc(1, sizeof(Eet_File_Header));
931    if (eet_test_close(!ef->header, ef))
932       return NULL;
933
934    ef->header->magic = EET_MAGIC_FILE_HEADER;
935
936    /* allocate directory block in ram */
937    ef->header->directory = calloc(1, sizeof(Eet_File_Directory));
938    if (eet_test_close(!ef->header->directory, ef))
939       return NULL;
940
941    /* 8 bit hash table (256 buckets) */
942    ef->header->directory->size = 8;
943    /* allocate base hash table */
944    ef->header->directory->nodes =
945       calloc(1, sizeof(Eet_File_Node *) * (1 << ef->header->directory->size));
946    if (eet_test_close(!ef->header->directory->nodes, ef))
947       return NULL;
948
949    signature_base_offset = 0;
950
951    /* actually read the directory block - all of it, into ram */
952    for (i = 0; i < num_directory_entries; ++i)
953      {
954         const char *name;
955         Eet_File_Node *efn;
956         int name_offset;
957         int name_size;
958         int hash;
959         int flag;
960
961         /* out directory block is inconsistent - we have oveerun our */
962         /* dynamic block buffer before we finished scanning dir entries */
963         efn = malloc(sizeof(Eet_File_Node));
964         if (eet_test_close(!efn, ef))
965           {
966              if (efn) free(efn); /* yes i know - we only get here if 
967                                   * efn is null/0 -> trying to shut up
968                                   * warning tools like cppcheck */
969              return NULL;
970           }
971
972         /* get entrie header */
973         GET_INT(efn->offset,    data, idx);
974         GET_INT(efn->size,      data, idx);
975         GET_INT(efn->data_size, data, idx);
976         GET_INT(name_offset,    data, idx);
977         GET_INT(name_size,      data, idx);
978         GET_INT(flag,           data, idx);
979
980         efn->compression = flag & 0x1 ? 1 : 0;
981         efn->ciphered = flag & 0x2 ? 1 : 0;
982         efn->alias = flag & 0x4 ? 1 : 0;
983
984 #define EFN_TEST(Test, Ef, Efn)\
985    if (eet_test_close(Test, Ef))\
986      {\
987         free(Efn);\
988         return NULL;\
989      }
990
991         /* check data pointer position */
992         EFN_TEST(!((efn->size > 0)
993                    && (efn->offset + efn->size <= ef->data_size)
994                    && (efn->offset > bytes_dictionary_entries +
995                        bytes_directory_entries)), ef, efn);
996
997         /* check name position */
998         EFN_TEST(!((name_size > 0)
999                    && (name_offset + name_size < ef->data_size)
1000                    && (name_offset >= bytes_dictionary_entries +
1001                        bytes_directory_entries)), ef, efn);
1002
1003         name = start + name_offset;
1004
1005         /* check '\0' at the end of name string */
1006         EFN_TEST(name[name_size - 1] != '\0', ef, efn);
1007
1008         efn->free_name = 0;
1009         efn->name = (char *)name;
1010         efn->name_size = name_size;
1011
1012         hash = _eet_hash_gen(efn->name, ef->header->directory->size);
1013         efn->next = ef->header->directory->nodes[hash];
1014         ef->header->directory->nodes[hash] = efn;
1015
1016         /* read-only mode, so currently we have no data loaded */
1017         if (ef->mode == EET_FILE_MODE_READ)
1018            efn->data = NULL;  /* read-write mode - read everything into ram */
1019         else
1020           {
1021              efn->data = malloc(efn->size);
1022              if (efn->data)
1023                 memcpy(efn->data, ef->data + efn->offset, efn->size);
1024           }
1025
1026         /* compute the possible position of a signature */
1027         if (signature_base_offset < efn->offset + efn->size)
1028            signature_base_offset = efn->offset + efn->size;
1029      }
1030
1031    ef->ed = NULL;
1032
1033    if (num_dictionary_entries)
1034      {
1035         const int *dico = (const int *)ef->data +
1036            EET_FILE2_DIRECTORY_ENTRY_COUNT * num_directory_entries +
1037            EET_FILE2_HEADER_COUNT;
1038         int j;
1039
1040         if (eet_test_close((num_dictionary_entries *
1041                             (int)EET_FILE2_DICTIONARY_ENTRY_SIZE + idx) >
1042                            (bytes_dictionary_entries + bytes_directory_entries),
1043                            ef))
1044            return NULL;
1045
1046         ef->ed = calloc(1, sizeof (Eet_Dictionary));
1047         if (eet_test_close(!ef->ed, ef))
1048            return NULL;
1049
1050         ef->ed->all = calloc(num_dictionary_entries, sizeof (Eet_String));
1051         if (eet_test_close(!ef->ed->all, ef))
1052            return NULL;
1053
1054         ef->ed->count = num_dictionary_entries;
1055         ef->ed->total = num_dictionary_entries;
1056         ef->ed->start = start + bytes_dictionary_entries +
1057            bytes_directory_entries;
1058         ef->ed->end = ef->ed->start;
1059
1060         for (j = 0; j < ef->ed->count; ++j)
1061           {
1062              int hash;
1063              int offset;
1064
1065              GET_INT(hash,                dico, idx);
1066              GET_INT(offset,              dico, idx);
1067              GET_INT(ef->ed->all[j].len,  dico, idx);
1068              GET_INT(ef->ed->all[j].prev, dico, idx);
1069              GET_INT(ef->ed->all[j].next, dico, idx);
1070
1071              /* Hash value could be stored on 8bits data, but this will break alignment of all the others data.
1072                 So stick to int and check the value. */
1073              if (eet_test_close(hash & 0xFFFFFF00, ef))
1074                 return NULL;
1075
1076              /* Check string position */
1077              if (eet_test_close(!((ef->ed->all[j].len > 0)
1078                                   && (offset >
1079                                       (bytes_dictionary_entries +
1080                                        bytes_directory_entries))
1081                                   && (offset + ef->ed->all[j].len <
1082                                       ef->data_size)), ef))
1083                 return NULL;
1084
1085              ef->ed->all[j].mmap = start + offset;
1086              ef->ed->all[j].str = NULL;
1087
1088              if (ef->ed->all[j].mmap + ef->ed->all[j].len > ef->ed->end)
1089                 ef->ed->end = ef->ed->all[j].mmap + ef->ed->all[j].len;
1090
1091              /* Check '\0' at the end of the string */
1092              if (eet_test_close(ef->ed->all[j].mmap[ef->ed->all[j].len - 1] !=
1093                                 '\0', ef))
1094                 return NULL;
1095
1096              ef->ed->all[j].hash = hash;
1097              if (ef->ed->all[j].prev == -1)
1098                 ef->ed->hash[hash] = j;
1099
1100              /* compute the possible position of a signature */
1101              if (signature_base_offset < offset + ef->ed->all[j].len)
1102                 signature_base_offset = offset + ef->ed->all[j].len;
1103           }
1104      }
1105
1106    /* Check if the file is signed */
1107    ef->x509_der = NULL;
1108    ef->x509_length = 0;
1109    ef->signature = NULL;
1110    ef->signature_length = 0;
1111
1112    if (signature_base_offset < ef->data_size)
1113      {
1114 #ifdef HAVE_SIGNATURE
1115         const unsigned char *buffer = ((const unsigned char *)ef->data) +
1116            signature_base_offset;
1117         ef->x509_der = eet_identity_check(ef->data,
1118                                           signature_base_offset,
1119                                           &ef->sha1,
1120                                           &ef->sha1_length,
1121                                           buffer,
1122                                           ef->data_size - signature_base_offset,
1123                                           &ef->signature,
1124                                           &ef->signature_length,
1125                                           &ef->x509_length);
1126
1127         if (eet_test_close(!ef->x509_der, ef))
1128            return NULL;
1129
1130 #else /* ifdef HAVE_SIGNATURE */
1131         ERR(
1132            "This file could be signed but you didn't compile the necessary code to check the signature.");
1133 #endif /* ifdef HAVE_SIGNATURE */
1134      }
1135
1136    return ef;
1137 } /* eet_internal_read2 */
1138
1139 #if EET_OLD_EET_FILE_FORMAT
1140 static Eet_File *
1141 eet_internal_read1(Eet_File *ef)
1142 {
1143    const unsigned char *dyn_buf = NULL;
1144    const unsigned char *p = NULL;
1145    int idx = 0;
1146    int num_entries;
1147    int byte_entries;
1148    int i;
1149
1150    WRN(
1151       "EET file format of '%s' is deprecated. You should just open it one time with mode == EET_FILE_MODE_READ_WRITE to solve this issue.",
1152       ef->path);
1153
1154    /* build header table if read mode */
1155    /* geat header */
1156    idx += sizeof(int);
1157    if (eet_test_close((int)ntohl(*((int *)ef->data)) != EET_MAGIC_FILE, ef))
1158       return NULL;
1159
1160 #define EXTRACT_INT(Value, Pointer, Index)\
1161    {\
1162       int tmp;\
1163       memcpy(&tmp, Pointer + Index, sizeof(int));\
1164       Value = ntohl(tmp);\
1165       Index += sizeof(int);\
1166    }
1167
1168    /* get entries count and byte count */
1169    EXTRACT_INT(num_entries,  ef->data, idx);
1170    EXTRACT_INT(byte_entries, ef->data, idx);
1171
1172    /* we can't have <= 0 values here - invalid */
1173    if (eet_test_close((num_entries <= 0) || (byte_entries <= 0), ef))
1174       return NULL;
1175
1176    /* we can't have more entires than minimum bytes for those! invalid! */
1177    if (eet_test_close((num_entries * 20) > byte_entries, ef))
1178       return NULL;
1179
1180    /* check we will not outrun the file limit */
1181    if (eet_test_close(((byte_entries + (int)sizeof(int) * 3) > ef->data_size),
1182                       ef))
1183       return NULL;
1184
1185    /* allocate header */
1186    ef->header = calloc(1, sizeof(Eet_File_Header));
1187    if (eet_test_close(!ef->header, ef))
1188       return NULL;
1189
1190    ef->header->magic = EET_MAGIC_FILE_HEADER;
1191
1192    /* allocate directory block in ram */
1193    ef->header->directory = calloc(1, sizeof(Eet_File_Directory));
1194    if (eet_test_close(!ef->header->directory, ef))
1195       return NULL;
1196
1197    /* 8 bit hash table (256 buckets) */
1198    ef->header->directory->size = 8;
1199    /* allocate base hash table */
1200    ef->header->directory->nodes =
1201       calloc(1, sizeof(Eet_File_Node *) * (1 << ef->header->directory->size));
1202    if (eet_test_close(!ef->header->directory->nodes, ef))
1203       return NULL;
1204
1205    /* actually read the directory block - all of it, into ram */
1206    dyn_buf = ef->data + idx;
1207
1208    /* parse directory block */
1209    p = dyn_buf;
1210
1211    for (i = 0; i < num_entries; i++)
1212      {
1213         Eet_File_Node *efn;
1214         void *data = NULL;
1215         int indexn = 0;
1216         int name_size;
1217         int hash;
1218         int k;
1219
1220 #define HEADER_SIZE (sizeof(int) * 5)
1221
1222         /* out directory block is inconsistent - we have oveerun our */
1223         /* dynamic block buffer before we finished scanning dir entries */
1224         if (eet_test_close(p + HEADER_SIZE >= (dyn_buf + byte_entries), ef))
1225            return NULL;
1226
1227         /* allocate all the ram needed for this stored node accounting */
1228         efn = malloc (sizeof(Eet_File_Node));
1229         if (eet_test_close(!efn, ef))
1230           {
1231              if (efn) free(efn); /* yes i know - we only get here if 
1232                                   * efn is null/0 -> trying to shut up
1233                                   * warning tools like cppcheck */
1234              return NULL;
1235           }
1236
1237         /* get entrie header */
1238         EXTRACT_INT(efn->offset,      p, indexn);
1239         EXTRACT_INT(efn->compression, p, indexn);
1240         EXTRACT_INT(efn->size,        p, indexn);
1241         EXTRACT_INT(efn->data_size,   p, indexn);
1242         EXTRACT_INT(name_size,        p, indexn);
1243
1244         efn->name_size = name_size;
1245         efn->ciphered = 0;
1246         efn->alias = 0;
1247
1248         /* invalid size */
1249         if (eet_test_close(efn->size <= 0, ef))
1250           {
1251              free(efn);
1252              return NULL;
1253           }
1254
1255         /* invalid name_size */
1256         if (eet_test_close(name_size <= 0, ef))
1257           {
1258              free(efn);
1259              return NULL;
1260           }
1261
1262         /* reading name would mean falling off end of dyn_buf - invalid */
1263         if (eet_test_close((p + 16 + name_size) > (dyn_buf + byte_entries), ef))
1264           {
1265              free(efn);
1266              return NULL;
1267           }
1268
1269         /* This code is useless if we dont want backward compatibility */
1270         for (k = name_size;
1271              k > 0 && ((unsigned char)*(p + HEADER_SIZE + k)) != 0; --k)
1272            ;
1273
1274         efn->free_name = ((unsigned char)*(p + HEADER_SIZE + k)) != 0;
1275
1276         if (efn->free_name)
1277           {
1278              efn->name = malloc(sizeof(char) * name_size + 1);
1279              if (eet_test_close(!efn->name, ef))
1280                {
1281                   free(efn);
1282                   return NULL;
1283                }
1284
1285              strncpy(efn->name, (char *)p + HEADER_SIZE, name_size);
1286              efn->name[name_size] = 0;
1287
1288              WRN(
1289                 "File: %s is not up to date for key \"%s\" - needs rebuilding sometime",
1290                 ef->path,
1291                 efn->name);
1292           }
1293         else
1294            /* The only really useful peace of code for efn->name (no backward compatibility) */
1295            efn->name = (char *)((unsigned char *)(p + HEADER_SIZE));
1296
1297         /* get hash bucket it should go in */
1298         hash = _eet_hash_gen(efn->name, ef->header->directory->size);
1299         efn->next = ef->header->directory->nodes[hash];
1300         ef->header->directory->nodes[hash] = efn;
1301
1302         /* read-only mode, so currently we have no data loaded */
1303         if (ef->mode == EET_FILE_MODE_READ)
1304            efn->data = NULL;  /* read-write mode - read everything into ram */
1305         else
1306           {
1307              data = malloc(efn->size);
1308              if (data)
1309                 memcpy(data, ef->data + efn->offset, efn->size);
1310
1311              efn->data = data;
1312           }
1313
1314         /* advance */
1315         p += HEADER_SIZE + name_size;
1316      }
1317    return ef;
1318 } /* eet_internal_read1 */
1319
1320 #endif /* if EET_OLD_EET_FILE_FORMAT */
1321
1322 /*
1323  * this should only be called when the cache lock is already held
1324  * (We could drop this restriction if we add a parameter to eet_test_close
1325  * that indicates if the lock is held or not.  For now it is easiest
1326  * to just require that it is always held.)
1327  */
1328 static Eet_File *
1329 eet_internal_read(Eet_File *ef)
1330 {
1331    const int *data = (const int *)ef->data;
1332
1333    if (eet_test_close((ef->data == (void *)-1) || (!ef->data), ef))
1334       return NULL;
1335
1336    if (eet_test_close(ef->data_size < (int)sizeof(int) * 3, ef))
1337       return NULL;
1338
1339    switch (ntohl(*data))
1340      {
1341 #if EET_OLD_EET_FILE_FORMAT
1342       case EET_MAGIC_FILE:
1343          return eet_internal_read1(ef);
1344
1345 #endif /* if EET_OLD_EET_FILE_FORMAT */
1346       case EET_MAGIC_FILE2:
1347          return eet_internal_read2(ef);
1348
1349       default:
1350          ef->delete_me_now = 1;
1351          eet_internal_close(ef, EINA_TRUE);
1352          break;
1353      } /* switch */
1354
1355    return NULL;
1356 } /* eet_internal_read */
1357
1358 static Eet_Error
1359 eet_internal_close(Eet_File *ef,
1360                    Eina_Bool locked)
1361 {
1362    Eet_Error err;
1363
1364    /* check to see its' an eet file pointer */
1365    if (eet_check_pointer(ef))
1366       return EET_ERROR_BAD_OBJECT;
1367
1368    if (!locked)
1369       LOCK_CACHE;
1370
1371    /* deref */
1372    ef->references--;
1373    /* if its still referenced - dont go any further */
1374    if (ef->references > 0)
1375       goto on_error;  /* flush any writes */
1376
1377    err = eet_flush2(ef);
1378
1379    eet_identity_unref(ef->key);
1380    ef->key = NULL;
1381
1382    /* if not urgent to delete it - dont free it - leave it in cache */
1383    if ((!ef->delete_me_now) && (ef->mode == EET_FILE_MODE_READ))
1384       goto on_error;
1385
1386    /* remove from cache */
1387    if (ef->mode == EET_FILE_MODE_READ)
1388       eet_cache_del(ef, &eet_readers, &eet_readers_num, &eet_readers_alloc);
1389    else if ((ef->mode == EET_FILE_MODE_WRITE) ||
1390             (
1391                ef
1392                ->
1393                mode == EET_FILE_MODE_READ_WRITE))
1394       eet_cache_del(ef, &eet_writers, &eet_writers_num, &eet_writers_alloc);
1395
1396    /* we can unlock the cache now */
1397    if (!locked)
1398       UNLOCK_CACHE;
1399
1400    DESTROY_FILE(ef);
1401
1402    /* free up data */
1403    if (ef->header)
1404      {
1405         if (ef->header->directory)
1406           {
1407              if (ef->header->directory->nodes)
1408                {
1409                   int i, num;
1410
1411                   num = (1 << ef->header->directory->size);
1412                   for (i = 0; i < num; i++)
1413                     {
1414                        Eet_File_Node *efn;
1415
1416                        while ((efn = ef->header->directory->nodes[i]))
1417                          {
1418                             if (efn->data)
1419                                free(efn->data);
1420
1421                             ef->header->directory->nodes[i] = efn->next;
1422
1423                             if (efn->free_name)
1424                                free(efn->name);
1425
1426                             free(efn);
1427                          }
1428                     }
1429                   free(ef->header->directory->nodes);
1430                }
1431
1432              free(ef->header->directory);
1433           }
1434
1435         free(ef->header);
1436      }
1437
1438    eet_dictionary_free(ef->ed);
1439
1440    if (ef->sha1)
1441       free(ef->sha1);
1442
1443    if (ef->data)
1444       munmap((void *)ef->data, ef->data_size);
1445
1446    if (ef->readfp)
1447       fclose(ef->readfp);
1448
1449    /* zero out ram for struct - caution tactic against stale memory use */
1450    memset(ef, 0, sizeof(Eet_File));
1451
1452    /* free it */
1453    free(ef);
1454    return err;
1455
1456 on_error:
1457    if (!locked)
1458       UNLOCK_CACHE;
1459
1460    return EET_ERROR_NONE;
1461 } /* eet_internal_close */
1462
1463 EAPI Eet_File *
1464 eet_memopen_read(const void *data,
1465                  size_t      size)
1466 {
1467    Eet_File *ef;
1468
1469    if (!data || size == 0)
1470       return NULL;
1471
1472    ef = malloc (sizeof (Eet_File));
1473    if (!ef)
1474       return NULL;
1475
1476    INIT_FILE(ef);
1477    ef->ed = NULL;
1478    ef->path = NULL;
1479    ef->key = NULL;
1480    ef->magic = EET_MAGIC_FILE;
1481    ef->references = 1;
1482    ef->mode = EET_FILE_MODE_READ;
1483    ef->header = NULL;
1484    ef->mtime = 0;
1485    ef->delete_me_now = 1;
1486    ef->readfp = NULL;
1487    ef->data = data;
1488    ef->data_size = size;
1489    ef->sha1 = NULL;
1490    ef->sha1_length = 0;
1491
1492    /* eet_internal_read expects the cache lock to be held when it is called */
1493    LOCK_CACHE;
1494    ef = eet_internal_read(ef);
1495    UNLOCK_CACHE;
1496    return ef;
1497 } /* eet_memopen_read */
1498
1499 EAPI Eet_File *
1500 eet_open(const char   *file,
1501          Eet_File_Mode mode)
1502 {
1503    FILE *fp;
1504    Eet_File *ef;
1505    int file_len;
1506    struct stat file_stat;
1507
1508    if (!file)
1509       return NULL;
1510
1511    /* find the current file handle in cache*/
1512    ef = NULL;
1513    LOCK_CACHE;
1514    if (mode == EET_FILE_MODE_READ)
1515      {
1516         ef = eet_cache_find((char *)file, eet_writers, eet_writers_num);
1517         if (ef)
1518           {
1519              eet_sync(ef);
1520              ef->references++;
1521              ef->delete_me_now = 1;
1522              eet_internal_close(ef, EINA_TRUE);
1523           }
1524
1525         ef = eet_cache_find((char *)file, eet_readers, eet_readers_num);
1526      }
1527    else if ((mode == EET_FILE_MODE_WRITE) ||
1528             (mode == EET_FILE_MODE_READ_WRITE))
1529      {
1530         ef = eet_cache_find((char *)file, eet_readers, eet_readers_num);
1531         if (ef)
1532           {
1533              ef->delete_me_now = 1;
1534              ef->references++;
1535              eet_internal_close(ef, EINA_TRUE);
1536           }
1537
1538         ef = eet_cache_find((char *)file, eet_writers, eet_writers_num);
1539      }
1540
1541    /* try open the file based on mode */
1542    if ((mode == EET_FILE_MODE_READ) || (mode == EET_FILE_MODE_READ_WRITE))
1543      {
1544         /* Prevent garbage in futur comparison. */
1545         file_stat.st_mtime = 0;
1546
1547         fp = fopen(file, "rb");
1548         if (!fp)
1549            goto open_error;
1550
1551         if (fstat(fileno(fp), &file_stat))
1552           {
1553              fclose(fp);
1554              fp = NULL;
1555              goto open_error;
1556           }
1557
1558         if (file_stat.st_size < ((int)sizeof(int) * 3))
1559           {
1560              fclose(fp);
1561              fp = NULL;
1562              goto open_error;
1563           }
1564
1565 open_error:
1566         if (!fp && mode == EET_FILE_MODE_READ)
1567            goto on_error;
1568      }
1569    else
1570      {
1571         if (mode != EET_FILE_MODE_WRITE)
1572            return NULL;
1573
1574         memset(&file_stat, 0, sizeof(file_stat));
1575
1576         fp = NULL;
1577      }
1578
1579    /* We found one */
1580    if (ef &&
1581        ((file_stat.st_mtime != ef->mtime) ||
1582         (file_stat.st_size != ef->data_size)))
1583      {
1584         ef->delete_me_now = 1;
1585         ef->references++;
1586         eet_internal_close(ef, EINA_TRUE);
1587         ef = NULL;
1588      }
1589
1590    if (ef)
1591      {
1592         /* reference it up and return it */
1593         if (fp)
1594            fclose(fp);
1595
1596         ef->references++;
1597         UNLOCK_CACHE;
1598         return ef;
1599      }
1600
1601    file_len = strlen(file) + 1;
1602
1603    /* Allocate struct for eet file and have it zero'd out */
1604    ef = malloc(sizeof(Eet_File) + file_len);
1605    if (!ef)
1606       goto on_error;
1607
1608    /* fill some of the members */
1609    INIT_FILE(ef);
1610    ef->key = NULL;
1611    ef->readfp = fp;
1612    ef->path = ((char *)ef) + sizeof(Eet_File);
1613    memcpy(ef->path, file, file_len);
1614    ef->magic = EET_MAGIC_FILE;
1615    ef->references = 1;
1616    ef->mode = mode;
1617    ef->header = NULL;
1618    ef->mtime = file_stat.st_mtime;
1619    ef->writes_pending = 0;
1620    ef->delete_me_now = 0;
1621    ef->data = NULL;
1622    ef->data_size = 0;
1623    ef->sha1 = NULL;
1624    ef->sha1_length = 0;
1625
1626    ef->ed = (mode == EET_FILE_MODE_WRITE)
1627       || (!ef->readfp && mode == EET_FILE_MODE_READ_WRITE) ?
1628       eet_dictionary_add() : NULL;
1629
1630    if (!ef->readfp &&
1631        (mode == EET_FILE_MODE_READ_WRITE || mode == EET_FILE_MODE_WRITE))
1632       goto empty_file;
1633
1634    /* if we can't open - bail out */
1635    if (eet_test_close(!ef->readfp, ef))
1636       goto on_error;
1637
1638    fcntl(fileno(ef->readfp), F_SETFD, FD_CLOEXEC);
1639    /* if we opened for read or read-write */
1640    if ((mode == EET_FILE_MODE_READ) || (mode == EET_FILE_MODE_READ_WRITE))
1641      {
1642         ef->data_size = file_stat.st_size;
1643         ef->data = mmap(NULL, ef->data_size, PROT_READ,
1644                         MAP_SHARED, fileno(ef->readfp), 0);
1645         if (eet_test_close((ef->data == MAP_FAILED), ef))
1646            goto on_error;
1647
1648         ef = eet_internal_read(ef);
1649         if (!ef)
1650            goto on_error;
1651      }
1652
1653 empty_file:
1654    /* add to cache */
1655    if (ef->references == 1)
1656      {
1657         if (ef->mode == EET_FILE_MODE_READ)
1658            eet_cache_add(ef, &eet_readers, &eet_readers_num, &eet_readers_alloc);
1659         else
1660         if ((ef->mode == EET_FILE_MODE_WRITE) ||
1661             (ef->mode == EET_FILE_MODE_READ_WRITE))
1662            eet_cache_add(ef, &eet_writers, &eet_writers_num, &eet_writers_alloc);
1663      }
1664
1665    UNLOCK_CACHE;
1666    return ef;
1667
1668 on_error:
1669    UNLOCK_CACHE;
1670    return NULL;
1671 } /* eet_open */
1672
1673 EAPI Eet_File_Mode
1674 eet_mode_get(Eet_File *ef)
1675 {
1676    /* check to see its' an eet file pointer */
1677    if ((!ef) || (ef->magic != EET_MAGIC_FILE))
1678       return EET_FILE_MODE_INVALID;
1679    else
1680       return ef->mode;
1681 } /* eet_mode_get */
1682
1683 EAPI const void *
1684 eet_identity_x509(Eet_File *ef,
1685                   int      *der_length)
1686 {
1687    if (!ef->x509_der)
1688       return NULL;
1689
1690    if (der_length)
1691       *der_length = ef->x509_length;
1692
1693    return ef->x509_der;
1694 } /* eet_identity_x509 */
1695
1696 EAPI const void *
1697 eet_identity_signature(Eet_File *ef,
1698                        int      *signature_length)
1699 {
1700    if (!ef->signature)
1701       return NULL;
1702
1703    if (signature_length)
1704       *signature_length = ef->signature_length;
1705
1706    return ef->signature;
1707 } /* eet_identity_signature */
1708
1709 EAPI const void *
1710 eet_identity_sha1(Eet_File *ef,
1711                   int      *sha1_length)
1712 {
1713    if (!ef->sha1)
1714       ef->sha1 = eet_identity_compute_sha1(ef->data,
1715                                            ef->data_size,
1716                                            &ef->sha1_length);
1717
1718    if (sha1_length)
1719       *sha1_length = ef->sha1_length;
1720
1721    return ef->sha1;
1722 } /* eet_identity_sha1 */
1723
1724 EAPI Eet_Error
1725 eet_identity_set(Eet_File *ef,
1726                  Eet_Key  *key)
1727 {
1728    Eet_Key *tmp = ef->key;
1729
1730    if (!ef)
1731       return EET_ERROR_BAD_OBJECT;
1732
1733    ef->key = key;
1734    eet_identity_ref(ef->key);
1735    eet_identity_unref(tmp);
1736
1737    /* flags that writes are pending */
1738    ef->writes_pending = 1;
1739
1740    return EET_ERROR_NONE;
1741 } /* eet_identity_set */
1742
1743 EAPI Eet_Error
1744 eet_close(Eet_File *ef)
1745 {
1746    return eet_internal_close(ef, EINA_FALSE);
1747 } /* eet_close */
1748
1749 EAPI void *
1750 eet_read_cipher(Eet_File   *ef,
1751                 const char *name,
1752                 int        *size_ret,
1753                 const char *cipher_key)
1754 {
1755    Eet_File_Node *efn;
1756    char *data = NULL;
1757    int size = 0;
1758
1759    if (size_ret)
1760       *size_ret = 0;
1761
1762    /* check to see its' an eet file pointer */
1763    if (eet_check_pointer(ef))
1764       return NULL;
1765
1766    if (!name)
1767       return NULL;
1768
1769    if ((ef->mode != EET_FILE_MODE_READ) &&
1770        (ef->mode != EET_FILE_MODE_READ_WRITE))
1771       return NULL;
1772
1773    /* no header, return NULL */
1774    if (eet_check_header(ef))
1775       return NULL;
1776
1777    LOCK_FILE(ef);
1778
1779    /* hunt hash bucket */
1780    efn = find_node_by_name(ef, name);
1781    if (!efn)
1782       goto on_error;
1783
1784    /* get size (uncompressed, if compressed at all) */
1785    size = efn->data_size;
1786
1787    /* allocate data */
1788    data = malloc(size);
1789    if (!data)
1790       goto on_error;
1791
1792    /* uncompressed data */
1793    if (efn->compression == 0)
1794      {
1795         void *data_deciphered = NULL;
1796         unsigned int data_deciphered_sz = 0;
1797         /* if we already have the data in ram... copy that */
1798
1799         if (efn->data)
1800            memcpy(data, efn->data, efn->size);
1801         else
1802         if (!read_data_from_disk(ef, efn, data, size))
1803            goto on_error;
1804
1805         if (efn->ciphered && cipher_key)
1806           {
1807              if (eet_decipher(data, size, cipher_key, strlen(cipher_key),
1808                               &data_deciphered, &data_deciphered_sz))
1809                {
1810                   if (data_deciphered)
1811                      free(data_deciphered);
1812
1813                   goto on_error;
1814                }
1815
1816              free(data);
1817              data = data_deciphered;
1818              size = data_deciphered_sz;
1819           }
1820      }
1821    /* compressed data */
1822    else
1823      {
1824         void *tmp_data;
1825         void *data_deciphered = NULL;
1826         unsigned int data_deciphered_sz = 0;
1827         int free_tmp = 0;
1828         int compr_size = efn->size;
1829         uLongf dlen;
1830
1831         /* if we already have the data in ram... copy that */
1832         if (efn->data)
1833            tmp_data = efn->data;
1834         else
1835           {
1836              tmp_data = malloc(compr_size);
1837              if (!tmp_data)
1838                 goto on_error;
1839
1840              free_tmp = 1;
1841
1842              if (!read_data_from_disk(ef, efn, tmp_data, compr_size))
1843                {
1844                   free(tmp_data);
1845                   goto on_error;
1846                }
1847           }
1848
1849         if (efn->ciphered && cipher_key)
1850           {
1851              if (eet_decipher(tmp_data, compr_size, cipher_key,
1852                               strlen(cipher_key), &data_deciphered,
1853                               &data_deciphered_sz))
1854                {
1855                   if (free_tmp)
1856                      free(tmp_data);
1857
1858                   if (data_deciphered)
1859                      free(data_deciphered);
1860
1861                   goto on_error;
1862                }
1863
1864              free(tmp_data);
1865              tmp_data = data_deciphered;
1866              compr_size = data_deciphered_sz;
1867           }
1868
1869         /* decompress it */
1870         dlen = size;
1871         if (uncompress((Bytef *)data, &dlen,
1872                        tmp_data, (uLongf)compr_size))
1873            goto on_error;
1874
1875         if (free_tmp)
1876            free(tmp_data);
1877      }
1878
1879    UNLOCK_FILE(ef);
1880
1881    /* handle alias */
1882    if (efn->alias)
1883      {
1884         void *tmp;
1885
1886         if (data[size - 1] != '\0')
1887            goto on_error;
1888
1889         tmp = eet_read_cipher(ef, data, size_ret, cipher_key);
1890
1891         free(data);
1892
1893         data = tmp;
1894      }
1895    else
1896    /* fill in return values */
1897    if (size_ret)
1898       *size_ret = size;
1899
1900    return data;
1901
1902 on_error:
1903    UNLOCK_FILE(ef);
1904    free(data);
1905    return NULL;
1906 } /* eet_read_cipher */
1907
1908 EAPI void *
1909 eet_read(Eet_File   *ef,
1910          const char *name,
1911          int        *size_ret)
1912 {
1913    return eet_read_cipher(ef, name, size_ret, NULL);
1914 } /* eet_read */
1915
1916 EAPI const void *
1917 eet_read_direct(Eet_File   *ef,
1918                 const char *name,
1919                 int        *size_ret)
1920 {
1921    Eet_File_Node *efn;
1922    const char *data = NULL;
1923    int size = 0;
1924
1925    if (size_ret)
1926       *size_ret = 0;
1927
1928    /* check to see its' an eet file pointer */
1929    if (eet_check_pointer(ef))
1930       return NULL;
1931
1932    if (!name)
1933       return NULL;
1934
1935    if ((ef->mode != EET_FILE_MODE_READ) &&
1936        (ef->mode != EET_FILE_MODE_READ_WRITE))
1937       return NULL;
1938
1939    /* no header, return NULL */
1940    if (eet_check_header(ef))
1941       return NULL;
1942
1943    LOCK_FILE(ef);
1944
1945    /* hunt hash bucket */
1946    efn = find_node_by_name(ef, name);
1947    if (!efn)
1948       goto on_error;
1949
1950    if (efn->offset < 0 && !efn->data)
1951       goto on_error;
1952
1953    /* get size (uncompressed, if compressed at all) */
1954    size = efn->data_size;
1955
1956    if (efn->alias)
1957      {
1958         data = efn->data ? efn->data : ef->data + efn->offset;
1959
1960         /* handle alias case */
1961         if (efn->compression)
1962           {
1963              char *tmp;
1964              int compr_size = efn->size;
1965              uLongf dlen;
1966
1967              tmp = alloca(sizeof (compr_size));
1968              dlen = size;
1969
1970              if (uncompress((Bytef *)tmp, &dlen, (Bytef *)data,
1971                             (uLongf)compr_size))
1972                 goto on_error;
1973
1974              if (tmp[compr_size - 1] != '\0')
1975                 goto on_error;
1976
1977              UNLOCK_FILE(ef);
1978
1979              return eet_read_direct(ef, tmp, size_ret);
1980           }
1981
1982         if (!data)
1983            goto on_error;
1984
1985         if (data[size - 1] != '\0')
1986            goto on_error;
1987
1988         UNLOCK_FILE(ef);
1989
1990         return eet_read_direct(ef, data, size_ret);
1991      }
1992    else
1993    /* uncompressed data */
1994    if (efn->compression == 0
1995        && efn->ciphered == 0)
1996       data = efn->data ? efn->data : ef->data + efn->offset;  /* compressed data */
1997    else
1998       data = NULL;
1999
2000    /* fill in return values */
2001    if (size_ret)
2002       *size_ret = size;
2003
2004    UNLOCK_FILE(ef);
2005
2006    return data;
2007
2008 on_error:
2009    UNLOCK_FILE(ef);
2010    return NULL;
2011 } /* eet_read_direct */
2012
2013 EAPI Eina_Bool
2014 eet_alias(Eet_File   *ef,
2015           const char *name,
2016           const char *destination,
2017           int         comp)
2018 {
2019    Eet_File_Node *efn;
2020    void *data2;
2021    Eina_Bool exists_already = EINA_FALSE;
2022    int data_size;
2023    int hash;
2024
2025    /* check to see its' an eet file pointer */
2026    if (eet_check_pointer(ef))
2027       return EINA_FALSE;
2028
2029    if ((!name) || (!destination))
2030       return EINA_FALSE;
2031
2032    if ((ef->mode != EET_FILE_MODE_WRITE) &&
2033        (ef->mode != EET_FILE_MODE_READ_WRITE))
2034       return EINA_FALSE;
2035
2036    LOCK_FILE(ef);
2037
2038    if (!ef->header)
2039      {
2040         /* allocate header */
2041         ef->header = calloc(1, sizeof(Eet_File_Header));
2042         if (!ef->header)
2043            goto on_error;
2044
2045         ef->header->magic = EET_MAGIC_FILE_HEADER;
2046         /* allocate directory block in ram */
2047         ef->header->directory = calloc(1, sizeof(Eet_File_Directory));
2048         if (!ef->header->directory)
2049           {
2050              free(ef->header);
2051              ef->header = NULL;
2052              goto on_error;
2053           }
2054
2055         /* 8 bit hash table (256 buckets) */
2056         ef->header->directory->size = 8;
2057         /* allocate base hash table */
2058         ef->header->directory->nodes =
2059            calloc(1, sizeof(Eet_File_Node *) *
2060                   (1 << ef->header->directory->size));
2061         if (!ef->header->directory->nodes)
2062           {
2063              free(ef->header->directory);
2064              ef->header = NULL;
2065              goto on_error;
2066           }
2067      }
2068
2069    /* figure hash bucket */
2070    hash = _eet_hash_gen(name, ef->header->directory->size);
2071
2072    data_size = comp ?
2073       12 + (((strlen(destination) + 1) * 101) / 100)
2074       : strlen(destination) + 1;
2075
2076    data2 = malloc(data_size);
2077    if (!data2)
2078       goto on_error;
2079
2080    /* if we want to compress */
2081    if (comp)
2082      {
2083         uLongf buflen;
2084
2085         /* compress the data with max compression */
2086         buflen = (uLongf)data_size;
2087         if (compress2((Bytef *)data2, &buflen, (Bytef *)destination,
2088                       (uLong)strlen(destination) + 1,
2089                       Z_BEST_COMPRESSION) != Z_OK)
2090           {
2091              free(data2);
2092              goto on_error;
2093           }
2094
2095         /* record compressed chunk size */
2096         data_size = (int)buflen;
2097         if (data_size < 0 || data_size >= (int)(strlen(destination) + 1))
2098           {
2099              comp = 0;
2100              data_size = strlen(destination) + 1;
2101           }
2102         else
2103           {
2104              void *data3;
2105
2106              data3 = realloc(data2, data_size);
2107              if (data3)
2108                 data2 = data3;
2109           }
2110      }
2111
2112    if (!comp)
2113       memcpy(data2, destination, data_size);
2114
2115    /* Does this node already exist? */
2116    for (efn = ef->header->directory->nodes[hash]; efn; efn = efn->next)
2117      {
2118         /* if it matches */
2119         if ((efn->name) && (eet_string_match(efn->name, name)))
2120           {
2121              free(efn->data);
2122              efn->alias = 1;
2123              efn->ciphered = 0;
2124              efn->compression = !!comp;
2125              efn->size = data_size;
2126              efn->data_size = strlen(destination) + 1;
2127              efn->data = data2;
2128              efn->offset = -1;
2129              exists_already = EINA_TRUE;
2130              break;
2131           }
2132      }
2133    if (!exists_already)
2134      {
2135         efn = malloc(sizeof(Eet_File_Node));
2136         if (!efn)
2137           {
2138              free(data2);
2139              goto on_error;
2140           }
2141
2142         efn->name = strdup(name);
2143         efn->name_size = strlen(efn->name) + 1;
2144         efn->free_name = 1;
2145
2146         efn->next = ef->header->directory->nodes[hash];
2147         ef->header->directory->nodes[hash] = efn;
2148         efn->offset = -1;
2149         efn->alias = 1;
2150         efn->ciphered = 0;
2151         efn->compression = !!comp;
2152         efn->size = data_size;
2153         efn->data_size = strlen(destination) + 1;
2154         efn->data = data2;
2155      }
2156
2157    /* flags that writes are pending */
2158    ef->writes_pending = 1;
2159
2160    UNLOCK_FILE(ef);
2161    return EINA_TRUE;
2162
2163 on_error:
2164    UNLOCK_FILE(ef);
2165    return EINA_FALSE;
2166 } /* eet_alias */
2167
2168 EAPI int
2169 eet_write_cipher(Eet_File   *ef,
2170                  const char *name,
2171                  const void *data,
2172                  int         size,
2173                  int         comp,
2174                  const char *cipher_key)
2175 {
2176    Eet_File_Node *efn;
2177    void *data2 = NULL;
2178    int exists_already = 0;
2179    int data_size;
2180    int hash;
2181
2182    /* check to see its' an eet file pointer */
2183    if (eet_check_pointer(ef))
2184       return 0;
2185
2186    if ((!name) || (!data) || (size <= 0))
2187       return 0;
2188
2189    if ((ef->mode != EET_FILE_MODE_WRITE) &&
2190        (ef->mode != EET_FILE_MODE_READ_WRITE))
2191       return 0;
2192
2193    LOCK_FILE(ef);
2194
2195    if (!ef->header)
2196      {
2197         /* allocate header */
2198         ef->header = calloc(1, sizeof(Eet_File_Header));
2199         if (!ef->header)
2200            goto on_error;
2201
2202         ef->header->magic = EET_MAGIC_FILE_HEADER;
2203         /* allocate directory block in ram */
2204         ef->header->directory = calloc(1, sizeof(Eet_File_Directory));
2205         if (!ef->header->directory)
2206           {
2207              free(ef->header);
2208              ef->header = NULL;
2209              goto on_error;
2210           }
2211
2212         /* 8 bit hash table (256 buckets) */
2213         ef->header->directory->size = 8;
2214         /* allocate base hash table */
2215         ef->header->directory->nodes =
2216            calloc(1, sizeof(Eet_File_Node *) *
2217                   (1 << ef->header->directory->size));
2218         if (!ef->header->directory->nodes)
2219           {
2220              free(ef->header->directory);
2221              ef->header = NULL;
2222              goto on_error;
2223           }
2224      }
2225
2226    /* figure hash bucket */
2227    hash = _eet_hash_gen(name, ef->header->directory->size);
2228
2229    data_size = comp ? 12 + ((size * 101) / 100) : size;
2230
2231    if (comp || !cipher_key)
2232      {
2233         data2 = malloc(data_size);
2234         if (!data2)
2235            goto on_error;
2236      }
2237
2238    /* if we want to compress */
2239    if (comp)
2240      {
2241         uLongf buflen;
2242
2243         /* compress the data with max compression */
2244         buflen = (uLongf)data_size;
2245         if (compress2((Bytef *)data2, &buflen, (Bytef *)data,
2246                       (uLong)size, Z_BEST_COMPRESSION) != Z_OK)
2247           {
2248              free(data2);
2249              goto on_error;
2250           }
2251
2252         /* record compressed chunk size */
2253         data_size = (int)buflen;
2254         if (data_size < 0 || data_size >= size)
2255           {
2256              comp = 0;
2257              data_size = size;
2258           }
2259         else
2260           {
2261              void *data3;
2262
2263              data3 = realloc(data2, data_size);
2264              if (data3)
2265                 data2 = data3;
2266           }
2267      }
2268
2269    if (cipher_key)
2270      {
2271         void *data_ciphered = NULL;
2272         unsigned int data_ciphered_sz = 0;
2273         const void *tmp;
2274
2275         tmp = comp ? data2 : data;
2276         if (!eet_cipher(tmp, data_size, cipher_key, strlen(cipher_key),
2277                         &data_ciphered, &data_ciphered_sz))
2278           {
2279              if (data2)
2280                 free(data2);
2281
2282              data2 = data_ciphered;
2283              data_size = data_ciphered_sz;
2284              size = (data_size > size) ? data_size : size;
2285           }
2286         else
2287           {
2288              if (data_ciphered)
2289                 free(data_ciphered);
2290
2291              cipher_key = NULL;
2292           }
2293      }
2294    else
2295      if (!comp)
2296        memcpy(data2, data, size);
2297
2298    /* Does this node already exist? */
2299    for (efn = ef->header->directory->nodes[hash]; efn; efn = efn->next)
2300      {
2301         /* if it matches */
2302         if ((efn->name) && (eet_string_match(efn->name, name)))
2303           {
2304              free(efn->data);
2305              efn->alias = 0;
2306              efn->ciphered = cipher_key ? 1 : 0;
2307              efn->compression = !!comp;
2308              efn->size = data_size;
2309              efn->data_size = size;
2310              efn->data = data2;
2311              efn->offset = -1;
2312              exists_already = 1;
2313              break;
2314           }
2315      }
2316    if (!exists_already)
2317      {
2318         efn = malloc(sizeof(Eet_File_Node));
2319         if (!efn)
2320           {
2321              free(data2);
2322              goto on_error;
2323           }
2324
2325         efn->name = strdup(name);
2326         efn->name_size = strlen(efn->name) + 1;
2327         efn->free_name = 1;
2328
2329         efn->next = ef->header->directory->nodes[hash];
2330         ef->header->directory->nodes[hash] = efn;
2331         efn->offset = -1;
2332         efn->alias = 0;
2333         efn->ciphered = cipher_key ? 1 : 0;
2334         efn->compression = !!comp;
2335         efn->size = data_size;
2336         efn->data_size = size;
2337         efn->data = data2;
2338      }
2339
2340    /* flags that writes are pending */
2341    ef->writes_pending = 1;
2342    UNLOCK_FILE(ef);
2343    return data_size;
2344
2345 on_error:
2346    UNLOCK_FILE(ef);
2347    return 0;
2348 } /* eet_write_cipher */
2349
2350 EAPI int
2351 eet_write(Eet_File   *ef,
2352           const char *name,
2353           const void *data,
2354           int         size,
2355           int         comp)
2356 {
2357    return eet_write_cipher(ef, name, data, size, comp, NULL);
2358 } /* eet_write */
2359
2360 EAPI int
2361 eet_delete(Eet_File   *ef,
2362            const char *name)
2363 {
2364    Eet_File_Node *efn;
2365    Eet_File_Node *pefn;
2366    int hash;
2367    int exists_already = 0;
2368
2369    /* check to see its' an eet file pointer */
2370    if (eet_check_pointer(ef))
2371       return 0;
2372
2373    if (!name)
2374       return 0;
2375
2376    /* deleting keys is only possible in RW or WRITE mode */
2377    if (ef->mode == EET_FILE_MODE_READ)
2378       return 0;
2379
2380    if (eet_check_header(ef))
2381       return 0;
2382
2383    LOCK_FILE(ef);
2384
2385    /* figure hash bucket */
2386    hash = _eet_hash_gen(name, ef->header->directory->size);
2387
2388    /* Does this node already exist? */
2389    for (pefn = NULL, efn = ef->header->directory->nodes[hash];
2390         efn;
2391         pefn = efn, efn = efn->next)
2392      {
2393         /* if it matches */
2394         if (eet_string_match(efn->name, name))
2395           {
2396              if (efn->data)
2397                 free(efn->data);
2398
2399              if (!pefn)
2400                 ef->header->directory->nodes[hash] = efn->next;
2401              else
2402                 pefn->next = efn->next;
2403
2404              if (efn->free_name)
2405                 free(efn->name);
2406
2407              free(efn);
2408              exists_already = 1;
2409              break;
2410           }
2411      }
2412    /* flags that writes are pending */
2413    if (exists_already)
2414       ef->writes_pending = 1;
2415
2416    UNLOCK_FILE(ef);
2417
2418    /* update access time */
2419    return exists_already;
2420 } /* eet_delete */
2421
2422 EAPI Eet_Dictionary *
2423 eet_dictionary_get(Eet_File *ef)
2424 {
2425    if (eet_check_pointer(ef))
2426       return NULL;
2427
2428    return ef->ed;
2429 } /* eet_dictionary_get */
2430
2431 EAPI char **
2432 eet_list(Eet_File   *ef,
2433          const char *glob,
2434          int        *count_ret)
2435 {
2436    Eet_File_Node *efn;
2437    char **list_ret = NULL;
2438    int list_count = 0;
2439    int list_count_alloc = 0;
2440    int i, num;
2441
2442    /* check to see its' an eet file pointer */
2443    if (eet_check_pointer(ef) || eet_check_header(ef) ||
2444        (!glob) ||
2445        ((ef->mode != EET_FILE_MODE_READ) &&
2446         (ef->mode != EET_FILE_MODE_READ_WRITE)))
2447      {
2448         if (count_ret)
2449            *count_ret = 0;
2450
2451         return NULL;
2452      }
2453
2454    if (!strcmp(glob, "*"))
2455       glob = NULL;
2456
2457    LOCK_FILE(ef);
2458
2459    /* loop through all entries */
2460    num = (1 << ef->header->directory->size);
2461    for (i = 0; i < num; i++)
2462      {
2463         for (efn = ef->header->directory->nodes[i]; efn; efn = efn->next)
2464           {
2465              /* if the entry matches the input glob
2466               * check for * explicitly, because on some systems, * isn't well
2467               * supported
2468               */
2469              if ((!glob) || !fnmatch(glob, efn->name, 0))
2470                {
2471                   /* add it to our list */
2472                   list_count++;
2473
2474                   /* only realloc in 32 entry chunks */
2475                   if (list_count > list_count_alloc)
2476                     {
2477                        char **new_list = NULL;
2478
2479                        list_count_alloc += 64;
2480                        new_list =
2481                           realloc(list_ret, list_count_alloc * (sizeof(char *)));
2482                        if (!new_list)
2483                          {
2484                             free(list_ret);
2485
2486                             goto on_error;
2487                          }
2488
2489                        list_ret = new_list;
2490                     }
2491
2492                   /* put pointer of name string in */
2493                   list_ret[list_count - 1] = efn->name;
2494                }
2495           }
2496      }
2497
2498    UNLOCK_FILE(ef);
2499
2500    /* return count and list */
2501    if (count_ret)
2502       *count_ret = list_count;
2503
2504    return list_ret;
2505
2506 on_error:
2507    UNLOCK_FILE(ef);
2508
2509    if (count_ret)
2510       *count_ret = 0;
2511
2512    return NULL;
2513 } /* eet_list */
2514
2515 EAPI int
2516 eet_num_entries(Eet_File *ef)
2517 {
2518    int i, num, ret = 0;
2519    Eet_File_Node *efn;
2520
2521    /* check to see its' an eet file pointer */
2522    if (eet_check_pointer(ef) || eet_check_header(ef) ||
2523        ((ef->mode != EET_FILE_MODE_READ) &&
2524         (ef->mode != EET_FILE_MODE_READ_WRITE)))
2525       return -1;
2526
2527    LOCK_FILE(ef);
2528
2529    /* loop through all entries */
2530    num = (1 << ef->header->directory->size);
2531    for (i = 0; i < num; i++)
2532      {
2533         for (efn = ef->header->directory->nodes[i]; efn; efn = efn->next)
2534            ret++;
2535      }
2536
2537    UNLOCK_FILE(ef);
2538
2539    return ret;
2540 } /* eet_num_entries */
2541
2542 static Eet_File_Node *
2543 find_node_by_name(Eet_File   *ef,
2544                   const char *name)
2545 {
2546    Eet_File_Node *efn;
2547    int hash;
2548
2549    /* get hash bucket this should be in */
2550    hash = _eet_hash_gen(name, ef->header->directory->size);
2551
2552    for (efn = ef->header->directory->nodes[hash]; efn; efn = efn->next)
2553      {
2554         if (eet_string_match(efn->name, name))
2555            return efn;
2556      }
2557
2558    return NULL;
2559 } /* find_node_by_name */
2560
2561 static int
2562 read_data_from_disk(Eet_File      *ef,
2563                     Eet_File_Node *efn,
2564                     void          *buf,
2565                     int            len)
2566 {
2567    if (efn->offset < 0)
2568       return 0;
2569
2570    if (ef->data)
2571      {
2572         if ((efn->offset + len) > ef->data_size)
2573            return 0;
2574
2575         memcpy(buf, ef->data + efn->offset, len);
2576      }
2577    else
2578      {
2579         if (!ef->readfp)
2580            return 0;
2581
2582         /* seek to data location */
2583         if (fseek(ef->readfp, efn->offset, SEEK_SET) < 0)
2584            return 0;
2585
2586         /* read it */
2587         len = fread(buf, len, 1, ef->readfp);
2588      }
2589
2590    return len;
2591 } /* read_data_from_disk */
2592