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