From 96afc0325e1c85bf1a76d63c6441cf8f044708fe Mon Sep 17 00:00:00 2001 From: Cyrille Bagard Date: Thu, 10 Oct 2019 23:45:56 +0200 Subject: Introduced first steps towards database snapshots. --- src/analysis/db/Makefile.am | 3 +- src/analysis/db/cdb.c | 391 +++++++++--------- src/analysis/db/collection.c | 3 +- src/analysis/db/protocol.h | 2 + src/analysis/db/server.c | 2 +- src/analysis/db/snapshot.c | 965 +++++++++++++++++++++++++++++++++++++++++++ src/analysis/db/snapshot.h | 78 ++++ src/core/logs.h | 9 + 8 files changed, 1260 insertions(+), 193 deletions(-) create mode 100644 src/analysis/db/snapshot.c create mode 100644 src/analysis/db/snapshot.h diff --git a/src/analysis/db/Makefile.am b/src/analysis/db/Makefile.am index b3616bd..7dce005 100644 --- a/src/analysis/db/Makefile.am +++ b/src/analysis/db/Makefile.am @@ -12,7 +12,8 @@ libanalysisdb_la_SOURCES = \ item-int.h \ item.h item.c \ protocol.h \ - server.h server.c + server.h server.c \ + snapshot.h snapshot.c libanalysisdb_la_LIBADD = \ items/libanalysisdbitems.la \ diff --git a/src/analysis/db/cdb.c b/src/analysis/db/cdb.c index ca39a58..e03b875 100644 --- a/src/analysis/db/cdb.c +++ b/src/analysis/db/cdb.c @@ -44,6 +44,7 @@ #include "collection.h" #include "protocol.h" +#include "snapshot.h" #include "../../common/compression.h" #include "../../common/cpp.h" #include "../../common/extstr.h" @@ -54,6 +55,9 @@ +/* ------------------------- COEUR DE LA GESTION D'ARCHIVES ------------------------- */ + + /* Informations relatives à un client */ typedef struct _cdb_client { @@ -73,17 +77,14 @@ struct _GCdbArchive rle_string hash; /* Empreinte cryptographique */ char *filename; /* Chemin d'accès à l'archive */ - + char *tmpdir; /* Répertoire de travail */ char *xml_desc; /* Fichier de description */ - char *sql_db; /* Base de données SQLite */ - xmlDocPtr xdoc; /* Document XML à créer */ - xmlXPathContextPtr context; /* Contexte pour les recherches*/ + GList *collections; /* Ensemble de modifications */ + GDbSnapshot *snapshot; /* Instantanés de bases SQL */ sqlite3 *db; /* Base de données à manipuler */ - GList *collections; /* Ensemble de modifications */ - cdb_client *clients; /* Connexions en place */ size_t count; /* Quantité de clients */ GMutex clients_access; /* Verrou pour l'accès */ @@ -116,7 +117,7 @@ static void g_cdb_archive_dispose(GCdbArchive *); static void g_cdb_archive_finalize(GCdbArchive *); /* Ouvre une archive avec tous les éléments à conserver. */ -static bool g_cdb_archive_read(GCdbArchive *); +static DBError g_cdb_archive_read(GCdbArchive *); @@ -124,27 +125,22 @@ static bool g_cdb_archive_read(GCdbArchive *); /* Crée la description XML correspondant à l'archive. */ -static bool g_cdb_archive_create_xml_desc(GCdbArchive *); +static bool g_cdb_archive_create_xml_desc(const GCdbArchive *, xmlDocPtr *, xmlXPathContextPtr *); /* Vérifie la conformité d'une description XML avec le serveur. */ -static bool g_cdb_archive_check_xml_version(const GCdbArchive *); - - - -/* ------------------------- ACCES A LA BASE DE DONNEES SQL ------------------------- */ +static bool g_cdb_archive_check_xml_version(const GCdbArchive *, xmlXPathContextPtr); -/* Crée la base de données correspondant à l'archive. */ -static bool g_cdb_archive_create_db(const GCdbArchive *); - - -/////////////////////////: +/* -------------------------- ACTUALISATION DE COLLECTIONS -------------------------- */ /* Crée et remplit les collections à partir de leurs bases. */ static bool g_cdb_archive_load_collections(GCdbArchive *); +/* Enregistre les signaux associés au suivi des collections. */ +static void g_cdb_archive_register_signals(GCdbArchive *); + /* Réagit à une modification au sein d'une collection donnée. */ static void on_collection_extended(GDbCollection *, GDbItem *, GCdbArchive *); @@ -156,6 +152,9 @@ static void g_cdb_archive_remove_client(GCdbArchive *, size_t); +/* ---------------------------------------------------------------------------------- */ +/* COEUR DE LA GESTION D'ARCHIVES */ +/* ---------------------------------------------------------------------------------- */ /* Indique le type défini pour une une archive d'éléments utilisateur. */ @@ -200,8 +199,12 @@ static void g_cdb_archive_class_init(GCdbArchiveClass *klass) static void g_cdb_archive_init(GCdbArchive *archive) { + archive->tmpdir = NULL; + archive->collections = create_collections_list(); + archive->snapshot = NULL; + g_mutex_init(&archive->clients_access); g_mutex_init(&archive->id_access); @@ -224,6 +227,8 @@ static void g_cdb_archive_init(GCdbArchive *archive) static void g_cdb_archive_dispose(GCdbArchive *archive) { + g_clear_object(&archive->snapshot); + g_cond_clear(&archive->id_cond); g_mutex_clear(&archive->id_access); @@ -256,29 +261,14 @@ static void g_cdb_archive_finalize(GCdbArchive *archive) assert(ret == SQLITE_OK); } - if (archive->xdoc != NULL) - close_xml_file(archive->xdoc, archive->context); - if (archive->xml_desc != NULL) - { - ret = unlink(archive->xml_desc); - if (ret != 0) LOG_ERROR_N("unlink"); - free(archive->xml_desc); - } - - if (archive->sql_db != NULL) - { - ret = unlink(archive->sql_db); - if (ret != 0) LOG_ERROR_N("unlink"); - - free(archive->sql_db); - - } - free(archive->filename); + if (archive->tmpdir != NULL) + free(archive->tmpdir); + exit_rle_string(&archive->hash); G_OBJECT_CLASS(g_cdb_archive_parent_class)->finalize(G_OBJECT(archive)); @@ -307,11 +297,14 @@ GCdbArchive *g_cdb_archive_new(const char *basedir, const char *tmpdir, const rl GCdbArchive *result; /* Adresse à retourner */ int ret; /* Retour d'un appel */ struct stat finfo; /* Information sur l'archive */ + const char *id; /* Identifiant d'instantané */ result = g_object_new(G_TYPE_CDB_ARCHIVE, NULL); dup_into_rle_string(&result->hash, get_rle_string(hash)); + *error = DBE_SYS_ERROR; + /* Chemin de l'archive */ result->filename = strdup(basedir); @@ -319,21 +312,20 @@ GCdbArchive *g_cdb_archive_new(const char *basedir, const char *tmpdir, const rl result->filename = stradd(result->filename, ".cdb.tar.xz"); if (!mkpath(result->filename)) - goto gcan_error; + goto error; /* Chemin des enregistrements temporaires */ + result->tmpdir = strdup(tmpdir); + if (!mkpath(tmpdir)) - goto gcan_error; + goto error; - ret = asprintf(&result->xml_desc, "%s" G_DIR_SEPARATOR_S "%s_desc.xml", tmpdir, result->hash.data); - if (ret == -1) goto gcan_no_tmp; + ret = asprintf(&result->xml_desc, "%s" G_DIR_SEPARATOR_S "%s_desc.xml", tmpdir, get_rle_string(hash)); + if (ret == -1) goto no_tmp; ret = ensure_path_exists(result->xml_desc); - if (ret == -1) goto gcan_no_tmp; - - ret = asprintf(&result->sql_db, "%s" G_DIR_SEPARATOR_S "%s_db.sql", tmpdir, result->hash.data); - if (ret == -1) goto gcan_no_tmp; + if (ret == -1) goto no_tmp; /* Création de l'archive si elle n'existe pas */ @@ -342,44 +334,74 @@ GCdbArchive *g_cdb_archive_new(const char *basedir, const char *tmpdir, const rl if (ret != 0) { /* Le soucis ne vient pas de l'absence du fichier... */ - if (errno != ENOENT) goto gcan_error; + if (errno != ENOENT) goto error; - g_cdb_archive_create_xml_desc(result); - g_cdb_archive_create_db(result); + result->snapshot = g_db_snapshot_new_empty(tmpdir, get_rle_string(hash), result->collections); - *error = g_cdb_archive_write(result); + if (result->snapshot == NULL) + goto error; - if (*error != DBE_NONE) - goto gcan_error; + /* Récupération de la base courante */ - } - else if (!S_ISREG(finfo.st_mode)) - goto gcan_error; + id = g_db_snapshot_get_current_id(result->snapshot); + assert(id != NULL); + + result->db = g_db_snapshot_get_database(result->snapshot, id); - /* Ouverture de l'archive */ + if (result->db == NULL) + { + *error = DBE_XML_ERROR; + goto error; + } - if (!g_cdb_archive_read(result)) - goto gcan_error; + *error = DBE_NONE; - if (!g_cdb_archive_check_xml_version(result)) - { - *error = DBE_XML_VERSION_ERROR; - goto gcan_error; } - /* Chargement des éléments sauvegardés */ + else if (!S_ISREG(finfo.st_mode)) + goto error; - if (!g_cdb_archive_load_collections(result)) + else { - *error = DBE_DB_LOADING_ERROR; - goto gcan_error; + /* Ouverture de l'archive */ + + *error = g_cdb_archive_read(result); + + if (*error != DBE_NONE) + goto error; + + /* Récupération de la base courante */ + + id = g_db_snapshot_get_current_id(result->snapshot); + assert(id != NULL); + + result->db = g_db_snapshot_get_database(result->snapshot, id); + + if (result->db == NULL) + { + *error = DBE_XML_ERROR; + goto error; + } + + /* Chargement des éléments sauvegardés */ + + if (!g_cdb_archive_load_collections(result)) + { + *error = DBE_DB_LOADING_ERROR; + goto error; + } + } + /* Ultimes connexions */ + + g_cdb_archive_register_signals(result); + return result; - gcan_no_tmp: + no_tmp: - gcan_error: + error: g_object_unref(G_OBJECT(result)); @@ -400,22 +422,25 @@ GCdbArchive *g_cdb_archive_new(const char *basedir, const char *tmpdir, const rl * * ******************************************************************************/ -static bool g_cdb_archive_read(GCdbArchive *archive) +static DBError g_cdb_archive_read(GCdbArchive *archive) { - bool result; /* Conclusion à retourner */ + DBError result; /* Conclusion à retourner */ struct archive *in; /* Archive à consulter */ int ret; /* Bilan d'un appel */ struct archive_entry *entry; /* Elément de l'archive */ const char *path; /* Désignation d'un fichier */ + xmlDocPtr xdoc; /* Document XML à créer */ + xmlXPathContextPtr context; /* Contexte pour les recherches*/ + bool status; /* Bilan d'un chargement */ - result = false; + result = DBE_ARCHIVE_ERROR; in = archive_read_new(); archive_read_support_filter_all(in); archive_read_support_format_all(in); ret = archive_read_open_filename(in, archive->filename, 10240 /* ?! */); - if (ret != ARCHIVE_OK) goto gcar_bad_archive; + if (ret != ARCHIVE_OK) goto bad_archive; for (ret = archive_read_next_header(in, &entry); ret == ARCHIVE_OK; @@ -426,33 +451,53 @@ static bool g_cdb_archive_read(GCdbArchive *archive) if (strcmp(path, "desc.xml") == 0) { if (!dump_archive_entry_into_file(in, entry, archive->xml_desc)) - goto gcar_exit; + goto load_error; - if (!open_xml_file(archive->xml_desc, &archive->xdoc, &archive->context)) - goto gcar_exit; + if (!open_xml_file(archive->xml_desc, &xdoc, &context)) + goto load_error; - } - else if (strcmp(path, "sql.db") == 0) - { - if (!dump_archive_entry_into_file(in, entry, archive->sql_db)) - goto gcar_exit; + if (!g_cdb_archive_check_xml_version(archive, context)) + { + result = DBE_XML_VERSION_ERROR; + goto load_error; + } + + archive->snapshot = g_db_snapshot_new_from_xml(archive->tmpdir, get_rle_string(&archive->hash), + xdoc, context); + + close_xml_file(xdoc, context); + + ret = unlink(archive->xml_desc); + if (ret != 0) LOG_ERROR_N("unlink"); + + if (archive->snapshot == NULL) + goto load_error; + + break; - ret = sqlite3_open(archive->sql_db, &archive->db); - if (ret != SQLITE_OK) - goto gcar_exit; } } - if (ret != ARCHIVE_EOF) - goto gcar_exit; + archive_read_close(in); + archive_read_free(in); + + in = archive_read_new(); + archive_read_support_filter_all(in); + archive_read_support_format_all(in); + + ret = archive_read_open_filename(in, archive->filename, 10240 /* ?! */); + if (ret != ARCHIVE_OK) goto bad_archive; + + status = g_db_snapshot_fill(archive->snapshot, in); + if (!status) goto load_error; - result = true; + result = DBE_NONE; - gcar_exit: + load_error: - gcar_bad_archive: + bad_archive: archive_read_close(in); archive_read_free(in); @@ -479,23 +524,54 @@ DBError g_cdb_archive_write(const GCdbArchive *archive) DBError result; /* Conclusion à retourner */ struct archive *out; /* Archive à constituer */ int ret; /* Bilan d'un appel */ - CPError status; /* Bilan d'une compression */ - - result = DBE_ARCHIVE_ERROR; + xmlDocPtr xdoc; /* Document XML à créer */ + xmlXPathContextPtr context; /* Contexte pour les recherches*/ + bool status; /* Bilan d'un appel */ + CPError error; /* Bilan d'une compression */ out = archive_write_new(); archive_write_add_filter_xz(out); archive_write_set_format_gnutar(out); ret = archive_write_open_filename(out, archive->filename); - if (ret != ARCHIVE_OK) goto gcaw_bad_archive; + if (ret != ARCHIVE_OK) + { + result = DBE_ARCHIVE_ERROR; + goto bad_archive; + } - status = add_file_into_archive(out, archive->xml_desc, "desc.xml"); + status = g_cdb_archive_create_xml_desc(archive, &xdoc, &context); - switch (status) + if (!status) + { + result = DBE_XML_ERROR; + goto bad_archive; + } + + /* Enregistrement des bases */ + + result = g_db_snapshot_save(archive->snapshot, xdoc, context, out); + + if (result != DBE_NONE) + goto bad_archive; + + /* Enregistrement du document XML */ + + status = save_xml_file(xdoc, archive->xml_desc); + + close_xml_file(xdoc, context); + + if (!status) + { + result = DBE_SYS_ERROR; + goto bad_xml; + } + + error = add_file_into_archive(out, archive->xml_desc, "desc.xml"); + + switch (error) { case CPE_NO_ERROR: - result = DBE_NONE; break; case CPE_SYSTEM_ERROR: @@ -508,29 +584,12 @@ DBError g_cdb_archive_write(const GCdbArchive *archive) } - if (result == DBE_NONE) - { - status = add_file_into_archive(out, archive->sql_db, "sql.db"); + bad_xml: - switch (status) - { - case CPE_NO_ERROR: - result = DBE_NONE; - break; + ret = unlink(archive->xml_desc); + if (ret != 0) LOG_ERROR_N("unlink"); - case CPE_SYSTEM_ERROR: - result = DBE_SYS_ERROR; - break; - - case CPE_ARCHIVE_ERROR: - result = DBE_ARCHIVE_ERROR; - break; - - } - - } - - gcaw_bad_archive: + bad_archive: archive_write_close(out); archive_write_free(out); @@ -569,6 +628,8 @@ int g_cdb_archive_compare_hash(const GCdbArchive *archive, const rle_string *has /****************************************************************************** * * * Paramètres : archive = archive à constituer. * +* xdoc = document XML à compléter. [OUT] * +* context = contexte pour les recherches. [OUT] * * * * Description : Crée la description XML correspondant à l'archive. * * * @@ -578,38 +639,20 @@ int g_cdb_archive_compare_hash(const GCdbArchive *archive, const rle_string *has * * ******************************************************************************/ -static bool g_cdb_archive_create_xml_desc(GCdbArchive *archive) +static bool g_cdb_archive_create_xml_desc(const GCdbArchive *archive, xmlDocPtr *xdoc, xmlXPathContextPtr *context) { bool result; /* Bilan à retourner */ - timestamp_t timestamp; /* Date de création */ - char tmp[sizeof(XSTR(UINT64_MAX))]; /* Stockage temporaire */ - - result = create_new_xml_file(&archive->xdoc, &archive->context); - if (result) - result = add_content_to_node(archive->xdoc, archive->context, - "/ChrysalideBinary/Version", PACKAGE_VERSION); - - if (result) - result = add_content_to_node(archive->xdoc, archive->context, - "/ChrysalideBinary/Protocol", XSTR(CDB_PROTOCOL_VERSION)); + result = create_new_xml_file(xdoc, context); if (result) - result = add_content_to_node(archive->xdoc, archive->context, - "/ChrysalideBinary/Hash", archive->hash.data); + result = add_content_to_node(*xdoc, *context, "/ChrysalideBinary/Version", PACKAGE_VERSION); if (result) - { - init_timestamp(×tamp); - snprintf(tmp, sizeof(tmp), "%" PRIu64, timestamp); - - result = add_content_to_node(archive->xdoc, archive->context, - "/ChrysalideBinary/CreationDate", tmp); - - } + result = add_content_to_node(*xdoc, *context, "/ChrysalideBinary/Protocol", XSTR(CDB_PROTOCOL_VERSION)); if (result) - result = save_xml_file(archive->xdoc, archive->xml_desc); + result = add_content_to_node(*xdoc, *context, "/ChrysalideBinary/Hash", archive->hash.data); return result; @@ -619,6 +662,7 @@ static bool g_cdb_archive_create_xml_desc(GCdbArchive *archive) /****************************************************************************** * * * Paramètres : archive = archive à consulter. * +* context = contexte pour les recherches. * * * * Description : Vérifie la conformité d'une description XML avec le serveur. * * * @@ -628,7 +672,7 @@ static bool g_cdb_archive_create_xml_desc(GCdbArchive *archive) * * ******************************************************************************/ -static bool g_cdb_archive_check_xml_version(const GCdbArchive *archive) +static bool g_cdb_archive_check_xml_version(const GCdbArchive *archive, xmlXPathContextPtr context) { bool result; /* Bilan à retourner */ char *version; /* Version protocolaire */ @@ -636,7 +680,7 @@ static bool g_cdb_archive_check_xml_version(const GCdbArchive *archive) result = NULL; - version = get_node_text_value(archive->context, "/ChrysalideBinary/Protocol"); + version = get_node_text_value(context, "/ChrysalideBinary/Protocol"); if (version == NULL) return false; used = strtoul(version, NULL, 16); @@ -650,16 +694,17 @@ static bool g_cdb_archive_check_xml_version(const GCdbArchive *archive) } + /* ---------------------------------------------------------------------------------- */ -/* ACCES A LA BASE DE DONNEES SQL */ +/* ACTUALISATION DE COLLECTIONS */ /* ---------------------------------------------------------------------------------- */ /****************************************************************************** * * -* Paramètres : archive = archive à constituer. * +* Paramètres : archive = archive dont les collections sont à initialiser. * * * -* Description : Crée la base de données correspondant à l'archive. * +* Description : Crée et remplit les collections à partir de leurs bases. * * * * Retour : Bilan de l'opération. * * * @@ -667,72 +712,40 @@ static bool g_cdb_archive_check_xml_version(const GCdbArchive *archive) * * ******************************************************************************/ -static bool g_cdb_archive_create_db(const GCdbArchive *archive) +static bool g_cdb_archive_load_collections(GCdbArchive *archive) { - bool result; /* Bilan à retourner */ - sqlite3 *db; /* Base de données à constituer*/ - int ret; /* Bilan de la création */ GList *iter; /* Boucle de parcours */ GDbCollection *collec; /* Collection visée manipulée */ - ret = sqlite3_open(archive->sql_db, &db); - - if (ret != SQLITE_OK) - { - fprintf(stderr, "sqlite3_open(): %s\n", sqlite3_errmsg(db)); - return false; - } - - result = true; - for (iter = g_list_first(archive->collections); - iter != NULL && result; + iter != NULL; iter = g_list_next(iter)) { collec = G_DB_COLLECTION(iter->data); - result = g_db_collection_create_db_table(collec, db); - } - - sqlite3_close(db); - - return result; - -} - - - - - - - - - - - - -/* ---------------------------------------------------------------------------------- */ -/* ACCES A LA BASE DE DONNEES SQL */ -/* ACCES A LA BASE DE DONNEES SQL */ -/* ---------------------------------------------------------------------------------- */ + if (!g_db_collection_load_all_items(collec, archive->db)) + return false; + } + return true; +} /****************************************************************************** * * -* Paramètres : archive = archive dont les collections sont à initialiser. * +* Paramètres : archive = archive dont les collections sont à suivre. * * * -* Description : Crée et remplit les collections à partir de leurs bases. * +* Description : Enregistre les signaux associés au suivi des collections. * * * -* Retour : Bilan de l'opération. * +* Retour : - * * * * Remarques : - * * * ******************************************************************************/ -static bool g_cdb_archive_load_collections(GCdbArchive *archive) +static void g_cdb_archive_register_signals(GCdbArchive *archive) { GList *iter; /* Boucle de parcours */ GDbCollection *collec; /* Collection visée manipulée */ @@ -742,15 +755,11 @@ static bool g_cdb_archive_load_collections(GCdbArchive *archive) iter = g_list_next(iter)) { collec = G_DB_COLLECTION(iter->data); - g_signal_connect(collec, "content-extended", G_CALLBACK(on_collection_extended), archive); - if (!g_db_collection_load_all_items(collec, archive->db)) - return false; + g_signal_connect(collec, "content-extended", G_CALLBACK(on_collection_extended), archive); } - return true; - } @@ -1001,6 +1010,8 @@ static void *g_cdb_archive_process(GCdbArchive *archive) LOG_ERROR(LMT_ERROR, _("Bad exchange")); + assert(0); + exit_packed_buffer(&in_pbuf); closed_exchange: diff --git a/src/analysis/db/collection.c b/src/analysis/db/collection.c index da48d3a..bcaf06f 100644 --- a/src/analysis/db/collection.c +++ b/src/analysis/db/collection.c @@ -426,7 +426,8 @@ bool g_db_collection_unpack(GDbCollection *collec, packed_buffer *pbuf, sqlite3 break; default: - /* Pour GCC : DBA_COUNT */ + g_object_unref(G_OBJECT(item)); + result = false; break; } diff --git a/src/analysis/db/protocol.h b/src/analysis/db/protocol.h index d429530..b42ad15 100644 --- a/src/analysis/db/protocol.h +++ b/src/analysis/db/protocol.h @@ -220,6 +220,8 @@ typedef enum _DBError DBE_XML_VERSION_ERROR, /* Vieille archive présente */ DBE_DB_LOADING_ERROR, /* Erreur pendant le chargement*/ + DBE_XML_ERROR, /* Erreur lors d'une définition*/ + DBE_COUNT } DBError; diff --git a/src/analysis/db/server.c b/src/analysis/db/server.c index bd221d7..6fae77e 100644 --- a/src/analysis/db/server.c +++ b/src/analysis/db/server.c @@ -200,7 +200,7 @@ static void g_hub_server_init(GHubServer *server) /****************************************************************************** * * -* Paramètres : archive = instance d'objet GLib à traiter. * +* Paramètres : server = instance d'objet GLib à traiter. * * * * Description : Supprime toutes les références externes. * * * diff --git a/src/analysis/db/snapshot.c b/src/analysis/db/snapshot.c new file mode 100644 index 0000000..d75ac49 --- /dev/null +++ b/src/analysis/db/snapshot.c @@ -0,0 +1,965 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * snapshot.c - prototypes gestion des instantanés de bases de données + * + * Copyright (C) 2019 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 Chrysalide. If not, see . + */ + + +#include "snapshot.h" + + +#include +#include +#include +#include +#include +#include + + +#include + + +#include "collection.h" +#include "../../common/compression.h" +#include "../../common/extstr.h" +#include "../../common/io.h" +#include "../../common/xml.h" +#include "../../core/logs.h" + + + +/* ------------------------ GESTION UNITAIRE DES INSTANTANES ------------------------ */ + + +/* Caractéristiques d'un instantané */ +typedef struct _snapshot_node_t +{ + struct _snapshot_node_t *parent; /* Parent hiérarchique */ + + char *name; /* Nom de l'instantané */ + char *desc; /* Description associée */ + + char *id; /* Identifiant attribué */ + char *path; /* Fichier extrait */ + + struct _snapshot_node_t **children; /* Sous-noeuds rattachés */ + size_t count; /* Quantité de ces noeuds */ + +} snapshot_node_t; + + +/* Constitue un nouveau noeud d'instantané. */ +static snapshot_node_t *create_snapshot_node(const char *); + +/* Libère la mémoire occupée par un noeud d'instantané. */ +static void destroy_snapshot_node(snapshot_node_t *); + +/* Définit le chemin vers une base de données pour un noeud. */ +static bool setup_snapshot_node_db_path(snapshot_node_t *, const char *, const char *); + +/* Valide la présence d'une base de données pour chaque noeud. */ +static bool check_snapshot_nodes(const snapshot_node_t *); + +/* Enregistre tous les éléments associés aux instantanés. */ +static DBError save_snapshot_node(const snapshot_node_t *, xmlDocPtr, xmlXPathContextPtr, struct archive *); + +/* Recherche le noeud d'instantané lié à un identifiant. */ +static snapshot_node_t *find_snapshot_node(snapshot_node_t *, const char *); + +/* Ajoute un instantané comme prolongement d'un instantané. */ +static void add_snapshot_node(snapshot_node_t *, snapshot_node_t *); + + + +/* --------------------- MANIPULATIONS D'ENSEMBLE D'INSTANTANES --------------------- */ + + +/* Gestionnaire d'instantanés de bases de données (instance) */ +struct _GDbSnapshot +{ + GObject parent; /* A laisser en premier */ + + char *tmpdir; /* Répertoire de travail */ + char *hash; /* Empreinte de binaire */ + + snapshot_node_t *nodes; /* Instantanés présents */ + snapshot_node_t *current; /* Instantané courant */ + +}; + +/* Gestionnaire d'instantanés de bases de données (classe) */ +struct _GDbSnapshotClass +{ + GObjectClass parent; /* A laisser en premier */ + +}; + + +/* Initialise la classe des gestionnaires d'instantanés. */ +static void g_db_snapshot_class_init(GDbSnapshotClass *); + +/* Initialise un gestionnaire d'instantanés de base de données. */ +static void g_db_snapshot_init(GDbSnapshot *); + +/* Supprime toutes les références externes. */ +static void g_db_snapshot_dispose(GDbSnapshot *); + +/* Procède à la libération totale de la mémoire. */ +static void g_db_snapshot_finalize(GDbSnapshot *); + +/* Prépare un gestionnaire d'instantanés de bases de données. */ +static GDbSnapshot *g_db_snapshot_new(const char *, const char *); + + + +/* ---------------------------------------------------------------------------------- */ +/* GESTION UNITAIRE DES INSTANTANES */ +/* ---------------------------------------------------------------------------------- */ + + +/****************************************************************************** +* * +* Paramètres : id = éventuel identifiant prédéfini. * +* * +* Description : Constitue un nouveau noeud d'instantané. * +* * +* Retour : Structure mise en place ou NULL en cas d'échec. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static snapshot_node_t *create_snapshot_node(const char *id) +{ + snapshot_node_t *result; /* Nouvel instantané à renvoyer*/ + int ret; /* Bilan d'une génération */ + unsigned char rand[32]; /* Tirage aléatoire */ + size_t i; /* Boucle de parcours */ + char _id[65]; /* Identifiant nouveau */ + + static char *alphabet = "0123456789abcdef"; + + if (id == NULL) + { + ret = RAND_bytes(rand, sizeof(rand)); + + if (ret != 1) + { + LOG_ERROR_OPENSSL; + result = NULL; + goto exit; + } + + for (i = 0; i < sizeof(rand); i++) + { + _id[i * 2 + 0] = alphabet[rand[i] & 0xf]; + _id[i * 2 + 1] = alphabet[(rand[i] >> 4) & 0xf]; + } + + _id[64] = '\0'; + + id = _id; + + } + + result = malloc(sizeof(snapshot_node_t)); + + result->parent = NULL; + + result->name = NULL; + result->desc = NULL; + + result->id = strdup(id); + result->path = NULL; + + result->children = NULL; + result->count = 0; + + exit: + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : node = noeud d'instantané à traiter. * +* * +* Description : Libère la mémoire occupée par un noeud d'instantané. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void destroy_snapshot_node(snapshot_node_t *node) +{ + size_t i; /* Boucle de parcours */ + int ret; /* Bilan d'un appel */ + + for (i = 0; i < node->count; i++) + destroy_snapshot_node(node->children[i]); + + if (node->name != NULL) + free(node->name); + + if (node->desc != NULL) + free(node->desc); + + free(node->id); + + if (node->path != NULL) + { + ret = unlink(node->path); + if (ret != 0) LOG_ERROR_N("unlink"); + + free(node->path); + + } + + if (node->children != NULL) + free(node->children); + + free(node); + +} + + +/****************************************************************************** +* * +* Paramètres : node = noeud d'instantané à traiter. * +* tmpdir = répertoire de travail temporaire. * +* hash = empreinte du binaire à représenter. * +* * +* Description : Définit le chemin vers une base de données pour un noeud. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static bool setup_snapshot_node_db_path(snapshot_node_t *node, const char *tmpdir, const char *hash) +{ + bool result; /* Bilan à retourner */ + int ret; /* Bilan d'une génération */ + + ret = asprintf(&node->path, "%s" G_DIR_SEPARATOR_S "%s_%s_db.sql", tmpdir, hash, node->id); + result = (ret > 0); + + if (result) + { + ret = ensure_path_exists(node->path); + result = (ret != -1); + } + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : node = départ du parcours de vérification. * +* * +* Description : Valide la présence d'une base de données pour chaque noeud. * +* * +* Retour : true si l'ensemble de noeuds est dans un état cohérent. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static bool check_snapshot_nodes(const snapshot_node_t *node) +{ + bool result; /* Bilan à retourner */ + size_t i; /* Boucle de parcours */ + + result = (node->path != NULL); + + if (!result) + log_variadic_message(LMT_ERROR, _("Database is missing for snapshot '%s'"), node->id); + + for (i = 0; i < node->count && result; i++) + result = check_snapshot_nodes(node->children[i]); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : node = départ du parcours. * +* xdoc = document XML à compléter. * +* context = contexte pour les recherches. * +* archive = archive en cours de constitution. * +* * +* Description : Enregistre tous les éléments associés aux instantanés. * +* * +* Retour : Identifiant de l'instantané courant. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static DBError save_snapshot_node(const snapshot_node_t *node, xmlDocPtr xdoc, xmlXPathContextPtr context, struct archive *archive) +{ + DBError result; /* Conclusion à retourner */ + char *name; /* Désignation d'une entrée */ + int ret; /* Bilan d'un appel */ + CPError error; /* Bilan d'une compression */ + xmlNodePtr xml_node; /* Nouveau noeud XML */ + bool status; /* Bilan d'un ajout XML */ + size_t i; /* Boucle de parcours */ + + /* Sauvegarde de la base de données */ + + ret = asprintf(&name, "%s.db", node->id); + + if (ret < 0) + { + result = DBE_SYS_ERROR; + goto exit; + } + + assert(node->path != NULL); + + error = add_file_into_archive(archive, node->path, name); + + free(name); + + switch (error) + { + case CPE_NO_ERROR: + break; + + case CPE_SYSTEM_ERROR: + result = DBE_SYS_ERROR; + goto exit; + break; + + case CPE_ARCHIVE_ERROR: + result = DBE_ARCHIVE_ERROR; + goto exit; + break; + + } + + /* Inscription dans le document XML */ + + xml_node = ensure_node_exist(xdoc, context, "/ChrysalideBinary/Snapshots"); + + if (xml_node == NULL) + { + result = DBE_XML_ERROR; + goto exit; + } + + xml_node = add_node_to_xpath(xdoc, context, "/ChrysalideBinary/Snapshots", "Snapshot"); + + if (xml_node == NULL) + { + result = DBE_XML_ERROR; + goto exit; + } + + status = _add_string_attribute_to_node(xml_node, "id", node->id); + + if (!status) + { + result = DBE_XML_ERROR; + goto exit; + } + + if (node->parent != NULL) + { + status = _add_string_attribute_to_node(xml_node, "parent", node->parent->id); + + if (!status) + { + result = DBE_XML_ERROR; + goto exit; + } + + } + + /* Poursuite des enregistrement */ + + result = DBE_NONE; + + for (i = 0; i < node->count && result == DBE_NONE; i++) + result = save_snapshot_node(node->children[i], xdoc, context, archive); + + exit: + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : node = départ du parcours de recherche. * +* id = identifiant de l'instantané visé. * +* * +* Description : Recherche le noeud d'instantané lié à un identifiant. * +* * +* Retour : Noeud trouvé ou NULL en cas d'échec. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static snapshot_node_t *find_snapshot_node(snapshot_node_t *node, const char *id) +{ + snapshot_node_t *result; /* Noeud trouvé à renvoyer */ + size_t i; /* Boucle de parcours */ + + if (strcmp(node->id, id) == 0) + result = node; + + else + { + result = NULL; + + for (i = 0; i < node->count && result == NULL; i++) + result = find_snapshot_node(node->children[i], id); + + } + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : node = instantané de rattachement. * +* child = instantané à attacher. * +* * +* Description : Ajoute un instantané comme prolongement d'un instantané. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void add_snapshot_node(snapshot_node_t *node, snapshot_node_t *child) +{ + node->children = realloc(node->children, ++node->count * sizeof(snapshot_node_t *)); + + node->children[node->count - 1] = child; + +} + + + + + +/* ---------------------------------------------------------------------------------- */ +/* MANIPULATIONS D'ENSEMBLE D'INSTANTANES */ +/* ---------------------------------------------------------------------------------- */ + + +/* Indique le type défini pour un gestionnaire d'instantanés de bases de données. */ +G_DEFINE_TYPE(GDbSnapshot, g_db_snapshot, G_TYPE_OBJECT); + + +/****************************************************************************** +* * +* Paramètres : klass = classe à initialiser. * +* * +* Description : Initialise la classe des gestionnaires d'instantanés. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_db_snapshot_class_init(GDbSnapshotClass *klass) +{ + GObjectClass *object; /* Autre version de la classe */ + + object = G_OBJECT_CLASS(klass); + + object->dispose = (GObjectFinalizeFunc/* ! */)g_db_snapshot_dispose; + object->finalize = (GObjectFinalizeFunc)g_db_snapshot_finalize; + +} + + +/****************************************************************************** +* * +* Paramètres : snap = instance à initialiser. * +* * +* Description : Initialise un gestionnaire d'instantanés de base de données. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_db_snapshot_init(GDbSnapshot *snap) +{ + snap->tmpdir = NULL; + snap->hash = NULL; + + snap->nodes = NULL; + + snap->current = NULL; + +} + + +/****************************************************************************** +* * +* Paramètres : snap = instance d'objet GLib à traiter. * +* * +* Description : Supprime toutes les références externes. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_db_snapshot_dispose(GDbSnapshot *snap) +{ + G_OBJECT_CLASS(g_db_snapshot_parent_class)->dispose(G_OBJECT(snap)); + +} + + +/****************************************************************************** +* * +* Paramètres : snap = instance d'objet GLib à traiter. * +* * +* Description : Procède à la libération totale de la mémoire. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_db_snapshot_finalize(GDbSnapshot *snap) +{ + if (snap->tmpdir != NULL) + free(snap->tmpdir); + + if (snap->hash != NULL) + free(snap->hash); + + if (snap->nodes != NULL) + destroy_snapshot_node(snap->nodes); + + G_OBJECT_CLASS(g_db_snapshot_parent_class)->finalize(G_OBJECT(snap)); + +} + + +/****************************************************************************** +* * +* Paramètres : tmpdir = répertoire de travail temporaire. * +* hash = empreinte du binaire à représenter. * +* * +* Description : Prépare un gestionnaire d'instantanés de bases de données. * +* * +* Retour : Structure mise en place ou NULL en cas d'échec. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static GDbSnapshot *g_db_snapshot_new(const char *tmpdir, const char *hash) +{ + GDbSnapshot *result; /* Adresse à retourner */ + + result = g_object_new(G_TYPE_DB_SNAPSHOT, NULL); + + result->tmpdir = strdup(tmpdir); + result->hash = strdup(hash); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : tmpdir = répertoire de travail temporaire. * +* hash = empreinte du binaire à représenter. * +* collections = ensemble de modifications par catégories. * +* * +* Description : Prépare un gestionnaire d'instantanés de bases de données. * +* * +* Retour : Structure mise en place ou NULL en cas d'échec. * +* * +* Remarques : - * +* * +******************************************************************************/ + +GDbSnapshot *g_db_snapshot_new_empty(const char *tmpdir, const char *hash, GList *collections) +{ + GDbSnapshot *result; /* Adresse à retourner */ + sqlite3 *db; /* Base de données à manipuler */ + int ret; /* Bilan de la création */ + bool status; /* Bilan d'une mise en place */ + GList *iter; /* Boucle de parcours */ + GDbCollection *collec; /* Collection visée manipulée */ + + result = g_db_snapshot_new(tmpdir, hash); + + result->nodes = create_snapshot_node(NULL); + + status = setup_snapshot_node_db_path(result->nodes, tmpdir, hash); + if (!status) goto error; + + result->current = result->nodes; + + ret = sqlite3_open(result->nodes->path, &db); + + if (ret != SQLITE_OK) + { + LOG_ERROR_SQLITE(db, "sqlite3_open"); + goto error_db; + } + + for (iter = g_list_first(collections); + iter != NULL; + iter = g_list_next(iter)) + { + collec = G_DB_COLLECTION(iter->data); + + status = g_db_collection_create_db_table(collec, db); + if (!status) goto error_db; + + } + + sqlite3_close(db); + + return result; + + error_db: + + sqlite3_close(db); + + error: + + g_object_unref(G_OBJECT(result)); + + return NULL; + +} + + +/****************************************************************************** +* * +* Paramètres : tmpdir = répertoire de travail temporaire. * +* hash = empreinte du binaire à représenter. * +* xdoc = document XML à compléter. * +* context = contexte pour les recherches. * +* * +* Description : Charge un gestionnaire d'instantanés de bases de données. * +* * +* Retour : Structure mise en place ou NULL en cas d'échec. * +* * +* Remarques : - * +* * +******************************************************************************/ + +GDbSnapshot *g_db_snapshot_new_from_xml(const char *tmpdir, const char *hash, xmlDocPtr xdoc, xmlXPathContextPtr context) +{ + GDbSnapshot *result; /* Adresse à retourner */ + xmlXPathObjectPtr xobject; /* Cible d'une recherche */ + size_t count; /* Nombre de contenus premiers */ + size_t i; /* Boucle de parcours */ + xmlNodePtr xml_node; /* Noeud XML avec propriétés */ + char *parent_id; /* Identifiant de noeud parent */ + snapshot_node_t *parent; /* Instantané parent trouvé */ + char *node_id; /* Identifiant de noeud courant*/ + snapshot_node_t *node; /* Instantané nouveau constitué*/ + + result = g_db_snapshot_new(tmpdir, hash); + + /* Chargement de l'ensemble des instantanés */ + + xobject = get_node_xpath_object(context, "/ChrysalideBinary/Snapshots/Snapshot"); + + count = XPATH_OBJ_NODES_COUNT(xobject); + + for (i = 0; i < count; i++) + { + xml_node = NODE_FROM_PATH_OBJ(xobject, i); + + parent_id = qck_get_node_prop_value(xml_node, "parent"); + + if (parent_id == NULL) + parent = NULL; + + else + { + if (result->nodes == NULL) + parent = NULL; + else + parent = find_snapshot_node(result->nodes, parent_id); + + free(parent_id); + + if (parent == NULL) + goto bad_xml; + + } + + node_id = qck_get_node_prop_value(xml_node, "id"); + + if (node_id == NULL) + goto bad_xml; + + node = create_snapshot_node(node_id); + + free(node_id); + + if (node == NULL) + goto bad_xml; + + if (parent == NULL) + { + if (result->nodes != NULL) + goto bad_xml; + + result->nodes = node; + + } + else + add_snapshot_node(parent, node); + + } + + if(xobject != NULL) + xmlXPathFreeObject(xobject); + + /* Détermination de l'instantané courant */ + + node_id = get_node_text_value(context, "/ChrysalideBinary/CurrentSnapshot"); + + if (node_id == NULL) + result->current = result->nodes; + else + { + result->current = find_snapshot_node(result->nodes, node_id); + free(node_id); + } + + if (result->current == NULL) + goto no_current; + + return result; + + bad_xml: + + if(xobject != NULL) + xmlXPathFreeObject(xobject); + + no_current: + + g_object_unref(G_OBJECT(result)); + + return NULL; + +} + + +/****************************************************************************** +* * +* Paramètres : snap = gestionnaire d'instantanés à constituer. * +* archive = archive en cours de lecture. * +* * +* Description : Associe une base de données aux instantanés chargés. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool g_db_snapshot_fill(GDbSnapshot *snap, struct archive *archive) +{ + bool result; /* Bilan à retourner */ + struct archive_entry *entry; /* Elément de l'archive */ + int ret; /* Bilan d'un appel */ + const char *path; /* Désignation d'un fichier */ + const char *dot; /* Début de l'extension */ + char *node_id; /* Identifiant de noeud */ + snapshot_node_t *node; /* Instantané trouvé */ + + result = false; + + for (ret = archive_read_next_header(archive, &entry); + ret == ARCHIVE_OK; + ret = archive_read_next_header(archive, &entry)) + { + path = archive_entry_pathname(entry); + + if (!_endswith(path, ".db", &dot)) + continue; + + node_id = strndup(path, dot - path); + + node = find_snapshot_node(snap->nodes, node_id); + + free(node_id); + + if (!setup_snapshot_node_db_path(node, snap->tmpdir, snap->hash)) + break; + + if (!dump_archive_entry_into_file(archive, entry, node->path)) + break; + + } + + if (ret != ARCHIVE_EOF) + goto exit; + + if (!check_snapshot_nodes(snap->nodes)) + goto exit; + + result = true; + + exit: + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : snap = gestionnaire d'instantanés à consulter. * +* xdoc = document XML à compléter. * +* context = contexte pour les recherches. * +* archive = archive en cours de constitution. * +* * +* Description : Enregistre tous les éléments associés aux instantanés. * +* * +* Retour : Bilan de l'opération sous forme de code d'erreur. * +* * +* Remarques : - * +* * +******************************************************************************/ + +DBError g_db_snapshot_save(const GDbSnapshot *snap, xmlDocPtr xdoc, xmlXPathContextPtr context, struct archive *archive) +{ + DBError result; /* Conclusion à retourner */ + bool status; /* Bilan d'un ajout XML */ + + assert(snap->current != NULL); + + status = add_content_to_node(xdoc, context, "/ChrysalideBinary/CurrentSnapshot", snap->current->id); + + if (!status) + result = DBE_XML_ERROR; + + else + result = save_snapshot_node(snap->nodes, xdoc, context, archive); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : snap = gestionnaire d'instantanés à consulter. * +* * +* Description : Fournit l'identifiant de l'instanné courant. * +* * +* Retour : Identifiant de l'instantané courant. * +* * +* Remarques : - * +* * +******************************************************************************/ + +const char *g_db_snapshot_get_current_id(const GDbSnapshot *snap) +{ + char *result; /* Indentifiant à retourner */ + + assert(snap->current != NULL); + + if (snap->current == NULL) + result = NULL; + else + result = snap->current->id; + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : snap = gestionnaire d'instantanés à consulter. * +* id = identifiant de l'instantané visé. * +* * +* Description : Fournit la base de données correspondant à instanné donné. * +* * +* Retour : Base de données liée à l'instantané demandé ou NULL. * +* * +* Remarques : - * +* * +******************************************************************************/ + +sqlite3 *g_db_snapshot_get_database(const GDbSnapshot *snap, const char *id) +{ + sqlite3 *result; /* Base SQLite à retourner */ + snapshot_node_t *node; /* Instantané trouvé */ + int ret; /* Bilan d'un appel */ + + node = find_snapshot_node(snap->nodes, id); + + if (node == NULL) + { + log_variadic_message(LMT_ERROR, _("Snapshot not found for id '%s'"), id); + result = NULL; + } + + else + { + ret = sqlite3_open(node->path, &result); + + if (ret != SQLITE_OK) + { + if (result != NULL) + sqlite3_close(result); + + result = NULL; + + } + + } + + return result; + +} diff --git a/src/analysis/db/snapshot.h b/src/analysis/db/snapshot.h new file mode 100644 index 0000000..452debd --- /dev/null +++ b/src/analysis/db/snapshot.h @@ -0,0 +1,78 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * snapshot.h - prototypes pour la gestion des instantanés de bases de données + * + * Copyright (C) 2019 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 Chrysalide. If not, see . + */ + + +#ifndef _ANALYSIS_DB_SNAPSHOT_H +#define _ANALYSIS_DB_SNAPSHOT_H + + +#include +#include +#include +#include +#include +#include + + +#include "protocol.h" + + + +#define G_TYPE_DB_SNAPSHOT g_db_snapshot_get_type() +#define G_DB_SNAPSHOT(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), G_TYPE_DB_SNAPSHOT, GDbSnapshot)) +#define G_IS_DB_SNAPSHOT(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), G_TYPE_DB_SNAPSHOT)) +#define G_DB_SNAPSHOT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), G_TYPE_DB_SNAPSHOT, GDbSnapshotClass)) +#define G_IS_DB_SNAPSHOT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), G_TYPE_DB_SNAPSHOT)) +#define G_DB_SNAPSHOT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), G_TYPE_DB_SNAPSHOT, GDbSnapshotClass)) + + +/* Gestionnaire d'instantanés de bases de données (instance) */ +typedef struct _GDbSnapshot GDbSnapshot; + +/* Gestionnaire d'instantanés de bases de données (classe) */ +typedef struct _GDbSnapshotClass GDbSnapshotClass; + + +/* Indique le type défini pour un gestionnaire d'instantanés de bases de données. */ +GType g_db_snapshot_get_type(void); + +/* Prépare un gestionnaire d'instantanés de bases de données. */ +GDbSnapshot *g_db_snapshot_new_empty(const char *, const char *, GList *); + +/* Charge un gestionnaire d'instantanés de bases de données. */ +GDbSnapshot *g_db_snapshot_new_from_xml(const char *, const char *, xmlDocPtr, xmlXPathContextPtr); + +/* Associe une base de données aux instantanés chargés. */ +bool g_db_snapshot_fill(GDbSnapshot *, struct archive *); + +/* Enregistre tous les éléments associés aux instantanés. */ +DBError g_db_snapshot_save(const GDbSnapshot *, xmlDocPtr, xmlXPathContextPtr, struct archive *); + +/* Fournit l'identifiant de l'instanné courant. */ +const char *g_db_snapshot_get_current_id(const GDbSnapshot *); + +/* Fournit la base de données correspondant à instanné donné. */ +sqlite3 *g_db_snapshot_get_database(const GDbSnapshot *, const char *); + + + +#endif /* _ANALYSIS_DB_SNAPSHOT_H */ diff --git a/src/core/logs.h b/src/core/logs.h index 8019382..5455a92 100644 --- a/src/core/logs.h +++ b/src/core/logs.h @@ -132,6 +132,15 @@ void log_variadic_message(LogMessageType, const char *, ...); } \ while (0) +#define LOG_ERROR_SQLITE(db, func) \ + do \ + { \ + const char *__msg; \ + __msg = (db != NULL ? sqlite3_errmsg(db) : "unabled to allocate memory"); \ + log_variadic_message(LMT_EXT_ERROR, "[%s:%u] %s: %s", __FUNCTION__, __LINE__, func, __msg); \ + } \ + while (0) + #endif /* _CORE_LOGS_H */ -- cgit v0.11.2-87-g4458