Add Tizen 2.0 packaging
[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           {
1491             UNLOCK_CACHE;
1492             return NULL;
1493           }
1494
1495         size = 0;
1496
1497         fp = NULL;
1498      }
1499
1500    /* We found one */
1501    if (ef && ef->readfp != fp)
1502      {
1503         ef->delete_me_now = 1;
1504         ef->references++;
1505         eet_internal_close(ef, EINA_TRUE);
1506         ef = NULL;
1507      }
1508
1509    if (ef)
1510      {
1511         /* reference it up and return it */
1512          if (fp)
1513            eina_file_close(fp);
1514
1515          ef->references++;
1516          UNLOCK_CACHE;
1517          return ef;
1518      }
1519
1520    file_len = strlen(file) + 1;
1521
1522    /* Allocate struct for eet file and have it zero'd out */
1523    ef = eet_file_malloc(1);
1524    if (!ef)
1525      goto on_error;
1526
1527    /* fill some of the members */
1528    INIT_FILE(ef);
1529    ef->key = NULL;
1530    ef->readfp = fp;
1531    ef->path = eina_stringshare_add_length(file, file_len);
1532    ef->magic = EET_MAGIC_FILE;
1533    ef->references = 1;
1534    ef->mode = mode;
1535    ef->header = NULL;
1536    ef->writes_pending = 0;
1537    ef->delete_me_now = 0;
1538    ef->data = NULL;
1539    ef->data_size = 0;
1540    ef->sha1 = NULL;
1541    ef->sha1_length = 0;
1542
1543    ef->ed = (mode == EET_FILE_MODE_WRITE)
1544      || (!ef->readfp && mode == EET_FILE_MODE_READ_WRITE) ?
1545      eet_dictionary_add() : NULL;
1546
1547    if (!ef->readfp &&
1548        (mode == EET_FILE_MODE_READ_WRITE || mode == EET_FILE_MODE_WRITE))
1549      goto empty_file;
1550
1551    /* if we can't open - bail out */
1552    if (eet_test_close(!ef->readfp, ef))
1553      goto on_error;
1554
1555    /* if we opened for read or read-write */
1556    if ((mode == EET_FILE_MODE_READ) || (mode == EET_FILE_MODE_READ_WRITE))
1557      {
1558         ef->data_size = size;
1559         ef->data = eina_file_map_all(fp, EINA_FILE_SEQUENTIAL);
1560         if (eet_test_close((ef->data == NULL), ef))
1561           goto on_error;
1562
1563         ef = eet_internal_read(ef);
1564         if (!ef)
1565           goto on_error;
1566      }
1567
1568 empty_file:
1569    /* add to cache */
1570    if (ef->references == 1)
1571      {
1572         if (ef->mode == EET_FILE_MODE_READ)
1573           eet_cache_add(ef, &eet_readers, &eet_readers_num, &eet_readers_alloc);
1574         else if ((ef->mode == EET_FILE_MODE_WRITE) ||
1575                  (ef->mode == EET_FILE_MODE_READ_WRITE))
1576           eet_cache_add(ef, &eet_writers, &eet_writers_num, &eet_writers_alloc);
1577      }
1578
1579    UNLOCK_CACHE;
1580    return ef;
1581
1582 on_error:
1583    UNLOCK_CACHE;
1584    return NULL;
1585 }
1586
1587 EAPI Eet_File_Mode
1588 eet_mode_get(Eet_File *ef)
1589 {
1590    /* check to see its' an eet file pointer */
1591     if ((!ef) || (ef->magic != EET_MAGIC_FILE))
1592       return EET_FILE_MODE_INVALID;
1593     else
1594       return ef->mode;
1595 }
1596
1597 EAPI const void *
1598 eet_identity_x509(Eet_File *ef,
1599                   int      *der_length)
1600 {
1601    if (!ef->x509_der)
1602      return NULL;
1603
1604    if (der_length)
1605      *der_length = ef->x509_length;
1606
1607    return ef->x509_der;
1608 }
1609
1610 EAPI const void *
1611 eet_identity_signature(Eet_File *ef,
1612                        int      *signature_length)
1613 {
1614    if (!ef->signature)
1615      return NULL;
1616
1617    if (signature_length)
1618      *signature_length = ef->signature_length;
1619
1620    return ef->signature;
1621 }
1622
1623 EAPI const void *
1624 eet_identity_sha1(Eet_File *ef,
1625                   int      *sha1_length)
1626 {
1627    if (!ef->sha1)
1628      ef->sha1 = eet_identity_compute_sha1(ef->data,
1629                                           ef->data_size,
1630                                           &ef->sha1_length);
1631
1632    if (sha1_length)
1633      *sha1_length = ef->sha1_length;
1634
1635    return ef->sha1;
1636 }
1637
1638 EAPI Eet_Error
1639 eet_identity_set(Eet_File *ef,
1640                  Eet_Key  *key)
1641 {
1642    Eet_Key *tmp;
1643
1644    if (!ef)
1645      return EET_ERROR_BAD_OBJECT;
1646
1647    tmp = ef->key;
1648    ef->key = key;
1649    eet_identity_ref(ef->key);
1650    eet_identity_unref(tmp);
1651
1652    /* flags that writes are pending */
1653    ef->writes_pending = 1;
1654
1655    return EET_ERROR_NONE;
1656 }
1657
1658 EAPI Eet_Error
1659 eet_close(Eet_File *ef)
1660 {
1661    return eet_internal_close(ef, EINA_FALSE);
1662 }
1663
1664 EAPI void *
1665 eet_read_cipher(Eet_File   *ef,
1666                 const char *name,
1667                 int        *size_ret,
1668                 const char *cipher_key)
1669 {
1670    Eet_File_Node *efn;
1671    char *data = NULL;
1672    unsigned long int size = 0;
1673
1674    if (size_ret)
1675      *size_ret = 0;
1676
1677    /* check to see its' an eet file pointer */
1678    if (eet_check_pointer(ef))
1679      return NULL;
1680
1681    if (!name)
1682      return NULL;
1683
1684    if ((ef->mode != EET_FILE_MODE_READ) &&
1685        (ef->mode != EET_FILE_MODE_READ_WRITE))
1686      return NULL;
1687
1688    /* no header, return NULL */
1689    if (eet_check_header(ef))
1690      return NULL;
1691
1692    LOCK_FILE(ef);
1693
1694    /* hunt hash bucket */
1695    efn = find_node_by_name(ef, name);
1696    if (!efn)
1697      goto on_error;
1698
1699    /* get size (uncompressed, if compressed at all) */
1700    size = efn->data_size;
1701
1702    /* allocate data */
1703    data = malloc(size);
1704    if (!data)
1705      goto on_error;
1706
1707    /* uncompressed data */
1708    if (efn->compression == 0)
1709      {
1710         void *data_deciphered = NULL;
1711         unsigned int data_deciphered_sz = 0;
1712         /* if we already have the data in ram... copy that */
1713
1714         if (efn->ciphered && efn->size > size)
1715           {
1716              size = efn->size;
1717              data = realloc(data, efn->size);
1718           }
1719
1720         if (efn->data)
1721           memcpy(data, efn->data, size);
1722         else
1723         if (!read_data_from_disk(ef, efn, data, size))
1724           goto on_error;
1725
1726         if (efn->ciphered && cipher_key)
1727           {
1728              if (eet_decipher(data, efn->size, cipher_key, strlen(cipher_key),
1729                               &data_deciphered, &data_deciphered_sz))
1730                {
1731                   if (data_deciphered)
1732                     free(data_deciphered);
1733
1734                   goto on_error;
1735                }
1736
1737              free(data);
1738              data = data_deciphered;
1739              size = data_deciphered_sz;
1740           }
1741      }
1742    /* compressed data */
1743    else
1744      {
1745         void *tmp_data = NULL;
1746         void *data_deciphered = NULL;
1747         unsigned int data_deciphered_sz = 0;
1748         int free_tmp = 0, ret;
1749         int compr_size = efn->size;
1750         uLongf dlen;
1751
1752         /* if we already have the data in ram... copy that */
1753         if (efn->data)
1754           tmp_data = efn->data;
1755         else
1756           {
1757              tmp_data = malloc(compr_size);
1758              if (!tmp_data)
1759                goto on_error;
1760
1761              free_tmp = 1;
1762
1763              if (!read_data_from_disk(ef, efn, tmp_data, compr_size))
1764                {
1765                   free(tmp_data);
1766                   goto on_error;
1767                }
1768           }
1769
1770         if (efn->ciphered && cipher_key)
1771           {
1772              if (eet_decipher(tmp_data, compr_size, cipher_key,
1773                               strlen(cipher_key), &data_deciphered,
1774                               &data_deciphered_sz))
1775                {
1776                   if (free_tmp)
1777                     free(tmp_data);
1778
1779                   if (data_deciphered)
1780                     free(data_deciphered);
1781
1782                   goto on_error;
1783                }
1784
1785              if (free_tmp)
1786                free(tmp_data);
1787              free_tmp = 1;
1788              tmp_data = data_deciphered;
1789              compr_size = data_deciphered_sz;
1790           }
1791
1792         /* decompress it */
1793         dlen = size;
1794         switch (efn->compression_type)
1795           {
1796            case EET_COMPRESSION_VERYFAST:
1797            case EET_COMPRESSION_SUPERFAST:
1798              ret = LZ4_uncompress(tmp_data, data, dlen);
1799              if (ret != compr_size)
1800                {
1801                   if (free_tmp)
1802                     free(tmp_data);
1803                   goto on_error;
1804                }
1805              break;
1806            default:
1807              if (uncompress((Bytef *)data, &dlen,
1808                             tmp_data, (uLongf)compr_size) != Z_OK)
1809                {
1810                   if (free_tmp)
1811                     free(tmp_data);
1812                   goto on_error;
1813                }
1814              break;
1815           }
1816
1817         if (free_tmp)
1818           free(tmp_data);
1819      }
1820
1821    UNLOCK_FILE(ef);
1822
1823    /* handle alias */
1824    if (efn->alias)
1825      {
1826         void *tmp;
1827
1828         if (data[size - 1] != '\0')
1829           goto on_error;
1830
1831         tmp = eet_read_cipher(ef, data, size_ret, cipher_key);
1832
1833         free(data);
1834
1835         data = tmp;
1836      }
1837    else
1838    /* fill in return values */
1839    if (size_ret)
1840      *size_ret = size;
1841
1842    return data;
1843
1844 on_error:
1845    UNLOCK_FILE(ef);
1846    free(data);
1847    return NULL;
1848 }
1849
1850 EAPI void *
1851 eet_read(Eet_File   *ef,
1852          const char *name,
1853          int        *size_ret)
1854 {
1855    return eet_read_cipher(ef, name, size_ret, NULL);
1856 }
1857
1858 EAPI const void *
1859 eet_read_direct(Eet_File   *ef,
1860                 const char *name,
1861                 int        *size_ret)
1862 {
1863    Eet_File_Node *efn;
1864    const char *data = NULL;
1865    int size = 0, ret;
1866
1867    if (size_ret)
1868      *size_ret = 0;
1869
1870    /* check to see its' an eet file pointer */
1871    if (eet_check_pointer(ef))
1872      return NULL;
1873
1874    if (!name)
1875      return NULL;
1876
1877    if ((ef->mode != EET_FILE_MODE_READ) &&
1878        (ef->mode != EET_FILE_MODE_READ_WRITE))
1879      return NULL;
1880
1881    /* no header, return NULL */
1882    if (eet_check_header(ef))
1883      return NULL;
1884
1885    LOCK_FILE(ef);
1886
1887    /* hunt hash bucket */
1888    efn = find_node_by_name(ef, name);
1889    if (!efn)
1890      goto on_error;
1891
1892    /* trick to detect data in memory instead of mmaped from disk */
1893    if (efn->offset > ef->data_size && !efn->data)
1894      goto on_error;
1895
1896    /* get size (uncompressed, if compressed at all) */
1897    size = efn->data_size;
1898
1899    if (efn->alias)
1900      {
1901         data = efn->data ? efn->data : ef->data + efn->offset;
1902
1903         /* handle alias case */
1904         if (efn->compression)
1905           {
1906              const void *retptr;
1907              char *tmp;
1908              int compr_size = efn->size;
1909              uLongf dlen;
1910              
1911              tmp = malloc(compr_size);
1912              if (!tmp) goto on_error;
1913              switch (efn->compression_type)
1914                {
1915                 case EET_COMPRESSION_VERYFAST:
1916                 case EET_COMPRESSION_SUPERFAST:
1917                   ret = LZ4_uncompress(data, tmp, size);
1918                   if (ret != compr_size)
1919                     {
1920                        free(tmp);
1921                        goto on_error;
1922                     }
1923                   break;
1924                 default:
1925                   dlen = size;
1926                   
1927                   if (uncompress((Bytef *)tmp, &dlen, (Bytef *)data,
1928                                  (uLongf)compr_size))
1929                     {
1930                        free(tmp);
1931                        goto on_error;
1932                     }
1933                }
1934              
1935              if (tmp[compr_size - 1] != '\0')
1936                {
1937                   free(tmp);
1938                   goto on_error;
1939                }
1940              
1941              UNLOCK_FILE(ef);
1942              
1943              retptr = eet_read_direct(ef, tmp, size_ret);
1944              free(tmp);
1945              return retptr;
1946           }
1947
1948         if (!data)
1949           goto on_error;
1950
1951         if (data[size - 1] != '\0')
1952           goto on_error;
1953
1954         UNLOCK_FILE(ef);
1955
1956         return eet_read_direct(ef, data, size_ret);
1957      }
1958    else
1959    /* uncompressed data */
1960    if ((efn->compression == 0) && (efn->ciphered == 0))
1961      data = efn->data ? efn->data : ef->data + efn->offset;  /* compressed data */
1962    else
1963      data = NULL;
1964
1965    /* fill in return values */
1966    if (size_ret)
1967      *size_ret = size;
1968
1969    UNLOCK_FILE(ef);
1970
1971    return data;
1972
1973 on_error:
1974    UNLOCK_FILE(ef);
1975    return NULL;
1976 }
1977
1978 EAPI const char *
1979 eet_alias_get(Eet_File   *ef,
1980               const char *name)
1981 {
1982    Eet_File_Node *efn;
1983    const char *data = NULL;
1984    int size = 0, ret;
1985
1986    /* check to see its' an eet file pointer */
1987    if (eet_check_pointer(ef))
1988      return NULL;
1989
1990    if (!name)
1991      return NULL;
1992
1993    if ((ef->mode != EET_FILE_MODE_READ) &&
1994        (ef->mode != EET_FILE_MODE_READ_WRITE))
1995      return NULL;
1996
1997    /* no header, return NULL */
1998    if (eet_check_header(ef))
1999      return NULL;
2000
2001    LOCK_FILE(ef);
2002
2003    /* hunt hash bucket */
2004    efn = find_node_by_name(ef, name);
2005    if (!efn)
2006      goto on_error;
2007
2008    /* trick to detect data in memory instead of mmaped from disk */
2009    if (efn->offset > ef->data_size && !efn->data)
2010      goto on_error;
2011
2012    /* get size (uncompressed, if compressed at all) */
2013    size = efn->data_size;
2014
2015    if (!efn->alias) return NULL;
2016    data = efn->data ? efn->data : ef->data + efn->offset;
2017
2018    /* handle alias case */
2019    if (efn->compression)
2020      {
2021         const char *retptr;
2022         char *tmp;
2023         int compr_size = efn->size;
2024         uLongf dlen;
2025                   
2026         tmp = malloc(compr_size);
2027         if (!tmp) goto on_error;
2028         switch (efn->compression_type)
2029           {
2030            case EET_COMPRESSION_VERYFAST:
2031            case EET_COMPRESSION_SUPERFAST:
2032              ret = LZ4_uncompress(data, tmp, size);
2033              if (ret != compr_size)
2034                {
2035                   free(tmp);
2036                   goto on_error;
2037                }
2038              break;
2039            default:
2040              dlen = size;
2041              
2042              if (uncompress((Bytef *)tmp, &dlen, (Bytef *)data,
2043                             (uLongf)compr_size))
2044                {
2045                   free(tmp);
2046                   goto on_error;
2047                }
2048           }
2049         
2050         if (tmp[compr_size - 1] != '\0')
2051           goto on_error;
2052
2053         UNLOCK_FILE(ef);
2054
2055         retptr = eina_stringshare_add(tmp);
2056         free(tmp);
2057         return retptr;
2058      }
2059
2060    if (!data)
2061      goto on_error;
2062
2063    if (data[size - 1] != '\0')
2064      goto on_error;
2065
2066    UNLOCK_FILE(ef);
2067
2068    return eina_stringshare_add(data);
2069
2070 on_error:
2071    UNLOCK_FILE(ef);
2072    return NULL;
2073 }
2074
2075 EAPI Eina_Bool
2076 eet_alias(Eet_File   *ef,
2077           const char *name,
2078           const char *destination,
2079           int         comp)
2080 {
2081    Eet_File_Node *efn;
2082    void *data2;
2083    Eina_Bool exists_already = EINA_FALSE;
2084    int data_size, ret, hash, slen;
2085
2086    /* check to see its' an eet file pointer */
2087    if (eet_check_pointer(ef))
2088      return EINA_FALSE;
2089
2090    if ((!name) || (!destination))
2091      return EINA_FALSE;
2092
2093    if ((ef->mode != EET_FILE_MODE_WRITE) &&
2094        (ef->mode != EET_FILE_MODE_READ_WRITE))
2095      return EINA_FALSE;
2096
2097    LOCK_FILE(ef);
2098
2099    if (!ef->header)
2100      {
2101         /* allocate header */
2102          ef->header = eet_file_header_calloc(1);
2103          if (!ef->header)
2104            goto on_error;
2105
2106          ef->header->magic = EET_MAGIC_FILE_HEADER;
2107          /* allocate directory block in ram */
2108          ef->header->directory = eet_file_directory_calloc(1);
2109          if (!ef->header->directory)
2110            {
2111               eet_file_header_mp_free(ef->header);
2112               ef->header = NULL;
2113               goto on_error;
2114            }
2115
2116          /* 8 bit hash table (256 buckets) */
2117          ef->header->directory->size = 8;
2118          /* allocate base hash table */
2119          ef->header->directory->nodes =
2120            calloc(1, sizeof(Eet_File_Node *) *
2121                   (1 << ef->header->directory->size));
2122          if (!ef->header->directory->nodes)
2123            {
2124               eet_file_directory_mp_free(ef->header->directory);
2125               ef->header = NULL;
2126               goto on_error;
2127            }
2128      }
2129
2130    /* figure hash bucket */
2131    hash = _eet_hash_gen(name, ef->header->directory->size);
2132
2133    slen = strlen(destination) + 1;
2134    data_size = comp ?
2135      12 + ((slen * 101) / 100)
2136      : slen;
2137    if (comp)
2138      {
2139         ret = LZ4_compressBound(slen);
2140         if ((ret > 0) && (ret > data_size)) data_size = ret;
2141      }
2142    
2143    data2 = malloc(data_size);
2144    if (!data2) goto on_error;
2145
2146    /* if we want to compress */
2147    if (comp)
2148      {
2149         switch (comp)
2150           {
2151            case EET_COMPRESSION_VERYFAST:
2152              ret = LZ4_compressHC((const char *)destination, (char *)data2,
2153                                   slen);
2154              if (ret <= 0)
2155                {
2156                   free(data2);
2157                   goto on_error;
2158                }
2159              data_size = ret;
2160              break;
2161            case EET_COMPRESSION_SUPERFAST:
2162              ret = LZ4_compress((const char *)destination, (char *)data2,
2163                                 slen);
2164              if (ret <= 0)
2165                {
2166                   free(data2);
2167                   goto on_error;
2168                }
2169              data_size = ret;
2170              break;
2171            default:
2172                {
2173                   uLongf buflen;
2174                   
2175                   /* compress the data with max compression */
2176                   buflen = (uLongf)data_size;
2177                   if (compress2((Bytef *)data2, &buflen, 
2178                                 (const Bytef *)destination,
2179                                 (uLong)slen, Z_BEST_COMPRESSION) != Z_OK)
2180                     {
2181                        free(data2);
2182                        goto on_error;
2183                     }
2184                   /* record compressed chunk size */
2185                   data_size = (int)buflen;
2186                }
2187              break;
2188           }
2189         if ((data_size < 0) || 
2190             (data_size >= (int)(strlen(destination) + 1)))
2191           {
2192              comp = 0;
2193              data_size = strlen(destination) + 1;
2194           }
2195         else
2196           {
2197              void *data3;
2198
2199              data3 = realloc(data2, data_size);
2200              if (data3) data2 = data3;
2201           }
2202      }
2203    
2204    if (!comp) memcpy(data2, destination, data_size);
2205
2206    /* Does this node already exist? */
2207    for (efn = ef->header->directory->nodes[hash]; efn; efn = efn->next)
2208      {
2209         /* if it matches */
2210          if ((efn->name) && (eet_string_match(efn->name, name)))
2211            {
2212               free(efn->data);
2213               efn->alias = 1;
2214               efn->ciphered = 0;
2215               efn->compression = !!comp;
2216               efn->compression_type = comp;
2217               efn->size = data_size;
2218               efn->data_size = strlen(destination) + 1;
2219               efn->data = data2;
2220               /* Put the offset above the limit to avoid direct access */
2221               efn->offset = ef->data_size + 1;
2222               exists_already = EINA_TRUE;
2223               break;
2224            }
2225      }
2226    if (!exists_already)
2227      {
2228         efn = eet_file_node_malloc(1);
2229         if (!efn)
2230           {
2231              free(data2);
2232              goto on_error;
2233           }
2234
2235         efn->name = strdup(name);
2236         efn->name_size = strlen(efn->name) + 1;
2237         efn->free_name = 1;
2238
2239         efn->next = ef->header->directory->nodes[hash];
2240         ef->header->directory->nodes[hash] = efn;
2241         /* Put the offset above the limit to avoid direct access */
2242         efn->offset = ef->data_size + 1;
2243         efn->alias = 1;
2244         efn->ciphered = 0;
2245         efn->compression = !!comp;
2246         efn->compression_type = comp;
2247         efn->size = data_size;
2248         efn->data_size = strlen(destination) + 1;
2249         efn->data = data2;
2250      }
2251
2252    /* flags that writes are pending */
2253    ef->writes_pending = 1;
2254
2255    UNLOCK_FILE(ef);
2256    return EINA_TRUE;
2257
2258 on_error:
2259    UNLOCK_FILE(ef);
2260    return EINA_FALSE;
2261 }
2262
2263 EAPI int
2264 eet_write_cipher(Eet_File   *ef,
2265                  const char *name,
2266                  const void *data,
2267                  int         size,
2268                  int         comp,
2269                  const char *cipher_key)
2270 {
2271    Eet_File_Node *efn;
2272    void *data2 = NULL;
2273    int exists_already = 0, data_size, hash, ret;
2274
2275    /* check to see its' an eet file pointer */
2276    if (eet_check_pointer(ef))
2277      return 0;
2278
2279    if ((!name) || (!data) || (size <= 0))
2280      return 0;
2281
2282    if ((ef->mode != EET_FILE_MODE_WRITE) &&
2283        (ef->mode != EET_FILE_MODE_READ_WRITE))
2284      return 0;
2285
2286    LOCK_FILE(ef);
2287
2288    if (!ef->header)
2289      {
2290         /* allocate header */
2291          ef->header = eet_file_header_calloc(1);
2292          if (!ef->header)
2293            goto on_error;
2294
2295          ef->header->magic = EET_MAGIC_FILE_HEADER;
2296          /* allocate directory block in ram */
2297          ef->header->directory = eet_file_directory_calloc(1);
2298          if (!ef->header->directory)
2299            {
2300               eet_file_header_mp_free(ef->header);
2301               ef->header = NULL;
2302               goto on_error;
2303            }
2304
2305          /* 8 bit hash table (256 buckets) */
2306          ef->header->directory->size = 8;
2307          /* allocate base hash table */
2308          ef->header->directory->nodes =
2309            calloc(1, sizeof(Eet_File_Node *) *
2310                   (1 << ef->header->directory->size));
2311          if (!ef->header->directory->nodes)
2312            {
2313               eet_file_directory_mp_free(ef->header->directory);
2314               ef->header = NULL;
2315               goto on_error;
2316            }
2317      }
2318
2319    /* figure hash bucket */
2320    hash = _eet_hash_gen(name, ef->header->directory->size);
2321
2322    UNLOCK_FILE(ef);
2323    
2324    data_size = comp ? 12 + ((size * 101) / 100) : size;
2325    if (comp)
2326      {
2327         ret = LZ4_compressBound(size);
2328         if ((ret > 0) && (ret > data_size)) data_size = ret;
2329      }
2330    
2331    if (comp || !cipher_key)
2332      {
2333         data2 = malloc(data_size);
2334         if (!data2)
2335           goto on_error;
2336      }
2337
2338    /* if we want to compress */
2339    if (comp)
2340      {
2341         switch (comp)
2342           {
2343            case EET_COMPRESSION_VERYFAST:
2344              ret = LZ4_compressHC((const char *)data, (char *)data2, size);
2345              if (ret <= 0)
2346                {
2347                   free(data2);
2348                   LOCK_FILE(ef);
2349                   goto on_error;
2350                }
2351              data_size = ret;
2352              break;
2353            case EET_COMPRESSION_SUPERFAST:
2354              ret = LZ4_compress((const char *)data, (char *)data2, size);
2355              if (ret <= 0)
2356                {
2357                   free(data2);
2358                   LOCK_FILE(ef);
2359                   goto on_error;
2360                }
2361              data_size = ret;
2362              break;
2363            default:
2364                {
2365                   uLongf buflen;
2366
2367                   /* compress the data with max compression */
2368                   buflen = (uLongf)data_size;
2369                   if (compress2((Bytef *)data2, &buflen, (Bytef *)data,
2370                                 (uLong)size, Z_BEST_COMPRESSION) != Z_OK)
2371                     {
2372                        free(data2);
2373                        LOCK_FILE(ef);
2374                        goto on_error;
2375                     }
2376                   /* record compressed chunk size */
2377                   data_size = (int)buflen;
2378                }
2379           }
2380         if ((data_size < 0) || (data_size >= size))
2381           {
2382              comp = 0;
2383              data_size = size;
2384           }
2385         else
2386           {
2387              void *data3;
2388
2389              data3 = realloc(data2, data_size);
2390              if (data3)
2391                data2 = data3;
2392           }
2393      }
2394
2395    if (cipher_key)
2396      {
2397         void *data_ciphered = NULL;
2398         unsigned int data_ciphered_sz = 0;
2399         const void *tmp;
2400
2401         tmp = comp ? data2 : data;
2402         if (!eet_cipher(tmp, data_size, cipher_key, strlen(cipher_key),
2403                         &data_ciphered, &data_ciphered_sz))
2404           {
2405              if (data2)
2406                free(data2);
2407
2408              data2 = data_ciphered;
2409              data_size = data_ciphered_sz;
2410           }
2411         else
2412           {
2413              if (data_ciphered)
2414                free(data_ciphered);
2415
2416              cipher_key = NULL;
2417           }
2418      }
2419    else
2420    if (!comp)
2421      memcpy(data2, data, size);
2422
2423    LOCK_FILE(ef);
2424    /* Does this node already exist? */
2425    for (efn = ef->header->directory->nodes[hash]; efn; efn = efn->next)
2426      {
2427         /* if it matches */
2428          if ((efn->name) && (eet_string_match(efn->name, name)))
2429            {
2430               free(efn->data);
2431               efn->alias = 0;
2432               efn->ciphered = cipher_key ? 1 : 0;
2433               efn->compression = !!comp;
2434               efn->compression_type = comp;
2435               efn->size = data_size;
2436               efn->data_size = size;
2437               efn->data = data2;
2438               /* Put the offset above the limit to avoid direct access */
2439               efn->offset = ef->data_size + 1;
2440               exists_already = 1;
2441               break;
2442            }
2443      }
2444    if (!exists_already)
2445      {
2446         efn = eet_file_node_malloc(1);
2447         if (!efn)
2448           {
2449              free(data2);
2450              goto on_error;
2451           }
2452
2453         efn->name = strdup(name);
2454         efn->name_size = strlen(efn->name) + 1;
2455         efn->free_name = 1;
2456
2457         efn->next = ef->header->directory->nodes[hash];
2458         ef->header->directory->nodes[hash] = efn;
2459         /* Put the offset above the limit to avoid direct access */
2460         efn->offset = ef->data_size + 1;
2461         efn->alias = 0;
2462         efn->ciphered = cipher_key ? 1 : 0;
2463         efn->compression = !!comp;
2464         efn->compression_type = comp;
2465         efn->size = data_size;
2466         efn->data_size = size;
2467         efn->data = data2;
2468      }
2469
2470    /* flags that writes are pending */
2471    ef->writes_pending = 1;
2472    UNLOCK_FILE(ef);
2473    return data_size;
2474
2475 on_error:
2476    UNLOCK_FILE(ef);
2477    return 0;
2478 }
2479
2480 EAPI int
2481 eet_write(Eet_File   *ef,
2482           const char *name,
2483           const void *data,
2484           int         size,
2485           int         comp)
2486 {
2487    return eet_write_cipher(ef, name, data, size, comp, NULL);
2488 }
2489
2490 EAPI int
2491 eet_delete(Eet_File   *ef,
2492            const char *name)
2493 {
2494    Eet_File_Node *efn;
2495    Eet_File_Node *pefn;
2496    int hash;
2497    int exists_already = 0;
2498
2499    /* check to see its' an eet file pointer */
2500    if (eet_check_pointer(ef))
2501      return 0;
2502
2503    if (!name)
2504      return 0;
2505
2506    /* deleting keys is only possible in RW or WRITE mode */
2507    if (ef->mode == EET_FILE_MODE_READ)
2508      return 0;
2509
2510    if (eet_check_header(ef))
2511      return 0;
2512
2513    LOCK_FILE(ef);
2514
2515    /* figure hash bucket */
2516    hash = _eet_hash_gen(name, ef->header->directory->size);
2517
2518    /* Does this node already exist? */
2519    for (pefn = NULL, efn = ef->header->directory->nodes[hash];
2520         efn;
2521         pefn = efn, efn = efn->next)
2522      {
2523         /* if it matches */
2524          if (eet_string_match(efn->name, name))
2525            {
2526               if (efn->data)
2527                 free(efn->data);
2528
2529               if (!pefn)
2530                 ef->header->directory->nodes[hash] = efn->next;
2531               else
2532                 pefn->next = efn->next;
2533
2534               if (efn->free_name)
2535                 free(efn->name);
2536
2537               eet_file_node_mp_free(efn);
2538               exists_already = 1;
2539               break;
2540            }
2541      }
2542    /* flags that writes are pending */
2543    if (exists_already)
2544      ef->writes_pending = 1;
2545
2546    UNLOCK_FILE(ef);
2547
2548    /* update access time */
2549    return exists_already;
2550 }
2551
2552 EAPI Eet_Dictionary *
2553 eet_dictionary_get(Eet_File *ef)
2554 {
2555    if (eet_check_pointer(ef))
2556      return NULL;
2557
2558    return ef->ed;
2559 }
2560
2561 EAPI char **
2562 eet_list(Eet_File   *ef,
2563          const char *glob,
2564          int        *count_ret)
2565 {
2566    Eet_File_Node *efn;
2567    char **list_ret = NULL;
2568    int list_count = 0;
2569    int list_count_alloc = 0;
2570    int i, num;
2571
2572    /* check to see its' an eet file pointer */
2573    if (eet_check_pointer(ef) || eet_check_header(ef) ||
2574        (!glob) ||
2575        ((ef->mode != EET_FILE_MODE_READ) &&
2576         (ef->mode != EET_FILE_MODE_READ_WRITE)))
2577      {
2578         if (count_ret)
2579           *count_ret = 0;
2580
2581         return NULL;
2582      }
2583
2584    if (!strcmp(glob, "*"))
2585      glob = NULL;
2586
2587    LOCK_FILE(ef);
2588
2589    /* loop through all entries */
2590    num = (1 << ef->header->directory->size);
2591    for (i = 0; i < num; i++)
2592      {
2593         for (efn = ef->header->directory->nodes[i]; efn; efn = efn->next)
2594           {
2595              /* if the entry matches the input glob
2596               * check for * explicitly, because on some systems, * isn't well
2597               * supported
2598               */
2599                if ((!glob) || !fnmatch(glob, efn->name, 0))
2600                  {
2601      /* add it to our list */
2602                      list_count++;
2603
2604      /* only realloc in 32 entry chunks */
2605                      if (list_count > list_count_alloc)
2606                        {
2607                           char **new_list = NULL;
2608
2609                           list_count_alloc += 64;
2610                           new_list =
2611                             realloc(list_ret, list_count_alloc * (sizeof(char *)));
2612                           if (!new_list)
2613                             {
2614                                free(list_ret);
2615
2616                                goto on_error;
2617                             }
2618
2619                           list_ret = new_list;
2620                        }
2621
2622      /* put pointer of name string in */
2623                      list_ret[list_count - 1] = efn->name;
2624                  }
2625           }
2626      }
2627
2628    UNLOCK_FILE(ef);
2629
2630    /* return count and list */
2631    if (count_ret)
2632      *count_ret = list_count;
2633
2634    return list_ret;
2635
2636 on_error:
2637    UNLOCK_FILE(ef);
2638
2639    if (count_ret)
2640      *count_ret = 0;
2641
2642    return NULL;
2643 }
2644
2645 EAPI int
2646 eet_num_entries(Eet_File *ef)
2647 {
2648    int i, num, ret = 0;
2649    Eet_File_Node *efn;
2650
2651    /* check to see its' an eet file pointer */
2652    if (eet_check_pointer(ef) || eet_check_header(ef) ||
2653        ((ef->mode != EET_FILE_MODE_READ) &&
2654         (ef->mode != EET_FILE_MODE_READ_WRITE)))
2655      return -1;
2656
2657    LOCK_FILE(ef);
2658
2659    /* loop through all entries */
2660    num = (1 << ef->header->directory->size);
2661    for (i = 0; i < num; i++)
2662      {
2663         for (efn = ef->header->directory->nodes[i]; efn; efn = efn->next)
2664           ret++;
2665      }
2666
2667    UNLOCK_FILE(ef);
2668
2669    return ret;
2670 }
2671
2672 static Eet_File_Node *
2673 find_node_by_name(Eet_File   *ef,
2674                   const char *name)
2675 {
2676    Eet_File_Node *efn;
2677    int hash;
2678
2679    /* get hash bucket this should be in */
2680    hash = _eet_hash_gen(name, ef->header->directory->size);
2681
2682    for (efn = ef->header->directory->nodes[hash]; efn; efn = efn->next)
2683      {
2684         if (eet_string_match(efn->name, name))
2685           return efn;
2686      }
2687
2688    return NULL;
2689 }
2690
2691 static int
2692 read_data_from_disk(Eet_File      *ef,
2693                     Eet_File_Node *efn,
2694                     void          *buf,
2695                     int            len)
2696 {
2697    if (efn->offset > ef->data_size)
2698      return 0;
2699
2700    if (!ef->data)
2701      return 0;
2702
2703    if ((efn->offset + len) > ef->data_size)
2704      return 0;
2705
2706    memcpy(buf, ef->data + efn->offset, len);
2707
2708    return len;
2709 }
2710