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