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