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