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