EFL 1.7 svn doobies
[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-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 <stdlib.h>
42 #include <string.h>
43 #include <stddef.h>
44 #include <stdint.h>
45 #ifdef HAVE_DIRENT_H
46 # include <dirent.h>
47 #endif
48 #include <sys/types.h>
49 #include <sys/stat.h>
50 #include <unistd.h>
51 #ifdef HAVE_SYS_MMAN_H
52 # include <sys/mman.h>
53 #endif
54 #include <fcntl.h>
55
56 #define PATH_DELIM '/'
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 #include "eina_lock.h"
68 #include "eina_mmap.h"
69 #include "eina_log.h"
70 #include "eina_xattr.h"
71
72 #ifdef HAVE_ESCAPE
73 # include <Escape.h>
74 #endif
75
76 /*============================================================================*
77  *                                  Local                                     *
78  *============================================================================*/
79
80 /**
81  * @cond LOCAL
82  */
83
84 #ifndef EINA_LOG_COLOR_DEFAULT
85 #define EINA_LOG_COLOR_DEFAULT EINA_COLOR_CYAN
86 #endif
87
88 #ifdef ERR
89 #undef ERR
90 #endif
91 #define ERR(...) EINA_LOG_DOM_ERR(_eina_file_log_dom, __VA_ARGS__)
92
93 #ifdef WRN
94 #undef WRN
95 #endif
96 #define WRN(...) EINA_LOG_DOM_WARN(_eina_file_log_dom, __VA_ARGS__)
97
98 #ifdef DBG
99 #undef DBG
100 #endif
101 #define DBG(...) EINA_LOG_DOM_DBG(_eina_file_log_dom, __VA_ARGS__)
102
103 #define EINA_SMALL_PAGE 4096
104 # define EINA_HUGE_PAGE 16 * 1024 * 1024
105
106 #ifdef HAVE_DIRENT_H
107 typedef struct _Eina_File_Iterator Eina_File_Iterator;
108 struct _Eina_File_Iterator
109 {
110    Eina_Iterator iterator;
111
112    DIR *dirp;
113    int length;
114
115    char dir[1];
116 };
117 #endif
118
119 struct _Eina_File
120 {
121    const char *filename;
122
123    Eina_Hash *map;
124    Eina_Hash *rmap;
125    void *global_map;
126
127    Eina_Lock lock;
128
129    unsigned long long length;
130    time_t mtime;
131    ino_t inode;
132 #ifdef _STAT_VER_LINUX
133    unsigned long int mtime_nsec;
134 #endif
135
136    int refcount;
137    int global_refcount;
138
139    int fd;
140
141    Eina_Bool shared : 1;
142    Eina_Bool delete_me : 1;
143    Eina_Bool global_faulty : 1;
144 };
145
146 typedef struct _Eina_File_Map Eina_File_Map;
147 struct _Eina_File_Map
148 {
149    void *map;
150
151    unsigned long int offset;
152    unsigned long int length;
153
154    int refcount;
155
156    Eina_Bool hugetlb : 1;
157    Eina_Bool faulty : 1;
158 };
159
160 static Eina_Hash *_eina_file_cache = NULL;
161 static Eina_Lock _eina_file_lock_cache;
162
163 static int _eina_file_log_dom = -1;
164
165 /*
166  * This complex piece of code is needed due to possible race condition.
167  * The code and description of the issue can be found at :
168  * http://womble.decadent.org.uk/readdir_r-advisory.html
169  */
170 #ifdef HAVE_DIRENT_H
171 static long
172 _eina_name_max(DIR *dirp)
173 {
174    long name_max;
175
176 #if defined(HAVE_FPATHCONF) && defined(HAVE_DIRFD) && defined(_PC_NAME_MAX)
177    name_max = fpathconf(dirfd(dirp), _PC_NAME_MAX);
178
179    if (name_max == -1)
180      {
181 # if defined(NAME_MAX)
182         name_max = (NAME_MAX > 255) ? NAME_MAX : 255;
183 # else
184         name_max = PATH_MAX;
185 # endif
186      }
187 #else
188 # if defined(NAME_MAX)
189    name_max = (NAME_MAX > 255) ? NAME_MAX : 255;
190 # else
191 #  ifdef _PC_NAME_MAX
192 #   warning "buffer size for readdir_r cannot be determined safely, best effort, but racy"
193    name_max = pathconf(dirp, _PC_NAME_MAX);
194 #  else
195 #   error "buffer size for readdir_r cannot be determined safely"
196 #  endif
197 # endif
198 #endif
199
200    return name_max;
201 }
202
203 static size_t
204 _eina_dirent_buffer_size(DIR *dirp)
205 {
206    long name_max = _eina_name_max(dirp);
207    size_t name_end;
208
209    name_end = (size_t) offsetof(struct dirent, d_name) + name_max + 1;
210
211    return (name_end > sizeof (struct dirent) ? name_end : sizeof (struct dirent));
212 }
213
214 static Eina_Bool
215 _eina_file_ls_iterator_next(Eina_File_Iterator *it, void **data)
216 {
217    struct dirent *dp;
218    char *name;
219    size_t length;
220
221    dp = alloca(_eina_dirent_buffer_size(it->dirp));
222
223    do
224      {
225         if (readdir_r(it->dirp, dp, &dp))
226           return EINA_FALSE;
227         if (dp == NULL)
228           return EINA_FALSE;
229      }
230    while ((dp->d_name[0] == '.') &&
231           ((dp->d_name[1] == '\0') ||
232            ((dp->d_name[1] == '.') && (dp->d_name[2] == '\0'))));
233
234 #ifdef _DIRENT_HAVE_D_NAMLEN
235    length = dp->d_namlen;
236 #else
237    length = strlen(dp->d_name);
238 #endif
239    name = alloca(length + 2 + it->length);
240
241    memcpy(name,                  it->dir,    it->length);
242    memcpy(name + it->length,     "/",        1);
243    memcpy(name + it->length + 1, dp->d_name, length + 1);
244
245    *data = (char *)eina_stringshare_add(name);
246    return EINA_TRUE;
247 }
248
249 static DIR *
250 _eina_file_ls_iterator_container(Eina_File_Iterator *it)
251 {
252    return it->dirp;
253 }
254
255 static void
256 _eina_file_ls_iterator_free(Eina_File_Iterator *it)
257 {
258    closedir(it->dirp);
259
260    EINA_MAGIC_SET(&it->iterator, 0);
261    free(it);
262 }
263
264 typedef struct _Eina_File_Direct_Iterator Eina_File_Direct_Iterator;
265 struct _Eina_File_Direct_Iterator
266 {
267    Eina_Iterator iterator;
268
269    DIR *dirp;
270    int length;
271
272    Eina_File_Direct_Info info;
273
274    char dir[1];
275 };
276
277 static Eina_Bool
278 _eina_file_direct_ls_iterator_next(Eina_File_Direct_Iterator *it, void **data)
279 {
280    struct dirent *dp;
281    size_t length;
282
283    dp = alloca(_eina_dirent_buffer_size(it->dirp));
284
285    do
286      {
287         if (readdir_r(it->dirp, dp, &dp))
288            return EINA_FALSE;
289         if (!dp)
290            return EINA_FALSE;
291
292 #ifdef _DIRENT_HAVE_D_NAMLEN
293         length = dp->d_namlen;
294 #else
295         length = strlen(dp->d_name);
296 #endif
297         if (it->info.name_start + length + 1 >= EINA_PATH_MAX)
298            continue;
299      }
300    while ((dp->d_name[0] == '.') &&
301           ((dp->d_name[1] == '\0') ||
302            ((dp->d_name[1] == '.') && (dp->d_name[2] == '\0'))));
303
304    memcpy(it->info.path + it->info.name_start, dp->d_name, length);
305    it->info.name_length = length;
306    it->info.path_length = it->info.name_start + length;
307    it->info.path[it->info.path_length] = '\0';
308
309 #ifdef _DIRENT_HAVE_D_TYPE
310    switch (dp->d_type)
311      {
312      case DT_FIFO:
313        it->info.type = EINA_FILE_FIFO;
314        break;
315      case DT_CHR:
316        it->info.type = EINA_FILE_CHR;
317        break;
318      case DT_DIR:
319        it->info.type = EINA_FILE_DIR;
320        break;
321      case DT_BLK:
322        it->info.type = EINA_FILE_BLK;
323        break;
324      case DT_REG:
325        it->info.type = EINA_FILE_REG;
326        break;
327      case DT_LNK:
328        it->info.type = EINA_FILE_LNK;
329        break;
330      case DT_SOCK:
331        it->info.type = EINA_FILE_SOCK;
332        break;
333      case DT_WHT:
334        it->info.type = EINA_FILE_WHT;
335        break;
336      default:
337        it->info.type = EINA_FILE_UNKNOWN;
338        break;
339      }
340 #else
341    it->info.type = EINA_FILE_UNKNOWN;
342 #endif
343
344    *data = &it->info;
345    return EINA_TRUE;
346 }
347
348 static DIR *
349 _eina_file_direct_ls_iterator_container(Eina_File_Direct_Iterator *it)
350 {
351    return it->dirp;
352 }
353
354 static void
355 _eina_file_direct_ls_iterator_free(Eina_File_Direct_Iterator *it)
356 {
357    closedir(it->dirp);
358
359    EINA_MAGIC_SET(&it->iterator, 0);
360    free(it);
361 }
362
363 static Eina_Bool
364 _eina_file_stat_ls_iterator_next(Eina_File_Direct_Iterator *it, void **data)
365 {
366    Eina_Stat st;
367
368    if (!_eina_file_direct_ls_iterator_next(it, data))
369      return EINA_FALSE;
370
371    if (it->info.type == EINA_FILE_UNKNOWN)
372      {
373         if (eina_file_statat(it->dirp, &it->info, &st) != 0)
374           it->info.type = EINA_FILE_UNKNOWN;
375      }
376
377    return EINA_TRUE;
378 }
379 #endif
380
381 static void
382 _eina_file_real_close(Eina_File *file)
383 {
384    if (file->refcount != 0) return;
385
386    eina_hash_free(file->rmap);
387    eina_hash_free(file->map);
388
389    if (file->global_map != MAP_FAILED)
390      munmap(file->global_map, file->length);
391
392    close(file->fd);
393
394    free(file);
395 }
396
397 static void
398 _eina_file_map_close(Eina_File_Map *map)
399 {
400    munmap(map->map, map->length);
401    free(map);
402 }
403
404 static unsigned int
405 _eina_file_map_key_length(const void *key __UNUSED__)
406 {
407    return sizeof (unsigned long int) * 2;
408 }
409
410 static int
411 _eina_file_map_key_cmp(const unsigned long int *key1, int key1_length __UNUSED__,
412                        const unsigned long int *key2, int key2_length __UNUSED__)
413 {
414    if (key1[0] - key2[0] == 0) return key1[1] - key2[1];
415    return key1[0] - key2[0];
416 }
417
418 static int
419 _eina_file_map_key_hash(const unsigned long int *key, int key_length __UNUSED__)
420 {
421    return eina_hash_int64(&key[0], sizeof (unsigned long int))
422      ^ eina_hash_int64(&key[1], sizeof (unsigned long int));
423 }
424
425 #ifndef MAP_POPULATE
426 static unsigned int
427 _eina_file_map_populate(char *map, unsigned int size, Eina_Bool hugetlb)
428 {
429    unsigned int r = 0xDEADBEEF;
430    unsigned int i;
431    unsigned int s;
432
433    s = hugetlb ? EINA_HUGE_PAGE : EINA_SMALL_PAGE;
434
435    for (i = 0; i < size; i += s)
436      r ^= map[i];
437
438    r ^= map[size];
439
440    return r;
441 }
442 #endif
443
444 static int
445 _eina_file_map_rule_apply(Eina_File_Populate rule, void *addr, unsigned long int size, Eina_Bool hugetlb)
446 {
447    int tmp = 42;
448    int flag = MADV_RANDOM;
449
450    switch (rule)
451      {
452       case EINA_FILE_RANDOM: flag = MADV_RANDOM; break;
453       case EINA_FILE_SEQUENTIAL: flag = MADV_SEQUENTIAL; break;
454       case EINA_FILE_POPULATE: flag = MADV_WILLNEED; break;
455       case EINA_FILE_WILLNEED: flag = MADV_WILLNEED; break;
456      }
457
458    madvise(addr, size, flag);
459
460 #ifndef MAP_POPULATE
461    if (rule == EINA_FILE_POPULATE)
462      tmp ^= _eina_file_map_populate(addr, size, hugetlb);
463 #else
464    (void) hugetlb;
465 #endif
466
467    return tmp;
468 }
469
470 static Eina_Bool
471 _eina_file_timestamp_compare(Eina_File *f, struct stat *st)
472 {
473    if (f->mtime != st->st_mtime) return EINA_FALSE;
474    if (f->length != (unsigned long long) st->st_size) return EINA_FALSE;
475    if (f->inode != st->st_ino) return EINA_FALSE;
476 #ifdef _STAT_VER_LINUX
477 # if (defined __USE_MISC && defined st_mtime)
478    if (f->mtime_nsec != (unsigned long int)st->st_mtim.tv_nsec)
479      return EINA_FALSE;
480 # else
481    if (f->mtime_nsec != (unsigned long int)st->st_mtimensec)
482      return EINA_FALSE;
483 # endif
484 #endif
485    return EINA_TRUE;
486 }
487
488 static void
489 slprintf(char *str, size_t size, const char *format, ...)
490 {
491    va_list ap;
492
493    va_start(ap, format);
494
495    vsnprintf(str, size, format, ap);
496    str[size - 1] = 0;
497
498    va_end(ap);
499 }
500
501 static char *
502 _eina_file_escape(const char *path, int *length)
503 {
504    char *result;
505    char *p;
506    char *q;
507    size_t len;
508
509    result = strdup(path ? path : "");
510    p = result;
511    q = result;
512
513    if (!result)
514      return NULL;
515
516    if (length) len = *length;
517    else len = strlen(result);
518
519    while ((p = strchr(p, '/')))
520      {
521         // remove double `/'
522         if (p[1] == '/')
523           {
524              memmove(p, p + 1, --len - (p - result));
525              result[len] = '\0';
526           }
527         else
528           if (p[1] == '.'
529               && p[2] == '.')
530             {
531                // remove `/../'
532                if (p[3] == '/')
533                  {
534                     char tmp;
535
536                     len -= p + 3 - q;
537                     memmove(q, p + 3, len - (q - result));
538                     result[len] = '\0';
539                     p = q;
540
541                     /* Update q correctly. */
542                     tmp = *p;
543                     *p = '\0';
544                     q = strrchr(result, '/');
545                     if (!q) q = result;
546                     *p = tmp;
547                  }
548                else
549                  // remove '/..$'
550                  if (p[3] == '\0')
551                    {
552                       len -= p + 2 - q;
553                       result[len] = '\0';
554                       q = p;
555                       ++p;
556                    }
557                  else
558                    {
559                       q = p;
560                       ++p;
561                    }
562             }
563           else
564             {
565                q = p;
566                ++p;
567             }
568      }
569
570    if (length)
571      *length = len;
572    return result;
573 }
574
575 /**
576  * @endcond
577  */
578
579 /*============================================================================*
580  *                                 Global                                     *
581  *============================================================================*/
582
583 Eina_Bool
584 eina_file_init(void)
585 {
586    _eina_file_log_dom = eina_log_domain_register("eina_file",
587                                                  EINA_LOG_COLOR_DEFAULT);
588    if (_eina_file_log_dom < 0)
589      {
590         EINA_LOG_ERR("Could not register log domain: eina_file");
591         return EINA_FALSE;
592      }
593
594    _eina_file_cache = eina_hash_string_djb2_new(NULL);
595    if (!_eina_file_cache)
596      {
597         ERR("Could not create cache.");
598         eina_log_domain_unregister(_eina_file_log_dom);
599         _eina_file_log_dom = -1;
600         return EINA_FALSE;
601      }
602
603    eina_lock_new(&_eina_file_lock_cache);
604
605    return EINA_TRUE;
606 }
607
608 Eina_Bool
609 eina_file_shutdown(void)
610 {
611    if (eina_hash_population(_eina_file_cache) > 0)
612      {
613         Eina_Iterator *it;
614         const char *key;
615
616         it = eina_hash_iterator_key_new(_eina_file_cache);
617         EINA_ITERATOR_FOREACH(it, key)
618           ERR("File [%s] still open !", key);
619         eina_iterator_free(it);
620      }
621
622    eina_hash_free(_eina_file_cache);
623
624    eina_lock_free(&_eina_file_lock_cache);
625
626    eina_log_domain_unregister(_eina_file_log_dom);
627    _eina_file_log_dom = -1;
628    return EINA_TRUE;
629 }
630
631 void
632 eina_file_mmap_faulty(void *addr, long page_size)
633 {
634    Eina_File_Map *m;
635    Eina_File *f;
636    Eina_Iterator *itf;
637    Eina_Iterator *itm;
638
639    /* NOTE: I actually don't know if other thread are running, I will try to take the lock.
640       It may be possible that if other thread are not running and they were in the middle of
641       accessing an Eina_File this lock are still taken and we will result as a deadlock. */
642    eina_lock_take(&_eina_file_lock_cache);
643
644    itf = eina_hash_iterator_data_new(_eina_file_cache);
645    EINA_ITERATOR_FOREACH(itf, f)
646      {
647         Eina_Bool faulty = EINA_FALSE;
648
649         eina_lock_take(&f->lock);
650
651         if (f->global_map)
652           {
653              if ((unsigned char *) addr < (((unsigned char *)f->global_map) + f->length) &&
654                  (((unsigned char *) addr) + page_size) >= (unsigned char *) f->global_map)
655                {
656                   f->global_faulty = EINA_TRUE;
657                   faulty = EINA_TRUE;
658                }
659           }
660
661         if (!faulty)
662           {
663              itm = eina_hash_iterator_data_new(f->map);
664              EINA_ITERATOR_FOREACH(itm, m)
665                {
666                   if ((unsigned char *) addr < (((unsigned char *)m->map) + m->length) &&
667                       (((unsigned char *) addr) + page_size) >= (unsigned char *) m->map)
668                     {
669                        m->faulty = EINA_TRUE;
670                        faulty = EINA_TRUE;
671                        break;
672                     }
673                }
674              eina_iterator_free(itm);
675           }
676
677         eina_lock_release(&f->lock);
678
679         if (faulty) break;
680      }
681    eina_iterator_free(itf);
682
683    eina_lock_release(&_eina_file_lock_cache);
684 }
685
686 /*============================================================================*
687  *                                   API                                      *
688  *============================================================================*/
689
690 EAPI char *
691 eina_file_path_sanitize(const char *path)
692 {
693    char *result = NULL;
694    int len;
695
696    if (!path) return NULL;
697
698    len = strlen(path);
699
700    if (*path != '/')
701      {
702         char cwd[PATH_MAX];
703         char *tmp = NULL;
704
705         tmp = getcwd(cwd, PATH_MAX);
706         if (!tmp) return NULL;
707
708         len += strlen(cwd) + 2;
709         tmp = alloca(sizeof (char) * len);
710
711         slprintf(tmp, len, "%s/%s", cwd, path);
712
713         result = tmp;
714      }
715
716    return _eina_file_escape(result ? result : path, &len);
717 }
718
719 EAPI Eina_Bool
720 eina_file_dir_list(const char *dir,
721                    Eina_Bool recursive,
722                    Eina_File_Dir_List_Cb cb,
723                    void *data)
724 {
725    Eina_File_Direct_Info *info;
726    Eina_Iterator *it;
727
728    EINA_SAFETY_ON_NULL_RETURN_VAL(cb,  EINA_FALSE);
729    EINA_SAFETY_ON_NULL_RETURN_VAL(dir, EINA_FALSE);
730    EINA_SAFETY_ON_TRUE_RETURN_VAL(dir[0] == '\0', EINA_FALSE);
731
732    it = eina_file_stat_ls(dir);
733    if (!it)
734       return EINA_FALSE;
735
736    EINA_ITERATOR_FOREACH(it, info)
737      {
738         cb(info->path + info->name_start, dir, data);
739
740         if (recursive == EINA_TRUE && info->type == EINA_FILE_DIR)
741           {
742              eina_file_dir_list(info->path, recursive, cb, data);
743           }
744      }
745
746    eina_iterator_free(it);
747
748    return EINA_TRUE;
749 }
750
751 EAPI Eina_Array *
752 eina_file_split(char *path)
753 {
754    Eina_Array *ea;
755    char *current;
756    size_t length;
757
758    EINA_SAFETY_ON_NULL_RETURN_VAL(path, NULL);
759
760    ea = eina_array_new(16);
761
762    if (!ea)
763       return NULL;
764
765    for (current = strchr(path, PATH_DELIM);
766         current;
767         path = current + 1, current = strchr(path, PATH_DELIM))
768      {
769         length = current - path;
770
771         if (length <= 0)
772            continue;
773
774         eina_array_push(ea, path);
775         *current = '\0';
776      }
777
778    if (*path != '\0')
779         eina_array_push(ea, path);
780
781    return ea;
782 }
783
784 EAPI Eina_Iterator *
785 eina_file_ls(const char *dir)
786 {
787 #ifdef HAVE_DIRENT_H
788    Eina_File_Iterator *it;
789    size_t length;
790
791    EINA_SAFETY_ON_NULL_RETURN_VAL(dir, NULL);
792
793    length = strlen(dir);
794    if (length < 1)
795       return NULL;
796
797    it = calloc(1, sizeof (Eina_File_Iterator) + length);
798    if (!it)
799       return NULL;
800
801    EINA_MAGIC_SET(&it->iterator, EINA_MAGIC_ITERATOR);
802
803    it->dirp = opendir(dir);
804    if (!it->dirp)
805      {
806         free(it);
807         return NULL;
808      }
809
810    memcpy(it->dir, dir, length + 1);
811    if (dir[length - 1] != '/')
812       it->length = length;
813    else
814       it->length = length - 1;
815
816    it->iterator.version = EINA_ITERATOR_VERSION;
817    it->iterator.next = FUNC_ITERATOR_NEXT(_eina_file_ls_iterator_next);
818    it->iterator.get_container = FUNC_ITERATOR_GET_CONTAINER(
819          _eina_file_ls_iterator_container);
820    it->iterator.free = FUNC_ITERATOR_FREE(_eina_file_ls_iterator_free);
821
822    return &it->iterator;
823 #else
824    (void) dir;
825    return NULL;
826 #endif
827 }
828
829 EAPI Eina_Iterator *
830 eina_file_direct_ls(const char *dir)
831 {
832 #ifdef HAVE_DIRENT_H
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_direct_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 #else
881    (void) dir;
882    return NULL;
883 #endif
884 }
885
886 EAPI Eina_Iterator *
887 eina_file_stat_ls(const char *dir)
888 {
889 #ifdef HAVE_DIRENT_H
890    Eina_File_Direct_Iterator *it;
891    size_t length;
892
893    EINA_SAFETY_ON_NULL_RETURN_VAL(dir, NULL);
894
895    length = strlen(dir);
896    if (length < 1)
897       return NULL;
898
899    it = calloc(1, sizeof(Eina_File_Direct_Iterator) + length);
900    if (!it)
901       return NULL;
902
903    EINA_MAGIC_SET(&it->iterator, EINA_MAGIC_ITERATOR);
904
905    it->dirp = opendir(dir);
906    if (!it->dirp)
907      {
908         free(it);
909         return NULL;
910      }
911
912    if (length + _eina_name_max(it->dirp) + 2 >= EINA_PATH_MAX)
913      {
914         _eina_file_direct_ls_iterator_free(it);
915         return NULL;
916      }
917
918    memcpy(it->dir,       dir, length + 1);
919    it->length = length;
920
921    memcpy(it->info.path, dir, length);
922    if (dir[length - 1] == '/')
923       it->info.name_start = length;
924    else
925      {
926         it->info.path[length] = '/';
927         it->info.name_start = length + 1;
928      }
929
930    it->iterator.version = EINA_ITERATOR_VERSION;
931    it->iterator.next = FUNC_ITERATOR_NEXT(_eina_file_stat_ls_iterator_next);
932    it->iterator.get_container = FUNC_ITERATOR_GET_CONTAINER(
933          _eina_file_direct_ls_iterator_container);
934    it->iterator.free = FUNC_ITERATOR_FREE(_eina_file_direct_ls_iterator_free);
935
936    return &it->iterator;
937 #else
938    (void) dir;
939    return NULL;
940 #endif
941 }
942
943 EAPI Eina_File *
944 eina_file_open(const char *path, Eina_Bool shared)
945 {
946    Eina_File *file;
947    Eina_File *n;
948    char *filename;
949    struct stat file_stat;
950    int fd = -1;
951 #ifdef HAVE_EXECVP
952    int flags;
953 #endif
954
955    EINA_SAFETY_ON_NULL_RETURN_VAL(path, NULL);
956
957    filename = eina_file_path_sanitize(path);
958    if (!filename) return NULL;
959
960    if (shared)
961 #ifdef HAVE_SHM_OPEN
962      fd = shm_open(filename, O_RDONLY, S_IRWXU | S_IRWXG | S_IRWXO);
963 #else
964      goto on_error;
965 #endif
966    else
967      fd = open(filename, O_RDONLY, S_IRWXU | S_IRWXG | S_IRWXO);
968
969    if (fd < 0) goto on_error;
970
971 #ifdef HAVE_EXECVP
972    flags = fcntl(fd, F_GETFD);
973    if (flags == -1)
974      goto on_error;
975
976    flags |= FD_CLOEXEC;
977    if (fcntl(fd, F_SETFD, flags) == -1)
978      goto on_error;
979 #endif
980
981    if (fstat(fd, &file_stat))
982      goto on_error;
983
984    eina_lock_take(&_eina_file_lock_cache);
985
986    file = eina_hash_find(_eina_file_cache, filename);
987    if ((file) && !_eina_file_timestamp_compare(file, &file_stat))
988      {
989         file->delete_me = EINA_TRUE;
990         eina_hash_del(_eina_file_cache, file->filename, file);
991         _eina_file_real_close(file);
992         file = NULL;
993      }
994
995    if (!file)
996      {
997         n = malloc(sizeof(Eina_File) + strlen(filename) + 1);
998         if (!n)
999           {
1000              eina_lock_release(&_eina_file_lock_cache);
1001              goto on_error;
1002           }
1003
1004         memset(n, 0, sizeof(Eina_File));
1005         n->filename = (char*) (n + 1);
1006         strcpy((char*) n->filename, filename);
1007         n->map = eina_hash_new(EINA_KEY_LENGTH(_eina_file_map_key_length),
1008                                EINA_KEY_CMP(_eina_file_map_key_cmp),
1009                                EINA_KEY_HASH(_eina_file_map_key_hash),
1010                                EINA_FREE_CB(_eina_file_map_close),
1011                                3);
1012         n->rmap = eina_hash_pointer_new(NULL);
1013         n->global_map = MAP_FAILED;
1014         n->length = file_stat.st_size;
1015         n->mtime = file_stat.st_mtime;
1016 #ifdef _STAT_VER_LINUX
1017 # if (defined __USE_MISC && defined st_mtime)
1018         n->mtime_nsec = (unsigned long int)file_stat.st_mtim.tv_nsec;
1019 # else
1020         n->mtime_nsec = (unsigned long int)file_stat.st_mtimensec;
1021 # endif
1022 #endif
1023         n->inode = file_stat.st_ino;
1024         n->fd = fd;
1025         n->shared = shared;
1026         eina_lock_new(&n->lock);
1027         eina_hash_direct_add(_eina_file_cache, n->filename, n);
1028      }
1029    else
1030      {
1031         close(fd);
1032         n = file;
1033      }
1034    eina_lock_take(&n->lock);
1035    n->refcount++;
1036    eina_lock_release(&n->lock);
1037
1038    eina_lock_release(&_eina_file_lock_cache);
1039
1040    free(filename);
1041
1042    return n;
1043
1044  on_error:
1045    free(filename);
1046    if (fd >= 0) close(fd);
1047    return NULL;
1048 }
1049
1050 EAPI void
1051 eina_file_close(Eina_File *file)
1052 {
1053    EINA_SAFETY_ON_NULL_RETURN(file);
1054
1055    eina_lock_take(&file->lock);
1056    file->refcount--;
1057    eina_lock_release(&file->lock);
1058
1059    if (file->refcount != 0) return;
1060    eina_lock_take(&_eina_file_lock_cache);
1061
1062    eina_hash_del(_eina_file_cache, file->filename, file);
1063    _eina_file_real_close(file);
1064
1065    eina_lock_release(&_eina_file_lock_cache);
1066 }
1067
1068 EAPI size_t
1069 eina_file_size_get(Eina_File *file)
1070 {
1071    EINA_SAFETY_ON_NULL_RETURN_VAL(file, 0);
1072    return file->length;
1073 }
1074
1075 EAPI time_t
1076 eina_file_mtime_get(Eina_File *file)
1077 {
1078    EINA_SAFETY_ON_NULL_RETURN_VAL(file, 0);
1079    return file->mtime;
1080 }
1081
1082 EAPI const char *
1083 eina_file_filename_get(Eina_File *file)
1084 {
1085    EINA_SAFETY_ON_NULL_RETURN_VAL(file, NULL);
1086    return file->filename;
1087 }
1088
1089 EAPI void *
1090 eina_file_map_all(Eina_File *file, Eina_File_Populate rule)
1091 {
1092    int flags = MAP_SHARED;
1093    void *ret = NULL;
1094
1095    EINA_SAFETY_ON_NULL_RETURN_VAL(file, NULL);
1096
1097 // bsd people will lack this feature
1098 #ifdef MAP_POPULATE
1099    if (rule == EINA_FILE_POPULATE) flags |= MAP_POPULATE;
1100 #endif
1101 #ifdef MAP_HUGETLB
1102    if (file->length > EINA_HUGE_PAGE) flags |= MAP_HUGETLB;
1103 #endif
1104
1105    eina_mmap_safety_enabled_set(EINA_TRUE);
1106    eina_lock_take(&file->lock);
1107    if (file->global_map == MAP_FAILED)
1108      file->global_map = mmap(NULL, file->length, PROT_READ, flags, file->fd, 0);
1109 #ifdef MAP_HUGETLB
1110    if ((file->global_map == MAP_FAILED) && (flags & MAP_HUGETLB))
1111      {
1112        flags &= ~MAP_HUGETLB;
1113        file->global_map = mmap(NULL, file->length, PROT_READ, flags, file->fd, 0);
1114      }
1115 #endif
1116
1117    if (file->global_map != MAP_FAILED)
1118      {
1119         Eina_Bool hugetlb = EINA_FALSE;
1120
1121 #ifdef MAP_HUGETLB
1122         hugetlb = !!(flags & MAP_HUGETLB);
1123 #endif
1124         _eina_file_map_rule_apply(rule, file->global_map, file->length, hugetlb);
1125         file->global_refcount++;
1126         ret = file->global_map;
1127      }
1128
1129    eina_lock_release(&file->lock);
1130    return ret;
1131 }
1132
1133 typedef struct _Eina_Lines_Iterator Eina_Lines_Iterator;
1134 struct _Eina_Lines_Iterator
1135 {
1136    Eina_Iterator iterator;
1137
1138    Eina_File *fp;
1139    const char *map;
1140    const char *end;
1141
1142    int boundary;
1143
1144    Eina_File_Line current;
1145 };
1146
1147 /* search '\r' and '\n' by preserving cache locality and page locality
1148    in doing a search inside 4K boundary.
1149  */
1150 static inline const char *
1151 _eina_fine_eol(const char *start, int boundary, const char *end)
1152 {
1153    const char *cr;
1154    const char *lf;
1155    unsigned long long chunk;
1156
1157    while (start < end)
1158      {
1159         chunk = start + boundary < end ? boundary : end - start;
1160         cr = memchr(start, '\r', chunk);
1161         lf = memchr(start, '\n', chunk);
1162         if (cr)
1163           {
1164              if (lf && lf < cr)
1165                return lf + 1;
1166              return cr + 1;
1167           }
1168         else if (lf)
1169            return lf + 1;
1170
1171         start += chunk;
1172         boundary = 4096;
1173      }
1174
1175    return end;
1176 }
1177
1178 static Eina_Bool
1179 _eina_file_map_lines_iterator_next(Eina_Lines_Iterator *it, void **data)
1180 {
1181    const char *eol;
1182    unsigned char match;
1183
1184    if (it->current.end >= it->end)
1185      return EINA_FALSE;
1186
1187    match = *it->current.end;
1188    while ((*it->current.end == '\n' || *it->current.end == '\r')
1189           && it->current.end < it->end)
1190      {
1191         if (match == *it->current.end)
1192           it->current.index++;
1193         it->current.end++;
1194      }
1195    it->current.index++;
1196
1197    if (it->current.end == it->end)
1198      return EINA_FALSE;
1199
1200    eol = _eina_fine_eol(it->current.end,
1201                         it->boundary,
1202                         it->end);
1203    it->boundary = (uintptr_t) eol & 0x3FF;
1204    if (it->boundary == 0) it->boundary = 4096;
1205
1206    it->current.start = it->current.end;
1207
1208    it->current.end = eol;
1209    it->current.length = eol - it->current.start - 1;
1210
1211    *data = &it->current;
1212    return EINA_TRUE;
1213 }
1214
1215 static Eina_File *
1216 _eina_file_map_lines_iterator_container(Eina_Lines_Iterator *it)
1217 {
1218    return it->fp;
1219 }
1220
1221 static void
1222 _eina_file_map_lines_iterator_free(Eina_Lines_Iterator *it)
1223 {
1224    eina_file_map_free(it->fp, (void*) it->map);
1225    eina_file_close(it->fp);
1226
1227    EINA_MAGIC_SET(&it->iterator, 0);
1228    free(it);
1229 }
1230
1231 EAPI Eina_Iterator *
1232 eina_file_map_lines(Eina_File *file)
1233 {
1234    Eina_Lines_Iterator *it;
1235
1236    EINA_SAFETY_ON_NULL_RETURN_VAL(file, NULL);
1237
1238    if (file->length == 0) return NULL;
1239
1240    it = calloc(1, sizeof (Eina_Lines_Iterator));
1241    if (!it) return NULL;
1242
1243    EINA_MAGIC_SET(&it->iterator, EINA_MAGIC_ITERATOR);
1244
1245    it->map = eina_file_map_all(file, EINA_FILE_SEQUENTIAL);
1246    if (!it->map)
1247      {
1248         free(it);
1249         return NULL;
1250      }
1251
1252    eina_lock_take(&file->lock);
1253    file->refcount++;
1254    eina_lock_release(&file->lock);
1255
1256    it->fp = file;
1257    it->boundary = 4096;
1258    it->current.start = it->map;
1259    it->current.end = it->current.start;
1260    it->current.index = 0;
1261    it->current.length = 0;
1262    it->end = it->map + it->fp->length;
1263
1264    it->iterator.version = EINA_ITERATOR_VERSION;
1265    it->iterator.next = FUNC_ITERATOR_NEXT(_eina_file_map_lines_iterator_next);
1266    it->iterator.get_container = FUNC_ITERATOR_GET_CONTAINER(_eina_file_map_lines_iterator_container);
1267    it->iterator.free = FUNC_ITERATOR_FREE(_eina_file_map_lines_iterator_free);
1268
1269    return &it->iterator;
1270 }
1271
1272 EAPI void *
1273 eina_file_map_new(Eina_File *file, Eina_File_Populate rule,
1274                   unsigned long int offset, unsigned long int length)
1275 {
1276    Eina_File_Map *map;
1277    unsigned long int key[2];
1278
1279    EINA_SAFETY_ON_NULL_RETURN_VAL(file, NULL);
1280
1281    if (offset > file->length)
1282      return NULL;
1283    if (offset + length > file->length)
1284      return NULL;
1285
1286    if (offset == 0 && length == file->length)
1287      return eina_file_map_all(file, rule);
1288
1289    key[0] = offset;
1290    key[1] = length;
1291
1292    eina_mmap_safety_enabled_set(EINA_TRUE);
1293    eina_lock_take(&file->lock);
1294
1295    map = eina_hash_find(file->map, &key);
1296    if (!map)
1297      {
1298         int flags = MAP_SHARED;
1299
1300 // bsd people will lack this feature
1301 #ifdef MAP_POPULATE
1302         if (rule == EINA_FILE_POPULATE) flags |= MAP_POPULATE;
1303 #endif
1304 #ifdef MAP_HUGETLB
1305         if (length > EINA_HUGE_PAGE) flags |= MAP_HUGETLB;
1306 #endif
1307
1308         map = malloc(sizeof (Eina_File_Map));
1309         if (!map) goto on_error;
1310
1311         map->map = mmap(NULL, length, PROT_READ, flags, file->fd, offset);
1312 #ifdef MAP_HUGETLB
1313         if (map->map == MAP_FAILED && (flags & MAP_HUGETLB))
1314           {
1315              flags &= ~MAP_HUGETLB;
1316              map->map = mmap(NULL, length, PROT_READ, flags, file->fd, offset);
1317           }
1318
1319         map->hugetlb = !!(flags & MAP_HUGETLB);
1320 #else
1321         map->hugetlb = EINA_FALSE;
1322 #endif
1323         map->offset = offset;
1324         map->length = length;
1325         map->refcount = 0;
1326
1327         if (map->map == MAP_FAILED) goto on_error;
1328
1329         eina_hash_add(file->map, &key, map);
1330         eina_hash_direct_add(file->rmap, map->map, map);
1331      }
1332
1333    map->refcount++;
1334
1335    _eina_file_map_rule_apply(rule, map->map, length, map->hugetlb);
1336
1337    eina_lock_release(&file->lock);
1338
1339    return map->map;
1340
1341  on_error:
1342    free(map);
1343    eina_lock_release(&file->lock);
1344
1345    return NULL;
1346 }
1347
1348 EAPI void
1349 eina_file_map_free(Eina_File *file, void *map)
1350 {
1351    EINA_SAFETY_ON_NULL_RETURN(file);
1352
1353    eina_lock_take(&file->lock);
1354
1355    if (file->global_map == map)
1356      {
1357         file->global_refcount--;
1358
1359         if (file->global_refcount > 0) goto on_exit;
1360
1361         munmap(file->global_map, file->length);
1362         file->global_map = MAP_FAILED;
1363      }
1364    else
1365      {
1366         Eina_File_Map *em;
1367         unsigned long int key[2];
1368
1369         em = eina_hash_find(file->rmap, &map);
1370         if (!em) goto on_exit;
1371
1372         em->refcount--;
1373
1374         if (em->refcount > 0) goto on_exit;
1375
1376         key[0] = em->offset;
1377         key[1] = em->length;
1378
1379         eina_hash_del(file->rmap, &map, em);
1380         eina_hash_del(file->map, &key, em);
1381      }
1382
1383  on_exit:
1384    eina_lock_release(&file->lock);
1385 }
1386
1387 EAPI Eina_Bool
1388 eina_file_map_faulted(Eina_File *file, void *map)
1389 {
1390    Eina_File_Map *em;
1391    Eina_Bool r = EINA_FALSE;
1392
1393    EINA_SAFETY_ON_NULL_RETURN_VAL(file, EINA_FALSE);
1394
1395    eina_lock_take(&file->lock);
1396
1397    if (file->global_map == map)
1398      {
1399         r = file->global_faulty;
1400      }
1401    else
1402      {
1403         em = eina_hash_find(file->rmap, &map);
1404         if (em) r = em->faulty;
1405      }
1406
1407    eina_lock_release(&file->lock);
1408
1409    return r;
1410 }
1411
1412 EAPI Eina_Iterator *
1413 eina_file_xattr_get(Eina_File *file)
1414 {
1415    EINA_SAFETY_ON_NULL_RETURN_VAL(file, NULL);
1416
1417    return eina_xattr_fd_ls(file->fd);
1418 }
1419
1420 EAPI Eina_Iterator *
1421 eina_file_xattr_value_get(Eina_File *file)
1422 {
1423    EINA_SAFETY_ON_NULL_RETURN_VAL(file, NULL);
1424
1425    return eina_xattr_value_fd_ls(file->fd);
1426 }
1427
1428 EAPI int
1429 eina_file_statat(void *container, Eina_File_Direct_Info *info, Eina_Stat *st)
1430 {
1431    struct stat buf;
1432 #ifdef HAVE_FSTATAT
1433    int fd;
1434 #endif
1435
1436    EINA_SAFETY_ON_NULL_RETURN_VAL(info, -1);
1437    EINA_SAFETY_ON_NULL_RETURN_VAL(st, -1);
1438
1439 #ifdef HAVE_FSTATAT
1440    fd = dirfd((DIR*) container);
1441    if (fstatat(fd, info->path + info->name_start, &buf, 0))
1442 #else
1443    (void)container;
1444    if (stat(info->path, &buf))
1445 #endif
1446      {
1447         if (info->type != EINA_FILE_LNK)
1448           info->type = EINA_FILE_UNKNOWN;
1449         return -1;
1450      }
1451
1452    if (info->type == EINA_FILE_UNKNOWN)
1453      {
1454         if (S_ISREG(buf.st_mode))
1455           info->type = EINA_FILE_REG;
1456         else if (S_ISDIR(buf.st_mode))
1457           info->type = EINA_FILE_DIR;
1458         else if (S_ISCHR(buf.st_mode))
1459           info->type = EINA_FILE_CHR;
1460         else if (S_ISBLK(buf.st_mode))
1461           info->type = EINA_FILE_BLK;
1462         else if (S_ISFIFO(buf.st_mode))
1463           info->type = EINA_FILE_FIFO;
1464         else if (S_ISLNK(buf.st_mode))
1465           info->type = EINA_FILE_LNK;
1466         else if (S_ISSOCK(buf.st_mode))
1467           info->type = EINA_FILE_SOCK;
1468         else
1469           info->type = EINA_FILE_UNKNOWN;
1470      }
1471
1472    st->dev = buf.st_dev;
1473    st->ino = buf.st_ino;
1474    st->mode = buf.st_mode;
1475    st->nlink = buf.st_nlink;
1476    st->uid = buf.st_uid;
1477    st->gid = buf.st_gid;
1478    st->rdev = buf.st_rdev;
1479    st->size = buf.st_size;
1480    st->blksize = buf.st_blksize;
1481    st->blocks = buf.st_blocks;
1482    st->atime = buf.st_atime;
1483    st->mtime = buf.st_mtime;
1484    st->ctime = buf.st_ctime;
1485 #ifdef _STAT_VER_LINUX
1486 # if (defined __USE_MISC && defined st_mtime)
1487    st->atimensec = buf.st_atim.tv_nsec;
1488    st->mtimensec = buf.st_mtim.tv_nsec;
1489    st->ctimensec = buf.st_ctim.tv_nsec;
1490 # else
1491    st->atimensec = buf.st_atimensec;
1492    st->mtimensec = buf.st_mtimensec;
1493    st->ctimensec = buf.st_ctimensec;
1494 # endif
1495 #else
1496    st->atimensec = 0;
1497    st->mtimensec = 0;
1498    st->ctimensec = 0;
1499 #endif
1500    return 0;
1501 }