Eina: cosmetic (move eina_file_mmap_faulty to the 'global' part)
[profile/ivi/eina.git] / src / lib / eina_file.c
index 2f3dfac..9d58950 100644 (file)
@@ -1,6 +1,6 @@
 /* EINA - EFL data type library
  * Copyright (C) 2007-2008 Jorge Luis Zapata Muga, Vincent Torri
- * Copyright (C) 2010 Cedric Bail
+ * Copyright (C) 2010-2011 Cedric Bail
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -38,23 +38,22 @@ extern "C"
 void *alloca (size_t);
 #endif
 
+#include <stdlib.h>
 #include <string.h>
 #include <stddef.h>
-#include <dirent.h>
+#ifdef HAVE_DIRENT_H
+# include <dirent.h>
+#endif
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <unistd.h>
-#include <sys/mman.h>
+#ifdef HAVE_SYS_MMAN_H
+# include <sys/mman.h>
+#endif
 #include <fcntl.h>
 
 #define PATH_DELIM '/'
 
-#ifdef __sun
-# ifndef NAME_MAX
-#  define NAME_MAX 255
-# endif
-#endif
-
 #include "eina_config.h"
 #include "eina_private.h"
 
@@ -64,11 +63,23 @@ void *alloca (size_t);
 #include "eina_stringshare.h"
 #include "eina_hash.h"
 #include "eina_list.h"
+#include "eina_lock.h"
+#include "eina_mmap.h"
+#include "eina_log.h"
+#include "eina_xattr.h"
+
+#ifdef HAVE_ESCAPE_H
+# include <Escape.h>
+#endif
 
 /*============================================================================*
  *                                  Local                                     *
  *============================================================================*/
 
+/**
+ * @cond LOCAL
+ */
+
 #ifndef EINA_LOG_COLOR_DEFAULT
 #define EINA_LOG_COLOR_DEFAULT EINA_COLOR_CYAN
 #endif
@@ -88,12 +99,11 @@ void *alloca (size_t);
 #endif
 #define DBG(...) EINA_LOG_DOM_DBG(_eina_file_log_dom, __VA_ARGS__)
 
-/**
- * @cond LOCAL
- */
-typedef struct _Eina_File_Iterator Eina_File_Iterator;
-typedef struct _Eina_File_Map Eina_File_Map;
+#define EINA_SMALL_PAGE 4096
+# define EINA_HUGE_PAGE 16 * 1024 * 1024
 
+#ifdef HAVE_DIRENT_H
+typedef struct _Eina_File_Iterator Eina_File_Iterator;
 struct _Eina_File_Iterator
 {
    Eina_Iterator iterator;
@@ -103,6 +113,7 @@ struct _Eina_File_Iterator
 
    char dir[1];
 };
+#endif
 
 struct _Eina_File
 {
@@ -112,8 +123,14 @@ struct _Eina_File
    Eina_Hash *rmap;
    void *global_map;
 
-   unsigned long length;
+   Eina_Lock lock;
+
+   unsigned long long length;
    time_t mtime;
+   ino_t inode;
+#ifdef _STAT_VER_LINUX
+   unsigned long int mtime_nsec;
+#endif
 
    int refcount;
    int global_refcount;
@@ -122,8 +139,10 @@ struct _Eina_File
 
    Eina_Bool shared : 1;
    Eina_Bool delete_me : 1;
+   Eina_Bool global_faulty : 1;
 };
 
+typedef struct _Eina_File_Map Eina_File_Map;
 struct _Eina_File_Map
 {
    void *map;
@@ -132,11 +151,13 @@ struct _Eina_File_Map
    unsigned long int length;
 
    int refcount;
+
+   Eina_Bool hugetlb : 1;
+   Eina_Bool faulty : 1;
 };
 
 static Eina_Hash *_eina_file_cache = NULL;
-static Eina_List *_eina_file_cache_lru = NULL;
-static Eina_List *_eina_file_cache_delete = NULL;
+static Eina_Lock _eina_file_lock_cache;
 
 static int _eina_file_log_dom = -1;
 
@@ -145,11 +166,11 @@ static int _eina_file_log_dom = -1;
  * The code and description of the issue can be found at :
  * http://womble.decadent.org.uk/readdir_r-advisory.html
  */
-static size_t
-_eina_dirent_buffer_size(DIR *dirp)
+#ifdef HAVE_DIRENT_H
+static long
+_eina_name_max(DIR *dirp)
 {
    long name_max;
-   size_t name_end;
 
 #if defined(HAVE_FPATHCONF) && defined(HAVE_DIRFD) && defined(_PC_NAME_MAX)
    name_max = fpathconf(dirfd(dirp), _PC_NAME_MAX);
@@ -174,6 +195,16 @@ _eina_dirent_buffer_size(DIR *dirp)
 #  endif
 # endif
 #endif
+
+   return name_max;
+}
+
+static size_t
+_eina_dirent_buffer_size(DIR *dirp)
+{
+   long name_max = _eina_name_max(dirp);
+   size_t name_end;
+
    name_end = (size_t) offsetof(struct dirent, d_name) + name_max + 1;
 
    return (name_end > sizeof (struct dirent) ? name_end : sizeof (struct dirent));
@@ -331,49 +362,26 @@ _eina_file_direct_ls_iterator_free(Eina_File_Direct_Iterator *it)
 static Eina_Bool
 _eina_file_stat_ls_iterator_next(Eina_File_Direct_Iterator *it, void **data)
 {
-   struct stat st;
+   Eina_Stat st;
 
    if (!_eina_file_direct_ls_iterator_next(it, data))
      return EINA_FALSE;
 
    if (it->info.type == EINA_FILE_UNKNOWN)
      {
-#ifdef HAVE_FSTATAT
-        int fd;
-
-        fd = dirfd(it->dirp);
-        if (fstatat(fd, it->info.path + it->info.name_start, &st, 0))
-#else
-        if (stat(it->info.path, &st))
-#endif
+        if (eina_file_statat(it->dirp, &it->info, &st) != 0)
           it->info.type = EINA_FILE_UNKNOWN;
-        else
-          {
-             if (S_ISREG(st.st_mode))
-               it->info.type = EINA_FILE_REG;
-             else if (S_ISDIR(st.st_mode))
-               it->info.type = EINA_FILE_DIR;
-             else if (S_ISCHR(st.st_mode))
-               it->info.type = EINA_FILE_CHR;
-             else if (S_ISBLK(st.st_mode))
-               it->info.type = EINA_FILE_BLK;
-             else if (S_ISFIFO(st.st_mode))
-               it->info.type = EINA_FILE_FIFO;
-             else if (S_ISLNK(st.st_mode))
-               it->info.type = EINA_FILE_LNK;
-             else if (S_ISSOCK(st.st_mode))
-               it->info.type = EINA_FILE_SOCK;
-             else
-               it->info.type = EINA_FILE_UNKNOWN;
-          }
      }
 
    return EINA_TRUE;
 }
+#endif
 
 static void
 _eina_file_real_close(Eina_File *file)
 {
+   if (file->refcount != 0) return;
+
    eina_hash_free(file->rmap);
    eina_hash_free(file->map);
 
@@ -382,8 +390,6 @@ _eina_file_real_close(Eina_File *file)
 
    close(file->fd);
 
-   eina_stringshare_del(file->filename);
-
    free(file);
 }
 
@@ -415,24 +421,160 @@ _eina_file_map_key_hash(const unsigned long int *key, int key_length __UNUSED__)
      ^ eina_hash_int64(&key[1], sizeof (unsigned long int));
 }
 
-static void
-_eina_file_map_rule_apply(Eina_File_Populate rule, void *addr, unsigned long int size)
+#ifndef MAP_POPULATE
+static unsigned int
+_eina_file_map_populate(char *map, unsigned int size, Eina_Bool hugetlb)
 {
-   int flag;
+   unsigned int r = 0xDEADBEEF;
+   unsigned int i;
+   unsigned int s;
+
+   s = hugetlb ? EINA_HUGE_PAGE : EINA_SMALL_PAGE;
+
+   for (i = 0; i < size; i += s)
+     r ^= map[i];
+
+   r ^= map[size];
+
+   return r;
+}
+#endif
+
+static int
+_eina_file_map_rule_apply(Eina_File_Populate rule, void *addr, unsigned long int size, Eina_Bool hugetlb)
+{
+   int tmp = 42;
+   int flag = MADV_RANDOM;
 
    switch (rule)
      {
       case EINA_FILE_RANDOM: flag = MADV_RANDOM; break;
       case EINA_FILE_SEQUENTIAL: flag = MADV_SEQUENTIAL; break;
-      case EINA_FILE_WILLNEED:
-      case EINA_FILE_POPULATE:
-          flag = MADV_WILLNEED;
-          break;
+      case EINA_FILE_POPULATE: flag = MADV_WILLNEED; break;
+      case EINA_FILE_WILLNEED: flag = MADV_WILLNEED; break;
      }
 
    madvise(addr, size, flag);
+
+#ifndef MAP_POPULATE
+   if (rule == EINA_FILE_POPULATE)
+     tmp ^= _eina_file_map_populate(addr, size, hugetlb);
+#else
+   (void) hugetlb;
+#endif
+
+   return tmp;
 }
 
+static Eina_Bool
+_eina_file_timestamp_compare(Eina_File *f, struct stat *st)
+{
+   if (f->mtime != st->st_mtime) return EINA_FALSE;
+   if (f->length != (unsigned long long) st->st_size) return EINA_FALSE;
+   if (f->inode != st->st_ino) return EINA_FALSE;
+#ifdef _STAT_VER_LINUX
+# if (defined __USE_MISC && defined st_mtime)
+   if (f->mtime_nsec != (unsigned long int)st->st_mtim.tv_nsec)
+     return EINA_FALSE;
+# else
+   if (f->mtime_nsec != (unsigned long int)st->st_mtimensec)
+     return EINA_FALSE;
+# endif
+#endif
+   return EINA_TRUE;
+}
+
+static void
+slprintf(char *str, size_t size, const char *format, ...)
+{
+   va_list ap;
+
+   va_start(ap, format);
+
+   vsnprintf(str, size, format, ap);
+   str[size - 1] = 0;
+
+   va_end(ap);
+}
+
+static char *
+_eina_file_escape(const char *path, int *length)
+{
+   char *result = strdup(path ? path : "");
+   char *p = result;
+   char *q = result;
+   int len;
+
+   if (!result)
+     return NULL;
+
+   if (length) len = *length;
+   else len = strlen(result);
+
+   while ((p = strchr(p, '/')))
+     {
+       // remove double `/'
+       if (p[1] == '/')
+         {
+            memmove(p, p + 1, --len - (p - result));
+            result[len] = '\0';
+         }
+       else
+         if (p[1] == '.'
+             && p[2] == '.')
+           {
+              // remove `/../'
+              if (p[3] == '/')
+                {
+                   char tmp;
+
+                   len -= p + 3 - q;
+                   memmove(q, p + 3, len - (q - result));
+                   result[len] = '\0';
+                   p = q;
+
+                   /* Update q correctly. */
+                   tmp = *p;
+                   *p = '\0';
+                   q = strrchr(result, '/');
+                   if (!q) q = result;
+                   *p = tmp;
+                }
+              else
+                // remove '/..$'
+                if (p[3] == '\0')
+                  {
+                     len -= p + 2 - q;
+                     result[len] = '\0';
+                     q = p;
+                     ++p;
+                  }
+                else
+                  {
+                     q = p;
+                     ++p;
+                  }
+           }
+         else
+           {
+              q = p;
+              ++p;
+           }
+     }
+
+   if (length)
+     *length = len;
+   return result;
+}
+
+/**
+ * @endcond
+ */
+
+/*============================================================================*
+ *                                 Global                                     *
+ *============================================================================*/
+
 Eina_Bool
 eina_file_init(void)
 {
@@ -444,7 +586,7 @@ eina_file_init(void)
         return EINA_FALSE;
      }
 
-   _eina_file_cache = eina_hash_string_djb2_new(EINA_FREE_CB(_eina_file_real_close));
+   _eina_file_cache = eina_hash_string_djb2_new(NULL);
    if (!_eina_file_cache)
      {
         ERR("Could not create cache.");
@@ -453,21 +595,14 @@ eina_file_init(void)
         return EINA_FALSE;
      }
 
+   eina_lock_new(&_eina_file_lock_cache);
+
    return EINA_TRUE;
 }
 
 Eina_Bool
 eina_file_shutdown(void)
 {
-   Eina_File *f;
-   Eina_List *l;
-
-   EINA_LIST_FREE(_eina_file_cache_delete, f)
-     _eina_file_real_close(f);
-
-   EINA_LIST_FOREACH(_eina_file_cache_lru, l, f)
-     eina_hash_del(_eina_file_cache, f->filename, f);
-
    if (eina_hash_population(_eina_file_cache) > 0)
      {
         Eina_Iterator *it;
@@ -481,23 +616,101 @@ eina_file_shutdown(void)
 
    eina_hash_free(_eina_file_cache);
 
+   eina_lock_free(&_eina_file_lock_cache);
+
    eina_log_domain_unregister(_eina_file_log_dom);
    _eina_file_log_dom = -1;
-   return EINA_FALSE;
+   return EINA_TRUE;
 }
 
-/**
- * @endcond
- */
+void
+eina_file_mmap_faulty(void *addr, long page_size)
+{
+   Eina_File_Map *m;
+   Eina_File *f;
+   Eina_Iterator *itf;
+   Eina_Iterator *itm;
 
-/*============================================================================*
- *                                 Global                                     *
- *============================================================================*/
+   /* NOTE: I actually don't know if other thread are running, I will try to take the lock.
+      It may be possible that if other thread are not running and they were in the middle of
+      accessing an Eina_File this lock are still taken and we will result as a deadlock. */
+   eina_lock_take(&_eina_file_lock_cache);
+
+   itf = eina_hash_iterator_data_new(_eina_file_cache);
+   EINA_ITERATOR_FOREACH(itf, f)
+     {
+        Eina_Bool faulty = EINA_FALSE;
+
+        eina_lock_take(&f->lock);
+
+        if (f->global_map)
+          {
+             if ((unsigned char *) addr < (((unsigned char *)f->global_map) + f->length) &&
+                 (((unsigned char *) addr) + page_size) >= (unsigned char *) f->global_map)
+               {
+                  f->global_faulty = EINA_TRUE;
+                  faulty = EINA_TRUE;
+               }
+          }
+
+        if (!faulty)
+          {
+             itm = eina_hash_iterator_data_new(f->map);
+             EINA_ITERATOR_FOREACH(itm, m)
+               {
+                  if ((unsigned char *) addr < (((unsigned char *)m->map) + m->length) &&
+                      (((unsigned char *) addr) + page_size) >= (unsigned char *) m->map)
+                    {
+                       m->faulty = EINA_TRUE;
+                       faulty = EINA_TRUE;
+                       break;
+                    }
+               }
+             eina_iterator_free(itm);
+          }
+
+        eina_lock_release(&f->lock);
+
+        if (faulty) break;
+     }
+   eina_iterator_free(itf);
+
+   eina_lock_release(&_eina_file_lock_cache);
+}
 
 /*============================================================================*
  *                                   API                                      *
  *============================================================================*/
 
+EAPI char *
+eina_file_path_sanitize(const char *path)
+{
+   char *result = NULL;
+   int len;
+
+   if (!path) return NULL;
+
+   len = strlen(path);
+
+   if (*path != '/')
+     {
+        char cwd[PATH_MAX];
+        char *tmp = NULL;
+
+        tmp = getcwd(cwd, PATH_MAX);
+        if (!tmp) return NULL;
+
+        len += strlen(cwd) + 2;
+        tmp = alloca(sizeof (char) * len);
+
+        slprintf(tmp, len, "%s/%s", cwd, path);
+
+        result = tmp;
+     }
+
+   return _eina_file_escape(result ? result : path, &len);
+}
+
 EAPI Eina_Bool
 eina_file_dir_list(const char *dir,
                    Eina_Bool recursive,
@@ -566,11 +779,11 @@ eina_file_split(char *path)
 EAPI Eina_Iterator *
 eina_file_ls(const char *dir)
 {
+#ifdef HAVE_DIRENT_H
    Eina_File_Iterator *it;
    size_t length;
 
-   if (!dir)
-      return NULL;
+   EINA_SAFETY_ON_NULL_RETURN_VAL(dir, NULL);
 
    length = strlen(dir);
    if (length < 1)
@@ -602,24 +815,25 @@ eina_file_ls(const char *dir)
    it->iterator.free = FUNC_ITERATOR_FREE(_eina_file_ls_iterator_free);
 
    return &it->iterator;
+#else
+   (void) dir;
+   return NULL;
+#endif
 }
 
 EAPI Eina_Iterator *
 eina_file_direct_ls(const char *dir)
 {
+#ifdef HAVE_DIRENT_H
    Eina_File_Direct_Iterator *it;
    size_t length;
 
-   if (!dir)
-      return NULL;
+   EINA_SAFETY_ON_NULL_RETURN_VAL(dir, NULL);
 
    length = strlen(dir);
    if (length < 1)
       return NULL;
 
-   if (length + NAME_MAX + 2 >= EINA_PATH_MAX)
-      return NULL;
-
    it = calloc(1, sizeof(Eina_File_Direct_Iterator) + length);
    if (!it)
       return NULL;
@@ -633,6 +847,12 @@ eina_file_direct_ls(const char *dir)
         return NULL;
      }
 
+   if (length + _eina_name_max(it->dirp) + 2 >= EINA_PATH_MAX)
+     {
+        _eina_file_direct_ls_iterator_free(it);
+        return NULL;
+     }
+
    memcpy(it->dir,       dir, length + 1);
    it->length = length;
 
@@ -652,24 +872,25 @@ eina_file_direct_ls(const char *dir)
    it->iterator.free = FUNC_ITERATOR_FREE(_eina_file_direct_ls_iterator_free);
 
    return &it->iterator;
+#else
+   (void) dir;
+   return NULL;
+#endif
 }
 
 EAPI Eina_Iterator *
 eina_file_stat_ls(const char *dir)
 {
+#ifdef HAVE_DIRENT_H
    Eina_File_Direct_Iterator *it;
    size_t length;
 
-   if (!dir)
-      return NULL;
+   EINA_SAFETY_ON_NULL_RETURN_VAL(dir, NULL);
 
    length = strlen(dir);
    if (length < 1)
       return NULL;
 
-   if (length + NAME_MAX + 2 >= EINA_PATH_MAX)
-      return NULL;
-
    it = calloc(1, sizeof(Eina_File_Direct_Iterator) + length);
    if (!it)
       return NULL;
@@ -683,6 +904,12 @@ eina_file_stat_ls(const char *dir)
         return NULL;
      }
 
+   if (length + _eina_name_max(it->dirp) + 2 >= EINA_PATH_MAX)
+     {
+        _eina_file_direct_ls_iterator_free(it);
+        return NULL;
+     }
+
    memcpy(it->dir,       dir, length + 1);
    it->length = length;
 
@@ -702,59 +929,75 @@ eina_file_stat_ls(const char *dir)
    it->iterator.free = FUNC_ITERATOR_FREE(_eina_file_direct_ls_iterator_free);
 
    return &it->iterator;
+#else
+   (void) dir;
+   return NULL;
+#endif
 }
 
 EAPI Eina_File *
-eina_file_open(const char *filename, Eina_Bool shared)
+eina_file_open(const char *path, Eina_Bool shared)
 {
    Eina_File *file;
    Eina_File *n;
+   char *filename;
    struct stat file_stat;
-   int fd;
-   Eina_Bool create = EINA_FALSE;
+   int fd = -1;
+#ifdef HAVE_EXECVP
+   int flags;
+#endif
+
+   EINA_SAFETY_ON_NULL_RETURN_VAL(path, NULL);
 
-   /* FIXME: always open absolute path (need to fix filename according to current
-      directory) */
+   filename = eina_file_path_sanitize(path);
+   if (!filename) return NULL;
 
    if (shared)
-     fd = shm_open(filename, O_RDONLY | O_CLOEXEC, ACCESSPERMS);
+#ifdef HAVE_SHMOPEN
+     fd = shm_open(filename, O_RDONLY, S_IRWXU | S_IRWXG | S_IRWXO);
+#else
+     goto on_error;
+#endif
    else
-     fd = open(filename, O_RDONLY | O_CLOEXEC, ACCESSPERMS);
+     fd = open(filename, O_RDONLY, S_IRWXU | S_IRWXG | S_IRWXO);
 
-   if (fd < 0) return NULL;
+   if (fd < 0) goto on_error;
+
+#ifdef HAVE_EXECVP
+   flags = fcntl(fd, F_GETFD);
+   if (flags == -1)
+     goto on_error;
+
+   flags |= FD_CLOEXEC;
+   if (fcntl(fd, F_SETFD, flags) == -1)
+     goto on_error;
+#endif
 
    if (fstat(fd, &file_stat))
-     {
-        close(fd);
-        return NULL;
-     }
+     goto on_error;
+
+   eina_lock_take(&_eina_file_lock_cache);
 
    file = eina_hash_find(_eina_file_cache, filename);
-   if (file && (file->mtime != file_stat.st_mtime
-                || file->length != file_stat.st_size))
+   if ((file) && _eina_file_timestamp_compare(file, &file_stat))
      {
-        create = EINA_TRUE;
-
-        if (file->refcount == 0)
-          {
-             _eina_file_cache_lru = eina_list_prepend(_eina_file_cache_lru, file);
-             eina_hash_del(_eina_file_cache, file->filename, file);
-
-             file = NULL;
-          }
-        else if (!file->delete_me)
-          {
-             file->delete_me = EINA_TRUE;
-             _eina_file_cache_delete = eina_list_prepend(_eina_file_cache_delete, file);
-          }
+        file->delete_me = EINA_TRUE;
+        eina_hash_del(_eina_file_cache, file->filename, file);
+        _eina_file_real_close(file);
+        file = NULL;
      }
 
-   if (!file || create)
+   if (!file)
      {
-        n = malloc(sizeof (Eina_File));
-        if (!n) goto on_error;
-
-        n->filename = eina_stringshare_add(filename);
+        n = malloc(sizeof (Eina_File) + strlen(filename) + 1);
+        if (!n)
+         {
+             eina_lock_release(&_eina_file_lock_cache);
+             goto on_error;
+         }
+
+        n->filename = (char*) (n + 1);
+        strcpy((char*) n->filename, filename);
         n->map = eina_hash_new(EINA_KEY_LENGTH(_eina_file_map_key_length),
                                EINA_KEY_CMP(_eina_file_map_key_cmp),
                                EINA_KEY_HASH(_eina_file_map_key_hash),
@@ -762,67 +1005,81 @@ eina_file_open(const char *filename, Eina_Bool shared)
                                3);
         n->rmap = eina_hash_pointer_new(NULL);
         n->global_map = MAP_FAILED;
+       n->global_refcount = 0;
         n->length = file_stat.st_size;
         n->mtime = file_stat.st_mtime;
+#ifdef _STAT_VER_LINUX
+# if (defined __USE_MISC && defined st_mtime)
+        n->mtime_nsec = (unsigned long int)file_stat.st_mtim.tv_nsec;
+# else
+        n->mtime_nsec = (unsigned long int)file_stat.st_mtimensec;
+# endif
+#endif
+        n->inode = file_stat.st_ino;
         n->refcount = 0;
         n->fd = fd;
         n->shared = shared;
         n->delete_me = EINA_FALSE;
-
-        eina_hash_set(_eina_file_cache, filename, n);
+        eina_lock_new(&n->lock);
+        eina_hash_direct_add(_eina_file_cache, n->filename, n);
      }
    else
      {
         close(fd);
-
         n = file;
-
-        if (n->refcount == 0)
-          _eina_file_cache_lru = eina_list_remove(_eina_file_cache_lru, n);
      }
-
+   eina_lock_take(&n->lock);
    n->refcount++;
+   eina_lock_release(&n->lock);
+
+   eina_lock_release(&_eina_file_lock_cache);
+
+   free(filename);
 
    return n;
 
  on_error:
-   close(fd);
+   free(filename);
+   if (fd >= 0) close(fd);
    return NULL;
 }
 
 EAPI void
 eina_file_close(Eina_File *file)
 {
+   EINA_SAFETY_ON_NULL_RETURN(file);
+
+   eina_lock_take(&file->lock);
    file->refcount--;
+   eina_lock_release(&file->lock);
 
-   if (file->refcount != 0) return ;
+   if (file->refcount != 0) return;
+   eina_lock_take(&_eina_file_lock_cache);
 
-   if (file->delete_me)
-     {
-        _eina_file_cache_delete = eina_list_remove(_eina_file_cache_delete, file);
-        _eina_file_real_close(file);
-     }
-   else
-     {
-        _eina_file_cache_lru = eina_list_prepend(_eina_file_cache_lru, file);
-     }
+   eina_hash_del(_eina_file_cache, file->filename, file);
+   _eina_file_real_close(file);
+   
+   eina_lock_release(&_eina_file_lock_cache);
 }
 
-EAPI unsigned long int
+EAPI size_t
 eina_file_size_get(Eina_File *file)
 {
+   EINA_SAFETY_ON_NULL_RETURN_VAL(file, 0);
    return file->length;
 }
 
 EAPI time_t
 eina_file_mtime_get(Eina_File *file)
 {
+   EINA_SAFETY_ON_NULL_RETURN_VAL(file, 0);
    return file->mtime;
 }
 
 EAPI const char *
 eina_file_filename_get(Eina_File *file)
 {
+   EINA_SAFETY_ON_NULL_RETURN_VAL(file, NULL);
    return file->filename;
 }
 
@@ -830,19 +1087,44 @@ EAPI void *
 eina_file_map_all(Eina_File *file, Eina_File_Populate rule)
 {
    int flags = MAP_SHARED;
+   void *ret = NULL;
+
+   EINA_SAFETY_ON_NULL_RETURN_VAL(file, NULL);
 
+// bsd people will lack this feature
+#ifdef MAP_POPULATE
    if (rule == EINA_FILE_POPULATE) flags |= MAP_POPULATE;
+#endif
+#ifdef MAP_HUGETLB
+   if (file->length > EINA_HUGE_PAGE) flags |= MAP_HUGETLB;
+#endif
 
+   eina_mmap_safety_enabled_set(EINA_TRUE);
+   eina_lock_take(&file->lock);
    if (file->global_map == MAP_FAILED)
      file->global_map = mmap(NULL, file->length, PROT_READ, flags, file->fd, 0);
+#ifdef MAP_HUGETLB
+   if ((file->global_map == MAP_FAILED) && (flags & MAP_HUGETLB))
+     {
+       flags &= ~MAP_HUGETLB;
+       file->global_map = mmap(NULL, file->length, PROT_READ, flags, file->fd, 0);
+     }
+#endif
 
    if (file->global_map != MAP_FAILED)
      {
-        _eina_file_map_rule_apply(rule, file->global_map, file->length);
+        Eina_Bool hugetlb = EINA_FALSE;
+
+#ifdef MAP_HUGETLB
+        hugetlb = !!(flags & MAP_HUGETLB);
+#endif
+        _eina_file_map_rule_apply(rule, file->global_map, file->length, hugetlb);
         file->global_refcount++;
-        return file->global_map;
+        ret = file->global_map;
      }
-   return NULL;
+
+   eina_lock_release(&file->lock);
+   return ret;
 }
 
 EAPI void *
@@ -852,6 +1134,8 @@ eina_file_map_new(Eina_File *file, Eina_File_Populate rule,
    Eina_File_Map *map;
    unsigned long int key[2];
 
+   EINA_SAFETY_ON_NULL_RETURN_VAL(file, NULL);
+
    if (offset > file->length)
      return NULL;
    if (offset + length > file->length)
@@ -863,26 +1147,42 @@ eina_file_map_new(Eina_File *file, Eina_File_Populate rule,
    key[0] = offset;
    key[1] = length;
 
+   eina_mmap_safety_enabled_set(EINA_TRUE);
+   eina_lock_take(&file->lock);
+
    map = eina_hash_find(file->map, &key);
    if (!map)
      {
         int flags = MAP_SHARED;
 
+// bsd people will lack this feature
+#ifdef MAP_POPULATE
         if (rule == EINA_FILE_POPULATE) flags |= MAP_POPULATE;
+#endif
+#ifdef MAP_HUGETLB
+        if (length > EINA_HUGE_PAGE) flags |= MAP_HUGETLB;
+#endif
 
         map = malloc(sizeof (Eina_File_Map));
-        if (!map) return NULL;
+        if (!map) goto on_error;
 
         map->map = mmap(NULL, length, PROT_READ, flags, file->fd, offset);
+#ifdef MAP_HUGETLB
+        if (map->map == MAP_FAILED && (flags & MAP_HUGETLB))
+          {
+             flags &= ~MAP_HUGETLB;
+             map->map = mmap(NULL, length, PROT_READ, flags, file->fd, offset);
+          }
+
+        map->hugetlb = !!(flags & MAP_HUGETLB);
+#else
+        map->hugetlb = EINA_FALSE;
+#endif
         map->offset = offset;
         map->length = length;
         map->refcount = 0;
 
-        if (map->map == MAP_FAILED)
-          {
-             free(map);
-             return NULL;
-          }
+        if (map->map == MAP_FAILED) goto on_error;
 
         eina_hash_add(file->map, &key, map);
         eina_hash_direct_add(file->rmap, map->map, map);
@@ -890,19 +1190,31 @@ eina_file_map_new(Eina_File *file, Eina_File_Populate rule,
 
    map->refcount++;
 
-   _eina_file_map_rule_apply(rule, map->map, length);
+   _eina_file_map_rule_apply(rule, map->map, length, map->hugetlb);
+
+   eina_lock_release(&file->lock);
 
    return map->map;
+
+ on_error:
+   free(map);
+   eina_lock_release(&file->lock);
+
+   return NULL;
 }
 
 EAPI void
 eina_file_map_free(Eina_File *file, void *map)
 {
+   EINA_SAFETY_ON_NULL_RETURN(file);
+
+   eina_lock_take(&file->lock);
+
    if (file->global_map == map)
      {
         file->global_refcount--;
 
-        if (file->global_refcount > 0) return ;
+        if (file->global_refcount > 0) goto on_exit;
 
         munmap(file->global_map, file->length);
         file->global_map = MAP_FAILED;
@@ -917,7 +1229,7 @@ eina_file_map_free(Eina_File *file, void *map)
 
         em->refcount--;
 
-        if (em->refcount > 0) return ;
+        if (em->refcount > 0) goto on_exit;
 
         key[0] = em->offset;
         key[1] = em->length;
@@ -925,6 +1237,114 @@ eina_file_map_free(Eina_File *file, void *map)
         eina_hash_del(file->rmap, &map, em);
         eina_hash_del(file->map, &key, em);
      }
+
+ on_exit:
+   eina_lock_release(&file->lock);
+}
+
+EAPI Eina_Bool
+eina_file_map_faulted(Eina_File *file, void *map)
+{
+   Eina_File_Map *em;
+
+   EINA_SAFETY_ON_NULL_RETURN_VAL(file, EINA_FALSE);
+
+   eina_lock_take(&file->lock);
+
+   if (file->global_map == map) return file->global_faulty;
+
+   em = eina_hash_find(file->rmap, &map);
+   if (!em) return EINA_FALSE;
+
+   return em->faulty;
 }
 
+EAPI Eina_Iterator *
+eina_file_xattr_get(Eina_File *file)
+{
+   EINA_SAFETY_ON_NULL_RETURN_VAL(file, NULL);
+
+   return eina_xattr_fd_ls(file->fd);
+}
+
+EAPI Eina_Iterator *
+eina_file_xattr_value_get(Eina_File *file)
+{
+   EINA_SAFETY_ON_NULL_RETURN_VAL(file, NULL);
+
+   return eina_xattr_value_fd_ls(file->fd);
+}
+
+EAPI int
+eina_file_statat(void *container, Eina_File_Direct_Info *info, Eina_Stat *st)
+{
+   struct stat buf;
+#ifdef HAVE_FSTATAT
+   int fd;
+#endif
 
+   EINA_SAFETY_ON_NULL_RETURN_VAL(info, -1);
+   EINA_SAFETY_ON_NULL_RETURN_VAL(st, -1);
+
+#ifdef HAVE_FSTATAT
+   fd = dirfd(container);
+   if (fstatat(fd, info->path + info->name_start, &buf, 0))
+#else
+   if (stat(info->path, &buf))
+#endif
+     {
+        if (info->type != EINA_FILE_LNK)
+          info->type = EINA_FILE_UNKNOWN;
+        return -1;
+     }
+
+   if (info->type == EINA_FILE_UNKNOWN)
+     {
+        if (S_ISREG(buf.st_mode))
+          info->type = EINA_FILE_REG;
+        else if (S_ISDIR(buf.st_mode))
+          info->type = EINA_FILE_DIR;
+        else if (S_ISCHR(buf.st_mode))
+          info->type = EINA_FILE_CHR;
+        else if (S_ISBLK(buf.st_mode))
+          info->type = EINA_FILE_BLK;
+        else if (S_ISFIFO(buf.st_mode))
+          info->type = EINA_FILE_FIFO;
+        else if (S_ISLNK(buf.st_mode))
+          info->type = EINA_FILE_LNK;
+        else if (S_ISSOCK(buf.st_mode))
+          info->type = EINA_FILE_SOCK;
+        else
+          info->type = EINA_FILE_UNKNOWN;
+     }
+
+   st->dev = buf.st_dev;
+   st->ino = buf.st_ino;
+   st->mode = buf.st_mode;
+   st->nlink = buf.st_nlink;
+   st->uid = buf.st_uid;
+   st->gid = buf.st_gid;
+   st->rdev = buf.st_rdev;
+   st->size = buf.st_size;
+   st->blksize = buf.st_blksize;
+   st->blocks = buf.st_blocks;
+   st->atime = buf.st_atime;
+   st->mtime = buf.st_mtime;
+   st->ctime = buf.st_ctime;
+#ifdef _STAT_VER_LINUX
+# if (defined __USE_MISC && defined st_mtime)
+   st->atimensec = buf.st_atim.tv_nsec;
+   st->mtimensec = buf.st_mtim.tv_nsec;
+   st->ctimensec = buf.st_ctim.tv_nsec;
+# else
+   st->atimensec = buf.st_atimensec;
+   st->mtimensec = buf.st_mtimensec;
+   st->ctimensec = buf.st_ctimensec;
+# endif
+#else
+   st->atimensec = 0;
+   st->mtimensec = 0;
+   st->ctimensec = 0;
+#endif
+   return 0;
+}