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