/* 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/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 *); /* Ajoute un instantané comme prolongement d'un instantané. */ static void add_snapshot_node(snapshot_node_t *, snapshot_node_t *); /* 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 */ }; /* 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 = 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; src = get_snapshot_info_id(&node->info); dest = get_snapshot_info_parent_id(&child->info); copy_snapshot_id(dest, src); } /****************************************************************************** * * * 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; } /****************************************************************************** * * * 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, 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); 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 *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); /* 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)); 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; 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 */ 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 result = save_snapshot_node(snap->nodes, xdoc, context, archive); 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. * * 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 snapshot_id_t *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'"), snapshot_id_as_string(id)); result = NULL; } else { ret = sqlite3_open(node->path, &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; }