efl/src/lib/eina/eina_file_common.c

1191 lines
27 KiB
C

/* EINA - EFL data type library
* Copyright (C) 2013 Cedric Bail
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library;
* if not, see <http://www.gnu.org/licenses/>.
*/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include <string.h>
#include <stdint.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#ifdef _WIN32
# include <evil_private.h> /* windows.h fcntl mkstemps mkdtemp */
#endif
#define COPY_BLOCKSIZE (4 * 1024 * 1024)
#include "eina_config.h"
#include "eina_private.h"
#include "eina_hash.h"
#include "eina_safety_checks.h"
#include "eina_file_common.h"
#include "eina_xattr.h"
#ifndef O_BINARY
# define O_BINARY 0
#endif
#ifdef MAP_FAILED
# undef MAP_FAILED
#endif
#define MAP_FAILED ((void *)-1)
Eina_Hash *_eina_file_cache = NULL;
Eina_Lock _eina_file_lock_cache;
#if defined(EINA_SAFETY_CHECKS) && defined(EINA_MAGIC_DEBUG)
# define EINA_FILE_MAGIC_CHECK(f, ...) do { \
if (EINA_UNLIKELY((f) == NULL)) \
{ \
EINA_SAFETY_ERROR("safety check failed: " # f " == NULL"); \
return __VA_ARGS__; \
} \
if (EINA_UNLIKELY((f)->__magic != EINA_FILE_MAGIC)) \
{ \
EINA_MAGIC_FAIL(f, EINA_FILE_MAGIC); \
return __VA_ARGS__; \
} \
} while (0)
#else
# define EINA_FILE_MAGIC_CHECK(f, ...) do {} while(0)
#endif
static Eina_Spinlock _eina_statgen_lock;
static Eina_Statgen _eina_statgen = 0;
EAPI void
eina_file_statgen_next(void)
{
eina_spinlock_take(&_eina_statgen_lock);
if (_eina_statgen != 0)
{
_eina_statgen++;
if (_eina_statgen == 0) _eina_statgen = 1;
}
eina_spinlock_release(&_eina_statgen_lock);
}
EAPI Eina_Statgen
eina_file_statgen_get(void)
{
Eina_Statgen s;
eina_spinlock_take(&_eina_statgen_lock);
s = _eina_statgen;
eina_spinlock_release(&_eina_statgen_lock);
return s;
}
EAPI void
eina_file_statgen_enable(void)
{
eina_spinlock_take(&_eina_statgen_lock);
if (_eina_statgen != 0) _eina_statgen = 1;
eina_spinlock_release(&_eina_statgen_lock);
}
EAPI void
eina_file_statgen_disable(void)
{
eina_spinlock_take(&_eina_statgen_lock);
_eina_statgen = 0;
eina_spinlock_release(&_eina_statgen_lock);
}
static char *
_eina_file_escape(char *path, size_t len)
{
char *result;
char *p;
char *q;
result = path;
p = result;
q = result;
if (!result)
return NULL;
#ifdef _WIN32
EVIL_PATH_SEP_WIN32_TO_UNIX(path);
#endif
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';
break;
}
else
{
q = p;
++p;
}
}
else
if (p[1] == '.'
&& p[2] == '/')
{
// remove '/./'
len -= 2;
memmove(p, p + 2, len - (p - result));
result[len] = '\0';
q = p;
++p;
}
else
{
q = p;
++p;
}
}
return result;
}
unsigned int
eina_file_map_key_length(const void *key EINA_UNUSED)
{
return sizeof (unsigned long int) * 2;
}
int
eina_file_map_key_cmp(const unsigned long long int *key1, int key1_length EINA_UNUSED,
const unsigned long long int *key2, int key2_length EINA_UNUSED)
{
if (key1[0] == key2[0])
{
if (key1[1] == key2[1]) return 0;
if (key1[1] > key2[1]) return 1;
return -1;
}
if (key1[0] > key2[0]) return 1;
return -1;
}
int
eina_file_map_key_hash(const unsigned long long int *key, int key_length EINA_UNUSED)
{
return eina_hash_int64(&key[0], sizeof (unsigned long long int))
^ eina_hash_int64(&key[1], sizeof (unsigned long long int));
}
void *
eina_file_virtual_map_all(Eina_File *file)
{
eina_lock_take(&file->lock);
file->global_refcount++;
eina_lock_release(&file->lock);
return file->global_map;
}
void *
eina_file_virtual_map_new(Eina_File *file,
unsigned long int offset, unsigned long int length)
{
Eina_File_Map *map;
unsigned long int key[2];
// offset and length has already been checked by the caller function
key[0] = offset;
key[1] = length;
eina_lock_take(&file->lock);
map = eina_hash_find(file->map, &key);
if (!map)
{
map = malloc(sizeof (Eina_File_Map));
if (!map) goto on_error;
map->map = ((char*) file->global_map) + offset;
map->offset = offset;
map->length = length;
map->refcount = 0;
eina_hash_add(file->map, &key, map);
eina_hash_direct_add(file->rmap, &map->map, map);
}
map->refcount++;
on_error:
eina_lock_release(&file->lock);
return map ? map->map : NULL;
}
void
eina_file_virtual_map_free(Eina_File *file, void *map)
{
Eina_File_Map *em;
eina_lock_take(&file->lock);
// map could equal global_map even if length != file->length
em = eina_hash_find(file->rmap, &map);
if (em)
{
unsigned long int key[2];
em->refcount--;
if (em->refcount > 0) goto on_exit;
key[0] = em->offset;
key[1] = em->length;
eina_hash_del(file->rmap, &map, em);
eina_hash_del(file->map, &key, em);
}
else
{
if (file->global_map == map)
{
file->global_refcount--;
}
}
on_exit:
eina_lock_release(&file->lock);
}
void
eina_file_common_map_free(Eina_File *file, void *map,
void (*free_func)(Eina_File_Map *map))
{
Eina_File_Map *em;
unsigned long int key[2];
Eina_List *l = NULL;
Eina_Bool hashed = EINA_TRUE;
em = eina_hash_find(file->rmap, &map);
if (!em)
{
EINA_LIST_FOREACH(file->dead_map, l, em)
if (em->map == map)
{
hashed = EINA_FALSE;
break ;
}
if (hashed) return ;
}
em->refcount--;
if (em->refcount > 0) return ;
key[0] = em->offset;
key[1] = em->length;
if (hashed)
{
eina_hash_del(file->rmap, &map, em);
eina_hash_del(file->map, &key, em);
}
else
{
file->dead_map = eina_list_remove_list(file->dead_map, l);
free_func(em);
}
}
void
eina_file_flush(Eina_File *file, unsigned long int length)
{
Eina_File_Map *tmp;
Eina_Iterator *it;
Eina_List *dead_map = NULL;
Eina_List *l;
eina_lock_take(&file->lock);
// File size changed
if (file->global_map != MAP_FAILED)
{
// Forget global map
tmp = malloc(sizeof (Eina_File_Map));
if (tmp)
{
tmp->map = file->global_map;
tmp->offset = 0;
tmp->length = file->length;
tmp->refcount = file->refcount;
file->dead_map = eina_list_append(file->dead_map, tmp);
}
file->global_map = MAP_FAILED;
file->global_refcount = 0;
}
it = eina_hash_iterator_data_new(file->map);
EINA_ITERATOR_FOREACH(it, tmp)
{
// Add out of limit map to dead_map
if (tmp->offset + tmp->length > length)
dead_map = eina_list_append(dead_map, tmp);
}
eina_iterator_free(it);
EINA_LIST_FOREACH(dead_map, l, tmp)
{
unsigned long int key[2];
key[0] = tmp->offset;
key[1] = tmp->length;
eina_hash_del(file->rmap, &tmp->map, tmp);
eina_hash_del(file->map, &key, tmp);
}
file->dead_map = eina_list_merge(file->dead_map, dead_map);
eina_lock_release(&file->lock);
}
// Private to this file API
static void
_eina_file_map_close(Eina_File_Map *map)
{
free(map);
}
// Global API
EAPI char *
eina_file_path_sanitize(const char *path)
{
Eina_Tmpstr *result = NULL;
char *r;
size_t len;
if (!path) return NULL;
len = strlen(path);
if (eina_file_path_relative(path))
{
result = eina_file_current_directory_get(path, len);
len = eina_tmpstr_len(result);
}
else
result = path;
r = _eina_file_escape(strdup(result ? result : ""), len);
if (result != path) eina_tmpstr_del(result);
return r;
}
EAPI Eina_File *
eina_file_virtualize(const char *virtual_name, const void *data, unsigned long long length, Eina_Bool copy)
{
Eina_File *file;
Eina_Nano_Time tp;
long int ti;
const char *tmpname = "/dev/mem/virtual\\/%16x";
size_t slen, head_padded;
EINA_SAFETY_ON_NULL_RETURN_VAL(data, NULL);
// Generate an almost uniq filename based on current nsec time.
if (_eina_time_get(&tp)) return NULL;
ti = _eina_time_convert(&tp);
slen = virtual_name ? strlen(virtual_name) + 1 : strlen(tmpname) + 17;
// align copied data at end of file struct to 16 bytes...
head_padded = 16 * ((sizeof(Eina_File) + slen + 15) / 16);
file = malloc(head_padded + (copy ? length : 0));
if (!file) return NULL;
memset(file, 0, sizeof(Eina_File));
EINA_MAGIC_SET(file, EINA_FILE_MAGIC);
file->filename = (char *)(file + 1);
if (virtual_name)
file->filename = eina_stringshare_add(virtual_name);
else
file->filename = eina_stringshare_printf(tmpname, ti);
eina_lock_recursive_new(&file->lock);
file->mtime = ti / 1000;
file->length = length;
#ifdef _STAT_VER_LINUX
file->mtime_nsec = ti;
#endif
file->refcount = 1;
#ifndef _WIN32
file->fd = -1;
#else
file->handle = INVALID_HANDLE_VALUE;
#endif
file->virtual = EINA_TRUE;
file->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),
EINA_FREE_CB(_eina_file_map_close),
3);
file->rmap = eina_hash_pointer_new(NULL);
if (copy)
{
file->copied = EINA_TRUE;
file->global_map = ((char *)file) + head_padded;
memcpy((char *)file->global_map, data, length);
}
else
{
file->global_map = (void *)data;
}
return file;
}
EAPI Eina_Bool
eina_file_virtual(Eina_File *file)
{
if (!file) return EINA_FALSE;
EINA_FILE_MAGIC_CHECK(file, EINA_FALSE);
return file->virtual;
}
EAPI Eina_File *
eina_file_dup(const Eina_File *f)
{
Eina_File *file = (Eina_File*) f;
if (file)
{
EINA_FILE_MAGIC_CHECK(f, NULL);
eina_lock_take(&file->lock);
// For ease of use and safety of the API, if you dup a virtualized file, we prefer to make a copy
if (file->virtual && !file->copied)
{
Eina_File *r;
r = eina_file_virtualize(file->filename, file->global_map, file->length, EINA_TRUE);
eina_lock_release(&file->lock);
return r;
}
file->refcount++;
eina_lock_release(&file->lock);
}
return file;
}
void
eina_file_clean_close(Eina_File *file)
{
// Generic destruction of the file
eina_hash_free(file->rmap); file->rmap = NULL;
eina_hash_free(file->map); file->map = NULL;
eina_stringshare_del(file->filename);
// Backend specific file resource close
eina_file_real_close(file);
// Final death
EINA_MAGIC_SET(file, 0);
free(file);
}
EAPI void
eina_file_close(Eina_File *file)
{
Eina_Bool leave = EINA_TRUE;
unsigned int key;
if (!file) return ;
EINA_FILE_MAGIC_CHECK(file);
eina_lock_take(&_eina_file_lock_cache);
eina_lock_take(&file->lock);
file->refcount--;
if (file->refcount == 0) leave = EINA_FALSE;
eina_lock_release(&file->lock);
if (leave) goto end;
key = eina_hash_superfast((void*) &file->filename, sizeof (void*));
if (eina_hash_find_by_hash(_eina_file_cache,
file->filename, 0, key) == file)
{
eina_hash_del_by_key_hash(_eina_file_cache,
file->filename, 0, key);
}
eina_file_clean_close(file);
end:
eina_lock_release(&_eina_file_lock_cache);
}
EAPI size_t
eina_file_size_get(const Eina_File *file)
{
EINA_FILE_MAGIC_CHECK(file, 0);
return file->length;
}
EAPI time_t
eina_file_mtime_get(const Eina_File *file)
{
EINA_FILE_MAGIC_CHECK(file, 0);
return file->mtime;
}
EAPI const char *
eina_file_filename_get(const Eina_File *file)
{
EINA_FILE_MAGIC_CHECK(file, NULL);
return file->filename;
}
Eina_Stringshare *
eina_file_sanitize(const char *path)
{
char *filename;
Eina_Stringshare *ss;
filename = eina_file_path_sanitize(path);
if (!filename) return NULL;
ss = eina_stringshare_add(filename);
free(filename);
return ss;
}
/* search '\r' and '\n' by preserving cache locality and page locality
in doing a search inside 4K boundary.
*/
static inline const char *
_eina_find_eol(const char *start, int boundary, const char *end)
{
const char *cr;
const char *lf;
unsigned long long chunk;
while (start < end)
{
chunk = start + boundary < end ? boundary : end - start;
cr = memchr(start, '\r', chunk);
lf = memchr(start, '\n', chunk);
if (cr)
{
if (lf && lf < cr)
return lf;
return cr;
}
else if (lf)
return lf;
start += chunk;
boundary = 4096;
}
return end;
}
static Eina_Bool
_eina_file_map_lines_iterator_next(Eina_Lines_Iterator *it, void **data)
{
const char *eol;
unsigned char match;
if (it->current.end >= it->end)
return EINA_FALSE;
match = *it->current.end;
if (it->current.index > 0)
it->current.end++;
while (it->current.end < it->end &&
(*it->current.end == '\n' || *it->current.end == '\r'))
{
if (match == *it->current.end)
break;
it->current.end++;
}
it->current.index++;
if (it->current.end == it->end)
return EINA_FALSE;
eol = _eina_find_eol(it->current.end,
it->boundary,
it->end);
it->boundary = (uintptr_t) eol & 0x3FF;
if (it->boundary == 0) it->boundary = 4096;
it->current.start = it->current.end;
it->current.end = eol;
it->current.length = eol - it->current.start;
*data = &it->current;
return EINA_TRUE;
}
static Eina_File *
_eina_file_map_lines_iterator_container(Eina_Lines_Iterator *it)
{
return it->fp;
}
static void
_eina_file_map_lines_iterator_free(Eina_Lines_Iterator *it)
{
eina_file_map_free(it->fp, (void*) it->map);
eina_file_close(it->fp);
EINA_MAGIC_SET(&it->iterator, 0);
free(it);
}
EAPI Eina_Iterator *
eina_file_map_lines(Eina_File *file)
{
Eina_Lines_Iterator *it;
EINA_FILE_MAGIC_CHECK(file, NULL);
if (file->length == 0) return NULL;
it = calloc(1, sizeof (Eina_Lines_Iterator));
if (!it) return NULL;
EINA_MAGIC_SET(&it->iterator, EINA_MAGIC_ITERATOR);
it->map = eina_file_map_all(file, EINA_FILE_SEQUENTIAL);
if (!it->map)
{
free(it);
return NULL;
}
eina_lock_take(&file->lock);
file->refcount++;
eina_lock_release(&file->lock);
it->fp = file;
it->boundary = 4096;
it->current.start = it->map;
it->current.end = it->current.start;
it->current.index = 0;
it->current.length = 0;
it->end = it->map + it->fp->length;
it->iterator.version = EINA_ITERATOR_VERSION;
it->iterator.next = FUNC_ITERATOR_NEXT(_eina_file_map_lines_iterator_next);
it->iterator.get_container = FUNC_ITERATOR_GET_CONTAINER(_eina_file_map_lines_iterator_container);
it->iterator.free = FUNC_ITERATOR_FREE(_eina_file_map_lines_iterator_free);
return &it->iterator;
}
static Eina_Bool
_eina_file_copy_write_internal(int fd, char *buf, size_t size)
{
size_t done = 0;
while (done < size)
{
ssize_t w = write(fd, buf + done, size - done);
if (w >= 0)
done += w;
else if ((errno != EAGAIN) && (errno != EINTR))
{
ERR("Error writing destination file during copy: %s",
strerror(errno));
return EINA_FALSE;
}
}
return EINA_TRUE;
}
static Eina_Bool
_eina_file_copy_read_internal(int fd, char *buf, off_t bufsize, ssize_t *readsize)
{
while (1)
{
ssize_t r = read(fd, buf, bufsize);
if (r == 0)
{
ERR("Premature end of source file during copy.");
return EINA_FALSE;
}
else if (r < 0)
{
if ((errno != EAGAIN) && (errno != EINTR))
{
ERR("Error reading source file during copy: %s",
strerror(errno));
return EINA_FALSE;
}
}
else
{
*readsize = r;
return EINA_TRUE;
}
}
}
#ifdef HAVE_SPLICE
static Eina_Bool
_eina_file_copy_write_splice_internal(int fd, int pipefd, size_t size)
{
size_t done = 0;
while (done < size)
{
ssize_t w = splice(pipefd, NULL, fd, NULL, size - done, SPLICE_F_MORE);
if (w >= 0)
done += w;
else if (errno == EINVAL)
{
INF("Splicing is not supported for destination file");
return EINA_FALSE;
}
else if ((errno != EAGAIN) && (errno != EINTR))
{
ERR("Error splicing to destination file during copy: %s",
strerror(errno));
return EINA_FALSE;
}
}
return EINA_TRUE;
}
static Eina_Bool
_eina_file_copy_read_splice_internal(int fd, int pipefd, off_t bufsize, ssize_t *readsize)
{
while (1)
{
ssize_t r = splice(fd, NULL, pipefd, NULL, bufsize, SPLICE_F_MORE);
if (r == 0)
{
ERR("Premature end of source file during splice.");
return EINA_FALSE;
}
else if (r < 0)
{
if (errno == EINVAL)
{
INF("Splicing is not supported for source file");
return EINA_FALSE;
}
else if ((errno != EAGAIN) && (errno != EINTR))
{
ERR("Error splicing from source file during copy: %s",
strerror(errno));
return EINA_FALSE;
}
}
else
{
*readsize = r;
return EINA_TRUE;
}
}
}
#endif
static Eina_Bool
_eina_file_copy_splice_internal(int s, int d, off_t total, Eina_File_Copy_Progress cb, const void *cb_data, Eina_Bool *splice_unsupported)
{
#ifdef HAVE_SPLICE
off_t bufsize = COPY_BLOCKSIZE;
off_t done;
Eina_Bool ret;
int pipefd[2];
*splice_unsupported = EINA_TRUE;
if (pipe(pipefd) < 0) return EINA_FALSE;
done = 0;
ret = EINA_TRUE;
while (done < total)
{
size_t todo;
ssize_t r;
if (done + bufsize < total)
todo = bufsize;
else
todo = total - done;
ret = _eina_file_copy_read_splice_internal(s, pipefd[1], todo, &r);
if (!ret) break;
ret = _eina_file_copy_write_splice_internal(d, pipefd[0], r);
if (!ret) break;
*splice_unsupported = EINA_FALSE;
done += r;
if (cb)
{
ret = cb((void *)cb_data, done, total);
if (!ret) break;
}
}
close(pipefd[0]);
close(pipefd[1]);
return ret;
#endif
*splice_unsupported = EINA_TRUE;
return EINA_FALSE;
(void)s;
(void)d;
(void)total;
(void)cb;
(void)cb_data;
}
static Eina_Bool
_eina_file_copy_internal(int s, int d, off_t total, Eina_File_Copy_Progress cb, const void *cb_data)
{
void *buf = NULL;
off_t bufsize = COPY_BLOCKSIZE;
off_t done;
Eina_Bool ret, splice_unsupported;
ret = _eina_file_copy_splice_internal(s, d, total, cb, cb_data,
&splice_unsupported);
if (ret)
return EINA_TRUE;
else if (!splice_unsupported) /* splice works, but copy failed anyway */
return EINA_FALSE;
/* make sure splice didn't change the position */
lseek(s, 0, SEEK_SET);
lseek(d, 0, SEEK_SET);
while ((bufsize > 0) && ((buf = malloc(bufsize)) == NULL))
bufsize /= 128;
EINA_SAFETY_ON_NULL_RETURN_VAL(buf, EINA_FALSE);
done = 0;
ret = EINA_TRUE;
while (done < total)
{
size_t todo;
ssize_t r;
if (done + bufsize < total)
todo = bufsize;
else
todo = total - done;
ret = _eina_file_copy_read_internal(s, buf, todo, &r);
if (!ret) break;
ret = _eina_file_copy_write_internal(d, buf, r);
if (!ret) break;
done += r;
if (cb)
{
ret = cb((void *)cb_data, done, total);
if (!ret) break;
}
}
free(buf);
return ret;
}
EAPI Eina_Bool
eina_file_copy(const char *src, const char *dst, Eina_File_Copy_Flags flags, Eina_File_Copy_Progress cb, const void *cb_data)
{
struct stat st;
int s, d = -1;
Eina_Bool success;
EINA_SAFETY_ON_NULL_RETURN_VAL(src, EINA_FALSE);
EINA_SAFETY_ON_NULL_RETURN_VAL(dst, EINA_FALSE);
s = open(src, O_RDONLY | O_BINARY);
EINA_SAFETY_ON_TRUE_RETURN_VAL (s < 0, EINA_FALSE);
success = (fstat(s, &st) == 0);
EINA_SAFETY_ON_FALSE_GOTO(success, end);
d = open(dst, O_WRONLY | O_BINARY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
EINA_SAFETY_ON_TRUE_GOTO(d < 0, end);
success = _eina_file_copy_internal(s, d, st.st_size, cb, cb_data);
if (success)
{
#ifdef HAVE_FCHMOD
if (flags & EINA_FILE_COPY_PERMISSION)
fchmod(d, st.st_mode);
#endif
if (flags & EINA_FILE_COPY_XATTR)
eina_xattr_fd_copy(s, d);
}
end:
if (d >= 0) close(d);
else success = EINA_FALSE;
close(s);
if (!success)
unlink(dst);
return success;
}
EAPI int
eina_file_mkstemp(const char *templatename, Eina_Tmpstr **path)
{
char buffer[PATH_MAX];
const char *XXXXXX = NULL, *sep;
int fd, len;
#ifndef _WIN32
mode_t old_umask;
#endif
EINA_SAFETY_ON_NULL_RETURN_VAL(templatename, -1);
sep = strchr(templatename, '/');
#ifdef _WIN32
if (!sep) sep = strchr(templatename, '\\');
#endif
if (sep)
{
len = eina_strlcpy(buffer, templatename, sizeof(buffer));
}
else
{
len = eina_file_path_join(buffer, sizeof(buffer),
eina_environment_tmp_get(), templatename);
}
/*
* Unix:
* Make sure temp file is created with secure permissions,
* http://man7.org/linux/man-pages/man3/mkstemp.3.html#NOTES
*
* Windows:
* no secure permissions anyway and the umask use below makes
* the file read-only.
*/
#ifndef _WIN32
old_umask = umask(S_IRWXG|S_IRWXO);
#endif
if ((XXXXXX = strstr(buffer, "XXXXXX.")) != NULL)
{
int suffixlen = buffer + len - XXXXXX - 6;
fd = mkstemps(buffer, suffixlen);
}
else
fd = mkstemp(buffer);
#ifndef _WIN32
umask(old_umask);
#endif
if (fd < 0)
{
if (path) *path = NULL;
return -1;
}
if (path) *path = eina_tmpstr_add(buffer);
return fd;
}
EAPI Eina_Bool
eina_file_mkdtemp(const char *templatename, Eina_Tmpstr **path)
{
char buffer[PATH_MAX];
char *tmpdirname, *sep;
EINA_SAFETY_ON_NULL_RETURN_VAL(templatename, EINA_FALSE);
sep = strchr(templatename, '/');
#ifdef _WIN32
if (!sep) sep = strchr(templatename, '\\');
#endif
if (sep)
{
eina_strlcpy(buffer, templatename, sizeof(buffer));
}
else
{
eina_file_path_join(buffer, sizeof(buffer),
eina_environment_tmp_get(), templatename);
}
tmpdirname = mkdtemp(buffer);
if (tmpdirname == NULL)
{
if (path) *path = NULL;
return EINA_FALSE;
}
if (path) *path = eina_tmpstr_add(tmpdirname);
return EINA_TRUE;
}
/*============================================================================*
* Global *
*============================================================================*/
Eina_Bool
eina_file_init(void)
{
_eina_file_log_dom = eina_log_domain_register("eina_file",
EINA_LOG_COLOR_DEFAULT);
if (_eina_file_log_dom < 0)
{
EINA_LOG_ERR("Could not register log domain: eina_file");
return EINA_FALSE;
}
_eina_file_cache = eina_hash_stringshared_new(NULL);
if (!_eina_file_cache)
{
ERR("Could not create cache.");
eina_log_domain_unregister(_eina_file_log_dom);
_eina_file_log_dom = -1;
return EINA_FALSE;
}
eina_spinlock_new(&_eina_statgen_lock);
eina_spinlock_take(&_eina_statgen_lock);
if (getenv("EINA_STATGEN")) _eina_statgen = 1;
eina_spinlock_release(&_eina_statgen_lock);
eina_lock_recursive_new(&_eina_file_lock_cache);
eina_magic_string_set(EINA_FILE_MAGIC, "Eina_File");
return EINA_TRUE;
}
Eina_Bool
eina_file_shutdown(void)
{
if (eina_hash_population(_eina_file_cache) > 0)
{
Eina_Iterator *it;
const char *key;
it = eina_hash_iterator_key_new(_eina_file_cache);
EINA_ITERATOR_FOREACH(it, key)
{
Eina_File *f = eina_hash_find(_eina_file_cache, key);
ERR("File [%s] still open %i times !", key, f->refcount);
}
eina_iterator_free(it);
}
eina_hash_free(_eina_file_cache);
_eina_file_cache = NULL;
eina_lock_free(&_eina_file_lock_cache);
eina_log_domain_unregister(_eina_file_log_dom);
_eina_file_log_dom = -1;
eina_spinlock_free(&_eina_statgen_lock);
return EINA_TRUE;
}
EAPI Eina_Bool
eina_file_close_on_exec(int fd, Eina_Bool on)
{
#ifdef _WIN32
return EINA_TRUE;
(void)fd;
(void)on;
#elif HAVE_FCNTL
int flags;
flags = fcntl(fd, F_GETFD);
if (flags < 0)
{
int errno_backup = errno;
ERR("%#x = fcntl(%d, F_GETFD): %s", flags, fd, strerror(errno));
errno = errno_backup;
return EINA_FALSE;
}
if (on)
flags |= FD_CLOEXEC;
else
flags &= (~flags);
if (fcntl(fd, F_SETFD, flags) == -1)
{
int errno_backup = errno;
ERR("fcntl(%d, F_SETFD, %#x): %s", fd, flags, strerror(errno));
errno = errno_backup;
return EINA_FALSE;
}
return EINA_TRUE;
#else
static Eina_Bool statement = EINA_FALSE;
if (!statement)
ERR("fcntl is not available on your platform. fd may leak when using exec.");
statement = EINA_TRUE;
return EINA_TRUE;
#endif
}