2008-02-20 15:34:52 +00:00
|
|
|
/*
|
|
|
|
* storage_conf.c: config handling for storage driver
|
|
|
|
*
|
|
|
|
* Copyright (C) 2006-2008 Red Hat, Inc.
|
|
|
|
* Copyright (C) 2006-2008 Daniel P. Berrange
|
|
|
|
*
|
|
|
|
* This library is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
|
|
* License as published by the Free Software Foundation; either
|
|
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This library 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
|
|
|
|
* Lesser General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
|
|
* License along with this library; if not, write to the Free Software
|
|
|
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
|
|
*
|
|
|
|
* Author: Daniel P. Berrange <berrange@redhat.com>
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <config.h>
|
|
|
|
|
|
|
|
#include <libxml/parser.h>
|
|
|
|
#include <libxml/tree.h>
|
|
|
|
#include <libxml/xpath.h>
|
|
|
|
#include <libxml/uri.h>
|
|
|
|
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <sys/types.h>
|
|
|
|
#include <sys/stat.h>
|
|
|
|
#include <dirent.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
#include <errno.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <fcntl.h>
|
|
|
|
#include <string.h>
|
|
|
|
|
Standardize use of header files, making internal.h primary.
* qemud/internal.h, qemud/qemud.h: Rename this file so it
doesn't conflict with src/internal.h.
* HACKING: Document how header files should be used.
* qemud/Makefile.am: Add src/ directory to includes.
* qemud/event.c, qemud/mdns.c, qemud/qemud.c, qemud/remote.c,
qemud/remote_protocol.c, qemud/remote_protocol.h,
qemud/remote_protocol.x, src/buf.c, src/libvirt.c,
src/nodeinfo.c, src/qemu_conf.c, src/qemu_driver.c,
src/stats_linux.c, src/storage_backend.c, src/storage_backend_fs.c,
src/storage_backend_iscsi.c, src/storage_backend_logical.c,
src/storage_conf.c, src/storage_driver.c, src/util.c,
src/util.h, src/virsh.c, src/virterror.c, src/xend_internal.c,
src/xml.c, tests/reconnect.c, tests/xmlrpctest.c,
tests/qparamtest.c: Standardize use of header files.
* docs/*, po/*: Rebuild docs.
2008-05-23 08:24:41 +00:00
|
|
|
#include "internal.h"
|
2008-02-20 15:34:52 +00:00
|
|
|
#include "storage_conf.h"
|
|
|
|
#include "storage_backend.h"
|
|
|
|
#include "xml.h"
|
|
|
|
#include "uuid.h"
|
|
|
|
#include "buf.h"
|
|
|
|
#include "util.h"
|
2008-06-06 11:09:57 +00:00
|
|
|
#include "memory.h"
|
2008-02-20 15:34:52 +00:00
|
|
|
|
|
|
|
#define virStorageLog(msg...) fprintf(stderr, msg)
|
|
|
|
|
|
|
|
void
|
|
|
|
virStorageVolDefFree(virStorageVolDefPtr def) {
|
|
|
|
int i;
|
2008-10-10 15:13:28 +00:00
|
|
|
|
|
|
|
if (!def)
|
|
|
|
return;
|
|
|
|
|
2008-06-06 11:09:57 +00:00
|
|
|
VIR_FREE(def->name);
|
|
|
|
VIR_FREE(def->key);
|
2008-02-20 15:34:52 +00:00
|
|
|
|
|
|
|
for (i = 0 ; i < def->source.nextent ; i++) {
|
2008-06-06 11:09:57 +00:00
|
|
|
VIR_FREE(def->source.extents[i].path);
|
2008-02-20 15:34:52 +00:00
|
|
|
}
|
2008-06-06 11:09:57 +00:00
|
|
|
VIR_FREE(def->source.extents);
|
2008-02-20 15:34:52 +00:00
|
|
|
|
2008-06-06 11:09:57 +00:00
|
|
|
VIR_FREE(def->target.path);
|
|
|
|
VIR_FREE(def->target.perms.label);
|
|
|
|
VIR_FREE(def);
|
2008-02-20 15:34:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
virStoragePoolDefFree(virStoragePoolDefPtr def) {
|
|
|
|
int i;
|
|
|
|
|
2008-10-10 15:13:28 +00:00
|
|
|
if (!def)
|
|
|
|
return;
|
|
|
|
|
2008-06-06 11:09:57 +00:00
|
|
|
VIR_FREE(def->name);
|
|
|
|
VIR_FREE(def->source.host.name);
|
2008-02-20 15:34:52 +00:00
|
|
|
for (i = 0 ; i < def->source.ndevice ; i++) {
|
2008-06-06 11:09:57 +00:00
|
|
|
VIR_FREE(def->source.devices[i].freeExtents);
|
|
|
|
VIR_FREE(def->source.devices[i].path);
|
2008-02-20 15:34:52 +00:00
|
|
|
}
|
2008-06-06 11:09:57 +00:00
|
|
|
VIR_FREE(def->source.devices);
|
|
|
|
VIR_FREE(def->source.dir);
|
2008-09-02 14:15:42 +00:00
|
|
|
VIR_FREE(def->source.name);
|
2008-02-20 15:34:52 +00:00
|
|
|
|
|
|
|
if (def->source.authType == VIR_STORAGE_POOL_AUTH_CHAP) {
|
2008-06-06 11:09:57 +00:00
|
|
|
VIR_FREE(def->source.auth.chap.login);
|
|
|
|
VIR_FREE(def->source.auth.chap.passwd);
|
2008-02-20 15:34:52 +00:00
|
|
|
}
|
|
|
|
|
2008-06-06 11:09:57 +00:00
|
|
|
VIR_FREE(def->target.path);
|
|
|
|
VIR_FREE(def->target.perms.label);
|
|
|
|
VIR_FREE(def);
|
2008-02-20 15:34:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
virStoragePoolObjFree(virStoragePoolObjPtr obj) {
|
2008-10-10 15:13:28 +00:00
|
|
|
if (!obj)
|
|
|
|
return;
|
|
|
|
|
|
|
|
virStoragePoolDefFree(obj->def);
|
|
|
|
virStoragePoolDefFree(obj->newDef);
|
2008-02-20 15:34:52 +00:00
|
|
|
|
2008-06-06 11:09:57 +00:00
|
|
|
VIR_FREE(obj->configFile);
|
|
|
|
VIR_FREE(obj->autostartLink);
|
|
|
|
VIR_FREE(obj);
|
2008-02-20 15:34:52 +00:00
|
|
|
}
|
|
|
|
|
2008-10-10 15:13:28 +00:00
|
|
|
void virStoragePoolObjListFree(virStoragePoolObjListPtr pools)
|
|
|
|
{
|
|
|
|
unsigned int i;
|
|
|
|
for (i = 0 ; i < pools->count ; i++)
|
|
|
|
virStoragePoolObjFree(pools->objs[i]);
|
|
|
|
VIR_FREE(pools->objs);
|
|
|
|
pools->count = 0;
|
|
|
|
}
|
|
|
|
|
2008-02-20 15:34:52 +00:00
|
|
|
void
|
2008-10-10 15:13:28 +00:00
|
|
|
virStoragePoolObjRemove(virStoragePoolObjListPtr pools,
|
2008-02-20 15:34:52 +00:00
|
|
|
virStoragePoolObjPtr pool)
|
|
|
|
{
|
2008-10-10 15:13:28 +00:00
|
|
|
unsigned int i;
|
2008-02-20 15:34:52 +00:00
|
|
|
|
2008-10-10 15:13:28 +00:00
|
|
|
for (i = 0 ; i < pools->count ; i++) {
|
|
|
|
if (pools->objs[i] == pool) {
|
|
|
|
virStoragePoolObjFree(pools->objs[i]);
|
2008-02-20 15:34:52 +00:00
|
|
|
|
2008-10-10 15:13:28 +00:00
|
|
|
if (i < (pools->count - 1))
|
|
|
|
memmove(pools->objs + i, pools->objs + i + 1,
|
|
|
|
sizeof(*(pools->objs)) * (pools->count - (i + 1)));
|
2008-02-20 15:34:52 +00:00
|
|
|
|
2008-10-10 15:13:28 +00:00
|
|
|
if (VIR_REALLOC_N(pools->objs, pools->count - 1) < 0) {
|
|
|
|
; /* Failure to reduce memory allocation isn't fatal */
|
|
|
|
}
|
|
|
|
pools->count--;
|
2008-02-20 15:34:52 +00:00
|
|
|
|
2008-10-10 15:13:28 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2008-02-20 15:34:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
virStoragePoolDefParseAuthChap(virConnectPtr conn,
|
|
|
|
xmlXPathContextPtr ctxt,
|
|
|
|
virStoragePoolAuthChapPtr auth) {
|
2008-07-25 14:27:25 +00:00
|
|
|
auth->login = virXPathString(conn, "string(/pool/source/auth/@login)", ctxt);
|
2008-02-20 15:34:52 +00:00
|
|
|
if (auth->login == NULL) {
|
|
|
|
virStorageReportError(conn, VIR_ERR_XML_ERROR,
|
2008-02-22 16:26:13 +00:00
|
|
|
"%s", _("missing auth host attribute"));
|
2008-02-20 15:34:52 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2008-07-25 14:27:25 +00:00
|
|
|
auth->passwd = virXPathString(conn, "string(/pool/source/auth/@passwd)", ctxt);
|
2008-02-20 15:34:52 +00:00
|
|
|
if (auth->passwd == NULL) {
|
|
|
|
virStorageReportError(conn, VIR_ERR_XML_ERROR,
|
2008-02-22 16:26:13 +00:00
|
|
|
"%s", _("missing auth passwd attribute"));
|
2008-02-20 15:34:52 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
virStoragePoolDefParsePerms(virConnectPtr conn,
|
|
|
|
xmlXPathContextPtr ctxt,
|
|
|
|
virStoragePermsPtr perms) {
|
|
|
|
char *mode;
|
|
|
|
long v;
|
|
|
|
|
2008-07-25 14:27:25 +00:00
|
|
|
mode = virXPathString(conn, "string(/pool/permissions/mode)", ctxt);
|
2008-02-20 15:34:52 +00:00
|
|
|
if (!mode) {
|
|
|
|
perms->mode = 0700;
|
|
|
|
} else {
|
|
|
|
char *end;
|
|
|
|
perms->mode = strtol(mode, &end, 8);
|
|
|
|
if (*end || perms->mode < 0 || perms->mode > 0777) {
|
|
|
|
virStorageReportError(conn, VIR_ERR_XML_ERROR,
|
2008-02-22 16:26:13 +00:00
|
|
|
"%s", _("malformed octal mode"));
|
2008-02-20 15:34:52 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-07-25 14:27:25 +00:00
|
|
|
if (virXPathNode(conn, "/pool/permissions/owner", ctxt) == NULL) {
|
2008-02-20 15:34:52 +00:00
|
|
|
perms->uid = getuid();
|
|
|
|
} else {
|
2008-07-25 14:27:25 +00:00
|
|
|
if (virXPathLong(conn, "number(/pool/permissions/owner)", ctxt, &v) < 0) {
|
2008-02-20 15:34:52 +00:00
|
|
|
virStorageReportError(conn, VIR_ERR_XML_ERROR,
|
2008-02-22 16:26:13 +00:00
|
|
|
"%s", _("malformed owner element"));
|
2008-02-20 15:34:52 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
perms->uid = (int)v;
|
|
|
|
}
|
|
|
|
|
2008-07-25 14:27:25 +00:00
|
|
|
if (virXPathNode(conn, "/pool/permissions/group", ctxt) == NULL) {
|
2008-09-05 12:03:45 +00:00
|
|
|
perms->gid = getgid();
|
2008-02-20 15:34:52 +00:00
|
|
|
} else {
|
2008-07-25 14:27:25 +00:00
|
|
|
if (virXPathLong(conn, "number(/pool/permissions/group)", ctxt, &v) < 0) {
|
2008-02-20 15:34:52 +00:00
|
|
|
virStorageReportError(conn, VIR_ERR_XML_ERROR,
|
2008-02-22 16:26:13 +00:00
|
|
|
"%s", _("malformed group element"));
|
2008-02-20 15:34:52 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
perms->gid = (int)v;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* NB, we're ignoring missing labels here - they'll simply inherit */
|
2008-07-25 14:27:25 +00:00
|
|
|
perms->label = virXPathString(conn, "string(/pool/permissions/label)", ctxt);
|
2008-02-20 15:34:52 +00:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static virStoragePoolDefPtr
|
|
|
|
virStoragePoolDefParseDoc(virConnectPtr conn,
|
|
|
|
xmlXPathContextPtr ctxt,
|
|
|
|
xmlNodePtr root) {
|
|
|
|
virStorageBackendPoolOptionsPtr options;
|
|
|
|
virStoragePoolDefPtr ret;
|
|
|
|
xmlChar *type = NULL;
|
|
|
|
char *uuid = NULL;
|
|
|
|
char *authType = NULL;
|
|
|
|
|
2008-06-06 11:09:57 +00:00
|
|
|
if (VIR_ALLOC(ret) < 0) {
|
|
|
|
virStorageReportError(conn, VIR_ERR_NO_MEMORY,
|
|
|
|
"%s", _("cannot allocate storage pool"));
|
2008-02-20 15:34:52 +00:00
|
|
|
return NULL;
|
2008-06-06 11:09:57 +00:00
|
|
|
}
|
2008-02-20 15:34:52 +00:00
|
|
|
|
|
|
|
if (STRNEQ((const char *)root->name, "pool")) {
|
|
|
|
virStorageReportError(conn, VIR_ERR_XML_ERROR,
|
2008-09-02 14:15:42 +00:00
|
|
|
"%s", _("unknown root elementi for storage pool"));
|
2008-02-20 15:34:52 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
type = xmlGetProp(root, BAD_CAST "type");
|
|
|
|
if ((ret->type = virStorageBackendFromString((const char *)type)) < 0)
|
|
|
|
goto cleanup;
|
|
|
|
xmlFree(type);
|
|
|
|
type = NULL;
|
|
|
|
|
|
|
|
if ((options = virStorageBackendPoolOptionsForType(ret->type)) == NULL) {
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
2008-09-02 14:15:42 +00:00
|
|
|
ret->name = virXPathString(conn, "string(/pool/name)", ctxt);
|
|
|
|
if (ret->name == NULL &&
|
|
|
|
options->flags & VIR_STORAGE_BACKEND_POOL_SOURCE_NAME)
|
|
|
|
ret->name = virXPathString(conn, "string(/pool/source/name)", ctxt);
|
|
|
|
if (ret->name == NULL) {
|
2008-02-20 15:34:52 +00:00
|
|
|
virStorageReportError(conn, VIR_ERR_XML_ERROR,
|
2008-09-02 14:15:42 +00:00
|
|
|
"%s", _("missing pool source name element"));
|
2008-02-20 15:34:52 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
2008-07-25 14:27:25 +00:00
|
|
|
uuid = virXPathString(conn, "string(/pool/uuid)", ctxt);
|
2008-02-20 15:34:52 +00:00
|
|
|
if (uuid == NULL) {
|
|
|
|
if (virUUIDGenerate(ret->uuid) < 0) {
|
|
|
|
virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR,
|
2008-02-22 16:26:13 +00:00
|
|
|
"%s", _("unable to generate uuid"));
|
2008-02-20 15:34:52 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (virUUIDParse(uuid, ret->uuid) < 0) {
|
|
|
|
virStorageReportError(conn, VIR_ERR_XML_ERROR,
|
2008-02-22 16:26:13 +00:00
|
|
|
"%s", _("malformed uuid element"));
|
2008-02-20 15:34:52 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
2008-06-06 11:09:57 +00:00
|
|
|
VIR_FREE(uuid);
|
2008-02-20 15:34:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (options->formatFromString) {
|
2008-07-25 14:27:25 +00:00
|
|
|
char *format = virXPathString(conn, "string(/pool/source/format/@type)", ctxt);
|
2008-10-16 15:06:03 +00:00
|
|
|
if ((ret->source.format = (options->formatFromString)(format)) < 0) {
|
2008-10-21 17:18:45 +00:00
|
|
|
virStorageReportError(conn, VIR_ERR_XML_ERROR,
|
|
|
|
_("unknown pool format type %s"), format);
|
2008-06-06 11:09:57 +00:00
|
|
|
VIR_FREE(format);
|
2008-02-20 15:34:52 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
2008-06-06 11:09:57 +00:00
|
|
|
VIR_FREE(format);
|
2008-02-20 15:34:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (options->flags & VIR_STORAGE_BACKEND_POOL_SOURCE_HOST) {
|
2008-07-25 14:27:25 +00:00
|
|
|
if ((ret->source.host.name = virXPathString(conn, "string(/pool/source/host/@name)", ctxt)) == NULL) {
|
2008-02-20 15:34:52 +00:00
|
|
|
virStorageReportError(conn, VIR_ERR_XML_ERROR,
|
2008-09-02 14:15:42 +00:00
|
|
|
"%s", _("missing storage pool source host name"));
|
2008-02-20 15:34:52 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (options->flags & VIR_STORAGE_BACKEND_POOL_SOURCE_DEVICE) {
|
|
|
|
xmlNodePtr *nodeset = NULL;
|
|
|
|
int nsource, i;
|
|
|
|
|
2008-09-22 19:57:47 +00:00
|
|
|
if ((nsource = virXPathNodeSet(conn, "/pool/source/device", ctxt, &nodeset)) < 0) {
|
2008-02-20 15:34:52 +00:00
|
|
|
virStorageReportError(conn, VIR_ERR_XML_ERROR,
|
2008-09-02 14:15:42 +00:00
|
|
|
"%s", _("cannot extract storage pool source devices"));
|
2008-02-20 15:34:52 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
2008-06-06 11:09:57 +00:00
|
|
|
if (VIR_ALLOC_N(ret->source.devices, nsource) < 0) {
|
|
|
|
VIR_FREE(nodeset);
|
2008-02-22 16:26:13 +00:00
|
|
|
virStorageReportError(conn, VIR_ERR_NO_MEMORY, "%s", _("device"));
|
2008-02-20 15:34:52 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
for (i = 0 ; i < nsource ; i++) {
|
|
|
|
xmlChar *path = xmlGetProp(nodeset[i], BAD_CAST "path");
|
|
|
|
if (path == NULL) {
|
2008-06-06 11:09:57 +00:00
|
|
|
VIR_FREE(nodeset);
|
2008-02-20 15:34:52 +00:00
|
|
|
virStorageReportError(conn, VIR_ERR_XML_ERROR,
|
2008-09-02 14:15:42 +00:00
|
|
|
"%s", _("missing storage pool source device path"));
|
2008-02-20 15:34:52 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
ret->source.devices[i].path = (char *)path;
|
|
|
|
}
|
2008-06-06 11:09:57 +00:00
|
|
|
VIR_FREE(nodeset);
|
2008-02-20 15:34:52 +00:00
|
|
|
ret->source.ndevice = nsource;
|
|
|
|
}
|
|
|
|
if (options->flags & VIR_STORAGE_BACKEND_POOL_SOURCE_DIR) {
|
2008-07-25 14:27:25 +00:00
|
|
|
if ((ret->source.dir = virXPathString(conn, "string(/pool/source/dir/@path)", ctxt)) == NULL) {
|
2008-02-20 15:34:52 +00:00
|
|
|
virStorageReportError(conn, VIR_ERR_XML_ERROR,
|
2008-09-02 14:15:42 +00:00
|
|
|
"%s", _("missing storage pool source path"));
|
2008-02-20 15:34:52 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
}
|
2008-09-02 14:15:42 +00:00
|
|
|
if (options->flags & VIR_STORAGE_BACKEND_POOL_SOURCE_NAME) {
|
|
|
|
ret->source.name = virXPathString(conn, "string(/pool/source/name)",
|
|
|
|
ctxt);
|
|
|
|
if (ret->source.name == NULL) {
|
|
|
|
/* source name defaults to pool name */
|
|
|
|
ret->source.name = strdup(ret->name);
|
2008-09-03 07:12:37 +00:00
|
|
|
if (ret->source.name == NULL) {
|
|
|
|
virStorageReportError(conn, VIR_ERR_NO_MEMORY, "%s",
|
|
|
|
_("pool name"));
|
|
|
|
goto cleanup;
|
|
|
|
}
|
2008-09-02 14:15:42 +00:00
|
|
|
}
|
|
|
|
}
|
2008-02-20 15:34:52 +00:00
|
|
|
|
|
|
|
|
2008-07-25 14:27:25 +00:00
|
|
|
authType = virXPathString(conn, "string(/pool/source/auth/@type)", ctxt);
|
2008-02-20 15:34:52 +00:00
|
|
|
if (authType == NULL) {
|
|
|
|
ret->source.authType = VIR_STORAGE_POOL_AUTH_NONE;
|
|
|
|
} else {
|
|
|
|
if (STREQ(authType, "chap")) {
|
|
|
|
ret->source.authType = VIR_STORAGE_POOL_AUTH_CHAP;
|
|
|
|
} else {
|
|
|
|
virStorageReportError(conn, VIR_ERR_XML_ERROR,
|
|
|
|
_("unknown auth type '%s'"),
|
|
|
|
(const char *)authType);
|
2008-06-06 11:09:57 +00:00
|
|
|
VIR_FREE(authType);
|
2008-02-20 15:34:52 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
2008-06-06 11:09:57 +00:00
|
|
|
VIR_FREE(authType);
|
2008-02-20 15:34:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (ret->source.authType == VIR_STORAGE_POOL_AUTH_CHAP) {
|
|
|
|
if (virStoragePoolDefParseAuthChap(conn, ctxt, &ret->source.auth.chap) < 0)
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
2008-07-25 14:27:25 +00:00
|
|
|
if ((ret->target.path = virXPathString(conn, "string(/pool/target/path)", ctxt)) == NULL) {
|
2008-02-20 15:34:52 +00:00
|
|
|
virStorageReportError(conn, VIR_ERR_XML_ERROR,
|
2008-09-02 14:15:42 +00:00
|
|
|
"%s", _("missing storage pool target path"));
|
2008-02-20 15:34:52 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (virStoragePoolDefParsePerms(conn, ctxt, &ret->target.perms) < 0)
|
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
cleanup:
|
2008-06-06 11:09:57 +00:00
|
|
|
VIR_FREE(uuid);
|
2008-04-29 19:43:57 +00:00
|
|
|
xmlFree(type);
|
2008-02-20 15:34:52 +00:00
|
|
|
virStoragePoolDefFree(ret);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2008-08-01 09:39:44 +00:00
|
|
|
/* Called from SAX on parsing errors in the XML. */
|
|
|
|
static void
|
|
|
|
catchXMLError (void *ctx, const char *msg ATTRIBUTE_UNUSED, ...)
|
|
|
|
{
|
|
|
|
xmlParserCtxtPtr ctxt = (xmlParserCtxtPtr) ctx;
|
|
|
|
|
|
|
|
if (ctxt) {
|
|
|
|
virConnectPtr conn = ctxt->_private;
|
|
|
|
|
|
|
|
if (conn &&
|
|
|
|
conn->err.code == VIR_ERR_NONE &&
|
|
|
|
ctxt->lastError.level == XML_ERR_FATAL &&
|
|
|
|
ctxt->lastError.message != NULL) {
|
|
|
|
virStorageReportError (conn, VIR_ERR_XML_DETAIL,
|
|
|
|
_("at line %d: %s"),
|
|
|
|
ctxt->lastError.line,
|
|
|
|
ctxt->lastError.message);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-02-20 15:34:52 +00:00
|
|
|
virStoragePoolDefPtr
|
|
|
|
virStoragePoolDefParse(virConnectPtr conn,
|
|
|
|
const char *xmlStr,
|
|
|
|
const char *filename) {
|
|
|
|
virStoragePoolDefPtr ret = NULL;
|
2008-08-01 09:39:44 +00:00
|
|
|
xmlParserCtxtPtr pctxt;
|
2008-02-20 15:34:52 +00:00
|
|
|
xmlDocPtr xml = NULL;
|
|
|
|
xmlNodePtr node = NULL;
|
|
|
|
xmlXPathContextPtr ctxt = NULL;
|
|
|
|
|
2008-08-01 09:39:44 +00:00
|
|
|
/* Set up a parser context so we can catch the details of XML errors. */
|
|
|
|
pctxt = xmlNewParserCtxt ();
|
|
|
|
if (!pctxt || !pctxt->sax)
|
|
|
|
goto cleanup;
|
|
|
|
pctxt->sax->error = catchXMLError;
|
|
|
|
pctxt->_private = conn;
|
|
|
|
|
|
|
|
if (conn) virResetError (&conn->err);
|
|
|
|
xml = xmlCtxtReadDoc (pctxt, BAD_CAST xmlStr,
|
|
|
|
filename ? filename : "storage.xml", NULL,
|
|
|
|
XML_PARSE_NOENT | XML_PARSE_NONET |
|
|
|
|
XML_PARSE_NOWARNING);
|
|
|
|
if (!xml) {
|
|
|
|
if (conn && conn->err.code == VIR_ERR_NONE)
|
|
|
|
virStorageReportError(conn, VIR_ERR_XML_ERROR,
|
2008-09-05 12:03:45 +00:00
|
|
|
"%s",_("failed to parse xml document"));
|
2008-02-20 15:34:52 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
ctxt = xmlXPathNewContext(xml);
|
|
|
|
if (ctxt == NULL) {
|
|
|
|
virStorageReportError(conn, VIR_ERR_NO_MEMORY,
|
2008-02-22 16:26:13 +00:00
|
|
|
"%s", _("xmlXPathContext"));
|
2008-02-20 15:34:52 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
node = xmlDocGetRootElement(xml);
|
|
|
|
if (node == NULL) {
|
|
|
|
virStorageReportError(conn, VIR_ERR_XML_ERROR,
|
2008-02-22 16:26:13 +00:00
|
|
|
"%s", _("missing root element"));
|
2008-02-20 15:34:52 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = virStoragePoolDefParseDoc(conn, ctxt, node);
|
|
|
|
|
2008-08-01 09:39:44 +00:00
|
|
|
xmlFreeParserCtxt (pctxt);
|
2008-02-20 15:34:52 +00:00
|
|
|
xmlXPathFreeContext(ctxt);
|
|
|
|
xmlFreeDoc(xml);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
cleanup:
|
2008-08-01 09:39:44 +00:00
|
|
|
xmlFreeParserCtxt (pctxt);
|
2008-02-20 15:34:52 +00:00
|
|
|
xmlXPathFreeContext(ctxt);
|
|
|
|
xmlFreeDoc(xml);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
char *
|
|
|
|
virStoragePoolDefFormat(virConnectPtr conn,
|
|
|
|
virStoragePoolDefPtr def) {
|
|
|
|
virStorageBackendPoolOptionsPtr options;
|
2008-04-28 15:14:59 +00:00
|
|
|
virBuffer buf = VIR_BUFFER_INITIALIZER;
|
2008-02-20 15:34:52 +00:00
|
|
|
const char *type;
|
|
|
|
char uuid[VIR_UUID_STRING_BUFLEN];
|
|
|
|
int i;
|
|
|
|
|
|
|
|
options = virStorageBackendPoolOptionsForType(def->type);
|
|
|
|
if (options == NULL)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
type = virStorageBackendToString(def->type);
|
|
|
|
if (!type) {
|
|
|
|
virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR,
|
2008-02-22 16:26:13 +00:00
|
|
|
"%s", _("unexpected pool type"));
|
2008-02-20 15:34:52 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
2008-04-28 15:14:59 +00:00
|
|
|
virBufferVSprintf(&buf, "<pool type='%s'>\n", type);
|
|
|
|
virBufferVSprintf(&buf," <name>%s</name>\n", def->name);
|
2008-02-20 15:34:52 +00:00
|
|
|
|
|
|
|
virUUIDFormat(def->uuid, uuid);
|
2008-04-28 15:14:59 +00:00
|
|
|
virBufferVSprintf(&buf," <uuid>%s</uuid>\n", uuid);
|
2008-02-20 15:34:52 +00:00
|
|
|
|
2008-04-28 15:14:59 +00:00
|
|
|
virBufferVSprintf(&buf," <capacity>%llu</capacity>\n",
|
|
|
|
def->capacity);
|
|
|
|
virBufferVSprintf(&buf," <allocation>%llu</allocation>\n",
|
|
|
|
def->allocation);
|
|
|
|
virBufferVSprintf(&buf," <available>%llu</available>\n",
|
|
|
|
def->available);
|
2008-02-20 15:34:52 +00:00
|
|
|
|
2008-04-28 15:14:59 +00:00
|
|
|
virBufferAddLit(&buf," <source>\n");
|
2008-02-20 15:34:52 +00:00
|
|
|
if ((options->flags & VIR_STORAGE_BACKEND_POOL_SOURCE_HOST) &&
|
2008-04-28 15:14:59 +00:00
|
|
|
def->source.host.name)
|
|
|
|
virBufferVSprintf(&buf," <host name='%s'/>\n", def->source.host.name);
|
|
|
|
|
2008-02-20 15:34:52 +00:00
|
|
|
if ((options->flags & VIR_STORAGE_BACKEND_POOL_SOURCE_DEVICE) &&
|
|
|
|
def->source.ndevice) {
|
|
|
|
for (i = 0 ; i < def->source.ndevice ; i++) {
|
2008-04-28 15:14:59 +00:00
|
|
|
virBufferVSprintf(&buf," <device path='%s'>\n", def->source.devices[i].path);
|
2008-02-20 15:34:52 +00:00
|
|
|
if (def->source.devices[i].nfreeExtent) {
|
|
|
|
int j;
|
|
|
|
for (j = 0 ; j < def->source.devices[i].nfreeExtent ; j++) {
|
2008-04-28 15:14:59 +00:00
|
|
|
virBufferVSprintf(&buf, " <freeExtent start='%llu' end='%llu'/>\n",
|
|
|
|
def->source.devices[i].freeExtents[j].start,
|
|
|
|
def->source.devices[i].freeExtents[j].end);
|
2008-02-20 15:34:52 +00:00
|
|
|
}
|
|
|
|
}
|
2008-04-28 15:14:59 +00:00
|
|
|
virBufferAddLit(&buf," </device>\n");
|
2008-02-20 15:34:52 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
if ((options->flags & VIR_STORAGE_BACKEND_POOL_SOURCE_DIR) &&
|
2008-04-28 15:14:59 +00:00
|
|
|
def->source.dir)
|
|
|
|
virBufferVSprintf(&buf," <dir path='%s'/>\n", def->source.dir);
|
2008-02-20 15:34:52 +00:00
|
|
|
if ((options->flags & VIR_STORAGE_BACKEND_POOL_SOURCE_ADAPTER) &&
|
2008-04-28 15:14:59 +00:00
|
|
|
def->source.adapter)
|
|
|
|
virBufferVSprintf(&buf," <adapter name='%s'/>\n", def->source.adapter);
|
2008-09-02 14:15:42 +00:00
|
|
|
if ((options->flags & VIR_STORAGE_BACKEND_POOL_SOURCE_NAME) &&
|
|
|
|
def->source.name)
|
|
|
|
virBufferVSprintf(&buf," <name>%s</name>\n", def->source.name);
|
2008-02-20 15:34:52 +00:00
|
|
|
|
|
|
|
if (options->formatToString) {
|
2008-10-16 15:06:03 +00:00
|
|
|
const char *format = (options->formatToString)(def->source.format);
|
2008-10-21 17:18:45 +00:00
|
|
|
if (!format) {
|
|
|
|
virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("unknown pool format number %d"),
|
|
|
|
def->source.format);
|
2008-02-20 15:34:52 +00:00
|
|
|
goto cleanup;
|
2008-10-21 17:18:45 +00:00
|
|
|
}
|
2008-04-28 15:14:59 +00:00
|
|
|
virBufferVSprintf(&buf," <format type='%s'/>\n", format);
|
2008-02-20 15:34:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-04-28 15:14:59 +00:00
|
|
|
if (def->source.authType == VIR_STORAGE_POOL_AUTH_CHAP)
|
|
|
|
virBufferVSprintf(&buf," <auth type='chap' login='%s' passwd='%s'>\n",
|
2008-02-20 15:34:52 +00:00
|
|
|
def->source.auth.chap.login,
|
2008-04-28 15:14:59 +00:00
|
|
|
def->source.auth.chap.passwd);
|
|
|
|
virBufferAddLit(&buf," </source>\n");
|
2008-02-20 15:34:52 +00:00
|
|
|
|
2008-04-28 15:14:59 +00:00
|
|
|
virBufferAddLit(&buf," <target>\n");
|
2008-02-20 15:34:52 +00:00
|
|
|
|
2008-04-28 15:14:59 +00:00
|
|
|
if (def->target.path)
|
|
|
|
virBufferVSprintf(&buf," <path>%s</path>\n", def->target.path);
|
2008-02-20 15:34:52 +00:00
|
|
|
|
2008-04-28 15:14:59 +00:00
|
|
|
virBufferAddLit(&buf," <permissions>\n");
|
|
|
|
virBufferVSprintf(&buf," <mode>0%o</mode>\n",
|
|
|
|
def->target.perms.mode);
|
|
|
|
virBufferVSprintf(&buf," <owner>%d</owner>\n",
|
|
|
|
def->target.perms.uid);
|
|
|
|
virBufferVSprintf(&buf," <group>%d</group>\n",
|
|
|
|
def->target.perms.gid);
|
2008-02-20 15:34:52 +00:00
|
|
|
|
2008-04-28 15:14:59 +00:00
|
|
|
if (def->target.perms.label)
|
|
|
|
virBufferVSprintf(&buf," <label>%s</label>\n",
|
|
|
|
def->target.perms.label);
|
2008-02-20 15:34:52 +00:00
|
|
|
|
2008-04-28 15:14:59 +00:00
|
|
|
virBufferAddLit(&buf," </permissions>\n");
|
|
|
|
virBufferAddLit(&buf," </target>\n");
|
|
|
|
virBufferAddLit(&buf,"</pool>\n");
|
2008-02-20 15:34:52 +00:00
|
|
|
|
2008-04-28 15:14:59 +00:00
|
|
|
if (virBufferError(&buf))
|
2008-02-20 15:34:52 +00:00
|
|
|
goto no_memory;
|
|
|
|
|
2008-04-28 15:14:59 +00:00
|
|
|
return virBufferContentAndReset(&buf);
|
2008-02-20 15:34:52 +00:00
|
|
|
|
|
|
|
no_memory:
|
2008-02-22 16:26:13 +00:00
|
|
|
virStorageReportError(conn, VIR_ERR_NO_MEMORY, "%s", _("xml"));
|
2008-02-20 15:34:52 +00:00
|
|
|
cleanup:
|
2008-04-28 15:14:59 +00:00
|
|
|
free(virBufferContentAndReset(&buf));
|
2008-02-20 15:34:52 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
virStorageVolDefParsePerms(virConnectPtr conn,
|
|
|
|
xmlXPathContextPtr ctxt,
|
|
|
|
virStoragePermsPtr perms) {
|
|
|
|
char *mode;
|
|
|
|
long v;
|
|
|
|
|
2008-07-25 14:27:25 +00:00
|
|
|
mode = virXPathString(conn, "string(/volume/permissions/mode)", ctxt);
|
2008-02-20 15:34:52 +00:00
|
|
|
if (!mode) {
|
|
|
|
perms->mode = 0600;
|
|
|
|
} else {
|
|
|
|
char *end = NULL;
|
|
|
|
perms->mode = strtol(mode, &end, 8);
|
|
|
|
if (*end || perms->mode < 0 || perms->mode > 0777) {
|
|
|
|
virStorageReportError(conn, VIR_ERR_XML_ERROR,
|
2008-02-22 16:26:13 +00:00
|
|
|
"%s", _("malformed octal mode"));
|
2008-02-20 15:34:52 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-07-25 14:27:25 +00:00
|
|
|
if (virXPathNode(conn, "/volume/permissions/owner", ctxt) == NULL) {
|
2008-02-20 15:34:52 +00:00
|
|
|
perms->uid = getuid();
|
|
|
|
} else {
|
2008-07-25 14:27:25 +00:00
|
|
|
if (virXPathLong(conn, "number(/volume/permissions/owner)", ctxt, &v) < 0) {
|
2008-02-20 15:34:52 +00:00
|
|
|
virStorageReportError(conn, VIR_ERR_XML_ERROR,
|
2008-02-22 16:26:13 +00:00
|
|
|
"%s", _("missing owner element"));
|
2008-02-20 15:34:52 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
perms->uid = (int)v;
|
|
|
|
}
|
2008-07-25 14:27:25 +00:00
|
|
|
if (virXPathNode(conn, "/volume/permissions/group", ctxt) == NULL) {
|
2008-02-20 15:34:52 +00:00
|
|
|
perms->gid = getgid();
|
|
|
|
} else {
|
2008-07-25 14:27:25 +00:00
|
|
|
if (virXPathLong(conn, "number(/volume/permissions/group)", ctxt, &v) < 0) {
|
2008-02-20 15:34:52 +00:00
|
|
|
virStorageReportError(conn, VIR_ERR_XML_ERROR,
|
2008-02-22 16:26:13 +00:00
|
|
|
"%s", _("missing owner element"));
|
2008-02-20 15:34:52 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
perms->gid = (int)v;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* NB, we're ignoring missing labels here - they'll simply inherit */
|
2008-07-25 14:27:25 +00:00
|
|
|
perms->label = virXPathString(conn, "string(/volume/permissions/label)", ctxt);
|
2008-02-20 15:34:52 +00:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
virStorageSize(virConnectPtr conn,
|
|
|
|
const char *unit,
|
|
|
|
const char *val,
|
|
|
|
unsigned long long *ret) {
|
|
|
|
unsigned long long mult;
|
|
|
|
char *end;
|
|
|
|
|
|
|
|
if (!unit) {
|
|
|
|
mult = 1;
|
|
|
|
} else {
|
|
|
|
switch (unit[0]) {
|
|
|
|
case 'k':
|
|
|
|
case 'K':
|
|
|
|
mult = 1024ull;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 'm':
|
|
|
|
case 'M':
|
|
|
|
mult = 1024ull * 1024ull;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 'g':
|
|
|
|
case 'G':
|
|
|
|
mult = 1024ull * 1024ull * 1024ull;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 't':
|
|
|
|
case 'T':
|
|
|
|
mult = 1024ull * 1024ull * 1024ull * 1024ull;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 'p':
|
|
|
|
case 'P':
|
|
|
|
mult = 1024ull * 1024ull * 1024ull * 1024ull * 1024ull;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 'y':
|
|
|
|
case 'Y':
|
|
|
|
mult = 1024ull * 1024ull * 1024ull * 1024ull * 1024ull *
|
|
|
|
1024ull;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 'z':
|
|
|
|
case 'Z':
|
|
|
|
mult = 1024ull * 1024ull * 1024ull * 1024ull * 1024ull *
|
|
|
|
1024ull * 1024ull;
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
virStorageReportError(conn, VIR_ERR_XML_ERROR,
|
|
|
|
_("unknown size units '%s'"), unit);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (virStrToLong_ull (val, &end, 10, ret) < 0) {
|
|
|
|
virStorageReportError(conn, VIR_ERR_XML_ERROR,
|
2008-02-22 16:26:13 +00:00
|
|
|
"%s", _("malformed capacity element"));
|
2008-02-20 15:34:52 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
if (*ret > (ULLONG_MAX / mult)) {
|
|
|
|
virStorageReportError(conn, VIR_ERR_XML_ERROR,
|
2008-02-22 16:26:13 +00:00
|
|
|
"%s", _("capacity element value too large"));
|
2008-02-20 15:34:52 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
*ret *= mult;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static virStorageVolDefPtr
|
|
|
|
virStorageVolDefParseDoc(virConnectPtr conn,
|
|
|
|
virStoragePoolDefPtr pool,
|
|
|
|
xmlXPathContextPtr ctxt,
|
|
|
|
xmlNodePtr root) {
|
|
|
|
virStorageVolDefPtr ret;
|
|
|
|
virStorageBackendVolOptionsPtr options;
|
|
|
|
char *allocation = NULL;
|
|
|
|
char *capacity = NULL;
|
|
|
|
char *unit = NULL;
|
|
|
|
|
|
|
|
options = virStorageBackendVolOptionsForType(pool->type);
|
|
|
|
if (options == NULL)
|
|
|
|
return NULL;
|
|
|
|
|
2008-06-06 11:09:57 +00:00
|
|
|
if (VIR_ALLOC(ret) < 0) {
|
|
|
|
virStorageReportError(conn, VIR_ERR_NO_MEMORY,
|
|
|
|
"%s", _("cannot allocate storage vol"));
|
2008-02-20 15:34:52 +00:00
|
|
|
return NULL;
|
2008-06-06 11:09:57 +00:00
|
|
|
}
|
2008-02-20 15:34:52 +00:00
|
|
|
|
|
|
|
if (STRNEQ((const char *)root->name, "volume")) {
|
|
|
|
virStorageReportError(conn, VIR_ERR_XML_ERROR,
|
2008-02-22 16:26:13 +00:00
|
|
|
"%s", _("unknown root element"));
|
2008-02-20 15:34:52 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
2008-07-25 14:27:25 +00:00
|
|
|
ret->name = virXPathString(conn, "string(/volume/name)", ctxt);
|
2008-02-20 15:34:52 +00:00
|
|
|
if (ret->name == NULL) {
|
|
|
|
virStorageReportError(conn, VIR_ERR_XML_ERROR,
|
2008-09-02 14:15:42 +00:00
|
|
|
"%s", _("missing volume name element"));
|
2008-02-20 15:34:52 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
2008-02-27 10:37:19 +00:00
|
|
|
/* Auto-generated so deliberately ignore */
|
2008-07-25 14:27:25 +00:00
|
|
|
/*ret->key = virXPathString(conn, "string(/volume/key)", ctxt);*/
|
2008-02-20 15:34:52 +00:00
|
|
|
|
2008-07-25 14:27:25 +00:00
|
|
|
capacity = virXPathString(conn, "string(/volume/capacity)", ctxt);
|
|
|
|
unit = virXPathString(conn, "string(/volume/capacity/@unit)", ctxt);
|
2008-02-20 15:34:52 +00:00
|
|
|
if (capacity == NULL) {
|
|
|
|
virStorageReportError(conn, VIR_ERR_XML_ERROR,
|
2008-02-22 16:26:13 +00:00
|
|
|
"%s", _("missing capacity element"));
|
2008-02-20 15:34:52 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
if (virStorageSize(conn, unit, capacity, &ret->capacity) < 0)
|
|
|
|
goto cleanup;
|
2008-06-06 11:09:57 +00:00
|
|
|
VIR_FREE(capacity);
|
|
|
|
VIR_FREE(unit);
|
2008-02-20 15:34:52 +00:00
|
|
|
|
2008-07-25 14:27:25 +00:00
|
|
|
allocation = virXPathString(conn, "string(/volume/allocation)", ctxt);
|
2008-02-20 15:34:52 +00:00
|
|
|
if (allocation) {
|
2008-07-25 14:27:25 +00:00
|
|
|
unit = virXPathString(conn, "string(/volume/allocation/@unit)", ctxt);
|
2008-02-20 15:34:52 +00:00
|
|
|
if (virStorageSize(conn, unit, allocation, &ret->allocation) < 0)
|
|
|
|
goto cleanup;
|
2008-06-06 11:09:57 +00:00
|
|
|
VIR_FREE(allocation);
|
|
|
|
VIR_FREE(unit);
|
2008-02-20 15:34:52 +00:00
|
|
|
} else {
|
|
|
|
ret->allocation = ret->capacity;
|
|
|
|
}
|
|
|
|
|
2008-07-25 14:27:25 +00:00
|
|
|
ret->target.path = virXPathString(conn, "string(/volume/target/path)", ctxt);
|
2008-02-20 15:34:52 +00:00
|
|
|
if (options->formatFromString) {
|
2008-07-25 14:27:25 +00:00
|
|
|
char *format = virXPathString(conn, "string(/volume/target/format/@type)", ctxt);
|
2008-10-16 15:06:03 +00:00
|
|
|
if ((ret->target.format = (options->formatFromString)(format)) < 0) {
|
2008-10-21 17:18:45 +00:00
|
|
|
virStorageReportError(conn, VIR_ERR_XML_ERROR,
|
|
|
|
_("unknown volume format type %s"), format);
|
2008-06-06 11:09:57 +00:00
|
|
|
VIR_FREE(format);
|
2008-02-20 15:34:52 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
2008-06-06 11:09:57 +00:00
|
|
|
VIR_FREE(format);
|
2008-02-20 15:34:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (virStorageVolDefParsePerms(conn, ctxt, &ret->target.perms) < 0)
|
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
cleanup:
|
2008-06-06 11:09:57 +00:00
|
|
|
VIR_FREE(allocation);
|
|
|
|
VIR_FREE(capacity);
|
|
|
|
VIR_FREE(unit);
|
2008-02-20 15:34:52 +00:00
|
|
|
virStorageVolDefFree(ret);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
virStorageVolDefPtr
|
|
|
|
virStorageVolDefParse(virConnectPtr conn,
|
|
|
|
virStoragePoolDefPtr pool,
|
|
|
|
const char *xmlStr,
|
|
|
|
const char *filename) {
|
|
|
|
virStorageVolDefPtr ret = NULL;
|
2008-08-01 09:39:44 +00:00
|
|
|
xmlParserCtxtPtr pctxt;
|
2008-02-20 15:34:52 +00:00
|
|
|
xmlDocPtr xml = NULL;
|
|
|
|
xmlNodePtr node = NULL;
|
|
|
|
xmlXPathContextPtr ctxt = NULL;
|
|
|
|
|
2008-08-01 09:39:44 +00:00
|
|
|
/* Set up a parser context so we can catch the details of XML errors. */
|
|
|
|
pctxt = xmlNewParserCtxt ();
|
|
|
|
if (!pctxt || !pctxt->sax)
|
|
|
|
goto cleanup;
|
|
|
|
pctxt->sax->error = catchXMLError;
|
|
|
|
pctxt->_private = conn;
|
|
|
|
|
|
|
|
if (conn) virResetError (&conn->err);
|
|
|
|
xml = xmlCtxtReadDoc (pctxt, BAD_CAST xmlStr,
|
|
|
|
filename ? filename : "storage.xml", NULL,
|
|
|
|
XML_PARSE_NOENT | XML_PARSE_NONET |
|
|
|
|
XML_PARSE_NOWARNING);
|
|
|
|
if (!xml) {
|
|
|
|
if (conn && conn->err.code == VIR_ERR_NONE)
|
|
|
|
virStorageReportError(conn, VIR_ERR_XML_ERROR,
|
2008-09-05 12:03:45 +00:00
|
|
|
"%s", _("failed to parse xml document"));
|
2008-02-20 15:34:52 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
ctxt = xmlXPathNewContext(xml);
|
|
|
|
if (ctxt == NULL) {
|
|
|
|
virStorageReportError(conn, VIR_ERR_NO_MEMORY,
|
2008-02-22 16:26:13 +00:00
|
|
|
"%s", _("xmlXPathContext"));
|
2008-02-20 15:34:52 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
node = xmlDocGetRootElement(xml);
|
|
|
|
if (node == NULL) {
|
|
|
|
virStorageReportError(conn, VIR_ERR_XML_ERROR,
|
2008-02-22 16:26:13 +00:00
|
|
|
"%s", _("missing root element"));
|
2008-02-20 15:34:52 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = virStorageVolDefParseDoc(conn, pool, ctxt, node);
|
|
|
|
|
2008-08-01 09:39:44 +00:00
|
|
|
xmlFreeParserCtxt (pctxt);
|
2008-02-20 15:34:52 +00:00
|
|
|
xmlXPathFreeContext(ctxt);
|
|
|
|
xmlFreeDoc(xml);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
cleanup:
|
2008-08-01 09:39:44 +00:00
|
|
|
xmlFreeParserCtxt (pctxt);
|
2008-02-20 15:34:52 +00:00
|
|
|
xmlXPathFreeContext(ctxt);
|
|
|
|
xmlFreeDoc(xml);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
char *
|
|
|
|
virStorageVolDefFormat(virConnectPtr conn,
|
|
|
|
virStoragePoolDefPtr pool,
|
|
|
|
virStorageVolDefPtr def) {
|
|
|
|
virStorageBackendVolOptionsPtr options;
|
2008-04-28 15:14:59 +00:00
|
|
|
virBuffer buf = VIR_BUFFER_INITIALIZER;
|
2008-06-06 11:09:57 +00:00
|
|
|
char *tmp;
|
2008-02-20 15:34:52 +00:00
|
|
|
|
|
|
|
options = virStorageBackendVolOptionsForType(pool->type);
|
|
|
|
if (options == NULL)
|
|
|
|
return NULL;
|
|
|
|
|
2008-04-28 15:14:59 +00:00
|
|
|
virBufferAddLit(&buf, "<volume>\n");
|
|
|
|
virBufferVSprintf(&buf," <name>%s</name>\n", def->name);
|
|
|
|
virBufferVSprintf(&buf," <key>%s</key>\n", def->key);
|
|
|
|
virBufferAddLit(&buf, " <source>\n");
|
2008-02-20 15:34:52 +00:00
|
|
|
|
|
|
|
if (def->source.nextent) {
|
|
|
|
int i;
|
|
|
|
const char *thispath = NULL;
|
|
|
|
for (i = 0 ; i < def->source.nextent ; i++) {
|
|
|
|
if (thispath == NULL ||
|
|
|
|
STRNEQ(thispath, def->source.extents[i].path)) {
|
|
|
|
if (thispath != NULL)
|
2008-04-28 15:14:59 +00:00
|
|
|
virBufferAddLit(&buf, " </device>\n");
|
|
|
|
|
|
|
|
virBufferVSprintf(&buf, " <device path='%s'>\n",
|
|
|
|
def->source.extents[i].path);
|
2008-02-20 15:34:52 +00:00
|
|
|
}
|
|
|
|
|
2008-04-28 15:14:59 +00:00
|
|
|
virBufferVSprintf(&buf,
|
|
|
|
" <extent start='%llu' end='%llu'/>\n",
|
|
|
|
def->source.extents[i].start,
|
|
|
|
def->source.extents[i].end);
|
2008-02-20 15:34:52 +00:00
|
|
|
thispath = def->source.extents[i].path;
|
|
|
|
}
|
|
|
|
if (thispath != NULL)
|
2008-04-28 15:14:59 +00:00
|
|
|
virBufferAddLit(&buf, " </device>\n");
|
2008-02-20 15:34:52 +00:00
|
|
|
}
|
2008-04-28 15:14:59 +00:00
|
|
|
virBufferAddLit(&buf, " </source>\n");
|
2008-02-20 15:34:52 +00:00
|
|
|
|
2008-04-28 15:14:59 +00:00
|
|
|
virBufferVSprintf(&buf," <capacity>%llu</capacity>\n",
|
|
|
|
def->capacity);
|
|
|
|
virBufferVSprintf(&buf," <allocation>%llu</allocation>\n",
|
|
|
|
def->allocation);
|
2008-02-20 15:34:52 +00:00
|
|
|
|
2008-04-28 15:14:59 +00:00
|
|
|
virBufferAddLit(&buf, " <target>\n");
|
2008-02-20 15:34:52 +00:00
|
|
|
|
2008-04-28 15:14:59 +00:00
|
|
|
if (def->target.path)
|
|
|
|
virBufferVSprintf(&buf," <path>%s</path>\n", def->target.path);
|
2008-02-20 15:34:52 +00:00
|
|
|
|
|
|
|
if (options->formatToString) {
|
2008-10-16 15:06:03 +00:00
|
|
|
const char *format = (options->formatToString)(def->target.format);
|
2008-10-21 17:18:45 +00:00
|
|
|
if (!format) {
|
|
|
|
virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("unknown volume format number %d"),
|
|
|
|
def->target.format);
|
2008-02-20 15:34:52 +00:00
|
|
|
goto cleanup;
|
2008-10-21 17:18:45 +00:00
|
|
|
}
|
2008-04-28 15:14:59 +00:00
|
|
|
virBufferVSprintf(&buf," <format type='%s'/>\n", format);
|
2008-02-20 15:34:52 +00:00
|
|
|
}
|
|
|
|
|
2008-04-28 15:14:59 +00:00
|
|
|
virBufferAddLit(&buf," <permissions>\n");
|
|
|
|
virBufferVSprintf(&buf," <mode>0%o</mode>\n",
|
|
|
|
def->target.perms.mode);
|
|
|
|
virBufferVSprintf(&buf," <owner>%d</owner>\n",
|
|
|
|
def->target.perms.uid);
|
|
|
|
virBufferVSprintf(&buf," <group>%d</group>\n",
|
|
|
|
def->target.perms.gid);
|
2008-02-20 15:34:52 +00:00
|
|
|
|
|
|
|
|
2008-04-28 15:14:59 +00:00
|
|
|
if (def->target.perms.label)
|
|
|
|
virBufferVSprintf(&buf," <label>%s</label>\n",
|
|
|
|
def->target.perms.label);
|
|
|
|
|
|
|
|
virBufferAddLit(&buf," </permissions>\n");
|
|
|
|
virBufferAddLit(&buf, " </target>\n");
|
|
|
|
virBufferAddLit(&buf,"</volume>\n");
|
2008-02-20 15:34:52 +00:00
|
|
|
|
2008-04-28 15:14:59 +00:00
|
|
|
if (virBufferError(&buf))
|
2008-02-20 15:34:52 +00:00
|
|
|
goto no_memory;
|
|
|
|
|
2008-04-28 15:14:59 +00:00
|
|
|
return virBufferContentAndReset(&buf);
|
2008-02-20 15:34:52 +00:00
|
|
|
|
|
|
|
no_memory:
|
2008-02-22 16:26:13 +00:00
|
|
|
virStorageReportError(conn, VIR_ERR_NO_MEMORY, "%s", _("xml"));
|
2008-02-20 15:34:52 +00:00
|
|
|
cleanup:
|
2008-06-06 11:09:57 +00:00
|
|
|
tmp = virBufferContentAndReset(&buf);
|
|
|
|
VIR_FREE(tmp);
|
2008-02-20 15:34:52 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
virStoragePoolObjPtr
|
2008-10-10 15:13:28 +00:00
|
|
|
virStoragePoolObjFindByUUID(virStoragePoolObjListPtr pools,
|
2008-02-20 15:34:52 +00:00
|
|
|
const unsigned char *uuid) {
|
2008-10-10 15:13:28 +00:00
|
|
|
unsigned int i;
|
2008-02-20 15:34:52 +00:00
|
|
|
|
2008-10-10 15:13:28 +00:00
|
|
|
for (i = 0 ; i < pools->count ; i++)
|
|
|
|
if (!memcmp(pools->objs[i]->def->uuid, uuid, VIR_UUID_BUFLEN))
|
|
|
|
return pools->objs[i];
|
2008-02-20 15:34:52 +00:00
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
virStoragePoolObjPtr
|
2008-10-10 15:13:28 +00:00
|
|
|
virStoragePoolObjFindByName(virStoragePoolObjListPtr pools,
|
2008-02-20 15:34:52 +00:00
|
|
|
const char *name) {
|
2008-10-10 15:13:28 +00:00
|
|
|
unsigned int i;
|
2008-02-20 15:34:52 +00:00
|
|
|
|
2008-10-10 15:13:28 +00:00
|
|
|
for (i = 0 ; i < pools->count ; i++)
|
|
|
|
if (STREQ(pools->objs[i]->def->name, name))
|
|
|
|
return pools->objs[i];
|
2008-02-20 15:34:52 +00:00
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
virStoragePoolObjClearVols(virStoragePoolObjPtr pool)
|
|
|
|
{
|
2008-10-10 15:13:28 +00:00
|
|
|
unsigned int i;
|
|
|
|
for (i = 0 ; i < pool->volumes.count ; i++)
|
|
|
|
virStorageVolDefFree(pool->volumes.objs[i]);
|
|
|
|
|
|
|
|
VIR_FREE(pool->volumes.objs);
|
|
|
|
pool->volumes.count = 0;
|
2008-02-20 15:34:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
virStorageVolDefPtr
|
|
|
|
virStorageVolDefFindByKey(virStoragePoolObjPtr pool,
|
|
|
|
const char *key) {
|
2008-10-10 15:13:28 +00:00
|
|
|
unsigned int i;
|
2008-02-20 15:34:52 +00:00
|
|
|
|
2008-10-10 15:13:28 +00:00
|
|
|
for (i = 0 ; i < pool->volumes.count ; i++)
|
|
|
|
if (STREQ(pool->volumes.objs[i]->key, key))
|
|
|
|
return pool->volumes.objs[i];
|
2008-02-20 15:34:52 +00:00
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
virStorageVolDefPtr
|
|
|
|
virStorageVolDefFindByPath(virStoragePoolObjPtr pool,
|
|
|
|
const char *path) {
|
2008-10-10 15:13:28 +00:00
|
|
|
unsigned int i;
|
2008-02-20 15:34:52 +00:00
|
|
|
|
2008-10-10 15:13:28 +00:00
|
|
|
for (i = 0 ; i < pool->volumes.count ; i++)
|
|
|
|
if (STREQ(pool->volumes.objs[i]->target.path, path))
|
|
|
|
return pool->volumes.objs[i];
|
2008-02-20 15:34:52 +00:00
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
virStorageVolDefPtr
|
|
|
|
virStorageVolDefFindByName(virStoragePoolObjPtr pool,
|
|
|
|
const char *name) {
|
2008-10-10 15:13:28 +00:00
|
|
|
unsigned int i;
|
2008-02-20 15:34:52 +00:00
|
|
|
|
2008-10-10 15:13:28 +00:00
|
|
|
for (i = 0 ; i < pool->volumes.count ; i++)
|
|
|
|
if (STREQ(pool->volumes.objs[i]->name, name))
|
|
|
|
return pool->volumes.objs[i];
|
2008-02-20 15:34:52 +00:00
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
virStoragePoolObjPtr
|
|
|
|
virStoragePoolObjAssignDef(virConnectPtr conn,
|
2008-10-10 15:13:28 +00:00
|
|
|
virStoragePoolObjListPtr pools,
|
2008-02-20 15:34:52 +00:00
|
|
|
virStoragePoolDefPtr def) {
|
|
|
|
virStoragePoolObjPtr pool;
|
|
|
|
|
2008-10-10 15:13:28 +00:00
|
|
|
if ((pool = virStoragePoolObjFindByName(pools, def->name))) {
|
2008-02-20 15:34:52 +00:00
|
|
|
if (!virStoragePoolObjIsActive(pool)) {
|
|
|
|
virStoragePoolDefFree(pool->def);
|
|
|
|
pool->def = def;
|
|
|
|
} else {
|
|
|
|
if (pool->newDef)
|
|
|
|
virStoragePoolDefFree(pool->newDef);
|
|
|
|
pool->newDef = def;
|
|
|
|
}
|
|
|
|
return pool;
|
|
|
|
}
|
|
|
|
|
2008-06-06 11:09:57 +00:00
|
|
|
if (VIR_ALLOC(pool) < 0) {
|
2008-02-20 15:34:52 +00:00
|
|
|
virStorageReportError(conn, VIR_ERR_NO_MEMORY,
|
2008-02-22 16:26:13 +00:00
|
|
|
"%s", _("pool"));
|
2008-02-20 15:34:52 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
pool->active = 0;
|
|
|
|
pool->def = def;
|
|
|
|
|
2008-10-10 15:13:28 +00:00
|
|
|
if (VIR_REALLOC_N(pools->objs, pools->count+1) < 0) {
|
|
|
|
pool->def = NULL;
|
|
|
|
virStoragePoolObjFree(pool);
|
|
|
|
virStorageReportError(conn, VIR_ERR_NO_MEMORY, NULL);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
pools->objs[pools->count++] = pool;
|
2008-02-20 15:34:52 +00:00
|
|
|
|
|
|
|
return pool;
|
|
|
|
}
|
|
|
|
|
|
|
|
static virStoragePoolObjPtr
|
2008-10-10 15:13:28 +00:00
|
|
|
virStoragePoolObjLoad(virConnectPtr conn,
|
|
|
|
virStoragePoolObjListPtr pools,
|
2008-02-20 15:34:52 +00:00
|
|
|
const char *file,
|
|
|
|
const char *path,
|
|
|
|
const char *xml,
|
|
|
|
const char *autostartLink) {
|
|
|
|
virStoragePoolDefPtr def;
|
|
|
|
virStoragePoolObjPtr pool;
|
|
|
|
|
|
|
|
if (!(def = virStoragePoolDefParse(NULL, xml, file))) {
|
|
|
|
virErrorPtr err = virGetLastError();
|
|
|
|
virStorageLog("Error parsing storage pool config '%s' : %s",
|
2008-10-21 17:15:53 +00:00
|
|
|
path, err ? err->message : NULL);
|
2008-02-20 15:34:52 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!virFileMatchesNameSuffix(file, def->name, ".xml")) {
|
|
|
|
virStorageLog("Storage pool config filename '%s' does not match pool name '%s'",
|
|
|
|
path, def->name);
|
|
|
|
virStoragePoolDefFree(def);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2008-10-10 15:13:28 +00:00
|
|
|
if (!(pool = virStoragePoolObjAssignDef(conn, pools, def))) {
|
2008-02-20 15:34:52 +00:00
|
|
|
virStorageLog("Failed to load storage pool config '%s': out of memory", path);
|
|
|
|
virStoragePoolDefFree(def);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
pool->configFile = strdup(path);
|
|
|
|
if (pool->configFile == NULL) {
|
|
|
|
virStorageLog("Failed to load storage pool config '%s': out of memory", path);
|
|
|
|
virStoragePoolDefFree(def);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
pool->autostartLink = strdup(autostartLink);
|
|
|
|
if (pool->autostartLink == NULL) {
|
|
|
|
virStorageLog("Failed to load storage pool config '%s': out of memory", path);
|
|
|
|
virStoragePoolDefFree(def);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
pool->autostart = virFileLinkPointsTo(pool->autostartLink,
|
|
|
|
pool->configFile);
|
|
|
|
|
|
|
|
return pool;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int
|
2008-10-10 15:13:28 +00:00
|
|
|
virStoragePoolLoadAllConfigs(virConnectPtr conn,
|
|
|
|
virStoragePoolObjListPtr pools,
|
|
|
|
const char *configDir,
|
|
|
|
const char *autostartDir) {
|
2008-02-20 15:34:52 +00:00
|
|
|
DIR *dir;
|
|
|
|
struct dirent *entry;
|
|
|
|
|
2008-10-10 15:13:28 +00:00
|
|
|
if (!(dir = opendir(configDir))) {
|
2008-02-20 15:34:52 +00:00
|
|
|
if (errno == ENOENT)
|
|
|
|
return 0;
|
|
|
|
virStorageLog("Failed to open dir '%s': %s",
|
2008-10-10 15:13:28 +00:00
|
|
|
configDir, strerror(errno));
|
2008-02-20 15:34:52 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
while ((entry = readdir(dir))) {
|
|
|
|
char *xml = NULL;
|
|
|
|
char path[PATH_MAX];
|
|
|
|
char autostartLink[PATH_MAX];
|
|
|
|
|
|
|
|
if (entry->d_name[0] == '.')
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (!virFileHasSuffix(entry->d_name, ".xml"))
|
|
|
|
continue;
|
|
|
|
|
2008-10-10 15:13:28 +00:00
|
|
|
if (virFileBuildPath(configDir, entry->d_name,
|
2008-02-20 15:34:52 +00:00
|
|
|
NULL, path, PATH_MAX) < 0) {
|
|
|
|
virStorageLog("Config filename '%s/%s' is too long",
|
2008-10-10 15:13:28 +00:00
|
|
|
configDir, entry->d_name);
|
2008-02-20 15:34:52 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2008-10-10 15:13:28 +00:00
|
|
|
if (virFileBuildPath(autostartDir, entry->d_name,
|
2008-02-20 15:34:52 +00:00
|
|
|
NULL, autostartLink, PATH_MAX) < 0) {
|
|
|
|
virStorageLog("Autostart link path '%s/%s' is too long",
|
2008-10-10 15:13:28 +00:00
|
|
|
autostartDir, entry->d_name);
|
2008-02-20 15:34:52 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (virFileReadAll(path, 8192, &xml) < 0)
|
|
|
|
continue;
|
|
|
|
|
2008-10-10 15:13:28 +00:00
|
|
|
virStoragePoolObjLoad(conn, pools, entry->d_name, path, xml, autostartLink);
|
2008-02-20 15:34:52 +00:00
|
|
|
|
2008-06-06 11:09:57 +00:00
|
|
|
VIR_FREE(xml);
|
2008-02-20 15:34:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
closedir(dir);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
virStoragePoolObjSaveDef(virConnectPtr conn,
|
|
|
|
virStorageDriverStatePtr driver,
|
|
|
|
virStoragePoolObjPtr pool,
|
|
|
|
virStoragePoolDefPtr def) {
|
|
|
|
char *xml;
|
|
|
|
int fd = -1, ret = -1;
|
|
|
|
ssize_t towrite;
|
|
|
|
|
|
|
|
if (!pool->configFile) {
|
|
|
|
int err;
|
|
|
|
char path[PATH_MAX];
|
|
|
|
|
|
|
|
if ((err = virFileMakePath(driver->configDir))) {
|
|
|
|
virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("cannot create config directory %s: %s"),
|
|
|
|
driver->configDir, strerror(err));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (virFileBuildPath(driver->configDir, def->name, ".xml",
|
|
|
|
path, sizeof(path)) < 0) {
|
|
|
|
virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR,
|
2008-02-22 16:26:13 +00:00
|
|
|
"%s", _("cannot construct config file path"));
|
2008-02-20 15:34:52 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
if (!(pool->configFile = strdup(path))) {
|
|
|
|
virStorageReportError(conn, VIR_ERR_NO_MEMORY,
|
2008-02-22 16:26:13 +00:00
|
|
|
"%s", _("configFile"));
|
2008-02-20 15:34:52 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (virFileBuildPath(driver->autostartDir, def->name, ".xml",
|
|
|
|
path, sizeof(path)) < 0) {
|
|
|
|
virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR,
|
2008-02-22 16:26:13 +00:00
|
|
|
"%s", _("cannot construct "
|
|
|
|
"autostart link path"));
|
2008-06-06 11:09:57 +00:00
|
|
|
VIR_FREE(pool->configFile);
|
2008-02-20 15:34:52 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
if (!(pool->autostartLink = strdup(path))) {
|
|
|
|
virStorageReportError(conn, VIR_ERR_NO_MEMORY,
|
2008-02-22 16:26:13 +00:00
|
|
|
"%s", _("config file"));
|
2008-06-06 11:09:57 +00:00
|
|
|
VIR_FREE(pool->configFile);
|
2008-02-20 15:34:52 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!(xml = virStoragePoolDefFormat(conn, def))) {
|
|
|
|
virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR,
|
2008-02-22 16:26:13 +00:00
|
|
|
"%s", _("failed to generate XML"));
|
2008-02-20 15:34:52 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((fd = open(pool->configFile,
|
|
|
|
O_WRONLY | O_CREAT | O_TRUNC,
|
|
|
|
S_IRUSR | S_IWUSR )) < 0) {
|
|
|
|
virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("cannot create config file %s: %s"),
|
|
|
|
pool->configFile, strerror(errno));
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
towrite = strlen(xml);
|
|
|
|
if (safewrite(fd, xml, towrite) != towrite) {
|
|
|
|
virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("cannot write config file %s: %s"),
|
|
|
|
pool->configFile, strerror(errno));
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (close(fd) < 0) {
|
|
|
|
virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("cannot save config file %s: %s"),
|
|
|
|
pool->configFile, strerror(errno));
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = 0;
|
|
|
|
|
|
|
|
cleanup:
|
|
|
|
if (fd != -1)
|
|
|
|
close(fd);
|
|
|
|
|
2008-06-06 11:09:57 +00:00
|
|
|
VIR_FREE(xml);
|
2008-02-20 15:34:52 +00:00
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
virStoragePoolObjDeleteDef(virConnectPtr conn,
|
|
|
|
virStoragePoolObjPtr pool) {
|
|
|
|
if (!pool->configFile) {
|
|
|
|
virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("no config file for %s"), pool->def->name);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (unlink(pool->configFile) < 0) {
|
|
|
|
virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("cannot remove config for %s"),
|
|
|
|
pool->def->name);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|