aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/libraries/eina/src/lib/eina_file.c
diff options
context:
space:
mode:
Diffstat (limited to 'libraries/eina/src/lib/eina_file.c')
-rw-r--r--libraries/eina/src/lib/eina_file.c1187
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
36extern "C"
37# endif
38void *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
98typedef struct _Eina_File_Iterator Eina_File_Iterator;
99typedef struct _Eina_File_Map Eina_File_Map;
100
101struct _Eina_File_Iterator
102{
103 Eina_Iterator iterator;
104
105 DIR *dirp;
106 int length;
107
108 char dir[1];
109};
110
111struct _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
137struct _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
149static Eina_Hash *_eina_file_cache = NULL;
150static Eina_Lock _eina_file_lock_cache;
151
152static 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 */
159static 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
191static 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
202static 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
237static DIR *
238_eina_file_ls_iterator_container(Eina_File_Iterator *it)
239{
240 return it->dirp;
241}
242
243static 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
252typedef struct _Eina_File_Direct_Iterator Eina_File_Direct_Iterator;
253struct _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
265static 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
336static DIR *
337_eina_file_direct_ls_iterator_container(Eina_File_Direct_Iterator *it)
338{
339 return it->dirp;
340}
341
342static 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
351static 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
394static 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
410static void
411_eina_file_map_close(Eina_File_Map *map)
412{
413 munmap(map->map, map->length);
414 free(map);
415}
416
417static unsigned int
418_eina_file_map_key_length(const void *key __UNUSED__)
419{
420 return sizeof (unsigned long int) * 2;
421}
422
423static 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
431static 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
439static 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
457static 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
483static 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
501static void
502slprintf(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
514static 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
584Eina_Bool
585eina_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
609Eina_Bool
610eina_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
644EAPI char *
645eina_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
673EAPI Eina_Bool
674eina_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
705EAPI Eina_Array *
706eina_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
738EAPI Eina_Iterator *
739eina_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
778EAPI Eina_Iterator *
779eina_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
830EAPI Eina_Iterator *
831eina_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
882EAPI Eina_File *
883eina_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
989EAPI void
990eina_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
1007EAPI size_t
1008eina_file_size_get(Eina_File *file)
1009{
1010 EINA_SAFETY_ON_NULL_RETURN_VAL(file, 0);
1011 return file->length;
1012}
1013
1014EAPI time_t
1015eina_file_mtime_get(Eina_File *file)
1016{
1017 EINA_SAFETY_ON_NULL_RETURN_VAL(file, 0);
1018 return file->mtime;
1019}
1020
1021EAPI const char *
1022eina_file_filename_get(Eina_File *file)
1023{
1024 EINA_SAFETY_ON_NULL_RETURN_VAL(file, NULL);
1025 return file->filename;
1026}
1027
1028EAPI void *
1029eina_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
1072EAPI void *
1073eina_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
1148EAPI void
1149eina_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