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