summaryrefslogtreecommitdiff
path: root/src/analysis/db/snapshot.c
diff options
context:
space:
mode:
authorCyrille Bagard <nocbos@gmail.com>2019-10-27 22:33:11 (GMT)
committerCyrille Bagard <nocbos@gmail.com>2019-10-27 23:10:41 (GMT)
commit609c184c3edb350a0da7fe29bf449a7189080c92 (patch)
tree4ddd6320ee58a6169cad377f9889a08298fbec47 /src/analysis/db/snapshot.c
parentd0547bc36bd6ccb84eff128fc6e4f2df034a705a (diff)
Implemented snapshot related management features.
Diffstat (limited to 'src/analysis/db/snapshot.c')
-rw-r--r--src/analysis/db/snapshot.c419
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;
+
+}