efl/legacy/eet/src/lib/eet_lib.c

2138 lines
54 KiB
C
Raw Normal View History

/*
* vim:ts=8:sw=3:sts=8:noexpandtab:cino=>5n-3f0^-2{2
*/
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#if defined(_WIN32) && ! defined(__CEGCC__)
# include <winsock2.h>
#endif
#ifdef HAVE_ALLOCA_H
# include <alloca.h>
#elif defined __GNUC__
# define alloca __builtin_alloca
#elif defined _AIX
# define alloca __alloca
#elif defined _MSC_VER
# include <malloc.h>
# define alloca _alloca
#else
# include <stddef.h>
# ifdef __cplusplus
extern "C"
# endif
void *alloca (size_t);
#endif
#include <stdio.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <time.h>
2008-03-28 10:29:22 -07:00
#include <string.h>
#include <fnmatch.h>
#include <fcntl.h>
#include <zlib.h>
#ifndef _MSC_VER
# include <unistd.h>
#endif
#ifdef HAVE_OPENSSL
#include <openssl/err.h>
#endif
#ifdef HAVE_NETINET_IN_H
# include <netinet/in.h>
#endif
#ifdef HAVE_EVIL
# include <Evil.h>
#endif
#ifdef HAVE_GNUTLS
# include <gnutls/gnutls.h>
# include <gcrypt.h>
#endif
#ifdef HAVE_OPENSSL
# include <openssl/err.h>
# include <openssl/evp.h>
#endif
#include <Eina.h>
#include "Eet.h"
#include "Eet_private.h"
#ifdef HAVE_REALPATH
# undef HAVE_REALPATH
#endif
2002-12-02 15:39:26 -08:00
#define EET_MAGIC_FILE 0x1ee7ff00
#define EET_MAGIC_FILE_HEADER 0x1ee7ff01
#define EET_MAGIC_FILE2 0x1ee70f42
2002-12-02 15:39:26 -08:00
typedef struct _Eet_File_Header Eet_File_Header;
typedef struct _Eet_File_Node Eet_File_Node;
typedef struct _Eet_File_Directory Eet_File_Directory;
struct _Eet_File
{
char *path;
FILE *fp;
FILE *readfp;
Eet_File_Header *header;
Eet_Dictionary *ed;
Eet_Key *key;
const unsigned char *data;
const void *x509_der;
const void *signature;
void *sha1;
Eet_File_Mode mode;
int magic;
int references;
int data_size;
int x509_length;
unsigned int signature_length;
int sha1_length;
time_t mtime;
unsigned char writes_pending : 1;
unsigned char delete_me_now : 1;
2002-12-02 15:39:26 -08:00
};
2002-12-02 15:39:26 -08:00
struct _Eet_File_Header
{
int magic;
Eet_File_Directory *directory;
};
2002-12-02 15:39:26 -08:00
struct _Eet_File_Directory
{
int size;
Eet_File_Node **nodes;
2002-12-02 15:39:26 -08:00
};
struct _Eet_File_Node
2002-12-02 15:39:26 -08:00
{
char *name;
void *data;
Eet_File_Node *next; /* FIXME: make buckets linked lists */
int offset;
int dictionary_offset;
int name_offset;
int name_size;
int size;
int data_size;
unsigned char free_name : 1;
unsigned char compression : 1;
unsigned char ciphered : 1;
2002-12-02 15:39:26 -08:00
};
#if 0
/* Version 2 */
2002-12-02 15:39:26 -08:00
/* NB: all int's are stored in network byte order on disk */
/* file format: */
int magic; /* magic number ie 0x1ee7ff00 */
int num_directory_entries; /* number of directory entries to follow */
int bytes_directory_entries; /* bytes of directory entries to follow */
struct
2002-12-02 15:39:26 -08:00
{
int offset; /* bytes offset into file for data chunk */
int flags; /* flags - for now 0 = uncompressed and clear, 1 = compressed and clear, 2 = uncompressed and ciphered, 3 = compressed and ciphered */
2002-12-02 15:39:26 -08:00
int size; /* size of the data chunk */
int data_size; /* size of the (uncompressed) data chunk */
int name_size; /* length in bytes of the name field */
char name[name_size]; /* name string (variable length) and \0 terminated */
2002-12-02 15:39:26 -08:00
} directory[num_directory_entries];
/* and now startes the data stream... */
#endif
2002-12-02 15:39:26 -08:00
#if 0
/* Version 3 */
/* NB: all int's are stored in network byte order on disk */
/* file format: */
int magic; /* magic number ie 0x1ee70f42 */
int num_directory_entries; /* number of directory entries to follow */
int num_dictionary_entries; /* number of dictionary entries to follow */
struct
{
int data_offset; /* bytes offset into file for data chunk */
int size; /* size of the data chunk */
int data_size; /* size of the (uncompressed) data chunk */
int name_offset; /* bytes offset into file for name string */
int name_size; /* length in bytes of the name field */
int flags; /* flags - for now 0 = uncompressed, 1 = compressed */
} directory[num_directory_entries];
struct
{
int hash;
int offset;
int size;
int prev;
int next;
} dictionary[num_dictionary_entries];
/* now start the string stream. */
/* and right after them the data stream. */
int magic_sign; /* Optional, only if the eet file is signed. */
int signature_length; /* Signature length. */
int x509_length; /* Public certificate that signed the file. */
char signature[signature_length]; /* The signature. */
char x509[x509_length]; /* The public certificate. */
#endif
#define EET_FILE2_HEADER_COUNT 3
#define EET_FILE2_DIRECTORY_ENTRY_COUNT 6
#define EET_FILE2_DICTIONARY_ENTRY_COUNT 5
#define EET_FILE2_HEADER_SIZE (sizeof(int) * EET_FILE2_HEADER_COUNT)
#define EET_FILE2_DIRECTORY_ENTRY_SIZE (sizeof(int) * EET_FILE2_DIRECTORY_ENTRY_COUNT)
#define EET_FILE2_DICTIONARY_ENTRY_SIZE (sizeof(int) * EET_FILE2_DICTIONARY_ENTRY_COUNT)
2002-12-02 15:39:26 -08:00
/* prototypes of internal calls */
static Eet_File *eet_cache_find(const char *path, Eet_File **cache, int cache_num);
static void eet_cache_add(Eet_File *ef, Eet_File ***cache, int *cache_num, int *cache_alloc);
static void eet_cache_del(Eet_File *ef, Eet_File ***cache, int *cache_num, int *cache_alloc);
static int eet_string_match(const char *s1, const char *s2);
#if 0 /* Unused */
static Eet_Error eet_flush(Eet_File *ef);
#endif
static Eet_Error eet_flush2(Eet_File *ef);
static Eet_File_Node *find_node_by_name(Eet_File *ef, const char *name);
static int read_data_from_disk(Eet_File *ef, Eet_File_Node *efn, void *buf, int len);
2002-12-02 15:39:26 -08:00
/* cache. i don't expect this to ever be large, so arrays will do */
static int eet_writers_num = 0;
static int eet_writers_alloc = 0;
static Eet_File **eet_writers = NULL;
static int eet_readers_num = 0;
static int eet_readers_alloc = 0;
static Eet_File **eet_readers = NULL;
static int eet_initcount = 0;
/* log domain variable */
int _eet_log_dom_global = -1;
/* Check to see its' an eet file pointer */
static inline int
eet_check_pointer(const Eet_File *ef)
{
if ((!ef) || (ef->magic != EET_MAGIC_FILE))
return 1;
return 0;
}
static inline int
eet_check_header(const Eet_File *ef)
{
if (!ef->header)
return 1;
if (!ef->header->directory)
return 1;
return 0;
}
static inline int
eet_test_close(int test, Eet_File *ef)
{
if (test)
{
ef->delete_me_now = 1;
eet_close(ef);
}
return test;
}
2002-12-02 15:39:26 -08:00
/* find an eet file in the currently in use cache */
static Eet_File *
eet_cache_find(const char *path, Eet_File **cache, int cache_num)
2002-12-02 15:39:26 -08:00
{
int i;
2002-12-02 15:39:26 -08:00
/* walk list */
for (i = 0; i < cache_num; i++)
{
/* if matches real path - return it */
2005-11-03 19:22:22 -08:00
if (eet_string_match(cache[i]->path, path))
{
if (!cache[i]->delete_me_now)
return cache[i];
}
2002-12-02 15:39:26 -08:00
}
2002-12-02 15:39:26 -08:00
/* not found */
return NULL;
}
/* add to end of cache */
static void
eet_cache_add(Eet_File *ef, Eet_File ***cache, int *cache_num, int *cache_alloc)
2002-12-02 15:39:26 -08:00
{
Eet_File **new_cache;
int new_cache_num;
int new_cache_alloc;
2005-11-06 19:15:22 -08:00
new_cache_num = *cache_num;
if (new_cache_num >= 64) /* avoid fd overruns - limit to 128 (most recent) in the cache */
2005-11-06 19:15:22 -08:00
{
Eet_File *del_ef = NULL;
int i;
2005-11-06 19:15:22 -08:00
new_cache = *cache;
for (i = 0; i < new_cache_num; i++)
{
if (new_cache[i]->references == 0)
{
del_ef = new_cache[i];
break;
}
}
2005-11-06 19:15:22 -08:00
if (del_ef)
{
del_ef->delete_me_now = 1;
2005-11-06 22:16:26 -08:00
eet_close(del_ef);
2005-11-06 19:15:22 -08:00
}
}
2002-12-02 15:39:26 -08:00
new_cache = *cache;
new_cache_num = *cache_num;
new_cache_alloc = *cache_alloc;
2002-12-02 15:39:26 -08:00
new_cache_num++;
if (new_cache_num > new_cache_alloc)
{
new_cache_alloc += 16;
new_cache = realloc(new_cache, new_cache_alloc * sizeof(Eet_File *));
if (!new_cache)
{
CRIT("BAD ERROR! Eet realloc of cache list failed. Abort");
abort();
}
}
2002-12-02 15:39:26 -08:00
new_cache[new_cache_num - 1] = ef;
*cache = new_cache;
*cache_num = new_cache_num;
*cache_alloc = new_cache_alloc;
2002-12-02 15:39:26 -08:00
}
/* delete from cache */
static void
eet_cache_del(Eet_File *ef, Eet_File ***cache, int *cache_num, int *cache_alloc)
2002-12-02 15:39:26 -08:00
{
Eet_File **new_cache;
int new_cache_num, new_cache_alloc;
2002-12-02 15:39:26 -08:00
int i, j;
new_cache = *cache;
new_cache_num = *cache_num;
new_cache_alloc = *cache_alloc;
if (new_cache_num <= 0)
return;
2002-12-02 15:39:26 -08:00
for (i = 0; i < new_cache_num; i++)
{
if (new_cache[i] == ef)
break;
2002-12-02 15:39:26 -08:00
}
if (i >= new_cache_num)
return;
2002-12-02 15:39:26 -08:00
new_cache_num--;
for (j = i; j < new_cache_num; j++)
new_cache[j] = new_cache[j + 1];
if (new_cache_num <= (new_cache_alloc - 16))
{
new_cache_alloc -= 16;
if (new_cache_num > 0)
{
new_cache = realloc(new_cache, new_cache_alloc * sizeof(Eet_File *));
if (!new_cache)
{
CRIT("BAD ERROR! Eet realloc of cache list failed. Abort");
abort();
}
}
else
{
free(new_cache);
new_cache = NULL;
}
}
2002-12-02 15:39:26 -08:00
*cache = new_cache;
*cache_num = new_cache_num;
*cache_alloc = new_cache_alloc;
2002-12-02 15:39:26 -08:00
}
/* internal string match. null friendly, catches same ptr */
2002-12-02 15:39:26 -08:00
static int
eet_string_match(const char *s1, const char *s2)
2002-12-02 15:39:26 -08:00
{
/* both null- no match */
if ((!s1) || (!s2)) return 0;
if (s1 == s2) return 1;
return (!strcmp(s1, s2));
2002-12-02 15:39:26 -08:00
}
/* flush out writes to a v2 eet file */
static Eet_Error
eet_flush2(Eet_File *ef)
{
Eet_File_Node *efn;
Eet_Error error = EET_ERROR_NONE;
int head[EET_FILE2_HEADER_COUNT];
int num_directory_entries = 0;
int num_dictionary_entries = 0;
int bytes_directory_entries = 0;
int bytes_dictionary_entries = 0;
int bytes_strings = 0;
int data_offset = 0;
int strings_offset = 0;
int num;
int i;
int j;
if (eet_check_pointer(ef))
return EET_ERROR_BAD_OBJECT;
if (eet_check_header(ef))
return EET_ERROR_EMPTY;
if ((ef->mode != EET_FILE_MODE_WRITE) && (ef->mode != EET_FILE_MODE_READ_WRITE))
return EET_ERROR_NOT_WRITABLE;
if (!ef->writes_pending)
return EET_ERROR_NONE;
if (ef->mode == EET_FILE_MODE_READ_WRITE && ef->fp == NULL)
{
int fd;
unlink(ef->path);
2008-09-08 04:11:51 -07:00
fd = open(ef->path, O_CREAT | O_TRUNC | O_RDWR, S_IRUSR | S_IWUSR);
ef->fp = fdopen(fd, "wb");
if (!ef->fp) return EET_ERROR_NOT_WRITABLE;
fcntl(fileno(ef->fp), F_SETFD, FD_CLOEXEC);
}
/* calculate string base offset and data base offset */
num = (1 << ef->header->directory->size);
for (i = 0; i < num; ++i)
{
for (efn = ef->header->directory->nodes[i]; efn; efn = efn->next)
{
num_directory_entries++;
bytes_strings += strlen(efn->name) + 1;
}
}
if (ef->ed)
{
num_dictionary_entries = ef->ed->count;
for (i = 0; i < num_dictionary_entries; ++i)
bytes_strings += ef->ed->all[i].len;
}
/* calculate section bytes size */
bytes_directory_entries = EET_FILE2_DIRECTORY_ENTRY_SIZE * num_directory_entries + EET_FILE2_HEADER_SIZE;
bytes_dictionary_entries = EET_FILE2_DICTIONARY_ENTRY_SIZE * num_dictionary_entries;
/* calculate per entry offset */
strings_offset = bytes_directory_entries + bytes_dictionary_entries;
data_offset = bytes_directory_entries + bytes_dictionary_entries + bytes_strings;
for (i = 0; i < num; ++i)
{
for (efn = ef->header->directory->nodes[i]; efn; efn = efn->next)
{
efn->offset = data_offset;
data_offset += efn->size;
efn->name_offset = strings_offset;
strings_offset += efn->name_size;
}
}
/* calculate dictionary strings offset */
if (ef->ed)
ef->ed->offset = strings_offset;
/* go thru and write the header */
head[0] = (int) htonl ((unsigned int) EET_MAGIC_FILE2);
head[1] = (int) htonl ((unsigned int) num_directory_entries);
head[2] = (int) htonl ((unsigned int) num_dictionary_entries);
fseek(ef->fp, 0, SEEK_SET);
if (fwrite(head, sizeof (head), 1, ef->fp) != 1)
goto write_error;
/* write directories entry */
for (i = 0; i < num; i++)
{
for (efn = ef->header->directory->nodes[i]; efn; efn = efn->next)
{
unsigned int flag;
int ibuf[EET_FILE2_DIRECTORY_ENTRY_COUNT];
flag = (efn->ciphered << 1) | efn->compression;
ibuf[0] = (int) htonl ((unsigned int) efn->offset);
ibuf[1] = (int) htonl ((unsigned int) efn->size);
ibuf[2] = (int) htonl ((unsigned int) efn->data_size);
ibuf[3] = (int) htonl ((unsigned int) efn->name_offset);
ibuf[4] = (int) htonl ((unsigned int) efn->name_size);
ibuf[5] = (int) htonl ((unsigned int) flag);
if (fwrite(ibuf, sizeof(ibuf), 1, ef->fp) != 1)
goto write_error;
}
}
/* write dictionnary */
if (ef->ed)
{
int offset = strings_offset;
for (j = 0; j < ef->ed->count; ++j)
{
int sbuf[EET_FILE2_DICTIONARY_ENTRY_COUNT];
sbuf[0] = (int) htonl ((unsigned int) ef->ed->all[j].hash);
sbuf[1] = (int) htonl ((unsigned int) offset);
sbuf[2] = (int) htonl ((unsigned int) ef->ed->all[j].len);
sbuf[3] = (int) htonl ((unsigned int) ef->ed->all[j].prev);
sbuf[4] = (int) htonl ((unsigned int) ef->ed->all[j].next);
offset += ef->ed->all[j].len;
if (fwrite(sbuf, sizeof (sbuf), 1, ef->fp) != 1)
goto write_error;
}
}
/* write directories name */
for (i = 0; i < num; i++)
{
for (efn = ef->header->directory->nodes[i]; efn; efn = efn->next)
{
if (fwrite(efn->name, efn->name_size, 1, ef->fp) != 1)
goto write_error;
}
}
/* write strings */
if (ef->ed)
2008-04-10 02:12:43 -07:00
{
for (j = 0; j < ef->ed->count; ++j)
{
if (ef->ed->all[j].str)
{
if (fwrite(ef->ed->all[j].str, ef->ed->all[j].len, 1, ef->fp) != 1)
goto write_error;
}
else
{
if (fwrite(ef->ed->all[j].mmap, ef->ed->all[j].len, 1, ef->fp) != 1)
goto write_error;
}
}
}
/* write data */
for (i = 0; i < num; i++)
{
for (efn = ef->header->directory->nodes[i]; efn; efn = efn->next)
{
if (fwrite(efn->data, efn->size, 1, ef->fp) != 1)
goto write_error;
}
}
/* flush all write to the file. */
fflush(ef->fp);
2009-03-17 18:47:33 -07:00
// this is going to really cause trouble. if ANYTHING this needs to go into a
// thread spawned off - but even then...
// in this case... ext4 is "wrong". (yes we can jump up and down and point posix
// manual pages at eachother, but ext4 broke behavior that has been in place
// for decades and that 1000's of apps rely on daily - that is that one operation
// to disk is committed to disk BEFORE following operations, so the fs retains
// a consistent state
// fsync(fileno(ef->fp));
/* append signature if required */
if (ef->key)
{
error = eet_identity_sign(ef->fp, ef->key);
if (error != EET_ERROR_NONE)
goto sign_error;
}
/* no more writes pending */
ef->writes_pending = 0;
return EET_ERROR_NONE;
write_error:
if (ferror(ef->fp))
{
switch (errno)
{
case EFBIG: error = EET_ERROR_WRITE_ERROR_FILE_TOO_BIG; break;
case EIO: error = EET_ERROR_WRITE_ERROR_IO_ERROR; break;
case ENOSPC: error = EET_ERROR_WRITE_ERROR_OUT_OF_SPACE; break;
case EPIPE: error = EET_ERROR_WRITE_ERROR_FILE_CLOSED; break;
default: error = EET_ERROR_WRITE_ERROR; break;
}
}
2008-09-08 04:17:04 -07:00
sign_error:
if (ef->fp) fclose(ef->fp);
ef->fp = NULL;
return error;
}
#if 0 /* Unused */
2002-12-02 15:39:26 -08:00
/* flush out writes to an eet file */
static Eet_Error
2002-12-02 15:39:26 -08:00
eet_flush(Eet_File *ef)
{
Eet_File_Node *efn;
int head[3];
int count = 0;
int size = 0;
int offset = 0;
int i;
int num;
/* check to see its' an eet file pointer */
if (eet_check_pointer(ef))
return EET_ERROR_BAD_OBJECT;
if (eet_check_header(ef))
return EET_ERROR_EMPTY;
if ((ef->mode != EET_FILE_MODE_WRITE) && (ef->mode != EET_FILE_MODE_READ_WRITE))
return EET_ERROR_NOT_WRITABLE;
if (!ef->writes_pending)
return EET_ERROR_NONE;
2002-12-02 15:39:26 -08:00
/* calculate total size in bytes of directory block */
num = (1 << ef->header->directory->size);
2002-12-02 15:39:26 -08:00
for (i = 0; i < num; i++)
{
for (efn = ef->header->directory->nodes[i]; efn; efn = efn->next)
2002-12-02 15:39:26 -08:00
{
2007-05-02 04:08:41 -07:00
size += 20 + strlen(efn->name) + 1;
count++;
2002-12-02 15:39:26 -08:00
}
}
/* calculate offsets per entry */
2002-12-02 15:39:26 -08:00
offset = 0;
for (i = 0; i < num; i++)
{
for (efn = ef->header->directory->nodes[i]; efn; efn = efn->next)
2002-12-02 15:39:26 -08:00
{
2007-05-02 04:08:41 -07:00
efn->offset = 12 + size + offset;
offset += efn->size;
2002-12-02 15:39:26 -08:00
}
}
2002-12-02 15:39:26 -08:00
/* go thru and write the header */
head[0] = (int) htonl ((unsigned int) EET_MAGIC_FILE);
head[1] = (int) htonl ((unsigned int) count);
head[2] = (int) htonl ((unsigned int) size);
2002-12-02 15:39:26 -08:00
fseek(ef->fp, 0, SEEK_SET);
if (fwrite(head, 12, 1, ef->fp) != 1)
goto write_error;
2002-12-02 15:39:26 -08:00
for (i = 0; i < num; i++)
{
for (efn = ef->header->directory->nodes[i]; efn; efn = efn->next)
2002-12-02 15:39:26 -08:00
{
unsigned int ibuf[5];
2007-05-02 04:08:41 -07:00
int name_size;
2007-05-02 04:08:41 -07:00
name_size = strlen(efn->name) + 1;
ibuf[0] = (int) htonl ((unsigned int) efn->offset);
ibuf[1] = (int) htonl ((unsigned int) efn->compression);
ibuf[2] = (int) htonl ((unsigned int) efn->size);
ibuf[3] = (int) htonl ((unsigned int) efn->data_size);
ibuf[4] = (int) htonl ((unsigned int) name_size);
2007-05-02 04:08:41 -07:00
if (fwrite(ibuf, sizeof(ibuf), 1, ef->fp) != 1)
goto write_error;
if (fwrite(efn->name, name_size, 1, ef->fp) != 1)
goto write_error;
2002-12-02 15:39:26 -08:00
}
}
2002-12-02 15:39:26 -08:00
/* write data */
for (i = 0; i < num; i++)
{
for (efn = ef->header->directory->nodes[i]; efn; efn = efn->next)
2002-12-02 15:39:26 -08:00
{
2007-05-02 04:08:41 -07:00
if (fwrite(efn->data, efn->size, 1, ef->fp) != 1)
goto write_error;
2002-12-02 15:39:26 -08:00
}
}
2002-12-02 15:39:26 -08:00
/* no more writes pending */
ef->writes_pending = 0;
return EET_ERROR_NONE;
write_error:
if (ferror(ef->fp))
{
switch (errno)
{
case EFBIG:
fclose(ef->fp);
ef->fp = NULL;
return EET_ERROR_WRITE_ERROR_FILE_TOO_BIG;
case EIO:
fclose(ef->fp);
ef->fp = NULL;
return EET_ERROR_WRITE_ERROR_IO_ERROR;
case ENOSPC:
fclose(ef->fp);
ef->fp = NULL;
return EET_ERROR_WRITE_ERROR_OUT_OF_SPACE;
case EPIPE:
fclose(ef->fp);
ef->fp = NULL;
return EET_ERROR_WRITE_ERROR_FILE_CLOSED;
default:
fclose(ef->fp);
ef->fp = NULL;
return EET_ERROR_WRITE_ERROR;
}
}
sign_error:
if (ef->fp) fclose(ef->fp);
ef->fp = NULL;
return EET_ERROR_WRITE_ERROR;
2002-12-02 15:39:26 -08:00
}
#endif
2002-12-02 15:39:26 -08:00
EAPI int
eet_init(void)
{
eet_initcount++;
if (eet_initcount > 1) return eet_initcount;
#ifdef HAVE_GNUTLS
/* Before the library can be used, it must initialize itself if needed. */
if (gcry_control (GCRYCTL_ANY_INITIALIZATION_P) == 0)
{
gcry_check_version(NULL);
/* Disable warning messages about problems with the secure memory subsystem.
This command should be run right after gcry_check_version. */
if (gcry_control(GCRYCTL_DISABLE_SECMEM_WARN))
return --eet_initcount;
/* This command is used to allocate a pool of secure memory and thus
enabling the use of secure memory. It also drops all extra privileges the
process has (i.e. if it is run as setuid (root)). If the argument nbytes
is 0, secure memory will be disabled. The minimum amount of secure memory
allocated is currently 16384 bytes; you may thus use a value of 1 to
request that default size. */
if (gcry_control(GCRYCTL_INIT_SECMEM, 16384, 0))
WRN("BIG FAT WARNING: I AM UNABLE TO REQUEST SECMEM, Cryptographic operation are at risk !");
}
if (gnutls_global_init())
return --eet_initcount;
#endif
#ifdef HAVE_OPENSSL
ERR_load_crypto_strings();
OpenSSL_add_all_algorithms();
#endif
if (!eina_init())
{
fprintf(stderr,"Eet: Eina init failed");
goto erro_eet_eina_init;
}
_eet_log_dom_global = eina_log_domain_register("Eet", EET_DEFAULT_LOG_COLOR);
if(_eet_log_dom_global < 0)
{
EINA_LOG_ERR("Eet Can not create a general log domain.");
goto error_eet_eina_log;
}
return eet_initcount;
error_eet_eina_log:
eina_shutdown();
erro_eet_eina_init:
#ifdef HAVE_GNUTLS
gnutls_global_deinit();
#endif
#ifdef HAVE_OPENSSL
EVP_cleanup();
ERR_free_strings();
#endif
return 0;
}
EAPI int
eet_shutdown(void)
{
eet_initcount--;
if (eet_initcount > 0) return eet_initcount;
eet_clearcache();
eina_log_domain_unregister(_eet_log_dom_global);
_eet_log_dom_global = -1;
eina_shutdown();
#ifdef HAVE_GNUTLS
gnutls_global_deinit();
#endif
#ifdef HAVE_OPENSSL
EVP_cleanup();
ERR_free_strings();
#endif
return eet_initcount;
}
EAPI void
eet_clearcache(void)
{
int num = 0;
int i;
/*
* We need to compute the list of eet file to close separately from the cache,
* due to eet_close removing them from the cache after each call.
*/
for (i = 0; i < eet_writers_num; i++)
{
if (eet_writers[i]->references <= 0) num++;
}
for (i = 0; i < eet_readers_num; i++)
{
if (eet_readers[i]->references <= 0) num++;
}
if (num > 0)
{
Eet_File **closelist = NULL;
closelist = alloca(num * sizeof(Eet_File *));
num = 0;
for (i = 0; i < eet_writers_num; i++)
{
if (eet_writers[i]->references <= 0)
{
closelist[num] = eet_writers[i];
eet_writers[i]->delete_me_now = 1;
num++;
}
}
for (i = 0; i < eet_readers_num; i++)
{
if (eet_readers[i]->references <= 0)
{
closelist[num] = eet_readers[i];
eet_readers[i]->delete_me_now = 1;
num++;
}
}
for (i = 0; i < num; i++)
{
eet_close(closelist[i]);
}
}
}
/* FIXME: MMAP race condition in READ_WRITE_MODE */
static Eet_File *
eet_internal_read2(Eet_File *ef)
{
const int *data = (const int*) ef->data;
const char *start = (const char*) ef->data;
int index = 0;
int num_directory_entries;
int bytes_directory_entries;
int num_dictionary_entries;
int bytes_dictionary_entries;
int signature_base_offset;
int i;
index += sizeof(int);
if (eet_test_close((int) ntohl(*data) != EET_MAGIC_FILE2, ef))
return NULL;
data++;
#define GET_INT(Value, Pointer, Index) \
{ \
Value = ntohl(*Pointer); \
Pointer++; \
Index += sizeof(int); \
}
/* get entries count and byte count */
GET_INT(num_directory_entries, data, index);
/* get dictionary count and byte count */
GET_INT(num_dictionary_entries, data, index);
bytes_directory_entries = EET_FILE2_DIRECTORY_ENTRY_SIZE * num_directory_entries + EET_FILE2_HEADER_SIZE;
bytes_dictionary_entries = EET_FILE2_DICTIONARY_ENTRY_SIZE * num_dictionary_entries;
/* we cant have <= 0 values here - invalid */
if (eet_test_close((num_directory_entries <= 0), ef))
return NULL;
/* we cant have more bytes directory and bytes in dictionaries than the size of the file */
if (eet_test_close((bytes_directory_entries + bytes_dictionary_entries) > ef->data_size, ef))
return NULL;
/* allocate header */
ef->header = calloc(1, sizeof(Eet_File_Header));
if (eet_test_close(!ef->header, ef))
return NULL;
ef->header->magic = EET_MAGIC_FILE_HEADER;
/* allocate directory block in ram */
ef->header->directory = calloc(1, sizeof(Eet_File_Directory));
if (eet_test_close(!ef->header->directory, ef))
return NULL;
/* 8 bit hash table (256 buckets) */
ef->header->directory->size = 8;
/* allocate base hash table */
ef->header->directory->nodes = calloc(1, sizeof(Eet_File_Node *) * (1 << ef->header->directory->size));
if (eet_test_close(!ef->header->directory->nodes, ef))
return NULL;
signature_base_offset = 0;
/* actually read the directory block - all of it, into ram */
for (i = 0; i < num_directory_entries; ++i)
{
const char *name;
Eet_File_Node *efn;
int name_offset;
int name_size;
int hash;
int flag;
/* out directory block is inconsistent - we have oveerun our */
/* dynamic block buffer before we finished scanning dir entries */
efn = malloc (sizeof(Eet_File_Node));
if (eet_test_close(!efn, ef))
return NULL;
/* get entrie header */
GET_INT(efn->offset, data, index);
GET_INT(efn->size, data, index);
GET_INT(efn->data_size, data, index);
GET_INT(name_offset, data, index);
GET_INT(name_size, data, index);
GET_INT(flag, data, index);
efn->compression = flag & 0x1 ? 1 : 0;
efn->ciphered = flag & 0x2 ? 1 : 0;
#define EFN_TEST(Test, Ef, Efn) \
if (eet_test_close(Test, Ef)) \
{ \
free(Efn); \
return NULL; \
}
/* check data pointer position */
EFN_TEST(!((efn->size > 0)
&& (efn->offset + efn->size <= ef->data_size)
&& (efn->offset > bytes_dictionary_entries + bytes_directory_entries)), ef, efn);
/* check name position */
EFN_TEST(!((name_size > 0)
&& (name_offset + name_size < ef->data_size)
&& (name_offset >= bytes_dictionary_entries + bytes_directory_entries)), ef, efn);
name = start + name_offset;
/* check '\0' at the end of name string */
EFN_TEST(name[name_size - 1] != '\0', ef, efn);
efn->free_name = 0;
efn->name = (char*) name;
efn->name_size = name_size;
hash = _eet_hash_gen(efn->name, ef->header->directory->size);
efn->next = ef->header->directory->nodes[hash];
ef->header->directory->nodes[hash] = efn;
/* read-only mode, so currently we have no data loaded */
if (ef->mode == EET_FILE_MODE_READ)
efn->data = NULL;
/* read-write mode - read everything into ram */
else
{
efn->data = malloc(efn->size);
if (efn->data)
memcpy(efn->data, ef->data + efn->offset, efn->size);
}
/* compute the possible position of a signature */
if (signature_base_offset < efn->offset + efn->size)
signature_base_offset = efn->offset + efn->size;
}
ef->ed = NULL;
if (num_dictionary_entries)
{
const int *dico = (const int*) ef->data + EET_FILE2_DIRECTORY_ENTRY_COUNT * num_directory_entries + EET_FILE2_HEADER_COUNT;
int j;
if (eet_test_close((num_dictionary_entries * (int) EET_FILE2_DICTIONARY_ENTRY_SIZE + index) > (bytes_dictionary_entries + bytes_directory_entries), ef))
return NULL;
ef->ed = calloc(1, sizeof (Eet_Dictionary));
if (eet_test_close(!ef->ed, ef)) return NULL;
ef->ed->all = calloc(num_dictionary_entries, sizeof (Eet_String));
if (eet_test_close(!ef->ed->all, ef)) return NULL;
ef->ed->count = num_dictionary_entries;
ef->ed->total = num_dictionary_entries;
ef->ed->start = start + bytes_dictionary_entries + bytes_directory_entries;
ef->ed->end = ef->ed->start;
for (j = 0; j < ef->ed->count; ++j)
{
int hash;
int offset;
GET_INT(hash, dico, index);
GET_INT(offset, dico, index);
GET_INT(ef->ed->all[j].len, dico, index);
GET_INT(ef->ed->all[j].prev, dico, index);
GET_INT(ef->ed->all[j].next, dico, index);
/* Hash value could be stored on 8bits data, but this will break alignment of all the others data.
So stick to int and check the value. */
if (eet_test_close(hash & 0xFFFFFF00, ef)) return NULL;
/* Check string position */
if (eet_test_close(!((ef->ed->all[j].len > 0)
&& (offset > (bytes_dictionary_entries + bytes_directory_entries))
&& (offset + ef->ed->all[j].len < ef->data_size)), ef))
return NULL;
ef->ed->all[j].mmap = start + offset;
ef->ed->all[j].str = NULL;
if (ef->ed->all[j].mmap + ef->ed->all[j].len > ef->ed->end)
ef->ed->end = ef->ed->all[j].mmap + ef->ed->all[j].len;
/* Check '\0' at the end of the string */
if (eet_test_close(ef->ed->all[j].mmap[ef->ed->all[j].len - 1] != '\0', ef)) return NULL;
ef->ed->all[j].hash = hash;
if (ef->ed->all[j].prev == -1)
ef->ed->hash[hash] = j;
/* compute the possible position of a signature */
if (signature_base_offset < offset + ef->ed->all[j].len)
signature_base_offset = offset + ef->ed->all[j].len;
}
}
/* Check if the file is signed */
ef->x509_der = NULL;
ef->x509_length = 0;
ef->signature = NULL;
ef->signature_length = 0;
if (signature_base_offset < ef->data_size)
{
#ifdef HAVE_SIGNATURE
const unsigned char *buffer = ((const unsigned char*) ef->data) + signature_base_offset;
ef->x509_der = eet_identity_check(ef->data, signature_base_offset,
&ef->sha1, &ef->sha1_length,
buffer, ef->data_size - signature_base_offset,
&ef->signature, &ef->signature_length,
&ef->x509_length);
if (eet_test_close(ef->x509_der == NULL, ef)) return NULL;
#else
ERR("This file could be signed but you didn't compile the necessary code to check the signature.");
#endif
}
return ef;
}
#if EET_OLD_EET_FILE_FORMAT
static Eet_File *
eet_internal_read1(Eet_File *ef)
{
const unsigned char *dyn_buf = NULL;
const unsigned char *p = NULL;
int index = 0;
int num_entries;
int byte_entries;
int i;
WRN("EET file format of '%s' is deprecated. You should just open it one time with mode == EET_FILE_MODE_READ_WRITE to solve this issue.", ef->path);
/* build header table if read mode */
/* geat header */
index += sizeof(int);
if (eet_test_close((int)ntohl(*((int *)ef->data)) != EET_MAGIC_FILE, ef))
return NULL;
#define EXTRACT_INT(Value, Pointer, Index) \
{ \
int tmp; \
memcpy(&tmp, Pointer + Index, sizeof(int)); \
Value = ntohl(tmp); \
Index += sizeof(int); \
}
/* get entries count and byte count */
EXTRACT_INT(num_entries, ef->data, index);
EXTRACT_INT(byte_entries, ef->data, index);
/* we cant have <= 0 values here - invalid */
if (eet_test_close((num_entries <= 0) || (byte_entries <= 0), ef))
return NULL;
/* we can't have more entires than minimum bytes for those! invalid! */
if (eet_test_close((num_entries * 20) > byte_entries, ef))
return NULL;
/* check we will not outrun the file limit */
if (eet_test_close(((byte_entries + (int) sizeof(int) * 3) > ef->data_size), ef))
return NULL;
/* allocate header */
ef->header = calloc(1, sizeof(Eet_File_Header));
if (eet_test_close(!ef->header, ef))
return NULL;
ef->header->magic = EET_MAGIC_FILE_HEADER;
/* allocate directory block in ram */
ef->header->directory = calloc(1, sizeof(Eet_File_Directory));
if (eet_test_close(!ef->header->directory, ef))
return NULL;
/* 8 bit hash table (256 buckets) */
ef->header->directory->size = 8;
/* allocate base hash table */
ef->header->directory->nodes = calloc(1, sizeof(Eet_File_Node *) * (1 << ef->header->directory->size));
if (eet_test_close(!ef->header->directory->nodes, ef))
return NULL;
/* actually read the directory block - all of it, into ram */
dyn_buf = ef->data + index;
/* parse directory block */
p = dyn_buf;
for (i = 0; i < num_entries; i++)
{
Eet_File_Node *efn;
void *data = NULL;
int indexn = 0;
int name_size;
int hash;
int k;
#define HEADER_SIZE (sizeof(int) * 5)
/* out directory block is inconsistent - we have oveerun our */
/* dynamic block buffer before we finished scanning dir entries */
if (eet_test_close(p + HEADER_SIZE >= (dyn_buf + byte_entries), ef))
return NULL;
/* allocate all the ram needed for this stored node accounting */
efn = malloc (sizeof(Eet_File_Node));
if (eet_test_close(!efn, ef))
return NULL;
/* get entrie header */
EXTRACT_INT(efn->offset, p, indexn);
EXTRACT_INT(efn->compression, p, indexn);
EXTRACT_INT(efn->size, p, indexn);
EXTRACT_INT(efn->data_size, p, indexn);
EXTRACT_INT(name_size, p, indexn);
efn->name_size = name_size;
efn->ciphered = 0;
/* invalid size */
if (eet_test_close(efn->size <= 0, ef))
{
free (efn);
return NULL;
}
/* invalid name_size */
if (eet_test_close(name_size <= 0, ef))
{
free (efn);
return NULL;
}
/* reading name would mean falling off end of dyn_buf - invalid */
if (eet_test_close((p + 16 + name_size) > (dyn_buf + byte_entries), ef))
{
free (efn);
return NULL;
}
/* This code is useless if we dont want backward compatibility */
for (k = name_size; k > 0 && ((unsigned char) * (p + HEADER_SIZE + k)) != 0; --k)
;
efn->free_name = ((unsigned char) * (p + HEADER_SIZE + k)) != 0;
if (efn->free_name)
{
efn->name = malloc(sizeof(char) * name_size + 1);
if (eet_test_close(efn->name == NULL, ef))
{
free (efn);
return NULL;
}
strncpy(efn->name, (char *)p + HEADER_SIZE, name_size);
efn->name[name_size] = 0;
WRN("File: %s is not up to date for key \"%s\" - needs rebuilding sometime", ef->path, efn->name);
}
else
/* The only really usefull peace of code for efn->name (no backward compatibility) */
efn->name = (char*)((unsigned char*)(p + HEADER_SIZE));
/* get hash bucket it should go in */
hash = _eet_hash_gen(efn->name, ef->header->directory->size);
efn->next = ef->header->directory->nodes[hash];
ef->header->directory->nodes[hash] = efn;
/* read-only mode, so currently we have no data loaded */
if (ef->mode == EET_FILE_MODE_READ)
efn->data = NULL;
/* read-write mode - read everything into ram */
else
{
data = malloc(efn->size);
if (data)
memcpy(data, ef->data + efn->offset, efn->size);
efn->data = data;
}
/* advance */
p += HEADER_SIZE + name_size;
}
return ef;
}
#endif
static Eet_File *
eet_internal_read(Eet_File *ef)
{
const int *data = (const int*) ef->data;
if (eet_test_close((ef->data == (void *)-1) || (ef->data == NULL), ef))
return NULL;
if (eet_test_close(ef->data_size < (int) sizeof(int) * 3, ef))
return NULL;
switch (ntohl(*data))
{
#if EET_OLD_EET_FILE_FORMAT
case EET_MAGIC_FILE:
return eet_internal_read1(ef);
#endif
case EET_MAGIC_FILE2:
return eet_internal_read2(ef);
default:
ef->delete_me_now = 1;
eet_close(ef);
break;
}
return NULL;
}
EAPI Eet_File *
eet_memopen_read(const void *data, size_t size)
{
Eet_File *ef;
if (data == NULL || size == 0)
return NULL;
ef = malloc (sizeof (Eet_File));
if (!ef)
return NULL;
ef->ed = NULL;
ef->path = NULL;
ef->key = NULL;
ef->magic = EET_MAGIC_FILE;
ef->references = 1;
ef->mode = EET_FILE_MODE_READ;
ef->header = NULL;
ef->mtime = 0;
ef->delete_me_now = 1;
ef->fp = NULL;
ef->readfp = NULL;
ef->data = data;
ef->data_size = size;
ef->sha1 = NULL;
ef->sha1_length = 0;
return eet_internal_read(ef);
}
EAPI Eet_File *
2005-01-17 00:27:23 -08:00
eet_open(const char *file, Eet_File_Mode mode)
2002-12-02 15:39:26 -08:00
{
FILE *fp;
Eet_File *ef;
int file_len;
struct stat file_stat;
if (!file)
return NULL;
2002-12-02 15:39:26 -08:00
/* find the current file handle in cache*/
ef = NULL;
if (mode == EET_FILE_MODE_READ)
{
2005-11-03 19:22:22 -08:00
ef = eet_cache_find((char *)file, eet_writers, eet_writers_num);
if (ef)
{
eet_flush2(ef);
ef->references++;
ef->delete_me_now = 1;
eet_close(ef);
}
2005-11-03 19:22:22 -08:00
ef = eet_cache_find((char *)file, eet_readers, eet_readers_num);
}
else if ((mode == EET_FILE_MODE_WRITE) ||
(mode == EET_FILE_MODE_READ_WRITE))
{
2005-11-03 19:22:22 -08:00
ef = eet_cache_find((char *)file, eet_readers, eet_readers_num);
if (ef)
{
ef->delete_me_now = 1;
ef->references++;
eet_close(ef);
}
2005-11-03 19:22:22 -08:00
ef = eet_cache_find((char *)file, eet_writers, eet_writers_num);
}
/* try open the file based on mode */
if ((mode == EET_FILE_MODE_READ) || (mode == EET_FILE_MODE_READ_WRITE))
{
/* Prevent garbage in futur comparison. */
file_stat.st_mtime = 0;
fp = fopen(file, "rb");
if (!fp) goto on_error;
if (fstat(fileno(fp), &file_stat))
{
fclose(fp);
fp = NULL;
goto on_error;
}
if ((mode == EET_FILE_MODE_READ) &&
(file_stat.st_size < ((int) sizeof(int) * 3)))
{
fclose(fp);
fp = NULL;
goto on_error;
}
on_error:
if (fp == NULL && mode == EET_FILE_MODE_READ) return NULL;
}
else
{
int fd;
if (mode != EET_FILE_MODE_WRITE) return NULL;
memset(&file_stat, 0, sizeof(file_stat));
/* opening for write - delete old copy of file right away */
unlink(file);
fd = open(file, O_CREAT | O_TRUNC | O_RDWR, S_IRUSR | S_IWUSR);
fp = fdopen(fd, "wb");
if (!fp) return NULL;
}
/* We found one */
if (ef && (file_stat.st_mtime != ef->mtime))
{
ef->delete_me_now = 1;
ef->references++;
eet_close(ef);
ef = NULL;
}
2002-12-02 15:39:26 -08:00
if (ef)
{
/* reference it up and return it */
if (fp != NULL) fclose(fp);
2002-12-02 15:39:26 -08:00
ef->references++;
return ef;
}
file_len = strlen(file) + 1;
/* Allocate struct for eet file and have it zero'd out */
ef = malloc(sizeof(Eet_File) + file_len);
if (!ef)
return NULL;
2002-12-02 15:39:26 -08:00
/* fill some of the members */
ef->fp = fp;
ef->key = NULL;
ef->readfp = NULL;
2005-11-03 19:22:22 -08:00
ef->path = ((char *)ef) + sizeof(Eet_File);
memcpy(ef->path, file, file_len);
2002-12-02 15:39:26 -08:00
ef->magic = EET_MAGIC_FILE;
ef->references = 1;
ef->mode = mode;
ef->header = NULL;
ef->mtime = file_stat.st_mtime;
ef->writes_pending = 0;
ef->delete_me_now = 0;
ef->data = NULL;
ef->data_size = 0;
ef->sha1 = NULL;
ef->sha1_length = 0;
2002-12-02 15:39:26 -08:00
ef->ed = (mode == EET_FILE_MODE_WRITE)
|| (ef->fp == NULL && mode == EET_FILE_MODE_READ_WRITE) ?
eet_dictionary_add() : NULL;
if (ef->fp == NULL && mode == EET_FILE_MODE_READ_WRITE) goto empty_file;
2002-12-02 15:39:26 -08:00
/* if we can't open - bail out */
if (eet_test_close(!ef->fp, ef))
return NULL;
fcntl(fileno(ef->fp), F_SETFD, FD_CLOEXEC);
/* if we opened for read or read-write */
if ((mode == EET_FILE_MODE_READ) || (mode == EET_FILE_MODE_READ_WRITE))
2002-12-02 15:39:26 -08:00
{
ef->data_size = file_stat.st_size;
2006-09-15 22:57:36 -07:00
ef->data = mmap(NULL, ef->data_size, PROT_READ,
MAP_SHARED, fileno(ef->fp), 0);
2008-06-08 20:07:35 -07:00
if (eet_test_close((ef->data == MAP_FAILED), ef))
return NULL;
ef = eet_internal_read(ef);
if (!ef)
return NULL;
2002-12-02 15:39:26 -08:00
}
empty_file:
/* we need to delete the original file in read-write mode and re-open for writing */
if (ef->mode == EET_FILE_MODE_READ_WRITE)
{
ef->readfp = ef->fp;
ef->fp = NULL;
}
2002-12-02 15:39:26 -08:00
/* add to cache */
if (ef->references == 1)
{
if (ef->mode == EET_FILE_MODE_READ)
eet_cache_add(ef, &eet_readers, &eet_readers_num, &eet_readers_alloc);
else
if ((ef->mode == EET_FILE_MODE_WRITE) || (ef->mode == EET_FILE_MODE_READ_WRITE))
eet_cache_add(ef, &eet_writers, &eet_writers_num, &eet_writers_alloc);
}
2002-12-02 15:39:26 -08:00
return ef;
}
EAPI Eet_File_Mode
eet_mode_get(Eet_File *ef)
{
/* check to see its' an eet file pointer */
if ((!ef) || (ef->magic != EET_MAGIC_FILE))
return EET_FILE_MODE_INVALID;
else
return ef->mode;
}
EAPI const void *
eet_identity_x509(Eet_File *ef, int *der_length)
{
if (!ef->x509_der) return NULL;
if (der_length) *der_length = ef->x509_length;
return ef->x509_der;
}
EAPI const void *
eet_identity_signature(Eet_File *ef, int *signature_length)
{
if (!ef->signature) return NULL;
if (signature_length) *signature_length = ef->signature_length;
return ef->signature;
}
EAPI const void *
eet_identity_sha1(Eet_File *ef, int *sha1_length)
{
if (!ef->sha1)
ef->sha1 = eet_identity_compute_sha1(ef->data, ef->data_size, &ef->sha1_length);
if (sha1_length) *sha1_length = ef->sha1_length;
return ef->sha1;
}
EAPI Eet_Error
eet_identity_set(Eet_File *ef, Eet_Key *key)
{
Eet_Key *tmp = ef->key;
if (!ef) return EET_ERROR_BAD_OBJECT;
ef->key = key;
eet_identity_ref(ef->key);
eet_identity_unref(tmp);
/* flags that writes are pending */
ef->writes_pending = 1;
return EET_ERROR_NONE;
}
EAPI Eet_Error
2002-12-02 15:39:26 -08:00
eet_close(Eet_File *ef)
{
Eet_Error err;
/* check to see its' an eet file pointer */
if (eet_check_pointer(ef))
return EET_ERROR_BAD_OBJECT;
2002-12-02 15:39:26 -08:00
/* deref */
ef->references--;
/* if its still referenced - dont go any further */
if (ef->references > 0) return EET_ERROR_NONE;
/* flush any writes */
err = eet_flush2(ef);
eet_identity_unref(ef->key);
ef->key = NULL;
/* if not urgent to delete it - dont free it - leave it in cache */
if ((!ef->delete_me_now) && (ef->mode == EET_FILE_MODE_READ))
return EET_ERROR_NONE;
2002-12-02 15:39:26 -08:00
/* remove from cache */
if (ef->mode == EET_FILE_MODE_READ)
eet_cache_del(ef, &eet_readers, &eet_readers_num, &eet_readers_alloc);
else if ((ef->mode == EET_FILE_MODE_WRITE) || (ef->mode == EET_FILE_MODE_READ_WRITE))
eet_cache_del(ef, &eet_writers, &eet_writers_num, &eet_writers_alloc);
2002-12-02 15:39:26 -08:00
/* free up data */
if (ef->header)
2002-12-02 15:39:26 -08:00
{
if (ef->header->directory)
{
if (ef->header->directory->nodes)
2002-12-02 15:39:26 -08:00
{
int i, num;
num = (1 << ef->header->directory->size);
2002-12-02 15:39:26 -08:00
for (i = 0; i < num; i++)
{
Eet_File_Node *efn;
while ((efn = ef->header->directory->nodes[i]))
2002-12-02 15:39:26 -08:00
{
if (efn->data)
free(efn->data);
ef->header->directory->nodes[i] = efn->next;
if (efn->free_name)
free(efn->name);
free(efn);
2002-12-02 15:39:26 -08:00
}
}
free(ef->header->directory->nodes);
2002-12-02 15:39:26 -08:00
}
free(ef->header->directory);
}
free(ef->header);
}
eet_dictionary_free(ef->ed);
if (ef->sha1) free(ef->sha1);
if (ef->data) munmap((void*)ef->data, ef->data_size);
if (ef->fp) fclose(ef->fp);
if (ef->readfp) fclose(ef->readfp);
2002-12-02 15:39:26 -08:00
/* zero out ram for struct - caution tactic against stale memory use */
memset(ef, 0, sizeof(Eet_File));
2002-12-02 15:39:26 -08:00
/* free it */
free(ef);
return err;
2002-12-02 15:39:26 -08:00
}
EAPI void *
eet_read_cipher(Eet_File *ef, const char *name, int *size_ret, const char *cipher_key)
2002-12-02 15:39:26 -08:00
{
void *data = NULL;
int size = 0;
Eet_File_Node *efn;
if (size_ret)
*size_ret = 0;
2005-11-29 10:49:30 -08:00
2002-12-02 15:39:26 -08:00
/* check to see its' an eet file pointer */
if (eet_check_pointer(ef))
return NULL;
if (!name)
return NULL;
if ((ef->mode != EET_FILE_MODE_READ) &&
(ef->mode != EET_FILE_MODE_READ_WRITE))
return NULL;
/* no header, return NULL */
if (eet_check_header(ef))
return NULL;
2005-11-29 10:49:30 -08:00
2002-12-02 15:39:26 -08:00
/* hunt hash bucket */
2005-11-29 10:49:30 -08:00
efn = find_node_by_name(ef, name);
if (!efn)
return NULL;
2005-11-29 10:49:30 -08:00
/* get size (uncompressed, if compressed at all) */
size = efn->data_size;
2005-11-29 03:44:51 -08:00
2005-11-29 10:49:30 -08:00
/* allocate data */
data = malloc(size);
if (!data)
return NULL;
2005-11-29 10:49:30 -08:00
/* uncompressed data */
if (efn->compression == 0)
{
void *data_deciphered = NULL;
unsigned int data_deciphered_sz = 0;
2005-11-29 10:49:30 -08:00
/* if we alreayd have the data in ram... copy that */
if (efn->data)
memcpy(data, efn->data, efn->size);
else
if (!read_data_from_disk(ef, efn, data, size))
{
free(data);
return NULL;
}
if (efn->ciphered && cipher_key)
{
if (eet_decipher(data, size, cipher_key, strlen(cipher_key), &data_deciphered, &data_deciphered_sz))
{
free(data);
if (data_deciphered) free(data_deciphered);
return NULL;
}
free(data);
data = data_deciphered;
size = data_deciphered_sz;
}
2005-11-29 10:49:30 -08:00
}
/* compressed data */
else
{
void *tmp_data;
void *data_deciphered = NULL;
unsigned int data_deciphered_sz = 0;
int free_tmp = 0;
int compr_size = efn->size;
uLongf dlen;
2005-11-29 03:44:51 -08:00
2005-11-29 10:49:30 -08:00
/* if we already have the data in ram... copy that */
if (efn->data)
tmp_data = efn->data;
else
{
tmp_data = malloc(compr_size);
if (!tmp_data)
{
free(data);
return NULL;
}
2005-11-29 03:44:51 -08:00
2005-11-29 10:49:30 -08:00
free_tmp = 1;
2005-11-29 03:44:51 -08:00
2005-11-29 10:49:30 -08:00
if (!read_data_from_disk(ef, efn, tmp_data, compr_size))
{
free(tmp_data);
2005-11-29 10:49:30 -08:00
free(data);
return NULL;
2002-12-02 15:39:26 -08:00
}
}
2005-11-29 10:49:30 -08:00
if (efn->ciphered && cipher_key)
{
if (eet_decipher(tmp_data, compr_size, cipher_key, strlen(cipher_key), &data_deciphered, &data_deciphered_sz))
{
free(data);
if (free_tmp) free(tmp_data);
if (data_deciphered) free(data_deciphered);
return NULL;
}
free(tmp_data);
tmp_data = data_deciphered;
compr_size = data_deciphered_sz;
}
2005-11-29 10:49:30 -08:00
/* decompress it */
dlen = size;
if (uncompress((Bytef *)data, &dlen,
tmp_data, (uLongf)compr_size))
{
free(data);
return NULL;
}
if (free_tmp)
free(tmp_data);
2002-12-02 15:39:26 -08:00
}
2005-11-29 10:49:30 -08:00
2002-12-02 15:39:26 -08:00
/* fill in return values */
if (size_ret)
*size_ret = size;
return data;
}
EAPI void *
eet_read(Eet_File *ef, const char *name, int *size_ret)
{
return eet_read_cipher(ef, name, size_ret, NULL);
}
EAPI const void *
eet_read_direct(Eet_File *ef, const char *name, int *size_ret)
{
const void *data = NULL;
int size = 0;
Eet_File_Node *efn;
if (size_ret)
*size_ret = 0;
/* check to see its' an eet file pointer */
if (eet_check_pointer(ef))
return NULL;
if (!name)
return NULL;
if ((ef->mode != EET_FILE_MODE_READ) &&
(ef->mode != EET_FILE_MODE_READ_WRITE))
return NULL;
/* no header, return NULL */
if (eet_check_header(ef))
return NULL;
/* hunt hash bucket */
efn = find_node_by_name(ef, name);
if (!efn)
return NULL;
if (efn->offset < 0 && efn->data == NULL)
return NULL;
/* get size (uncompressed, if compressed at all) */
size = efn->data_size;
/* uncompressed data */
if (efn->compression == 0
&& efn->ciphered == 0)
data = efn->data ? efn->data : ef->data + efn->offset;
/* compressed data */
else
data = NULL;
/* fill in return values */
if (size_ret)
*size_ret = size;
2002-12-02 15:39:26 -08:00
return data;
}
EAPI int
eet_write_cipher(Eet_File *ef, const char *name, const void *data, int size, int compress, const char *cipher_key)
2002-12-02 15:39:26 -08:00
{
Eet_File_Node *efn;
void *data2 = NULL;
int exists_already = 0;
int data_size;
int hash;
/* check to see its' an eet file pointer */
if (eet_check_pointer(ef))
return 0;
if ((!name) || (!data) || (size <= 0))
return 0;
if ((ef->mode != EET_FILE_MODE_WRITE) &&
(ef->mode != EET_FILE_MODE_READ_WRITE))
2002-12-02 15:39:26 -08:00
return 0;
if (!ef->header)
{
/* allocate header */
ef->header = calloc(1, sizeof(Eet_File_Header));
if (!ef->header)
return 0;
2002-12-02 15:39:26 -08:00
ef->header->magic = EET_MAGIC_FILE_HEADER;
/* allocate directory block in ram */
ef->header->directory = calloc(1, sizeof(Eet_File_Directory));
if (!ef->header->directory)
return 0;
2002-12-02 15:39:26 -08:00
/* 8 bit hash table (256 buckets) */
ef->header->directory->size = 8;
/* allocate base hash table */
ef->header->directory->nodes = calloc(1, sizeof(Eet_File_Node *) * (1 << ef->header->directory->size));
if (!ef->header->directory->nodes)
return 0;
2002-12-02 15:39:26 -08:00
}
2005-05-26 03:53:53 -07:00
/* figure hash bucket */
hash = _eet_hash_gen(name, ef->header->directory->size);
2005-05-26 03:53:53 -07:00
data_size = compress ? 12 + ((size * 101) / 100) : size;
2005-05-26 03:53:53 -07:00
if (compress || !cipher_key)
{
data2 = malloc(data_size);
if (!data2)
return 0;
}
2002-12-02 15:39:26 -08:00
/* if we want to compress */
if (compress)
2002-12-02 15:39:26 -08:00
{
uLongf buflen;
2002-12-02 15:39:26 -08:00
/* compress the data with max compression */
2002-12-14 17:32:13 -08:00
buflen = (uLongf)data_size;
2005-05-26 03:22:56 -07:00
if (compress2((Bytef *)data2, &buflen, (Bytef *)data,
(uLong)size, Z_BEST_COMPRESSION) != Z_OK)
2002-12-02 15:39:26 -08:00
{
free(data2);
return 0;
}
/* record compressed chunk size */
data_size = (int)buflen;
if (data_size < 0 || data_size >= size)
2002-12-02 15:39:26 -08:00
{
compress = 0;
data_size = size;
}
2002-12-14 17:32:13 -08:00
else
{
void *data3;
2002-12-14 17:32:13 -08:00
data3 = realloc(data2, data_size);
if (data3)
data2 = data3;
2002-12-14 17:32:13 -08:00
}
2002-12-02 15:39:26 -08:00
}
if (cipher_key)
{
void *data_ciphered = NULL;
unsigned int data_ciphered_sz = 0;
const void *tmp;
tmp = data2 ? data2 : data;
if (!eet_cipher(tmp, data_size, cipher_key, strlen(cipher_key), &data_ciphered, &data_ciphered_sz))
{
if (data2) free(data2);
data2 = data_ciphered;
data_size = data_ciphered_sz;
size = (data_size > size) ? data_size : size;
}
else
{
if (data_ciphered) free(data_ciphered);
cipher_key = NULL;
}
}
else
if (!compress)
memcpy(data2, data, size);
/* Does this node already exist? */
for (efn = ef->header->directory->nodes[hash]; efn; efn = efn->next)
2002-12-02 15:39:26 -08:00
{
/* if it matches */
if ((efn->name) && (eet_string_match(efn->name, name)))
{
free(efn->data);
efn->ciphered = cipher_key ? 1 : 0;
efn->compression = !!compress;
efn->size = data_size;
efn->data_size = size;
efn->data = data2;
efn->offset = -1;
exists_already = 1;
break;
}
2002-12-02 15:39:26 -08:00
}
if (!exists_already)
{
efn = malloc(sizeof(Eet_File_Node));
if (!efn)
{
free(data2);
return 0;
}
efn->name = strdup(name);
efn->name_size = strlen(efn->name) + 1;
efn->free_name = 1;
efn->next = ef->header->directory->nodes[hash];
ef->header->directory->nodes[hash] = efn;
efn->offset = -1;
efn->ciphered = cipher_key ? 1 : 0;
efn->compression = !!compress;
efn->size = data_size;
efn->data_size = size;
efn->data = data2;
}
2002-12-02 15:39:26 -08:00
/* flags that writes are pending */
ef->writes_pending = 1;
2003-06-16 06:39:42 -07:00
return data_size;
2002-12-02 15:39:26 -08:00
}
EAPI int
eet_write(Eet_File *ef, const char *name, const void *data, int size, int compress)
{
return eet_write_cipher(ef, name, data, size, compress, NULL);
}
EAPI int
eet_delete(Eet_File *ef, const char *name)
2004-12-01 20:25:25 -08:00
{
Eet_File_Node *efn;
Eet_File_Node *pefn;
int hash;
int exists_already = 0;
/* check to see its' an eet file pointer */
if (eet_check_pointer(ef))
return 0;
if (!name)
2004-12-01 20:25:25 -08:00
return 0;
2005-11-03 19:22:22 -08:00
/* deleting keys is only possible in RW or WRITE mode */
if (ef->mode == EET_FILE_MODE_READ)
return 0;
if (eet_check_header(ef))
return 0;
2004-12-01 20:25:25 -08:00
/* figure hash bucket */
hash = _eet_hash_gen(name, ef->header->directory->size);
2004-12-01 20:25:25 -08:00
/* Does this node already exist? */
for (pefn = NULL, efn = ef->header->directory->nodes[hash];
efn;
pefn = efn, efn = efn->next)
2004-12-01 20:25:25 -08:00
{
/* if it matches */
if (eet_string_match(efn->name, name))
2004-12-01 20:25:25 -08:00
{
if (efn->data)
free(efn->data);
if (pefn == NULL)
ef->header->directory->nodes[hash] = efn->next;
else
pefn->next = efn->next;
if (efn->free_name) free(efn->name);
free(efn);
exists_already = 1;
break;
2004-12-01 20:25:25 -08:00
}
}
/* flags that writes are pending */
if (exists_already)
ef->writes_pending = 1;
2004-12-01 20:25:25 -08:00
/* update access time */
return exists_already;
}
EAPI Eet_Dictionary *
eet_dictionary_get(Eet_File *ef)
{
if (eet_check_pointer(ef)) return NULL;
return ef->ed;
}
EAPI char **
eet_list(Eet_File *ef, const char *glob, int *count_ret)
2002-12-02 15:39:26 -08:00
{
Eet_File_Node *efn;
char **list_ret = NULL;
int list_count = 0;
int list_count_alloc = 0;
int i, num;
2002-12-02 15:39:26 -08:00
/* check to see its' an eet file pointer */
if (eet_check_pointer(ef) || eet_check_header(ef) ||
(!glob) ||
((ef->mode != EET_FILE_MODE_READ) &&
(ef->mode != EET_FILE_MODE_READ_WRITE)))
2002-12-02 15:39:26 -08:00
{
if (count_ret)
*count_ret = 0;
2002-12-02 15:39:26 -08:00
return NULL;
}
2002-12-02 15:39:26 -08:00
/* loop through all entries */
num = (1 << ef->header->directory->size);
2002-12-02 15:39:26 -08:00
for (i = 0; i < num; i++)
{
for (efn = ef->header->directory->nodes[i]; efn; efn = efn->next)
2002-12-02 15:39:26 -08:00
{
/* if the entry matches the input glob
* check for * explicitly, because on some systems, * isn't well
* supported
*/
if ((!strcmp (glob, "*")) || !fnmatch(glob, efn->name, 0))
2002-12-02 15:39:26 -08:00
{
/* add it to our list */
list_count++;
2002-12-02 15:39:26 -08:00
/* only realloc in 32 entry chunks */
if (list_count > list_count_alloc)
{
char **new_list = NULL;
2005-11-03 19:22:22 -08:00
list_count_alloc += 64;
2002-12-02 15:39:26 -08:00
new_list = realloc(list_ret, list_count_alloc * (sizeof(char *)));
if (!new_list)
{
free(list_ret);
if (count_ret)
*count_ret = 0;
return NULL;
2002-12-02 15:39:26 -08:00
}
list_ret = new_list;
}
2002-12-02 15:39:26 -08:00
/* put pointer of name string in */
list_ret[list_count - 1] = efn->name;
2002-12-02 15:39:26 -08:00
}
}
}
2002-12-02 15:39:26 -08:00
/* return count and list */
if (count_ret)
*count_ret = list_count;
2002-12-02 15:39:26 -08:00
return list_ret;
}
EAPI int
eet_num_entries(Eet_File *ef)
{
int i, num, ret = 0;
Eet_File_Node *efn;
/* check to see its' an eet file pointer */
if (eet_check_pointer(ef) || eet_check_header(ef) ||
((ef->mode != EET_FILE_MODE_READ) &&
(ef->mode != EET_FILE_MODE_READ_WRITE)))
return -1;
/* loop through all entries */
num = (1 << ef->header->directory->size);
for (i = 0; i < num; i++)
{
for (efn = ef->header->directory->nodes[i]; efn; efn = efn->next)
ret++;
}
return ret;
}
2005-11-29 10:49:30 -08:00
static Eet_File_Node *
find_node_by_name(Eet_File *ef, const char *name)
2005-11-29 10:49:30 -08:00
{
Eet_File_Node *efn;
int hash;
/* get hash bucket this should be in */
hash = _eet_hash_gen(name, ef->header->directory->size);
2005-11-29 10:49:30 -08:00
for (efn = ef->header->directory->nodes[hash]; efn; efn = efn->next)
{
if (eet_string_match(efn->name, name))
return efn;
}
2005-11-29 10:49:30 -08:00
return NULL;
}
static int
read_data_from_disk(Eet_File *ef, Eet_File_Node *efn, void *buf, int len)
{
if (efn->offset < 0) return 0;
if (ef->data)
{
if ((efn->offset + len) > ef->data_size) return 0;
memcpy(buf, ef->data + efn->offset, len);
}
else
{
/* seek to data location */
if (fseek(ef->fp, efn->offset, SEEK_SET) < 0)
return 0;
/* read it */
len = fread(buf, len, 1, ef->fp);
}
return len;
2005-11-29 10:49:30 -08:00
}