|  | #include <stdio.h> | 
|  | #include <limits.h> | 
|  | #include <stdlib.h> | 
|  | #include <string.h> | 
|  | #include <libxml/parser.h> | 
|  | #include <libxml/tree.h> | 
|  | #include <libxml/xmlwriter.h> | 
|  | #include <errno.h> | 
|  | #include <unistd.h> | 
|  |  | 
|  | #include "snapshot.h" | 
|  | #include "vzerror.h" | 
|  | #include "list.h" | 
|  | #include "logger.h" | 
|  |  | 
|  | struct xml_node_param { | 
|  | list_elem_t list; | 
|  | void *p; | 
|  | }; | 
|  |  | 
|  | static void free_node_list(list_head_t *head) | 
|  | { | 
|  | struct xml_node_param *tmp, *it; | 
|  |  | 
|  | list_for_each_safe(it, tmp, head, list) { | 
|  | list_del(&it->list); | 
|  | free(it); | 
|  | } | 
|  | } | 
|  |  | 
|  | static struct xml_node_param *add_xml_node(list_head_t *head, void *node, int tail) | 
|  | { | 
|  | struct xml_node_param *p; | 
|  |  | 
|  | p = malloc(sizeof(struct xml_node_param)); | 
|  | if (p == NULL) { | 
|  | logger(-1, ENOMEM, "malloc"); | 
|  | return NULL; | 
|  | } | 
|  | p->p = node; | 
|  |  | 
|  | if (tail) | 
|  | list_add_tail(&p->list, head); | 
|  | else | 
|  | list_add(&p->list, head); | 
|  | return p; | 
|  | } | 
|  |  | 
|  | static xmlNodePtr find_child_node(xmlNode *cur_node, const char *elem) | 
|  | { | 
|  | xmlNodePtr child; | 
|  |  | 
|  | for (child = cur_node->xmlChildrenNode; child != NULL; child = child->next) { | 
|  | if (!xmlStrcmp(child->name, (const xmlChar *) elem) && | 
|  | child->type == XML_ELEMENT_NODE) | 
|  | { | 
|  | return child; | 
|  | } | 
|  | } | 
|  | return NULL; | 
|  | } | 
|  |  | 
|  | static xmlNodePtr seek(xmlNodePtr root, const char *elem) | 
|  | { | 
|  | xmlNodePtr childNode = root; | 
|  | const char *path, *p; | 
|  | char nodename[128]; | 
|  | int last = 0; | 
|  |  | 
|  | path = elem; | 
|  | if (path[0] == '/') | 
|  | path++; | 
|  | if (path[0] == 0) | 
|  | return NULL; | 
|  | while (!last) { | 
|  | if ((p = strchr(path, '/')) == NULL) { | 
|  | p = path + strlen(path); | 
|  | last = 1; | 
|  | } | 
|  | snprintf(nodename, p - path + 1, "%s", path); | 
|  | childNode = find_child_node(childNode, nodename); | 
|  | if (childNode == NULL) | 
|  | return NULL; | 
|  | path = ++p; | 
|  | } | 
|  | return childNode; | 
|  | } | 
|  |  | 
|  | static const char *get_element_txt(xmlNode *node) | 
|  | { | 
|  | xmlNode *child; | 
|  |  | 
|  | for (child = node->xmlChildrenNode; child; child = child->next) { | 
|  | if (child->type == XML_TEXT_NODE || | 
|  | child->type == XML_CDATA_SECTION_NODE) | 
|  | return (const char*)child->content; | 
|  | } | 
|  | return NULL; | 
|  | } | 
|  |  | 
|  | static int add_child_nodes(struct vzctl_snapshot_tree *tree, list_head_t *pool, | 
|  | xmlNode *cur_node, const char *parent_guid) | 
|  | { | 
|  | xmlNode *node; | 
|  | xmlChar *guid = NULL; | 
|  | xmlChar *val = NULL; | 
|  | const char *name = NULL; | 
|  | const char *date = NULL; | 
|  | const char *desc = NULL; | 
|  | int ret; | 
|  | int current; | 
|  |  | 
|  | cur_node = seek(cur_node, "SavedStateItem"); | 
|  | if (cur_node == NULL) | 
|  | return 0; | 
|  |  | 
|  | ret = VZ_RESOURCE_ERROR; | 
|  | for (; cur_node; cur_node = cur_node->next) { | 
|  | if (cur_node->type != XML_ELEMENT_NODE) | 
|  | continue; | 
|  | guid = xmlGetProp(cur_node, BAD_CAST "guid"); | 
|  | if (guid == NULL) { | 
|  | logger(-1, 0, "Invalid snapshot file format: no guid attribute"); | 
|  | goto err; | 
|  | } | 
|  | current = 0; | 
|  | val = xmlGetProp(cur_node, BAD_CAST "current"); | 
|  | if (val != NULL) { | 
|  | current = (strcasecmp((const char *)val, "yes") == 0) ? 1 : 0; | 
|  | free(val); | 
|  | } | 
|  | name = NULL; | 
|  | node = seek(cur_node, "Name"); | 
|  | if (node != NULL) | 
|  | name = get_element_txt(node); | 
|  | date = NULL; | 
|  | node = seek(cur_node, "DateTime"); | 
|  | if (node != NULL) | 
|  | date = get_element_txt(node); | 
|  | desc = NULL; | 
|  | node = seek(cur_node, "Description"); | 
|  | if (node != NULL) | 
|  | desc = get_element_txt(node); | 
|  |  | 
|  | if (vzctl_add_snapshot_tree_entry(tree, current, (const char *) guid, parent_guid, | 
|  | name, date, desc)) | 
|  | goto err; | 
|  | if (add_xml_node(pool, cur_node, 1) == NULL) | 
|  | goto err; | 
|  | free(guid); | 
|  | guid = NULL; | 
|  | } | 
|  | ret = 0; | 
|  | err: | 
|  | free(guid); | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | static int parse_xml(const char *basedir, xmlNode *root_node, struct vzctl_snapshot_tree *tree) | 
|  | { | 
|  | xmlNode *cur_node ; | 
|  | xmlChar *guid; | 
|  | list_head_t pool; | 
|  | struct xml_node_param *tmp, *it; | 
|  | int ret = 0; | 
|  |  | 
|  | list_head_init(&pool); | 
|  | cur_node = seek(root_node, "/SavedStateItem"); | 
|  | if (cur_node == NULL) | 
|  | return 0; | 
|  | if (add_xml_node(&pool, cur_node, 1) == NULL) | 
|  | return VZ_RESOURCE_ERROR; | 
|  | while (!list_empty(&pool)) { | 
|  | list_for_each_safe(it, tmp, &pool, list) { | 
|  | xmlNode *cur_node = (xmlNode*) it->p; | 
|  |  | 
|  | guid = xmlGetProp(cur_node, BAD_CAST "guid"); | 
|  | if (guid == NULL) { | 
|  | logger(-1, 0, "Invalid snapshot file format: no guid attribute"); | 
|  | ret = -1; | 
|  | break; | 
|  | } | 
|  | ret = add_child_nodes(tree, &pool, cur_node, (const char *)guid); | 
|  | free(guid); | 
|  | if (ret) | 
|  | break; | 
|  | list_del(&it->list); | 
|  | free(it); | 
|  | } | 
|  | } | 
|  | free_node_list(&pool); | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | static int update_child_by_guid(struct vzctl_snapshot_tree *tree, list_head_t *head, const char *guid) | 
|  | { | 
|  | int i, cnt = 0; | 
|  | struct xml_node_param *it; | 
|  | list_head_t childs; | 
|  |  | 
|  | list_head_init(&childs); | 
|  | for (i = 0; i < tree->nsnapshots; i++) { | 
|  | if (strcmp(tree->snapshots[i]->parent_guid, guid) != 0) | 
|  | continue; | 
|  | if (add_xml_node(&childs, tree->snapshots[i]->guid, 1) == NULL) | 
|  | return -1; | 
|  | cnt++; | 
|  | } | 
|  | // add on top | 
|  | list_for_each_prev(it, &childs, list) { | 
|  | if (add_xml_node(head, it->p, 0) == NULL) | 
|  | return -1; | 
|  | } | 
|  | free_node_list(&childs); | 
|  | return cnt; | 
|  | } | 
|  |  | 
|  | #define WRITE_ELEMENT(name, data)						\ | 
|  | if ((rc = xmlTextWriterWriteElement(writer, BAD_CAST name,		\ | 
|  | BAD_CAST (data ? data : ""))) < 0) {	\ | 
|  | return vzctl_err(-1, 0, "WriteElement %s rc=%d\n", name, rc);	\ | 
|  | } | 
|  |  | 
|  | static int write_SavedStateItem(xmlTextWriterPtr writer, struct vzctl_snapshot_data *snapshot) | 
|  | { | 
|  | int rc; | 
|  |  | 
|  | rc = xmlTextWriterStartElement(writer, BAD_CAST "SavedStateItem"); | 
|  | if (rc < 0) | 
|  | return vzctl_err(-1, 0, "Error at WriterStartElemen"); | 
|  | rc = xmlTextWriterWriteAttribute(writer, BAD_CAST "guid", BAD_CAST snapshot->guid); | 
|  | if (rc < 0) | 
|  | return vzctl_err(-1, 0, "Error at WriteAttribute"); | 
|  | if (snapshot->current) { | 
|  | rc = xmlTextWriterWriteAttribute(writer, BAD_CAST "current", BAD_CAST "yes"); | 
|  | if (rc < 0) | 
|  | return vzctl_err(-1, 0, "Error at WriteAttribute"); | 
|  | } | 
|  | WRITE_ELEMENT("Name", snapshot->name) | 
|  | WRITE_ELEMENT("DateTime", snapshot->date) | 
|  | WRITE_ELEMENT("Creator", NULL) | 
|  | WRITE_ELEMENT("ScreenShot", NULL) | 
|  | xmlTextWriterStartElement(writer, BAD_CAST "Description"); | 
|  | xmlTextWriterWriteCDATA(writer, BAD_CAST (snapshot->desc ? snapshot->desc : "")); | 
|  | xmlTextWriterEndElement(writer); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int is_last_in_subtree(struct vzctl_snapshot_tree *tree, int snap_idx) | 
|  | { | 
|  | int i, max = -1; | 
|  |  | 
|  | for (i = 0; i < tree->nsnapshots; i++) { | 
|  | if (strcmp(tree->snapshots[i]->parent_guid, | 
|  | tree->snapshots[snap_idx]->parent_guid) == 0) | 
|  | max = i; | 
|  | } | 
|  | return (max == snap_idx); | 
|  | } | 
|  |  | 
|  | static void write_close_tag(xmlTextWriterPtr writer, | 
|  | struct vzctl_snapshot_tree *tree, const char *guid) | 
|  | { | 
|  | int i; | 
|  |  | 
|  | do { | 
|  | if (xmlTextWriterEndElement(writer) < 0) | 
|  | vzctl_err(-1, 0, "Error at xmlTextWriterEndElement"); | 
|  | if (strcmp(guid, "") == 0) | 
|  | break; | 
|  | i = vzctl_find_snapshot_by_guid(tree, guid); | 
|  | if (i == -1) | 
|  | break; | 
|  | guid = tree->snapshots[i]->parent_guid; | 
|  | } while (is_last_in_subtree(tree, i)); | 
|  | } | 
|  |  | 
|  | int vzctl_store_snapshot_tree(const char *fname, struct vzctl_snapshot_tree *tree) | 
|  | { | 
|  | int i, rc = -1; | 
|  | xmlTextWriterPtr writer = NULL; | 
|  | xmlDocPtr doc = NULL; | 
|  | char tmpfname[PATH_MAX]; | 
|  | struct xml_node_param *it; | 
|  | list_head_t pool; | 
|  | char *guid; | 
|  |  | 
|  | logger(0, 0, "Storing %s", fname); | 
|  | list_head_init(&pool); | 
|  | doc = xmlNewDoc(BAD_CAST XML_DEFAULT_VERSION); | 
|  | if (doc == NULL) | 
|  | return vzctl_err(-1, 0, "Error creating the xml document tree"); | 
|  | /* Create a new XmlWriter for DOM tree, with no compression. */ | 
|  | writer = xmlNewTextWriterTree(doc, NULL, 0); | 
|  | if (writer == NULL) { | 
|  | vzctl_err(-1, 0, "Error creating the xml writer"); | 
|  | goto err; | 
|  | } | 
|  |  | 
|  | /* Start the document with the xml default for the version, | 
|  | * encoding ISO 8859-1 and the default for the standalone | 
|  | * declaration. */ | 
|  | rc = xmlTextWriterStartDocument(writer, NULL, NULL, NULL); | 
|  | if (rc < 0) { | 
|  | vzctl_err(-1, 0, "Error at xmlTextWriterStartDocument"); | 
|  | goto err; | 
|  | } | 
|  | rc = xmlTextWriterStartElement(writer, BAD_CAST "ParallelsSavedStates"); | 
|  | if (rc < 0) { | 
|  | vzctl_err(-1, 0, "Error at ParallelsSavedStates"); | 
|  | goto err; | 
|  | } | 
|  | if (tree->nsnapshots == 0) { | 
|  | // ParallelsSavedStates | 
|  | xmlTextWriterEndElement(writer); | 
|  | goto out; | 
|  | } | 
|  | // add initial entry | 
|  | if (update_child_by_guid(tree, &pool, "") == -1) { | 
|  | rc = VZ_RESOURCE_ERROR; | 
|  | goto err; | 
|  | } | 
|  | rc = xmlTextWriterStartElement(writer, BAD_CAST "SavedStateItem"); | 
|  | if (rc < 0) { | 
|  | vzctl_err(-1, 0, "Error at ParallelsSavedStates"); | 
|  | goto err; | 
|  | } | 
|  | xmlTextWriterWriteAttribute(writer, BAD_CAST "guid", BAD_CAST ""); | 
|  | if (rc < 0) { | 
|  | vzctl_err(-1, 0, "Error at WriteAttribute"); | 
|  | goto err; | 
|  | } | 
|  | WRITE_ELEMENT("Name", NULL) | 
|  | WRITE_ELEMENT("DateTime", NULL) | 
|  | WRITE_ELEMENT("Creator", NULL) | 
|  | WRITE_ELEMENT("ScreenShot", NULL) | 
|  | WRITE_ELEMENT("Description", NULL) | 
|  |  | 
|  | while (!list_empty(&pool)) { | 
|  | list_for_each(it, &pool, list) { | 
|  | struct vzctl_snapshot_data *snapshot; | 
|  |  | 
|  | guid = it->p; | 
|  | i = vzctl_find_snapshot_by_guid(tree, guid); | 
|  | if (i == -1) { | 
|  | vzctl_err(-1, 0, "Inconsistent snapshot: no %s found", | 
|  | guid); | 
|  | goto err; | 
|  | } | 
|  | snapshot = tree->snapshots[i]; | 
|  | rc = write_SavedStateItem(writer, snapshot); | 
|  | if (rc) | 
|  | goto err; | 
|  | rc = update_child_by_guid(tree, &pool, guid); | 
|  | if (rc == -1) | 
|  | goto err; | 
|  | else if (rc == 0)// no more child | 
|  | write_close_tag(writer, tree, guid); | 
|  |  | 
|  | list_del(&it->list); | 
|  | free(it); | 
|  | break; | 
|  | } | 
|  | } | 
|  | out: | 
|  | // <SavedStateItem guid =""> | 
|  | xmlTextWriterEndElement(writer); | 
|  | // ParallelsSavedStates | 
|  | xmlTextWriterEndElement(writer); | 
|  |  | 
|  | xmlFreeTextWriter(writer); | 
|  | writer = NULL; | 
|  | snprintf(tmpfname, sizeof(tmpfname), "%s.tmp", fname); | 
|  | rc = xmlSaveFormatFile(tmpfname, doc, 1); | 
|  | if (rc < 0) { | 
|  | vzctl_err(-1, 0, "Error at xmlSaveFormatFile %s", tmpfname); | 
|  | goto err; | 
|  | } | 
|  | rc = rename(tmpfname, fname); | 
|  | if (rc) { | 
|  | vzctl_err(-1, errno, "Can't rename %s -> %s", | 
|  | tmpfname, fname); | 
|  | unlink(tmpfname); | 
|  | goto err; | 
|  | } | 
|  | rc = 0; | 
|  | err: | 
|  | free_node_list(&pool); | 
|  |  | 
|  | if (writer) | 
|  | xmlFreeTextWriter(writer); | 
|  | if (doc) | 
|  | xmlFreeDoc(doc); | 
|  |  | 
|  | return rc; | 
|  | } | 
|  |  | 
|  | int vzctl_read_snapshot_tree(const char *fname, struct vzctl_snapshot_tree *tree) | 
|  | { | 
|  | int ret; | 
|  | xmlDoc *doc = NULL; | 
|  | xmlNode *root_element = NULL; | 
|  |  | 
|  | LIBXML_TEST_VERSION | 
|  |  | 
|  | doc = xmlReadFile(fname, NULL, 0); | 
|  | if (doc == NULL) | 
|  | return vzctl_err(VZ_SYSTEM_ERROR, 0, "Error: could not parse file %s", fname); | 
|  |  | 
|  | root_element = xmlDocGetRootElement(doc); | 
|  |  | 
|  | ret = parse_xml(fname, root_element, tree); | 
|  |  | 
|  | xmlFreeDoc(doc); | 
|  | xmlCleanupParser(); | 
|  |  | 
|  | return ret; | 
|  | } |