bdfe2a76a8ca6300f616ad70009faac61cece2f3
[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 = strdup(path ? path : "");
505    char *p = result;
506    char *q = result;
507    int len;
508
509    if (!result)
510      return NULL;
511
512    if (length) len = *length;
513    else len = strlen(result);
514
515    while ((p = strchr(p, '/')))
516      {
517         // remove double `/'
518         if (p[1] == '/')
519           {
520              memmove(p, p + 1, --len - (p - result));
521              result[len] = '\0';
522           }
523         else
524           if (p[1] == '.'
525               && p[2] == '.')
526             {
527                // remove `/../'
528                if (p[3] == '/')
529                  {
530                     char tmp;
531
532                     len -= p + 3 - q;
533                     memmove(q, p + 3, len - (q - result));
534                     result[len] = '\0';
535                     p = q;
536
537                     /* Update q correctly. */
538                     tmp = *p;
539                     *p = '\0';
540                     q = strrchr(result, '/');
541                     if (!q) q = result;
542                     *p = tmp;
543                  }
544                else
545                  // remove '/..$'
546                  if (p[3] == '\0')
547                    {
548                       len -= p + 2 - q;
549                       result[len] = '\0';
550                       q = p;
551                       ++p;
552                    }
553                  else
554                    {
555                       q = p;
556                       ++p;
557                    }
558             }
559           else
560             {
561                q = p;
562                ++p;
563             }
564      }
565
566    if (length)
567      *length = len;
568    return result;
569 }
570
571 /**
572  * @endcond
573  */
574
575 /*============================================================================*
576  *                                 Global                                     *
577  *============================================================================*/
578
579 Eina_Bool
580 eina_file_init(void)
581 {
582    _eina_file_log_dom = eina_log_domain_register("eina_file",
583                                                  EINA_LOG_COLOR_DEFAULT);
584    if (_eina_file_log_dom < 0)
585      {
586         EINA_LOG_ERR("Could not register log domain: eina_file");
587         return EINA_FALSE;
588      }
589
590    _eina_file_cache = eina_hash_string_djb2_new(NULL);
591    if (!_eina_file_cache)
592      {
593         ERR("Could not create cache.");
594         eina_log_domain_unregister(_eina_file_log_dom);
595         _eina_file_log_dom = -1;
596         return EINA_FALSE;
597      }
598
599    eina_lock_new(&_eina_file_lock_cache);
600
601    return EINA_TRUE;
602 }
603
604 Eina_Bool
605 eina_file_shutdown(void)
606 {
607    if (eina_hash_population(_eina_file_cache) > 0)
608      {
609         Eina_Iterator *it;
610         const char *key;
611
612         it = eina_hash_iterator_key_new(_eina_file_cache);
613         EINA_ITERATOR_FOREACH(it, key)
614           ERR("File [%s] still open !", key);
615         eina_iterator_free(it);
616      }
617
618    eina_hash_free(_eina_file_cache);
619
620    eina_lock_free(&_eina_file_lock_cache);
621
622    eina_log_domain_unregister(_eina_file_log_dom);
623    _eina_file_log_dom = -1;
624    return EINA_TRUE;
625 }
626
627 void
628 eina_file_mmap_faulty(void *addr, long page_size)
629 {
630    Eina_File_Map *m;
631    Eina_File *f;
632    Eina_Iterator *itf;
633    Eina_Iterator *itm;
634
635    /* NOTE: I actually don't know if other thread are running, I will try to take the lock.
636       It may be possible that if other thread are not running and they were in the middle of
637       accessing an Eina_File this lock are still taken and we will result as a deadlock. */
638    eina_lock_take(&_eina_file_lock_cache);
639
640    itf = eina_hash_iterator_data_new(_eina_file_cache);
641    EINA_ITERATOR_FOREACH(itf, f)
642      {
643         Eina_Bool faulty = EINA_FALSE;
644
645         eina_lock_take(&f->lock);
646
647         if (f->global_map)
648           {
649              if ((unsigned char *) addr < (((unsigned char *)f->global_map) + f->length) &&
650                  (((unsigned char *) addr) + page_size) >= (unsigned char *) f->global_map)
651                {
652                   f->global_faulty = EINA_TRUE;
653                   faulty = EINA_TRUE;
654                }
655           }
656
657         if (!faulty)
658           {
659              itm = eina_hash_iterator_data_new(f->map);
660              EINA_ITERATOR_FOREACH(itm, m)
661                {
662                   if ((unsigned char *) addr < (((unsigned char *)m->map) + m->length) &&
663                       (((unsigned char *) addr) + page_size) >= (unsigned char *) m->map)
664                     {
665                        m->faulty = EINA_TRUE;
666                        faulty = EINA_TRUE;
667                        break;
668                     }
669                }
670              eina_iterator_free(itm);
671           }
672
673         eina_lock_release(&f->lock);
674
675         if (faulty) break;
676      }
677    eina_iterator_free(itf);
678
679    eina_lock_release(&_eina_file_lock_cache);
680 }
681
682 /*============================================================================*
683  *                                   API                                      *
684  *============================================================================*/
685
686 EAPI char *
687 eina_file_path_sanitize(const char *path)
688 {
689    char *result = NULL;
690    int len;
691
692    if (!path) return NULL;
693
694    len = strlen(path);
695
696    if (*path != '/')
697      {
698         char cwd[PATH_MAX];
699         char *tmp = NULL;
700
701         tmp = getcwd(cwd, PATH_MAX);
702         if (!tmp) return NULL;
703
704         len += strlen(cwd) + 2;
705         tmp = alloca(sizeof (char) * len);
706
707         slprintf(tmp, len, "%s/%s", cwd, path);
708
709         result = tmp;
710      }
711
712    return _eina_file_escape(result ? result : path, &len);
713 }
714
715 EAPI Eina_Bool
716 eina_file_dir_list(const char *dir,
717                    Eina_Bool recursive,
718                    Eina_File_Dir_List_Cb cb,
719                    void *data)
720 {
721    Eina_File_Direct_Info *info;
722    Eina_Iterator *it;
723
724    EINA_SAFETY_ON_NULL_RETURN_VAL(cb,  EINA_FALSE);
725    EINA_SAFETY_ON_NULL_RETURN_VAL(dir, EINA_FALSE);
726    EINA_SAFETY_ON_TRUE_RETURN_VAL(dir[0] == '\0', EINA_FALSE);
727
728    it = eina_file_stat_ls(dir);
729    if (!it)
730       return EINA_FALSE;
731
732    EINA_ITERATOR_FOREACH(it, info)
733      {
734         cb(info->path + info->name_start, dir, data);
735
736         if (recursive == EINA_TRUE && info->type == EINA_FILE_DIR)
737           {
738              eina_file_dir_list(info->path, recursive, cb, data);
739           }
740      }
741
742    eina_iterator_free(it);
743
744    return EINA_TRUE;
745 }
746
747 EAPI Eina_Array *
748 eina_file_split(char *path)
749 {
750    Eina_Array *ea;
751    char *current;
752    size_t length;
753
754    EINA_SAFETY_ON_NULL_RETURN_VAL(path, NULL);
755
756    ea = eina_array_new(16);
757
758    if (!ea)
759       return NULL;
760
761    for (current = strchr(path, PATH_DELIM);
762         current;
763         path = current + 1, current = strchr(path, PATH_DELIM))
764      {
765         length = current - path;
766
767         if (length <= 0)
768            continue;
769
770         eina_array_push(ea, path);
771         *current = '\0';
772      }
773
774    if (*path != '\0')
775         eina_array_push(ea, path);
776
777    return ea;
778 }
779
780 EAPI Eina_Iterator *
781 eina_file_ls(const char *dir)
782 {
783 #ifdef HAVE_DIRENT_H
784    Eina_File_Iterator *it;
785    size_t length;
786
787    EINA_SAFETY_ON_NULL_RETURN_VAL(dir, NULL);
788
789    length = strlen(dir);
790    if (length < 1)
791       return NULL;
792
793    it = calloc(1, sizeof (Eina_File_Iterator) + length);
794    if (!it)
795       return NULL;
796
797    EINA_MAGIC_SET(&it->iterator, EINA_MAGIC_ITERATOR);
798
799    it->dirp = opendir(dir);
800    if (!it->dirp)
801      {
802         free(it);
803         return NULL;
804      }
805
806    memcpy(it->dir, dir, length + 1);
807    if (dir[length - 1] != '/')
808       it->length = length;
809    else
810       it->length = length - 1;
811
812    it->iterator.version = EINA_ITERATOR_VERSION;
813    it->iterator.next = FUNC_ITERATOR_NEXT(_eina_file_ls_iterator_next);
814    it->iterator.get_container = FUNC_ITERATOR_GET_CONTAINER(
815          _eina_file_ls_iterator_container);
816    it->iterator.free = FUNC_ITERATOR_FREE(_eina_file_ls_iterator_free);
817
818    return &it->iterator;
819 #else
820    (void) dir;
821    return NULL;
822 #endif
823 }
824
825 EAPI Eina_Iterator *
826 eina_file_direct_ls(const char *dir)
827 {
828 #ifdef HAVE_DIRENT_H
829    Eina_File_Direct_Iterator *it;
830    size_t length;
831
832    EINA_SAFETY_ON_NULL_RETURN_VAL(dir, NULL);
833
834    length = strlen(dir);
835    if (length < 1)
836       return NULL;
837
838    it = calloc(1, sizeof(Eina_File_Direct_Iterator) + length);
839    if (!it)
840       return NULL;
841
842    EINA_MAGIC_SET(&it->iterator, EINA_MAGIC_ITERATOR);
843
844    it->dirp = opendir(dir);
845    if (!it->dirp)
846      {
847         free(it);
848         return NULL;
849      }
850
851    if (length + _eina_name_max(it->dirp) + 2 >= EINA_PATH_MAX)
852      {
853         _eina_file_direct_ls_iterator_free(it);
854         return NULL;
855      }
856
857    memcpy(it->dir,       dir, length + 1);
858    it->length = length;
859
860    memcpy(it->info.path, dir, length);
861    if (dir[length - 1] == '/')
862       it->info.name_start = length;
863    else
864      {
865         it->info.path[length] = '/';
866         it->info.name_start = length + 1;
867      }
868
869    it->iterator.version = EINA_ITERATOR_VERSION;
870    it->iterator.next = FUNC_ITERATOR_NEXT(_eina_file_direct_ls_iterator_next);
871    it->iterator.get_container = FUNC_ITERATOR_GET_CONTAINER(
872          _eina_file_direct_ls_iterator_container);
873    it->iterator.free = FUNC_ITERATOR_FREE(_eina_file_direct_ls_iterator_free);
874
875    return &it->iterator;
876 #else
877    (void) dir;
878    return NULL;
879 #endif
880 }
881
882 EAPI Eina_Iterator *
883 eina_file_stat_ls(const char *dir)
884 {
885 #ifdef HAVE_DIRENT_H
886    Eina_File_Direct_Iterator *it;
887    size_t length;
888
889    EINA_SAFETY_ON_NULL_RETURN_VAL(dir, NULL);
890
891    length = strlen(dir);
892    if (length < 1)
893       return NULL;
894
895    it = calloc(1, sizeof(Eina_File_Direct_Iterator) + length);
896    if (!it)
897       return NULL;
898
899    EINA_MAGIC_SET(&it->iterator, EINA_MAGIC_ITERATOR);
900
901    it->dirp = opendir(dir);
902    if (!it->dirp)
903      {
904         free(it);
905         return NULL;
906      }
907
908    if (length + _eina_name_max(it->dirp) + 2 >= EINA_PATH_MAX)
909      {
910         _eina_file_direct_ls_iterator_free(it);
911         return NULL;
912      }
913
914    memcpy(it->dir,       dir, length + 1);
915    it->length = length;
916
917    memcpy(it->info.path, dir, length);
918    if (dir[length - 1] == '/')
919       it->info.name_start = length;
920    else
921      {
922         it->info.path[length] = '/';
923         it->info.name_start = length + 1;
924      }
925
926    it->iterator.version = EINA_ITERATOR_VERSION;
927    it->iterator.next = FUNC_ITERATOR_NEXT(_eina_file_stat_ls_iterator_next);
928    it->iterator.get_container = FUNC_ITERATOR_GET_CONTAINER(
929          _eina_file_direct_ls_iterator_container);
930    it->iterator.free = FUNC_ITERATOR_FREE(_eina_file_direct_ls_iterator_free);
931
932    return &it->iterator;
933 #else
934    (void) dir;
935    return NULL;
936 #endif
937 }
938
939 EAPI Eina_File *
940 eina_file_open(const char *path, Eina_Bool shared)
941 {
942    Eina_File *file;
943    Eina_File *n;
944    char *filename;
945    struct stat file_stat;
946    int fd = -1;
947 #ifdef HAVE_EXECVP
948    int flags;
949 #endif
950
951    EINA_SAFETY_ON_NULL_RETURN_VAL(path, NULL);
952
953    filename = eina_file_path_sanitize(path);
954    if (!filename) return NULL;
955
956    if (shared)
957 #ifdef HAVE_SHM_OPEN
958      fd = shm_open(filename, O_RDONLY, S_IRWXU | S_IRWXG | S_IRWXO);
959 #else
960      goto on_error;
961 #endif
962    else
963      fd = open(filename, O_RDONLY, S_IRWXU | S_IRWXG | S_IRWXO);
964
965    if (fd < 0) goto on_error;
966
967 #ifdef HAVE_EXECVP
968    flags = fcntl(fd, F_GETFD);
969    if (flags == -1)
970      goto on_error;
971
972    flags |= FD_CLOEXEC;
973    if (fcntl(fd, F_SETFD, flags) == -1)
974      goto on_error;
975 #endif
976
977    if (fstat(fd, &file_stat))
978      goto on_error;
979
980    eina_lock_take(&_eina_file_lock_cache);
981
982    file = eina_hash_find(_eina_file_cache, filename);
983    if ((file) && !_eina_file_timestamp_compare(file, &file_stat))
984      {
985         file->delete_me = EINA_TRUE;
986         eina_hash_del(_eina_file_cache, file->filename, file);
987         _eina_file_real_close(file);
988         file = NULL;
989      }
990
991    if (!file)
992      {
993         n = malloc(sizeof(Eina_File) + strlen(filename) + 1);
994         if (!n)
995           {
996              eina_lock_release(&_eina_file_lock_cache);
997              goto on_error;
998           }
999
1000         memset(n, 0, sizeof(Eina_File));
1001         n->filename = (char*) (n + 1);
1002         strcpy((char*) n->filename, filename);
1003         n->map = eina_hash_new(EINA_KEY_LENGTH(_eina_file_map_key_length),
1004                                EINA_KEY_CMP(_eina_file_map_key_cmp),
1005                                EINA_KEY_HASH(_eina_file_map_key_hash),
1006                                EINA_FREE_CB(_eina_file_map_close),
1007                                3);
1008         n->rmap = eina_hash_pointer_new(NULL);
1009         n->global_map = MAP_FAILED;
1010         n->length = file_stat.st_size;
1011         n->mtime = file_stat.st_mtime;
1012 #ifdef _STAT_VER_LINUX
1013 # if (defined __USE_MISC && defined st_mtime)
1014         n->mtime_nsec = (unsigned long int)file_stat.st_mtim.tv_nsec;
1015 # else
1016         n->mtime_nsec = (unsigned long int)file_stat.st_mtimensec;
1017 # endif
1018 #endif
1019         n->inode = file_stat.st_ino;
1020         n->fd = fd;
1021         n->shared = shared;
1022         eina_lock_new(&n->lock);
1023         eina_hash_direct_add(_eina_file_cache, n->filename, n);
1024      }
1025    else
1026      {
1027         close(fd);
1028         n = file;
1029      }
1030    eina_lock_take(&n->lock);
1031    n->refcount++;
1032    eina_lock_release(&n->lock);
1033
1034    eina_lock_release(&_eina_file_lock_cache);
1035
1036    free(filename);
1037
1038    return n;
1039
1040  on_error:
1041    free(filename);
1042    if (fd >= 0) close(fd);
1043    return NULL;
1044 }
1045
1046 EAPI void
1047 eina_file_close(Eina_File *file)
1048 {
1049    EINA_SAFETY_ON_NULL_RETURN(file);
1050
1051    eina_lock_take(&file->lock);
1052    file->refcount--;
1053    eina_lock_release(&file->lock);
1054
1055    if (file->refcount != 0) return;
1056    eina_lock_take(&_eina_file_lock_cache);
1057
1058    eina_hash_del(_eina_file_cache, file->filename, file);
1059    _eina_file_real_close(file);
1060
1061    eina_lock_release(&_eina_file_lock_cache);
1062 }
1063
1064 EAPI size_t
1065 eina_file_size_get(Eina_File *file)
1066 {
1067    EINA_SAFETY_ON_NULL_RETURN_VAL(file, 0);
1068    return file->length;
1069 }
1070
1071 EAPI time_t
1072 eina_file_mtime_get(Eina_File *file)
1073 {
1074    EINA_SAFETY_ON_NULL_RETURN_VAL(file, 0);
1075    return file->mtime;
1076 }
1077
1078 EAPI const char *
1079 eina_file_filename_get(Eina_File *file)
1080 {
1081    EINA_SAFETY_ON_NULL_RETURN_VAL(file, NULL);
1082    return file->filename;
1083 }
1084
1085 EAPI void *
1086 eina_file_map_all(Eina_File *file, Eina_File_Populate rule)
1087 {
1088    int flags = MAP_SHARED;
1089    void *ret = NULL;
1090
1091    EINA_SAFETY_ON_NULL_RETURN_VAL(file, NULL);
1092
1093 // bsd people will lack this feature
1094 #ifdef MAP_POPULATE
1095    if (rule == EINA_FILE_POPULATE) flags |= MAP_POPULATE;
1096 #endif
1097 #ifdef MAP_HUGETLB
1098    if (file->length > EINA_HUGE_PAGE) flags |= MAP_HUGETLB;
1099 #endif
1100
1101    eina_mmap_safety_enabled_set(EINA_TRUE);
1102    eina_lock_take(&file->lock);
1103    if (file->global_map == MAP_FAILED)
1104      file->global_map = mmap(NULL, file->length, PROT_READ, flags, file->fd, 0);
1105 #ifdef MAP_HUGETLB
1106    if ((file->global_map == MAP_FAILED) && (flags & MAP_HUGETLB))
1107      {
1108        flags &= ~MAP_HUGETLB;
1109        file->global_map = mmap(NULL, file->length, PROT_READ, flags, file->fd, 0);
1110      }
1111 #endif
1112
1113    if (file->global_map != MAP_FAILED)
1114      {
1115         Eina_Bool hugetlb = EINA_FALSE;
1116
1117 #ifdef MAP_HUGETLB
1118         hugetlb = !!(flags & MAP_HUGETLB);
1119 #endif
1120         _eina_file_map_rule_apply(rule, file->global_map, file->length, hugetlb);
1121         file->global_refcount++;
1122         ret = file->global_map;
1123      }
1124
1125    eina_lock_release(&file->lock);
1126    return ret;
1127 }
1128
1129 typedef struct _Eina_Lines_Iterator Eina_Lines_Iterator;
1130 struct _Eina_Lines_Iterator
1131 {
1132    Eina_Iterator iterator;
1133
1134    Eina_File *fp;
1135    const char *map;
1136    const char *end;
1137
1138    int boundary;
1139
1140    Eina_File_Line current;
1141 };
1142
1143 /* search '\r' and '\n' by preserving cache locality and page locality
1144    in doing a search inside 4K boundary.
1145  */
1146 static inline const char *
1147 _eina_fine_eol(const char *start, int boundary, const char *end)
1148 {
1149    const char *cr;
1150    const char *lf;
1151    unsigned long long chunk;
1152
1153    while (start < end)
1154      {
1155         chunk = start + boundary < end ? boundary : end - start;
1156         cr = memchr(start, '\r', chunk);
1157         lf = memchr(start, '\n', chunk);
1158         if (cr)
1159           {
1160              if (lf && lf < cr)
1161                return lf + 1;
1162              return cr + 1;
1163           }
1164         else if (lf)
1165            return lf + 1;
1166
1167         start += chunk;
1168         boundary = 4096;
1169      }
1170
1171    return end;
1172 }
1173
1174 static Eina_Bool
1175 _eina_file_map_lines_iterator_next(Eina_Lines_Iterator *it, void **data)
1176 {
1177    const char *eol;
1178    unsigned char match;
1179
1180    if (it->current.end >= it->end)
1181      return EINA_FALSE;
1182
1183    match = *it->current.end;
1184    while ((*it->current.end == '\n' || *it->current.end == '\r')
1185           && it->current.end < it->end)
1186      {
1187         if (match == *it->current.end)
1188           it->current.index++;
1189         it->current.end++;
1190      }
1191    it->current.index++;
1192
1193    if (it->current.end == it->end)
1194      return EINA_FALSE;
1195
1196    eol = _eina_fine_eol(it->current.end,
1197                         it->boundary,
1198                         it->end);
1199    it->boundary = (uintptr_t) eol & 0x3FF;
1200    if (it->boundary == 0) it->boundary = 4096;
1201
1202    it->current.start = it->current.end;
1203
1204    it->current.end = eol;
1205    it->current.length = eol - it->current.start - 1;
1206
1207    *data = &it->current;
1208    return EINA_TRUE;
1209 }
1210
1211 static Eina_File *
1212 _eina_file_map_lines_iterator_container(Eina_Lines_Iterator *it)
1213 {
1214    return it->fp;
1215 }
1216
1217 static void
1218 _eina_file_map_lines_iterator_free(Eina_Lines_Iterator *it)
1219 {
1220    eina_file_map_free(it->fp, (void*) it->map);
1221    eina_file_close(it->fp);
1222
1223    EINA_MAGIC_SET(&it->iterator, 0);
1224    free(it);
1225 }
1226
1227 EAPI Eina_Iterator *
1228 eina_file_map_lines(Eina_File *file)
1229 {
1230    Eina_Lines_Iterator *it;
1231
1232    EINA_SAFETY_ON_NULL_RETURN_VAL(file, NULL);
1233
1234    if (file->length == 0) return NULL;
1235
1236    it = calloc(1, sizeof (Eina_Lines_Iterator));
1237    if (!it) return NULL;
1238
1239    EINA_MAGIC_SET(&it->iterator, EINA_MAGIC_ITERATOR);
1240
1241    it->map = eina_file_map_all(file, EINA_FILE_SEQUENTIAL);
1242    if (!it->map)
1243      {
1244         free(it);
1245         return NULL;
1246      }
1247
1248    eina_lock_take(&file->lock);
1249    file->refcount++;
1250    eina_lock_release(&file->lock);
1251
1252    it->fp = file;
1253    it->boundary = 4096;
1254    it->current.start = it->map;
1255    it->current.end = it->current.start;
1256    it->current.index = 0;
1257    it->current.length = 0;
1258    it->end = it->map + it->fp->length;
1259
1260    it->iterator.version = EINA_ITERATOR_VERSION;
1261    it->iterator.next = FUNC_ITERATOR_NEXT(_eina_file_map_lines_iterator_next);
1262    it->iterator.get_container = FUNC_ITERATOR_GET_CONTAINER(_eina_file_map_lines_iterator_container);
1263    it->iterator.free = FUNC_ITERATOR_FREE(_eina_file_map_lines_iterator_free);
1264
1265    return &it->iterator;
1266 }
1267
1268 EAPI void *
1269 eina_file_map_new(Eina_File *file, Eina_File_Populate rule,
1270                   unsigned long int offset, unsigned long int length)
1271 {
1272    Eina_File_Map *map;
1273    unsigned long int key[2];
1274
1275    EINA_SAFETY_ON_NULL_RETURN_VAL(file, NULL);
1276
1277    if (offset > file->length)
1278      return NULL;
1279    if (offset + length > file->length)
1280      return NULL;
1281
1282    if (offset == 0 && length == file->length)
1283      return eina_file_map_all(file, rule);
1284
1285    key[0] = offset;
1286    key[1] = length;
1287
1288    eina_mmap_safety_enabled_set(EINA_TRUE);
1289    eina_lock_take(&file->lock);
1290
1291    map = eina_hash_find(file->map, &key);
1292    if (!map)
1293      {
1294         int flags = MAP_SHARED;
1295
1296 // bsd people will lack this feature
1297 #ifdef MAP_POPULATE
1298         if (rule == EINA_FILE_POPULATE) flags |= MAP_POPULATE;
1299 #endif
1300 #ifdef MAP_HUGETLB
1301         if (length > EINA_HUGE_PAGE) flags |= MAP_HUGETLB;
1302 #endif
1303
1304         map = malloc(sizeof (Eina_File_Map));
1305         if (!map) goto on_error;
1306
1307         map->map = mmap(NULL, length, PROT_READ, flags, file->fd, offset);
1308 #ifdef MAP_HUGETLB
1309         if (map->map == MAP_FAILED && (flags & MAP_HUGETLB))
1310           {
1311              flags &= ~MAP_HUGETLB;
1312              map->map = mmap(NULL, length, PROT_READ, flags, file->fd, offset);
1313           }
1314
1315         map->hugetlb = !!(flags & MAP_HUGETLB);
1316 #else
1317         map->hugetlb = EINA_FALSE;
1318 #endif
1319         map->offset = offset;
1320         map->length = length;
1321         map->refcount = 0;
1322
1323         if (map->map == MAP_FAILED) goto on_error;
1324
1325         eina_hash_add(file->map, &key, map);
1326         eina_hash_direct_add(file->rmap, map->map, map);
1327      }
1328
1329    map->refcount++;
1330
1331    _eina_file_map_rule_apply(rule, map->map, length, map->hugetlb);
1332
1333    eina_lock_release(&file->lock);
1334
1335    return map->map;
1336
1337  on_error:
1338    free(map);
1339    eina_lock_release(&file->lock);
1340
1341    return NULL;
1342 }
1343
1344 EAPI void
1345 eina_file_map_free(Eina_File *file, void *map)
1346 {
1347    EINA_SAFETY_ON_NULL_RETURN(file);
1348
1349    eina_lock_take(&file->lock);
1350
1351    if (file->global_map == map)
1352      {
1353         file->global_refcount--;
1354
1355         if (file->global_refcount > 0) goto on_exit;
1356
1357         munmap(file->global_map, file->length);
1358         file->global_map = MAP_FAILED;
1359      }
1360    else
1361      {
1362         Eina_File_Map *em;
1363         unsigned long int key[2];
1364
1365         em = eina_hash_find(file->rmap, &map);
1366         if (!em) goto on_exit;
1367
1368         em->refcount--;
1369
1370         if (em->refcount > 0) goto on_exit;
1371
1372         key[0] = em->offset;
1373         key[1] = em->length;
1374
1375         eina_hash_del(file->rmap, &map, em);
1376         eina_hash_del(file->map, &key, em);
1377      }
1378
1379  on_exit:
1380    eina_lock_release(&file->lock);
1381 }
1382
1383 EAPI Eina_Bool
1384 eina_file_map_faulted(Eina_File *file, void *map)
1385 {
1386    Eina_File_Map *em;
1387    Eina_Bool r = EINA_FALSE;
1388
1389    EINA_SAFETY_ON_NULL_RETURN_VAL(file, EINA_FALSE);
1390
1391    eina_lock_take(&file->lock);
1392
1393    if (file->global_map == map)
1394      {
1395         r = file->global_faulty;
1396      }
1397    else
1398      {
1399         em = eina_hash_find(file->rmap, &map);
1400         if (em) r = em->faulty;
1401      }
1402
1403    eina_lock_release(&file->lock);
1404
1405    return r;
1406 }
1407
1408 EAPI Eina_Iterator *
1409 eina_file_xattr_get(Eina_File *file)
1410 {
1411    EINA_SAFETY_ON_NULL_RETURN_VAL(file, NULL);
1412
1413    return eina_xattr_fd_ls(file->fd);
1414 }
1415
1416 EAPI Eina_Iterator *
1417 eina_file_xattr_value_get(Eina_File *file)
1418 {
1419    EINA_SAFETY_ON_NULL_RETURN_VAL(file, NULL);
1420
1421    return eina_xattr_value_fd_ls(file->fd);
1422 }
1423
1424 EAPI int
1425 eina_file_statat(void *container, Eina_File_Direct_Info *info, Eina_Stat *st)
1426 {
1427    struct stat buf;
1428 #ifdef HAVE_FSTATAT
1429    int fd;
1430 #endif
1431
1432    EINA_SAFETY_ON_NULL_RETURN_VAL(info, -1);
1433    EINA_SAFETY_ON_NULL_RETURN_VAL(st, -1);
1434
1435 #ifdef HAVE_FSTATAT
1436    fd = dirfd((DIR*) container);
1437    if (fstatat(fd, info->path + info->name_start, &buf, 0))
1438 #else
1439    (void)container;
1440    if (stat(info->path, &buf))
1441 #endif
1442      {
1443         if (info->type != EINA_FILE_LNK)
1444           info->type = EINA_FILE_UNKNOWN;
1445         return -1;
1446      }
1447
1448    if (info->type == EINA_FILE_UNKNOWN)
1449      {
1450         if (S_ISREG(buf.st_mode))
1451           info->type = EINA_FILE_REG;
1452         else if (S_ISDIR(buf.st_mode))
1453           info->type = EINA_FILE_DIR;
1454         else if (S_ISCHR(buf.st_mode))
1455           info->type = EINA_FILE_CHR;
1456         else if (S_ISBLK(buf.st_mode))
1457           info->type = EINA_FILE_BLK;
1458         else if (S_ISFIFO(buf.st_mode))
1459           info->type = EINA_FILE_FIFO;
1460         else if (S_ISLNK(buf.st_mode))
1461           info->type = EINA_FILE_LNK;
1462         else if (S_ISSOCK(buf.st_mode))
1463           info->type = EINA_FILE_SOCK;
1464         else
1465           info->type = EINA_FILE_UNKNOWN;
1466      }
1467
1468    st->dev = buf.st_dev;
1469    st->ino = buf.st_ino;
1470    st->mode = buf.st_mode;
1471    st->nlink = buf.st_nlink;
1472    st->uid = buf.st_uid;
1473    st->gid = buf.st_gid;
1474    st->rdev = buf.st_rdev;
1475    st->size = buf.st_size;
1476    st->blksize = buf.st_blksize;
1477    st->blocks = buf.st_blocks;
1478    st->atime = buf.st_atime;
1479    st->mtime = buf.st_mtime;
1480    st->ctime = buf.st_ctime;
1481 #ifdef _STAT_VER_LINUX
1482 # if (defined __USE_MISC && defined st_mtime)
1483    st->atimensec = buf.st_atim.tv_nsec;
1484    st->mtimensec = buf.st_mtim.tv_nsec;
1485    st->ctimensec = buf.st_ctim.tv_nsec;
1486 # else
1487    st->atimensec = buf.st_atimensec;
1488    st->mtimensec = buf.st_mtimensec;
1489    st->ctimensec = buf.st_ctimensec;
1490 # endif
1491 #else
1492    st->atimensec = 0;
1493    st->mtimensec = 0;
1494    st->ctimensec = 0;
1495 #endif
1496    return 0;
1497 }