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