[svn merge] Changeset 51650 - Revert and reapply badnull patch
[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 (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 shutdown_eet:
769    eet_node_shutdown();
770 unregister_log_domain:
771    eina_log_domain_unregister(_eet_log_dom_global);
772    _eet_log_dom_global = -1;
773 shutdown_eina:
774    eina_shutdown();
775    return --eet_init_count;
776 } /* eet_init */
777
778 EAPI int
779 eet_shutdown(void)
780 {
781    if (--eet_init_count != 0)
782       return eet_init_count;
783
784    eet_clearcache();
785    eet_node_shutdown();
786 #ifdef HAVE_GNUTLS
787    gnutls_global_deinit();
788 #endif /* ifdef HAVE_GNUTLS */
789 #ifdef HAVE_OPENSSL
790    EVP_cleanup();
791    ERR_free_strings();
792 #endif /* ifdef HAVE_OPENSSL */
793    eina_log_domain_unregister(_eet_log_dom_global);
794    _eet_log_dom_global = -1;
795    eina_shutdown();
796
797    return eet_init_count;
798 } /* eet_shutdown */
799
800 EAPI Eet_Error
801 eet_sync(Eet_File *ef)
802 {
803    Eet_Error ret;
804
805    if (eet_check_pointer(ef))
806       return EET_ERROR_BAD_OBJECT;
807
808    if ((ef->mode != EET_FILE_MODE_WRITE) &&
809        (ef->mode != EET_FILE_MODE_READ_WRITE))
810       return EET_ERROR_NOT_WRITABLE;
811
812    if (!ef->writes_pending)
813       return EET_ERROR_NONE;
814
815    LOCK_FILE(ef);
816
817    ret = eet_flush2(ef);
818
819    UNLOCK_FILE(ef);
820    return ret;
821 } /* eet_sync */
822
823 EAPI void
824 eet_clearcache(void)
825 {
826    int num = 0;
827    int i;
828
829    /*
830     * We need to compute the list of eet file to close separately from the cache,
831     * due to eet_close removing them from the cache after each call.
832     */
833    LOCK_CACHE;
834    for (i = 0; i < eet_writers_num; i++)
835      {
836         if (eet_writers[i]->references <= 0)
837            num++;
838      }
839
840    for (i = 0; i < eet_readers_num; i++)
841      {
842         if (eet_readers[i]->references <= 0)
843            num++;
844      }
845
846    if (num > 0)
847      {
848         Eet_File **closelist = NULL;
849
850         closelist = alloca(num * sizeof(Eet_File *));
851         num = 0;
852         for (i = 0; i < eet_writers_num; i++)
853           {
854              if (eet_writers[i]->references <= 0)
855                {
856                   closelist[num] = eet_writers[i];
857                   eet_writers[i]->delete_me_now = 1;
858                   num++;
859                }
860           }
861
862         for (i = 0; i < eet_readers_num; i++)
863           {
864              if (eet_readers[i]->references <= 0)
865                {
866                   closelist[num] = eet_readers[i];
867                   eet_readers[i]->delete_me_now = 1;
868                   num++;
869                }
870           }
871
872         for (i = 0; i < num; i++)
873           {
874              eet_internal_close(closelist[i], EINA_TRUE);
875           }
876      }
877
878    UNLOCK_CACHE;
879 } /* eet_clearcache */
880
881 /* FIXME: MMAP race condition in READ_WRITE_MODE */
882 static Eet_File *
883 eet_internal_read2(Eet_File *ef)
884 {
885    const int *data = (const int *)ef->data;
886    const char *start = (const char *)ef->data;
887    int idx = 0;
888    int num_directory_entries;
889    int bytes_directory_entries;
890    int num_dictionary_entries;
891    int bytes_dictionary_entries;
892    int signature_base_offset;
893    int i;
894
895    idx += sizeof(int);
896    if (eet_test_close((int)ntohl(*data) != EET_MAGIC_FILE2, ef))
897       return NULL;
898
899    data++;
900
901 #define GET_INT(Value, Pointer, Index)\
902    {\
903       Value = ntohl(*Pointer);\
904       Pointer++;\
905       Index += sizeof(int);\
906    }
907
908    /* get entries count and byte count */
909    GET_INT(num_directory_entries,  data, idx);
910    /* get dictionary count and byte count */
911    GET_INT(num_dictionary_entries, data, idx);
912
913    bytes_directory_entries = EET_FILE2_DIRECTORY_ENTRY_SIZE *
914       num_directory_entries + EET_FILE2_HEADER_SIZE;
915    bytes_dictionary_entries = EET_FILE2_DICTIONARY_ENTRY_SIZE *
916       num_dictionary_entries;
917
918    /* we cant have <= 0 values here - invalid */
919    if (eet_test_close((num_directory_entries <= 0), ef))
920       return NULL;
921
922    /* we cant have more bytes directory and bytes in dictionaries than the size of the file */
923    if (eet_test_close((bytes_directory_entries + bytes_dictionary_entries) >
924                       ef->data_size, ef))
925       return NULL;
926
927    /* allocate header */
928    ef->header = calloc(1, sizeof(Eet_File_Header));
929    if (eet_test_close(!ef->header, ef))
930       return NULL;
931
932    ef->header->magic = EET_MAGIC_FILE_HEADER;
933
934    /* allocate directory block in ram */
935    ef->header->directory = calloc(1, sizeof(Eet_File_Directory));
936    if (eet_test_close(!ef->header->directory, ef))
937       return NULL;
938
939    /* 8 bit hash table (256 buckets) */
940    ef->header->directory->size = 8;
941    /* allocate base hash table */
942    ef->header->directory->nodes =
943       calloc(1, sizeof(Eet_File_Node *) * (1 << ef->header->directory->size));
944    if (eet_test_close(!ef->header->directory->nodes, ef))
945       return NULL;
946
947    signature_base_offset = 0;
948
949    /* actually read the directory block - all of it, into ram */
950    for (i = 0; i < num_directory_entries; ++i)
951      {
952         const char *name;
953         Eet_File_Node *efn;
954         int name_offset;
955         int name_size;
956         int hash;
957         int flag;
958
959         /* out directory block is inconsistent - we have oveerun our */
960         /* dynamic block buffer before we finished scanning dir entries */
961         efn = malloc(sizeof(Eet_File_Node));
962         if (eet_test_close(!efn, ef))
963           {
964              if (efn) free(efn); /* yes i know - we only get here if 
965                                   * efn is null/0 -> trying to shut up
966                                   * warning tools like cppcheck */
967              return NULL;
968           }
969
970         /* get entrie header */
971         GET_INT(efn->offset,    data, idx);
972         GET_INT(efn->size,      data, idx);
973         GET_INT(efn->data_size, data, idx);
974         GET_INT(name_offset,    data, idx);
975         GET_INT(name_size,      data, idx);
976         GET_INT(flag,           data, idx);
977
978         efn->compression = flag & 0x1 ? 1 : 0;
979         efn->ciphered = flag & 0x2 ? 1 : 0;
980         efn->alias = flag & 0x4 ? 1 : 0;
981
982 #define EFN_TEST(Test, Ef, Efn)\
983    if (eet_test_close(Test, Ef))\
984      {\
985         free(Efn);\
986         return NULL;\
987      }
988
989         /* check data pointer position */
990         EFN_TEST(!((efn->size > 0)
991                    && (efn->offset + efn->size <= ef->data_size)
992                    && (efn->offset > bytes_dictionary_entries +
993                        bytes_directory_entries)), ef, efn);
994
995         /* check name position */
996         EFN_TEST(!((name_size > 0)
997                    && (name_offset + name_size < ef->data_size)
998                    && (name_offset >= bytes_dictionary_entries +
999                        bytes_directory_entries)), ef, efn);
1000
1001         name = start + name_offset;
1002
1003         /* check '\0' at the end of name string */
1004         EFN_TEST(name[name_size - 1] != '\0', ef, efn);
1005
1006         efn->free_name = 0;
1007         efn->name = (char *)name;
1008         efn->name_size = name_size;
1009
1010         hash = _eet_hash_gen(efn->name, ef->header->directory->size);
1011         efn->next = ef->header->directory->nodes[hash];
1012         ef->header->directory->nodes[hash] = efn;
1013
1014         /* read-only mode, so currently we have no data loaded */
1015         if (ef->mode == EET_FILE_MODE_READ)
1016            efn->data = NULL;  /* read-write mode - read everything into ram */
1017         else
1018           {
1019              efn->data = malloc(efn->size);
1020              if (efn->data)
1021                 memcpy(efn->data, ef->data + efn->offset, efn->size);
1022           }
1023
1024         /* compute the possible position of a signature */
1025         if (signature_base_offset < efn->offset + efn->size)
1026            signature_base_offset = efn->offset + efn->size;
1027      }
1028
1029    ef->ed = NULL;
1030
1031    if (num_dictionary_entries)
1032      {
1033         const int *dico = (const int *)ef->data +
1034            EET_FILE2_DIRECTORY_ENTRY_COUNT * num_directory_entries +
1035            EET_FILE2_HEADER_COUNT;
1036         int j;
1037
1038         if (eet_test_close((num_dictionary_entries *
1039                             (int)EET_FILE2_DICTIONARY_ENTRY_SIZE + idx) >
1040                            (bytes_dictionary_entries + bytes_directory_entries),
1041                            ef))
1042            return NULL;
1043
1044         ef->ed = calloc(1, sizeof (Eet_Dictionary));
1045         if (eet_test_close(!ef->ed, ef))
1046            return NULL;
1047
1048         ef->ed->all = calloc(num_dictionary_entries, sizeof (Eet_String));
1049         if (eet_test_close(!ef->ed->all, ef))
1050            return NULL;
1051
1052         ef->ed->count = num_dictionary_entries;
1053         ef->ed->total = num_dictionary_entries;
1054         ef->ed->start = start + bytes_dictionary_entries +
1055            bytes_directory_entries;
1056         ef->ed->end = ef->ed->start;
1057
1058         for (j = 0; j < ef->ed->count; ++j)
1059           {
1060              int hash;
1061              int offset;
1062
1063              GET_INT(hash,                dico, idx);
1064              GET_INT(offset,              dico, idx);
1065              GET_INT(ef->ed->all[j].len,  dico, idx);
1066              GET_INT(ef->ed->all[j].prev, dico, idx);
1067              GET_INT(ef->ed->all[j].next, dico, idx);
1068
1069              /* Hash value could be stored on 8bits data, but this will break alignment of all the others data.
1070                 So stick to int and check the value. */
1071              if (eet_test_close(hash & 0xFFFFFF00, ef))
1072                 return NULL;
1073
1074              /* Check string position */
1075              if (eet_test_close(!((ef->ed->all[j].len > 0)
1076                                   && (offset >
1077                                       (bytes_dictionary_entries +
1078                                        bytes_directory_entries))
1079                                   && (offset + ef->ed->all[j].len <
1080                                       ef->data_size)), ef))
1081                 return NULL;
1082
1083              ef->ed->all[j].mmap = start + offset;
1084              ef->ed->all[j].str = NULL;
1085
1086              if (ef->ed->all[j].mmap + ef->ed->all[j].len > ef->ed->end)
1087                 ef->ed->end = ef->ed->all[j].mmap + ef->ed->all[j].len;
1088
1089              /* Check '\0' at the end of the string */
1090              if (eet_test_close(ef->ed->all[j].mmap[ef->ed->all[j].len - 1] !=
1091                                 '\0', ef))
1092                 return NULL;
1093
1094              ef->ed->all[j].hash = hash;
1095              if (ef->ed->all[j].prev == -1)
1096                 ef->ed->hash[hash] = j;
1097
1098              /* compute the possible position of a signature */
1099              if (signature_base_offset < offset + ef->ed->all[j].len)
1100                 signature_base_offset = offset + ef->ed->all[j].len;
1101           }
1102      }
1103
1104    /* Check if the file is signed */
1105    ef->x509_der = NULL;
1106    ef->x509_length = 0;
1107    ef->signature = NULL;
1108    ef->signature_length = 0;
1109
1110    if (signature_base_offset < ef->data_size)
1111      {
1112 #ifdef HAVE_SIGNATURE
1113         const unsigned char *buffer = ((const unsigned char *)ef->data) +
1114            signature_base_offset;
1115         ef->x509_der = eet_identity_check(ef->data,
1116                                           signature_base_offset,
1117                                           &ef->sha1,
1118                                           &ef->sha1_length,
1119                                           buffer,
1120                                           ef->data_size - signature_base_offset,
1121                                           &ef->signature,
1122                                           &ef->signature_length,
1123                                           &ef->x509_length);
1124
1125         if (eet_test_close(!ef->x509_der, ef))
1126            return NULL;
1127
1128 #else /* ifdef HAVE_SIGNATURE */
1129         ERR(
1130            "This file could be signed but you didn't compile the necessary code to check the signature.");
1131 #endif /* ifdef HAVE_SIGNATURE */
1132      }
1133
1134    return ef;
1135 } /* eet_internal_read2 */
1136
1137 #if EET_OLD_EET_FILE_FORMAT
1138 static Eet_File *
1139 eet_internal_read1(Eet_File *ef)
1140 {
1141    const unsigned char *dyn_buf = NULL;
1142    const unsigned char *p = NULL;
1143    int idx = 0;
1144    int num_entries;
1145    int byte_entries;
1146    int i;
1147
1148    WRN(
1149       "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.",
1150       ef->path);
1151
1152    /* build header table if read mode */
1153    /* geat header */
1154    idx += sizeof(int);
1155    if (eet_test_close((int)ntohl(*((int *)ef->data)) != EET_MAGIC_FILE, ef))
1156       return NULL;
1157
1158 #define EXTRACT_INT(Value, Pointer, Index)\
1159    {\
1160       int tmp;\
1161       memcpy(&tmp, Pointer + Index, sizeof(int));\
1162       Value = ntohl(tmp);\
1163       Index += sizeof(int);\
1164    }
1165
1166    /* get entries count and byte count */
1167    EXTRACT_INT(num_entries,  ef->data, idx);
1168    EXTRACT_INT(byte_entries, ef->data, idx);
1169
1170    /* we cant have <= 0 values here - invalid */
1171    if (eet_test_close((num_entries <= 0) || (byte_entries <= 0), ef))
1172       return NULL;
1173
1174    /* we can't have more entires than minimum bytes for those! invalid! */
1175    if (eet_test_close((num_entries * 20) > byte_entries, ef))
1176       return NULL;
1177
1178    /* check we will not outrun the file limit */
1179    if (eet_test_close(((byte_entries + (int)sizeof(int) * 3) > ef->data_size),
1180                       ef))
1181       return NULL;
1182
1183    /* allocate header */
1184    ef->header = calloc(1, sizeof(Eet_File_Header));
1185    if (eet_test_close(!ef->header, ef))
1186       return NULL;
1187
1188    ef->header->magic = EET_MAGIC_FILE_HEADER;
1189
1190    /* allocate directory block in ram */
1191    ef->header->directory = calloc(1, sizeof(Eet_File_Directory));
1192    if (eet_test_close(!ef->header->directory, ef))
1193       return NULL;
1194
1195    /* 8 bit hash table (256 buckets) */
1196    ef->header->directory->size = 8;
1197    /* allocate base hash table */
1198    ef->header->directory->nodes =
1199       calloc(1, sizeof(Eet_File_Node *) * (1 << ef->header->directory->size));
1200    if (eet_test_close(!ef->header->directory->nodes, ef))
1201       return NULL;
1202
1203    /* actually read the directory block - all of it, into ram */
1204    dyn_buf = ef->data + idx;
1205
1206    /* parse directory block */
1207    p = dyn_buf;
1208
1209    for (i = 0; i < num_entries; i++)
1210      {
1211         Eet_File_Node *efn;
1212         void *data = NULL;
1213         int indexn = 0;
1214         int name_size;
1215         int hash;
1216         int k;
1217
1218 #define HEADER_SIZE (sizeof(int) * 5)
1219
1220         /* out directory block is inconsistent - we have oveerun our */
1221         /* dynamic block buffer before we finished scanning dir entries */
1222         if (eet_test_close(p + HEADER_SIZE >= (dyn_buf + byte_entries), ef))
1223            return NULL;
1224
1225         /* allocate all the ram needed for this stored node accounting */
1226         efn = malloc (sizeof(Eet_File_Node));
1227         if (eet_test_close(!efn, ef))
1228           {
1229              if (efn) free(efn); /* yes i know - we only get here if 
1230                                   * efn is null/0 -> trying to shut up
1231                                   * warning tools like cppcheck */
1232              return NULL;
1233           }
1234
1235         /* get entrie header */
1236         EXTRACT_INT(efn->offset,      p, indexn);
1237         EXTRACT_INT(efn->compression, p, indexn);
1238         EXTRACT_INT(efn->size,        p, indexn);
1239         EXTRACT_INT(efn->data_size,   p, indexn);
1240         EXTRACT_INT(name_size,        p, indexn);
1241
1242         efn->name_size = name_size;
1243         efn->ciphered = 0;
1244         efn->alias = 0;
1245
1246         /* invalid size */
1247         if (eet_test_close(efn->size <= 0, ef))
1248           {
1249              free(efn);
1250              return NULL;
1251           }
1252
1253         /* invalid name_size */
1254         if (eet_test_close(name_size <= 0, ef))
1255           {
1256              free(efn);
1257              return NULL;
1258           }
1259
1260         /* reading name would mean falling off end of dyn_buf - invalid */
1261         if (eet_test_close((p + 16 + name_size) > (dyn_buf + byte_entries), ef))
1262           {
1263              free(efn);
1264              return NULL;
1265           }
1266
1267         /* This code is useless if we dont want backward compatibility */
1268         for (k = name_size;
1269              k > 0 && ((unsigned char)*(p + HEADER_SIZE + k)) != 0; --k)
1270            ;
1271
1272         efn->free_name = ((unsigned char)*(p + HEADER_SIZE + k)) != 0;
1273
1274         if (efn->free_name)
1275           {
1276              efn->name = malloc(sizeof(char) * name_size + 1);
1277              if (eet_test_close(!efn->name, ef))
1278                {
1279                   free(efn);
1280                   return NULL;
1281                }
1282
1283              strncpy(efn->name, (char *)p + HEADER_SIZE, name_size);
1284              efn->name[name_size] = 0;
1285
1286              WRN(
1287                 "File: %s is not up to date for key \"%s\" - needs rebuilding sometime",
1288                 ef->path,
1289                 efn->name);
1290           }
1291         else
1292            /* The only really usefull peace of code for efn->name (no backward compatibility) */
1293            efn->name = (char *)((unsigned char *)(p + HEADER_SIZE));
1294
1295         /* get hash bucket it should go in */
1296         hash = _eet_hash_gen(efn->name, ef->header->directory->size);
1297         efn->next = ef->header->directory->nodes[hash];
1298         ef->header->directory->nodes[hash] = efn;
1299
1300         /* read-only mode, so currently we have no data loaded */
1301         if (ef->mode == EET_FILE_MODE_READ)
1302            efn->data = NULL;  /* read-write mode - read everything into ram */
1303         else
1304           {
1305              data = malloc(efn->size);
1306              if (data)
1307                 memcpy(data, ef->data + efn->offset, efn->size);
1308
1309              efn->data = data;
1310           }
1311
1312         /* advance */
1313         p += HEADER_SIZE + name_size;
1314      }
1315    return ef;
1316 } /* eet_internal_read1 */
1317
1318 #endif /* if EET_OLD_EET_FILE_FORMAT */
1319
1320 /*
1321  * this should only be called when the cache lock is already held
1322  * (We could drop this restriction if we add a parameter to eet_test_close
1323  * that indicates if the lock is held or not.  For now it is easiest
1324  * to just require that it is always held.)
1325  */
1326 static Eet_File *
1327 eet_internal_read(Eet_File *ef)
1328 {
1329    const int *data = (const int *)ef->data;
1330
1331    if (eet_test_close((ef->data == (void *)-1) || (!ef->data), ef))
1332       return NULL;
1333
1334    if (eet_test_close(ef->data_size < (int)sizeof(int) * 3, ef))
1335       return NULL;
1336
1337    switch (ntohl(*data))
1338      {
1339 #if EET_OLD_EET_FILE_FORMAT
1340       case EET_MAGIC_FILE:
1341          return eet_internal_read1(ef);
1342
1343 #endif /* if EET_OLD_EET_FILE_FORMAT */
1344       case EET_MAGIC_FILE2:
1345          return eet_internal_read2(ef);
1346
1347       default:
1348          ef->delete_me_now = 1;
1349          eet_internal_close(ef, EINA_TRUE);
1350          break;
1351      } /* switch */
1352
1353    return NULL;
1354 } /* eet_internal_read */
1355
1356 static Eet_Error
1357 eet_internal_close(Eet_File *ef,
1358                    Eina_Bool locked)
1359 {
1360    Eet_Error err;
1361
1362    /* check to see its' an eet file pointer */
1363    if (eet_check_pointer(ef))
1364       return EET_ERROR_BAD_OBJECT;
1365
1366    if (!locked)
1367       LOCK_CACHE;
1368
1369    /* deref */
1370    ef->references--;
1371    /* if its still referenced - dont go any further */
1372    if (ef->references > 0)
1373       goto on_error;  /* flush any writes */
1374
1375    err = eet_flush2(ef);
1376
1377    eet_identity_unref(ef->key);
1378    ef->key = NULL;
1379
1380    /* if not urgent to delete it - dont free it - leave it in cache */
1381    if ((!ef->delete_me_now) && (ef->mode == EET_FILE_MODE_READ))
1382       goto on_error;
1383
1384    /* remove from cache */
1385    if (ef->mode == EET_FILE_MODE_READ)
1386       eet_cache_del(ef, &eet_readers, &eet_readers_num, &eet_readers_alloc);
1387    else if ((ef->mode == EET_FILE_MODE_WRITE) ||
1388             (
1389                ef
1390                ->
1391                mode == EET_FILE_MODE_READ_WRITE))
1392       eet_cache_del(ef, &eet_writers, &eet_writers_num, &eet_writers_alloc);
1393
1394    /* we can unlock the cache now */
1395    if (!locked)
1396       UNLOCK_CACHE;
1397
1398    DESTROY_FILE(ef);
1399
1400    /* free up data */
1401    if (ef->header)
1402      {
1403         if (ef->header->directory)
1404           {
1405              if (ef->header->directory->nodes)
1406                {
1407                   int i, num;
1408
1409                   num = (1 << ef->header->directory->size);
1410                   for (i = 0; i < num; i++)
1411                     {
1412                        Eet_File_Node *efn;
1413
1414                        while ((efn = ef->header->directory->nodes[i]))
1415                          {
1416                             if (efn->data)
1417                                free(efn->data);
1418
1419                             ef->header->directory->nodes[i] = efn->next;
1420
1421                             if (efn->free_name)
1422                                free(efn->name);
1423
1424                             free(efn);
1425                          }
1426                     }
1427                   free(ef->header->directory->nodes);
1428                }
1429
1430              free(ef->header->directory);
1431           }
1432
1433         free(ef->header);
1434      }
1435
1436    eet_dictionary_free(ef->ed);
1437
1438    if (ef->sha1)
1439       free(ef->sha1);
1440
1441    if (ef->data)
1442       munmap((void *)ef->data, ef->data_size);
1443
1444    if (ef->readfp)
1445       fclose(ef->readfp);
1446
1447    /* zero out ram for struct - caution tactic against stale memory use */
1448    memset(ef, 0, sizeof(Eet_File));
1449
1450    /* free it */
1451    free(ef);
1452    return err;
1453
1454 on_error:
1455    if (!locked)
1456       UNLOCK_CACHE;
1457
1458    return EET_ERROR_NONE;
1459 } /* eet_internal_close */
1460
1461 EAPI Eet_File *
1462 eet_memopen_read(const void *data,
1463                  size_t      size)
1464 {
1465    Eet_File *ef;
1466
1467    if (!data || size == 0)
1468       return NULL;
1469
1470    ef = malloc (sizeof (Eet_File));
1471    if (!ef)
1472       return NULL;
1473
1474    INIT_FILE(ef);
1475    ef->ed = NULL;
1476    ef->path = NULL;
1477    ef->key = NULL;
1478    ef->magic = EET_MAGIC_FILE;
1479    ef->references = 1;
1480    ef->mode = EET_FILE_MODE_READ;
1481    ef->header = NULL;
1482    ef->mtime = 0;
1483    ef->delete_me_now = 1;
1484    ef->readfp = NULL;
1485    ef->data = data;
1486    ef->data_size = size;
1487    ef->sha1 = NULL;
1488    ef->sha1_length = 0;
1489
1490    /* eet_internal_read expects the cache lock to be held when it is called */
1491    LOCK_CACHE;
1492    ef = eet_internal_read(ef);
1493    UNLOCK_CACHE;
1494    return ef;
1495 } /* eet_memopen_read */
1496
1497 EAPI Eet_File *
1498 eet_open(const char   *file,
1499          Eet_File_Mode mode)
1500 {
1501    FILE *fp;
1502    Eet_File *ef;
1503    int file_len;
1504    struct stat file_stat;
1505
1506    if (!file)
1507       return NULL;
1508
1509    /* find the current file handle in cache*/
1510    ef = NULL;
1511    LOCK_CACHE;
1512    if (mode == EET_FILE_MODE_READ)
1513      {
1514         ef = eet_cache_find((char *)file, eet_writers, eet_writers_num);
1515         if (ef)
1516           {
1517              eet_sync(ef);
1518              ef->references++;
1519              ef->delete_me_now = 1;
1520              eet_internal_close(ef, EINA_TRUE);
1521           }
1522
1523         ef = eet_cache_find((char *)file, eet_readers, eet_readers_num);
1524      }
1525    else if ((mode == EET_FILE_MODE_WRITE) ||
1526             (mode == EET_FILE_MODE_READ_WRITE))
1527      {
1528         ef = eet_cache_find((char *)file, eet_readers, eet_readers_num);
1529         if (ef)
1530           {
1531              ef->delete_me_now = 1;
1532              ef->references++;
1533              eet_internal_close(ef, EINA_TRUE);
1534           }
1535
1536         ef = eet_cache_find((char *)file, eet_writers, eet_writers_num);
1537      }
1538
1539    /* try open the file based on mode */
1540    if ((mode == EET_FILE_MODE_READ) || (mode == EET_FILE_MODE_READ_WRITE))
1541      {
1542         /* Prevent garbage in futur comparison. */
1543         file_stat.st_mtime = 0;
1544
1545         fp = fopen(file, "rb");
1546         if (!fp)
1547            goto open_error;
1548
1549         if (fstat(fileno(fp), &file_stat))
1550           {
1551              fclose(fp);
1552              fp = NULL;
1553              goto open_error;
1554           }
1555
1556         if ((mode == EET_FILE_MODE_READ) &&
1557             (file_stat.st_size < ((int)sizeof(int) * 3)))
1558           {
1559              fclose(fp);
1560              fp = NULL;
1561              goto open_error;
1562           }
1563
1564 open_error:
1565         if (!fp && mode == EET_FILE_MODE_READ)
1566            goto on_error;
1567      }
1568    else
1569      {
1570         if (mode != EET_FILE_MODE_WRITE)
1571            return NULL;
1572
1573         memset(&file_stat, 0, sizeof(file_stat));
1574
1575         fp = NULL;
1576      }
1577
1578    /* We found one */
1579    if (ef &&
1580        ((file_stat.st_mtime != ef->mtime) ||
1581         (file_stat.st_size != ef->data_size)))
1582      {
1583         ef->delete_me_now = 1;
1584         ef->references++;
1585         eet_internal_close(ef, EINA_TRUE);
1586         ef = NULL;
1587      }
1588
1589    if (ef)
1590      {
1591         /* reference it up and return it */
1592         if (fp)
1593            fclose(fp);
1594
1595         ef->references++;
1596         UNLOCK_CACHE;
1597         return ef;
1598      }
1599
1600    file_len = strlen(file) + 1;
1601
1602    /* Allocate struct for eet file and have it zero'd out */
1603    ef = malloc(sizeof(Eet_File) + file_len);
1604    if (!ef)
1605       goto on_error;
1606
1607    /* fill some of the members */
1608    INIT_FILE(ef);
1609    ef->key = NULL;
1610    ef->readfp = fp;
1611    ef->path = ((char *)ef) + sizeof(Eet_File);
1612    memcpy(ef->path, file, file_len);
1613    ef->magic = EET_MAGIC_FILE;
1614    ef->references = 1;
1615    ef->mode = mode;
1616    ef->header = NULL;
1617    ef->mtime = file_stat.st_mtime;
1618    ef->writes_pending = 0;
1619    ef->delete_me_now = 0;
1620    ef->data = NULL;
1621    ef->data_size = 0;
1622    ef->sha1 = NULL;
1623    ef->sha1_length = 0;
1624
1625    ef->ed = (mode == EET_FILE_MODE_WRITE)
1626       || (!ef->readfp && mode == EET_FILE_MODE_READ_WRITE) ?
1627       eet_dictionary_add() : NULL;
1628
1629    if (!ef->readfp &&
1630        (mode == EET_FILE_MODE_READ_WRITE || mode == EET_FILE_MODE_WRITE))
1631       goto empty_file;
1632
1633    /* if we can't open - bail out */
1634    if (eet_test_close(!ef->readfp, ef))
1635       goto on_error;
1636
1637    fcntl(fileno(ef->readfp), F_SETFD, FD_CLOEXEC);
1638    /* if we opened for read or read-write */
1639    if ((mode == EET_FILE_MODE_READ) || (mode == EET_FILE_MODE_READ_WRITE))
1640      {
1641         ef->data_size = file_stat.st_size;
1642         ef->data = mmap(NULL, ef->data_size, PROT_READ,
1643                         MAP_SHARED, fileno(ef->readfp), 0);
1644         if (eet_test_close((ef->data == MAP_FAILED), ef))
1645            goto on_error;
1646
1647         ef = eet_internal_read(ef);
1648         if (!ef)
1649            goto on_error;
1650      }
1651
1652 empty_file:
1653    /* add to cache */
1654    if (ef->references == 1)
1655      {
1656         if (ef->mode == EET_FILE_MODE_READ)
1657            eet_cache_add(ef, &eet_readers, &eet_readers_num, &eet_readers_alloc);
1658         else
1659         if ((ef->mode == EET_FILE_MODE_WRITE) ||
1660             (ef->mode == EET_FILE_MODE_READ_WRITE))
1661            eet_cache_add(ef, &eet_writers, &eet_writers_num, &eet_writers_alloc);
1662      }
1663
1664    UNLOCK_CACHE;
1665    return ef;
1666
1667 on_error:
1668    UNLOCK_CACHE;
1669    return NULL;
1670 } /* eet_open */
1671
1672 EAPI Eet_File_Mode
1673 eet_mode_get(Eet_File *ef)
1674 {
1675    /* check to see its' an eet file pointer */
1676    if ((!ef) || (ef->magic != EET_MAGIC_FILE))
1677       return EET_FILE_MODE_INVALID;
1678    else
1679       return ef->mode;
1680 } /* eet_mode_get */
1681
1682 EAPI const void *
1683 eet_identity_x509(Eet_File *ef,
1684                   int      *der_length)
1685 {
1686    if (!ef->x509_der)
1687       return NULL;
1688
1689    if (der_length)
1690       *der_length = ef->x509_length;
1691
1692    return ef->x509_der;
1693 } /* eet_identity_x509 */
1694
1695 EAPI const void *
1696 eet_identity_signature(Eet_File *ef,
1697                        int      *signature_length)
1698 {
1699    if (!ef->signature)
1700       return NULL;
1701
1702    if (signature_length)
1703       *signature_length = ef->signature_length;
1704
1705    return ef->signature;
1706 } /* eet_identity_signature */
1707
1708 EAPI const void *
1709 eet_identity_sha1(Eet_File *ef,
1710                   int      *sha1_length)
1711 {
1712    if (!ef->sha1)
1713       ef->sha1 = eet_identity_compute_sha1(ef->data,
1714                                            ef->data_size,
1715                                            &ef->sha1_length);
1716
1717    if (sha1_length)
1718       *sha1_length = ef->sha1_length;
1719
1720    return ef->sha1;
1721 } /* eet_identity_sha1 */
1722
1723 EAPI Eet_Error
1724 eet_identity_set(Eet_File *ef,
1725                  Eet_Key  *key)
1726 {
1727    Eet_Key *tmp = ef->key;
1728
1729    if (!ef)
1730       return EET_ERROR_BAD_OBJECT;
1731
1732    ef->key = key;
1733    eet_identity_ref(ef->key);
1734    eet_identity_unref(tmp);
1735
1736    /* flags that writes are pending */
1737    ef->writes_pending = 1;
1738
1739    return EET_ERROR_NONE;
1740 } /* eet_identity_set */
1741
1742 EAPI Eet_Error
1743 eet_close(Eet_File *ef)
1744 {
1745    return eet_internal_close(ef, EINA_FALSE);
1746 } /* eet_close */
1747
1748 EAPI void *
1749 eet_read_cipher(Eet_File   *ef,
1750                 const char *name,
1751                 int        *size_ret,
1752                 const char *cipher_key)
1753 {
1754    Eet_File_Node *efn;
1755    char *data = NULL;
1756    int size = 0;
1757
1758    if (size_ret)
1759       *size_ret = 0;
1760
1761    /* check to see its' an eet file pointer */
1762    if (eet_check_pointer(ef))
1763       return NULL;
1764
1765    if (!name)
1766       return NULL;
1767
1768    if ((ef->mode != EET_FILE_MODE_READ) &&
1769        (ef->mode != EET_FILE_MODE_READ_WRITE))
1770       return NULL;
1771
1772    /* no header, return NULL */
1773    if (eet_check_header(ef))
1774       return NULL;
1775
1776    LOCK_FILE(ef);
1777
1778    /* hunt hash bucket */
1779    efn = find_node_by_name(ef, name);
1780    if (!efn)
1781       goto on_error;
1782
1783    /* get size (uncompressed, if compressed at all) */
1784    size = efn->data_size;
1785
1786    /* allocate data */
1787    data = malloc(size);
1788    if (!data)
1789       goto on_error;
1790
1791    /* uncompressed data */
1792    if (efn->compression == 0)
1793      {
1794         void *data_deciphered = NULL;
1795         unsigned int data_deciphered_sz = 0;
1796         /* if we alreayd have the data in ram... copy that */
1797
1798         if (efn->data)
1799            memcpy(data, efn->data, efn->size);
1800         else
1801         if (!read_data_from_disk(ef, efn, data, size))
1802            goto on_error;
1803
1804         if (efn->ciphered && cipher_key)
1805           {
1806              if (eet_decipher(data, size, cipher_key, strlen(cipher_key),
1807                               &data_deciphered, &data_deciphered_sz))
1808                {
1809                   if (data_deciphered)
1810                      free(data_deciphered);
1811
1812                   goto on_error;
1813                }
1814
1815              free(data);
1816              data = data_deciphered;
1817              size = data_deciphered_sz;
1818           }
1819      }
1820    /* compressed data */
1821    else
1822      {
1823         void *tmp_data;
1824         void *data_deciphered = NULL;
1825         unsigned int data_deciphered_sz = 0;
1826         int free_tmp = 0;
1827         int compr_size = efn->size;
1828         uLongf dlen;
1829
1830         /* if we already have the data in ram... copy that */
1831         if (efn->data)
1832            tmp_data = efn->data;
1833         else
1834           {
1835              tmp_data = malloc(compr_size);
1836              if (!tmp_data)
1837                 goto on_error;
1838
1839              free_tmp = 1;
1840
1841              if (!read_data_from_disk(ef, efn, tmp_data, compr_size))
1842                {
1843                   free(tmp_data);
1844                   goto on_error;
1845                }
1846           }
1847
1848         if (efn->ciphered && cipher_key)
1849           {
1850              if (eet_decipher(tmp_data, compr_size, cipher_key,
1851                               strlen(cipher_key), &data_deciphered,
1852                               &data_deciphered_sz))
1853                {
1854                   if (free_tmp)
1855                      free(tmp_data);
1856
1857                   if (data_deciphered)
1858                      free(data_deciphered);
1859
1860                   goto on_error;
1861                }
1862
1863              free(tmp_data);
1864              tmp_data = data_deciphered;
1865              compr_size = data_deciphered_sz;
1866           }
1867
1868         /* decompress it */
1869         dlen = size;
1870         if (uncompress((Bytef *)data, &dlen,
1871                        tmp_data, (uLongf)compr_size))
1872            goto on_error;
1873
1874         if (free_tmp)
1875            free(tmp_data);
1876      }
1877
1878    UNLOCK_FILE(ef);
1879
1880    /* handle alias */
1881    if (efn->alias)
1882      {
1883         void *tmp;
1884
1885         if (data[size - 1] != '\0')
1886            goto on_error;
1887
1888         tmp = eet_read_cipher(ef, data, size_ret, cipher_key);
1889
1890         free(data);
1891
1892         data = tmp;
1893      }
1894    else
1895    /* fill in return values */
1896    if (size_ret)
1897       *size_ret = size;
1898
1899    return data;
1900
1901 on_error:
1902    UNLOCK_FILE(ef);
1903    free(data);
1904    return NULL;
1905 } /* eet_read_cipher */
1906
1907 EAPI void *
1908 eet_read(Eet_File   *ef,
1909          const char *name,
1910          int        *size_ret)
1911 {
1912    return eet_read_cipher(ef, name, size_ret, NULL);
1913 } /* eet_read */
1914
1915 EAPI const void *
1916 eet_read_direct(Eet_File   *ef,
1917                 const char *name,
1918                 int        *size_ret)
1919 {
1920    Eet_File_Node *efn;
1921    const char *data = NULL;
1922    int size = 0;
1923
1924    if (size_ret)
1925       *size_ret = 0;
1926
1927    /* check to see its' an eet file pointer */
1928    if (eet_check_pointer(ef))
1929       return NULL;
1930
1931    if (!name)
1932       return NULL;
1933
1934    if ((ef->mode != EET_FILE_MODE_READ) &&
1935        (ef->mode != EET_FILE_MODE_READ_WRITE))
1936       return NULL;
1937
1938    /* no header, return NULL */
1939    if (eet_check_header(ef))
1940       return NULL;
1941
1942    LOCK_FILE(ef);
1943
1944    /* hunt hash bucket */
1945    efn = find_node_by_name(ef, name);
1946    if (!efn)
1947       goto on_error;
1948
1949    if (efn->offset < 0 && !efn->data)
1950       goto on_error;
1951
1952    /* get size (uncompressed, if compressed at all) */
1953    size = efn->data_size;
1954
1955    if (efn->alias)
1956      {
1957         data = efn->data ? efn->data : ef->data + efn->offset;
1958
1959         /* handle alias case */
1960         if (efn->compression)
1961           {
1962              char *tmp;
1963              int compr_size = efn->size;
1964              uLongf dlen;
1965
1966              tmp = alloca(sizeof (compr_size));
1967              dlen = size;
1968
1969              if (uncompress((Bytef *)tmp, &dlen, (Bytef *)data,
1970                             (uLongf)compr_size))
1971                 goto on_error;
1972
1973              if (tmp[compr_size - 1] != '\0')
1974                 goto on_error;
1975
1976              UNLOCK_FILE(ef);
1977
1978              return eet_read_direct(ef, tmp, size_ret);
1979           }
1980
1981         if (!data)
1982            goto on_error;
1983
1984         if (data[size - 1] != '\0')
1985            goto on_error;
1986
1987         UNLOCK_FILE(ef);
1988
1989         return eet_read_direct(ef, data, size_ret);
1990      }
1991    else
1992    /* uncompressed data */
1993    if (efn->compression == 0
1994        && efn->ciphered == 0)
1995       data = efn->data ? efn->data : ef->data + efn->offset;  /* compressed data */
1996    else
1997       data = NULL;
1998
1999    /* fill in return values */
2000    if (size_ret)
2001       *size_ret = size;
2002
2003    UNLOCK_FILE(ef);
2004
2005    return data;
2006
2007 on_error:
2008    UNLOCK_FILE(ef);
2009    return NULL;
2010 } /* eet_read_direct */
2011
2012 EAPI Eina_Bool
2013 eet_alias(Eet_File   *ef,
2014           const char *name,
2015           const char *destination,
2016           int         comp)
2017 {
2018    Eet_File_Node *efn;
2019    void *data2;
2020    Eina_Bool exists_already = EINA_FALSE;
2021    int data_size;
2022    int hash;
2023
2024    /* check to see its' an eet file pointer */
2025    if (eet_check_pointer(ef))
2026       return EINA_FALSE;
2027
2028    if ((!name) || (!destination))
2029       return EINA_FALSE;
2030
2031    if ((ef->mode != EET_FILE_MODE_WRITE) &&
2032        (ef->mode != EET_FILE_MODE_READ_WRITE))
2033       return EINA_FALSE;
2034
2035    LOCK_FILE(ef);
2036
2037    if (!ef->header)
2038      {
2039         /* allocate header */
2040         ef->header = calloc(1, sizeof(Eet_File_Header));
2041         if (!ef->header)
2042            goto on_error;
2043
2044         ef->header->magic = EET_MAGIC_FILE_HEADER;
2045         /* allocate directory block in ram */
2046         ef->header->directory = calloc(1, sizeof(Eet_File_Directory));
2047         if (!ef->header->directory)
2048           {
2049              free(ef->header);
2050              ef->header = NULL;
2051              goto on_error;
2052           }
2053
2054         /* 8 bit hash table (256 buckets) */
2055         ef->header->directory->size = 8;
2056         /* allocate base hash table */
2057         ef->header->directory->nodes =
2058            calloc(1, sizeof(Eet_File_Node *) *
2059                   (1 << ef->header->directory->size));
2060         if (!ef->header->directory->nodes)
2061           {
2062              free(ef->header->directory);
2063              ef->header = NULL;
2064              goto on_error;
2065           }
2066      }
2067
2068    /* figure hash bucket */
2069    hash = _eet_hash_gen(name, ef->header->directory->size);
2070
2071    data_size = comp ?
2072       12 + (((strlen(destination) + 1) * 101) / 100)
2073       : strlen(destination) + 1;
2074
2075    data2 = malloc(data_size);
2076    if (!data2)
2077       goto on_error;
2078
2079    /* if we want to compress */
2080    if (comp)
2081      {
2082         uLongf buflen;
2083
2084         /* compress the data with max compression */
2085         buflen = (uLongf)data_size;
2086         if (compress2((Bytef *)data2, &buflen, (Bytef *)destination,
2087                       (uLong)strlen(destination) + 1,
2088                       Z_BEST_COMPRESSION) != Z_OK)
2089           {
2090              free(data2);
2091              goto on_error;
2092           }
2093
2094         /* record compressed chunk size */
2095         data_size = (int)buflen;
2096         if (data_size < 0 || data_size >= (int)(strlen(destination) + 1))
2097           {
2098              comp = 0;
2099              data_size = strlen(destination) + 1;
2100           }
2101         else
2102           {
2103              void *data3;
2104
2105              data3 = realloc(data2, data_size);
2106              if (data3)
2107                 data2 = data3;
2108           }
2109      }
2110
2111    if (!comp)
2112       memcpy(data2, destination, data_size);
2113
2114    /* Does this node already exist? */
2115    for (efn = ef->header->directory->nodes[hash]; efn; efn = efn->next)
2116      {
2117         /* if it matches */
2118         if ((efn->name) && (eet_string_match(efn->name, name)))
2119           {
2120              free(efn->data);
2121              efn->alias = 1;
2122              efn->ciphered = 0;
2123              efn->compression = !!comp;
2124              efn->size = data_size;
2125              efn->data_size = strlen(destination) + 1;
2126              efn->data = data2;
2127              efn->offset = -1;
2128              exists_already = EINA_TRUE;
2129              break;
2130           }
2131      }
2132    if (!exists_already)
2133      {
2134         efn = malloc(sizeof(Eet_File_Node));
2135         if (!efn)
2136           {
2137              free(data2);
2138              goto on_error;
2139           }
2140
2141         efn->name = strdup(name);
2142         efn->name_size = strlen(efn->name) + 1;
2143         efn->free_name = 1;
2144
2145         efn->next = ef->header->directory->nodes[hash];
2146         ef->header->directory->nodes[hash] = efn;
2147         efn->offset = -1;
2148         efn->alias = 1;
2149         efn->ciphered = 0;
2150         efn->compression = !!comp;
2151         efn->size = data_size;
2152         efn->data_size = strlen(destination) + 1;
2153         efn->data = data2;
2154      }
2155
2156    /* flags that writes are pending */
2157    ef->writes_pending = 1;
2158
2159    UNLOCK_FILE(ef);
2160    return EINA_TRUE;
2161
2162 on_error:
2163    UNLOCK_FILE(ef);
2164    return EINA_FALSE;
2165 } /* eet_alias */
2166
2167 EAPI int
2168 eet_write_cipher(Eet_File   *ef,
2169                  const char *name,
2170                  const void *data,
2171                  int         size,
2172                  int         comp,
2173                  const char *cipher_key)
2174 {
2175    Eet_File_Node *efn;
2176    void *data2 = NULL;
2177    int exists_already = 0;
2178    int data_size;
2179    int hash;
2180
2181    /* check to see its' an eet file pointer */
2182    if (eet_check_pointer(ef))
2183       return 0;
2184
2185    if ((!name) || (!data) || (size <= 0))
2186       return 0;
2187
2188    if ((ef->mode != EET_FILE_MODE_WRITE) &&
2189        (ef->mode != EET_FILE_MODE_READ_WRITE))
2190       return 0;
2191
2192    LOCK_FILE(ef);
2193
2194    if (!ef->header)
2195      {
2196         /* allocate header */
2197         ef->header = calloc(1, sizeof(Eet_File_Header));
2198         if (!ef->header)
2199            goto on_error;
2200
2201         ef->header->magic = EET_MAGIC_FILE_HEADER;
2202         /* allocate directory block in ram */
2203         ef->header->directory = calloc(1, sizeof(Eet_File_Directory));
2204         if (!ef->header->directory)
2205           {
2206              free(ef->header);
2207              ef->header = NULL;
2208              goto on_error;
2209           }
2210
2211         /* 8 bit hash table (256 buckets) */
2212         ef->header->directory->size = 8;
2213         /* allocate base hash table */
2214         ef->header->directory->nodes =
2215            calloc(1, sizeof(Eet_File_Node *) *
2216                   (1 << ef->header->directory->size));
2217         if (!ef->header->directory->nodes)
2218           {
2219              free(ef->header->directory);
2220              ef->header = NULL;
2221              goto on_error;
2222           }
2223      }
2224
2225    /* figure hash bucket */
2226    hash = _eet_hash_gen(name, ef->header->directory->size);
2227
2228    data_size = comp ? 12 + ((size * 101) / 100) : size;
2229
2230    if (comp || !cipher_key)
2231      {
2232         data2 = malloc(data_size);
2233         if (!data2)
2234            goto on_error;
2235      }
2236
2237    /* if we want to compress */
2238    if (comp)
2239      {
2240         uLongf buflen;
2241
2242         /* compress the data with max compression */
2243         buflen = (uLongf)data_size;
2244         if (compress2((Bytef *)data2, &buflen, (Bytef *)data,
2245                       (uLong)size, Z_BEST_COMPRESSION) != Z_OK)
2246           {
2247              free(data2);
2248              goto on_error;
2249           }
2250
2251         /* record compressed chunk size */
2252         data_size = (int)buflen;
2253         if (data_size < 0 || data_size >= size)
2254           {
2255              comp = 0;
2256              data_size = size;
2257           }
2258         else
2259           {
2260              void *data3;
2261
2262              data3 = realloc(data2, data_size);
2263              if (data3)
2264                 data2 = data3;
2265           }
2266      }
2267
2268    if (cipher_key)
2269      {
2270         void *data_ciphered = NULL;
2271         unsigned int data_ciphered_sz = 0;
2272         const void *tmp;
2273
2274         tmp = data2 ? data2 : data;
2275         if (!eet_cipher(tmp, data_size, cipher_key, strlen(cipher_key),
2276                         &data_ciphered, &data_ciphered_sz))
2277           {
2278              if (data2)
2279                 free(data2);
2280
2281              data2 = data_ciphered;
2282              data_size = data_ciphered_sz;
2283              size = (data_size > size) ? data_size : size;
2284           }
2285         else
2286           {
2287              if (data_ciphered)
2288                 free(data_ciphered);
2289
2290              cipher_key = NULL;
2291           }
2292      }
2293    else
2294    if (!comp)
2295       memcpy(data2, data, size);
2296
2297    /* Does this node already exist? */
2298    for (efn = ef->header->directory->nodes[hash]; efn; efn = efn->next)
2299      {
2300         /* if it matches */
2301         if ((efn->name) && (eet_string_match(efn->name, name)))
2302           {
2303              free(efn->data);
2304              efn->alias = 0;
2305              efn->ciphered = cipher_key ? 1 : 0;
2306              efn->compression = !!comp;
2307              efn->size = data_size;
2308              efn->data_size = size;
2309              efn->data = data2;
2310              efn->offset = -1;
2311              exists_already = 1;
2312              break;
2313           }
2314      }
2315    if (!exists_already)
2316      {
2317         efn = malloc(sizeof(Eet_File_Node));
2318         if (!efn)
2319           {
2320              free(data2);
2321              goto on_error;
2322           }
2323
2324         efn->name = strdup(name);
2325         efn->name_size = strlen(efn->name) + 1;
2326         efn->free_name = 1;
2327
2328         efn->next = ef->header->directory->nodes[hash];
2329         ef->header->directory->nodes[hash] = efn;
2330         efn->offset = -1;
2331         efn->alias = 0;
2332         efn->ciphered = cipher_key ? 1 : 0;
2333         efn->compression = !!comp;
2334         efn->size = data_size;
2335         efn->data_size = size;
2336         efn->data = data2;
2337      }
2338
2339    /* flags that writes are pending */
2340    ef->writes_pending = 1;
2341    UNLOCK_FILE(ef);
2342    return data_size;
2343
2344 on_error:
2345    UNLOCK_FILE(ef);
2346    return 0;
2347 } /* eet_write_cipher */
2348
2349 EAPI int
2350 eet_write(Eet_File   *ef,
2351           const char *name,
2352           const void *data,
2353           int         size,
2354           int         comp)
2355 {
2356    return eet_write_cipher(ef, name, data, size, comp, NULL);
2357 } /* eet_write */
2358
2359 EAPI int
2360 eet_delete(Eet_File   *ef,
2361            const char *name)
2362 {
2363    Eet_File_Node *efn;
2364    Eet_File_Node *pefn;
2365    int hash;
2366    int exists_already = 0;
2367
2368    /* check to see its' an eet file pointer */
2369    if (eet_check_pointer(ef))
2370       return 0;
2371
2372    if (!name)
2373       return 0;
2374
2375    /* deleting keys is only possible in RW or WRITE mode */
2376    if (ef->mode == EET_FILE_MODE_READ)
2377       return 0;
2378
2379    if (eet_check_header(ef))
2380       return 0;
2381
2382    LOCK_FILE(ef);
2383
2384    /* figure hash bucket */
2385    hash = _eet_hash_gen(name, ef->header->directory->size);
2386
2387    /* Does this node already exist? */
2388    for (pefn = NULL, efn = ef->header->directory->nodes[hash];
2389         efn;
2390         pefn = efn, efn = efn->next)
2391      {
2392         /* if it matches */
2393         if (eet_string_match(efn->name, name))
2394           {
2395              if (efn->data)
2396                 free(efn->data);
2397
2398              if (!pefn)
2399                 ef->header->directory->nodes[hash] = efn->next;
2400              else
2401                 pefn->next = efn->next;
2402
2403              if (efn->free_name)
2404                 free(efn->name);
2405
2406              free(efn);
2407              exists_already = 1;
2408              break;
2409           }
2410      }
2411    /* flags that writes are pending */
2412    if (exists_already)
2413       ef->writes_pending = 1;
2414
2415    UNLOCK_FILE(ef);
2416
2417    /* update access time */
2418    return exists_already;
2419 } /* eet_delete */
2420
2421 EAPI Eet_Dictionary *
2422 eet_dictionary_get(Eet_File *ef)
2423 {
2424    if (eet_check_pointer(ef))
2425       return NULL;
2426
2427    return ef->ed;
2428 } /* eet_dictionary_get */
2429
2430 EAPI char **
2431 eet_list(Eet_File   *ef,
2432          const char *glob,
2433          int        *count_ret)
2434 {
2435    Eet_File_Node *efn;
2436    char **list_ret = NULL;
2437    int list_count = 0;
2438    int list_count_alloc = 0;
2439    int i, num;
2440
2441    /* check to see its' an eet file pointer */
2442    if (eet_check_pointer(ef) || eet_check_header(ef) ||
2443        (!glob) ||
2444        ((ef->mode != EET_FILE_MODE_READ) &&
2445         (ef->mode != EET_FILE_MODE_READ_WRITE)))
2446      {
2447         if (count_ret)
2448            *count_ret = 0;
2449
2450         return NULL;
2451      }
2452
2453    if (!strcmp(glob, "*"))
2454       glob = NULL;
2455
2456    LOCK_FILE(ef);
2457
2458    /* loop through all entries */
2459    num = (1 << ef->header->directory->size);
2460    for (i = 0; i < num; i++)
2461      {
2462         for (efn = ef->header->directory->nodes[i]; efn; efn = efn->next)
2463           {
2464              /* if the entry matches the input glob
2465               * check for * explicitly, because on some systems, * isn't well
2466               * supported
2467               */
2468              if ((!glob) || !fnmatch(glob, efn->name, 0))
2469                {
2470                   /* add it to our list */
2471                   list_count++;
2472
2473                   /* only realloc in 32 entry chunks */
2474                   if (list_count > list_count_alloc)
2475                     {
2476                        char **new_list = NULL;
2477
2478                        list_count_alloc += 64;
2479                        new_list =
2480                           realloc(list_ret, list_count_alloc * (sizeof(char *)));
2481                        if (!new_list)
2482                          {
2483                             free(list_ret);
2484
2485                             goto on_error;
2486                          }
2487
2488                        list_ret = new_list;
2489                     }
2490
2491                   /* put pointer of name string in */
2492                   list_ret[list_count - 1] = efn->name;
2493                }
2494           }
2495      }
2496
2497    UNLOCK_FILE(ef);
2498
2499    /* return count and list */
2500    if (count_ret)
2501       *count_ret = list_count;
2502
2503    return list_ret;
2504
2505 on_error:
2506    UNLOCK_FILE(ef);
2507
2508    if (count_ret)
2509       *count_ret = 0;
2510
2511    return NULL;
2512 } /* eet_list */
2513
2514 EAPI int
2515 eet_num_entries(Eet_File *ef)
2516 {
2517    int i, num, ret = 0;
2518    Eet_File_Node *efn;
2519
2520    /* check to see its' an eet file pointer */
2521    if (eet_check_pointer(ef) || eet_check_header(ef) ||
2522        ((ef->mode != EET_FILE_MODE_READ) &&
2523         (ef->mode != EET_FILE_MODE_READ_WRITE)))
2524       return -1;
2525
2526    LOCK_FILE(ef);
2527
2528    /* loop through all entries */
2529    num = (1 << ef->header->directory->size);
2530    for (i = 0; i < num; i++)
2531      {
2532         for (efn = ef->header->directory->nodes[i]; efn; efn = efn->next)
2533            ret++;
2534      }
2535
2536    UNLOCK_FILE(ef);
2537
2538    return ret;
2539 } /* eet_num_entries */
2540
2541 static Eet_File_Node *
2542 find_node_by_name(Eet_File   *ef,
2543                   const char *name)
2544 {
2545    Eet_File_Node *efn;
2546    int hash;
2547
2548    /* get hash bucket this should be in */
2549    hash = _eet_hash_gen(name, ef->header->directory->size);
2550
2551    for (efn = ef->header->directory->nodes[hash]; efn; efn = efn->next)
2552      {
2553         if (eet_string_match(efn->name, name))
2554            return efn;
2555      }
2556
2557    return NULL;
2558 } /* find_node_by_name */
2559
2560 static int
2561 read_data_from_disk(Eet_File      *ef,
2562                     Eet_File_Node *efn,
2563                     void          *buf,
2564                     int            len)
2565 {
2566    if (efn->offset < 0)
2567       return 0;
2568
2569    if (ef->data)
2570      {
2571         if ((efn->offset + len) > ef->data_size)
2572            return 0;
2573
2574         memcpy(buf, ef->data + efn->offset, len);
2575      }
2576    else
2577      {
2578         if (!ef->readfp)
2579            return 0;
2580
2581         /* seek to data location */
2582         if (fseek(ef->readfp, efn->offset, SEEK_SET) < 0)
2583            return 0;
2584
2585         /* read it */
2586         len = fread(buf, len, 1, ef->readfp);
2587      }
2588
2589    return len;
2590 } /* read_data_from_disk */
2591