Eina : eina_file : eina_file_size_get() should return a size_t
[profile/ivi/eina.git] / src / lib / eina_file.c
1 /* EINA - EFL data type library
2  * Copyright (C) 2007-2008 Jorge Luis Zapata Muga, Vincent Torri
3  * Copyright (C) 2010 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 #ifdef __sun
53 # ifndef NAME_MAX
54 #  define NAME_MAX 255
55 # endif
56 #endif
57
58 #include "eina_config.h"
59 #include "eina_private.h"
60
61 /* undefs EINA_ARG_NONULL() so NULL checks are not compiled out! */
62 #include "eina_safety_checks.h"
63 #include "eina_file.h"
64 #include "eina_stringshare.h"
65 #include "eina_hash.h"
66 #include "eina_list.h"
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    unsigned long long length;
120    time_t mtime;
121    ino_t inode;
122
123    int refcount;
124    int global_refcount;
125
126    int fd;
127
128    Eina_Bool shared : 1;
129    Eina_Bool delete_me : 1;
130 };
131
132 struct _Eina_File_Map
133 {
134    void *map;
135
136    unsigned long int offset;
137    unsigned long int length;
138
139    int refcount;
140 };
141
142 static Eina_Hash *_eina_file_cache = NULL;
143 static Eina_List *_eina_file_cache_lru = NULL;
144 static Eina_List *_eina_file_cache_delete = NULL;
145
146 static int _eina_file_log_dom = -1;
147
148 /*
149  * This complex piece of code is needed due to possible race condition.
150  * The code and description of the issue can be found at :
151  * http://womble.decadent.org.uk/readdir_r-advisory.html
152  */
153 static size_t
154 _eina_dirent_buffer_size(DIR *dirp)
155 {
156    long name_max;
157    size_t name_end;
158
159 #if defined(HAVE_FPATHCONF) && defined(HAVE_DIRFD) && defined(_PC_NAME_MAX)
160    name_max = fpathconf(dirfd(dirp), _PC_NAME_MAX);
161
162    if (name_max == -1)
163      {
164 # if defined(NAME_MAX)
165         name_max = (NAME_MAX > 255) ? NAME_MAX : 255;
166 # else
167         name_max = PATH_MAX;
168 # endif
169      }
170 #else
171 # if defined(NAME_MAX)
172    name_max = (NAME_MAX > 255) ? NAME_MAX : 255;
173 # else
174 #  ifdef _PC_NAME_MAX
175 #   warning "buffer size for readdir_r cannot be determined safely, best effort, but racy"
176    name_max = pathconf(dirp, _PC_NAME_MAX);
177 #  else
178 #   error "buffer size for readdir_r cannot be determined safely"
179 #  endif
180 # endif
181 #endif
182    name_end = (size_t) offsetof(struct dirent, d_name) + name_max + 1;
183
184    return (name_end > sizeof (struct dirent) ? name_end : sizeof (struct dirent));
185 }
186
187 static Eina_Bool
188 _eina_file_ls_iterator_next(Eina_File_Iterator *it, void **data)
189 {
190    struct dirent *dp;
191    char *name;
192    size_t length;
193
194    dp = alloca(_eina_dirent_buffer_size(it->dirp));
195
196    do
197      {
198         if (readdir_r(it->dirp, dp, &dp))
199           return EINA_FALSE;
200         if (dp == NULL)
201           return EINA_FALSE;
202      }
203    while ((dp->d_name[0] == '.') &&
204           ((dp->d_name[1] == '\0') ||
205            ((dp->d_name[1] == '.') && (dp->d_name[2] == '\0'))));
206
207 #ifdef _DIRENT_HAVE_D_NAMLEN
208    length = dp->d_namlen;
209 #else
210    length = strlen(dp->d_name);
211 #endif
212    name = alloca(length + 2 + it->length);
213
214    memcpy(name,                  it->dir,    it->length);
215    memcpy(name + it->length,     "/",        1);
216    memcpy(name + it->length + 1, dp->d_name, length + 1);
217
218    *data = (char *)eina_stringshare_add(name);
219    return EINA_TRUE;
220 }
221
222 static DIR *
223 _eina_file_ls_iterator_container(Eina_File_Iterator *it)
224 {
225    return it->dirp;
226 }
227
228 static void
229 _eina_file_ls_iterator_free(Eina_File_Iterator *it)
230 {
231    closedir(it->dirp);
232
233    EINA_MAGIC_SET(&it->iterator, 0);
234    free(it);
235 }
236
237 typedef struct _Eina_File_Direct_Iterator Eina_File_Direct_Iterator;
238 struct _Eina_File_Direct_Iterator
239 {
240    Eina_Iterator iterator;
241
242    DIR *dirp;
243    int length;
244
245    Eina_File_Direct_Info info;
246
247    char dir[1];
248 };
249
250 static Eina_Bool
251 _eina_file_direct_ls_iterator_next(Eina_File_Direct_Iterator *it, void **data)
252 {
253    struct dirent *dp;
254    size_t length;
255
256    dp = alloca(_eina_dirent_buffer_size(it->dirp));
257
258    do
259      {
260         if (readdir_r(it->dirp, dp, &dp))
261            return EINA_FALSE;
262         if (!dp)
263            return EINA_FALSE;
264
265 #ifdef _DIRENT_HAVE_D_NAMLEN
266         length = dp->d_namlen;
267 #else
268         length = strlen(dp->d_name);
269 #endif
270         if (it->info.name_start + length + 1 >= EINA_PATH_MAX)
271            continue;
272      }
273    while ((dp->d_name[0] == '.') &&
274           ((dp->d_name[1] == '\0') ||
275            ((dp->d_name[1] == '.') && (dp->d_name[2] == '\0'))));
276
277    memcpy(it->info.path + it->info.name_start, dp->d_name, length);
278    it->info.name_length = length;
279    it->info.path_length = it->info.name_start + length;
280    it->info.path[it->info.path_length] = '\0';
281
282 #ifdef _DIRENT_HAVE_D_TYPE
283    switch (dp->d_type)
284      {
285      case DT_FIFO:
286        it->info.type = EINA_FILE_FIFO;
287        break;
288      case DT_CHR:
289        it->info.type = EINA_FILE_CHR;
290        break;
291      case DT_DIR:
292        it->info.type = EINA_FILE_DIR;
293        break;
294      case DT_BLK:
295        it->info.type = EINA_FILE_BLK;
296        break;
297      case DT_REG:
298        it->info.type = EINA_FILE_REG;
299        break;
300      case DT_LNK:
301        it->info.type = EINA_FILE_LNK;
302        break;
303      case DT_SOCK:
304        it->info.type = EINA_FILE_SOCK;
305        break;
306      case DT_WHT:
307        it->info.type = EINA_FILE_WHT;
308        break;
309      default:
310        it->info.type = EINA_FILE_UNKNOWN;
311        break;
312      }
313 #else
314    it->info.type = EINA_FILE_UNKNOWN;
315 #endif
316
317    *data = &it->info;
318    return EINA_TRUE;
319 }
320
321 static DIR *
322 _eina_file_direct_ls_iterator_container(Eina_File_Direct_Iterator *it)
323 {
324    return it->dirp;
325 }
326
327 static void
328 _eina_file_direct_ls_iterator_free(Eina_File_Direct_Iterator *it)
329 {
330    closedir(it->dirp);
331
332    EINA_MAGIC_SET(&it->iterator, 0);
333    free(it);
334 }
335
336 static Eina_Bool
337 _eina_file_stat_ls_iterator_next(Eina_File_Direct_Iterator *it, void **data)
338 {
339    struct stat st;
340
341    if (!_eina_file_direct_ls_iterator_next(it, data))
342      return EINA_FALSE;
343
344    if (it->info.type == EINA_FILE_UNKNOWN)
345      {
346 #ifdef HAVE_FSTATAT
347         int fd;
348
349         fd = dirfd(it->dirp);
350         if (fstatat(fd, it->info.path + it->info.name_start, &st, 0))
351 #else
352         if (stat(it->info.path, &st))
353 #endif
354           it->info.type = EINA_FILE_UNKNOWN;
355         else
356           {
357              if (S_ISREG(st.st_mode))
358                it->info.type = EINA_FILE_REG;
359              else if (S_ISDIR(st.st_mode))
360                it->info.type = EINA_FILE_DIR;
361              else if (S_ISCHR(st.st_mode))
362                it->info.type = EINA_FILE_CHR;
363              else if (S_ISBLK(st.st_mode))
364                it->info.type = EINA_FILE_BLK;
365              else if (S_ISFIFO(st.st_mode))
366                it->info.type = EINA_FILE_FIFO;
367              else if (S_ISLNK(st.st_mode))
368                it->info.type = EINA_FILE_LNK;
369              else if (S_ISSOCK(st.st_mode))
370                it->info.type = EINA_FILE_SOCK;
371              else
372                it->info.type = EINA_FILE_UNKNOWN;
373           }
374      }
375
376    return EINA_TRUE;
377 }
378
379 static void
380 _eina_file_real_close(Eina_File *file)
381 {
382    if (file->refcount != 0) return ;
383
384    eina_hash_free(file->rmap);
385    eina_hash_free(file->map);
386
387    if (file->global_map != MAP_FAILED)
388      munmap(file->global_map, file->length);
389
390    close(file->fd);
391
392    free(file);
393 }
394
395 static void
396 _eina_file_map_close(Eina_File_Map *map)
397 {
398    munmap(map->map, map->length);
399    free(map);
400 }
401
402 static unsigned int
403 _eina_file_map_key_length(const void *key __UNUSED__)
404 {
405    return sizeof (unsigned long int) * 2;
406 }
407
408 static int
409 _eina_file_map_key_cmp(const unsigned long int *key1, int key1_length __UNUSED__,
410                        const unsigned long int *key2, int key2_length __UNUSED__)
411 {
412    if (key1[0] - key2[0] == 0) return key1[1] - key2[1];
413    return key1[0] - key2[0];
414 }
415
416 static int
417 _eina_file_map_key_hash(const unsigned long int *key, int key_length __UNUSED__)
418 {
419    return eina_hash_int64(&key[0], sizeof (unsigned long int))
420      ^ eina_hash_int64(&key[1], sizeof (unsigned long int));
421 }
422
423 #ifndef MAP_POPULATE
424 static int
425 _eina_file_map_populate(char *map, unsigned int size)
426 {
427    int r = 0xDEADBEEF;
428    int i;
429    int s;
430
431 #ifdef MAP_HUGETLB
432    s = size > EINA_HUGE_PAGE ? EINA_HUGE_PAGE : EINA_SMALL_PAGE;
433 #else
434    s = EINA_SMALL_PAGE;
435 #endif
436
437    for (i = 0; i < size; i += s)
438      r ^= map[i];
439
440    r ^= map[size];
441
442    return r;
443 }
444 #endif
445
446 static int
447 _eina_file_map_rule_apply(Eina_File_Populate rule, void *addr, unsigned long int size)
448 {
449    int tmp = 42;
450    int flag = MADV_RANDOM;
451
452    switch (rule)
453      {
454       case EINA_FILE_RANDOM: flag = MADV_RANDOM; break;
455       case EINA_FILE_SEQUENTIAL: flag = MADV_SEQUENTIAL; break;
456       case EINA_FILE_POPULATE: flag = MADV_WILLNEED; break;
457       case EINA_FILE_WILLNEED: flag = MADV_WILLNEED; break;
458      }
459
460    madvise(addr, size, flag);
461
462 #ifndef MAP_POPULATE
463    if (rule == EINA_FILE_POPULATE)
464      tmp ^= _eina_file_map_populate(addr, size);
465 #endif
466
467    return tmp;
468 }
469
470 Eina_Bool
471 eina_file_init(void)
472 {
473    _eina_file_log_dom = eina_log_domain_register("eina_file",
474                                                  EINA_LOG_COLOR_DEFAULT);
475    if (_eina_file_log_dom < 0)
476      {
477         EINA_LOG_ERR("Could not register log domain: eina_file");
478         return EINA_FALSE;
479      }
480
481    _eina_file_cache = eina_hash_string_djb2_new(EINA_FREE_CB(_eina_file_real_close));
482    if (!_eina_file_cache)
483      {
484         ERR("Could not create cache.");
485         eina_log_domain_unregister(_eina_file_log_dom);
486         _eina_file_log_dom = -1;
487         return EINA_FALSE;
488      }
489
490    return EINA_TRUE;
491 }
492
493 Eina_Bool
494 eina_file_shutdown(void)
495 {
496    Eina_File *f;
497    Eina_List *l;
498
499    EINA_LIST_FREE(_eina_file_cache_delete, f)
500      _eina_file_real_close(f);
501
502    EINA_LIST_FOREACH(_eina_file_cache_lru, l, f)
503      eina_hash_del(_eina_file_cache, f->filename, f);
504
505    if (eina_hash_population(_eina_file_cache) > 0)
506      {
507         Eina_Iterator *it;
508         const char *key;
509
510         it = eina_hash_iterator_key_new(_eina_file_cache);
511         EINA_ITERATOR_FOREACH(it, key)
512           ERR("File [%s] still open !", key);
513         eina_iterator_free(it);
514      }
515
516    eina_hash_free(_eina_file_cache);
517
518    eina_log_domain_unregister(_eina_file_log_dom);
519    _eina_file_log_dom = -1;
520    return EINA_TRUE;
521 }
522
523 /**
524  * @endcond
525  */
526
527 /*============================================================================*
528  *                                 Global                                     *
529  *============================================================================*/
530
531 /*============================================================================*
532  *                                   API                                      *
533  *============================================================================*/
534
535 EAPI Eina_Bool
536 eina_file_dir_list(const char *dir,
537                    Eina_Bool recursive,
538                    Eina_File_Dir_List_Cb cb,
539                    void *data)
540 {
541    Eina_File_Direct_Info *info;
542    Eina_Iterator *it;
543
544    EINA_SAFETY_ON_NULL_RETURN_VAL(cb,  EINA_FALSE);
545    EINA_SAFETY_ON_NULL_RETURN_VAL(dir, EINA_FALSE);
546    EINA_SAFETY_ON_TRUE_RETURN_VAL(dir[0] == '\0', EINA_FALSE);
547
548    it = eina_file_stat_ls(dir);
549    if (!it)
550       return EINA_FALSE;
551
552    EINA_ITERATOR_FOREACH(it, info)
553      {
554         cb(info->path + info->name_start, dir, data);
555
556         if (recursive == EINA_TRUE && info->type == EINA_FILE_DIR)
557           {
558              eina_file_dir_list(info->path, recursive, cb, data);
559           }
560      }
561
562    eina_iterator_free(it);
563
564    return EINA_TRUE;
565 }
566
567 EAPI Eina_Array *
568 eina_file_split(char *path)
569 {
570    Eina_Array *ea;
571    char *current;
572    size_t length;
573
574    EINA_SAFETY_ON_NULL_RETURN_VAL(path, NULL);
575
576    ea = eina_array_new(16);
577
578    if (!ea)
579       return NULL;
580
581    for (current = strchr(path, PATH_DELIM);
582         current;
583         path = current + 1, current = strchr(path, PATH_DELIM))
584      {
585         length = current - path;
586
587         if (length <= 0)
588            continue;
589
590         eina_array_push(ea, path);
591         *current = '\0';
592      }
593
594    if (*path != '\0')
595         eina_array_push(ea, path);
596
597    return ea;
598 }
599
600 EAPI Eina_Iterator *
601 eina_file_ls(const char *dir)
602 {
603    Eina_File_Iterator *it;
604    size_t length;
605
606    if (!dir)
607       return NULL;
608
609    length = strlen(dir);
610    if (length < 1)
611       return NULL;
612
613    it = calloc(1, sizeof (Eina_File_Iterator) + length);
614    if (!it)
615       return NULL;
616
617    EINA_MAGIC_SET(&it->iterator, EINA_MAGIC_ITERATOR);
618
619    it->dirp = opendir(dir);
620    if (!it->dirp)
621      {
622         free(it);
623         return NULL;
624      }
625
626    memcpy(it->dir, dir, length + 1);
627    if (dir[length - 1] != '/')
628       it->length = length;
629    else
630       it->length = length - 1;
631
632    it->iterator.version = EINA_ITERATOR_VERSION;
633    it->iterator.next = FUNC_ITERATOR_NEXT(_eina_file_ls_iterator_next);
634    it->iterator.get_container = FUNC_ITERATOR_GET_CONTAINER(
635          _eina_file_ls_iterator_container);
636    it->iterator.free = FUNC_ITERATOR_FREE(_eina_file_ls_iterator_free);
637
638    return &it->iterator;
639 }
640
641 EAPI Eina_Iterator *
642 eina_file_direct_ls(const char *dir)
643 {
644    Eina_File_Direct_Iterator *it;
645    size_t length;
646
647    if (!dir)
648       return NULL;
649
650    length = strlen(dir);
651    if (length < 1)
652       return NULL;
653
654    if (length + NAME_MAX + 2 >= EINA_PATH_MAX)
655       return NULL;
656
657    it = calloc(1, sizeof(Eina_File_Direct_Iterator) + length);
658    if (!it)
659       return NULL;
660
661    EINA_MAGIC_SET(&it->iterator, EINA_MAGIC_ITERATOR);
662
663    it->dirp = opendir(dir);
664    if (!it->dirp)
665      {
666         free(it);
667         return NULL;
668      }
669
670    memcpy(it->dir,       dir, length + 1);
671    it->length = length;
672
673    memcpy(it->info.path, dir, length);
674    if (dir[length - 1] == '/')
675       it->info.name_start = length;
676    else
677      {
678         it->info.path[length] = '/';
679         it->info.name_start = length + 1;
680      }
681
682    it->iterator.version = EINA_ITERATOR_VERSION;
683    it->iterator.next = FUNC_ITERATOR_NEXT(_eina_file_direct_ls_iterator_next);
684    it->iterator.get_container = FUNC_ITERATOR_GET_CONTAINER(
685          _eina_file_direct_ls_iterator_container);
686    it->iterator.free = FUNC_ITERATOR_FREE(_eina_file_direct_ls_iterator_free);
687
688    return &it->iterator;
689 }
690
691 EAPI Eina_Iterator *
692 eina_file_stat_ls(const char *dir)
693 {
694    Eina_File_Direct_Iterator *it;
695    size_t length;
696
697    if (!dir)
698       return NULL;
699
700    length = strlen(dir);
701    if (length < 1)
702       return NULL;
703
704    if (length + NAME_MAX + 2 >= EINA_PATH_MAX)
705       return NULL;
706
707    it = calloc(1, sizeof(Eina_File_Direct_Iterator) + length);
708    if (!it)
709       return NULL;
710
711    EINA_MAGIC_SET(&it->iterator, EINA_MAGIC_ITERATOR);
712
713    it->dirp = opendir(dir);
714    if (!it->dirp)
715      {
716         free(it);
717         return NULL;
718      }
719
720    memcpy(it->dir,       dir, length + 1);
721    it->length = length;
722
723    memcpy(it->info.path, dir, length);
724    if (dir[length - 1] == '/')
725       it->info.name_start = length;
726    else
727      {
728         it->info.path[length] = '/';
729         it->info.name_start = length + 1;
730      }
731
732    it->iterator.version = EINA_ITERATOR_VERSION;
733    it->iterator.next = FUNC_ITERATOR_NEXT(_eina_file_stat_ls_iterator_next);
734    it->iterator.get_container = FUNC_ITERATOR_GET_CONTAINER(
735          _eina_file_direct_ls_iterator_container);
736    it->iterator.free = FUNC_ITERATOR_FREE(_eina_file_direct_ls_iterator_free);
737
738    return &it->iterator;
739 }
740
741 EAPI Eina_File *
742 eina_file_open(const char *filename, Eina_Bool shared)
743 {
744    Eina_File *file;
745    Eina_File *n;
746    struct stat file_stat;
747    int fd;
748    int flags;
749    Eina_Bool create = EINA_FALSE;
750
751    /*
752      FIXME: always open absolute path
753      (need to fix filename according to current directory)
754    */
755
756    if (shared)
757      fd = shm_open(filename, O_RDONLY, S_IRWXU | S_IRWXG | S_IRWXO);
758    else
759      fd = open(filename, O_RDONLY, S_IRWXU | S_IRWXG | S_IRWXO);
760
761    if (fd < 0) return NULL;
762
763    flags = fcntl(fd, F_GETFD);
764    if (flags == -1)
765      goto on_error;
766
767    flags |= FD_CLOEXEC;
768    if (fcntl(fd, F_SETFD, flags) == -1)
769      goto on_error;
770
771    if (fstat(fd, &file_stat))
772      goto on_error;
773
774    file = eina_hash_find(_eina_file_cache, filename);
775    if (file && (file->mtime != file_stat.st_mtime
776                 || file->length != (unsigned long long) file_stat.st_size
777                 || file->inode != file_stat.st_ino))
778      {
779         create = EINA_TRUE;
780
781         if (file->refcount == 0)
782           {
783              _eina_file_cache_lru = eina_list_remove(_eina_file_cache_lru, file);
784              eina_hash_del(_eina_file_cache, file->filename, file);
785
786              file = NULL;
787           }
788         else if (!file->delete_me)
789           {
790              file->delete_me = EINA_TRUE;
791              _eina_file_cache_delete = eina_list_prepend(_eina_file_cache_delete, file);
792           }
793      }
794
795    if (!file || create)
796      {
797         n = malloc(sizeof (Eina_File) + strlen(filename) + 1);
798         if (!n) goto on_error;
799
800         n->filename = (char*) (n + 1);
801         strcpy((char*) n->filename, filename);
802         n->map = eina_hash_new(EINA_KEY_LENGTH(_eina_file_map_key_length),
803                                EINA_KEY_CMP(_eina_file_map_key_cmp),
804                                EINA_KEY_HASH(_eina_file_map_key_hash),
805                                EINA_FREE_CB(_eina_file_map_close),
806                                3);
807         n->rmap = eina_hash_pointer_new(NULL);
808         n->global_map = MAP_FAILED;
809         n->global_refcount = 0;
810         n->length = file_stat.st_size;
811         n->mtime = file_stat.st_mtime;
812         n->inode = file_stat.st_ino;
813         n->refcount = 0;
814         n->fd = fd;
815         n->shared = shared;
816         n->delete_me = EINA_FALSE;
817
818         if (file) eina_hash_del(_eina_file_cache, filename, file);
819         eina_hash_direct_add(_eina_file_cache, n->filename, n);
820      }
821    else
822      {
823         close(fd);
824
825         n = file;
826
827         if (n->refcount == 0)
828           _eina_file_cache_lru = eina_list_remove(_eina_file_cache_lru, n);
829      }
830
831    n->refcount++;
832
833    return n;
834
835  on_error:
836    close(fd);
837    return NULL;
838 }
839
840 EAPI void
841 eina_file_close(Eina_File *file)
842 {
843    file->refcount--;
844
845    if (file->refcount != 0) return ;
846
847    if (file->delete_me)
848      {
849         _eina_file_cache_delete = eina_list_remove(_eina_file_cache_delete, file);
850         _eina_file_real_close(file);
851      }
852    else
853      {
854         _eina_file_cache_lru = eina_list_prepend(_eina_file_cache_lru, file);
855      }
856 }
857
858 EAPI size_t
859 eina_file_size_get(Eina_File *file)
860 {
861    return file->length;
862 }
863
864 EAPI time_t
865 eina_file_mtime_get(Eina_File *file)
866 {
867    return file->mtime;
868 }
869
870 EAPI const char *
871 eina_file_filename_get(Eina_File *file)
872 {
873    return file->filename;
874 }
875
876 EAPI void *
877 eina_file_map_all(Eina_File *file, Eina_File_Populate rule)
878 {
879    int flags = MAP_SHARED;
880
881 // bsd people will lack this feature
882 #ifdef MAP_POPULATE
883    if (rule == EINA_FILE_POPULATE) flags |= MAP_POPULATE;
884 #endif
885 #ifdef MAP_HUGETLB
886    if (file->length > EINA_HUGE_PAGE) flags |= MAP_HUGETLB;
887 #endif
888
889    if (file->global_map == MAP_FAILED)
890      file->global_map = mmap(NULL, file->length, PROT_READ, flags, file->fd, 0);
891
892    if (file->global_map != MAP_FAILED)
893      {
894         _eina_file_map_rule_apply(rule, file->global_map, file->length);
895         file->global_refcount++;
896         return file->global_map;
897      }
898    return NULL;
899 }
900
901 EAPI void *
902 eina_file_map_new(Eina_File *file, Eina_File_Populate rule,
903                   unsigned long int offset, unsigned long int length)
904 {
905    Eina_File_Map *map;
906    unsigned long int key[2];
907
908    if (offset > file->length)
909      return NULL;
910    if (offset + length > file->length)
911      return NULL;
912
913    if (offset == 0 && length == file->length)
914      return eina_file_map_all(file, rule);
915
916    key[0] = offset;
917    key[1] = length;
918
919    map = eina_hash_find(file->map, &key);
920    if (!map)
921      {
922         int flags = MAP_SHARED;
923
924 // bsd people will lack this feature
925 #ifdef MAP_POPULATE
926         if (rule == EINA_FILE_POPULATE) flags |= MAP_POPULATE;
927 #endif
928 #ifdef MAP_HUGETLB
929         if (length > EINA_HUGE_PAGE) flags |= MAP_HUGETLB;
930 #endif
931
932         map = malloc(sizeof (Eina_File_Map));
933         if (!map) return NULL;
934
935         map->map = mmap(NULL, length, PROT_READ, flags, file->fd, offset);
936         map->offset = offset;
937         map->length = length;
938         map->refcount = 0;
939
940         if (map->map == MAP_FAILED)
941           {
942              free(map);
943              return NULL;
944           }
945
946         eina_hash_add(file->map, &key, map);
947         eina_hash_direct_add(file->rmap, map->map, map);
948      }
949
950    map->refcount++;
951
952    _eina_file_map_rule_apply(rule, map->map, length);
953
954    return map->map;
955 }
956
957 EAPI void
958 eina_file_map_free(Eina_File *file, void *map)
959 {
960    if (file->global_map == map)
961      {
962         file->global_refcount--;
963
964         if (file->global_refcount > 0) return ;
965
966         munmap(file->global_map, file->length);
967         file->global_map = MAP_FAILED;
968      }
969    else
970      {
971         Eina_File_Map *em;
972         unsigned long int key[2];
973
974         em = eina_hash_find(file->rmap, &map);
975         if (!em) return ;
976
977         em->refcount--;
978
979         if (em->refcount > 0) return ;
980
981         key[0] = em->offset;
982         key[1] = em->length;
983
984         eina_hash_del(file->rmap, &map, em);
985         eina_hash_del(file->map, &key, em);
986      }
987 }
988
989