1 /* EINA - EFL data type library
2 * Copyright (C) 2007-2008 Jorge Luis Zapata Muga, Vincent Torri
3 * Copyright (C) 2010 Cedric Bail
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.
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.
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/>.
26 #elif defined __GNUC__
27 # define alloca __builtin_alloca
29 # define alloca __alloca
30 #elif defined _MSC_VER
32 # define alloca _alloca
38 void *alloca (size_t);
44 #include <sys/types.h>
50 #define PATH_DELIM '/'
58 #include "eina_config.h"
59 #include "eina_private.h"
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"
68 /*============================================================================*
70 *============================================================================*/
76 #ifndef EINA_LOG_COLOR_DEFAULT
77 #define EINA_LOG_COLOR_DEFAULT EINA_COLOR_CYAN
83 #define ERR(...) EINA_LOG_DOM_ERR(_eina_file_log_dom, __VA_ARGS__)
88 #define WRN(...) EINA_LOG_DOM_WARN(_eina_file_log_dom, __VA_ARGS__)
93 #define DBG(...) EINA_LOG_DOM_DBG(_eina_file_log_dom, __VA_ARGS__)
95 #define EINA_SMALL_PAGE 4096
96 # define EINA_HUGE_PAGE 16 * 1024 * 1024
98 typedef struct _Eina_File_Iterator Eina_File_Iterator;
99 typedef struct _Eina_File_Map Eina_File_Map;
101 struct _Eina_File_Iterator
103 Eina_Iterator iterator;
113 const char *filename;
119 unsigned long long length;
128 Eina_Bool shared : 1;
129 Eina_Bool delete_me : 1;
132 struct _Eina_File_Map
136 unsigned long int offset;
137 unsigned long int length;
142 static Eina_Hash *_eina_file_cache = NULL;
143 static Eina_List *_eina_file_cache_lru = NULL;
144 static Eina_List *_eina_file_cache_delete = NULL;
146 static int _eina_file_log_dom = -1;
149 * This complex piece of code is needed due to possible race condition.
150 * The code and description of the issue can be found at :
151 * http://womble.decadent.org.uk/readdir_r-advisory.html
154 _eina_dirent_buffer_size(DIR *dirp)
159 #if defined(HAVE_FPATHCONF) && defined(HAVE_DIRFD) && defined(_PC_NAME_MAX)
160 name_max = fpathconf(dirfd(dirp), _PC_NAME_MAX);
164 # if defined(NAME_MAX)
165 name_max = (NAME_MAX > 255) ? NAME_MAX : 255;
171 # if defined(NAME_MAX)
172 name_max = (NAME_MAX > 255) ? NAME_MAX : 255;
175 # warning "buffer size for readdir_r cannot be determined safely, best effort, but racy"
176 name_max = pathconf(dirp, _PC_NAME_MAX);
178 # error "buffer size for readdir_r cannot be determined safely"
182 name_end = (size_t) offsetof(struct dirent, d_name) + name_max + 1;
184 return (name_end > sizeof (struct dirent) ? name_end : sizeof (struct dirent));
188 _eina_file_ls_iterator_next(Eina_File_Iterator *it, void **data)
194 dp = alloca(_eina_dirent_buffer_size(it->dirp));
198 if (readdir_r(it->dirp, dp, &dp))
203 while ((dp->d_name[0] == '.') &&
204 ((dp->d_name[1] == '\0') ||
205 ((dp->d_name[1] == '.') && (dp->d_name[2] == '\0'))));
207 #ifdef _DIRENT_HAVE_D_NAMLEN
208 length = dp->d_namlen;
210 length = strlen(dp->d_name);
212 name = alloca(length + 2 + it->length);
214 memcpy(name, it->dir, it->length);
215 memcpy(name + it->length, "/", 1);
216 memcpy(name + it->length + 1, dp->d_name, length + 1);
218 *data = (char *)eina_stringshare_add(name);
223 _eina_file_ls_iterator_container(Eina_File_Iterator *it)
229 _eina_file_ls_iterator_free(Eina_File_Iterator *it)
233 EINA_MAGIC_SET(&it->iterator, 0);
237 typedef struct _Eina_File_Direct_Iterator Eina_File_Direct_Iterator;
238 struct _Eina_File_Direct_Iterator
240 Eina_Iterator iterator;
245 Eina_File_Direct_Info info;
251 _eina_file_direct_ls_iterator_next(Eina_File_Direct_Iterator *it, void **data)
256 dp = alloca(_eina_dirent_buffer_size(it->dirp));
260 if (readdir_r(it->dirp, dp, &dp))
265 #ifdef _DIRENT_HAVE_D_NAMLEN
266 length = dp->d_namlen;
268 length = strlen(dp->d_name);
270 if (it->info.name_start + length + 1 >= EINA_PATH_MAX)
273 while ((dp->d_name[0] == '.') &&
274 ((dp->d_name[1] == '\0') ||
275 ((dp->d_name[1] == '.') && (dp->d_name[2] == '\0'))));
277 memcpy(it->info.path + it->info.name_start, dp->d_name, length);
278 it->info.name_length = length;
279 it->info.path_length = it->info.name_start + length;
280 it->info.path[it->info.path_length] = '\0';
282 #ifdef _DIRENT_HAVE_D_TYPE
286 it->info.type = EINA_FILE_FIFO;
289 it->info.type = EINA_FILE_CHR;
292 it->info.type = EINA_FILE_DIR;
295 it->info.type = EINA_FILE_BLK;
298 it->info.type = EINA_FILE_REG;
301 it->info.type = EINA_FILE_LNK;
304 it->info.type = EINA_FILE_SOCK;
307 it->info.type = EINA_FILE_WHT;
310 it->info.type = EINA_FILE_UNKNOWN;
314 it->info.type = EINA_FILE_UNKNOWN;
322 _eina_file_direct_ls_iterator_container(Eina_File_Direct_Iterator *it)
328 _eina_file_direct_ls_iterator_free(Eina_File_Direct_Iterator *it)
332 EINA_MAGIC_SET(&it->iterator, 0);
337 _eina_file_stat_ls_iterator_next(Eina_File_Direct_Iterator *it, void **data)
341 if (!_eina_file_direct_ls_iterator_next(it, data))
344 if (it->info.type == EINA_FILE_UNKNOWN)
349 fd = dirfd(it->dirp);
350 if (fstatat(fd, it->info.path + it->info.name_start, &st, 0))
352 if (stat(it->info.path, &st))
354 it->info.type = EINA_FILE_UNKNOWN;
357 if (S_ISREG(st.st_mode))
358 it->info.type = EINA_FILE_REG;
359 else if (S_ISDIR(st.st_mode))
360 it->info.type = EINA_FILE_DIR;
361 else if (S_ISCHR(st.st_mode))
362 it->info.type = EINA_FILE_CHR;
363 else if (S_ISBLK(st.st_mode))
364 it->info.type = EINA_FILE_BLK;
365 else if (S_ISFIFO(st.st_mode))
366 it->info.type = EINA_FILE_FIFO;
367 else if (S_ISLNK(st.st_mode))
368 it->info.type = EINA_FILE_LNK;
369 else if (S_ISSOCK(st.st_mode))
370 it->info.type = EINA_FILE_SOCK;
372 it->info.type = EINA_FILE_UNKNOWN;
380 _eina_file_real_close(Eina_File *file)
382 if (file->refcount != 0) return ;
384 eina_hash_free(file->rmap);
385 eina_hash_free(file->map);
387 if (file->global_map != MAP_FAILED)
388 munmap(file->global_map, file->length);
396 _eina_file_map_close(Eina_File_Map *map)
398 munmap(map->map, map->length);
403 _eina_file_map_key_length(const void *key __UNUSED__)
405 return sizeof (unsigned long int) * 2;
409 _eina_file_map_key_cmp(const unsigned long int *key1, int key1_length __UNUSED__,
410 const unsigned long int *key2, int key2_length __UNUSED__)
412 if (key1[0] - key2[0] == 0) return key1[1] - key2[1];
413 return key1[0] - key2[0];
417 _eina_file_map_key_hash(const unsigned long int *key, int key_length __UNUSED__)
419 return eina_hash_int64(&key[0], sizeof (unsigned long int))
420 ^ eina_hash_int64(&key[1], sizeof (unsigned long int));
425 _eina_file_map_populate(char *map, unsigned int size)
432 s = size > EINA_HUGE_PAGE ? EINA_HUGE_PAGE : EINA_SMALL_PAGE;
437 for (i = 0; i < size; i += s)
447 _eina_file_map_rule_apply(Eina_File_Populate rule, void *addr, unsigned long int size)
450 int flag = MADV_RANDOM;
454 case EINA_FILE_RANDOM: flag = MADV_RANDOM; break;
455 case EINA_FILE_SEQUENTIAL: flag = MADV_SEQUENTIAL; break;
456 case EINA_FILE_POPULATE: flag = MADV_WILLNEED; break;
457 case EINA_FILE_WILLNEED: flag = MADV_WILLNEED; break;
460 madvise(addr, size, flag);
463 if (rule == EINA_FILE_POPULATE)
464 tmp ^= _eina_file_map_populate(addr, size);
473 _eina_file_log_dom = eina_log_domain_register("eina_file",
474 EINA_LOG_COLOR_DEFAULT);
475 if (_eina_file_log_dom < 0)
477 EINA_LOG_ERR("Could not register log domain: eina_file");
481 _eina_file_cache = eina_hash_string_djb2_new(EINA_FREE_CB(_eina_file_real_close));
482 if (!_eina_file_cache)
484 ERR("Could not create cache.");
485 eina_log_domain_unregister(_eina_file_log_dom);
486 _eina_file_log_dom = -1;
494 eina_file_shutdown(void)
499 EINA_LIST_FREE(_eina_file_cache_delete, f)
500 _eina_file_real_close(f);
502 EINA_LIST_FOREACH(_eina_file_cache_lru, l, f)
503 eina_hash_del(_eina_file_cache, f->filename, f);
505 if (eina_hash_population(_eina_file_cache) > 0)
510 it = eina_hash_iterator_key_new(_eina_file_cache);
511 EINA_ITERATOR_FOREACH(it, key)
512 ERR("File [%s] still open !", key);
513 eina_iterator_free(it);
516 eina_hash_free(_eina_file_cache);
518 eina_log_domain_unregister(_eina_file_log_dom);
519 _eina_file_log_dom = -1;
527 /*============================================================================*
529 *============================================================================*/
531 /*============================================================================*
533 *============================================================================*/
536 eina_file_dir_list(const char *dir,
538 Eina_File_Dir_List_Cb cb,
541 Eina_File_Direct_Info *info;
544 EINA_SAFETY_ON_NULL_RETURN_VAL(cb, EINA_FALSE);
545 EINA_SAFETY_ON_NULL_RETURN_VAL(dir, EINA_FALSE);
546 EINA_SAFETY_ON_TRUE_RETURN_VAL(dir[0] == '\0', EINA_FALSE);
548 it = eina_file_stat_ls(dir);
552 EINA_ITERATOR_FOREACH(it, info)
554 cb(info->path + info->name_start, dir, data);
556 if (recursive == EINA_TRUE && info->type == EINA_FILE_DIR)
558 eina_file_dir_list(info->path, recursive, cb, data);
562 eina_iterator_free(it);
568 eina_file_split(char *path)
574 EINA_SAFETY_ON_NULL_RETURN_VAL(path, NULL);
576 ea = eina_array_new(16);
581 for (current = strchr(path, PATH_DELIM);
583 path = current + 1, current = strchr(path, PATH_DELIM))
585 length = current - path;
590 eina_array_push(ea, path);
595 eina_array_push(ea, path);
601 eina_file_ls(const char *dir)
603 Eina_File_Iterator *it;
609 length = strlen(dir);
613 it = calloc(1, sizeof (Eina_File_Iterator) + length);
617 EINA_MAGIC_SET(&it->iterator, EINA_MAGIC_ITERATOR);
619 it->dirp = opendir(dir);
626 memcpy(it->dir, dir, length + 1);
627 if (dir[length - 1] != '/')
630 it->length = length - 1;
632 it->iterator.version = EINA_ITERATOR_VERSION;
633 it->iterator.next = FUNC_ITERATOR_NEXT(_eina_file_ls_iterator_next);
634 it->iterator.get_container = FUNC_ITERATOR_GET_CONTAINER(
635 _eina_file_ls_iterator_container);
636 it->iterator.free = FUNC_ITERATOR_FREE(_eina_file_ls_iterator_free);
638 return &it->iterator;
642 eina_file_direct_ls(const char *dir)
644 Eina_File_Direct_Iterator *it;
650 length = strlen(dir);
654 if (length + NAME_MAX + 2 >= EINA_PATH_MAX)
657 it = calloc(1, sizeof(Eina_File_Direct_Iterator) + length);
661 EINA_MAGIC_SET(&it->iterator, EINA_MAGIC_ITERATOR);
663 it->dirp = opendir(dir);
670 memcpy(it->dir, dir, length + 1);
673 memcpy(it->info.path, dir, length);
674 if (dir[length - 1] == '/')
675 it->info.name_start = length;
678 it->info.path[length] = '/';
679 it->info.name_start = length + 1;
682 it->iterator.version = EINA_ITERATOR_VERSION;
683 it->iterator.next = FUNC_ITERATOR_NEXT(_eina_file_direct_ls_iterator_next);
684 it->iterator.get_container = FUNC_ITERATOR_GET_CONTAINER(
685 _eina_file_direct_ls_iterator_container);
686 it->iterator.free = FUNC_ITERATOR_FREE(_eina_file_direct_ls_iterator_free);
688 return &it->iterator;
692 eina_file_stat_ls(const char *dir)
694 Eina_File_Direct_Iterator *it;
700 length = strlen(dir);
704 if (length + NAME_MAX + 2 >= EINA_PATH_MAX)
707 it = calloc(1, sizeof(Eina_File_Direct_Iterator) + length);
711 EINA_MAGIC_SET(&it->iterator, EINA_MAGIC_ITERATOR);
713 it->dirp = opendir(dir);
720 memcpy(it->dir, dir, length + 1);
723 memcpy(it->info.path, dir, length);
724 if (dir[length - 1] == '/')
725 it->info.name_start = length;
728 it->info.path[length] = '/';
729 it->info.name_start = length + 1;
732 it->iterator.version = EINA_ITERATOR_VERSION;
733 it->iterator.next = FUNC_ITERATOR_NEXT(_eina_file_stat_ls_iterator_next);
734 it->iterator.get_container = FUNC_ITERATOR_GET_CONTAINER(
735 _eina_file_direct_ls_iterator_container);
736 it->iterator.free = FUNC_ITERATOR_FREE(_eina_file_direct_ls_iterator_free);
738 return &it->iterator;
742 eina_file_open(const char *filename, Eina_Bool shared)
746 struct stat file_stat;
749 Eina_Bool create = EINA_FALSE;
752 FIXME: always open absolute path
753 (need to fix filename according to current directory)
757 fd = shm_open(filename, O_RDONLY, S_IRWXU | S_IRWXG | S_IRWXO);
759 fd = open(filename, O_RDONLY, S_IRWXU | S_IRWXG | S_IRWXO);
761 if (fd < 0) return NULL;
763 flags = fcntl(fd, F_GETFD);
768 if (fcntl(fd, F_SETFD, flags) == -1)
771 if (fstat(fd, &file_stat))
774 file = eina_hash_find(_eina_file_cache, filename);
775 if (file && (file->mtime != file_stat.st_mtime
776 || file->length != (unsigned long long) file_stat.st_size
777 || file->inode != file_stat.st_ino))
781 if (file->refcount == 0)
783 _eina_file_cache_lru = eina_list_remove(_eina_file_cache_lru, file);
784 eina_hash_del(_eina_file_cache, file->filename, file);
788 else if (!file->delete_me)
790 file->delete_me = EINA_TRUE;
791 _eina_file_cache_delete = eina_list_prepend(_eina_file_cache_delete, file);
797 n = malloc(sizeof (Eina_File) + strlen(filename) + 1);
798 if (!n) goto on_error;
800 n->filename = (char*) (n + 1);
801 strcpy((char*) n->filename, filename);
802 n->map = eina_hash_new(EINA_KEY_LENGTH(_eina_file_map_key_length),
803 EINA_KEY_CMP(_eina_file_map_key_cmp),
804 EINA_KEY_HASH(_eina_file_map_key_hash),
805 EINA_FREE_CB(_eina_file_map_close),
807 n->rmap = eina_hash_pointer_new(NULL);
808 n->global_map = MAP_FAILED;
809 n->global_refcount = 0;
810 n->length = file_stat.st_size;
811 n->mtime = file_stat.st_mtime;
812 n->inode = file_stat.st_ino;
816 n->delete_me = EINA_FALSE;
818 if (file) eina_hash_del(_eina_file_cache, filename, file);
819 eina_hash_direct_add(_eina_file_cache, n->filename, n);
827 if (n->refcount == 0)
828 _eina_file_cache_lru = eina_list_remove(_eina_file_cache_lru, n);
841 eina_file_close(Eina_File *file)
845 if (file->refcount != 0) return ;
849 _eina_file_cache_delete = eina_list_remove(_eina_file_cache_delete, file);
850 _eina_file_real_close(file);
854 _eina_file_cache_lru = eina_list_prepend(_eina_file_cache_lru, file);
859 eina_file_size_get(Eina_File *file)
865 eina_file_mtime_get(Eina_File *file)
871 eina_file_filename_get(Eina_File *file)
873 return file->filename;
877 eina_file_map_all(Eina_File *file, Eina_File_Populate rule)
879 int flags = MAP_SHARED;
881 // bsd people will lack this feature
883 if (rule == EINA_FILE_POPULATE) flags |= MAP_POPULATE;
886 if (file->length > EINA_HUGE_PAGE) flags |= MAP_HUGETLB;
889 if (file->global_map == MAP_FAILED)
890 file->global_map = mmap(NULL, file->length, PROT_READ, flags, file->fd, 0);
892 if (file->global_map != MAP_FAILED)
894 _eina_file_map_rule_apply(rule, file->global_map, file->length);
895 file->global_refcount++;
896 return file->global_map;
902 eina_file_map_new(Eina_File *file, Eina_File_Populate rule,
903 unsigned long int offset, unsigned long int length)
906 unsigned long int key[2];
908 if (offset > file->length)
910 if (offset + length > file->length)
913 if (offset == 0 && length == file->length)
914 return eina_file_map_all(file, rule);
919 map = eina_hash_find(file->map, &key);
922 int flags = MAP_SHARED;
924 // bsd people will lack this feature
926 if (rule == EINA_FILE_POPULATE) flags |= MAP_POPULATE;
929 if (length > EINA_HUGE_PAGE) flags |= MAP_HUGETLB;
932 map = malloc(sizeof (Eina_File_Map));
933 if (!map) return NULL;
935 map->map = mmap(NULL, length, PROT_READ, flags, file->fd, offset);
936 map->offset = offset;
937 map->length = length;
940 if (map->map == MAP_FAILED)
946 eina_hash_add(file->map, &key, map);
947 eina_hash_direct_add(file->rmap, map->map, map);
952 _eina_file_map_rule_apply(rule, map->map, length);
958 eina_file_map_free(Eina_File *file, void *map)
960 if (file->global_map == map)
962 file->global_refcount--;
964 if (file->global_refcount > 0) return ;
966 munmap(file->global_map, file->length);
967 file->global_map = MAP_FAILED;
972 unsigned long int key[2];
974 em = eina_hash_find(file->rmap, &map);
979 if (em->refcount > 0) return ;
984 eina_hash_del(file->rmap, &map, em);
985 eina_hash_del(file->map, &key, em);