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