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>
51 # include <sys/xattr.h>
54 #define PATH_DELIM '/'
62 #include "eina_config.h"
63 #include "eina_private.h"
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"
74 /*============================================================================*
76 *============================================================================*/
82 #ifndef EINA_LOG_COLOR_DEFAULT
83 #define EINA_LOG_COLOR_DEFAULT EINA_COLOR_CYAN
89 #define ERR(...) EINA_LOG_DOM_ERR(_eina_file_log_dom, __VA_ARGS__)
94 #define WRN(...) EINA_LOG_DOM_WARN(_eina_file_log_dom, __VA_ARGS__)
99 #define DBG(...) EINA_LOG_DOM_DBG(_eina_file_log_dom, __VA_ARGS__)
101 #define EINA_SMALL_PAGE 4096
102 # define EINA_HUGE_PAGE 16 * 1024 * 1024
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;
108 struct _Eina_File_Iterator
110 Eina_Iterator iterator;
118 struct _Eina_Xattr_Iterator
120 Eina_Iterator iterator;
130 const char *filename;
138 unsigned long long length;
147 Eina_Bool shared : 1;
148 Eina_Bool delete_me : 1;
151 struct _Eina_File_Map
155 unsigned long int offset;
156 unsigned long int length;
161 static Eina_Hash *_eina_file_cache = NULL;
162 static Eina_Lock _eina_file_lock_cache;
164 static int _eina_file_log_dom = -1;
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
172 _eina_dirent_buffer_size(DIR *dirp)
177 #if defined(HAVE_FPATHCONF) && defined(HAVE_DIRFD) && defined(_PC_NAME_MAX)
178 name_max = fpathconf(dirfd(dirp), _PC_NAME_MAX);
182 # if defined(NAME_MAX)
183 name_max = (NAME_MAX > 255) ? NAME_MAX : 255;
189 # if defined(NAME_MAX)
190 name_max = (NAME_MAX > 255) ? NAME_MAX : 255;
193 # warning "buffer size for readdir_r cannot be determined safely, best effort, but racy"
194 name_max = pathconf(dirp, _PC_NAME_MAX);
196 # error "buffer size for readdir_r cannot be determined safely"
200 name_end = (size_t) offsetof(struct dirent, d_name) + name_max + 1;
202 return (name_end > sizeof (struct dirent) ? name_end : sizeof (struct dirent));
206 _eina_file_ls_iterator_next(Eina_File_Iterator *it, void **data)
212 dp = alloca(_eina_dirent_buffer_size(it->dirp));
216 if (readdir_r(it->dirp, dp, &dp))
221 while ((dp->d_name[0] == '.') &&
222 ((dp->d_name[1] == '\0') ||
223 ((dp->d_name[1] == '.') && (dp->d_name[2] == '\0'))));
225 #ifdef _DIRENT_HAVE_D_NAMLEN
226 length = dp->d_namlen;
228 length = strlen(dp->d_name);
230 name = alloca(length + 2 + it->length);
232 memcpy(name, it->dir, it->length);
233 memcpy(name + it->length, "/", 1);
234 memcpy(name + it->length + 1, dp->d_name, length + 1);
236 *data = (char *)eina_stringshare_add(name);
241 _eina_file_ls_iterator_container(Eina_File_Iterator *it)
247 _eina_file_ls_iterator_free(Eina_File_Iterator *it)
251 EINA_MAGIC_SET(&it->iterator, 0);
255 typedef struct _Eina_File_Direct_Iterator Eina_File_Direct_Iterator;
256 struct _Eina_File_Direct_Iterator
258 Eina_Iterator iterator;
263 Eina_File_Direct_Info info;
269 _eina_file_direct_ls_iterator_next(Eina_File_Direct_Iterator *it, void **data)
274 dp = alloca(_eina_dirent_buffer_size(it->dirp));
278 if (readdir_r(it->dirp, dp, &dp))
283 #ifdef _DIRENT_HAVE_D_NAMLEN
284 length = dp->d_namlen;
286 length = strlen(dp->d_name);
288 if (it->info.name_start + length + 1 >= EINA_PATH_MAX)
291 while ((dp->d_name[0] == '.') &&
292 ((dp->d_name[1] == '\0') ||
293 ((dp->d_name[1] == '.') && (dp->d_name[2] == '\0'))));
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';
300 #ifdef _DIRENT_HAVE_D_TYPE
304 it->info.type = EINA_FILE_FIFO;
307 it->info.type = EINA_FILE_CHR;
310 it->info.type = EINA_FILE_DIR;
313 it->info.type = EINA_FILE_BLK;
316 it->info.type = EINA_FILE_REG;
319 it->info.type = EINA_FILE_LNK;
322 it->info.type = EINA_FILE_SOCK;
325 it->info.type = EINA_FILE_WHT;
328 it->info.type = EINA_FILE_UNKNOWN;
332 it->info.type = EINA_FILE_UNKNOWN;
340 _eina_file_direct_ls_iterator_container(Eina_File_Direct_Iterator *it)
346 _eina_file_direct_ls_iterator_free(Eina_File_Direct_Iterator *it)
350 EINA_MAGIC_SET(&it->iterator, 0);
355 _eina_file_stat_ls_iterator_next(Eina_File_Direct_Iterator *it, void **data)
359 if (!_eina_file_direct_ls_iterator_next(it, data))
362 if (it->info.type == EINA_FILE_UNKNOWN)
367 fd = dirfd(it->dirp);
368 if (fstatat(fd, it->info.path + it->info.name_start, &st, 0))
370 if (stat(it->info.path, &st))
372 it->info.type = EINA_FILE_UNKNOWN;
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;
390 it->info.type = EINA_FILE_UNKNOWN;
399 _eina_xattr_ls_iterator_next(Eina_Xattr_Iterator *it, void **data)
401 if (it->offset >= it->length)
404 *data = it->xattr + it->offset;
405 it->offset += strlen(it->xattr + it->offset) + 1;
411 _eina_xattr_ls_iterator_container(Eina_Xattr_Iterator *it __UNUSED__)
417 _eina_xattr_ls_iterator_free(Eina_Xattr_Iterator *it)
419 EINA_MAGIC_SET(&it->iterator, 0);
425 _eina_file_real_close(Eina_File *file)
427 if (file->refcount != 0) return;
429 eina_hash_free(file->rmap);
430 eina_hash_free(file->map);
432 if (file->global_map != MAP_FAILED)
433 munmap(file->global_map, file->length);
441 _eina_file_map_close(Eina_File_Map *map)
443 munmap(map->map, map->length);
448 _eina_file_map_key_length(const void *key __UNUSED__)
450 return sizeof (unsigned long int) * 2;
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__)
457 if (key1[0] - key2[0] == 0) return key1[1] - key2[1];
458 return key1[0] - key2[0];
462 _eina_file_map_key_hash(const unsigned long int *key, int key_length __UNUSED__)
464 return eina_hash_int64(&key[0], sizeof (unsigned long int))
465 ^ eina_hash_int64(&key[1], sizeof (unsigned long int));
470 _eina_file_map_populate(char *map, unsigned int size)
477 s = size > EINA_HUGE_PAGE ? EINA_HUGE_PAGE : EINA_SMALL_PAGE;
482 for (i = 0; i < size; i += s)
492 _eina_file_map_rule_apply(Eina_File_Populate rule, void *addr, unsigned long int size)
495 int flag = MADV_RANDOM;
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;
505 madvise(addr, size, flag);
508 if (rule == EINA_FILE_POPULATE)
509 tmp ^= _eina_file_map_populate(addr, size);
518 _eina_file_log_dom = eina_log_domain_register("eina_file",
519 EINA_LOG_COLOR_DEFAULT);
520 if (_eina_file_log_dom < 0)
522 EINA_LOG_ERR("Could not register log domain: eina_file");
526 _eina_file_cache = eina_hash_string_djb2_new(EINA_FREE_CB(_eina_file_real_close));
527 if (!_eina_file_cache)
529 ERR("Could not create cache.");
530 eina_log_domain_unregister(_eina_file_log_dom);
531 _eina_file_log_dom = -1;
535 eina_lock_new(&_eina_file_lock_cache);
541 eina_file_shutdown(void)
543 if (eina_hash_population(_eina_file_cache) > 0)
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);
554 eina_hash_free(_eina_file_cache);
556 eina_lock_free(&_eina_file_lock_cache);
558 eina_log_domain_unregister(_eina_file_log_dom);
559 _eina_file_log_dom = -1;
567 /*============================================================================*
569 *============================================================================*/
571 /*============================================================================*
573 *============================================================================*/
576 eina_file_dir_list(const char *dir,
578 Eina_File_Dir_List_Cb cb,
581 Eina_File_Direct_Info *info;
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);
588 it = eina_file_stat_ls(dir);
592 EINA_ITERATOR_FOREACH(it, info)
594 cb(info->path + info->name_start, dir, data);
596 if (recursive == EINA_TRUE && info->type == EINA_FILE_DIR)
598 eina_file_dir_list(info->path, recursive, cb, data);
602 eina_iterator_free(it);
608 eina_file_split(char *path)
614 EINA_SAFETY_ON_NULL_RETURN_VAL(path, NULL);
616 ea = eina_array_new(16);
621 for (current = strchr(path, PATH_DELIM);
623 path = current + 1, current = strchr(path, PATH_DELIM))
625 length = current - path;
630 eina_array_push(ea, path);
635 eina_array_push(ea, path);
641 eina_file_ls(const char *dir)
643 Eina_File_Iterator *it;
646 EINA_SAFETY_ON_NULL_RETURN_VAL(dir, NULL);
648 length = strlen(dir);
652 it = calloc(1, sizeof (Eina_File_Iterator) + length);
656 EINA_MAGIC_SET(&it->iterator, EINA_MAGIC_ITERATOR);
658 it->dirp = opendir(dir);
665 memcpy(it->dir, dir, length + 1);
666 if (dir[length - 1] != '/')
669 it->length = length - 1;
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);
677 return &it->iterator;
681 eina_file_direct_ls(const char *dir)
683 Eina_File_Direct_Iterator *it;
686 EINA_SAFETY_ON_NULL_RETURN_VAL(dir, NULL);
688 length = strlen(dir);
692 if (length + NAME_MAX + 2 >= EINA_PATH_MAX)
695 it = calloc(1, sizeof(Eina_File_Direct_Iterator) + length);
699 EINA_MAGIC_SET(&it->iterator, EINA_MAGIC_ITERATOR);
701 it->dirp = opendir(dir);
708 memcpy(it->dir, dir, length + 1);
711 memcpy(it->info.path, dir, length);
712 if (dir[length - 1] == '/')
713 it->info.name_start = length;
716 it->info.path[length] = '/';
717 it->info.name_start = length + 1;
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);
726 return &it->iterator;
730 eina_file_stat_ls(const char *dir)
732 Eina_File_Direct_Iterator *it;
735 EINA_SAFETY_ON_NULL_RETURN_VAL(dir, NULL);
737 length = strlen(dir);
741 if (length + NAME_MAX + 2 >= EINA_PATH_MAX)
744 it = calloc(1, sizeof(Eina_File_Direct_Iterator) + length);
748 EINA_MAGIC_SET(&it->iterator, EINA_MAGIC_ITERATOR);
750 it->dirp = opendir(dir);
757 memcpy(it->dir, dir, length + 1);
760 memcpy(it->info.path, dir, length);
761 if (dir[length - 1] == '/')
762 it->info.name_start = length;
765 it->info.path[length] = '/';
766 it->info.name_start = length + 1;
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);
775 return &it->iterator;
779 eina_xattr_ls(const char *file)
781 Eina_Xattr_Iterator *it;
784 EINA_SAFETY_ON_NULL_RETURN_VAL(file, NULL);
787 length = listxattr(file, NULL, 0);
788 if (length <= 0) return NULL;
790 it = calloc(1, sizeof (Eina_Xattr_Iterator) + length - 1);
791 if (!it) return NULL;
793 EINA_MAGIC_SET(&it->iterator, EINA_MAGIC_ITERATOR);
795 it->length = listxattr(file, it->xattr, length);
796 if (it->length != length)
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);
807 return &it->iterator;
814 eina_xattr_get(const char *file, const char *attribute, ssize_t *size)
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);
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))
829 if (!ret) return NULL;
831 tmp = getxattr(file, attribute, ret, *size);
844 eina_xattr_set(const char *file, const char *attribute, const void *data, ssize_t length, Eina_Xattr_Flags flags)
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);
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;
862 if (setxattr(file, attribute, data, length, iflags))
868 eina_file_open(const char *filename, Eina_Bool shared)
872 struct stat file_stat;
876 EINA_SAFETY_ON_NULL_RETURN_VAL(filename, NULL);
879 FIXME: always open absolute path
880 (need to fix filename according to current directory)
885 fd = shm_open(filename, O_RDONLY, S_IRWXU | S_IRWXG | S_IRWXO);
890 fd = open(filename, O_RDONLY, S_IRWXU | S_IRWXG | S_IRWXO);
892 if (fd < 0) return NULL;
894 flags = fcntl(fd, F_GETFD);
899 if (fcntl(fd, F_SETFD, flags) == -1)
902 if (fstat(fd, &file_stat))
905 eina_lock_take(&_eina_file_lock_cache);
907 file = eina_hash_find(_eina_file_cache, filename);
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
914 file->delete_me = EINA_TRUE;
915 eina_hash_del(_eina_file_cache, file->filename, file);
921 n = malloc(sizeof (Eina_File) + strlen(filename) + 1);
922 if (!n) goto on_error;
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),
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;
940 n->delete_me = EINA_FALSE;
941 eina_lock_new(&n->lock);
942 eina_hash_direct_add(_eina_file_cache, n->filename, n);
949 eina_lock_take(&n->lock);
951 eina_lock_release(&n->lock);
953 eina_lock_release(&_eina_file_lock_cache);
963 eina_file_close(Eina_File *file)
965 EINA_SAFETY_ON_NULL_RETURN(file);
967 eina_lock_take(&file->lock);
969 eina_lock_release(&file->lock);
971 if (file->refcount != 0) return;
972 eina_hash_del(_eina_file_cache, file->filename, file);
976 eina_file_size_get(Eina_File *file)
978 EINA_SAFETY_ON_NULL_RETURN_VAL(file, 0);
983 eina_file_mtime_get(Eina_File *file)
985 EINA_SAFETY_ON_NULL_RETURN_VAL(file, 0);
990 eina_file_filename_get(Eina_File *file)
992 EINA_SAFETY_ON_NULL_RETURN_VAL(file, NULL);
993 return file->filename;
997 eina_file_map_all(Eina_File *file, Eina_File_Populate rule)
999 int flags = MAP_SHARED;
1002 EINA_SAFETY_ON_NULL_RETURN_VAL(file, NULL);
1004 // bsd people will lack this feature
1006 if (rule == EINA_FILE_POPULATE) flags |= MAP_POPULATE;
1009 if (file->length > EINA_HUGE_PAGE) flags |= MAP_HUGETLB;
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);
1017 if (file->global_map != MAP_FAILED)
1019 _eina_file_map_rule_apply(rule, file->global_map, file->length);
1020 file->global_refcount++;
1021 ret = file->global_map;
1024 eina_lock_release(&file->lock);
1029 eina_file_map_new(Eina_File *file, Eina_File_Populate rule,
1030 unsigned long int offset, unsigned long int length)
1033 unsigned long int key[2];
1035 EINA_SAFETY_ON_NULL_RETURN_VAL(file, NULL);
1037 if (offset > file->length)
1039 if (offset + length > file->length)
1042 if (offset == 0 && length == file->length)
1043 return eina_file_map_all(file, rule);
1048 eina_mmap_safety_enabled_set(EINA_TRUE);
1049 eina_lock_take(&file->lock);
1051 map = eina_hash_find(file->map, &key);
1054 int flags = MAP_SHARED;
1056 // bsd people will lack this feature
1058 if (rule == EINA_FILE_POPULATE) flags |= MAP_POPULATE;
1061 if (length > EINA_HUGE_PAGE) flags |= MAP_HUGETLB;
1064 map = malloc(sizeof (Eina_File_Map));
1065 if (!map) goto on_error;
1067 map->map = mmap(NULL, length, PROT_READ, flags, file->fd, offset);
1068 map->offset = offset;
1069 map->length = length;
1072 if (map->map == MAP_FAILED) goto on_error;
1074 eina_hash_add(file->map, &key, map);
1075 eina_hash_direct_add(file->rmap, map->map, map);
1080 _eina_file_map_rule_apply(rule, map->map, length);
1082 eina_lock_release(&file->lock);
1088 eina_lock_release(&file->lock);
1094 eina_file_map_free(Eina_File *file, void *map)
1096 EINA_SAFETY_ON_NULL_RETURN(file);
1098 eina_lock_take(&file->lock);
1100 if (file->global_map == map)
1102 file->global_refcount--;
1104 if (file->global_refcount > 0) goto on_exit;
1106 munmap(file->global_map, file->length);
1107 file->global_map = MAP_FAILED;
1112 unsigned long int key[2];
1114 em = eina_hash_find(file->rmap, &map);
1119 if (em->refcount > 0) goto on_exit;
1121 key[0] = em->offset;
1122 key[1] = em->length;
1124 eina_hash_del(file->rmap, &map, em);
1125 eina_hash_del(file->map, &key, em);
1129 eina_lock_release(&file->lock);