/* 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 .
*/
#include "storage.h"
#include
#include
#include
#include
#include
#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;
}