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