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