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