diff options
Diffstat (limited to 'src/glibext/storage.c')
-rw-r--r-- | src/glibext/storage.c | 994 |
1 files changed, 637 insertions, 357 deletions
diff --git a/src/glibext/storage.c b/src/glibext/storage.c index 610a0f6..0e6620b 100644 --- a/src/glibext/storage.c +++ b/src/glibext/storage.c @@ -2,7 +2,7 @@ /* Chrysalide - Outil d'analyse de fichiers binaires * storage.c - conservation hors mémoire d'objets choisis * - * Copyright (C) 2020 Cyrille Bagard + * Copyright (C) 2020-2025 Cyrille Bagard * * This file is part of Chrysalide. * @@ -28,20 +28,23 @@ #include <malloc.h> #include <string.h> #include <unistd.h> -#include <stdarg.h> +#include <zip.h> #include "storage-int.h" -#include "../db/misc/rlestr.h" -#include "../../common/io.h" -#include "../../common/leb128.h" -#include "../../common/pathname.h" -#include "../../core/logs.h" +#include "../common/cpp.h" +#include "../common/pathname.h" +#include "../core/logs.h" - -#define STORAGE_MAGIC "CSTR" -#define STORAGE_NUMBER "\x00\x01" +/** + * 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. */ @@ -51,10 +54,13 @@ static void g_object_storage_class_init(GObjectStorageClass *); static void g_object_storage_init(GObjectStorage *); /* Supprime toutes les références externes. */ -static void g_object_storage_dispose(GObjectStorage *); +static void g_object_storage_dispose(GObject *); /* Procède à la libération totale de la mémoire. */ -static void g_object_storage_finalize(GObjectStorage *); +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 *); @@ -62,11 +68,8 @@ static storage_backend_t *g_object_storage_find_backend(GObjectStorage *, const /* Ajoute le support d'un nouveau groupe d'objets construits. */ static bool g_object_storage_add_backend(GObjectStorage *, const char *, storage_backend_t **); -/* Extrait d'un tampon des enregistrements spécifiques. */ -static bool g_object_storage_load_backend(GObjectStorage *, packed_buffer_t *); - -/* Place dans un tampon les données liées à des enregistrements. */ -static bool pack_storage_backend(const storage_backend_t *, packed_buffer_t *); +/* Charge un objet à partir de données rassemblées. */ +static GSerializableObject *g_object_storage_load_object_unlocked(GObjectStorage *, const char *, off64_t); @@ -92,8 +95,8 @@ static void g_object_storage_class_init(GObjectStorageClass *klass) object = G_OBJECT_CLASS(klass); - object->dispose = (GObjectFinalizeFunc/* ! */)g_object_storage_dispose; - object->finalize = (GObjectFinalizeFunc)g_object_storage_finalize; + object->dispose = g_object_storage_dispose; + object->finalize = g_object_storage_finalize; } @@ -112,9 +115,12 @@ static void g_object_storage_class_init(GObjectStorageClass *klass) static void g_object_storage_init(GObjectStorage *storage) { - storage->tpmem = g_type_memory_new(); + init_sized_binary(&storage->type); + storage->version = 0; - storage->hash = NULL; + init_sized_binary(&storage->uid); + + storage->tpmem = g_type_memory_new(); storage->backends = NULL; storage->count = 0; @@ -125,7 +131,7 @@ static void g_object_storage_init(GObjectStorage *storage) /****************************************************************************** * * -* Paramètres : storage = instance d'objet GLib à traiter. * +* Paramètres : object = instance d'objet GLib à traiter. * * * * Description : Supprime toutes les références externes. * * * @@ -135,18 +141,22 @@ static void g_object_storage_init(GObjectStorage *storage) * * ******************************************************************************/ -static void g_object_storage_dispose(GObjectStorage *storage) +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(G_OBJECT(storage)); + G_OBJECT_CLASS(g_object_storage_parent_class)->dispose(object); } /****************************************************************************** * * -* Paramètres : storage = instance d'objet GLib à traiter. * +* Paramètres : object = instance d'objet GLib à traiter. * * * * Description : Procède à la libération totale de la mémoire. * * * @@ -156,18 +166,27 @@ static void g_object_storage_dispose(GObjectStorage *storage) * * ******************************************************************************/ -static void g_object_storage_finalize(GObjectStorage *storage) +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 @@ -193,17 +212,20 @@ static void g_object_storage_finalize(GObjectStorage *storage) g_mutex_clear(&storage->mutex); - if (storage->hash != NULL) - free(storage->hash); + exit_sized_binary(&storage->type); + + exit_sized_binary(&storage->uid); - G_OBJECT_CLASS(g_object_storage_parent_class)->finalize(G_OBJECT(storage)); + G_OBJECT_CLASS(g_object_storage_parent_class)->finalize(object); } /****************************************************************************** * * -* Paramètres : loaded = contenu binaire à associer. * +* 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. * * * @@ -213,13 +235,14 @@ static void g_object_storage_finalize(GObjectStorage *storage) * * ******************************************************************************/ -GObjectStorage *g_object_storage_new(const char *hash) +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); - result->hash = strdup(hash); + if (!g_object_storage_create(result, type, version, uid)) + g_clear_object(&result); return result; @@ -228,7 +251,39 @@ GObjectStorage *g_object_storage_new(const char *hash) /****************************************************************************** * * -* Paramètres : pbuf = zone tampon à lire. * +* 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. * * * @@ -238,140 +293,297 @@ GObjectStorage *g_object_storage_new(const char *hash) * * ******************************************************************************/ -GObjectStorage *g_object_storage_load(packed_buffer_t *pbuf) +GObjectStorage *g_object_storage_load(const char *filename) { GObjectStorage *result; /* Structure à retourner */ - char header[6]; /* Entête attendue des données */ + 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 */ - rle_string str; /* Chaîne à conserver */ - uleb128_t count; /* Nombre de groupes à charger */ - uleb128_t i; /* Boucle de parcours */ + 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; - status = extract_packed_buffer(pbuf, header, 6, false); - if (!status) goto quick_exit; + storage = g_object_new(G_TYPE_OBJECT_STORAGE, NULL); - if (strncmp(header, STORAGE_MAGIC STORAGE_NUMBER, 6) != 0) - goto quick_exit; + 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); - setup_empty_rle_string(&str); + if (entries_count < 2) + goto exit_with_archive; - status = unpack_rle_string(&str, pbuf); - if (!status) goto quick_exit; + data = NULL; - if (get_rle_string(&str) == NULL) + /* Extraction de la partie de contrôle */ + + ret = zip_stat_index(archive, 0, ZIP_FL_UNCHANGED, &stats); + if (ret != 0) { - exit_rle_string(&str); - goto quick_exit; + LOG_ERROR_ZIP("zip_stat_index", zip_get_error(archive)); + goto exit_with_archive; } - result = g_object_new(G_TYPE_OBJECT_STORAGE, NULL); - - result->hash = strdup(get_rle_string(&str)); + if ((stats.valid & (ZIP_STAT_NAME | ZIP_STAT_SIZE)) != (ZIP_STAT_NAME | ZIP_STAT_SIZE)) + goto exit_with_archive; - exit_rle_string(&str); + if (strcmp(stats.name, "control") != 0) + goto exit_with_archive; - status = g_type_memory_load_types(result->tpmem, pbuf); - if (!status) goto exit_while_loading; + if (stats.size < (6 + 2 + 1 + 1 + 1)) + goto exit_with_archive; - status = unpack_uleb128(&count, pbuf); + 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; + } - for (i = 0; i < count && status; i++) - status = g_object_storage_load_backend(result, pbuf); + data = malloc(stats.size); - exit_while_loading: + got = zip_fread(file, data, stats.size); - if (!status) + ret = zip_fclose(file); + if (ret != 0) { - g_object_unref(G_OBJECT(result)); - result = NULL; + zip_error_set(&error, ret, 0); + LOG_ERROR_ZIP("zip_fclose", &error); + goto exit_with_data; } - quick_exit: + if (got != stats.size) + goto exit_with_data; - return result; + 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; -/****************************************************************************** -* * -* Paramètres : storage = gestionnaire de conservations à manipuler. * -* pbuf = zone tampon à remplir. [OUT] * -* * -* Description : Sauvegarde le support d'une conservation d'objets en place. * -* * -* Retour : Bilan de l'opération. * -* * -* Remarques : - * -* * -******************************************************************************/ + status = unpack_sized_binary(&storage->type, &pos, max); + if (!status) goto exit_with_data; -bool g_object_storage_store(GObjectStorage *storage, packed_buffer_t *pbuf) -{ - bool result; /* Bilan à retourner */ - rle_string str; /* Chaîne à conserver */ - size_t i; /* Boucle de parcours */ + if (pos >= max) + goto exit_with_data; - result = extend_packed_buffer(pbuf, STORAGE_MAGIC STORAGE_NUMBER, 6, false); + storage->version = *(uint8_t *)pos; + pos = (uint8_t *)pos + 1; - if (result) + 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) { - init_static_rle_string(&str, storage->hash); + LOG_ERROR_ZIP("zip_stat_index", zip_get_error(archive)); + goto exit_with_archive; + } - result = pack_rle_string(&str, pbuf); + if ((stats.valid & (ZIP_STAT_NAME | ZIP_STAT_SIZE)) != (ZIP_STAT_NAME | ZIP_STAT_SIZE)) + goto exit_with_archive; - exit_rle_string(&str); + 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; } - g_mutex_lock(&storage->mutex); + data = malloc(stats.size); - if (result) - result = g_type_memory_store_types(storage->tpmem, pbuf); + got = zip_fread(file, data, stats.size); - if (result) - result = pack_uleb128((uleb128_t []){ storage->count }, pbuf); + ret = zip_fclose(file); + if (ret != 0) + { + zip_error_set(&error, ret, 0); + LOG_ERROR_ZIP("zip_fclose", &error); + goto exit_with_data; + } - for (i = 0; i < storage->count && result; i++) - result = pack_storage_backend(&storage->backends[i], pbuf); + asprintf(&prefix, "%s-types", storage->uid.static_data); - g_mutex_unlock(&storage->mutex); + fd = make_tmp_file(prefix, "cache", &tpmem_filename); - return result; + free(prefix); -} + if (fd == -1) + goto exit_with_data; + status = safe_write(fd, data, stats.size); + if (!status) + { + close(fd); + goto exit_with_data; + } -/****************************************************************************** -* * -* 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 : - * -* * -******************************************************************************/ + moved = lseek(fd, 0, SEEK_SET); + if (moved == ((off_t)-1)) + { + LOG_ERROR_N("lseek"); + close(fd); + goto exit_with_data; + } -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 */ + status = g_type_memory_load(storage->tpmem, fd); - assert(!g_mutex_trylock(&storage->mutex)); + close(fd); - for (i = 0; i < storage->count; i++) - if (strcmp(storage->backends[i].name, name) == 0) - break; + if (!status) + goto exit_with_data; - if (i == storage->count) - result = NULL; - else - result = &storage->backends[i]; + 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; @@ -380,11 +592,10 @@ static storage_backend_t *g_object_storage_find_backend(GObjectStorage *storage, /****************************************************************************** * * -* 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. * +* Paramètres : storage = gestionnaire de conservations à manipuler. * +* filename = fichier de destination à constituer. * * * -* Description : Ajoute le support d'un nouveau groupe d'objets construits. * +* Description : Sauvegarde le support d'une conservation d'objets en place. * * * * Retour : Bilan de l'opération. * * * @@ -392,46 +603,199 @@ static storage_backend_t *g_object_storage_find_backend(GObjectStorage *storage, * * ******************************************************************************/ -static bool g_object_storage_add_backend(GObjectStorage *storage, const char *name, storage_backend_t **backend) +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 */ - char *filename; /* Chemin d'accès aux données */ 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; - *backend = NULL; + 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; + } - assert(!g_mutex_trylock(&storage->mutex)); + zip_error_init(&error); - if (g_object_storage_find_backend(storage, name) != NULL) - goto exit; + tpmem_filename = NULL; - /* Préparatifs */ + /* Fichier de contrôle */ - asprintf(&prefix, "%s-%s", storage->hash, name); + type_buf = pack_sized_binary(&storage->type, &type_buflen); - fd = make_tmp_file(prefix, "cache", &filename); + 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; + goto exit_with_lock; - /* Inscription en bonne et due forme */ + status = g_type_memory_store(storage->tpmem, fd); - storage->backends = realloc(storage->backends, ++storage->count * sizeof(storage_backend_t)); + close(fd); - *backend = &storage->backends[storage->count - 1]; + if (!status) + goto exit_with_lock; - (*backend)->name = strdup(name); + 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; + } - (*backend)->filename = filename; - (*backend)->fd = fd; + 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; @@ -441,71 +805,67 @@ static bool g_object_storage_add_backend(GObjectStorage *storage, const char *na /****************************************************************************** * * -* Paramètres : storage = gestionnaire de conservations à compléter. * -* pbuf = zone tampon à lire. * +* Paramètres : storage = gestionnaire de conservations à consulter. * +* name = désignation d'un nouveau groupe d'objets. * * * -* Description : Extrait d'un tampon des enregistrements spécifiques. * +* Description : Assure l'inexistence d'un groupe avec un nom donné. * * * -* Retour : Bilan de l'opération. * +* Retour : Bilan des recherches. * * * * Remarques : - * * * ******************************************************************************/ -static bool g_object_storage_load_backend(GObjectStorage *storage, packed_buffer_t *pbuf) +static bool g_object_storage_has_no_backend_named(GObjectStorage *storage, const char *name) { bool result; /* Bilan à retourner */ - rle_string str; /* Chaîne à conserver */ - bool status; /* Bilan de lecture de contenu */ - storage_backend_t *backend; /* Informations à intégrer */ - uleb128_t length; /* Taille des données à charger*/ - off_t moved; /* Nouvelle position établie */ - - result = false; - - g_mutex_lock(&storage->mutex); - - /* Récupération du nom et création du support */ - - setup_empty_rle_string(&str); + size_t i; /* Boucle de parcours */ - status = unpack_rle_string(&str, pbuf); - if (!status) goto exit; + result = true; - if (get_rle_string(&str) == NULL) + for (i = 0; i < storage->count && result; i++) { - exit_rle_string(&str); - goto exit; - } - - status = g_object_storage_add_backend(storage, get_rle_string(&str), &backend); + if (storage->backends[i].name == NULL) + break; - exit_rle_string(&str); + if (strcmp(storage->backends[i].name, name) == 0) + result = false; - if (!status) goto exit; + } - /* Récupération du contenu */ + return result; - status = unpack_uleb128(&length, pbuf); - if (!status) goto exit; +} - status = safe_write(backend->fd, pbuf->data + pbuf->pos, length); - if (!status) goto exit; - advance_packed_buffer(pbuf, length); +/****************************************************************************** +* * +* 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 : - * +* * +******************************************************************************/ - moved = lseek(backend->fd, 0, SEEK_SET); - if (moved == ((off_t)-1)) - { - LOG_ERROR_N("lseek"); - goto exit; - } +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 */ - result = true; + assert(!g_mutex_trylock(&storage->mutex)); - exit: + for (i = 0; i < storage->count; i++) + if (strcmp(storage->backends[i].name, name) == 0) + break; - g_mutex_unlock(&storage->mutex); + if (i == storage->count) + result = NULL; + else + result = &storage->backends[i]; return result; @@ -514,10 +874,11 @@ static bool g_object_storage_load_backend(GObjectStorage *storage, packed_buffer /****************************************************************************** * * -* Paramètres : backend = stockage des enregistrements spécifiques. * -* pbuf = zone tampon à remplir. [OUT] * +* 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 : Place dans un tampon les données liées à des enregistrements.* +* Description : Ajoute le support d'un nouveau groupe d'objets construits. * * * * Retour : Bilan de l'opération. * * * @@ -525,72 +886,45 @@ static bool g_object_storage_load_backend(GObjectStorage *storage, packed_buffer * * ******************************************************************************/ -static bool pack_storage_backend(const storage_backend_t *backend, packed_buffer_t *pbuf) +static bool g_object_storage_add_backend(GObjectStorage *storage, const char *name, storage_backend_t **backend) { bool result; /* Bilan à retourner */ - rle_string str; /* Chaîne à conserver */ - bool status; /* Bilan de lecture de contenu */ - off_t current; /* Position courante */ - off_t moved; /* Nouvelle position établie */ - void *data; /* Données à transférer */ + 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; - /* Inscription du nom */ + *backend = NULL; - init_static_rle_string(&str, backend->name); + assert(!g_mutex_trylock(&storage->mutex)); - status = pack_rle_string(&str, pbuf); + if (g_object_storage_find_backend(storage, name) != NULL) + goto exit; - exit_rle_string(&str); + /* Préparatifs */ - if (!status) goto exit; + asprintf(&prefix, "%s-%s", storage->uid.static_data, name); - /* Inscription du contenu */ + fd = make_tmp_file(prefix, "cache", &filename); - current = lseek(backend->fd, 0, SEEK_CUR); - if (current == ((off_t)-1)) - { - LOG_ERROR_N("lseek"); - goto exit; - } + free(prefix); - moved = lseek(backend->fd, 0, SEEK_SET); - if (moved == ((off_t)-1)) - { - LOG_ERROR_N("lseek"); + if (fd == -1) goto exit; - } - - data = malloc(current); - if (data == NULL) - { - LOG_ERROR_N("malloc"); - goto restore; - } - status = safe_read(backend->fd, data, current); - if (!status) goto free_mem; - - status = pack_uleb128((uleb128_t []){ current }, pbuf); - if (!status) goto free_mem; - - status = extend_packed_buffer(pbuf, data, current, false); + /* Inscription en bonne et due forme */ - free_mem: + storage->backends = realloc(storage->backends, ++storage->count * sizeof(storage_backend_t)); - free(data); + *backend = &storage->backends[storage->count - 1]; - restore: + (*backend)->name = strdup(name); - moved = lseek(backend->fd, current, SEEK_SET); - if (moved == ((off_t)-1)) - { - LOG_ERROR_N("lseek"); - goto exit; - } + (*backend)->filename = filename; + (*backend)->fd = fd; - result = status; + result = true; exit: @@ -602,7 +936,7 @@ static bool pack_storage_backend(const storage_backend_t *backend, packed_buffer /****************************************************************************** * * * Paramètres : storage = gestionnaire à manipuler. * -* name = désignation d'un nouveau groupe d'objets. * +* 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. * @@ -613,56 +947,44 @@ static bool pack_storage_backend(const storage_backend_t *backend, packed_buffer * * ******************************************************************************/ -GSerializableObject *g_object_storage_load_object(GObjectStorage *storage, const char *name, off64_t pos) +static GSerializableObject *g_object_storage_load_object_unlocked(GObjectStorage *storage, const char *name, off64_t pos) { GSerializableObject *result; /* Instance à retourner */ - bool status; /* Bilan d'une opération */ storage_backend_t *backend; /* Informations à consulter */ - packed_buffer_t pbuf; /* Tampon des données à lire */ off64_t new; /* Nouvelle position de lecture*/ + bool status; /* Bilan d'une opération */ result = NULL; - /* Chargement */ - - status = false; + assert(!g_mutex_trylock(&storage->mutex)); - g_mutex_lock(&storage->mutex); + /* Chargement */ backend = g_object_storage_find_backend(storage, name); + if (backend == NULL) goto exit; - if (backend != NULL) + new = lseek64(backend->fd, pos, SEEK_SET); + if (new == (off_t)-1) { - new = lseek64(backend->fd, pos, SEEK_SET); - - if (new == pos) - { - init_packed_buffer(&pbuf); - status = read_packed_buffer(&pbuf, backend->fd); - } - + LOG_ERROR_N("lseek64"); + goto exit; } - g_mutex_unlock(&storage->mutex); - - if (!status) - goto exit; + assert (new == pos); /* Phase de conversion */ - result = G_SERIALIZABLE_OBJECT(g_type_memory_create_object(storage->tpmem, &pbuf)); + result = G_SERIALIZABLE_OBJECT(g_type_memory_create_object_from_gtype(storage->tpmem, backend->fd)); if (result) { - status = g_serializable_object_load(result, storage, &pbuf); + status = g_serializable_object_load(result, storage, backend->fd); if (!status) g_clear_object(&result); } - exit_packed_buffer(&pbuf); - exit: return result; @@ -673,10 +995,10 @@ GSerializableObject *g_object_storage_load_object(GObjectStorage *storage, const /****************************************************************************** * * * Paramètres : storage = gestionnaire à manipuler. * -* name = désignation d'un nouveau groupe d'objets. * -* pbuf = zone tampon à parcourir. * +* name = désignation d'un groupe d'objets à consulter. * +* pos = tête de lecture avant écriture. * * * -* Description : Charge un objet interne à partir de données rassemblées. * +* Description : Charge un objet à partir de données rassemblées. * * * * Retour : Objet restauré en mémoire ou NULL en cas d'échec. * * * @@ -684,18 +1006,15 @@ GSerializableObject *g_object_storage_load_object(GObjectStorage *storage, const * * ******************************************************************************/ -GSerializableObject *g_object_storage_unpack_object(GObjectStorage *storage, const char *name, packed_buffer_t *pbuf) +GSerializableObject *g_object_storage_load_object(GObjectStorage *storage, const char *name, off64_t pos) { GSerializableObject *result; /* Instance à retourner */ - uint64_t pos; /* Localisation des données */ - bool status; /* Bilan d'une opération */ - result = NULL; + g_mutex_lock(&storage->mutex); - status = extract_packed_buffer(pbuf, &pos, sizeof(uint64_t), true); + result = g_object_storage_load_object_unlocked(storage, name, pos); - if (status) - result = g_object_storage_load_object(storage, name, pos); + g_mutex_unlock(&storage->mutex); return result; @@ -704,60 +1023,70 @@ GSerializableObject *g_object_storage_unpack_object(GObjectStorage *storage, con /****************************************************************************** * * -* Paramètres : storage = gestionnaire à manipuler. * -* name = désignation d'un nouveau groupe d'objets. * -* pbuf = zone tampon à parcourir. * -* expected = type d'objet attendu. * -* ... = élément restauré ou NULL en cas d'échec. [OUT] * +* Paramètres : storage = gestionnaire à manipuler. * +* name = désignation d'un groupe d'objets à consulter. * +* target_name = désignation d'un second groupe d'objets ciblé. * * * -* Description : Charge un objet interne à partir de données rassemblées. * +* Description : Charge un objet interne à partir d'une référence embarquée. * * * -* Retour : Bilan de l'opération. * +* Retour : Objet restauré en mémoire ou NULL en cas d'échec. * * * * Remarques : - * * * ******************************************************************************/ -bool g_object_storage_unpack_object_2(GObjectStorage *storage, const char *name, packed_buffer_t *pbuf, GType expected, ...) +GSerializableObject *g_object_storage_unpack_object(GObjectStorage *storage, const char *name, const char *target_name) { - bool result; /* Bilan d'une opération */ - uint64_t pos; /* Localisation des données */ - GSerializableObject *instance; /* Objet rechargé à valider */ - va_list ap; /* Liste d'arguments variables */ - void **object; /* Lieu d'enregistrement final */ + 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 = extract_packed_buffer(pbuf, &pos, sizeof(uint64_t), true); + result = NULL; - if (result) - { - if (pos == 0) - *object = NULL; + g_mutex_lock(&storage->mutex); - else - { - instance = g_object_storage_load_object(storage, name, pos); + /* Récupération de la position */ - result = G_TYPE_CHECK_INSTANCE_TYPE(instance, expected); + backend = g_object_storage_find_backend(storage, name); + if (backend == NULL) goto exit; - if (result) - { - va_start(ap, expected); + status = load_uleb128(&pos, backend->fd); + if (!status) goto exit; - object = va_arg(ap, void **); + saved = lseek64(backend->fd, 0, SEEK_CUR); + if (saved == (off_t)-1) + { + LOG_ERROR_N("lseek64"); + goto exit; + } - *object = instance; + /* Chargement */ - va_end(ap); + result = g_object_storage_load_object_unlocked(storage, target_name, pos); - } + if (result == NULL) goto exit; - else - g_clear_object(&instance); + /* 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; } @@ -766,7 +1095,7 @@ bool g_object_storage_unpack_object_2(GObjectStorage *storage, const char *name, /****************************************************************************** * * * Paramètres : storage = gestionnaire à manipuler. * -* name = désignation d'un nouveau groupe d'objets. * +* name = désignation d'un groupe d'objets, nouveau ou non. * * object = objet sérialisable à traiter. * * pos = tête de lecture avant écriture. [OUT] * * * @@ -781,22 +1110,9 @@ bool g_object_storage_unpack_object_2(GObjectStorage *storage, const char *name, bool g_object_storage_store_object(GObjectStorage *storage, const char *name, const GSerializableObject *object, off64_t *pos) { bool result; /* Bilan à retourner */ - packed_buffer_t pbuf; /* Tampon des données à écrire */ storage_backend_t *backend; /* Informations à consulter */ off64_t tmp; /* Conservation éphémère */ - /* Phase de conversion */ - - init_packed_buffer(&pbuf); - - result = g_type_memory_store_object_gtype(storage->tpmem, G_OBJECT(object), &pbuf); - if (!result) goto exit; - - result = g_serializable_object_store(object, storage, &pbuf); - if (!result) goto exit; - - /* Enregistrement */ - result = false; g_mutex_lock(&storage->mutex); @@ -814,54 +1130,18 @@ bool g_object_storage_store_object(GObjectStorage *storage, const char *name, co *pos = lseek64(backend->fd, 0, SEEK_CUR); if (*pos != (off64_t)-1) - result = write_packed_buffer(&pbuf, backend->fd); - - } - - g_mutex_unlock(&storage->mutex); - - /* Sortie propre */ - - exit: - - exit_packed_buffer(&pbuf); - - return result; - -} - - -/****************************************************************************** -* * -* Paramètres : storage = gestionnaire à manipuler. * -* name = désignation d'un nouveau groupe d'objets. * -* pbuf = zone tampon à remplir. * -* * -* Description : Sauvegarde un object interne sous forme de données. * -* * -* Retour : Bilan de l'opération. * -* * -* Remarques : - * -* * -******************************************************************************/ - -bool g_object_storage_pack_object(GObjectStorage *storage, const char *name, const GSerializableObject *object, packed_buffer_t *pbuf) -{ - bool result; /* Bilan à retourner */ - off64_t pos; /* Localisation des données */ - - if (object == NULL) - result = extend_packed_buffer(pbuf, (uint64_t []){ 0 }, sizeof(uint64_t), true); + { + result = g_type_memory_store_object_gtype(storage->tpmem, G_OBJECT(object), backend->fd); - else - { - result = g_object_storage_store_object(storage, name, object, &pos); + if (result) + result = g_serializable_object_store(object, storage, backend->fd); - if (result) - result = extend_packed_buffer(pbuf, (uint64_t []){ pos }, sizeof(uint64_t), true); + } } + g_mutex_unlock(&storage->mutex); + return result; } |