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