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