diff options
Diffstat (limited to '')
-rw-r--r-- | libraries/eina/src/lib/eina_file.c | 1187 |
1 files changed, 1187 insertions, 0 deletions
diff --git a/libraries/eina/src/lib/eina_file.c b/libraries/eina/src/lib/eina_file.c new file mode 100644 index 0000000..84b9e78 --- /dev/null +++ b/libraries/eina/src/lib/eina_file.c | |||
@@ -0,0 +1,1187 @@ | |||
1 | /* EINA - EFL data type library | ||
2 | * Copyright (C) 2007-2008 Jorge Luis Zapata Muga, Vincent Torri | ||
3 | * Copyright (C) 2010-2011 Cedric Bail | ||
4 | * | ||
5 | * This library is free software; you can redistribute it and/or | ||
6 | * modify it under the terms of the GNU Lesser General Public | ||
7 | * License as published by the Free Software Foundation; either | ||
8 | * version 2.1 of the License, or (at your option) any later version. | ||
9 | * | ||
10 | * This library is distributed in the hope that it will be useful, | ||
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
13 | * Lesser General Public License for more details. | ||
14 | * | ||
15 | * You should have received a copy of the GNU Lesser General Public | ||
16 | * License along with this library; | ||
17 | * if not, see <http://www.gnu.org/licenses/>. | ||
18 | */ | ||
19 | |||
20 | #ifdef HAVE_CONFIG_H | ||
21 | # include "config.h" | ||
22 | #endif | ||
23 | |||
24 | #ifdef HAVE_ALLOCA_H | ||
25 | # include <alloca.h> | ||
26 | #elif defined __GNUC__ | ||
27 | # define alloca __builtin_alloca | ||
28 | #elif defined _AIX | ||
29 | # define alloca __alloca | ||
30 | #elif defined _MSC_VER | ||
31 | # include <malloc.h> | ||
32 | # define alloca _alloca | ||
33 | #else | ||
34 | # include <stddef.h> | ||
35 | # ifdef __cplusplus | ||
36 | extern "C" | ||
37 | # endif | ||
38 | void *alloca (size_t); | ||
39 | #endif | ||
40 | |||
41 | #include <string.h> | ||
42 | #include <stddef.h> | ||
43 | #include <dirent.h> | ||
44 | #include <sys/types.h> | ||
45 | #include <sys/stat.h> | ||
46 | #include <unistd.h> | ||
47 | #include <sys/mman.h> | ||
48 | #include <fcntl.h> | ||
49 | |||
50 | #define PATH_DELIM '/' | ||
51 | |||
52 | #include "eina_config.h" | ||
53 | #include "eina_private.h" | ||
54 | |||
55 | /* undefs EINA_ARG_NONULL() so NULL checks are not compiled out! */ | ||
56 | #include "eina_safety_checks.h" | ||
57 | #include "eina_file.h" | ||
58 | #include "eina_stringshare.h" | ||
59 | #include "eina_hash.h" | ||
60 | #include "eina_list.h" | ||
61 | #include "eina_lock.h" | ||
62 | #include "eina_mmap.h" | ||
63 | |||
64 | #ifdef HAVE_ESCAPE_H | ||
65 | # include <Escape.h> | ||
66 | #endif | ||
67 | |||
68 | /*============================================================================* | ||
69 | * Local * | ||
70 | *============================================================================*/ | ||
71 | |||
72 | /** | ||
73 | * @cond LOCAL | ||
74 | */ | ||
75 | |||
76 | #ifndef EINA_LOG_COLOR_DEFAULT | ||
77 | #define EINA_LOG_COLOR_DEFAULT EINA_COLOR_CYAN | ||
78 | #endif | ||
79 | |||
80 | #ifdef ERR | ||
81 | #undef ERR | ||
82 | #endif | ||
83 | #define ERR(...) EINA_LOG_DOM_ERR(_eina_file_log_dom, __VA_ARGS__) | ||
84 | |||
85 | #ifdef WRN | ||
86 | #undef WRN | ||
87 | #endif | ||
88 | #define WRN(...) EINA_LOG_DOM_WARN(_eina_file_log_dom, __VA_ARGS__) | ||
89 | |||
90 | #ifdef DBG | ||
91 | #undef DBG | ||
92 | #endif | ||
93 | #define DBG(...) EINA_LOG_DOM_DBG(_eina_file_log_dom, __VA_ARGS__) | ||
94 | |||
95 | #define EINA_SMALL_PAGE 4096 | ||
96 | # define EINA_HUGE_PAGE 16 * 1024 * 1024 | ||
97 | |||
98 | typedef struct _Eina_File_Iterator Eina_File_Iterator; | ||
99 | typedef struct _Eina_File_Map Eina_File_Map; | ||
100 | |||
101 | struct _Eina_File_Iterator | ||
102 | { | ||
103 | Eina_Iterator iterator; | ||
104 | |||
105 | DIR *dirp; | ||
106 | int length; | ||
107 | |||
108 | char dir[1]; | ||
109 | }; | ||
110 | |||
111 | struct _Eina_File | ||
112 | { | ||
113 | const char *filename; | ||
114 | |||
115 | Eina_Hash *map; | ||
116 | Eina_Hash *rmap; | ||
117 | void *global_map; | ||
118 | |||
119 | Eina_Lock lock; | ||
120 | |||
121 | unsigned long long length; | ||
122 | time_t mtime; | ||
123 | ino_t inode; | ||
124 | #ifdef _STAT_VER_LINUX | ||
125 | unsigned long int mtime_nsec; | ||
126 | #endif | ||
127 | |||
128 | int refcount; | ||
129 | int global_refcount; | ||
130 | |||
131 | int fd; | ||
132 | |||
133 | Eina_Bool shared : 1; | ||
134 | Eina_Bool delete_me : 1; | ||
135 | }; | ||
136 | |||
137 | struct _Eina_File_Map | ||
138 | { | ||
139 | void *map; | ||
140 | |||
141 | unsigned long int offset; | ||
142 | unsigned long int length; | ||
143 | |||
144 | int refcount; | ||
145 | |||
146 | Eina_Bool hugetlb : 1; | ||
147 | }; | ||
148 | |||
149 | static Eina_Hash *_eina_file_cache = NULL; | ||
150 | static Eina_Lock _eina_file_lock_cache; | ||
151 | |||
152 | static int _eina_file_log_dom = -1; | ||
153 | |||
154 | /* | ||
155 | * This complex piece of code is needed due to possible race condition. | ||
156 | * The code and description of the issue can be found at : | ||
157 | * http://womble.decadent.org.uk/readdir_r-advisory.html | ||
158 | */ | ||
159 | static long | ||
160 | _eina_name_max(DIR *dirp) | ||
161 | { | ||
162 | long name_max; | ||
163 | |||
164 | #if defined(HAVE_FPATHCONF) && defined(HAVE_DIRFD) && defined(_PC_NAME_MAX) | ||
165 | name_max = fpathconf(dirfd(dirp), _PC_NAME_MAX); | ||
166 | |||
167 | if (name_max == -1) | ||
168 | { | ||
169 | # if defined(NAME_MAX) | ||
170 | name_max = (NAME_MAX > 255) ? NAME_MAX : 255; | ||
171 | # else | ||
172 | name_max = PATH_MAX; | ||
173 | # endif | ||
174 | } | ||
175 | #else | ||
176 | # if defined(NAME_MAX) | ||
177 | name_max = (NAME_MAX > 255) ? NAME_MAX : 255; | ||
178 | # else | ||
179 | # ifdef _PC_NAME_MAX | ||
180 | # warning "buffer size for readdir_r cannot be determined safely, best effort, but racy" | ||
181 | name_max = pathconf(dirp, _PC_NAME_MAX); | ||
182 | # else | ||
183 | # error "buffer size for readdir_r cannot be determined safely" | ||
184 | # endif | ||
185 | # endif | ||
186 | #endif | ||
187 | |||
188 | return name_max; | ||
189 | } | ||
190 | |||
191 | static size_t | ||
192 | _eina_dirent_buffer_size(DIR *dirp) | ||
193 | { | ||
194 | long name_max = _eina_name_max(dirp); | ||
195 | size_t name_end; | ||
196 | |||
197 | name_end = (size_t) offsetof(struct dirent, d_name) + name_max + 1; | ||
198 | |||
199 | return (name_end > sizeof (struct dirent) ? name_end : sizeof (struct dirent)); | ||
200 | } | ||
201 | |||
202 | static Eina_Bool | ||
203 | _eina_file_ls_iterator_next(Eina_File_Iterator *it, void **data) | ||
204 | { | ||
205 | struct dirent *dp; | ||
206 | char *name; | ||
207 | size_t length; | ||
208 | |||
209 | dp = alloca(_eina_dirent_buffer_size(it->dirp)); | ||
210 | |||
211 | do | ||
212 | { | ||
213 | if (readdir_r(it->dirp, dp, &dp)) | ||
214 | return EINA_FALSE; | ||
215 | if (dp == NULL) | ||
216 | return EINA_FALSE; | ||
217 | } | ||
218 | while ((dp->d_name[0] == '.') && | ||
219 | ((dp->d_name[1] == '\0') || | ||
220 | ((dp->d_name[1] == '.') && (dp->d_name[2] == '\0')))); | ||
221 | |||
222 | #ifdef _DIRENT_HAVE_D_NAMLEN | ||
223 | length = dp->d_namlen; | ||
224 | #else | ||
225 | length = strlen(dp->d_name); | ||
226 | #endif | ||
227 | name = alloca(length + 2 + it->length); | ||
228 | |||
229 | memcpy(name, it->dir, it->length); | ||
230 | memcpy(name + it->length, "/", 1); | ||
231 | memcpy(name + it->length + 1, dp->d_name, length + 1); | ||
232 | |||
233 | *data = (char *)eina_stringshare_add(name); | ||
234 | return EINA_TRUE; | ||
235 | } | ||
236 | |||
237 | static DIR * | ||
238 | _eina_file_ls_iterator_container(Eina_File_Iterator *it) | ||
239 | { | ||
240 | return it->dirp; | ||
241 | } | ||
242 | |||
243 | static void | ||
244 | _eina_file_ls_iterator_free(Eina_File_Iterator *it) | ||
245 | { | ||
246 | closedir(it->dirp); | ||
247 | |||
248 | EINA_MAGIC_SET(&it->iterator, 0); | ||
249 | free(it); | ||
250 | } | ||
251 | |||
252 | typedef struct _Eina_File_Direct_Iterator Eina_File_Direct_Iterator; | ||
253 | struct _Eina_File_Direct_Iterator | ||
254 | { | ||
255 | Eina_Iterator iterator; | ||
256 | |||
257 | DIR *dirp; | ||
258 | int length; | ||
259 | |||
260 | Eina_File_Direct_Info info; | ||
261 | |||
262 | char dir[1]; | ||
263 | }; | ||
264 | |||
265 | static Eina_Bool | ||
266 | _eina_file_direct_ls_iterator_next(Eina_File_Direct_Iterator *it, void **data) | ||
267 | { | ||
268 | struct dirent *dp; | ||
269 | size_t length; | ||
270 | |||
271 | dp = alloca(_eina_dirent_buffer_size(it->dirp)); | ||
272 | |||
273 | do | ||
274 | { | ||
275 | if (readdir_r(it->dirp, dp, &dp)) | ||
276 | return EINA_FALSE; | ||
277 | if (!dp) | ||
278 | return EINA_FALSE; | ||
279 | |||
280 | #ifdef _DIRENT_HAVE_D_NAMLEN | ||
281 | length = dp->d_namlen; | ||
282 | #else | ||
283 | length = strlen(dp->d_name); | ||
284 | #endif | ||
285 | if (it->info.name_start + length + 1 >= EINA_PATH_MAX) | ||
286 | continue; | ||
287 | } | ||
288 | while ((dp->d_name[0] == '.') && | ||
289 | ((dp->d_name[1] == '\0') || | ||
290 | ((dp->d_name[1] == '.') && (dp->d_name[2] == '\0')))); | ||
291 | |||
292 | memcpy(it->info.path + it->info.name_start, dp->d_name, length); | ||
293 | it->info.name_length = length; | ||
294 | it->info.path_length = it->info.name_start + length; | ||
295 | it->info.path[it->info.path_length] = '\0'; | ||
296 | |||
297 | #ifdef _DIRENT_HAVE_D_TYPE | ||
298 | switch (dp->d_type) | ||
299 | { | ||
300 | case DT_FIFO: | ||
301 | it->info.type = EINA_FILE_FIFO; | ||
302 | break; | ||
303 | case DT_CHR: | ||
304 | it->info.type = EINA_FILE_CHR; | ||
305 | break; | ||
306 | case DT_DIR: | ||
307 | it->info.type = EINA_FILE_DIR; | ||
308 | break; | ||
309 | case DT_BLK: | ||
310 | it->info.type = EINA_FILE_BLK; | ||
311 | break; | ||
312 | case DT_REG: | ||
313 | it->info.type = EINA_FILE_REG; | ||
314 | break; | ||
315 | case DT_LNK: | ||
316 | it->info.type = EINA_FILE_LNK; | ||
317 | break; | ||
318 | case DT_SOCK: | ||
319 | it->info.type = EINA_FILE_SOCK; | ||
320 | break; | ||
321 | case DT_WHT: | ||
322 | it->info.type = EINA_FILE_WHT; | ||
323 | break; | ||
324 | default: | ||
325 | it->info.type = EINA_FILE_UNKNOWN; | ||
326 | break; | ||
327 | } | ||
328 | #else | ||
329 | it->info.type = EINA_FILE_UNKNOWN; | ||
330 | #endif | ||
331 | |||
332 | *data = &it->info; | ||
333 | return EINA_TRUE; | ||
334 | } | ||
335 | |||
336 | static DIR * | ||
337 | _eina_file_direct_ls_iterator_container(Eina_File_Direct_Iterator *it) | ||
338 | { | ||
339 | return it->dirp; | ||
340 | } | ||
341 | |||
342 | static void | ||
343 | _eina_file_direct_ls_iterator_free(Eina_File_Direct_Iterator *it) | ||
344 | { | ||
345 | closedir(it->dirp); | ||
346 | |||
347 | EINA_MAGIC_SET(&it->iterator, 0); | ||
348 | free(it); | ||
349 | } | ||
350 | |||
351 | static Eina_Bool | ||
352 | _eina_file_stat_ls_iterator_next(Eina_File_Direct_Iterator *it, void **data) | ||
353 | { | ||
354 | struct stat st; | ||
355 | |||
356 | if (!_eina_file_direct_ls_iterator_next(it, data)) | ||
357 | return EINA_FALSE; | ||
358 | |||
359 | if (it->info.type == EINA_FILE_UNKNOWN) | ||
360 | { | ||
361 | #ifdef HAVE_FSTATAT | ||
362 | int fd; | ||
363 | |||
364 | fd = dirfd(it->dirp); | ||
365 | if (fstatat(fd, it->info.path + it->info.name_start, &st, 0)) | ||
366 | #else | ||
367 | if (stat(it->info.path, &st)) | ||
368 | #endif | ||
369 | it->info.type = EINA_FILE_UNKNOWN; | ||
370 | else | ||
371 | { | ||
372 | if (S_ISREG(st.st_mode)) | ||
373 | it->info.type = EINA_FILE_REG; | ||
374 | else if (S_ISDIR(st.st_mode)) | ||
375 | it->info.type = EINA_FILE_DIR; | ||
376 | else if (S_ISCHR(st.st_mode)) | ||
377 | it->info.type = EINA_FILE_CHR; | ||
378 | else if (S_ISBLK(st.st_mode)) | ||
379 | it->info.type = EINA_FILE_BLK; | ||
380 | else if (S_ISFIFO(st.st_mode)) | ||
381 | it->info.type = EINA_FILE_FIFO; | ||
382 | else if (S_ISLNK(st.st_mode)) | ||
383 | it->info.type = EINA_FILE_LNK; | ||
384 | else if (S_ISSOCK(st.st_mode)) | ||
385 | it->info.type = EINA_FILE_SOCK; | ||
386 | else | ||
387 | it->info.type = EINA_FILE_UNKNOWN; | ||
388 | } | ||
389 | } | ||
390 | |||
391 | return EINA_TRUE; | ||
392 | } | ||
393 | |||
394 | static void | ||
395 | _eina_file_real_close(Eina_File *file) | ||
396 | { | ||
397 | if (file->refcount != 0) return; | ||
398 | |||
399 | eina_hash_free(file->rmap); | ||
400 | eina_hash_free(file->map); | ||
401 | |||
402 | if (file->global_map != MAP_FAILED) | ||
403 | munmap(file->global_map, file->length); | ||
404 | |||
405 | close(file->fd); | ||
406 | |||
407 | free(file); | ||
408 | } | ||
409 | |||
410 | static void | ||
411 | _eina_file_map_close(Eina_File_Map *map) | ||
412 | { | ||
413 | munmap(map->map, map->length); | ||
414 | free(map); | ||
415 | } | ||
416 | |||
417 | static unsigned int | ||
418 | _eina_file_map_key_length(const void *key __UNUSED__) | ||
419 | { | ||
420 | return sizeof (unsigned long int) * 2; | ||
421 | } | ||
422 | |||
423 | static int | ||
424 | _eina_file_map_key_cmp(const unsigned long int *key1, int key1_length __UNUSED__, | ||
425 | const unsigned long int *key2, int key2_length __UNUSED__) | ||
426 | { | ||
427 | if (key1[0] - key2[0] == 0) return key1[1] - key2[1]; | ||
428 | return key1[0] - key2[0]; | ||
429 | } | ||
430 | |||
431 | static int | ||
432 | _eina_file_map_key_hash(const unsigned long int *key, int key_length __UNUSED__) | ||
433 | { | ||
434 | return eina_hash_int64(&key[0], sizeof (unsigned long int)) | ||
435 | ^ eina_hash_int64(&key[1], sizeof (unsigned long int)); | ||
436 | } | ||
437 | |||
438 | #ifndef MAP_POPULATE | ||
439 | static unsigned int | ||
440 | _eina_file_map_populate(char *map, unsigned int size, Eina_Bool hugetlb) | ||
441 | { | ||
442 | unsigned int r = 0xDEADBEEF; | ||
443 | unsigned int i; | ||
444 | unsigned int s; | ||
445 | |||
446 | s = hugetlb ? EINA_HUGE_PAGE : EINA_SMALL_PAGE; | ||
447 | |||
448 | for (i = 0; i < size; i += s) | ||
449 | r ^= map[i]; | ||
450 | |||
451 | r ^= map[size]; | ||
452 | |||
453 | return r; | ||
454 | } | ||
455 | #endif | ||
456 | |||
457 | static int | ||
458 | _eina_file_map_rule_apply(Eina_File_Populate rule, void *addr, unsigned long int size, Eina_Bool hugetlb) | ||
459 | { | ||
460 | int tmp = 42; | ||
461 | int flag = MADV_RANDOM; | ||
462 | |||
463 | switch (rule) | ||
464 | { | ||
465 | case EINA_FILE_RANDOM: flag = MADV_RANDOM; break; | ||
466 | case EINA_FILE_SEQUENTIAL: flag = MADV_SEQUENTIAL; break; | ||
467 | case EINA_FILE_POPULATE: flag = MADV_WILLNEED; break; | ||
468 | case EINA_FILE_WILLNEED: flag = MADV_WILLNEED; break; | ||
469 | } | ||
470 | |||
471 | madvise(addr, size, flag); | ||
472 | |||
473 | #ifndef MAP_POPULATE | ||
474 | if (rule == EINA_FILE_POPULATE) | ||
475 | tmp ^= _eina_file_map_populate(addr, size, hugetlb); | ||
476 | #else | ||
477 | (void) hugetlb; | ||
478 | #endif | ||
479 | |||
480 | return tmp; | ||
481 | } | ||
482 | |||
483 | static Eina_Bool | ||
484 | _eina_file_timestamp_compare(Eina_File *f, struct stat *st) | ||
485 | { | ||
486 | if (f->mtime != st->st_mtime) return EINA_FALSE; | ||
487 | if (f->length != (unsigned long long) st->st_size) return EINA_FALSE; | ||
488 | if (f->inode != st->st_ino) return EINA_FALSE; | ||
489 | #ifdef _STAT_VER_LINUX | ||
490 | # if (defined __USE_MISC && defined st_mtime) | ||
491 | if (f->mtime_nsec != (unsigned long int)st->st_mtim.tv_nsec) | ||
492 | return EINA_FALSE; | ||
493 | # else | ||
494 | if (f->mtime_nsec != (unsigned long int)st->st_mtimensec) | ||
495 | return EINA_FALSE; | ||
496 | # endif | ||
497 | #endif | ||
498 | return EINA_TRUE; | ||
499 | } | ||
500 | |||
501 | static void | ||
502 | slprintf(char *str, size_t size, const char *format, ...) | ||
503 | { | ||
504 | va_list ap; | ||
505 | |||
506 | va_start(ap, format); | ||
507 | |||
508 | vsnprintf(str, size, format, ap); | ||
509 | str[size - 1] = 0; | ||
510 | |||
511 | va_end(ap); | ||
512 | } | ||
513 | |||
514 | static char* | ||
515 | _eina_file_escape(const char* path, int* length) | ||
516 | { | ||
517 | char *result = strdup(path ? path : ""); | ||
518 | char *p = result; | ||
519 | char *q = result; | ||
520 | int len; | ||
521 | |||
522 | if (!result) | ||
523 | return NULL; | ||
524 | |||
525 | if (length) len = *length; | ||
526 | else len = strlen(result); | ||
527 | |||
528 | while ((p = strchr(p, '/'))) | ||
529 | { | ||
530 | // remove double `/' | ||
531 | if (p[1] == '/') | ||
532 | { | ||
533 | memmove(p, p + 1, --len - (p - result)); | ||
534 | result[len] = '\0'; | ||
535 | } | ||
536 | else | ||
537 | if (p[1] == '.' | ||
538 | && p[2] == '.') | ||
539 | { | ||
540 | // remove `/../' | ||
541 | if (p[3] == '/') | ||
542 | { | ||
543 | char tmp; | ||
544 | |||
545 | len -= p + 3 - q; | ||
546 | memmove(q, p + 3, len - (q - result)); | ||
547 | result[len] = '\0'; | ||
548 | p = q; | ||
549 | |||
550 | /* Update q correctly. */ | ||
551 | tmp = *p; | ||
552 | *p = '\0'; | ||
553 | q = strrchr(result, '/'); | ||
554 | if (!q) q = result; | ||
555 | *p = tmp; | ||
556 | } | ||
557 | else | ||
558 | // remove '/..$' | ||
559 | if (p[3] == '\0') | ||
560 | { | ||
561 | len -= p + 2 - q; | ||
562 | result[len] = '\0'; | ||
563 | q = p; | ||
564 | ++p; | ||
565 | } | ||
566 | else | ||
567 | { | ||
568 | q = p; | ||
569 | ++p; | ||
570 | } | ||
571 | } | ||
572 | else | ||
573 | { | ||
574 | q = p; | ||
575 | ++p; | ||
576 | } | ||
577 | } | ||
578 | |||
579 | if (length) | ||
580 | *length = len; | ||
581 | return result; | ||
582 | } | ||
583 | |||
584 | Eina_Bool | ||
585 | eina_file_init(void) | ||
586 | { | ||
587 | _eina_file_log_dom = eina_log_domain_register("eina_file", | ||
588 | EINA_LOG_COLOR_DEFAULT); | ||
589 | if (_eina_file_log_dom < 0) | ||
590 | { | ||
591 | EINA_LOG_ERR("Could not register log domain: eina_file"); | ||
592 | return EINA_FALSE; | ||
593 | } | ||
594 | |||
595 | _eina_file_cache = eina_hash_string_djb2_new(NULL); | ||
596 | if (!_eina_file_cache) | ||
597 | { | ||
598 | ERR("Could not create cache."); | ||
599 | eina_log_domain_unregister(_eina_file_log_dom); | ||
600 | _eina_file_log_dom = -1; | ||
601 | return EINA_FALSE; | ||
602 | } | ||
603 | |||
604 | eina_lock_new(&_eina_file_lock_cache); | ||
605 | |||
606 | return EINA_TRUE; | ||
607 | } | ||
608 | |||
609 | Eina_Bool | ||
610 | eina_file_shutdown(void) | ||
611 | { | ||
612 | if (eina_hash_population(_eina_file_cache) > 0) | ||
613 | { | ||
614 | Eina_Iterator *it; | ||
615 | const char *key; | ||
616 | |||
617 | it = eina_hash_iterator_key_new(_eina_file_cache); | ||
618 | EINA_ITERATOR_FOREACH(it, key) | ||
619 | ERR("File [%s] still open !", key); | ||
620 | eina_iterator_free(it); | ||
621 | } | ||
622 | |||
623 | eina_hash_free(_eina_file_cache); | ||
624 | |||
625 | eina_lock_free(&_eina_file_lock_cache); | ||
626 | |||
627 | eina_log_domain_unregister(_eina_file_log_dom); | ||
628 | _eina_file_log_dom = -1; | ||
629 | return EINA_TRUE; | ||
630 | } | ||
631 | |||
632 | /** | ||
633 | * @endcond | ||
634 | */ | ||
635 | |||
636 | /*============================================================================* | ||
637 | * Global * | ||
638 | *============================================================================*/ | ||
639 | |||
640 | /*============================================================================* | ||
641 | * API * | ||
642 | *============================================================================*/ | ||
643 | |||
644 | EAPI char * | ||
645 | eina_file_path_sanitize(const char *path) | ||
646 | { | ||
647 | char *result = NULL; | ||
648 | int len; | ||
649 | |||
650 | if (!path) return NULL; | ||
651 | |||
652 | len = strlen(path); | ||
653 | |||
654 | if (*path != '/') | ||
655 | { | ||
656 | char cwd[PATH_MAX]; | ||
657 | char *tmp = NULL; | ||
658 | |||
659 | tmp = getcwd(cwd, PATH_MAX); | ||
660 | if (!tmp) return NULL; | ||
661 | |||
662 | len += strlen(cwd) + 2; | ||
663 | tmp = alloca(sizeof (char) * len); | ||
664 | |||
665 | slprintf(tmp, len, "%s/%s", cwd, path); | ||
666 | |||
667 | result = tmp; | ||
668 | } | ||
669 | |||
670 | return _eina_file_escape(result ? result : path, &len); | ||
671 | } | ||
672 | |||
673 | EAPI Eina_Bool | ||
674 | eina_file_dir_list(const char *dir, | ||
675 | Eina_Bool recursive, | ||
676 | Eina_File_Dir_List_Cb cb, | ||
677 | void *data) | ||
678 | { | ||
679 | Eina_File_Direct_Info *info; | ||
680 | Eina_Iterator *it; | ||
681 | |||
682 | EINA_SAFETY_ON_NULL_RETURN_VAL(cb, EINA_FALSE); | ||
683 | EINA_SAFETY_ON_NULL_RETURN_VAL(dir, EINA_FALSE); | ||
684 | EINA_SAFETY_ON_TRUE_RETURN_VAL(dir[0] == '\0', EINA_FALSE); | ||
685 | |||
686 | it = eina_file_stat_ls(dir); | ||
687 | if (!it) | ||
688 | return EINA_FALSE; | ||
689 | |||
690 | EINA_ITERATOR_FOREACH(it, info) | ||
691 | { | ||
692 | cb(info->path + info->name_start, dir, data); | ||
693 | |||
694 | if (recursive == EINA_TRUE && info->type == EINA_FILE_DIR) | ||
695 | { | ||
696 | eina_file_dir_list(info->path, recursive, cb, data); | ||
697 | } | ||
698 | } | ||
699 | |||
700 | eina_iterator_free(it); | ||
701 | |||
702 | return EINA_TRUE; | ||
703 | } | ||
704 | |||
705 | EAPI Eina_Array * | ||
706 | eina_file_split(char *path) | ||
707 | { | ||
708 | Eina_Array *ea; | ||
709 | char *current; | ||
710 | size_t length; | ||
711 | |||
712 | EINA_SAFETY_ON_NULL_RETURN_VAL(path, NULL); | ||
713 | |||
714 | ea = eina_array_new(16); | ||
715 | |||
716 | if (!ea) | ||
717 | return NULL; | ||
718 | |||
719 | for (current = strchr(path, PATH_DELIM); | ||
720 | current; | ||
721 | path = current + 1, current = strchr(path, PATH_DELIM)) | ||
722 | { | ||
723 | length = current - path; | ||
724 | |||
725 | if (length <= 0) | ||
726 | continue; | ||
727 | |||
728 | eina_array_push(ea, path); | ||
729 | *current = '\0'; | ||
730 | } | ||
731 | |||
732 | if (*path != '\0') | ||
733 | eina_array_push(ea, path); | ||
734 | |||
735 | return ea; | ||
736 | } | ||
737 | |||
738 | EAPI Eina_Iterator * | ||
739 | eina_file_ls(const char *dir) | ||
740 | { | ||
741 | Eina_File_Iterator *it; | ||
742 | size_t length; | ||
743 | |||
744 | EINA_SAFETY_ON_NULL_RETURN_VAL(dir, NULL); | ||
745 | |||
746 | length = strlen(dir); | ||
747 | if (length < 1) | ||
748 | return NULL; | ||
749 | |||
750 | it = calloc(1, sizeof (Eina_File_Iterator) + length); | ||
751 | if (!it) | ||
752 | return NULL; | ||
753 | |||
754 | EINA_MAGIC_SET(&it->iterator, EINA_MAGIC_ITERATOR); | ||
755 | |||
756 | it->dirp = opendir(dir); | ||
757 | if (!it->dirp) | ||
758 | { | ||
759 | free(it); | ||
760 | return NULL; | ||
761 | } | ||
762 | |||
763 | memcpy(it->dir, dir, length + 1); | ||
764 | if (dir[length - 1] != '/') | ||
765 | it->length = length; | ||
766 | else | ||
767 | it->length = length - 1; | ||
768 | |||
769 | it->iterator.version = EINA_ITERATOR_VERSION; | ||
770 | it->iterator.next = FUNC_ITERATOR_NEXT(_eina_file_ls_iterator_next); | ||
771 | it->iterator.get_container = FUNC_ITERATOR_GET_CONTAINER( | ||
772 | _eina_file_ls_iterator_container); | ||
773 | it->iterator.free = FUNC_ITERATOR_FREE(_eina_file_ls_iterator_free); | ||
774 | |||
775 | return &it->iterator; | ||
776 | } | ||
777 | |||
778 | EAPI Eina_Iterator * | ||
779 | eina_file_direct_ls(const char *dir) | ||
780 | { | ||
781 | Eina_File_Direct_Iterator *it; | ||
782 | size_t length; | ||
783 | |||
784 | EINA_SAFETY_ON_NULL_RETURN_VAL(dir, NULL); | ||
785 | |||
786 | length = strlen(dir); | ||
787 | if (length < 1) | ||
788 | return NULL; | ||
789 | |||
790 | it = calloc(1, sizeof(Eina_File_Direct_Iterator) + length); | ||
791 | if (!it) | ||
792 | return NULL; | ||
793 | |||
794 | EINA_MAGIC_SET(&it->iterator, EINA_MAGIC_ITERATOR); | ||
795 | |||
796 | it->dirp = opendir(dir); | ||
797 | if (!it->dirp) | ||
798 | { | ||
799 | free(it); | ||
800 | return NULL; | ||
801 | } | ||
802 | |||
803 | if (length + _eina_name_max(it->dirp) + 2 >= EINA_PATH_MAX) | ||
804 | { | ||
805 | _eina_file_direct_ls_iterator_free(it); | ||
806 | return NULL; | ||
807 | } | ||
808 | |||
809 | memcpy(it->dir, dir, length + 1); | ||
810 | it->length = length; | ||
811 | |||
812 | memcpy(it->info.path, dir, length); | ||
813 | if (dir[length - 1] == '/') | ||
814 | it->info.name_start = length; | ||
815 | else | ||
816 | { | ||
817 | it->info.path[length] = '/'; | ||
818 | it->info.name_start = length + 1; | ||
819 | } | ||
820 | |||
821 | it->iterator.version = EINA_ITERATOR_VERSION; | ||
822 | it->iterator.next = FUNC_ITERATOR_NEXT(_eina_file_direct_ls_iterator_next); | ||
823 | it->iterator.get_container = FUNC_ITERATOR_GET_CONTAINER( | ||
824 | _eina_file_direct_ls_iterator_container); | ||
825 | it->iterator.free = FUNC_ITERATOR_FREE(_eina_file_direct_ls_iterator_free); | ||
826 | |||
827 | return &it->iterator; | ||
828 | } | ||
829 | |||
830 | EAPI Eina_Iterator * | ||
831 | eina_file_stat_ls(const char *dir) | ||
832 | { | ||
833 | Eina_File_Direct_Iterator *it; | ||
834 | size_t length; | ||
835 | |||
836 | EINA_SAFETY_ON_NULL_RETURN_VAL(dir, NULL); | ||
837 | |||
838 | length = strlen(dir); | ||
839 | if (length < 1) | ||
840 | return NULL; | ||
841 | |||
842 | it = calloc(1, sizeof(Eina_File_Direct_Iterator) + length); | ||
843 | if (!it) | ||
844 | return NULL; | ||
845 | |||
846 | EINA_MAGIC_SET(&it->iterator, EINA_MAGIC_ITERATOR); | ||
847 | |||
848 | it->dirp = opendir(dir); | ||
849 | if (!it->dirp) | ||
850 | { | ||
851 | free(it); | ||
852 | return NULL; | ||
853 | } | ||
854 | |||
855 | if (length + _eina_name_max(it->dirp) + 2 >= EINA_PATH_MAX) | ||
856 | { | ||
857 | _eina_file_direct_ls_iterator_free(it); | ||
858 | return NULL; | ||
859 | } | ||
860 | |||
861 | memcpy(it->dir, dir, length + 1); | ||
862 | it->length = length; | ||
863 | |||
864 | memcpy(it->info.path, dir, length); | ||
865 | if (dir[length - 1] == '/') | ||
866 | it->info.name_start = length; | ||
867 | else | ||
868 | { | ||
869 | it->info.path[length] = '/'; | ||
870 | it->info.name_start = length + 1; | ||
871 | } | ||
872 | |||
873 | it->iterator.version = EINA_ITERATOR_VERSION; | ||
874 | it->iterator.next = FUNC_ITERATOR_NEXT(_eina_file_stat_ls_iterator_next); | ||
875 | it->iterator.get_container = FUNC_ITERATOR_GET_CONTAINER( | ||
876 | _eina_file_direct_ls_iterator_container); | ||
877 | it->iterator.free = FUNC_ITERATOR_FREE(_eina_file_direct_ls_iterator_free); | ||
878 | |||
879 | return &it->iterator; | ||
880 | } | ||
881 | |||
882 | EAPI Eina_File * | ||
883 | eina_file_open(const char *path, Eina_Bool shared) | ||
884 | { | ||
885 | Eina_File *file; | ||
886 | Eina_File *n; | ||
887 | char *filename; | ||
888 | struct stat file_stat; | ||
889 | int fd = -1; | ||
890 | int flags; | ||
891 | |||
892 | EINA_SAFETY_ON_NULL_RETURN_VAL(path, NULL); | ||
893 | |||
894 | filename = eina_file_path_sanitize(path); | ||
895 | if (!filename) return NULL; | ||
896 | |||
897 | if (shared) | ||
898 | #ifdef HAVE_SHMOPEN | ||
899 | fd = shm_open(filename, O_RDONLY, S_IRWXU | S_IRWXG | S_IRWXO); | ||
900 | #else | ||
901 | goto on_error; | ||
902 | #endif | ||
903 | else | ||
904 | fd = open(filename, O_RDONLY, S_IRWXU | S_IRWXG | S_IRWXO); | ||
905 | |||
906 | if (fd < 0) goto on_error; | ||
907 | |||
908 | #ifdef HAVE_EXECVP | ||
909 | flags = fcntl(fd, F_GETFD); | ||
910 | if (flags == -1) | ||
911 | goto on_error; | ||
912 | |||
913 | flags |= FD_CLOEXEC; | ||
914 | if (fcntl(fd, F_SETFD, flags) == -1) | ||
915 | goto on_error; | ||
916 | #endif | ||
917 | |||
918 | if (fstat(fd, &file_stat)) | ||
919 | goto on_error; | ||
920 | |||
921 | eina_lock_take(&_eina_file_lock_cache); | ||
922 | |||
923 | file = eina_hash_find(_eina_file_cache, filename); | ||
924 | if ((file) && _eina_file_timestamp_compare(file, &file_stat)) | ||
925 | { | ||
926 | file->delete_me = EINA_TRUE; | ||
927 | eina_hash_del(_eina_file_cache, file->filename, file); | ||
928 | _eina_file_real_close(file); | ||
929 | file = NULL; | ||
930 | } | ||
931 | |||
932 | if (!file) | ||
933 | { | ||
934 | n = malloc(sizeof (Eina_File) + strlen(filename) + 1); | ||
935 | if (!n) | ||
936 | { | ||
937 | eina_lock_release(&_eina_file_lock_cache); | ||
938 | goto on_error; | ||
939 | } | ||
940 | |||
941 | n->filename = (char*) (n + 1); | ||
942 | strcpy((char*) n->filename, filename); | ||
943 | n->map = eina_hash_new(EINA_KEY_LENGTH(_eina_file_map_key_length), | ||
944 | EINA_KEY_CMP(_eina_file_map_key_cmp), | ||
945 | EINA_KEY_HASH(_eina_file_map_key_hash), | ||
946 | EINA_FREE_CB(_eina_file_map_close), | ||
947 | 3); | ||
948 | n->rmap = eina_hash_pointer_new(NULL); | ||
949 | n->global_map = MAP_FAILED; | ||
950 | n->global_refcount = 0; | ||
951 | n->length = file_stat.st_size; | ||
952 | n->mtime = file_stat.st_mtime; | ||
953 | #ifdef _STAT_VER_LINUX | ||
954 | # if (defined __USE_MISC && defined st_mtime) | ||
955 | n->mtime_nsec = (unsigned long int)file_stat.st_mtim.tv_nsec; | ||
956 | # else | ||
957 | n->mtime_nsec = (unsigned long int)file_stat.st_mtimensec; | ||
958 | # endif | ||
959 | #endif | ||
960 | n->inode = file_stat.st_ino; | ||
961 | n->refcount = 0; | ||
962 | n->fd = fd; | ||
963 | n->shared = shared; | ||
964 | n->delete_me = EINA_FALSE; | ||
965 | eina_lock_new(&n->lock); | ||
966 | eina_hash_direct_add(_eina_file_cache, n->filename, n); | ||
967 | } | ||
968 | else | ||
969 | { | ||
970 | close(fd); | ||
971 | n = file; | ||
972 | } | ||
973 | eina_lock_take(&n->lock); | ||
974 | n->refcount++; | ||
975 | eina_lock_release(&n->lock); | ||
976 | |||
977 | eina_lock_release(&_eina_file_lock_cache); | ||
978 | |||
979 | free(filename); | ||
980 | |||
981 | return n; | ||
982 | |||
983 | on_error: | ||
984 | free(filename); | ||
985 | if (fd >= 0) close(fd); | ||
986 | return NULL; | ||
987 | } | ||
988 | |||
989 | EAPI void | ||
990 | eina_file_close(Eina_File *file) | ||
991 | { | ||
992 | EINA_SAFETY_ON_NULL_RETURN(file); | ||
993 | |||
994 | eina_lock_take(&file->lock); | ||
995 | file->refcount--; | ||
996 | eina_lock_release(&file->lock); | ||
997 | |||
998 | if (file->refcount != 0) return; | ||
999 | eina_lock_take(&_eina_file_lock_cache); | ||
1000 | |||
1001 | eina_hash_del(_eina_file_cache, file->filename, file); | ||
1002 | _eina_file_real_close(file); | ||
1003 | |||
1004 | eina_lock_release(&_eina_file_lock_cache); | ||
1005 | } | ||
1006 | |||
1007 | EAPI size_t | ||
1008 | eina_file_size_get(Eina_File *file) | ||
1009 | { | ||
1010 | EINA_SAFETY_ON_NULL_RETURN_VAL(file, 0); | ||
1011 | return file->length; | ||
1012 | } | ||
1013 | |||
1014 | EAPI time_t | ||
1015 | eina_file_mtime_get(Eina_File *file) | ||
1016 | { | ||
1017 | EINA_SAFETY_ON_NULL_RETURN_VAL(file, 0); | ||
1018 | return file->mtime; | ||
1019 | } | ||
1020 | |||
1021 | EAPI const char * | ||
1022 | eina_file_filename_get(Eina_File *file) | ||
1023 | { | ||
1024 | EINA_SAFETY_ON_NULL_RETURN_VAL(file, NULL); | ||
1025 | return file->filename; | ||
1026 | } | ||
1027 | |||
1028 | EAPI void * | ||
1029 | eina_file_map_all(Eina_File *file, Eina_File_Populate rule) | ||
1030 | { | ||
1031 | int flags = MAP_SHARED; | ||
1032 | void *ret = NULL; | ||
1033 | |||
1034 | EINA_SAFETY_ON_NULL_RETURN_VAL(file, NULL); | ||
1035 | |||
1036 | // bsd people will lack this feature | ||
1037 | #ifdef MAP_POPULATE | ||
1038 | if (rule == EINA_FILE_POPULATE) flags |= MAP_POPULATE; | ||
1039 | #endif | ||
1040 | #ifdef MAP_HUGETLB | ||
1041 | if (file->length > EINA_HUGE_PAGE) flags |= MAP_HUGETLB; | ||
1042 | #endif | ||
1043 | |||
1044 | eina_mmap_safety_enabled_set(EINA_TRUE); | ||
1045 | eina_lock_take(&file->lock); | ||
1046 | if (file->global_map == MAP_FAILED) | ||
1047 | file->global_map = mmap(NULL, file->length, PROT_READ, flags, file->fd, 0); | ||
1048 | #ifdef MAP_HUGETLB | ||
1049 | if ((file->global_map == MAP_FAILED) && (flags & MAP_HUGETLB)) | ||
1050 | { | ||
1051 | flags &= ~MAP_HUGETLB; | ||
1052 | file->global_map = mmap(NULL, file->length, PROT_READ, flags, file->fd, 0); | ||
1053 | } | ||
1054 | #endif | ||
1055 | |||
1056 | if (file->global_map != MAP_FAILED) | ||
1057 | { | ||
1058 | Eina_Bool hugetlb = EINA_FALSE; | ||
1059 | |||
1060 | #ifdef MAP_HUGETLB | ||
1061 | hugetlb = !!(flags & MAP_HUGETLB); | ||
1062 | #endif | ||
1063 | _eina_file_map_rule_apply(rule, file->global_map, file->length, hugetlb); | ||
1064 | file->global_refcount++; | ||
1065 | ret = file->global_map; | ||
1066 | } | ||
1067 | |||
1068 | eina_lock_release(&file->lock); | ||
1069 | return ret; | ||
1070 | } | ||
1071 | |||
1072 | EAPI void * | ||
1073 | eina_file_map_new(Eina_File *file, Eina_File_Populate rule, | ||
1074 | unsigned long int offset, unsigned long int length) | ||
1075 | { | ||
1076 | Eina_File_Map *map; | ||
1077 | unsigned long int key[2]; | ||
1078 | |||
1079 | EINA_SAFETY_ON_NULL_RETURN_VAL(file, NULL); | ||
1080 | |||
1081 | if (offset > file->length) | ||
1082 | return NULL; | ||
1083 | if (offset + length > file->length) | ||
1084 | return NULL; | ||
1085 | |||
1086 | if (offset == 0 && length == file->length) | ||
1087 | return eina_file_map_all(file, rule); | ||
1088 | |||
1089 | key[0] = offset; | ||
1090 | key[1] = length; | ||
1091 | |||
1092 | eina_mmap_safety_enabled_set(EINA_TRUE); | ||
1093 | eina_lock_take(&file->lock); | ||
1094 | |||
1095 | map = eina_hash_find(file->map, &key); | ||
1096 | if (!map) | ||
1097 | { | ||
1098 | int flags = MAP_SHARED; | ||
1099 | |||
1100 | // bsd people will lack this feature | ||
1101 | #ifdef MAP_POPULATE | ||
1102 | if (rule == EINA_FILE_POPULATE) flags |= MAP_POPULATE; | ||
1103 | #endif | ||
1104 | #ifdef MAP_HUGETLB | ||
1105 | if (length > EINA_HUGE_PAGE) flags |= MAP_HUGETLB; | ||
1106 | #endif | ||
1107 | |||
1108 | map = malloc(sizeof (Eina_File_Map)); | ||
1109 | if (!map) goto on_error; | ||
1110 | |||
1111 | map->map = mmap(NULL, length, PROT_READ, flags, file->fd, offset); | ||
1112 | #ifdef MAP_HUGETLB | ||
1113 | if (map->map == MAP_FAILED && (flags & MAP_HUGETLB)) | ||
1114 | { | ||
1115 | flags &= ~MAP_HUGETLB; | ||
1116 | map->map = mmap(NULL, length, PROT_READ, flags, file->fd, offset); | ||
1117 | } | ||
1118 | |||
1119 | map->hugetlb = !!(flags & MAP_HUGETLB); | ||
1120 | #else | ||
1121 | map->hugetlb = EINA_FALSE; | ||
1122 | #endif | ||
1123 | map->offset = offset; | ||
1124 | map->length = length; | ||
1125 | map->refcount = 0; | ||
1126 | |||
1127 | if (map->map == MAP_FAILED) goto on_error; | ||
1128 | |||
1129 | eina_hash_add(file->map, &key, map); | ||
1130 | eina_hash_direct_add(file->rmap, map->map, map); | ||
1131 | } | ||
1132 | |||
1133 | map->refcount++; | ||
1134 | |||
1135 | _eina_file_map_rule_apply(rule, map->map, length, map->hugetlb); | ||
1136 | |||
1137 | eina_lock_release(&file->lock); | ||
1138 | |||
1139 | return map->map; | ||
1140 | |||
1141 | on_error: | ||
1142 | free(map); | ||
1143 | eina_lock_release(&file->lock); | ||
1144 | |||
1145 | return NULL; | ||
1146 | } | ||
1147 | |||
1148 | EAPI void | ||
1149 | eina_file_map_free(Eina_File *file, void *map) | ||
1150 | { | ||
1151 | EINA_SAFETY_ON_NULL_RETURN(file); | ||
1152 | |||
1153 | eina_lock_take(&file->lock); | ||
1154 | |||
1155 | if (file->global_map == map) | ||
1156 | { | ||
1157 | file->global_refcount--; | ||
1158 | |||
1159 | if (file->global_refcount > 0) goto on_exit; | ||
1160 | |||
1161 | munmap(file->global_map, file->length); | ||
1162 | file->global_map = MAP_FAILED; | ||
1163 | } | ||
1164 | else | ||
1165 | { | ||
1166 | Eina_File_Map *em; | ||
1167 | unsigned long int key[2]; | ||
1168 | |||
1169 | em = eina_hash_find(file->rmap, &map); | ||
1170 | if (!em) return ; | ||
1171 | |||
1172 | em->refcount--; | ||
1173 | |||
1174 | if (em->refcount > 0) goto on_exit; | ||
1175 | |||
1176 | key[0] = em->offset; | ||
1177 | key[1] = em->length; | ||
1178 | |||
1179 | eina_hash_del(file->rmap, &map, em); | ||
1180 | eina_hash_del(file->map, &key, em); | ||
1181 | } | ||
1182 | |||
1183 | on_exit: | ||
1184 | eina_lock_release(&file->lock); | ||
1185 | } | ||
1186 | |||
1187 | |||