/* 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; }