* eet: improve speed for Eet_String users.
[framework/uifw/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 (fwrite(ef->ed->all[j].str, ef->ed->all[j].len, 1, fp) != 1)
640              goto write_error;
641         }
642
643    /* write data */
644    for (i = 0; i < num; i++)
645      {
646         for (efn = ef->header->directory->nodes[i]; efn; efn = efn->next)
647           {
648              if (fwrite(efn->data, efn->size, 1, fp) != 1)
649                 goto write_error;
650           }
651      }
652
653    /* flush all write to the file. */
654    fflush(fp);
655 // this is going to really cause trouble. if ANYTHING this needs to go into a
656 // thread spawned off - but even then...
657 // in this case... ext4 is "wrong". (yes we can jump up and down and point posix
658 // manual pages at eachother, but ext4 broke behavior that has been in place
659 // for decades and that 1000's of apps rely on daily - that is that one operation
660 // to disk is committed to disk BEFORE following operations, so the fs retains
661 // a consistent state
662 //   fsync(fileno(fp));
663
664    /* append signature if required */
665    if (ef->key)
666      {
667         error = eet_identity_sign(fp, ef->key);
668         if (error != EET_ERROR_NONE)
669            goto sign_error;
670      }
671
672    /* no more writes pending */
673    ef->writes_pending = 0;
674
675    fclose(fp);
676
677    return EET_ERROR_NONE;
678
679 write_error:
680    if (ferror(fp))
681      {
682         switch (errno)
683           {
684            case EFBIG: error = EET_ERROR_WRITE_ERROR_FILE_TOO_BIG; break;
685
686            case EIO: error = EET_ERROR_WRITE_ERROR_IO_ERROR; break;
687
688            case ENOSPC: error = EET_ERROR_WRITE_ERROR_OUT_OF_SPACE; break;
689
690            case EPIPE: error = EET_ERROR_WRITE_ERROR_FILE_CLOSED; break;
691
692            default: error = EET_ERROR_WRITE_ERROR; break;
693           } /* switch */
694      }
695
696 sign_error:
697    fclose(fp);
698    return error;
699 } /* eet_flush2 */
700
701 EAPI int
702 eet_init(void)
703 {
704    if (++eet_init_count != 1)
705       return eet_init_count;
706
707    if (!eina_init())
708      {
709         fprintf(stderr, "Eet: Eina init failed");
710         return --eet_init_count;
711      }
712
713    _eet_log_dom_global = eina_log_domain_register("eet", EET_DEFAULT_LOG_COLOR);
714    if (_eet_log_dom_global < 0)
715      {
716         EINA_LOG_ERR("Eet Can not create a general log domain.");
717         goto shutdown_eina;
718      }
719
720    if (!eet_node_init())
721      {
722         EINA_LOG_ERR("Eet: Eet_Node mempool creation failed");
723         goto unregister_log_domain;
724      }
725
726 #ifdef HAVE_GNUTLS
727    /* Before the library can be used, it must initialize itself if needed. */
728    if (gcry_control(GCRYCTL_ANY_INITIALIZATION_P) == 0)
729      {
730         gcry_check_version(NULL);
731         /* Disable warning messages about problems with the secure memory subsystem.
732            This command should be run right after gcry_check_version. */
733         if (gcry_control(GCRYCTL_DISABLE_SECMEM_WARN))
734            goto shutdown_eet;  /* This command is used to allocate a pool of secure memory and thus
735                                   enabling the use of secure memory. It also drops all extra privileges the
736                                   process has (i.e. if it is run as setuid (root)). If the argument nbytes
737                                   is 0, secure memory will be disabled. The minimum amount of secure memory
738                                   allocated is currently 16384 bytes; you may thus use a value of 1 to
739                                   request that default size. */
740
741         if (gcry_control(GCRYCTL_INIT_SECMEM, 16384, 0))
742            WRN(
743               "BIG FAT WARNING: I AM UNABLE TO REQUEST SECMEM, Cryptographic operation are at risk !");
744      }
745
746 # ifdef EFL_HAVE_POSIX_THREADS
747    if (gcry_control(GCRYCTL_SET_THREAD_CBS, &gcry_threads_pthread))
748       WRN(
749          "YOU ARE USING PTHREADS, BUT I CANNOT INITIALIZE THREADSAFE GCRYPT OPERATIONS!");
750
751 # endif /* ifdef EFL_HAVE_POSIX_THREADS */
752    if (gnutls_global_init())
753       goto shutdown_eet;
754
755 #endif /* ifdef HAVE_GNUTLS */
756 #ifdef HAVE_OPENSSL
757    ERR_load_crypto_strings();
758    OpenSSL_add_all_algorithms();
759 #endif /* ifdef HAVE_OPENSSL */
760
761    return eet_init_count;
762
763 #ifdef HAVE_GNUTLS
764 shutdown_eet:
765 #endif   
766    eet_node_shutdown();
767 unregister_log_domain:
768    eina_log_domain_unregister(_eet_log_dom_global);
769    _eet_log_dom_global = -1;
770 shutdown_eina:
771    eina_shutdown();
772    return --eet_init_count;
773 } /* eet_init */
774
775 EAPI int
776 eet_shutdown(void)
777 {
778    if (--eet_init_count != 0)
779       return eet_init_count;
780
781    eet_clearcache();
782    eet_node_shutdown();
783 #ifdef HAVE_GNUTLS
784    gnutls_global_deinit();
785 #endif /* ifdef HAVE_GNUTLS */
786 #ifdef HAVE_OPENSSL
787    EVP_cleanup();
788    ERR_free_strings();
789 #endif /* ifdef HAVE_OPENSSL */
790    eina_log_domain_unregister(_eet_log_dom_global);
791    _eet_log_dom_global = -1;
792    eina_shutdown();
793
794    return eet_init_count;
795 } /* eet_shutdown */
796
797 EAPI Eet_Error
798 eet_sync(Eet_File *ef)
799 {
800    Eet_Error ret;
801
802    if (eet_check_pointer(ef))
803       return EET_ERROR_BAD_OBJECT;
804
805    if ((ef->mode != EET_FILE_MODE_WRITE) &&
806        (ef->mode != EET_FILE_MODE_READ_WRITE))
807       return EET_ERROR_NOT_WRITABLE;
808
809    if (!ef->writes_pending)
810       return EET_ERROR_NONE;
811
812    LOCK_FILE(ef);
813
814    ret = eet_flush2(ef);
815
816    UNLOCK_FILE(ef);
817    return ret;
818 } /* eet_sync */
819
820 EAPI void
821 eet_clearcache(void)
822 {
823    int num = 0;
824    int i;
825
826    /*
827     * We need to compute the list of eet file to close separately from the cache,
828     * due to eet_close removing them from the cache after each call.
829     */
830    LOCK_CACHE;
831    for (i = 0; i < eet_writers_num; i++)
832      {
833         if (eet_writers[i]->references <= 0)
834            num++;
835      }
836
837    for (i = 0; i < eet_readers_num; i++)
838      {
839         if (eet_readers[i]->references <= 0)
840            num++;
841      }
842
843    if (num > 0)
844      {
845         Eet_File **closelist = NULL;
846
847         closelist = alloca(num * sizeof(Eet_File *));
848         num = 0;
849         for (i = 0; i < eet_writers_num; i++)
850           {
851              if (eet_writers[i]->references <= 0)
852                {
853                   closelist[num] = eet_writers[i];
854                   eet_writers[i]->delete_me_now = 1;
855                   num++;
856                }
857           }
858
859         for (i = 0; i < eet_readers_num; i++)
860           {
861              if (eet_readers[i]->references <= 0)
862                {
863                   closelist[num] = eet_readers[i];
864                   eet_readers[i]->delete_me_now = 1;
865                   num++;
866                }
867           }
868
869         for (i = 0; i < num; i++)
870           {
871              eet_internal_close(closelist[i], EINA_TRUE);
872           }
873      }
874
875    UNLOCK_CACHE;
876 } /* eet_clearcache */
877
878 /* FIXME: MMAP race condition in READ_WRITE_MODE */
879 static Eet_File *
880 eet_internal_read2(Eet_File *ef)
881 {
882    const int *data = (const int *)ef->data;
883    const char *start = (const char *)ef->data;
884    int idx = 0;
885    int num_directory_entries;
886    int bytes_directory_entries;
887    int num_dictionary_entries;
888    int bytes_dictionary_entries;
889    int signature_base_offset;
890    int i;
891
892    idx += sizeof(int);
893    if (eet_test_close((int)ntohl(*data) != EET_MAGIC_FILE2, ef))
894       return NULL;
895
896    data++;
897
898 #define GET_INT(Value, Pointer, Index)\
899    {\
900       Value = ntohl(*Pointer);\
901       Pointer++;\
902       Index += sizeof(int);\
903    }
904
905    /* get entries count and byte count */
906    GET_INT(num_directory_entries,  data, idx);
907    /* get dictionary count and byte count */
908    GET_INT(num_dictionary_entries, data, idx);
909
910    bytes_directory_entries = EET_FILE2_DIRECTORY_ENTRY_SIZE *
911       num_directory_entries + EET_FILE2_HEADER_SIZE;
912    bytes_dictionary_entries = EET_FILE2_DICTIONARY_ENTRY_SIZE *
913       num_dictionary_entries;
914
915    /* we can't have <= 0 values here - invalid */
916    if (eet_test_close((num_directory_entries <= 0), ef))
917       return NULL;
918
919    /* we can't have more bytes directory and bytes in dictionaries than the size of the file */
920    if (eet_test_close((bytes_directory_entries + bytes_dictionary_entries) >
921                       ef->data_size, ef))
922       return NULL;
923
924    /* allocate header */
925    ef->header = calloc(1, sizeof(Eet_File_Header));
926    if (eet_test_close(!ef->header, ef))
927       return NULL;
928
929    ef->header->magic = EET_MAGIC_FILE_HEADER;
930
931    /* allocate directory block in ram */
932    ef->header->directory = calloc(1, sizeof(Eet_File_Directory));
933    if (eet_test_close(!ef->header->directory, ef))
934       return NULL;
935
936    /* 8 bit hash table (256 buckets) */
937    ef->header->directory->size = 8;
938    /* allocate base hash table */
939    ef->header->directory->nodes =
940       calloc(1, sizeof(Eet_File_Node *) * (1 << ef->header->directory->size));
941    if (eet_test_close(!ef->header->directory->nodes, ef))
942       return NULL;
943
944    signature_base_offset = 0;
945
946    /* actually read the directory block - all of it, into ram */
947    for (i = 0; i < num_directory_entries; ++i)
948      {
949         const char *name;
950         Eet_File_Node *efn;
951         int name_offset;
952         int name_size;
953         int hash;
954         int flag;
955
956         /* out directory block is inconsistent - we have oveerun our */
957         /* dynamic block buffer before we finished scanning dir entries */
958         efn = malloc(sizeof(Eet_File_Node));
959         if (eet_test_close(!efn, ef))
960           {
961              if (efn) free(efn); /* yes i know - we only get here if 
962                                   * efn is null/0 -> trying to shut up
963                                   * warning tools like cppcheck */
964              return NULL;
965           }
966
967         /* get entrie header */
968         GET_INT(efn->offset,    data, idx);
969         GET_INT(efn->size,      data, idx);
970         GET_INT(efn->data_size, data, idx);
971         GET_INT(name_offset,    data, idx);
972         GET_INT(name_size,      data, idx);
973         GET_INT(flag,           data, idx);
974
975         efn->compression = flag & 0x1 ? 1 : 0;
976         efn->ciphered = flag & 0x2 ? 1 : 0;
977         efn->alias = flag & 0x4 ? 1 : 0;
978
979 #define EFN_TEST(Test, Ef, Efn)\
980    if (eet_test_close(Test, Ef))\
981      {\
982         free(Efn);\
983         return NULL;\
984      }
985
986         /* check data pointer position */
987         EFN_TEST(!((efn->size > 0)
988                    && (efn->offset + efn->size <= ef->data_size)
989                    && (efn->offset > bytes_dictionary_entries +
990                        bytes_directory_entries)), ef, efn);
991
992         /* check name position */
993         EFN_TEST(!((name_size > 0)
994                    && (name_offset + name_size < ef->data_size)
995                    && (name_offset >= bytes_dictionary_entries +
996                        bytes_directory_entries)), ef, efn);
997
998         name = start + name_offset;
999
1000         /* check '\0' at the end of name string */
1001         EFN_TEST(name[name_size - 1] != '\0', ef, efn);
1002
1003         efn->free_name = 0;
1004         efn->name = (char *)name;
1005         efn->name_size = name_size;
1006
1007         hash = _eet_hash_gen(efn->name, ef->header->directory->size);
1008         efn->next = ef->header->directory->nodes[hash];
1009         ef->header->directory->nodes[hash] = efn;
1010
1011         /* read-only mode, so currently we have no data loaded */
1012         if (ef->mode == EET_FILE_MODE_READ)
1013            efn->data = NULL;  /* read-write mode - read everything into ram */
1014         else
1015           {
1016              efn->data = malloc(efn->size);
1017              if (efn->data)
1018                 memcpy(efn->data, ef->data + efn->offset, efn->size);
1019           }
1020
1021         /* compute the possible position of a signature */
1022         if (signature_base_offset < efn->offset + efn->size)
1023            signature_base_offset = efn->offset + efn->size;
1024      }
1025
1026    ef->ed = NULL;
1027
1028    if (num_dictionary_entries)
1029      {
1030         const int *dico = (const int *)ef->data +
1031            EET_FILE2_DIRECTORY_ENTRY_COUNT * num_directory_entries +
1032            EET_FILE2_HEADER_COUNT;
1033         int j;
1034
1035         if (eet_test_close((num_dictionary_entries *
1036                             (int)EET_FILE2_DICTIONARY_ENTRY_SIZE + idx) >
1037                            (bytes_dictionary_entries + bytes_directory_entries),
1038                            ef))
1039            return NULL;
1040
1041         ef->ed = calloc(1, sizeof (Eet_Dictionary));
1042         if (eet_test_close(!ef->ed, ef))
1043            return NULL;
1044
1045         ef->ed->all = calloc(num_dictionary_entries, sizeof (Eet_String));
1046         if (eet_test_close(!ef->ed->all, ef))
1047            return NULL;
1048
1049         ef->ed->count = num_dictionary_entries;
1050         ef->ed->total = num_dictionary_entries;
1051         ef->ed->start = start + bytes_dictionary_entries +
1052            bytes_directory_entries;
1053         ef->ed->end = ef->ed->start;
1054
1055         for (j = 0; j < ef->ed->count; ++j)
1056           {
1057              int hash;
1058              int offset;
1059
1060              GET_INT(hash,                dico, idx);
1061              GET_INT(offset,              dico, idx);
1062              GET_INT(ef->ed->all[j].len,  dico, idx);
1063              GET_INT(ef->ed->all[j].prev, dico, idx);
1064              GET_INT(ef->ed->all[j].next, dico, idx);
1065
1066              /* Hash value could be stored on 8bits data, but this will break alignment of all the others data.
1067                 So stick to int and check the value. */
1068              if (eet_test_close(hash & 0xFFFFFF00, ef))
1069                 return NULL;
1070
1071              /* Check string position */
1072              if (eet_test_close(!((ef->ed->all[j].len > 0)
1073                                   && (offset >
1074                                       (bytes_dictionary_entries +
1075                                        bytes_directory_entries))
1076                                   && (offset + ef->ed->all[j].len <
1077                                       ef->data_size)), ef))
1078                 return NULL;
1079
1080              ef->ed->all[j].str = start + offset;
1081
1082              if (ef->ed->all[j].str + ef->ed->all[j].len > ef->ed->end)
1083                 ef->ed->end = ef->ed->all[j].str + ef->ed->all[j].len;
1084
1085              /* Check '\0' at the end of the string */
1086              if (eet_test_close(ef->ed->all[j].str[ef->ed->all[j].len - 1] !=
1087                                 '\0', ef))
1088                 return NULL;
1089
1090              ef->ed->all[j].hash = hash;
1091              if (ef->ed->all[j].prev == -1)
1092                 ef->ed->hash[hash] = j;
1093
1094              /* compute the possible position of a signature */
1095              if (signature_base_offset < offset + ef->ed->all[j].len)
1096                 signature_base_offset = offset + ef->ed->all[j].len;
1097           }
1098      }
1099
1100    /* Check if the file is signed */
1101    ef->x509_der = NULL;
1102    ef->x509_length = 0;
1103    ef->signature = NULL;
1104    ef->signature_length = 0;
1105
1106    if (signature_base_offset < ef->data_size)
1107      {
1108 #ifdef HAVE_SIGNATURE
1109         const unsigned char *buffer = ((const unsigned char *)ef->data) +
1110            signature_base_offset;
1111         ef->x509_der = eet_identity_check(ef->data,
1112                                           signature_base_offset,
1113                                           &ef->sha1,
1114                                           &ef->sha1_length,
1115                                           buffer,
1116                                           ef->data_size - signature_base_offset,
1117                                           &ef->signature,
1118                                           &ef->signature_length,
1119                                           &ef->x509_length);
1120
1121         if (eet_test_close(!ef->x509_der, ef))
1122            return NULL;
1123
1124 #else /* ifdef HAVE_SIGNATURE */
1125         ERR(
1126            "This file could be signed but you didn't compile the necessary code to check the signature.");
1127 #endif /* ifdef HAVE_SIGNATURE */
1128      }
1129
1130    return ef;
1131 } /* eet_internal_read2 */
1132
1133 #if EET_OLD_EET_FILE_FORMAT
1134 static Eet_File *
1135 eet_internal_read1(Eet_File *ef)
1136 {
1137    const unsigned char *dyn_buf = NULL;
1138    const unsigned char *p = NULL;
1139    int idx = 0;
1140    int num_entries;
1141    int byte_entries;
1142    int i;
1143
1144    WRN(
1145       "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.",
1146       ef->path);
1147
1148    /* build header table if read mode */
1149    /* geat header */
1150    idx += sizeof(int);
1151    if (eet_test_close((int)ntohl(*((int *)ef->data)) != EET_MAGIC_FILE, ef))
1152       return NULL;
1153
1154 #define EXTRACT_INT(Value, Pointer, Index)\
1155    {\
1156       int tmp;\
1157       memcpy(&tmp, Pointer + Index, sizeof(int));\
1158       Value = ntohl(tmp);\
1159       Index += sizeof(int);\
1160    }
1161
1162    /* get entries count and byte count */
1163    EXTRACT_INT(num_entries,  ef->data, idx);
1164    EXTRACT_INT(byte_entries, ef->data, idx);
1165
1166    /* we can't have <= 0 values here - invalid */
1167    if (eet_test_close((num_entries <= 0) || (byte_entries <= 0), ef))
1168       return NULL;
1169
1170    /* we can't have more entires than minimum bytes for those! invalid! */
1171    if (eet_test_close((num_entries * 20) > byte_entries, ef))
1172       return NULL;
1173
1174    /* check we will not outrun the file limit */
1175    if (eet_test_close(((byte_entries + (int)sizeof(int) * 3) > ef->data_size),
1176                       ef))
1177       return NULL;
1178
1179    /* allocate header */
1180    ef->header = calloc(1, sizeof(Eet_File_Header));
1181    if (eet_test_close(!ef->header, ef))
1182       return NULL;
1183
1184    ef->header->magic = EET_MAGIC_FILE_HEADER;
1185
1186    /* allocate directory block in ram */
1187    ef->header->directory = calloc(1, sizeof(Eet_File_Directory));
1188    if (eet_test_close(!ef->header->directory, ef))
1189       return NULL;
1190
1191    /* 8 bit hash table (256 buckets) */
1192    ef->header->directory->size = 8;
1193    /* allocate base hash table */
1194    ef->header->directory->nodes =
1195       calloc(1, sizeof(Eet_File_Node *) * (1 << ef->header->directory->size));
1196    if (eet_test_close(!ef->header->directory->nodes, ef))
1197       return NULL;
1198
1199    /* actually read the directory block - all of it, into ram */
1200    dyn_buf = ef->data + idx;
1201
1202    /* parse directory block */
1203    p = dyn_buf;
1204
1205    for (i = 0; i < num_entries; i++)
1206      {
1207         Eet_File_Node *efn;
1208         void *data = NULL;
1209         int indexn = 0;
1210         int name_size;
1211         int hash;
1212         int k;
1213
1214 #define HEADER_SIZE (sizeof(int) * 5)
1215
1216         /* out directory block is inconsistent - we have oveerun our */
1217         /* dynamic block buffer before we finished scanning dir entries */
1218         if (eet_test_close(p + HEADER_SIZE >= (dyn_buf + byte_entries), ef))
1219            return NULL;
1220
1221         /* allocate all the ram needed for this stored node accounting */
1222         efn = malloc (sizeof(Eet_File_Node));
1223         if (eet_test_close(!efn, ef))
1224           {
1225              if (efn) free(efn); /* yes i know - we only get here if 
1226                                   * efn is null/0 -> trying to shut up
1227                                   * warning tools like cppcheck */
1228              return NULL;
1229           }
1230
1231         /* get entrie header */
1232         EXTRACT_INT(efn->offset,      p, indexn);
1233         EXTRACT_INT(efn->compression, p, indexn);
1234         EXTRACT_INT(efn->size,        p, indexn);
1235         EXTRACT_INT(efn->data_size,   p, indexn);
1236         EXTRACT_INT(name_size,        p, indexn);
1237
1238         efn->name_size = name_size;
1239         efn->ciphered = 0;
1240         efn->alias = 0;
1241
1242         /* invalid size */
1243         if (eet_test_close(efn->size <= 0, ef))
1244           {
1245              free(efn);
1246              return NULL;
1247           }
1248
1249         /* invalid name_size */
1250         if (eet_test_close(name_size <= 0, ef))
1251           {
1252              free(efn);
1253              return NULL;
1254           }
1255
1256         /* reading name would mean falling off end of dyn_buf - invalid */
1257         if (eet_test_close((p + 16 + name_size) > (dyn_buf + byte_entries), ef))
1258           {
1259              free(efn);
1260              return NULL;
1261           }
1262
1263         /* This code is useless if we dont want backward compatibility */
1264         for (k = name_size;
1265              k > 0 && ((unsigned char)*(p + HEADER_SIZE + k)) != 0; --k)
1266            ;
1267
1268         efn->free_name = ((unsigned char)*(p + HEADER_SIZE + k)) != 0;
1269
1270         if (efn->free_name)
1271           {
1272              efn->name = malloc(sizeof(char) * name_size + 1);
1273              if (eet_test_close(!efn->name, ef))
1274                {
1275                   free(efn);
1276                   return NULL;
1277                }
1278
1279              strncpy(efn->name, (char *)p + HEADER_SIZE, name_size);
1280              efn->name[name_size] = 0;
1281
1282              WRN(
1283                 "File: %s is not up to date for key \"%s\" - needs rebuilding sometime",
1284                 ef->path,
1285                 efn->name);
1286           }
1287         else
1288            /* The only really useful peace of code for efn->name (no backward compatibility) */
1289            efn->name = (char *)((unsigned char *)(p + HEADER_SIZE));
1290
1291         /* get hash bucket it should go in */
1292         hash = _eet_hash_gen(efn->name, ef->header->directory->size);
1293         efn->next = ef->header->directory->nodes[hash];
1294         ef->header->directory->nodes[hash] = efn;
1295
1296         /* read-only mode, so currently we have no data loaded */
1297         if (ef->mode == EET_FILE_MODE_READ)
1298            efn->data = NULL;  /* read-write mode - read everything into ram */
1299         else
1300           {
1301              data = malloc(efn->size);
1302              if (data)
1303                 memcpy(data, ef->data + efn->offset, efn->size);
1304
1305              efn->data = data;
1306           }
1307
1308         /* advance */
1309         p += HEADER_SIZE + name_size;
1310      }
1311    return ef;
1312 } /* eet_internal_read1 */
1313
1314 #endif /* if EET_OLD_EET_FILE_FORMAT */
1315
1316 /*
1317  * this should only be called when the cache lock is already held
1318  * (We could drop this restriction if we add a parameter to eet_test_close
1319  * that indicates if the lock is held or not.  For now it is easiest
1320  * to just require that it is always held.)
1321  */
1322 static Eet_File *
1323 eet_internal_read(Eet_File *ef)
1324 {
1325    const int *data = (const int *)ef->data;
1326
1327    if (eet_test_close((ef->data == (void *)-1) || (!ef->data), ef))
1328       return NULL;
1329
1330    if (eet_test_close(ef->data_size < (int)sizeof(int) * 3, ef))
1331       return NULL;
1332
1333    switch (ntohl(*data))
1334      {
1335 #if EET_OLD_EET_FILE_FORMAT
1336       case EET_MAGIC_FILE:
1337          return eet_internal_read1(ef);
1338
1339 #endif /* if EET_OLD_EET_FILE_FORMAT */
1340       case EET_MAGIC_FILE2:
1341          return eet_internal_read2(ef);
1342
1343       default:
1344          ef->delete_me_now = 1;
1345          eet_internal_close(ef, EINA_TRUE);
1346          break;
1347      } /* switch */
1348
1349    return NULL;
1350 } /* eet_internal_read */
1351
1352 static Eet_Error
1353 eet_internal_close(Eet_File *ef,
1354                    Eina_Bool locked)
1355 {
1356    Eet_Error err;
1357
1358    /* check to see its' an eet file pointer */
1359    if (eet_check_pointer(ef))
1360       return EET_ERROR_BAD_OBJECT;
1361
1362    if (!locked)
1363       LOCK_CACHE;
1364
1365    /* deref */
1366    ef->references--;
1367    /* if its still referenced - dont go any further */
1368    if (ef->references > 0)
1369       goto on_error;  /* flush any writes */
1370
1371    err = eet_flush2(ef);
1372
1373    eet_identity_unref(ef->key);
1374    ef->key = NULL;
1375
1376    /* if not urgent to delete it - dont free it - leave it in cache */
1377    if ((!ef->delete_me_now) && (ef->mode == EET_FILE_MODE_READ))
1378       goto on_error;
1379
1380    /* remove from cache */
1381    if (ef->mode == EET_FILE_MODE_READ)
1382       eet_cache_del(ef, &eet_readers, &eet_readers_num, &eet_readers_alloc);
1383    else if ((ef->mode == EET_FILE_MODE_WRITE) ||
1384             (
1385                ef
1386                ->
1387                mode == EET_FILE_MODE_READ_WRITE))
1388       eet_cache_del(ef, &eet_writers, &eet_writers_num, &eet_writers_alloc);
1389
1390    /* we can unlock the cache now */
1391    if (!locked)
1392       UNLOCK_CACHE;
1393
1394    DESTROY_FILE(ef);
1395
1396    /* free up data */
1397    if (ef->header)
1398      {
1399         if (ef->header->directory)
1400           {
1401              if (ef->header->directory->nodes)
1402                {
1403                   int i, num;
1404
1405                   num = (1 << ef->header->directory->size);
1406                   for (i = 0; i < num; i++)
1407                     {
1408                        Eet_File_Node *efn;
1409
1410                        while ((efn = ef->header->directory->nodes[i]))
1411                          {
1412                             if (efn->data)
1413                                free(efn->data);
1414
1415                             ef->header->directory->nodes[i] = efn->next;
1416
1417                             if (efn->free_name)
1418                                free(efn->name);
1419
1420                             free(efn);
1421                          }
1422                     }
1423                   free(ef->header->directory->nodes);
1424                }
1425
1426              free(ef->header->directory);
1427           }
1428
1429         free(ef->header);
1430      }
1431
1432    eet_dictionary_free(ef->ed);
1433
1434    if (ef->sha1)
1435       free(ef->sha1);
1436
1437    if (ef->data)
1438       munmap((void *)ef->data, ef->data_size);
1439
1440    if (ef->readfp)
1441       fclose(ef->readfp);
1442
1443    /* zero out ram for struct - caution tactic against stale memory use */
1444    memset(ef, 0, sizeof(Eet_File));
1445
1446    /* free it */
1447    free(ef);
1448    return err;
1449
1450 on_error:
1451    if (!locked)
1452       UNLOCK_CACHE;
1453
1454    return EET_ERROR_NONE;
1455 } /* eet_internal_close */
1456
1457 EAPI Eet_File *
1458 eet_memopen_read(const void *data,
1459                  size_t      size)
1460 {
1461    Eet_File *ef;
1462
1463    if (!data || size == 0)
1464       return NULL;
1465
1466    ef = malloc (sizeof (Eet_File));
1467    if (!ef)
1468       return NULL;
1469
1470    INIT_FILE(ef);
1471    ef->ed = NULL;
1472    ef->path = NULL;
1473    ef->key = NULL;
1474    ef->magic = EET_MAGIC_FILE;
1475    ef->references = 1;
1476    ef->mode = EET_FILE_MODE_READ;
1477    ef->header = NULL;
1478    ef->mtime = 0;
1479    ef->delete_me_now = 1;
1480    ef->readfp = NULL;
1481    ef->data = data;
1482    ef->data_size = size;
1483    ef->sha1 = NULL;
1484    ef->sha1_length = 0;
1485
1486    /* eet_internal_read expects the cache lock to be held when it is called */
1487    LOCK_CACHE;
1488    ef = eet_internal_read(ef);
1489    UNLOCK_CACHE;
1490    return ef;
1491 } /* eet_memopen_read */
1492
1493 EAPI Eet_File *
1494 eet_open(const char   *file,
1495          Eet_File_Mode mode)
1496 {
1497    FILE *fp;
1498    Eet_File *ef;
1499    int file_len;
1500    struct stat file_stat;
1501
1502    if (!file)
1503       return NULL;
1504
1505    /* find the current file handle in cache*/
1506    ef = NULL;
1507    LOCK_CACHE;
1508    if (mode == EET_FILE_MODE_READ)
1509      {
1510         ef = eet_cache_find((char *)file, eet_writers, eet_writers_num);
1511         if (ef)
1512           {
1513              eet_sync(ef);
1514              ef->references++;
1515              ef->delete_me_now = 1;
1516              eet_internal_close(ef, EINA_TRUE);
1517           }
1518
1519         ef = eet_cache_find((char *)file, eet_readers, eet_readers_num);
1520      }
1521    else if ((mode == EET_FILE_MODE_WRITE) ||
1522             (mode == EET_FILE_MODE_READ_WRITE))
1523      {
1524         ef = eet_cache_find((char *)file, eet_readers, eet_readers_num);
1525         if (ef)
1526           {
1527              ef->delete_me_now = 1;
1528              ef->references++;
1529              eet_internal_close(ef, EINA_TRUE);
1530           }
1531
1532         ef = eet_cache_find((char *)file, eet_writers, eet_writers_num);
1533      }
1534
1535    /* try open the file based on mode */
1536    if ((mode == EET_FILE_MODE_READ) || (mode == EET_FILE_MODE_READ_WRITE))
1537      {
1538         /* Prevent garbage in futur comparison. */
1539         file_stat.st_mtime = 0;
1540
1541         fp = fopen(file, "rb");
1542         if (!fp)
1543            goto open_error;
1544
1545         if (fstat(fileno(fp), &file_stat))
1546           {
1547              fclose(fp);
1548              fp = NULL;
1549
1550              memset(&file_stat, 0, sizeof(file_stat));
1551
1552              goto open_error;
1553           }
1554
1555         if (file_stat.st_size < ((int)sizeof(int) * 3))
1556           {
1557              fclose(fp);
1558              fp = NULL;
1559
1560              memset(&file_stat, 0, sizeof(file_stat));
1561
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->ciphered && efn->size > size)
1800           {
1801              size = efn->size;
1802              data = realloc(data, efn->size);
1803           }
1804
1805         if (efn->data)
1806            memcpy(data, efn->data, size);
1807         else
1808           if (!read_data_from_disk(ef, efn, data, size))
1809             goto on_error;
1810
1811         if (efn->ciphered && cipher_key)
1812           {
1813              if (eet_decipher(data, efn->size, cipher_key, strlen(cipher_key),
1814                               &data_deciphered, &data_deciphered_sz))
1815                {
1816                   if (data_deciphered)
1817                      free(data_deciphered);
1818
1819                   goto on_error;
1820                }
1821
1822              free(data);
1823              data = data_deciphered;
1824              size = data_deciphered_sz;
1825           }
1826      }
1827    /* compressed data */
1828    else
1829      {
1830         void *tmp_data = NULL;
1831         void *data_deciphered = NULL;
1832         unsigned int data_deciphered_sz = 0;
1833         int free_tmp = 0;
1834         int compr_size = efn->size;
1835         uLongf dlen;
1836
1837         /* if we already have the data in ram... copy that */
1838         if (efn->data)
1839            tmp_data = efn->data;
1840         else
1841           {
1842              tmp_data = malloc(compr_size);
1843              if (!tmp_data)
1844                 goto on_error;
1845
1846              free_tmp = 1;
1847
1848              if (!read_data_from_disk(ef, efn, tmp_data, compr_size))
1849                {
1850                   free(tmp_data);
1851                   goto on_error;
1852                }
1853           }
1854
1855         if (efn->ciphered && cipher_key)
1856           {
1857              if (eet_decipher(tmp_data, compr_size, cipher_key,
1858                               strlen(cipher_key), &data_deciphered,
1859                               &data_deciphered_sz))
1860                {
1861                   if (free_tmp)
1862                      free(tmp_data);
1863
1864                   if (data_deciphered)
1865                      free(data_deciphered);
1866
1867                   goto on_error;
1868                }
1869
1870              if (free_tmp)
1871                 free(tmp_data);
1872              free_tmp = 1;
1873              tmp_data = data_deciphered;
1874              compr_size = data_deciphered_sz;
1875           }
1876
1877         /* decompress it */
1878         dlen = size;
1879         if (uncompress((Bytef *)data, &dlen,
1880                        tmp_data, (uLongf)compr_size))
1881           {
1882              if (free_tmp)
1883                 free(tmp_data);
1884              goto on_error;
1885           }
1886
1887         if (free_tmp)
1888            free(tmp_data);
1889      }
1890
1891    UNLOCK_FILE(ef);
1892
1893    /* handle alias */
1894    if (efn->alias)
1895      {
1896         void *tmp;
1897
1898         if (data[size - 1] != '\0')
1899            goto on_error;
1900
1901         tmp = eet_read_cipher(ef, data, size_ret, cipher_key);
1902
1903         free(data);
1904
1905         data = tmp;
1906      }
1907    else
1908    /* fill in return values */
1909    if (size_ret)
1910       *size_ret = size;
1911
1912    return data;
1913
1914 on_error:
1915    UNLOCK_FILE(ef);
1916    free(data);
1917    return NULL;
1918 } /* eet_read_cipher */
1919
1920 EAPI void *
1921 eet_read(Eet_File   *ef,
1922          const char *name,
1923          int        *size_ret)
1924 {
1925    return eet_read_cipher(ef, name, size_ret, NULL);
1926 } /* eet_read */
1927
1928 EAPI const void *
1929 eet_read_direct(Eet_File   *ef,
1930                 const char *name,
1931                 int        *size_ret)
1932 {
1933    Eet_File_Node *efn;
1934    const char *data = NULL;
1935    int size = 0;
1936
1937    if (size_ret)
1938       *size_ret = 0;
1939
1940    /* check to see its' an eet file pointer */
1941    if (eet_check_pointer(ef))
1942       return NULL;
1943
1944    if (!name)
1945       return NULL;
1946
1947    if ((ef->mode != EET_FILE_MODE_READ) &&
1948        (ef->mode != EET_FILE_MODE_READ_WRITE))
1949       return NULL;
1950
1951    /* no header, return NULL */
1952    if (eet_check_header(ef))
1953       return NULL;
1954
1955    LOCK_FILE(ef);
1956
1957    /* hunt hash bucket */
1958    efn = find_node_by_name(ef, name);
1959    if (!efn)
1960       goto on_error;
1961
1962    if (efn->offset < 0 && !efn->data)
1963       goto on_error;
1964
1965    /* get size (uncompressed, if compressed at all) */
1966    size = efn->data_size;
1967
1968    if (efn->alias)
1969      {
1970         data = efn->data ? efn->data : ef->data + efn->offset;
1971
1972         /* handle alias case */
1973         if (efn->compression)
1974           {
1975              char *tmp;
1976              int compr_size = efn->size;
1977              uLongf dlen;
1978
1979              tmp = alloca(sizeof (compr_size));
1980              dlen = size;
1981
1982              if (uncompress((Bytef *)tmp, &dlen, (Bytef *)data,
1983                             (uLongf)compr_size))
1984                 goto on_error;
1985
1986              if (tmp[compr_size - 1] != '\0')
1987                 goto on_error;
1988
1989              UNLOCK_FILE(ef);
1990
1991              return eet_read_direct(ef, tmp, size_ret);
1992           }
1993
1994         if (!data)
1995            goto on_error;
1996
1997         if (data[size - 1] != '\0')
1998            goto on_error;
1999
2000         UNLOCK_FILE(ef);
2001
2002         return eet_read_direct(ef, data, size_ret);
2003      }
2004    else
2005    /* uncompressed data */
2006    if (efn->compression == 0
2007        && efn->ciphered == 0)
2008       data = efn->data ? efn->data : ef->data + efn->offset;  /* compressed data */
2009    else
2010       data = NULL;
2011
2012    /* fill in return values */
2013    if (size_ret)
2014       *size_ret = size;
2015
2016    UNLOCK_FILE(ef);
2017
2018    return data;
2019
2020 on_error:
2021    UNLOCK_FILE(ef);
2022    return NULL;
2023 } /* eet_read_direct */
2024
2025 EAPI Eina_Bool
2026 eet_alias(Eet_File   *ef,
2027           const char *name,
2028           const char *destination,
2029           int         comp)
2030 {
2031    Eet_File_Node *efn;
2032    void *data2;
2033    Eina_Bool exists_already = EINA_FALSE;
2034    int data_size;
2035    int hash;
2036
2037    /* check to see its' an eet file pointer */
2038    if (eet_check_pointer(ef))
2039       return EINA_FALSE;
2040
2041    if ((!name) || (!destination))
2042       return EINA_FALSE;
2043
2044    if ((ef->mode != EET_FILE_MODE_WRITE) &&
2045        (ef->mode != EET_FILE_MODE_READ_WRITE))
2046       return EINA_FALSE;
2047
2048    LOCK_FILE(ef);
2049
2050    if (!ef->header)
2051      {
2052         /* allocate header */
2053         ef->header = calloc(1, sizeof(Eet_File_Header));
2054         if (!ef->header)
2055            goto on_error;
2056
2057         ef->header->magic = EET_MAGIC_FILE_HEADER;
2058         /* allocate directory block in ram */
2059         ef->header->directory = calloc(1, sizeof(Eet_File_Directory));
2060         if (!ef->header->directory)
2061           {
2062              free(ef->header);
2063              ef->header = NULL;
2064              goto on_error;
2065           }
2066
2067         /* 8 bit hash table (256 buckets) */
2068         ef->header->directory->size = 8;
2069         /* allocate base hash table */
2070         ef->header->directory->nodes =
2071            calloc(1, sizeof(Eet_File_Node *) *
2072                   (1 << ef->header->directory->size));
2073         if (!ef->header->directory->nodes)
2074           {
2075              free(ef->header->directory);
2076              ef->header = NULL;
2077              goto on_error;
2078           }
2079      }
2080
2081    /* figure hash bucket */
2082    hash = _eet_hash_gen(name, ef->header->directory->size);
2083
2084    data_size = comp ?
2085       12 + (((strlen(destination) + 1) * 101) / 100)
2086       : strlen(destination) + 1;
2087
2088    data2 = malloc(data_size);
2089    if (!data2)
2090       goto on_error;
2091
2092    /* if we want to compress */
2093    if (comp)
2094      {
2095         uLongf buflen;
2096
2097         /* compress the data with max compression */
2098         buflen = (uLongf)data_size;
2099         if (compress2((Bytef *)data2, &buflen, (Bytef *)destination,
2100                       (uLong)strlen(destination) + 1,
2101                       Z_BEST_COMPRESSION) != Z_OK)
2102           {
2103              free(data2);
2104              goto on_error;
2105           }
2106
2107         /* record compressed chunk size */
2108         data_size = (int)buflen;
2109         if (data_size < 0 || data_size >= (int)(strlen(destination) + 1))
2110           {
2111              comp = 0;
2112              data_size = strlen(destination) + 1;
2113           }
2114         else
2115           {
2116              void *data3;
2117
2118              data3 = realloc(data2, data_size);
2119              if (data3)
2120                 data2 = data3;
2121           }
2122      }
2123
2124    if (!comp)
2125       memcpy(data2, destination, data_size);
2126
2127    /* Does this node already exist? */
2128    for (efn = ef->header->directory->nodes[hash]; efn; efn = efn->next)
2129      {
2130         /* if it matches */
2131         if ((efn->name) && (eet_string_match(efn->name, name)))
2132           {
2133              free(efn->data);
2134              efn->alias = 1;
2135              efn->ciphered = 0;
2136              efn->compression = !!comp;
2137              efn->size = data_size;
2138              efn->data_size = strlen(destination) + 1;
2139              efn->data = data2;
2140              efn->offset = -1;
2141              exists_already = EINA_TRUE;
2142              break;
2143           }
2144      }
2145    if (!exists_already)
2146      {
2147         efn = malloc(sizeof(Eet_File_Node));
2148         if (!efn)
2149           {
2150              free(data2);
2151              goto on_error;
2152           }
2153
2154         efn->name = strdup(name);
2155         efn->name_size = strlen(efn->name) + 1;
2156         efn->free_name = 1;
2157
2158         efn->next = ef->header->directory->nodes[hash];
2159         ef->header->directory->nodes[hash] = efn;
2160         efn->offset = -1;
2161         efn->alias = 1;
2162         efn->ciphered = 0;
2163         efn->compression = !!comp;
2164         efn->size = data_size;
2165         efn->data_size = strlen(destination) + 1;
2166         efn->data = data2;
2167      }
2168
2169    /* flags that writes are pending */
2170    ef->writes_pending = 1;
2171
2172    UNLOCK_FILE(ef);
2173    return EINA_TRUE;
2174
2175 on_error:
2176    UNLOCK_FILE(ef);
2177    return EINA_FALSE;
2178 } /* eet_alias */
2179
2180 EAPI int
2181 eet_write_cipher(Eet_File   *ef,
2182                  const char *name,
2183                  const void *data,
2184                  int         size,
2185                  int         comp,
2186                  const char *cipher_key)
2187 {
2188    Eet_File_Node *efn;
2189    void *data2 = NULL;
2190    int exists_already = 0;
2191    int data_size;
2192    int hash;
2193
2194    /* check to see its' an eet file pointer */
2195    if (eet_check_pointer(ef))
2196       return 0;
2197
2198    if ((!name) || (!data) || (size <= 0))
2199       return 0;
2200
2201    if ((ef->mode != EET_FILE_MODE_WRITE) &&
2202        (ef->mode != EET_FILE_MODE_READ_WRITE))
2203       return 0;
2204
2205    LOCK_FILE(ef);
2206
2207    if (!ef->header)
2208      {
2209         /* allocate header */
2210         ef->header = calloc(1, sizeof(Eet_File_Header));
2211         if (!ef->header)
2212            goto on_error;
2213
2214         ef->header->magic = EET_MAGIC_FILE_HEADER;
2215         /* allocate directory block in ram */
2216         ef->header->directory = calloc(1, sizeof(Eet_File_Directory));
2217         if (!ef->header->directory)
2218           {
2219              free(ef->header);
2220              ef->header = NULL;
2221              goto on_error;
2222           }
2223
2224         /* 8 bit hash table (256 buckets) */
2225         ef->header->directory->size = 8;
2226         /* allocate base hash table */
2227         ef->header->directory->nodes =
2228            calloc(1, sizeof(Eet_File_Node *) *
2229                   (1 << ef->header->directory->size));
2230         if (!ef->header->directory->nodes)
2231           {
2232              free(ef->header->directory);
2233              ef->header = NULL;
2234              goto on_error;
2235           }
2236      }
2237
2238    /* figure hash bucket */
2239    hash = _eet_hash_gen(name, ef->header->directory->size);
2240
2241    data_size = comp ? 12 + ((size * 101) / 100) : size;
2242
2243    if (comp || !cipher_key)
2244      {
2245         data2 = malloc(data_size);
2246         if (!data2)
2247            goto on_error;
2248      }
2249
2250    /* if we want to compress */
2251    if (comp)
2252      {
2253         uLongf buflen;
2254
2255         /* compress the data with max compression */
2256         buflen = (uLongf)data_size;
2257         if (compress2((Bytef *)data2, &buflen, (Bytef *)data,
2258                       (uLong)size, Z_BEST_COMPRESSION) != Z_OK)
2259           {
2260              free(data2);
2261              goto on_error;
2262           }
2263
2264         /* record compressed chunk size */
2265         data_size = (int)buflen;
2266         if (data_size < 0 || data_size >= size)
2267           {
2268              comp = 0;
2269              data_size = size;
2270           }
2271         else
2272           {
2273              void *data3;
2274
2275              data3 = realloc(data2, data_size);
2276              if (data3)
2277                 data2 = data3;
2278           }
2279      }
2280
2281    if (cipher_key)
2282      {
2283         void *data_ciphered = NULL;
2284         unsigned int data_ciphered_sz = 0;
2285         const void *tmp;
2286
2287         tmp = comp ? data2 : data;
2288         if (!eet_cipher(tmp, data_size, cipher_key, strlen(cipher_key),
2289                         &data_ciphered, &data_ciphered_sz))
2290           {
2291              if (data2)
2292                 free(data2);
2293
2294              data2 = data_ciphered;
2295              data_size = data_ciphered_sz;
2296           }
2297         else
2298           {
2299              if (data_ciphered)
2300                 free(data_ciphered);
2301
2302              cipher_key = NULL;
2303           }
2304      }
2305    else
2306      if (!comp)
2307        memcpy(data2, data, size);
2308
2309    /* Does this node already exist? */
2310    for (efn = ef->header->directory->nodes[hash]; efn; efn = efn->next)
2311      {
2312         /* if it matches */
2313         if ((efn->name) && (eet_string_match(efn->name, name)))
2314           {
2315              free(efn->data);
2316              efn->alias = 0;
2317              efn->ciphered = cipher_key ? 1 : 0;
2318              efn->compression = !!comp;
2319              efn->size = data_size;
2320              efn->data_size = size;
2321              efn->data = data2;
2322              efn->offset = -1;
2323              exists_already = 1;
2324              break;
2325           }
2326      }
2327    if (!exists_already)
2328      {
2329         efn = malloc(sizeof(Eet_File_Node));
2330         if (!efn)
2331           {
2332              free(data2);
2333              goto on_error;
2334           }
2335
2336         efn->name = strdup(name);
2337         efn->name_size = strlen(efn->name) + 1;
2338         efn->free_name = 1;
2339
2340         efn->next = ef->header->directory->nodes[hash];
2341         ef->header->directory->nodes[hash] = efn;
2342         efn->offset = -1;
2343         efn->alias = 0;
2344         efn->ciphered = cipher_key ? 1 : 0;
2345         efn->compression = !!comp;
2346         efn->size = data_size;
2347         efn->data_size = size;
2348         efn->data = data2;
2349      }
2350
2351    /* flags that writes are pending */
2352    ef->writes_pending = 1;
2353    UNLOCK_FILE(ef);
2354    return data_size;
2355
2356 on_error:
2357    UNLOCK_FILE(ef);
2358    return 0;
2359 } /* eet_write_cipher */
2360
2361 EAPI int
2362 eet_write(Eet_File   *ef,
2363           const char *name,
2364           const void *data,
2365           int         size,
2366           int         comp)
2367 {
2368    return eet_write_cipher(ef, name, data, size, comp, NULL);
2369 } /* eet_write */
2370
2371 EAPI int
2372 eet_delete(Eet_File   *ef,
2373            const char *name)
2374 {
2375    Eet_File_Node *efn;
2376    Eet_File_Node *pefn;
2377    int hash;
2378    int exists_already = 0;
2379
2380    /* check to see its' an eet file pointer */
2381    if (eet_check_pointer(ef))
2382       return 0;
2383
2384    if (!name)
2385       return 0;
2386
2387    /* deleting keys is only possible in RW or WRITE mode */
2388    if (ef->mode == EET_FILE_MODE_READ)
2389       return 0;
2390
2391    if (eet_check_header(ef))
2392       return 0;
2393
2394    LOCK_FILE(ef);
2395
2396    /* figure hash bucket */
2397    hash = _eet_hash_gen(name, ef->header->directory->size);
2398
2399    /* Does this node already exist? */
2400    for (pefn = NULL, efn = ef->header->directory->nodes[hash];
2401         efn;
2402         pefn = efn, efn = efn->next)
2403      {
2404         /* if it matches */
2405         if (eet_string_match(efn->name, name))
2406           {
2407              if (efn->data)
2408                 free(efn->data);
2409
2410              if (!pefn)
2411                 ef->header->directory->nodes[hash] = efn->next;
2412              else
2413                 pefn->next = efn->next;
2414
2415              if (efn->free_name)
2416                 free(efn->name);
2417
2418              free(efn);
2419              exists_already = 1;
2420              break;
2421           }
2422      }
2423    /* flags that writes are pending */
2424    if (exists_already)
2425       ef->writes_pending = 1;
2426
2427    UNLOCK_FILE(ef);
2428
2429    /* update access time */
2430    return exists_already;
2431 } /* eet_delete */
2432
2433 EAPI Eet_Dictionary *
2434 eet_dictionary_get(Eet_File *ef)
2435 {
2436    if (eet_check_pointer(ef))
2437       return NULL;
2438
2439    return ef->ed;
2440 } /* eet_dictionary_get */
2441
2442 EAPI char **
2443 eet_list(Eet_File   *ef,
2444          const char *glob,
2445          int        *count_ret)
2446 {
2447    Eet_File_Node *efn;
2448    char **list_ret = NULL;
2449    int list_count = 0;
2450    int list_count_alloc = 0;
2451    int i, num;
2452
2453    /* check to see its' an eet file pointer */
2454    if (eet_check_pointer(ef) || eet_check_header(ef) ||
2455        (!glob) ||
2456        ((ef->mode != EET_FILE_MODE_READ) &&
2457         (ef->mode != EET_FILE_MODE_READ_WRITE)))
2458      {
2459         if (count_ret)
2460            *count_ret = 0;
2461
2462         return NULL;
2463      }
2464
2465    if (!strcmp(glob, "*"))
2466       glob = NULL;
2467
2468    LOCK_FILE(ef);
2469
2470    /* loop through all entries */
2471    num = (1 << ef->header->directory->size);
2472    for (i = 0; i < num; i++)
2473      {
2474         for (efn = ef->header->directory->nodes[i]; efn; efn = efn->next)
2475           {
2476              /* if the entry matches the input glob
2477               * check for * explicitly, because on some systems, * isn't well
2478               * supported
2479               */
2480              if ((!glob) || !fnmatch(glob, efn->name, 0))
2481                {
2482                   /* add it to our list */
2483                   list_count++;
2484
2485                   /* only realloc in 32 entry chunks */
2486                   if (list_count > list_count_alloc)
2487                     {
2488                        char **new_list = NULL;
2489
2490                        list_count_alloc += 64;
2491                        new_list =
2492                           realloc(list_ret, list_count_alloc * (sizeof(char *)));
2493                        if (!new_list)
2494                          {
2495                             free(list_ret);
2496
2497                             goto on_error;
2498                          }
2499
2500                        list_ret = new_list;
2501                     }
2502
2503                   /* put pointer of name string in */
2504                   list_ret[list_count - 1] = efn->name;
2505                }
2506           }
2507      }
2508
2509    UNLOCK_FILE(ef);
2510
2511    /* return count and list */
2512    if (count_ret)
2513       *count_ret = list_count;
2514
2515    return list_ret;
2516
2517 on_error:
2518    UNLOCK_FILE(ef);
2519
2520    if (count_ret)
2521       *count_ret = 0;
2522
2523    return NULL;
2524 } /* eet_list */
2525
2526 EAPI int
2527 eet_num_entries(Eet_File *ef)
2528 {
2529    int i, num, ret = 0;
2530    Eet_File_Node *efn;
2531
2532    /* check to see its' an eet file pointer */
2533    if (eet_check_pointer(ef) || eet_check_header(ef) ||
2534        ((ef->mode != EET_FILE_MODE_READ) &&
2535         (ef->mode != EET_FILE_MODE_READ_WRITE)))
2536       return -1;
2537
2538    LOCK_FILE(ef);
2539
2540    /* loop through all entries */
2541    num = (1 << ef->header->directory->size);
2542    for (i = 0; i < num; i++)
2543      {
2544         for (efn = ef->header->directory->nodes[i]; efn; efn = efn->next)
2545            ret++;
2546      }
2547
2548    UNLOCK_FILE(ef);
2549
2550    return ret;
2551 } /* eet_num_entries */
2552
2553 static Eet_File_Node *
2554 find_node_by_name(Eet_File   *ef,
2555                   const char *name)
2556 {
2557    Eet_File_Node *efn;
2558    int hash;
2559
2560    /* get hash bucket this should be in */
2561    hash = _eet_hash_gen(name, ef->header->directory->size);
2562
2563    for (efn = ef->header->directory->nodes[hash]; efn; efn = efn->next)
2564      {
2565         if (eet_string_match(efn->name, name))
2566            return efn;
2567      }
2568
2569    return NULL;
2570 } /* find_node_by_name */
2571
2572 static int
2573 read_data_from_disk(Eet_File      *ef,
2574                     Eet_File_Node *efn,
2575                     void          *buf,
2576                     int            len)
2577 {
2578    if (efn->offset < 0)
2579       return 0;
2580
2581    if (ef->data)
2582      {
2583         if ((efn->offset + len) > ef->data_size)
2584            return 0;
2585
2586         memcpy(buf, ef->data + efn->offset, len);
2587      }
2588    else
2589      {
2590         if (!ef->readfp)
2591            return 0;
2592
2593         /* seek to data location */
2594         if (fseek(ef->readfp, efn->offset, SEEK_SET) < 0)
2595            return 0;
2596
2597         /* read it */
2598         len = fread(buf, len, 1, ef->readfp);
2599      }
2600
2601    return len;
2602 } /* read_data_from_disk */
2603