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