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