diff options
author | Cyrille Bagard <nocbos@gmail.com> | 2019-10-27 22:33:11 (GMT) |
---|---|---|
committer | Cyrille Bagard <nocbos@gmail.com> | 2019-10-27 23:10:41 (GMT) |
commit | 609c184c3edb350a0da7fe29bf449a7189080c92 (patch) | |
tree | 4ddd6320ee58a6169cad377f9889a08298fbec47 /src/analysis/db/snapshot.c | |
parent | d0547bc36bd6ccb84eff128fc6e4f2df034a705a (diff) |
Implemented snapshot related management features.
Diffstat (limited to 'src/analysis/db/snapshot.c')
-rw-r--r-- | src/analysis/db/snapshot.c | 419 |
1 files changed, 396 insertions, 23 deletions
diff --git a/src/analysis/db/snapshot.c b/src/analysis/db/snapshot.c index 2cd50f6..e07129e 100644 --- a/src/analysis/db/snapshot.c +++ b/src/analysis/db/snapshot.c @@ -38,6 +38,7 @@ #include "../../common/compression.h" #include "../../common/extstr.h" #include "../../common/io.h" +#include "../../common/sqlite.h" #include "../../common/xml.h" #include "../../core/logs.h" @@ -79,9 +80,15 @@ static DBError save_snapshot_node(const snapshot_node_t *, xmlDocPtr, xmlXPathCo /* Recherche le noeud d'instantané lié à un identifiant. */ static snapshot_node_t *find_snapshot_node(snapshot_node_t *, const snapshot_id_t *); +/* Détermine si un instantané est compris dans une branche. */ +static bool contain_snapshot_node(const snapshot_node_t *, const snapshot_node_t *); + /* Ajoute un instantané comme prolongement d'un instantané. */ static void add_snapshot_node(snapshot_node_t *, snapshot_node_t *); +/* Fait disparaître un instantané dans une arborescence. */ +static void remove_snapshot_node(snapshot_node_t *, bool); + /* Collecte les descriptions d'une arborescence d'instantanés. */ static bool pack_snapshot_node(const snapshot_node_t *, packed_buffer *); @@ -101,6 +108,8 @@ struct _GDbSnapshot snapshot_node_t *nodes; /* Instantanés présents */ snapshot_node_t *current; /* Instantané courant */ + char *current_db; /* Base de données SQLite */ + }; /* Gestionnaire d'instantanés de bases de données (classe) */ @@ -487,6 +496,34 @@ static snapshot_node_t *find_snapshot_node(snapshot_node_t *node, const snapshot /****************************************************************************** * * +* Paramètres : node = départ du parcours de recherche. * +* target = instantané recherché. * +* * +* Description : Détermine si un instantané est compris dans une branche. * +* * +* Retour : Noeud trouvé ou NULL en cas d'échec. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static bool contain_snapshot_node(const snapshot_node_t *node, const snapshot_node_t *target) +{ + bool result; /* Bilan à faire remonter */ + size_t i; /* Boucle de parcours */ + + result = (node == target); + + for (i = 0; i < node->count && !result; i++) + result = contain_snapshot_node(node->children[i], target); + + return result; + +} + + +/****************************************************************************** +* * * Paramètres : node = instantané de rattachement. * * child = instantané à attacher. * * * @@ -507,6 +544,8 @@ static void add_snapshot_node(snapshot_node_t *node, snapshot_node_t *child) node->children[node->count - 1] = child; + child->parent = node; + src = get_snapshot_info_id(&node->info); dest = get_snapshot_info_parent_id(&child->info); @@ -517,6 +556,78 @@ static void add_snapshot_node(snapshot_node_t *node, snapshot_node_t *child) /****************************************************************************** * * +* Paramètres : node = instantané à traiter. * +* rec = précise si les enfants sont à rattacher au parent. * +* * +* Description : Fait disparaître un instantané dans une arborescence. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void remove_snapshot_node(snapshot_node_t *node, bool rec) +{ + snapshot_node_t *parent; /* Accès direct */ + size_t i; /* Boucle de parcours */ + + parent = node->parent; + + assert(parent != NULL); + + /* Coupe de la branche */ + + assert(parent->count > 0); + + if (parent->count == 1) + { + free(parent->children); + + parent->children = NULL; + parent->count = 0; + + } + + else + { + for (i = 0; i < parent->count; i++) + if (parent->children[i] == node) + break; + + assert(i < parent->count); + + if ((i + 1) < parent->count) + memmove(&parent->children[i], &parent->children[i + 1], + (parent->count - i - 1) * sizeof(snapshot_node_t *)); + + parent->children = realloc(parent->children, --parent->count * sizeof(snapshot_node_t *)); + + } + + /* Rattachement des enfants ? */ + + if (!rec) + { + for (i = 0; i < node->count; i++) + add_snapshot_node(parent, node->children[i]); + + free(node->children); + + node->children = NULL; + node->count = 0; + + } + + /* Suppression */ + + destroy_snapshot_node(node); + +} + + +/****************************************************************************** +* * * Paramètres : node = définition d'instantané à consulter. * * pbuf = paquet de données où venir inscrire des infos. * * * @@ -595,9 +706,10 @@ static void g_db_snapshot_init(GDbSnapshot *snap) snap->hash = NULL; snap->nodes = NULL; - snap->current = NULL; + snap->current_db = NULL; + } @@ -643,6 +755,9 @@ static void g_db_snapshot_finalize(GDbSnapshot *snap) if (snap->nodes != NULL) destroy_snapshot_node(snap->nodes); + if (snap->current_db != NULL) + free(snap->current_db); + G_OBJECT_CLASS(g_db_snapshot_parent_class)->finalize(G_OBJECT(snap)); } @@ -664,12 +779,30 @@ static void g_db_snapshot_finalize(GDbSnapshot *snap) static GDbSnapshot *g_db_snapshot_new(const char *tmpdir, const char *hash) { GDbSnapshot *result; /* Adresse à retourner */ + int ret; /* Bilan d'une génération */ + bool status; /* Bilan de la création */ result = g_object_new(G_TYPE_DB_SNAPSHOT, NULL); result->tmpdir = strdup(tmpdir); result->hash = strdup(hash); + ret = asprintf(&result->current_db, "%s" G_DIR_SEPARATOR_S "%s_current_db.sql", tmpdir, hash); + + status = (ret > 0); + + if (status) + { + ret = ensure_path_exists(result->current_db); + status = (ret != -1); + } + + if (!status) + { + g_object_unref(G_OBJECT(result)); + result = NULL; + } + return result; } @@ -699,6 +832,7 @@ GDbSnapshot *g_db_snapshot_new_empty(const char *tmpdir, const char *hash, GList GDbCollection *collec; /* Collection visée manipulée */ result = g_db_snapshot_new(tmpdir, hash); + if (result == NULL) goto exit; result->nodes = create_snapshot_node(NULL, 0, NULL, NULL); @@ -728,6 +862,11 @@ GDbSnapshot *g_db_snapshot_new_empty(const char *tmpdir, const char *hash, GList sqlite3_close(db); + status = copy_file(result->current_db, result->nodes->path); + + if (!status) + goto error; + return result; error_db: @@ -738,6 +877,8 @@ GDbSnapshot *g_db_snapshot_new_empty(const char *tmpdir, const char *hash, GList g_object_unref(G_OBJECT(result)); + exit: + return NULL; } @@ -776,6 +917,7 @@ GDbSnapshot *g_db_snapshot_new_from_xml(const char *tmpdir, const char *hash, xm snapshot_id_t node_id; /* Identifiant de noeud courant*/ result = g_db_snapshot_new(tmpdir, hash); + if (result == NULL) goto exit; /* Chargement de l'ensemble des instantanés */ @@ -890,6 +1032,8 @@ GDbSnapshot *g_db_snapshot_new_from_xml(const char *tmpdir, const char *hash, xm g_object_unref(G_OBJECT(result)); + exit: + return NULL; } @@ -931,6 +1075,15 @@ bool g_db_snapshot_fill(GDbSnapshot *snap, struct archive *archive) if (!_endswith(path, ".db", &dot)) continue; + if (strcmp(path, "current.db") == 0) + { + if (!dump_archive_entry_into_file(archive, entry, snap->current_db)) + break; + + continue; + + } + raw_id = strndup(path, dot - path); status = init_snapshot_id_from_text(&node_id, raw_id); @@ -988,6 +1141,7 @@ DBError g_db_snapshot_save(const GDbSnapshot *snap, xmlDocPtr xdoc, xmlXPathCont DBError result; /* Conclusion à retourner */ const snapshot_id_t *id; /* Identifiant attribué */ bool status; /* Bilan d'un ajout XML */ + CPError ret; /* Bilan d'une compression */ assert(snap->current != NULL); @@ -999,8 +1153,32 @@ DBError g_db_snapshot_save(const GDbSnapshot *snap, xmlDocPtr xdoc, xmlXPathCont result = DBE_XML_ERROR; else + { + ret = add_file_into_archive(archive, snap->current_db, "current.db"); + + switch (ret) + { + 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; + + } + result = save_snapshot_node(snap->nodes, xdoc, context, archive); + } + + exit: + return result; } @@ -1042,7 +1220,6 @@ bool g_db_snapshot_get_current_id(const GDbSnapshot *snap, snapshot_id_t *id) /****************************************************************************** * * * Paramètres : snap = gestionnaire d'instantanés à consulter. * -* id = identifiant de l'instantané visé. * * * * Description : Fournit la base de données correspondant à instanné donné. * * * @@ -1052,32 +1229,19 @@ bool g_db_snapshot_get_current_id(const GDbSnapshot *snap, snapshot_id_t *id) * * ******************************************************************************/ -sqlite3 *g_db_snapshot_get_database(const GDbSnapshot *snap, const snapshot_id_t *id) +sqlite3 *g_db_snapshot_get_database(const GDbSnapshot *snap) { 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'"), snapshot_id_as_string(id)); - result = NULL; - } + ret = sqlite3_open(snap->current_db, &result); - else + if (ret != SQLITE_OK) { - ret = sqlite3_open(node->path, &result); - - if (ret != SQLITE_OK) - { - if (result != NULL) - sqlite3_close(result); - - result = NULL; + if (result != NULL) + sqlite3_close(result); - } + result = NULL; } @@ -1112,7 +1276,7 @@ bool g_db_snapshot_pack_all(const GDbSnapshot *snap, packed_buffer *pbuf) /****************************************************************************** * * -* Paramètres : snap = gestionnaire d'instantanés à consulter. * +* Paramètres : snap = gestionnaire d'instantanés à consulter. * * pbuf = paquet de données où venir puiser les infos. * * * * Description : Actualise la désignation d'un instantané donné. * @@ -1177,7 +1341,7 @@ DBError g_db_snapshot_set_name(const GDbSnapshot *snap, packed_buffer *pbuf) /****************************************************************************** * * -* Paramètres : snap = gestionnaire d'instantanés à consulter. * +* Paramètres : snap = gestionnaire d'instantanés à consulter. * * pbuf = paquet de données où venir puiser les infos. * * * * Description : Actualise la description d'un instantané donné. * @@ -1238,3 +1402,212 @@ DBError g_db_snapshot_set_desc(const GDbSnapshot *snap, packed_buffer *pbuf) return result; } + + +/****************************************************************************** +* * +* Paramètres : snap = gestionnaire d'instantanés à consulter. * +* pbuf = paquet de données où venir puiser les infos. * +* reload = indique un besoin de rechargement de la base. [OUT] * +* * +* Description : Restaure un instantané de l'arborescence. * +* * +* Retour : Bilan de l'opération sous forme de code d'erreur. * +* * +* Remarques : - * +* * +******************************************************************************/ + +DBError g_db_snapshot_restore(GDbSnapshot *snap, packed_buffer *pbuf, bool *reload) +{ + DBError result; /* Conclusion à retourner */ + snapshot_id_t id; /* Identifiant d'instantané */ + bool status; /* Bilan d'une récupération */ + snapshot_node_t *node; /* Instantané trouvé */ + + result = DBE_NONE; + + /* Lecture des arguments */ + + setup_empty_snapshot_id(&id); + + status = unpack_snapshot_id(&id, pbuf); + if (!status) + { + result = DBE_BAD_EXCHANGE; + goto bad_exchange; + } + + /* Traitement */ + + node = find_snapshot_node(snap->nodes, &id); + + if (node == NULL) + { + log_variadic_message(LMT_ERROR, _("Snapshot not found for id '%s'"), snapshot_id_as_string(&id)); + result = DBE_SNAPSHOT_NOT_FOUND; + } + + else if (node == snap->current) + { + log_simple_message(LMT_WARNING, _("No need to restore the current snapshot")); + *reload = false; + } + + else + { + status = copy_file(snap->current_db, node->path); + + if (!status) + { + log_variadic_message(LMT_ERROR, _("Failed to restore snapshot from '%s' to '%s'"), + node->path, snap->current_db); + result = DBE_SNAPSHOT_RESTORE_FAILURE; + } + + else + { + snap->current = node; + *reload = true; + } + + } + + bad_exchange: + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : snap = gestionnaire d'instantanés à consulter. * +* db = base de données courante. * +* * +* Description : Crée un nouvel instantanés dans l'arborescence. * +* * +* Retour : Bilan de l'opération sous forme de code d'erreur. * +* * +* Remarques : - * +* * +******************************************************************************/ + +DBError g_db_snapshot_create(GDbSnapshot *snap, sqlite3 *db) +{ + DBError result; /* Conclusion à retourner */ + snapshot_node_t *new; /* Nouvel instantané */ + bool status; /* Bilan d'une récupération */ + + result = DBE_NONE; + + new = create_snapshot_node(NULL, 0, NULL, NULL); + + status = setup_snapshot_node_db_path(new, snap->tmpdir, snap->hash); + if (!status) + { + result = DBE_SYS_ERROR; + destroy_snapshot_node(new); + goto error; + } + + status = backup_db(db, new->path); + if (!status) + { + result = DBE_SYS_ERROR; + destroy_snapshot_node(new); + goto error; + } + + add_snapshot_node(snap->current, new); + + snap->current = new; + + error: + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : snap = gestionnaire d'instantanés à consulter. * +* pbuf = paquet de données où venir puiser les infos. * +* changed = indique si l'instantané courant a bougé. [OUT] * +* * +* Description : Supprime un instantané dans l'arborescence. * +* * +* Retour : Bilan de l'opération sous forme de code d'erreur. * +* * +* Remarques : - * +* * +******************************************************************************/ + +DBError g_db_snapshot_remove(GDbSnapshot *snap, packed_buffer *pbuf, bool *changed) +{ + DBError result; /* Conclusion à retourner */ + snapshot_id_t id; /* Identifiant d'instantané */ + bool status; /* Bilan d'une récupération */ + uint8_t tmp; /* Stockage temporaire */ + bool rec; /* Indicateur de récursivité */ + snapshot_node_t *node; /* Instantané trouvé */ + + result = DBE_NONE; + + *changed = false; + + /* Lecture des arguments */ + + setup_empty_snapshot_id(&id); + + status = unpack_snapshot_id(&id, pbuf); + if (!status) + { + result = DBE_BAD_EXCHANGE; + goto bad_exchange; + } + + status = extract_packed_buffer(pbuf, &tmp, sizeof(uint8_t), false); + if (!status) + { + result = DBE_BAD_EXCHANGE; + goto bad_exchange; + } + + rec = (tmp == 0x1); + + /* Traitement */ + + node = find_snapshot_node(snap->nodes, &id); + + if (node == NULL) + { + log_variadic_message(LMT_ERROR, _("Snapshot not found for id '%s'"), snapshot_id_as_string(&id)); + result = DBE_SNAPSHOT_NOT_FOUND; + } + + else if (node == snap->nodes) + { + log_simple_message(LMT_ERROR, _("Root snapshot can not be removed")); + result = DBE_SNAPSHOT_ROOT_REMOVAL; + } + + else + { + /* Réassignation éventuelle */ + if ((rec && contain_snapshot_node(node, snap->current)) || (!rec && node == snap->current)) + { + snap->current = node->parent; + *changed = true; + } + + remove_snapshot_node(node, rec); + + } + + bad_exchange: + + return result; + +} |