blob: ad51e972c7152668ce30e6d5e1c4d13961fd1a6d [file] [log] [blame] [raw]
#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;
}