mirror of
https://gitlab.com/libvirt/libvirt.git
synced 2024-11-02 03:11:12 +00:00
2f4682a9f8
virXPathNodeSet() could return -1 when doing an evaluation failure due to xmlXPathEval() from libxml2 behaviour. * src/util/xml.c: make sure we always return 0 unless the returned XPath type is of the wrong type (meaning the query passed didn't evaluate to a node set and code must be fixed)
517 lines
14 KiB
C
517 lines
14 KiB
C
/*
|
|
* xml.c: XML based interfaces for the libvir library
|
|
*
|
|
* Copyright (C) 2005, 2007-2009 Red Hat, Inc.
|
|
*
|
|
* See COPYING.LIB for the License of this software
|
|
*
|
|
* Daniel Veillard <veillard@redhat.com>
|
|
*/
|
|
|
|
#include <config.h>
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <stdarg.h>
|
|
#include <limits.h>
|
|
#include <math.h> /* for isnan() */
|
|
|
|
#include "virterror_internal.h"
|
|
#include "xml.h"
|
|
#include "buf.h"
|
|
#include "util.h"
|
|
#include "memory.h"
|
|
|
|
#define VIR_FROM_THIS VIR_FROM_XML
|
|
|
|
#define virXMLError(conn, code, fmt...) \
|
|
virReportErrorHelper(conn, VIR_FROM_XML, code, __FILE__, \
|
|
__FUNCTION__, __LINE__, fmt)
|
|
|
|
|
|
/************************************************************************
|
|
* *
|
|
* Wrappers around libxml2 XPath specific functions *
|
|
* *
|
|
************************************************************************/
|
|
|
|
/**
|
|
* virXPathString:
|
|
* @xpath: the XPath string to evaluate
|
|
* @ctxt: an XPath context
|
|
*
|
|
* Convenience function to evaluate an XPath string
|
|
*
|
|
* Returns a new string which must be deallocated by the caller or NULL
|
|
* if the evaluation failed.
|
|
*/
|
|
char *
|
|
virXPathString(virConnectPtr conn,
|
|
const char *xpath,
|
|
xmlXPathContextPtr ctxt)
|
|
{
|
|
xmlXPathObjectPtr obj;
|
|
xmlNodePtr relnode;
|
|
char *ret;
|
|
|
|
if ((ctxt == NULL) || (xpath == NULL)) {
|
|
virXMLError(conn, VIR_ERR_INTERNAL_ERROR,
|
|
"%s", _("Invalid parameter to virXPathString()"));
|
|
return (NULL);
|
|
}
|
|
relnode = ctxt->node;
|
|
obj = xmlXPathEval(BAD_CAST xpath, ctxt);
|
|
ctxt->node = relnode;
|
|
if ((obj == NULL) || (obj->type != XPATH_STRING) ||
|
|
(obj->stringval == NULL) || (obj->stringval[0] == 0)) {
|
|
xmlXPathFreeObject(obj);
|
|
return (NULL);
|
|
}
|
|
ret = strdup((char *) obj->stringval);
|
|
xmlXPathFreeObject(obj);
|
|
if (ret == NULL) {
|
|
virReportOOMError(conn);
|
|
}
|
|
return (ret);
|
|
}
|
|
|
|
/**
|
|
* virXPathStringLimit:
|
|
* @xpath: the XPath string to evaluate
|
|
* @maxlen: maximum length permittred string
|
|
* @ctxt: an XPath context
|
|
*
|
|
* Wrapper for virXPathString, which validates the length of the returned
|
|
* string.
|
|
*
|
|
* Returns a new string which must be deallocated by the caller or NULL if
|
|
* the evaluation failed.
|
|
*/
|
|
char *
|
|
virXPathStringLimit(virConnectPtr conn,
|
|
const char *xpath,
|
|
size_t maxlen,
|
|
xmlXPathContextPtr ctxt)
|
|
{
|
|
char *tmp = virXPathString(conn, xpath, ctxt);
|
|
|
|
if (tmp != NULL && strlen(tmp) >= maxlen) {
|
|
virXMLError(conn, VIR_ERR_INTERNAL_ERROR,
|
|
_("\'%s\' value longer than %Zd bytes in virXPathStringLimit()"),
|
|
xpath, maxlen);
|
|
return NULL;
|
|
}
|
|
|
|
return tmp;
|
|
}
|
|
|
|
/**
|
|
* virXPathNumber:
|
|
* @xpath: the XPath string to evaluate
|
|
* @ctxt: an XPath context
|
|
* @value: the returned double value
|
|
*
|
|
* Convenience function to evaluate an XPath number
|
|
*
|
|
* Returns 0 in case of success in which case @value is set,
|
|
* or -1 if the evaluation failed.
|
|
*/
|
|
int
|
|
virXPathNumber(virConnectPtr conn,
|
|
const char *xpath,
|
|
xmlXPathContextPtr ctxt,
|
|
double *value)
|
|
{
|
|
xmlXPathObjectPtr obj;
|
|
xmlNodePtr relnode;
|
|
|
|
if ((ctxt == NULL) || (xpath == NULL) || (value == NULL)) {
|
|
virXMLError(conn, VIR_ERR_INTERNAL_ERROR,
|
|
"%s", _("Invalid parameter to virXPathNumber()"));
|
|
return (-1);
|
|
}
|
|
relnode = ctxt->node;
|
|
obj = xmlXPathEval(BAD_CAST xpath, ctxt);
|
|
ctxt->node = relnode;
|
|
if ((obj == NULL) || (obj->type != XPATH_NUMBER) ||
|
|
(isnan(obj->floatval))) {
|
|
xmlXPathFreeObject(obj);
|
|
return (-1);
|
|
}
|
|
|
|
*value = obj->floatval;
|
|
xmlXPathFreeObject(obj);
|
|
return (0);
|
|
}
|
|
|
|
static int
|
|
virXPathLongBase(virConnectPtr conn,
|
|
const char *xpath,
|
|
xmlXPathContextPtr ctxt,
|
|
int base,
|
|
long *value)
|
|
{
|
|
xmlXPathObjectPtr obj;
|
|
xmlNodePtr relnode;
|
|
int ret = 0;
|
|
|
|
if ((ctxt == NULL) || (xpath == NULL) || (value == NULL)) {
|
|
virXMLError(conn, VIR_ERR_INTERNAL_ERROR,
|
|
"%s", _("Invalid parameter to virXPathLong()"));
|
|
return (-1);
|
|
}
|
|
relnode = ctxt->node;
|
|
obj = xmlXPathEval(BAD_CAST xpath, ctxt);
|
|
ctxt->node = relnode;
|
|
if ((obj != NULL) && (obj->type == XPATH_STRING) &&
|
|
(obj->stringval != NULL) && (obj->stringval[0] != 0)) {
|
|
char *conv = NULL;
|
|
long val;
|
|
|
|
val = strtol((const char *) obj->stringval, &conv, base);
|
|
if (conv == (const char *) obj->stringval) {
|
|
ret = -2;
|
|
} else {
|
|
*value = val;
|
|
}
|
|
} else if ((obj != NULL) && (obj->type == XPATH_NUMBER) &&
|
|
(!(isnan(obj->floatval)))) {
|
|
*value = (long) obj->floatval;
|
|
if (*value != obj->floatval) {
|
|
ret = -2;
|
|
}
|
|
} else {
|
|
ret = -1;
|
|
}
|
|
|
|
xmlXPathFreeObject(obj);
|
|
return (ret);
|
|
}
|
|
|
|
/**
|
|
* virXPathLong:
|
|
* @xpath: the XPath string to evaluate
|
|
* @ctxt: an XPath context
|
|
* @value: the returned long value
|
|
*
|
|
* Convenience function to evaluate an XPath number
|
|
*
|
|
* Returns 0 in case of success in which case @value is set,
|
|
* or -1 if the XPath evaluation failed or -2 if the
|
|
* value doesn't have a long format.
|
|
*/
|
|
int
|
|
virXPathLong(virConnectPtr conn,
|
|
const char *xpath,
|
|
xmlXPathContextPtr ctxt,
|
|
long *value)
|
|
{
|
|
return virXPathLongBase(conn, xpath, ctxt, 10, value);
|
|
}
|
|
|
|
/**
|
|
* virXPathLongHex:
|
|
* @xpath: the XPath string to evaluate
|
|
* @ctxt: an XPath context
|
|
* @value: the returned long value
|
|
*
|
|
* Convenience function to evaluate an XPath number
|
|
* according to a base of 16
|
|
*
|
|
* Returns 0 in case of success in which case @value is set,
|
|
* or -1 if the XPath evaluation failed or -2 if the
|
|
* value doesn't have a long format.
|
|
*/
|
|
int
|
|
virXPathLongHex(virConnectPtr conn,
|
|
const char *xpath,
|
|
xmlXPathContextPtr ctxt,
|
|
long *value)
|
|
{
|
|
return virXPathLongBase(conn, xpath, ctxt, 16, value);
|
|
}
|
|
|
|
static int
|
|
virXPathULongBase(virConnectPtr conn,
|
|
const char *xpath,
|
|
xmlXPathContextPtr ctxt,
|
|
int base,
|
|
unsigned long *value)
|
|
{
|
|
xmlXPathObjectPtr obj;
|
|
xmlNodePtr relnode;
|
|
int ret = 0;
|
|
|
|
if ((ctxt == NULL) || (xpath == NULL) || (value == NULL)) {
|
|
virXMLError(conn, VIR_ERR_INTERNAL_ERROR,
|
|
"%s", _("Invalid parameter to virXPathULong()"));
|
|
return (-1);
|
|
}
|
|
relnode = ctxt->node;
|
|
obj = xmlXPathEval(BAD_CAST xpath, ctxt);
|
|
ctxt->node = relnode;
|
|
if ((obj != NULL) && (obj->type == XPATH_STRING) &&
|
|
(obj->stringval != NULL) && (obj->stringval[0] != 0)) {
|
|
char *conv = NULL;
|
|
long val;
|
|
|
|
val = strtoul((const char *) obj->stringval, &conv, base);
|
|
if (conv == (const char *) obj->stringval) {
|
|
ret = -2;
|
|
} else {
|
|
*value = val;
|
|
}
|
|
} else if ((obj != NULL) && (obj->type == XPATH_NUMBER) &&
|
|
(!(isnan(obj->floatval)))) {
|
|
*value = (unsigned long) obj->floatval;
|
|
if (*value != obj->floatval) {
|
|
ret = -2;
|
|
}
|
|
} else {
|
|
ret = -1;
|
|
}
|
|
|
|
xmlXPathFreeObject(obj);
|
|
return (ret);
|
|
}
|
|
|
|
/**
|
|
* virXPathULong:
|
|
* @xpath: the XPath string to evaluate
|
|
* @ctxt: an XPath context
|
|
* @value: the returned long value
|
|
*
|
|
* Convenience function to evaluate an XPath number
|
|
*
|
|
* Returns 0 in case of success in which case @value is set,
|
|
* or -1 if the XPath evaluation failed or -2 if the
|
|
* value doesn't have a long format.
|
|
*/
|
|
int
|
|
virXPathULong(virConnectPtr conn,
|
|
const char *xpath,
|
|
xmlXPathContextPtr ctxt,
|
|
unsigned long *value)
|
|
{
|
|
return virXPathULongBase(conn, xpath, ctxt, 10, value);
|
|
}
|
|
|
|
/**
|
|
* virXPathUHex:
|
|
* @xpath: the XPath string to evaluate
|
|
* @ctxt: an XPath context
|
|
* @value: the returned long value
|
|
*
|
|
* Convenience function to evaluate an XPath number
|
|
* according to base of 16
|
|
*
|
|
* Returns 0 in case of success in which case @value is set,
|
|
* or -1 if the XPath evaluation failed or -2 if the
|
|
* value doesn't have a long format.
|
|
*/
|
|
int
|
|
virXPathULongHex(virConnectPtr conn,
|
|
const char *xpath,
|
|
xmlXPathContextPtr ctxt,
|
|
unsigned long *value)
|
|
{
|
|
return virXPathULongBase(conn, xpath, ctxt, 16, value);
|
|
}
|
|
|
|
/**
|
|
* virXPathULongLong:
|
|
* @xpath: the XPath string to evaluate
|
|
* @ctxt: an XPath context
|
|
* @value: the returned long long value
|
|
*
|
|
* Convenience function to evaluate an XPath number
|
|
*
|
|
* Returns 0 in case of success in which case @value is set,
|
|
* or -1 if the XPath evaluation failed or -2 if the
|
|
* value doesn't have a long format.
|
|
*/
|
|
int
|
|
virXPathULongLong(virConnectPtr conn,
|
|
const char *xpath,
|
|
xmlXPathContextPtr ctxt,
|
|
unsigned long long *value)
|
|
{
|
|
xmlXPathObjectPtr obj;
|
|
xmlNodePtr relnode;
|
|
int ret = 0;
|
|
|
|
if ((ctxt == NULL) || (xpath == NULL) || (value == NULL)) {
|
|
virXMLError(conn, VIR_ERR_INTERNAL_ERROR,
|
|
"%s", _("Invalid parameter to virXPathULong()"));
|
|
return (-1);
|
|
}
|
|
relnode = ctxt->node;
|
|
obj = xmlXPathEval(BAD_CAST xpath, ctxt);
|
|
ctxt->node = relnode;
|
|
if ((obj != NULL) && (obj->type == XPATH_STRING) &&
|
|
(obj->stringval != NULL) && (obj->stringval[0] != 0)) {
|
|
char *conv = NULL;
|
|
unsigned long long val;
|
|
|
|
val = strtoull((const char *) obj->stringval, &conv, 10);
|
|
if (conv == (const char *) obj->stringval) {
|
|
ret = -2;
|
|
} else {
|
|
*value = val;
|
|
}
|
|
} else if ((obj != NULL) && (obj->type == XPATH_NUMBER) &&
|
|
(!(isnan(obj->floatval)))) {
|
|
*value = (unsigned long long) obj->floatval;
|
|
if (*value != obj->floatval) {
|
|
ret = -2;
|
|
}
|
|
} else {
|
|
ret = -1;
|
|
}
|
|
|
|
xmlXPathFreeObject(obj);
|
|
return (ret);
|
|
}
|
|
|
|
char *
|
|
virXMLPropString(xmlNodePtr node,
|
|
const char *name)
|
|
{
|
|
return (char *)xmlGetProp(node, BAD_CAST name);
|
|
}
|
|
|
|
/**
|
|
* virXPathBoolean:
|
|
* @xpath: the XPath string to evaluate
|
|
* @ctxt: an XPath context
|
|
*
|
|
* Convenience function to evaluate an XPath boolean
|
|
*
|
|
* Returns 0 if false, 1 if true, or -1 if the evaluation failed.
|
|
*/
|
|
int
|
|
virXPathBoolean(virConnectPtr conn,
|
|
const char *xpath,
|
|
xmlXPathContextPtr ctxt)
|
|
{
|
|
xmlXPathObjectPtr obj;
|
|
xmlNodePtr relnode;
|
|
int ret;
|
|
|
|
if ((ctxt == NULL) || (xpath == NULL)) {
|
|
virXMLError(conn, VIR_ERR_INTERNAL_ERROR,
|
|
"%s", _("Invalid parameter to virXPathBoolean()"));
|
|
return (-1);
|
|
}
|
|
relnode = ctxt->node;
|
|
obj = xmlXPathEval(BAD_CAST xpath, ctxt);
|
|
ctxt->node = relnode;
|
|
if ((obj == NULL) || (obj->type != XPATH_BOOLEAN) ||
|
|
(obj->boolval < 0) || (obj->boolval > 1)) {
|
|
xmlXPathFreeObject(obj);
|
|
return (-1);
|
|
}
|
|
ret = obj->boolval;
|
|
|
|
xmlXPathFreeObject(obj);
|
|
return (ret);
|
|
}
|
|
|
|
/**
|
|
* virXPathNode:
|
|
* @xpath: the XPath string to evaluate
|
|
* @ctxt: an XPath context
|
|
*
|
|
* Convenience function to evaluate an XPath node set and returning
|
|
* only one node, the first one in the set if any
|
|
*
|
|
* Returns a pointer to the node or NULL if the evaluation failed.
|
|
*/
|
|
xmlNodePtr
|
|
virXPathNode(virConnectPtr conn,
|
|
const char *xpath,
|
|
xmlXPathContextPtr ctxt)
|
|
{
|
|
xmlXPathObjectPtr obj;
|
|
xmlNodePtr relnode;
|
|
xmlNodePtr ret;
|
|
|
|
if ((ctxt == NULL) || (xpath == NULL)) {
|
|
virXMLError(conn, VIR_ERR_INTERNAL_ERROR,
|
|
"%s", _("Invalid parameter to virXPathNode()"));
|
|
return (NULL);
|
|
}
|
|
relnode = ctxt->node;
|
|
obj = xmlXPathEval(BAD_CAST xpath, ctxt);
|
|
ctxt->node = relnode;
|
|
if ((obj == NULL) || (obj->type != XPATH_NODESET) ||
|
|
(obj->nodesetval == NULL) || (obj->nodesetval->nodeNr <= 0) ||
|
|
(obj->nodesetval->nodeTab == NULL)) {
|
|
xmlXPathFreeObject(obj);
|
|
return (NULL);
|
|
}
|
|
|
|
ret = obj->nodesetval->nodeTab[0];
|
|
xmlXPathFreeObject(obj);
|
|
return (ret);
|
|
}
|
|
|
|
/**
|
|
* virXPathNodeSet:
|
|
* @xpath: the XPath string to evaluate
|
|
* @ctxt: an XPath context
|
|
* @list: the returned list of nodes (or NULL if only count matters)
|
|
*
|
|
* Convenience function to evaluate an XPath node set
|
|
*
|
|
* Returns the number of nodes found in which case @list is set (and
|
|
* must be freed) or -1 if the evaluation failed.
|
|
*/
|
|
int
|
|
virXPathNodeSet(virConnectPtr conn,
|
|
const char *xpath,
|
|
xmlXPathContextPtr ctxt,
|
|
xmlNodePtr **list)
|
|
{
|
|
xmlXPathObjectPtr obj;
|
|
xmlNodePtr relnode;
|
|
int ret;
|
|
|
|
if ((ctxt == NULL) || (xpath == NULL)) {
|
|
virXMLError(conn, VIR_ERR_INTERNAL_ERROR,
|
|
"%s", _("Invalid parameter to virXPathNodeSet()"));
|
|
return (-1);
|
|
}
|
|
|
|
if (list != NULL)
|
|
*list = NULL;
|
|
|
|
relnode = ctxt->node;
|
|
obj = xmlXPathEval(BAD_CAST xpath, ctxt);
|
|
ctxt->node = relnode;
|
|
if (obj == NULL)
|
|
return(0);
|
|
if (obj->type != XPATH_NODESET) {
|
|
xmlXPathFreeObject(obj);
|
|
return (-1);
|
|
}
|
|
if ((obj->nodesetval == NULL) || (obj->nodesetval->nodeNr < 0)) {
|
|
xmlXPathFreeObject(obj);
|
|
return (0);
|
|
}
|
|
|
|
ret = obj->nodesetval->nodeNr;
|
|
if (list != NULL && ret) {
|
|
if (VIR_ALLOC_N(*list, ret) < 0) {
|
|
virReportOOMError(conn);
|
|
ret = -1;
|
|
} else {
|
|
memcpy(*list, obj->nodesetval->nodeTab,
|
|
ret * sizeof(xmlNodePtr));
|
|
}
|
|
}
|
|
xmlXPathFreeObject(obj);
|
|
return (ret);
|
|
}
|