/* 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 "collection.h" #include "../../common/compression.h" #include "../../common/extstr.h" #include "../../common/io.h" #include "../../common/sqlite.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 */ snapshot_info_t info; /* Détails de l'instantané */ 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 *, uint64_t, const char *, 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 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 *); /* --------------------- 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 */ char *current_db; /* Base de données SQLite */ }; /* 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 = source de données éventuelle pour l'identifiant. * * created = source de données pour la date de création. * * name = source de données éventuelle pour le nom. * * desc = source de données éventuelle pour la description. * * * * 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, uint64_t created, const char *name, const char *desc) { snapshot_node_t *result; /* Nouvel instantané à renvoyer*/ bool status; /* Bilan d'une génération */ result = malloc(sizeof(snapshot_node_t)); result->parent = NULL; if (id == NULL) status = init_snapshot_info(&result->info); else status = init_snapshot_info_from_text(&result->info, id, created, name, desc); if (!status) { free(result); result = NULL; goto exit; } 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]); exit_snapshot_info(&node->info); 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 */ snapshot_id_t *id; /* Identifiant attribué */ int ret; /* Bilan d'une génération */ id = get_snapshot_info_id(&node->info); ret = asprintf(&node->path, "%s" G_DIR_SEPARATOR_S "%s_%s_db.sql", tmpdir, hash, snapshot_id_as_string(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 */ const snapshot_id_t *id; /* Identifiant attribué */ size_t i; /* Boucle de parcours */ result = (node->path != NULL); if (!result) { id = get_snapshot_info_id(&node->info); log_variadic_message(LMT_ERROR, _("Database is missing for snapshot '%s'"), snapshot_id_as_string(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 */ const snapshot_id_t *id; /* Identifiant attribué */ 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 */ timestamp_t created; /* Date de création */ const char *value; /* Valeur éventuelle à inscrire*/ size_t i; /* Boucle de parcours */ /* Sauvegarde de la base de données */ id = get_snapshot_info_id(&node->info); ret = asprintf(&name, "%s.db", snapshot_id_as_string(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", snapshot_id_as_string(id)); if (!status) { result = DBE_XML_ERROR; goto exit; } created = get_snapshot_info_created(&node->info); status = _add_uint64_attribute_to_node(xml_node, "created", created); if (!status) { result = DBE_XML_ERROR; goto exit; } value = get_snapshot_info_name(&node->info); if (value != NULL) { status = _add_string_attribute_to_node(xml_node, "name", value); if (!status) { result = DBE_XML_ERROR; goto exit; } } value = get_snapshot_info_desc(&node->info); if (value != NULL) { status = _add_string_attribute_to_node(xml_node, "desc", value); if (!status) { result = DBE_XML_ERROR; goto exit; } } if (node->parent != NULL) { id = get_snapshot_info_id(&node->parent->info); status = _add_string_attribute_to_node(xml_node, "parent", snapshot_id_as_string(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 snapshot_id_t *id) { snapshot_node_t *result; /* Noeud trouvé à renvoyer */ snapshot_id_t *node_id; /* Identifiant attribué */ size_t i; /* Boucle de parcours */ node_id = get_snapshot_info_id(&node->info); if (cmp_snapshot_id(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 = 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. * * * * Description : Ajoute un instantané comme prolongement d'un instantané. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void add_snapshot_node(snapshot_node_t *node, snapshot_node_t *child) { snapshot_id_t *src; /* Identifiant d'instantané #0 */ snapshot_id_t *dest; /* Identifiant d'instantané #1 */ node->children = realloc(node->children, ++node->count * sizeof(snapshot_node_t *)); node->children[node->count - 1] = child; child->parent = node; src = get_snapshot_info_id(&node->info); dest = get_snapshot_info_parent_id(&child->info); copy_snapshot_id(dest, src); } /****************************************************************************** * * * 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. * * * * Description : Collecte les descriptions d'une arborescence d'instantanés. * * * * Retour : Bilan du déroulement des opérations. * * * * Remarques : - * * * ******************************************************************************/ static bool pack_snapshot_node(const snapshot_node_t *node, packed_buffer *pbuf) { bool result; /* Bilan à retourner */ size_t i; /* Boucle de parcours */ result = pack_snapshot_info(&node->info, pbuf); for (i = 0; i < node->count && result; i++) result = pack_snapshot_node(node->children[i], pbuf); return result; } /* ---------------------------------------------------------------------------------- */ /* 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; snap->current_db = 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); if (snap->current_db != NULL) free(snap->current_db); 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 */ 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; } /****************************************************************************** * * * 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); if (result == NULL) goto exit; result->nodes = create_snapshot_node(NULL, 0, NULL, 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); status = copy_file(result->current_db, result->nodes->path); if (!status) goto error; return result; error_db: sqlite3_close(db); error: g_object_unref(G_OBJECT(result)); exit: 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 *raw_id; /* Identifiant brut à convertir*/ bool status; /* Bilan d'une conversion */ snapshot_id_t parent_id; /* Identifiant de noeud parent */ snapshot_node_t *parent; /* Instantané parent trouvé */ uint64_t created; /* Date de création */ char *name; /* Nom d'instantané */ char *desc; /* Description d'instantané */ snapshot_node_t *node; /* Instantané nouveau constitué*/ 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 */ 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); raw_id = qck_get_node_prop_value(xml_node, "parent"); if (raw_id == NULL) parent = NULL; else { if (result->nodes == NULL) parent = NULL; else { status = init_snapshot_id_from_text(&parent_id, raw_id); if (status) parent = find_snapshot_node(result->nodes, &parent_id); else parent = NULL; } free(raw_id); if (parent == NULL) goto bad_xml; } raw_id = qck_get_node_prop_value(xml_node, "id"); if (raw_id == NULL) goto bad_xml; status = qck_get_node_prop_uint64_value(xml_node, "created", &created); if (!status) goto bad_xml; name = qck_get_node_prop_value(xml_node, "name"); desc = qck_get_node_prop_value(xml_node, "desc"); node = create_snapshot_node(raw_id, created, name, desc); free(raw_id); if (name != NULL) free(name); if (desc != NULL) free(desc); 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 */ raw_id = get_node_text_value(context, "/ChrysalideBinary/CurrentSnapshot"); if (raw_id == NULL) result->current = result->nodes; else { status = init_snapshot_id_from_text(&node_id, raw_id); free(raw_id); if (status) result->current = find_snapshot_node(result->nodes, &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)); exit: 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 *raw_id; /* Identifiant brut à convertir*/ snapshot_id_t node_id; /* Identifiant de noeud courant*/ bool status; /* Bilan d'une conversion */ 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; 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); free(raw_id); if (!status) break; node = find_snapshot_node(snap->nodes, &node_id); if (node == NULL) break; 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 */ const snapshot_id_t *id; /* Identifiant attribué */ bool status; /* Bilan d'un ajout XML */ CPError ret; /* Bilan d'une compression */ assert(snap->current != NULL); id = get_snapshot_info_id(&snap->current->info); status = add_content_to_node(xdoc, context, "/ChrysalideBinary/CurrentSnapshot", snapshot_id_as_string(id)); if (!status) 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; } /****************************************************************************** * * * Paramètres : snap = gestionnaire d'instantanés à consulter. * * id = identifiant de l'instantané courant. [OUT] * * * * Description : Fournit l'identifiant de l'instanné courant. * * * * Retour : Validité de la trouvaille. * * * * Remarques : - * * * ******************************************************************************/ bool g_db_snapshot_get_current_id(const GDbSnapshot *snap, snapshot_id_t *id) { bool result; /* Bilan à retourner */ assert(snap->current != NULL); if (snap->current == NULL) result = false; else { copy_snapshot_id(id, get_snapshot_info_id(&snap->current->info)); result = true; } return result; } /****************************************************************************** * * * Paramètres : snap = gestionnaire d'instantanés à consulter. * * * * 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) { sqlite3 *result; /* Base SQLite à retourner */ int ret; /* Bilan d'un appel */ ret = sqlite3_open(snap->current_db, &result); if (ret != SQLITE_OK) { if (result != NULL) sqlite3_close(result); result = NULL; } return result; } /****************************************************************************** * * * Paramètres : snap = gestionnaire d'instantanés à consulter. * * pbuf = paquet de données où venir inscrire des infos. * * * * Description : Collecte les descriptions de l'ensemble des instantanés. * * * * Retour : Bilan du déroulement des opérations. * * * * Remarques : - * * * ******************************************************************************/ bool g_db_snapshot_pack_all(const GDbSnapshot *snap, packed_buffer *pbuf) { bool result; /* Bilan à retourner */ result = pack_snapshot_node(snap->nodes, pbuf); return result; } /****************************************************************************** * * * 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é. * * * * Retour : Bilan de l'opération sous forme de code d'erreur. * * * * Remarques : - * * * ******************************************************************************/ DBError g_db_snapshot_set_name(const GDbSnapshot *snap, packed_buffer *pbuf) { DBError result; /* Conclusion à retourner */ snapshot_id_t id; /* Identifiant d'instantané */ bool status; /* Bilan d'une récupération */ rle_string string; /* Chaîne à transmettre */ 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; } setup_empty_rle_string(&string); status = unpack_rle_string(&string, 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 set_snapshot_info_name(&node->info, get_rle_string(&string)); exit_rle_string(&string); bad_exchange: return result; } /****************************************************************************** * * * 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é. * * * * Retour : Bilan de l'opération sous forme de code d'erreur. * * * * Remarques : - * * * ******************************************************************************/ DBError g_db_snapshot_set_desc(const GDbSnapshot *snap, packed_buffer *pbuf) { DBError result; /* Conclusion à retourner */ snapshot_id_t id; /* Identifiant d'instantané */ bool status; /* Bilan d'une récupération */ rle_string string; /* Chaîne à transmettre */ 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; } setup_empty_rle_string(&string); status = unpack_rle_string(&string, 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 set_snapshot_info_desc(&node->info, get_rle_string(&string)); exit_rle_string(&string); bad_exchange: 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; }