diff options
Diffstat (limited to 'src/glibext/storage.c')
-rw-r--r-- | src/glibext/storage.c | 1147 |
1 files changed, 1147 insertions, 0 deletions
diff --git a/src/glibext/storage.c b/src/glibext/storage.c new file mode 100644 index 0000000..0a3c4e7 --- /dev/null +++ b/src/glibext/storage.c @@ -0,0 +1,1147 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * storage.c - conservation hors mémoire d'objets choisis + * + * Copyright (C) 2020-2025 Cyrille Bagard + * + * This file is part of Chrysalide. + * + * Chrysalide is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Chrysalide 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Foobar. If not, see <http://www.gnu.org/licenses/>. + */ + + +#include "storage.h" + + +#include <assert.h> +#include <malloc.h> +#include <string.h> +#include <unistd.h> +#include <zip.h> + + +#include "storage-int.h" +#include "../common/cpp.h" +#include "../common/pathname.h" +#include "../core/logs.h" + + +/** + * Historique du format : + * + * - 09/03/25 : 1.0 (version initiale) + * + */ +#define STORAGE_MAGIC "COBSTR" +#define STORAGE_NUMBER "\x01\x00" + + +/* Initialise la classe des conservations d'objets en place. */ +static void g_object_storage_class_init(GObjectStorageClass *); + +/* Initialise une instance de conservation d'objets en place. */ +static void g_object_storage_init(GObjectStorage *); + +/* Supprime toutes les références externes. */ +static void g_object_storage_dispose(GObject *); + +/* Procède à la libération totale de la mémoire. */ +static void g_object_storage_finalize(GObject *); + +/* Assure l'inexistence d'un groupe avec un nom donné. */ +static bool g_object_storage_has_no_backend_named(GObjectStorage *, const char *); + +/* Retrouve l'encadrement pour un nouveau groupe d'objets. */ +static storage_backend_t *g_object_storage_find_backend(GObjectStorage *, const char *); + +/* Ajoute le support d'un nouveau groupe d'objets construits. */ +static bool g_object_storage_add_backend(GObjectStorage *, const char *, storage_backend_t **); + +/* Charge un objet à partir de données rassemblées. */ +static GSerializableObject *g_object_storage_load_object_unlocked(GObjectStorage *, const char *, off64_t); + + + +/* Indique le type défini pour une conservation d'objets construits. */ +G_DEFINE_TYPE(GObjectStorage, g_object_storage, G_TYPE_OBJECT); + + +/****************************************************************************** +* * +* Paramètres : klass = classe à initialiser. * +* * +* Description : Initialise la classe des conservations d'objets en place. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_object_storage_class_init(GObjectStorageClass *klass) +{ + GObjectClass *object; /* Autre version de la classe */ + + object = G_OBJECT_CLASS(klass); + + object->dispose = g_object_storage_dispose; + object->finalize = g_object_storage_finalize; + +} + + +/****************************************************************************** +* * +* Paramètres : storage = instance à initialiser. * +* * +* Description : Initialise une instance de conservation d'objets en place. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_object_storage_init(GObjectStorage *storage) +{ + init_sized_binary(&storage->type); + storage->version = 0; + + init_sized_binary(&storage->uid); + + storage->tpmem = g_type_memory_new(); + + storage->backends = NULL; + storage->count = 0; + g_mutex_init(&storage->mutex); + +} + + +/****************************************************************************** +* * +* Paramètres : object = instance d'objet GLib à traiter. * +* * +* Description : Supprime toutes les références externes. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_object_storage_dispose(GObject *object) +{ + GObjectStorage *storage; /* Version spécialisée */ + + storage = G_OBJECT_STORAGE(object); + + g_clear_object(&storage->tpmem); + + G_OBJECT_CLASS(g_object_storage_parent_class)->dispose(object); + +} + + +/****************************************************************************** +* * +* Paramètres : object = instance d'objet GLib à traiter. * +* * +* Description : Procède à la libération totale de la mémoire. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_object_storage_finalize(GObject *object) +{ + GObjectStorage *storage; /* Version spécialisée */ + size_t i; /* Boucle de parcours */ + storage_backend_t *backend; /* Gestionnaire à manipuler */ + int ret; /* Bilan d'un appel */ + + storage = G_OBJECT_STORAGE(object); + + g_mutex_lock(&storage->mutex); + + for (i = 0; i < storage->count; i++) + { + backend = &storage->backends[i]; + + /** + * Chargement incomplet depuis g_object_storage_load(). + */ + if (backend->name == NULL) + break; + + if (backend->fd != -1) + close(backend->fd); + else + assert(false); + + ret = access(backend->filename, W_OK); + if (ret == 0) + { + ret = unlink(backend->filename); + if (ret != 0) LOG_ERROR_N("unlink"); + } + + free(backend->name); + + free(backend->filename); + + } + + if (storage->backends != NULL) + free(storage->backends); + + g_mutex_unlock(&storage->mutex); + + g_mutex_clear(&storage->mutex); + + exit_sized_binary(&storage->type); + + exit_sized_binary(&storage->uid); + + G_OBJECT_CLASS(g_object_storage_parent_class)->finalize(object); + +} + + +/****************************************************************************** +* * +* Paramètres : type = type global à indiquer dans une conservation. * +* version = numéro de version associé. * +* uid = identifiant arbitraire mais unique pour distinguer.* +* * +* Description : Crée le support d'une conservation d'objets en place. * +* * +* Retour : Mécanismes mis en place. * +* * +* Remarques : - * +* * +******************************************************************************/ + +GObjectStorage *g_object_storage_new(const char *type, uint8_t version, const char *uid) +{ + GObjectStorage *result; /* Structure à retourner */ + + result = g_object_new(G_TYPE_OBJECT_STORAGE, NULL); + + if (!g_object_storage_create(result, type, version, uid)) + g_clear_object(&result); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : storage = stockage d'objets à initialiser. * +* type = type global à indiquer dans une conservation. * +* version = numéro de version associé. * +* uid = identifiant arbitraire mais unique pour distinguer.* +* * +* Description : Met en place un support d'une conservation d'objets en place.* +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool g_object_storage_create(GObjectStorage *storage, const char *type, uint8_t version, const char *uid) +{ + bool result; /* Bilan à retourner */ + + result = true; + + dup_into_sized_binary(&storage->type, type, strlen(type)); + + storage->version = version; + + dup_into_sized_binary(&storage->uid, uid, strlen(uid) + 1); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : filename = fichier de source à traiter. * +* * +* Description : Charge le support d'une conservation d'objets en place. * +* * +* Retour : Gestionnaire de conservations construit ou NULL si erreur. * +* * +* Remarques : - * +* * +******************************************************************************/ + +GObjectStorage *g_object_storage_load(const char *filename) +{ + GObjectStorage *result; /* Structure à retourner */ + GObjectStorage *storage; /* Structure en construction */ + int err; /* Eventuel code d'erreur */ + zip_t *archive; /* Archive ZIP à manipuler */ + zip_error_t error; /* Suivi des erreurs obtenues */ + char *tpmem_filename; /* Chemin d'accès pour types */ + zip_int64_t entries_count; /* Nombre d'éléments ZIP */ + void *data; /* Données (décompressées) */ + zip_stat_t stats; /* Information sur les données */ + zip_file_t *file; /* Echantillon à extraire */ + zip_int64_t got; /* Nombre d'octets lus */ + int ret; /* Bilan d'un appel */ + const void *pos; /* Tête de lecture */ + const void *max; /* Fin des données lisibles */ + bool status; /* Bilan d'une extraction */ + char *prefix; /* Début de nom de fichier */ + int fd; /* Descripteur de flux ouvert */ + off_t moved; /* Nouvelle position établie */ + zip_int64_t i; /* Boucle de parcours */ + storage_backend_t *backend; /* Informations à intégrer */ + const char *slash; /* Pointeur vers un caractère /*/ + + result = NULL; + + storage = g_object_new(G_TYPE_OBJECT_STORAGE, NULL); + + archive = zip_open(filename, ZIP_RDONLY, &err); + if (archive == NULL) + { + zip_error_init_with_code(&error, err); + LOG_ERROR_ZIP("zip_open", &error); + goto exit; + } + + zip_error_init(&error); + + tpmem_filename = NULL; + + /* Validation du nombre d'entrées */ + + entries_count = zip_get_num_entries(archive, ZIP_FL_UNCHANGED); + + if (entries_count < 2) + goto exit_with_archive; + + data = NULL; + + /* Extraction de la partie de contrôle */ + + ret = zip_stat_index(archive, 0, ZIP_FL_UNCHANGED, &stats); + if (ret != 0) + { + LOG_ERROR_ZIP("zip_stat_index", zip_get_error(archive)); + goto exit_with_archive; + } + + if ((stats.valid & (ZIP_STAT_NAME | ZIP_STAT_SIZE)) != (ZIP_STAT_NAME | ZIP_STAT_SIZE)) + goto exit_with_archive; + + if (strcmp(stats.name, "control") != 0) + goto exit_with_archive; + + if (stats.size < (6 + 2 + 1 + 1 + 1)) + goto exit_with_archive; + + file = zip_fopen_index(archive, 0, ZIP_FL_UNCHANGED); + if (file == NULL) + { + LOG_ERROR_ZIP("zip_fopen_index", zip_get_error(archive)); + goto exit_with_archive; + } + + data = malloc(stats.size); + + got = zip_fread(file, data, stats.size); + + ret = zip_fclose(file); + if (ret != 0) + { + zip_error_set(&error, ret, 0); + LOG_ERROR_ZIP("zip_fclose", &error); + goto exit_with_data; + } + + if (got != stats.size) + goto exit_with_data; + + if (memcmp(data, STORAGE_MAGIC, 6) != 0) + goto exit_with_data; + + if (memcmp(((uint8_t *)data) + 6, STORAGE_NUMBER, 2) != 0) + goto exit_with_data; + + pos = (uint8_t *)data + 8; + max = (uint8_t *)data + got; + + status = unpack_sized_binary(&storage->type, &pos, max); + if (!status) goto exit_with_data; + + if (pos >= max) + goto exit_with_data; + + storage->version = *(uint8_t *)pos; + pos = (uint8_t *)pos + 1; + + unpack_sized_binary_as_string(&storage->uid, &pos, max); + if (!status) goto exit_with_data; + + if (pos != max) + goto exit_with_data; + + free(data); + data = NULL; + + /* Extraction de la conservation des types */ + + ret = zip_stat_index(archive, 1, ZIP_FL_UNCHANGED, &stats); + if (ret != 0) + { + LOG_ERROR_ZIP("zip_stat_index", zip_get_error(archive)); + goto exit_with_archive; + } + + if ((stats.valid & (ZIP_STAT_NAME | ZIP_STAT_SIZE)) != (ZIP_STAT_NAME | ZIP_STAT_SIZE)) + goto exit_with_archive; + + if (strcmp(stats.name, "types") != 0) + goto exit_with_archive; + + file = zip_fopen_index(archive, 1, ZIP_FL_UNCHANGED); + if (file == NULL) + { + LOG_ERROR_ZIP("zip_fopen_index", zip_get_error(archive)); + goto exit_with_archive; + } + + data = malloc(stats.size); + + got = zip_fread(file, data, stats.size); + + ret = zip_fclose(file); + if (ret != 0) + { + zip_error_set(&error, ret, 0); + LOG_ERROR_ZIP("zip_fclose", &error); + goto exit_with_data; + } + + asprintf(&prefix, "%s-types", storage->uid.static_data); + + fd = make_tmp_file(prefix, "cache", &tpmem_filename); + + free(prefix); + + if (fd == -1) + goto exit_with_data; + + status = safe_write(fd, data, stats.size); + if (!status) + { + close(fd); + goto exit_with_data; + } + + moved = lseek(fd, 0, SEEK_SET); + if (moved == ((off_t)-1)) + { + LOG_ERROR_N("lseek"); + close(fd); + goto exit_with_data; + } + + status = g_type_memory_load(storage->tpmem, fd); + + close(fd); + + if (!status) + goto exit_with_data; + + free(data); + data = NULL; + + /* Extraction des différents objects restants */ + + if (entries_count > 2) + { + storage->count = entries_count - 2; + storage->backends = calloc(storage->count, sizeof(storage_backend_t)); + + for (i = 2; i < entries_count; i++) + { + backend = &storage->backends[i - 2]; + + ret = zip_stat_index(archive, i, ZIP_FL_UNCHANGED, &stats); + if (ret != 0) + { + LOG_ERROR_ZIP("zip_stat_index", zip_get_error(archive)); + goto exit_with_archive; + } + + if ((stats.valid & (ZIP_STAT_NAME | ZIP_STAT_SIZE)) != (ZIP_STAT_NAME | ZIP_STAT_SIZE)) + goto exit_with_archive; + + if (strncmp(stats.name, SL("backends/")) != 0) + goto exit_with_archive; + + slash = strchr(stats.name, '/'); + + if (slash == NULL) + goto exit_with_archive; + + if (strchr(slash + 1, '/') != NULL) + goto exit_with_archive; + + if (!g_object_storage_has_no_backend_named(storage, slash + 1)) + goto exit_with_archive; + + file = zip_fopen_index(archive, i, ZIP_FL_UNCHANGED); + if (file == NULL) + { + LOG_ERROR_ZIP("zip_fopen_index", zip_get_error(archive)); + goto exit_with_archive; + } + + data = malloc(stats.size); + + got = zip_fread(file, data, stats.size); + + ret = zip_fclose(file); + if (ret != 0) + { + zip_error_set(&error, ret, 0); + LOG_ERROR_ZIP("zip_fclose", &error); + goto exit_with_data; + } + + backend->name = strdup(slash + 1); + + asprintf(&prefix, "%s-%s", storage->uid.static_data, backend->name); + + backend->fd = make_tmp_file(prefix, "cache", &backend->filename); + + free(prefix); + + if (backend->fd == -1) + goto exit_with_data; + + status = safe_write(backend->fd, data, stats.size); + if (!status) goto exit_with_data; + + moved = lseek(backend->fd, 0, SEEK_SET); + if (moved == ((off_t)-1)) + { + LOG_ERROR_N("lseek"); + goto exit_with_data; + } + + free(data); + data = NULL; + + } + + } + + /* Clôture des opérations */ + + result = storage; + ref_object(storage); + + exit_with_data: + + if (data != NULL) + free(data); + + exit_with_archive: + + ret = zip_close(archive); + + if (ret != 0) + LOG_ERROR_ZIP("zip_close", zip_get_error(archive)); + + if (tpmem_filename != NULL) + unlink(tpmem_filename); + + zip_error_fini(&error); + + exit: + + unref_object(storage); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : storage = gestionnaire de conservations à manipuler. * +* filename = fichier de destination à constituer. * +* * +* Description : Sauvegarde le support d'une conservation d'objets en place. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool g_object_storage_store(GObjectStorage *storage, const char *filename) +{ + bool result; /* Bilan à retourner */ + int err; /* Eventuel code d'erreur */ + zip_t *archive; /* Archive ZIP à manipuler */ + zip_error_t error; /* Suivi des erreurs obtenues */ + char *tpmem_filename; /* Chemin d'accès pour types */ + void *type_buf; /* Données pour le type */ + size_t type_buflen; /* Quantité de ces données */ + void *uid_buf; /* Données pour l'identifiant */ + size_t uid_buflen; /* Quantité de ces données */ + size_t control_len; /* Taille des premières données*/ + uint8_t *control; /* Premières données du fichier*/ + zip_source_t *zip_data; /* Données ZIP à intégrer */ + zip_int64_t index; /* Nouvel index du contenu */ + int ret; /* Bilan d'un appel */ + char *prefix; /* Début de nom de fichier */ + int fd; /* Descripteur de flux ouvert */ + bool status; /* Bilan d'une écriture */ + size_t i; /* Boucle de parcours */ + char *zip_name; /* Destination pour l'archive */ + + result = false; + + archive = zip_open(filename, ZIP_CREATE | ZIP_TRUNCATE, &err); + if (archive == NULL) + { + zip_error_init_with_code(&error, err); + LOG_ERROR_ZIP("zip_open", &error); + goto exit; + } + + zip_error_init(&error); + + tpmem_filename = NULL; + + /* Fichier de contrôle */ + + type_buf = pack_sized_binary(&storage->type, &type_buflen); + + uid_buf = pack_sized_binary_as_string(&storage->uid, &uid_buflen); + + assert((sizeof(STORAGE_MAGIC) - 1 + sizeof(STORAGE_NUMBER) - 1) == 8); + + control_len = 8 + type_buflen + 1 + uid_buflen; + control = malloc(control_len * sizeof(uint8_t)); + + memcpy(control, STORAGE_MAGIC, 6); + memcpy(control + 6, STORAGE_NUMBER, 2); + + memcpy(control + 8, type_buf, type_buflen); + + control[8 + type_buflen] = storage->version; + + memcpy(control + 8 + type_buflen + 1, uid_buf, uid_buflen); + + zip_data = zip_source_buffer_create(control, control_len, 0, &error); + if (zip_data == NULL) + { + LOG_ERROR_ZIP("zip_source_buffer_create", &error); + goto exit_with_control; + } + + index = zip_file_add(archive, "control", zip_data, ZIP_FL_ENC_UTF_8); + if (index == -1) + { + zip_source_free(zip_data); + LOG_ERROR_ZIP("zip_file_add", zip_get_error(archive)); + goto exit_with_control; + } + + ret = zip_set_file_compression(archive, index, ZIP_CM_STORE, 0 /* comp_flags */); + if (ret == -1) + { + LOG_ERROR_ZIP("zip_set_file_compression", zip_get_error(archive)); + goto exit_with_control; + } + + /* Composants de la conservation */ + + g_mutex_lock(&storage->mutex); + + /* Conservation des types */ + + asprintf(&prefix, "%s-types", storage->uid.static_data); + + fd = make_tmp_file(prefix, "cache", &tpmem_filename); + + free(prefix); + + if (fd == -1) + goto exit_with_lock; + + status = g_type_memory_store(storage->tpmem, fd); + + close(fd); + + if (!status) + goto exit_with_lock; + + zip_data = zip_source_file_create(tpmem_filename, 0, -1 /* ZIP_LENGTH_TO_END */, &error); + if (zip_data == NULL) + { + LOG_ERROR_ZIP("zip_source_file_create", &error); + goto exit_with_lock; + } + + index = zip_file_add(archive, "types", zip_data, ZIP_FL_ENC_UTF_8); + if (index == -1) + { + zip_source_free(zip_data); + LOG_ERROR_ZIP("zip_file_add", zip_get_error(archive)); + goto exit_with_lock; + } + + ret = zip_set_file_compression(archive, index, ZIP_CM_DEFLATE, 9 /* comp_flags */); + if (ret == -1) + { + LOG_ERROR_ZIP("zip_set_file_compression", zip_get_error(archive)); + goto exit_with_lock; + } + + /* Conservation des objets */ + + for (i = 0; i < storage->count; i++) + { + zip_data = zip_source_file_create(storage->backends[i].filename, 0, -1 /* ZIP_LENGTH_TO_END */, &error); + if (zip_data == NULL) + { + LOG_ERROR_ZIP("zip_source_file_create", &error); + goto exit_with_lock; + } + + /** + * Pas besoin de distinguer les chemins UNIX et Windows ici. + * + * Cf. https://pkware.cachefly.net/webdocs/casestudies/APPNOTE.TXT : + * + * 4.4.17 file name: (Variable) + * + * The path stored MUST NOT contain a drive or + * device letter, or a leading slash. All slashes + * MUST be forward slashes '/' as opposed to + * backwards slashes '\' for compatibility with Amiga + * and UNIX file systems etc. If input came from standard + * input, there is no file name field. + * + */ + + asprintf(&zip_name, "backends/%s", storage->backends[i].name); + + index = zip_file_add(archive, zip_name, zip_data, ZIP_FL_ENC_UTF_8); + + free(zip_name); + + if (index == -1) + { + zip_source_free(zip_data); + LOG_ERROR_ZIP("zip_file_add", zip_get_error(archive)); + goto exit_with_lock; + } + + ret = zip_set_file_compression(archive, index, ZIP_CM_DEFLATE, 9 /* comp_flags */); + if (ret == -1) + { + LOG_ERROR_ZIP("zip_set_file_compression", zip_get_error(archive)); + goto exit_with_lock; + } + + } + + result = true; + + /* Clôture des opérations */ + + exit_with_lock: + + g_mutex_unlock(&storage->mutex); + + exit_with_control: + + ret = zip_close(archive); + + if (ret != 0) + LOG_ERROR_ZIP("zip_close", zip_get_error(archive)); + + free(control); + + if (tpmem_filename != NULL) + unlink(tpmem_filename); + + zip_error_fini(&error); + + exit: + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : storage = gestionnaire de conservations à consulter. * +* name = désignation d'un nouveau groupe d'objets. * +* * +* Description : Assure l'inexistence d'un groupe avec un nom donné. * +* * +* Retour : Bilan des recherches. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static bool g_object_storage_has_no_backend_named(GObjectStorage *storage, const char *name) +{ + bool result; /* Bilan à retourner */ + size_t i; /* Boucle de parcours */ + + result = true; + + for (i = 0; i < storage->count && result; i++) + { + if (storage->backends[i].name == NULL) + break; + + if (strcmp(storage->backends[i].name, name) == 0) + result = false; + + } + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : storage = gestionnaire de conservations à compléter. * +* name = désignation d'un nouveau groupe d'objets. * +* * +* Description : Retrouve l'encadrement pour un nouveau groupe d'objets. * +* * +* Retour : Informations liées à un groupe ou NULL en cas d'échec. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static storage_backend_t *g_object_storage_find_backend(GObjectStorage *storage, const char *name) +{ + storage_backend_t *result; /* Encadrement à retourner */ + size_t i; /* Boucle de parcours */ + + assert(!g_mutex_trylock(&storage->mutex)); + + for (i = 0; i < storage->count; i++) + if (strcmp(storage->backends[i].name, name) == 0) + break; + + if (i == storage->count) + result = NULL; + else + result = &storage->backends[i]; + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : storage = gestionnaire de conservations à compléter. * +* name = désignation d'un nouveau groupe d'objets. * +* backend = support mis en place pour les enregistrements. * +* * +* Description : Ajoute le support d'un nouveau groupe d'objets construits. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static bool g_object_storage_add_backend(GObjectStorage *storage, const char *name, storage_backend_t **backend) +{ + bool result; /* Bilan à retourner */ + char *prefix; /* Début de nom de fichier */ + char *filename; /* Chemin d'accès aux données */ + int fd; /* Descripteur de flux ouvert */ + + result = false; + + *backend = NULL; + + assert(!g_mutex_trylock(&storage->mutex)); + + if (g_object_storage_find_backend(storage, name) != NULL) + goto exit; + + /* Préparatifs */ + + asprintf(&prefix, "%s-%s", storage->uid.static_data, name); + + fd = make_tmp_file(prefix, "cache", &filename); + + free(prefix); + + if (fd == -1) + goto exit; + + /* Inscription en bonne et due forme */ + + storage->backends = realloc(storage->backends, ++storage->count * sizeof(storage_backend_t)); + + *backend = &storage->backends[storage->count - 1]; + + (*backend)->name = strdup(name); + + (*backend)->filename = filename; + (*backend)->fd = fd; + + result = true; + + exit: + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : storage = gestionnaire à manipuler. * +* name = désignation d'un groupe d'objets à consulter. * +* pos = tête de lecture avant écriture. * +* * +* Description : Charge un objet à partir de données rassemblées. * +* * +* Retour : Objet restauré en mémoire ou NULL en cas d'échec. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static GSerializableObject *g_object_storage_load_object_unlocked(GObjectStorage *storage, const char *name, off64_t pos) +{ + GSerializableObject *result; /* Instance à retourner */ + storage_backend_t *backend; /* Informations à consulter */ + off64_t new; /* Nouvelle position de lecture*/ + bool status; /* Bilan d'une opération */ + + result = NULL; + + assert(!g_mutex_trylock(&storage->mutex)); + + /* Chargement */ + + backend = g_object_storage_find_backend(storage, name); + if (backend == NULL) goto exit; + + new = lseek64(backend->fd, pos, SEEK_SET); + if (new == (off_t)-1) + { + LOG_ERROR_N("lseek64"); + goto exit; + } + + assert (new == pos); + + /* Phase de conversion */ + + result = G_SERIALIZABLE_OBJECT(g_type_memory_create_object_from_gtype(storage->tpmem, backend->fd)); + + if (result) + { + status = g_serializable_object_load(result, storage, backend->fd); + + if (!status) + g_clear_object(&result); + + } + + exit: + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : storage = gestionnaire à manipuler. * +* name = désignation d'un groupe d'objets à consulter. * +* pos = tête de lecture avant écriture. * +* * +* Description : Charge un objet à partir de données rassemblées. * +* * +* Retour : Objet restauré en mémoire ou NULL en cas d'échec. * +* * +* Remarques : - * +* * +******************************************************************************/ + +GSerializableObject *g_object_storage_load_object(GObjectStorage *storage, const char *name, off64_t pos) +{ + GSerializableObject *result; /* Instance à retourner */ + + g_mutex_lock(&storage->mutex); + + result = g_object_storage_load_object_unlocked(storage, name, pos); + + g_mutex_unlock(&storage->mutex); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : storage = gestionnaire à manipuler. * +* fd = flux de données de l'objet courant. * +* name = désignation du groupe de l'objets à extraire. * +* * +* Description : Charge un objet interne à partir d'une référence embarquée. * +* * +* Retour : Objet restauré en mémoire ou NULL en cas d'échec. * +* * +* Remarques : - * +* * +******************************************************************************/ + +GSerializableObject *g_object_storage_unpack_object(GObjectStorage *storage, int fd, const char *name) +{ + GSerializableObject *result; /* Instance à retourner */ + storage_backend_t *backend; /* Informations à consulter */ + uleb128_t pos; /* Localisation des données */ + bool status; /* Bilan d'une opération */ + off64_t saved; /* Sauvegarde de position */ + off64_t new; /* Nouvelle position de lecture*/ + + result = NULL; + + g_mutex_lock(&storage->mutex); + + /* Récupération de la position */ + + backend = g_object_storage_find_backend(storage, name); + if (backend == NULL) goto exit; + + status = load_uleb128(&pos, backend->fd); + if (!status) goto exit; + + saved = lseek64(backend->fd, 0, SEEK_CUR); + if (saved == (off_t)-1) + { + LOG_ERROR_N("lseek64"); + goto exit; + } + + /* Chargement */ + + result = g_object_storage_load_object_unlocked(storage, name, pos); + + if (result == NULL) goto exit; + + /* Restauration de la position courante */ + + new = lseek64(backend->fd, saved, SEEK_SET); + if (new == (off_t)-1) + { + LOG_ERROR_N("lseek64"); + + g_clear_object(&result); + goto exit; + + } + + assert (new == saved); + + exit: + + g_mutex_unlock(&storage->mutex); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : storage = gestionnaire à manipuler. * +* name = désignation d'un groupe d'objets, nouveau ou non. * +* object = objet sérialisable à traiter. * +* pos = tête de lecture avant écriture. [OUT] * +* * +* Description : Sauvegarde un object sous forme de données rassemblées. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool g_object_storage_store_object(GObjectStorage *storage, const char *name, const GSerializableObject *object, off64_t *pos) +{ + bool result; /* Bilan à retourner */ + storage_backend_t *backend; /* Informations à consulter */ + off64_t tmp; /* Conservation éphémère */ + + result = false; + + g_mutex_lock(&storage->mutex); + + backend = g_object_storage_find_backend(storage, name); + + if (backend == NULL) + g_object_storage_add_backend(storage, name, &backend); + + if (backend != NULL) + { + if (pos == NULL) + pos = &tmp; + + *pos = lseek64(backend->fd, 0, SEEK_CUR); + + if (*pos != (off64_t)-1) + { + result = g_type_memory_store_object_gtype(storage->tpmem, G_OBJECT(object), backend->fd); + + if (result) + result = g_serializable_object_store(object, storage, backend->fd); + + } + + } + + g_mutex_unlock(&storage->mutex); + + return result; + +} |