/* * storage_conf.c: config handling for storage driver * * Copyright (C) 2006-2011 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 */ #include #include #include #include #include #include #include #include #include #include #include "virterror_internal.h" #include "datatypes.h" #include "storage_conf.h" #include "storage_file.h" #include "xml.h" #include "uuid.h" #include "buf.h" #include "util.h" #include "memory.h" #include "virfile.h" #define VIR_FROM_THIS VIR_FROM_STORAGE VIR_ENUM_IMPL(virStoragePool, VIR_STORAGE_POOL_LAST, "dir", "fs", "netfs", "logical", "disk", "iscsi", "scsi", "mpath") VIR_ENUM_IMPL(virStoragePoolFormatFileSystem, VIR_STORAGE_POOL_FS_LAST, "auto", "ext2", "ext3", "ext4", "ufs", "iso9660", "udf", "gfs", "gfs2", "vfat", "hfs+", "xfs", "ocfs2") VIR_ENUM_IMPL(virStoragePoolFormatFileSystemNet, VIR_STORAGE_POOL_NETFS_LAST, "auto", "nfs", "glusterfs", "cifs") VIR_ENUM_IMPL(virStoragePoolFormatDisk, VIR_STORAGE_POOL_DISK_LAST, "unknown", "dos", "dvh", "gpt", "mac", "bsd", "pc98", "sun", "lvm2") VIR_ENUM_IMPL(virStoragePoolFormatLogical, VIR_STORAGE_POOL_LOGICAL_LAST, "unknown", "lvm2") VIR_ENUM_IMPL(virStorageVolFormatDisk, VIR_STORAGE_VOL_DISK_LAST, "none", "linux", "fat16", "fat32", "linux-swap", "linux-lvm", "linux-raid", "extended") VIR_ENUM_IMPL(virStoragePartedFsType, VIR_STORAGE_PARTED_FS_TYPE_LAST, "ext2", "ext2", "fat16", "fat32", "linux-swap", "ext2", "ext2", "extended") typedef const char *(*virStorageVolFormatToString)(int format); typedef int (*virStorageVolFormatFromString)(const char *format); typedef const char *(*virStoragePoolFormatToString)(int format); typedef int (*virStoragePoolFormatFromString)(const char *format); typedef struct _virStorageVolOptions virStorageVolOptions; typedef virStorageVolOptions *virStorageVolOptionsPtr; struct _virStorageVolOptions { int defaultFormat; virStorageVolFormatToString formatToString; virStorageVolFormatFromString formatFromString; }; /* Flags to indicate mandatory components in the pool source */ enum { VIR_STORAGE_POOL_SOURCE_HOST = (1<<0), VIR_STORAGE_POOL_SOURCE_DEVICE = (1<<1), VIR_STORAGE_POOL_SOURCE_DIR = (1<<2), VIR_STORAGE_POOL_SOURCE_ADAPTER = (1<<3), VIR_STORAGE_POOL_SOURCE_NAME = (1<<4), VIR_STORAGE_POOL_SOURCE_INITIATOR_IQN = (1<<5), }; typedef struct _virStoragePoolOptions virStoragePoolOptions; typedef virStoragePoolOptions *virStoragePoolOptionsPtr; struct _virStoragePoolOptions { unsigned int flags; int defaultFormat; virStoragePoolFormatToString formatToString; virStoragePoolFormatFromString formatFromString; }; typedef struct _virStoragePoolTypeInfo virStoragePoolTypeInfo; typedef virStoragePoolTypeInfo *virStoragePoolTypeInfoPtr; struct _virStoragePoolTypeInfo { int poolType; virStoragePoolOptions poolOptions; virStorageVolOptions volOptions; }; static virStoragePoolTypeInfo poolTypeInfo[] = { { .poolType = VIR_STORAGE_POOL_LOGICAL, .poolOptions = { .flags = (VIR_STORAGE_POOL_SOURCE_NAME | VIR_STORAGE_POOL_SOURCE_DEVICE), .defaultFormat = VIR_STORAGE_POOL_LOGICAL_LVM2, .formatFromString = virStoragePoolFormatLogicalTypeFromString, .formatToString = virStoragePoolFormatLogicalTypeToString, }, }, { .poolType = VIR_STORAGE_POOL_DIR, .volOptions = { .defaultFormat = VIR_STORAGE_FILE_RAW, .formatFromString = virStorageFileFormatTypeFromString, .formatToString = virStorageFileFormatTypeToString, }, }, { .poolType = VIR_STORAGE_POOL_FS, .poolOptions = { .flags = (VIR_STORAGE_POOL_SOURCE_DEVICE), .defaultFormat = VIR_STORAGE_POOL_FS_AUTO, .formatFromString = virStoragePoolFormatFileSystemTypeFromString, .formatToString = virStoragePoolFormatFileSystemTypeToString, }, .volOptions = { .defaultFormat = VIR_STORAGE_FILE_RAW, .formatFromString = virStorageFileFormatTypeFromString, .formatToString = virStorageFileFormatTypeToString, }, }, { .poolType = VIR_STORAGE_POOL_NETFS, .poolOptions = { .flags = (VIR_STORAGE_POOL_SOURCE_HOST | VIR_STORAGE_POOL_SOURCE_DIR), .defaultFormat = VIR_STORAGE_POOL_NETFS_AUTO, .formatFromString = virStoragePoolFormatFileSystemNetTypeFromString, .formatToString = virStoragePoolFormatFileSystemNetTypeToString, }, .volOptions = { .defaultFormat = VIR_STORAGE_FILE_RAW, .formatFromString = virStorageFileFormatTypeFromString, .formatToString = virStorageFileFormatTypeToString, }, }, { .poolType = VIR_STORAGE_POOL_ISCSI, .poolOptions = { .flags = (VIR_STORAGE_POOL_SOURCE_HOST | VIR_STORAGE_POOL_SOURCE_DEVICE | VIR_STORAGE_POOL_SOURCE_INITIATOR_IQN), }, .volOptions = { .formatToString = virStoragePoolFormatDiskTypeToString, } }, { .poolType = VIR_STORAGE_POOL_SCSI, .poolOptions = { .flags = (VIR_STORAGE_POOL_SOURCE_ADAPTER), }, .volOptions = { .formatToString = virStoragePoolFormatDiskTypeToString, } }, { .poolType = VIR_STORAGE_POOL_MPATH, .volOptions = { .formatToString = virStoragePoolFormatDiskTypeToString, } }, { .poolType = VIR_STORAGE_POOL_DISK, .poolOptions = { .flags = (VIR_STORAGE_POOL_SOURCE_DEVICE), .defaultFormat = VIR_STORAGE_POOL_DISK_UNKNOWN, .formatFromString = virStoragePoolFormatDiskTypeFromString, .formatToString = virStoragePoolFormatDiskTypeToString, }, .volOptions = { .defaultFormat = VIR_STORAGE_VOL_DISK_NONE, .formatFromString = virStorageVolFormatDiskTypeFromString, .formatToString = virStorageVolFormatDiskTypeToString, }, } }; static virStoragePoolTypeInfoPtr virStoragePoolTypeInfoLookup(int type) { unsigned int i; for (i = 0; i < ARRAY_CARDINALITY(poolTypeInfo) ; i++) if (poolTypeInfo[i].poolType == type) return &poolTypeInfo[i]; virStorageReportError(VIR_ERR_INTERNAL_ERROR, _("missing backend for pool type %d"), type); return NULL; } static virStoragePoolOptionsPtr virStoragePoolOptionsForPoolType(int type) { virStoragePoolTypeInfoPtr backend = virStoragePoolTypeInfoLookup(type); if (backend == NULL) return NULL; return &backend->poolOptions; } static virStorageVolOptionsPtr virStorageVolOptionsForPoolType(int type) { virStoragePoolTypeInfoPtr backend = virStoragePoolTypeInfoLookup(type); if (backend == NULL) return NULL; return &backend->volOptions; } void virStorageVolDefFree(virStorageVolDefPtr def) { int i; if (!def) return; VIR_FREE(def->name); VIR_FREE(def->key); for (i = 0 ; i < def->source.nextent ; i++) { VIR_FREE(def->source.extents[i].path); } VIR_FREE(def->source.extents); VIR_FREE(def->target.path); VIR_FREE(def->target.perms.label); virStorageEncryptionFree(def->target.encryption); VIR_FREE(def->backingStore.path); VIR_FREE(def->backingStore.perms.label); virStorageEncryptionFree(def->backingStore.encryption); VIR_FREE(def); } void virStoragePoolSourceFree(virStoragePoolSourcePtr source) { int i; if (!source) return; VIR_FREE(source->host.name); for (i = 0 ; i < source->ndevice ; i++) { VIR_FREE(source->devices[i].freeExtents); VIR_FREE(source->devices[i].path); } VIR_FREE(source->devices); VIR_FREE(source->dir); VIR_FREE(source->name); VIR_FREE(source->adapter); VIR_FREE(source->initiator.iqn); VIR_FREE(source->vendor); VIR_FREE(source->product); if (source->authType == VIR_STORAGE_POOL_AUTH_CHAP) { VIR_FREE(source->auth.chap.login); VIR_FREE(source->auth.chap.passwd); } } void virStoragePoolDefFree(virStoragePoolDefPtr def) { if (!def) return; VIR_FREE(def->name); virStoragePoolSourceFree(&def->source); VIR_FREE(def->target.path); VIR_FREE(def->target.perms.label); VIR_FREE(def); } void virStoragePoolObjFree(virStoragePoolObjPtr obj) { if (!obj) return; virStoragePoolObjClearVols(obj); virStoragePoolDefFree(obj->def); virStoragePoolDefFree(obj->newDef); VIR_FREE(obj->configFile); VIR_FREE(obj->autostartLink); virMutexDestroy(&obj->lock); VIR_FREE(obj); } 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; } void virStoragePoolObjRemove(virStoragePoolObjListPtr pools, virStoragePoolObjPtr pool) { unsigned int i; virStoragePoolObjUnlock(pool); for (i = 0 ; i < pools->count ; i++) { virStoragePoolObjLock(pools->objs[i]); if (pools->objs[i] == pool) { virStoragePoolObjUnlock(pools->objs[i]); virStoragePoolObjFree(pools->objs[i]); if (i < (pools->count - 1)) memmove(pools->objs + i, pools->objs + i + 1, sizeof(*(pools->objs)) * (pools->count - (i + 1))); if (VIR_REALLOC_N(pools->objs, pools->count - 1) < 0) { ; /* Failure to reduce memory allocation isn't fatal */ } pools->count--; break; } virStoragePoolObjUnlock(pools->objs[i]); } } static int virStoragePoolDefParseAuthChap(xmlXPathContextPtr ctxt, virStoragePoolAuthChapPtr auth) { auth->login = virXPathString("string(./auth/@login)", ctxt); if (auth->login == NULL) { virStorageReportError(VIR_ERR_XML_ERROR, "%s", _("missing auth host attribute")); return -1; } auth->passwd = virXPathString("string(./auth/@passwd)", ctxt); if (auth->passwd == NULL) { virStorageReportError(VIR_ERR_XML_ERROR, "%s", _("missing auth passwd attribute")); return -1; } return 0; } static int virStoragePoolDefParseSource(xmlXPathContextPtr ctxt, virStoragePoolSourcePtr source, int pool_type, xmlNodePtr node) { int ret = -1; xmlNodePtr relnode, *nodeset = NULL; char *authType = NULL; int nsource, i; virStoragePoolOptionsPtr options; char *port = NULL; relnode = ctxt->node; ctxt->node = node; if ((options = virStoragePoolOptionsForPoolType(pool_type)) == NULL) { goto cleanup; } source->name = virXPathString("string(./name)", ctxt); if (options->formatFromString) { char *format = virXPathString("string(./format/@type)", ctxt); if (format == NULL) source->format = options->defaultFormat; else source->format = options->formatFromString(format); if (source->format < 0) { virStorageReportError(VIR_ERR_XML_ERROR, _("unknown pool format type %s"), format); VIR_FREE(format); goto cleanup; } VIR_FREE(format); } source->host.name = virXPathString("string(./host/@name)", ctxt); port = virXPathString("string(./host/@port)", ctxt); if (port) { if (virStrToLong_i(port, NULL, 10, &source->host.port) < 0) { virStorageReportError(VIR_ERR_XML_ERROR, _("Invalid port number: %s"), port); goto cleanup; } } source->initiator.iqn = virXPathString("string(./initiator/iqn/@name)", ctxt); nsource = virXPathNodeSet("./device", ctxt, &nodeset); if (nsource < 0) goto cleanup; if (nsource > 0) { if (VIR_ALLOC_N(source->devices, nsource) < 0) { VIR_FREE(nodeset); virReportOOMError(); goto cleanup; } for (i = 0 ; i < nsource ; i++) { char *path = virXMLPropString(nodeset[i], "path"); if (path == NULL) { VIR_FREE(nodeset); virStorageReportError(VIR_ERR_XML_ERROR, "%s", _("missing storage pool source device path")); goto cleanup; } source->devices[i].path = path; } source->ndevice = nsource; } source->dir = virXPathString("string(./dir/@path)", ctxt); source->adapter = virXPathString("string(./adapter/@name)", ctxt); authType = virXPathString("string(./auth/@type)", ctxt); if (authType == NULL) { source->authType = VIR_STORAGE_POOL_AUTH_NONE; } else { if (STREQ(authType, "chap")) { source->authType = VIR_STORAGE_POOL_AUTH_CHAP; } else { virStorageReportError(VIR_ERR_XML_ERROR, _("unknown auth type '%s'"), (const char *)authType); goto cleanup; } } if (source->authType == VIR_STORAGE_POOL_AUTH_CHAP) { if (virStoragePoolDefParseAuthChap(ctxt, &source->auth.chap) < 0) goto cleanup; } source->vendor = virXPathString("string(./vendor/@name)", ctxt); source->product = virXPathString("string(./product/@name)", ctxt); ret = 0; cleanup: ctxt->node = relnode; VIR_FREE(port); VIR_FREE(authType); VIR_FREE(nodeset); return ret; } virStoragePoolSourcePtr virStoragePoolDefParseSourceString(const char *srcSpec, int pool_type) { xmlDocPtr doc = NULL; xmlNodePtr node = NULL; xmlXPathContextPtr xpath_ctxt = NULL; virStoragePoolSourcePtr def = NULL, ret = NULL; if (!(doc = virXMLParseStringCtxt(srcSpec, "srcSpec.xml", &xpath_ctxt))) { goto cleanup; } if (VIR_ALLOC(def) < 0) { virReportOOMError(); goto cleanup; } node = virXPathNode("/source", xpath_ctxt); if (!node) { virStorageReportError(VIR_ERR_XML_ERROR, "%s", _("root element was not source")); goto cleanup; } if (virStoragePoolDefParseSource(xpath_ctxt, def, pool_type, node) < 0) goto cleanup; ret = def; def = NULL; cleanup: virStoragePoolSourceFree(def); VIR_FREE(def); xmlFreeDoc(doc); xmlXPathFreeContext(xpath_ctxt); return ret; } static int virStorageDefParsePerms(xmlXPathContextPtr ctxt, virStoragePermsPtr perms, const char *permxpath, int defaultmode) { char *mode; long v; int ret = -1; xmlNodePtr relnode; xmlNodePtr node; node = virXPathNode(permxpath, ctxt); if (node == NULL) { /* Set default values if there is not element */ perms->mode = defaultmode; perms->uid = -1; perms->gid = -1; perms->label = NULL; return 0; } relnode = ctxt->node; ctxt->node = node; mode = virXPathString("string(./mode)", ctxt); if (!mode) { perms->mode = defaultmode; } else { char *end = NULL; perms->mode = strtol(mode, &end, 8); if (*end || perms->mode < 0 || perms->mode > 0777) { VIR_FREE(mode); virStorageReportError(VIR_ERR_XML_ERROR, "%s", _("malformed octal mode")); goto error; } VIR_FREE(mode); } if (virXPathNode("./owner", ctxt) == NULL) { perms->uid = -1; } else { if (virXPathLong("number(./owner)", ctxt, &v) < 0) { virStorageReportError(VIR_ERR_XML_ERROR, "%s", _("malformed owner element")); goto error; } perms->uid = (int)v; } if (virXPathNode("./group", ctxt) == NULL) { perms->gid = -1; } else { if (virXPathLong("number(./group)", ctxt, &v) < 0) { virStorageReportError(VIR_ERR_XML_ERROR, "%s", _("malformed group element")); goto error; } perms->gid = (int)v; } /* NB, we're ignoring missing labels here - they'll simply inherit */ perms->label = virXPathString("string(./label)", ctxt); ret = 0; error: ctxt->node = relnode; return ret; } static virStoragePoolDefPtr virStoragePoolDefParseXML(xmlXPathContextPtr ctxt) { virStoragePoolOptionsPtr options; virStoragePoolDefPtr ret; xmlNodePtr source_node; char *type = NULL; char *uuid = NULL; char *tmppath; if (VIR_ALLOC(ret) < 0) { virReportOOMError(); return NULL; } type = virXPathString("string(./@type)", ctxt); if ((ret->type = virStoragePoolTypeFromString((const char *)type)) < 0) { virStorageReportError(VIR_ERR_INTERNAL_ERROR, _("unknown storage pool type %s"), (const char*)type); goto cleanup; } xmlFree(type); type = NULL; if ((options = virStoragePoolOptionsForPoolType(ret->type)) == NULL) { goto cleanup; } source_node = virXPathNode("./source", ctxt); if (source_node) { if (virStoragePoolDefParseSource(ctxt, &ret->source, ret->type, source_node) < 0) goto cleanup; } ret->name = virXPathString("string(./name)", ctxt); if (ret->name == NULL && options->flags & VIR_STORAGE_POOL_SOURCE_NAME) ret->name = ret->source.name; if (ret->name == NULL) { virStorageReportError(VIR_ERR_XML_ERROR, "%s", _("missing pool source name element")); goto cleanup; } uuid = virXPathString("string(./uuid)", ctxt); if (uuid == NULL) { if (virUUIDGenerate(ret->uuid) < 0) { virStorageReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("unable to generate uuid")); goto cleanup; } } else { if (virUUIDParse(uuid, ret->uuid) < 0) { virStorageReportError(VIR_ERR_XML_ERROR, "%s", _("malformed uuid element")); goto cleanup; } VIR_FREE(uuid); } if (options->flags & VIR_STORAGE_POOL_SOURCE_HOST) { if (!ret->source.host.name) { virStorageReportError(VIR_ERR_XML_ERROR, "%s", _("missing storage pool source host name")); goto cleanup; } } if (options->flags & VIR_STORAGE_POOL_SOURCE_DIR) { if (!ret->source.dir) { virStorageReportError(VIR_ERR_XML_ERROR, "%s", _("missing storage pool source path")); goto cleanup; } } if (options->flags & VIR_STORAGE_POOL_SOURCE_NAME) { if (ret->source.name == NULL) { /* source name defaults to pool name */ ret->source.name = strdup(ret->name); if (ret->source.name == NULL) { virReportOOMError(); goto cleanup; } } } if (options->flags & VIR_STORAGE_POOL_SOURCE_ADAPTER) { if (!ret->source.adapter) { virStorageReportError(VIR_ERR_XML_ERROR, "%s", _("missing storage pool source adapter name")); goto cleanup; } } /* If DEVICE is the only source type, then its required */ if (options->flags == VIR_STORAGE_POOL_SOURCE_DEVICE) { if (!ret->source.ndevice) { virStorageReportError(VIR_ERR_XML_ERROR, "%s", _("missing storage pool source device name")); goto cleanup; } } if ((tmppath = virXPathString("string(./target/path)", ctxt)) == NULL) { virStorageReportError(VIR_ERR_XML_ERROR, "%s", _("missing storage pool target path")); goto cleanup; } ret->target.path = virFileSanitizePath(tmppath); VIR_FREE(tmppath); if (!ret->target.path) goto cleanup; if (virStorageDefParsePerms(ctxt, &ret->target.perms, "./target/permissions", 0700) < 0) goto cleanup; return ret; cleanup: VIR_FREE(uuid); xmlFree(type); virStoragePoolDefFree(ret); return NULL; } virStoragePoolDefPtr virStoragePoolDefParseNode(xmlDocPtr xml, xmlNodePtr root) { xmlXPathContextPtr ctxt = NULL; virStoragePoolDefPtr def = NULL; if (STRNEQ((const char *)root->name, "pool")) { virStorageReportError(VIR_ERR_XML_ERROR, "%s", _("unknown root element for storage pool")); goto cleanup; } ctxt = xmlXPathNewContext(xml); if (ctxt == NULL) { virReportOOMError(); goto cleanup; } ctxt->node = root; def = virStoragePoolDefParseXML(ctxt); cleanup: xmlXPathFreeContext(ctxt); return def; } static virStoragePoolDefPtr virStoragePoolDefParse(const char *xmlStr, const char *filename) { virStoragePoolDefPtr ret = NULL; xmlDocPtr xml; if ((xml = virXMLParse(filename, xmlStr, "storage.xml"))) { ret = virStoragePoolDefParseNode(xml, xmlDocGetRootElement(xml)); xmlFreeDoc(xml); } return ret; } virStoragePoolDefPtr virStoragePoolDefParseString(const char *xmlStr) { return virStoragePoolDefParse(xmlStr, NULL); } virStoragePoolDefPtr virStoragePoolDefParseFile(const char *filename) { return virStoragePoolDefParse(NULL, filename); } static int virStoragePoolSourceFormat(virBufferPtr buf, virStoragePoolOptionsPtr options, virStoragePoolSourcePtr src) { int i, j; virBufferAddLit(buf," \n"); if ((options->flags & VIR_STORAGE_POOL_SOURCE_HOST) && src->host.name) { virBufferAsprintf(buf, " host.name); if (src->host.port) virBufferAsprintf(buf, " port='%d'", src->host.port); virBufferAddLit(buf, "/>\n"); } if ((options->flags & VIR_STORAGE_POOL_SOURCE_DEVICE) && src->ndevice) { for (i = 0 ; i < src->ndevice ; i++) { if (src->devices[i].nfreeExtent) { virBufferAsprintf(buf," \n", src->devices[i].path); for (j = 0 ; j < src->devices[i].nfreeExtent ; j++) { virBufferAsprintf(buf, " \n", src->devices[i].freeExtents[j].start, src->devices[i].freeExtents[j].end); } virBufferAddLit(buf," \n"); } else virBufferAsprintf(buf, " \n", src->devices[i].path); } } if ((options->flags & VIR_STORAGE_POOL_SOURCE_DIR) && src->dir) virBufferAsprintf(buf," \n", src->dir); if ((options->flags & VIR_STORAGE_POOL_SOURCE_ADAPTER) && src->adapter) virBufferAsprintf(buf," \n", src->adapter); if ((options->flags & VIR_STORAGE_POOL_SOURCE_NAME) && src->name) virBufferAsprintf(buf," %s\n", src->name); if ((options->flags & VIR_STORAGE_POOL_SOURCE_INITIATOR_IQN) && src->initiator.iqn) { virBufferAddLit(buf," \n"); virBufferEscapeString(buf," \n", src->initiator.iqn); virBufferAddLit(buf," \n"); } if (options->formatToString) { const char *format = (options->formatToString)(src->format); if (!format) { virStorageReportError(VIR_ERR_INTERNAL_ERROR, _("unknown pool format number %d"), src->format); return -1; } virBufferAsprintf(buf," \n", format); } if (src->authType == VIR_STORAGE_POOL_AUTH_CHAP) virBufferAsprintf(buf," \n", src->auth.chap.login, src->auth.chap.passwd); if (src->vendor != NULL) { virBufferEscapeString(buf," \n", src->vendor); } if (src->product != NULL) { virBufferEscapeString(buf," \n", src->product); } virBufferAddLit(buf," \n"); return 0; } char * virStoragePoolDefFormat(virStoragePoolDefPtr def) { virStoragePoolOptionsPtr options; virBuffer buf = VIR_BUFFER_INITIALIZER; const char *type; char uuid[VIR_UUID_STRING_BUFLEN]; options = virStoragePoolOptionsForPoolType(def->type); if (options == NULL) return NULL; type = virStoragePoolTypeToString(def->type); if (!type) { virStorageReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("unexpected pool type")); goto cleanup; } virBufferAsprintf(&buf, "\n", type); virBufferAsprintf(&buf," %s\n", def->name); virUUIDFormat(def->uuid, uuid); virBufferAsprintf(&buf," %s\n", uuid); virBufferAsprintf(&buf," %llu\n", def->capacity); virBufferAsprintf(&buf," %llu\n", def->allocation); virBufferAsprintf(&buf," %llu\n", def->available); if (virStoragePoolSourceFormat(&buf, options, &def->source) < 0) goto cleanup; virBufferAddLit(&buf," \n"); if (def->target.path) virBufferAsprintf(&buf," %s\n", def->target.path); virBufferAddLit(&buf," \n"); virBufferAsprintf(&buf," 0%o\n", def->target.perms.mode); virBufferAsprintf(&buf," %d\n", def->target.perms.uid); virBufferAsprintf(&buf," %d\n", def->target.perms.gid); if (def->target.perms.label) virBufferAsprintf(&buf," \n", def->target.perms.label); virBufferAddLit(&buf," \n"); virBufferAddLit(&buf," \n"); virBufferAddLit(&buf,"\n"); if (virBufferError(&buf)) goto no_memory; return virBufferContentAndReset(&buf); no_memory: virReportOOMError(); cleanup: virBufferFreeAndReset(&buf); return NULL; } static int virStorageSize(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 'e': case 'E': mult = 1024ull * 1024ull * 1024ull * 1024ull * 1024ull * 1024ull; break; default: virStorageReportError(VIR_ERR_XML_ERROR, _("unknown size units '%s'"), unit); return -1; } } if (virStrToLong_ull (val, &end, 10, ret) < 0) { virStorageReportError(VIR_ERR_XML_ERROR, "%s", _("malformed capacity element")); return -1; } if (*ret > (ULLONG_MAX / mult)) { virStorageReportError(VIR_ERR_XML_ERROR, "%s", _("capacity element value too large")); return -1; } *ret *= mult; return 0; } static virStorageVolDefPtr virStorageVolDefParseXML(virStoragePoolDefPtr pool, xmlXPathContextPtr ctxt) { virStorageVolDefPtr ret; virStorageVolOptionsPtr options; char *allocation = NULL; char *capacity = NULL; char *unit = NULL; xmlNodePtr node; options = virStorageVolOptionsForPoolType(pool->type); if (options == NULL) return NULL; if (VIR_ALLOC(ret) < 0) { virReportOOMError(); return NULL; } ret->name = virXPathString("string(./name)", ctxt); if (ret->name == NULL) { virStorageReportError(VIR_ERR_XML_ERROR, "%s", _("missing volume name element")); goto cleanup; } /* Auto-generated so deliberately ignore */ /*ret->key = virXPathString("string(./key)", ctxt);*/ capacity = virXPathString("string(./capacity)", ctxt); unit = virXPathString("string(./capacity/@unit)", ctxt); if (capacity == NULL) { virStorageReportError(VIR_ERR_XML_ERROR, "%s", _("missing capacity element")); goto cleanup; } if (virStorageSize(unit, capacity, &ret->capacity) < 0) goto cleanup; VIR_FREE(capacity); VIR_FREE(unit); allocation = virXPathString("string(./allocation)", ctxt); if (allocation) { unit = virXPathString("string(./allocation/@unit)", ctxt); if (virStorageSize(unit, allocation, &ret->allocation) < 0) goto cleanup; VIR_FREE(allocation); VIR_FREE(unit); } else { ret->allocation = ret->capacity; } ret->target.path = virXPathString("string(./target/path)", ctxt); if (options->formatFromString) { char *format = virXPathString("string(./target/format/@type)", ctxt); if (format == NULL) ret->target.format = options->defaultFormat; else ret->target.format = (options->formatFromString)(format); if (ret->target.format < 0) { virStorageReportError(VIR_ERR_XML_ERROR, _("unknown volume format type %s"), format); VIR_FREE(format); goto cleanup; } VIR_FREE(format); } if (virStorageDefParsePerms(ctxt, &ret->target.perms, "./target/permissions", 0600) < 0) goto cleanup; node = virXPathNode("./target/encryption", ctxt); if (node != NULL) { ret->target.encryption = virStorageEncryptionParseNode(ctxt->doc, node); if (ret->target.encryption == NULL) goto cleanup; } ret->backingStore.path = virXPathString("string(./backingStore/path)", ctxt); if (options->formatFromString) { char *format = virXPathString("string(./backingStore/format/@type)", ctxt); if (format == NULL) ret->backingStore.format = options->defaultFormat; else ret->backingStore.format = (options->formatFromString)(format); if (ret->backingStore.format < 0) { virStorageReportError(VIR_ERR_XML_ERROR, _("unknown volume format type %s"), format); VIR_FREE(format); goto cleanup; } VIR_FREE(format); } if (virStorageDefParsePerms(ctxt, &ret->backingStore.perms, "./backingStore/permissions", 0600) < 0) goto cleanup; return ret; cleanup: VIR_FREE(allocation); VIR_FREE(capacity); VIR_FREE(unit); virStorageVolDefFree(ret); return NULL; } virStorageVolDefPtr virStorageVolDefParseNode(virStoragePoolDefPtr pool, xmlDocPtr xml, xmlNodePtr root) { xmlXPathContextPtr ctxt = NULL; virStorageVolDefPtr def = NULL; if (STRNEQ((const char *)root->name, "volume")) { virStorageReportError(VIR_ERR_XML_ERROR, "%s", _("unknown root element for storage vol")); goto cleanup; } ctxt = xmlXPathNewContext(xml); if (ctxt == NULL) { virReportOOMError(); goto cleanup; } ctxt->node = root; def = virStorageVolDefParseXML(pool, ctxt); cleanup: xmlXPathFreeContext(ctxt); return def; } static virStorageVolDefPtr virStorageVolDefParse(virStoragePoolDefPtr pool, const char *xmlStr, const char *filename) { virStorageVolDefPtr ret = NULL; xmlDocPtr xml; if ((xml = virXMLParse(filename, xmlStr, "storage.xml"))) { ret = virStorageVolDefParseNode(pool, xml, xmlDocGetRootElement(xml)); xmlFreeDoc(xml); } return ret; } virStorageVolDefPtr virStorageVolDefParseString(virStoragePoolDefPtr pool, const char *xmlStr) { return virStorageVolDefParse(pool, xmlStr, NULL); } virStorageVolDefPtr virStorageVolDefParseFile(virStoragePoolDefPtr pool, const char *filename) { return virStorageVolDefParse(pool, NULL, filename); } static int virStorageVolTargetDefFormat(virStorageVolOptionsPtr options, virBufferPtr buf, virStorageVolTargetPtr def, const char *type) { virBufferAsprintf(buf, " <%s>\n", type); if (def->path) virBufferAsprintf(buf," %s\n", def->path); if (options->formatToString) { const char *format = (options->formatToString)(def->format); if (!format) { virStorageReportError(VIR_ERR_INTERNAL_ERROR, _("unknown volume format number %d"), def->format); return -1; } virBufferAsprintf(buf," \n", format); } virBufferAddLit(buf," \n"); virBufferAsprintf(buf," 0%o\n", def->perms.mode); virBufferAsprintf(buf," %d\n", def->perms.uid); virBufferAsprintf(buf," %d\n", def->perms.gid); if (def->perms.label) virBufferAsprintf(buf," \n", def->perms.label); virBufferAddLit(buf," \n"); if (def->encryption != NULL && virStorageEncryptionFormat(buf, def->encryption, 4) < 0) return -1; virBufferAsprintf(buf, " \n", type); return 0; } char * virStorageVolDefFormat(virStoragePoolDefPtr pool, virStorageVolDefPtr def) { virStorageVolOptionsPtr options; virBuffer buf = VIR_BUFFER_INITIALIZER; options = virStorageVolOptionsForPoolType(pool->type); if (options == NULL) return NULL; virBufferAddLit(&buf, "\n"); virBufferAsprintf(&buf," %s\n", def->name); virBufferAsprintf(&buf," %s\n", def->key); virBufferAddLit(&buf, " \n"); 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) virBufferAddLit(&buf, " \n"); virBufferAsprintf(&buf, " \n", def->source.extents[i].path); } virBufferAsprintf(&buf, " \n", def->source.extents[i].start, def->source.extents[i].end); thispath = def->source.extents[i].path; } if (thispath != NULL) virBufferAddLit(&buf, " \n"); } virBufferAddLit(&buf, " \n"); virBufferAsprintf(&buf," %llu\n", def->capacity); virBufferAsprintf(&buf," %llu\n", def->allocation); if (virStorageVolTargetDefFormat(options, &buf, &def->target, "target") < 0) goto cleanup; if (def->backingStore.path && virStorageVolTargetDefFormat(options, &buf, &def->backingStore, "backingStore") < 0) goto cleanup; virBufferAddLit(&buf,"\n"); if (virBufferError(&buf)) goto no_memory; return virBufferContentAndReset(&buf); no_memory: virReportOOMError(); cleanup: virBufferFreeAndReset(&buf); return NULL; } virStoragePoolObjPtr virStoragePoolObjFindByUUID(virStoragePoolObjListPtr pools, const unsigned char *uuid) { unsigned int i; for (i = 0 ; i < pools->count ; i++) { virStoragePoolObjLock(pools->objs[i]); if (!memcmp(pools->objs[i]->def->uuid, uuid, VIR_UUID_BUFLEN)) return pools->objs[i]; virStoragePoolObjUnlock(pools->objs[i]); } return NULL; } virStoragePoolObjPtr virStoragePoolObjFindByName(virStoragePoolObjListPtr pools, const char *name) { unsigned int i; for (i = 0 ; i < pools->count ; i++) { virStoragePoolObjLock(pools->objs[i]); if (STREQ(pools->objs[i]->def->name, name)) return pools->objs[i]; virStoragePoolObjUnlock(pools->objs[i]); } return NULL; } void virStoragePoolObjClearVols(virStoragePoolObjPtr pool) { 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; } virStorageVolDefPtr virStorageVolDefFindByKey(virStoragePoolObjPtr pool, const char *key) { unsigned int i; for (i = 0 ; i < pool->volumes.count ; i++) if (STREQ(pool->volumes.objs[i]->key, key)) return pool->volumes.objs[i]; return NULL; } virStorageVolDefPtr virStorageVolDefFindByPath(virStoragePoolObjPtr pool, const char *path) { unsigned int i; for (i = 0 ; i < pool->volumes.count ; i++) if (STREQ(pool->volumes.objs[i]->target.path, path)) return pool->volumes.objs[i]; return NULL; } virStorageVolDefPtr virStorageVolDefFindByName(virStoragePoolObjPtr pool, const char *name) { unsigned int i; for (i = 0 ; i < pool->volumes.count ; i++) if (STREQ(pool->volumes.objs[i]->name, name)) return pool->volumes.objs[i]; return NULL; } virStoragePoolObjPtr virStoragePoolObjAssignDef(virStoragePoolObjListPtr pools, virStoragePoolDefPtr def) { virStoragePoolObjPtr pool; if ((pool = virStoragePoolObjFindByName(pools, def->name))) { if (!virStoragePoolObjIsActive(pool)) { virStoragePoolDefFree(pool->def); pool->def = def; } else { virStoragePoolDefFree(pool->newDef); pool->newDef = def; } return pool; } if (VIR_ALLOC(pool) < 0) { virReportOOMError(); return NULL; } if (virMutexInit(&pool->lock) < 0) { virStorageReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("cannot initialize mutex")); VIR_FREE(pool); return NULL; } virStoragePoolObjLock(pool); pool->active = 0; pool->def = def; if (VIR_REALLOC_N(pools->objs, pools->count+1) < 0) { pool->def = NULL; virStoragePoolObjUnlock(pool); virStoragePoolObjFree(pool); virReportOOMError(); return NULL; } pools->objs[pools->count++] = pool; return pool; } static virStoragePoolObjPtr virStoragePoolObjLoad(virStoragePoolObjListPtr pools, const char *file, const char *path, const char *autostartLink) { virStoragePoolDefPtr def; virStoragePoolObjPtr pool; if (!(def = virStoragePoolDefParseFile(path))) { return NULL; } if (!virFileMatchesNameSuffix(file, def->name, ".xml")) { virStorageReportError(VIR_ERR_XML_ERROR, _("Storage pool config filename '%s' does not match pool name '%s'"), path, def->name); virStoragePoolDefFree(def); return NULL; } if (!(pool = virStoragePoolObjAssignDef(pools, def))) { virStoragePoolDefFree(def); return NULL; } VIR_FREE(pool->configFile); /* for driver reload */ pool->configFile = strdup(path); if (pool->configFile == NULL) { virReportOOMError(); virStoragePoolDefFree(def); return NULL; } VIR_FREE(pool->autostartLink); /* for driver reload */ pool->autostartLink = strdup(autostartLink); if (pool->autostartLink == NULL) { virReportOOMError(); virStoragePoolDefFree(def); return NULL; } pool->autostart = virFileLinkPointsTo(pool->autostartLink, pool->configFile); return pool; } int virStoragePoolLoadAllConfigs(virStoragePoolObjListPtr pools, const char *configDir, const char *autostartDir) { DIR *dir; struct dirent *entry; if (!(dir = opendir(configDir))) { if (errno == ENOENT) return 0; virReportSystemError(errno, _("Failed to open dir '%s'"), configDir); return -1; } while ((entry = readdir(dir))) { char *path; char *autostartLink; virStoragePoolObjPtr pool; if (entry->d_name[0] == '.') continue; if (!virFileHasSuffix(entry->d_name, ".xml")) continue; if (!(path = virFileBuildPath(configDir, entry->d_name, NULL))) continue; if (!(autostartLink = virFileBuildPath(autostartDir, entry->d_name, NULL))) { VIR_FREE(path); continue; } pool = virStoragePoolObjLoad(pools, entry->d_name, path, autostartLink); if (pool) virStoragePoolObjUnlock(pool); VIR_FREE(path); VIR_FREE(autostartLink); } closedir(dir); return 0; } int virStoragePoolObjSaveDef(virStorageDriverStatePtr driver, virStoragePoolObjPtr pool, virStoragePoolDefPtr def) { char *xml; int fd = -1, ret = -1; ssize_t towrite; if (!pool->configFile) { if (virFileMakePath(driver->configDir) < 0) { virReportSystemError(errno, _("cannot create config directory %s"), driver->configDir); return -1; } if (!(pool->configFile = virFileBuildPath(driver->configDir, def->name, ".xml"))) { return -1; } if (!(pool->autostartLink = virFileBuildPath(driver->autostartDir, def->name, ".xml"))) { VIR_FREE(pool->configFile); return -1; } } if (!(xml = virStoragePoolDefFormat(def))) { virStorageReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("failed to generate XML")); return -1; } if ((fd = open(pool->configFile, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR )) < 0) { virReportSystemError(errno, _("cannot create config file %s"), pool->configFile); goto cleanup; } virEmitXMLWarning(fd, def->name, "pool-edit"); towrite = strlen(xml); if (safewrite(fd, xml, towrite) != towrite) { virReportSystemError(errno, _("cannot write config file %s"), pool->configFile); goto cleanup; } if (VIR_CLOSE(fd) < 0) { virReportSystemError(errno, _("cannot save config file %s"), pool->configFile); goto cleanup; } ret = 0; cleanup: VIR_FORCE_CLOSE(fd); VIR_FREE(xml); return ret; } int virStoragePoolObjDeleteDef(virStoragePoolObjPtr pool) { if (!pool->configFile) { virStorageReportError(VIR_ERR_INTERNAL_ERROR, _("no config file for %s"), pool->def->name); return -1; } if (unlink(pool->configFile) < 0) { virStorageReportError(VIR_ERR_INTERNAL_ERROR, _("cannot remove config for %s"), pool->def->name); return -1; } return 0; } virStoragePoolSourcePtr virStoragePoolSourceListNewSource(virStoragePoolSourceListPtr list) { virStoragePoolSourcePtr source; if (VIR_REALLOC_N(list->sources, list->nsources+1) < 0) { virReportOOMError(); return NULL; } source = &list->sources[list->nsources++]; memset(source, 0, sizeof(*source)); return source; } char *virStoragePoolSourceListFormat(virStoragePoolSourceListPtr def) { virStoragePoolOptionsPtr options; virBuffer buf = VIR_BUFFER_INITIALIZER; const char *type; int i; options = virStoragePoolOptionsForPoolType(def->type); if (options == NULL) return NULL; type = virStoragePoolTypeToString(def->type); if (!type) { virStorageReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("unexpected pool type")); goto cleanup; } virBufferAddLit(&buf, "\n"); for (i = 0; i < def->nsources; i++) { virStoragePoolSourceFormat(&buf, options, &def->sources[i]); } virBufferAddLit(&buf, "\n"); if (virBufferError(&buf)) goto no_memory; return virBufferContentAndReset(&buf); no_memory: virReportOOMError(); cleanup: virBufferFreeAndReset(&buf); return NULL; } /* * virStoragePoolObjIsDuplicate: * @doms : virStoragePoolObjListPtr to search * @def : virStoragePoolDefPtr definition of pool to lookup * @check_active: If true, ensure that pool is not active * * Returns: -1 on error * 0 if pool is new * 1 if pool is a duplicate */ int virStoragePoolObjIsDuplicate(virStoragePoolObjListPtr pools, virStoragePoolDefPtr def, unsigned int check_active) { int ret = -1; int dupPool = 0; virStoragePoolObjPtr pool = NULL; /* See if a Pool with matching UUID already exists */ pool = virStoragePoolObjFindByUUID(pools, def->uuid); if (pool) { /* UUID matches, but if names don't match, refuse it */ if (STRNEQ(pool->def->name, def->name)) { char uuidstr[VIR_UUID_STRING_BUFLEN]; virUUIDFormat(pool->def->uuid, uuidstr); virStorageReportError(VIR_ERR_OPERATION_FAILED, _("pool '%s' is already defined with uuid %s"), pool->def->name, uuidstr); goto cleanup; } if (check_active) { /* UUID & name match, but if Pool is already active, refuse it */ if (virStoragePoolObjIsActive(pool)) { virStorageReportError(VIR_ERR_OPERATION_INVALID, _("pool is already active as '%s'"), pool->def->name); goto cleanup; } } dupPool = 1; } else { /* UUID does not match, but if a name matches, refuse it */ pool = virStoragePoolObjFindByName(pools, def->name); if (pool) { char uuidstr[VIR_UUID_STRING_BUFLEN]; virUUIDFormat(pool->def->uuid, uuidstr); virStorageReportError(VIR_ERR_OPERATION_FAILED, _("pool '%s' already exists with uuid %s"), def->name, uuidstr); goto cleanup; } } ret = dupPool; cleanup: if (pool) virStoragePoolObjUnlock(pool); return ret; } void virStoragePoolObjLock(virStoragePoolObjPtr obj) { virMutexLock(&obj->lock); } void virStoragePoolObjUnlock(virStoragePoolObjPtr obj) { virMutexUnlock(&obj->lock); }