Signed-off-by: Nikolay Shirokovskiy <nshirokovskiy@xxxxxxxxxxxxx> Signed-off-by: Maxim Nestratov <mnestratov@xxxxxxxxxxxxx> --- docs/formatfs.html.in | 208 ++++++ docs/fspool.html.in | 41 ++ docs/sitemap.html.in | 4 + po/POTFILES.in | 1 + src/Makefile.am | 5 + src/conf/fs_conf.c | 1637 ++++++++++++++++++++++++++++++++++++++++++++++ src/conf/fs_conf.h | 323 +++++++++ src/libvirt_private.syms | 42 ++ 8 files changed, 2261 insertions(+) create mode 100644 docs/formatfs.html.in create mode 100644 docs/fspool.html.in create mode 100644 src/conf/fs_conf.c create mode 100644 src/conf/fs_conf.h diff --git a/docs/formatfs.html.in b/docs/formatfs.html.in new file mode 100644 index 0000000..5d3ee17 --- /dev/null +++ b/docs/formatfs.html.in @@ -0,0 +1,208 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml"> + <body> + <h1>Filesystem pool and item XML format</h1> + + <ul id="toc"></ul> + + <h2><a name="FSPool">Filesystem pool XML</a></h2> + + <p> + Filesystem pools is a facility to manage filesystems. They has a lot in common with + storage pools (nearly the same api and way of managment). However the managment units + are completely different. To avoid the misunderstanding in fspool usage use this XML + description. See <a href="fspool.html">fspool driver page</a> + </p> + + <p> + The root element is named <code>fspool</code>. It has attribute <code>type</code>, + at the moment fspool has only one type <code>dir</code>. It represents fspool that + holds filesystems in subdirectories on a host filesystem. + </p> + + <h3><a name="FSPoolFirst">General metadata</a></h3> + + <pre> + <fspool type="dir"> + <name>virfs</name> + <uuid>4584ee21-db40-4e98-980e-44802c47b62f</uuid> + <allocation>10000000</allocation> + <capacity>50000000</capacity> + <available>40000000</available> + ...</pre> + + <dl> + <dt><code>name</code></dt> + <dd>Providing a name for the fspool which is unique to the host. + This is mandatory when defining a filesystem pool. <span class="since">Since 2.2.1</span></dd> + <dt><code>uuid</code></dt> + <dd>Providing an identifier for the fspool which is globally unique. + This is optional when defining a fspool, a UUID will be generated if + omitted. <span class="since">Since 2.2.1</span></dd> + <dt><code>allocation</code></dt> + <dd>Providing the total storage allocation for the pool. This value is in bytes. + This is not applicable when creating a fspool. <span class="since">Since 2.2.1</span></dd> + <dt><code>capacity</code></dt> + <dd>Providing the total capacity for the fspool. This value is in bytes. This + is not applicable when creating a fspool. <span class="since">Since 2.2.1</span></dd> + <dt><code>available</code></dt> + <dd>Providing the free space available for allocating new items + in the fspool. This value is in bytes. This is not applicable when creating a + fspool. <span class="since">Since 2.2.1</span></dd> + </dl> + + <h3><a name="FSPoolSource">Source elements</a></h3> + + <p> + A single <code>source</code> element is contained within the top level + <code>fspool</code> element. This tag is used to describe the source of + the fspool. At the moment it is left empty. + </p> + + <h3><a name="FSPoolTarget">Target elements</a></h3> + + <p> + A single <code>target</code> element is contained within the top level + <code>fspool</code> element for all types of fspools (fspool types <code>dir</code>). + This tag is used to describe the mapping of fspool into the host filesystem. + It can contain the following child elements: + </p> + + <pre> + ... + <target> + <path>/path/in/host/fs</path> + <permissions> + <owner>107</owner> + <group>107</group> + <mode>0744</mode> + </permissions> + </target> + </fspool></pre> + + <dl> + <dt><code>path</code></dt> + <dd>Provides the location at which the fspool will be mapped into + the local filesystem namespace, as an absolute path. For a fspool with directory + backend it will be a fully qualified name of the directory in which items + will be created. + <span class="since">Since 2.2.1</span> + </dd> + <dt><code>permissions</code></dt> + <dd> It provides information about the permissions to use for the + final directory when the fspool is built. There are 4 child elements. + The <code>mode</code> element contains the octal permission set. + The <code>mode</code> defaults to 0755 when not provided. + The <code>owner</code> element contains the numeric user ID. + The <code>group</code> element contains the numeric group ID. + If <code>owner</code> or <code>group</code> aren't specified when + creating a directory, the values are inherited from the parent + directory. + </dd> + </dl> + + <h2><a name="FSItem">Item XML</a></h2> + <p> + An item is a directory that holds single filesystem. The filesystem item + XML format is available <span class="since">since 2.2.1</span> + </p> + + <h3><a name="FSItemFirst">General metadata</a></h3> + + <pre> + <item> + <name>item1</name> + <key>/path/to/fspool/</key> + <allocation>0</allocation> + <capacity unit="T">1</capacity> + ...</pre> + + <dl> + <dt><code>name</code></dt> + <dd>Providing a name for the item which is unique to the fspool. + This is mandatory when defining an item.</dd> + <dt><code>key</code></dt> + <dd>Providing an identifier for the item which identifies a + single item. It is impossible to have two distinct keys + identifying a single item. This field cannot be set when creating + an item: it is always generated. + <span class="since">Since 2.2.1</span></dd> + <dt><code>allocation</code></dt> + <dd>Providing the total storage allocation for the item.</dd> + <dt><code>capacity</code></dt> + <dd>Providing the logical capacity for the item.</dd> + </dl> + + <h3><a name="FsItemTarget">Target elements</a></h3> + + <p> + A single <code>target</code> element is contained within the top level + <code>item</code> element. This tag is used to describe the mapping of + the fspool item into the host filesystem. It can contain the following + child elements: + </p> + + <pre> + ... + <target> + <path>/var/lib/virt/images/sparse.img</path> + <format type='qcow2'/> + <permissions> + <owner>107</owner> + <group>107</group> + <mode>0744</mode> + </permissions> + </target></pre> + + <dl> + <dt><code>permissions</code></dt> + <dd>Provides information about the permissions to use + when creating items. There are 4 child elements. + The <code>mode</code> element contains the octal permission set. + The <code>mode</code> defaults to 0600 when not provided. + The <code>owner</code> element contains the numeric user ID. + The <code>group</code> element contains the numeric group ID. + If <code>owner</code> or <code>group</code> aren't specified when + creating fspool item, the values are inherited from the parent + directory. + </dd> + </dl> + + + <h2><a name="examples">Example configuration</a></h2> + + <p> + Here is an example of fspool and item. + </p> + + <h3><a name="exampleDir">Directory based fspool</a></h3> + + <pre> + <fspool type="dir"> + <name>my_fspool</name> + <source> + </source> + <target> + <path>/my_fspool_dir</path> + </target> + </fspool></pre> + + <h3><a name="exampleItem">Fspool Item</a></h3> + + <pre> + <item> + <name>item1</name> + <allocation>0</allocation> + <capacity unit="T">1</capacity> + <target> + <permissions> + <owner>107</owner> + <group>107</group> + <mode>0744</mode> + </permissions> + </target> + </item></pre> + + </body> +</html> diff --git a/docs/fspool.html.in b/docs/fspool.html.in new file mode 100644 index 0000000..bee110d --- /dev/null +++ b/docs/fspool.html.in @@ -0,0 +1,41 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml"> + <body> + <h1 >Filesystem Pool Management</h1> + <p> + Libvirt provides filesystems managment on the physical host through + fspools and items. + </p> + <p> + Fspool is the entity that holds filesystems. It has a lot in + common with storage pools and volumes. However, fspool deals with + filesystems and not with the block devices. Single filesystem in fspool + is called an item. + </p> + <p> + Libvirt supports the following fspool types: + </p> + <ul> + <li> + <a href="#FSPoolBackendDir">Directory backend</a> + </li> + </ul> + + <h2><a name="FSPoolBackendDir">Fspool with directory backend</a></h2> + <p> + A fspool with a type of <code>dir</code> provides the means to manage + filesystems within a directory on a single host. + </p> + + <h3>Example pool input definition</h3> + <pre> + <fspool type="dir"> + <name>virtfs</name> + <target> + <path>/var/lib/virt/fs</path> + </target> + </fspool></pre> + + </body> +</html> diff --git a/docs/sitemap.html.in b/docs/sitemap.html.in index 757d5aa..cde6cf1 100644 --- a/docs/sitemap.html.in +++ b/docs/sitemap.html.in @@ -181,6 +181,10 @@ <span>The storage pool and volume XML format</span> </li> <li> + <a href="formatfs.html">Filesystem pool</a> + <span>The filesystem pool and filesystem item XML format</span> + </li> + <li> <a href="formatstorageencryption.html">Storage Encryption</a> <span>Storage volume encryption XML format</span> </li> diff --git a/po/POTFILES.in b/po/POTFILES.in index af00155..f4d2f25 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -25,6 +25,7 @@ src/conf/domain_addr.c src/conf/domain_capabilities.c src/conf/domain_conf.c src/conf/domain_event.c +src/conf/fs_conf.c src/conf/interface_conf.c src/conf/netdev_bandwidth_conf.c src/conf/netdev_vlan_conf.c diff --git a/src/Makefile.am b/src/Makefile.am index 7b911bd..d3c6e67 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -399,6 +399,10 @@ CHRDEV_CONF_SOURCES = \ DEVICE_CONF_SOURCES = \ conf/device_conf.c conf/device_conf.h +# FS driver generic impl APIs +FS_CONF_SOURCES = \ + conf/fs_conf.h conf/fs_conf.c + CONF_SOURCES = \ $(NETDEV_CONF_SOURCES) \ $(DOMAIN_CONF_SOURCES) \ @@ -411,6 +415,7 @@ CONF_SOURCES = \ $(NWFILTER_CONF_SOURCES) \ $(NODE_DEVICE_CONF_SOURCES) \ $(STORAGE_CONF_SOURCES) \ + $(FS_CONF_SOURCES) \ $(INTERFACE_CONF_SOURCES) \ $(SECRET_CONF_SOURCES) \ $(CPU_CONF_SOURCES) \ diff --git a/src/conf/fs_conf.c b/src/conf/fs_conf.c new file mode 100644 index 0000000..80f2664 --- /dev/null +++ b/src/conf/fs_conf.c @@ -0,0 +1,1637 @@ +/* + * fs_conf.c: config handling for fs driver + * Author: Olga Krishtal <okrishtal@xxxxxxxxxxxxx> + * + * Copyright (C) 2016 Parallels IP Holdings GmbH + * + * 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, see + * <http://www.gnu.org/licenses/>. + */ + +#include <config.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> + +#include "virerror.h" +#include "datatypes.h" +#include "fs_conf.h" + +#include "virxml.h" +#include "viruuid.h" +#include "virbuffer.h" +#include "viralloc.h" +#include "virfile.h" +#include "virstring.h" +#include "virlog.h" + +#define VIR_FROM_THIS VIR_FROM_FSPOOL + +VIR_LOG_INIT("conf.fs_conf"); + +VIR_ENUM_IMPL(virFSPool, + VIR_FSPOOL_LAST, + "dir") + +VIR_ENUM_IMPL(virFSItem, + VIR_FSITEM_LAST, + "dir") + +/* Flags to indicate mandatory components in the fspool source */ +enum { + VIR_FSPOOL_SOURCE_DIR = (1 << 2), + VIR_FSPOOL_SOURCE_NAME = (1 << 4), + VIR_FSPOOL_SOURCE_NETWORK = (1 << 6), +}; + +typedef const char *(*virFSItemFormatToString)(int format); +typedef int (*virFSItemFormatFromString)(const char *format); + +typedef const char *(*virFSPoolFormatToString)(int format); +typedef int (*virFSPoolFormatFromString)(const char *format); + +typedef struct _virFSItemOptions virFSItemOptions; +typedef virFSItemOptions *virFSItemOptionsPtr; +struct _virFSItemOptions { + int defaultFormat; + virFSItemFormatToString formatToString; + virFSItemFormatFromString formatFromString; +}; + +typedef struct _virFSPoolOptions virFSPoolOptions; +typedef virFSPoolOptions *virFSPoolOptionsPtr; +struct _virFSPoolOptions { + unsigned int flags; + int defaultFormat; + virFSPoolFormatToString formatToString; + virFSPoolFormatFromString formatFromString; +}; + +typedef struct _virFSPoolTypeInfo virFSPoolTypeInfo; +typedef virFSPoolTypeInfo *virFSPoolTypeInfoPtr; +struct _virFSPoolTypeInfo { + int fspoolType; + virFSPoolOptions fspoolOptions; + virFSItemOptions itemOptions; +}; + +static virFSPoolTypeInfo fspoolTypeInfo[] = { + {.fspoolType = VIR_FSPOOL_DIR} +}; + + +static virFSPoolTypeInfoPtr +virFSPoolTypeInfoLookup(int type) +{ + size_t i; + for (i = 0; i < ARRAY_CARDINALITY(fspoolTypeInfo); i++) + if (fspoolTypeInfo[i].fspoolType == type) + return &fspoolTypeInfo[i]; + + virReportError(VIR_ERR_INTERNAL_ERROR, + _("missing backend for fspool type %d"), type); + return NULL; +} + +static virFSPoolOptionsPtr +virFSPoolOptionsForPoolType(int type) +{ + virFSPoolTypeInfoPtr backend = virFSPoolTypeInfoLookup(type); + if (backend == NULL) + return NULL; + return &backend->fspoolOptions; +} + +static virFSItemOptionsPtr +virFSItemOptionsForPoolType(int type) +{ + virFSPoolTypeInfoPtr backend = virFSPoolTypeInfoLookup(type); + if (backend == NULL) + return NULL; + return &backend->itemOptions; +} + +static void +virFSSourcePoolDefFree(virFSSourcePoolDefPtr def) +{ + if (!def) + return; + + VIR_FREE(def->pool); + VIR_FREE(def->item); + + VIR_FREE(def); +} + +static void +virFSPermsFree(virFSPermsPtr def) +{ + if (!def) + return; + + VIR_FREE(def->label); + VIR_FREE(def); +} + +static void +virFSSourceClear(virFSSourcePtr def) +{ + if (!def) + return; + + VIR_FREE(def->path); + virFSSourcePoolDefFree(def->srcpool); + VIR_FREE(def->driverName); + virFSPermsFree(def->perms); +} + +void +virFSItemDefFree(virFSItemDefPtr def) +{ + if (!def) + return; + + VIR_FREE(def->name); + VIR_FREE(def->key); + + virFSSourceClear(&def->target); + VIR_FREE(def); +} + +void +virFSPoolSourceClear(virFSPoolSourcePtr source) +{ + if (!source) + return; + + VIR_FREE(source->dir); + VIR_FREE(source->name); + VIR_FREE(source->product); +} + +void +virFSPoolSourceFree(virFSPoolSourcePtr source) +{ + virFSPoolSourceClear(source); + VIR_FREE(source); +} + +void +virFSPoolDefFree(virFSPoolDefPtr def) +{ + if (!def) + return; + + VIR_FREE(def->name); + + virFSPoolSourceClear(&def->source); + + VIR_FREE(def->target.path); + VIR_FREE(def->target.perms.label); + VIR_FREE(def); +} + +void +virFSPoolObjFree(virFSPoolObjPtr obj) +{ + if (!obj) + return; + + virFSPoolObjClearItems(obj); + + virFSPoolDefFree(obj->def); + virFSPoolDefFree(obj->newDef); + + VIR_FREE(obj->configFile); + VIR_FREE(obj->autostartLink); + + virMutexDestroy(&obj->lock); + + VIR_FREE(obj); +} + +void +virFSPoolObjListFree(virFSPoolObjListPtr fspools) +{ + size_t i; + for (i = 0; i < fspools->count; i++) + virFSPoolObjFree(fspools->objs[i]); + VIR_FREE(fspools->objs); + fspools->count = 0; +} + +void +virFSPoolObjRemove(virFSPoolObjListPtr fspools, + virFSPoolObjPtr fspool) +{ + size_t i; + + virFSPoolObjUnlock(fspool); + + for (i = 0; i < fspools->count; i++) { + virFSPoolObjLock(fspools->objs[i]); + if (fspools->objs[i] == fspool) { + virFSPoolObjUnlock(fspools->objs[i]); + virFSPoolObjFree(fspools->objs[i]); + + VIR_DELETE_ELEMENT(fspools->objs, i, fspools->count); + break; + } + virFSPoolObjUnlock(fspools->objs[i]); + } +} + +static int +virFSPoolDefParseSource(xmlXPathContextPtr ctxt, + virFSPoolSourcePtr source, + int fspool_type, + xmlNodePtr node) +{ + int ret = -1; + xmlNodePtr relnode /*, authnode, *nodeset = NULL*/; + virFSPoolOptionsPtr options; + + relnode = ctxt->node; + ctxt->node = node; + + if ((options = virFSPoolOptionsForPoolType(fspool_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) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("unknown fspool format type %s"), format); + VIR_FREE(format); + goto cleanup; + } + VIR_FREE(format); + } + + source->dir = virXPathString("string(./dir/@path)", ctxt); + + source->product = virXPathString("string(./product/@name)", ctxt); + + ret = 0; + cleanup: + ctxt->node = relnode; + + return ret; +} + +virFSPoolSourcePtr +virFSPoolDefParseSourceString(const char *srcSpec, + int fspool_type) +{ + xmlDocPtr doc = NULL; + xmlNodePtr node = NULL; + xmlXPathContextPtr xpath_ctxt = NULL; + virFSPoolSourcePtr def = NULL, ret = NULL; + + if (!(doc = virXMLParseStringCtxt(srcSpec, + _("(storage_source_specification)"), + &xpath_ctxt))) + goto cleanup; + + if (VIR_ALLOC(def) < 0) + goto cleanup; + + if (!(node = virXPathNode("/source", xpath_ctxt))) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("root element was not source")); + goto cleanup; + } + + if (virFSPoolDefParseSource(xpath_ctxt, def, fspool_type, + node) < 0) + goto cleanup; + + ret = def; + def = NULL; + cleanup: + virFSPoolSourceFree(def); + xmlFreeDoc(doc); + xmlXPathFreeContext(xpath_ctxt); + + return ret; +} + +static int +virFSDefParsePerms(xmlXPathContextPtr ctxt, + virFSPermsPtr perms, + const char *permxpath) +{ + char *mode; + long long val; + int ret = -1; + xmlNodePtr relnode; + xmlNodePtr node; + + node = virXPathNode(permxpath, ctxt); + if (node == NULL) { + /* Set default values if there is not <permissions> element */ + perms->mode = (mode_t) -1; + perms->uid = (uid_t) -1; + perms->gid = (gid_t) -1; + perms->label = NULL; + return 0; + } + + relnode = ctxt->node; + ctxt->node = node; + + if ((mode = virXPathString("string(./mode)", ctxt))) { + int tmp; + + if (virStrToLong_i(mode, NULL, 8, &tmp) < 0 || (tmp & ~0777)) { + VIR_FREE(mode); + virReportError(VIR_ERR_XML_ERROR, "%s", + _("malformed octal mode")); + goto error; + } + perms->mode = tmp; + VIR_FREE(mode); + } else { + perms->mode = (mode_t) -1; + } + + if (virXPathNode("./owner", ctxt) == NULL) { + perms->uid = (uid_t) -1; + } else { + /* We previously could output -1, so continue to parse it */ + if (virXPathLongLong("number(./owner)", ctxt, &val) < 0 || + ((uid_t)val != val && + val != -1)) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("malformed owner element")); + goto error; + } + + perms->uid = val; + } + + if (virXPathNode("./group", ctxt) == NULL) { + perms->gid = (gid_t) -1; + } else { + /* We previously could output -1, so continue to parse it */ + if (virXPathLongLong("number(./group)", ctxt, &val) < 0 || + ((gid_t) val != val && + val != -1)) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("malformed group element")); + goto error; + } + perms->gid = val; + } + + /* 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 virFSPoolDefPtr +virFSPoolDefParseXML(xmlXPathContextPtr ctxt) +{ + virFSPoolOptionsPtr options; + virFSPoolDefPtr ret; + xmlNodePtr source_node; + char *type = NULL; + char *uuid = NULL; + char *target_path = NULL; + + if (VIR_ALLOC(ret) < 0) + return NULL; + + type = virXPathString("string(./@type)", ctxt); + if (type == NULL) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("fspool missing type attribute")); + goto error; + } + + if ((ret->type = virFSPoolTypeFromString(type)) < 0) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("unknown fspool type %s"), type); + goto error; + } + + if ((options = virFSPoolOptionsForPoolType(ret->type)) == NULL) + goto error; + + source_node = virXPathNode("./source", ctxt); + if (source_node) { + if (virFSPoolDefParseSource(ctxt, &ret->source, ret->type, + source_node) < 0) + goto error; + } + + ret->name = virXPathString("string(./name)", ctxt); + if (ret->name == NULL && + options->flags & VIR_FSPOOL_SOURCE_NAME) + ret->name = ret->source.name; + if (ret->name == NULL) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("missing pool source name element")); + goto error; + } + + if (strchr(ret->name, '/')) { + virReportError(VIR_ERR_XML_ERROR, + _("name %s cannot contain '/'"), ret->name); + goto error; + } + + uuid = virXPathString("string(./uuid)", ctxt); + if (uuid == NULL) { + if (virUUIDGenerate(ret->uuid) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("unable to generate uuid")); + goto error; + } + } else { + if (virUUIDParse(uuid, ret->uuid) < 0) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("malformed uuid element")); + goto error; + } + } + + + if (options->flags & VIR_FSPOOL_SOURCE_DIR) { + if (!ret->source.dir) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("missing storage pool source path")); + goto error; + } + } + if (options->flags & VIR_FSPOOL_SOURCE_NAME) { + if (ret->source.name == NULL) { + /* source name defaults to pool name */ + if (VIR_STRDUP(ret->source.name, ret->name) < 0) + goto error; + } + } + target_path = virXPathString("string(./target/path)", ctxt); + if (!target_path) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("missing storage pool target path")); + goto error; + } + ret->target.path = virFileSanitizePath(target_path); + if (!ret->target.path) + goto error; + + if (virFSDefParsePerms(ctxt, &ret->target.perms, + "./target/permissions") < 0) + goto error; + + cleanup: + VIR_FREE(uuid); + VIR_FREE(type); + VIR_FREE(target_path); + return ret; + + error: + virFSPoolDefFree(ret); + ret = NULL; + goto cleanup; +} + +virFSPoolDefPtr +virFSPoolDefParseNode(xmlDocPtr xml, + xmlNodePtr root) +{ + xmlXPathContextPtr ctxt = NULL; + virFSPoolDefPtr def = NULL; + + if (!xmlStrEqual(root->name, BAD_CAST "fspool")) { + virReportError(VIR_ERR_XML_ERROR, + _("unexpected root element <%s>, " + "expecting <fspool>"), + root->name); + goto cleanup; + } + + ctxt = xmlXPathNewContext(xml); + if (ctxt == NULL) { + virReportOOMError(); + goto cleanup; + } + + ctxt->node = root; + def = virFSPoolDefParseXML(ctxt); + cleanup: + xmlXPathFreeContext(ctxt); + return def; +} + +static virFSPoolDefPtr +virFSPoolDefParse(const char *xmlStr, + const char *filename) +{ + virFSPoolDefPtr ret = NULL; + xmlDocPtr xml; + + if ((xml = virXMLParse(filename, xmlStr, _("(fs_pool_definition)")))) { + ret = virFSPoolDefParseNode(xml, xmlDocGetRootElement(xml)); + xmlFreeDoc(xml); + } + + return ret; +} + +virFSPoolDefPtr +virFSPoolDefParseString(const char *xmlStr) +{ + return virFSPoolDefParse(xmlStr, NULL); +} + +virFSPoolDefPtr +virFSPoolDefParseFile(const char *filename) +{ + return virFSPoolDefParse(NULL, filename); +} + +static int +virFSPoolSourceFormat(virBufferPtr buf, + virFSPoolOptionsPtr options, + virFSPoolSourcePtr src) +{ + + virBufferAddLit(buf, "<source>\n"); + virBufferAdjustIndent(buf, 2); + + if (options->flags & VIR_FSPOOL_SOURCE_DIR) + virBufferEscapeString(buf, "<dir path='%s'/>\n", src->dir); + + if (options->flags & VIR_FSPOOL_SOURCE_NAME) + virBufferEscapeString(buf, "<name>%s</name>\n", src->name); + + if (options->formatToString) { + const char *format = (options->formatToString)(src->format); + if (!format) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("unknown pool format number %d"), + src->format); + return -1; + } + virBufferAsprintf(buf, "<format type='%s'/>\n", format); + } + + + virBufferEscapeString(buf, "<product name='%s'/>\n", src->product); + + virBufferAdjustIndent(buf, -2); + virBufferAddLit(buf, "</source>\n"); + return 0; +} + + +static int +virFSPoolDefFormatBuf(virBufferPtr buf, + virFSPoolDefPtr def) +{ + virFSPoolOptionsPtr options; + char uuid[VIR_UUID_STRING_BUFLEN]; + const char *type; + + options = virFSPoolOptionsForPoolType(def->type); + if (options == NULL) + return -1; + + type = virFSPoolTypeToString(def->type); + if (!type) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("unexpected fspool type")); + return -1; + } + virBufferAsprintf(buf, "<fspool type='%s'>\n", type); + virBufferAdjustIndent(buf, 2); + virBufferEscapeString(buf, "<name>%s</name>\n", def->name); + + virUUIDFormat(def->uuid, uuid); + virBufferAsprintf(buf, "<uuid>%s</uuid>\n", uuid); + + virBufferAsprintf(buf, "<capacity unit='bytes'>%llu</capacity>\n", + def->capacity); + virBufferAsprintf(buf, "<allocation unit='bytes'>%llu</allocation>\n", + def->allocation); + virBufferAsprintf(buf, "<available unit='bytes'>%llu</available>\n", + def->available); + + if (virFSPoolSourceFormat(buf, options, &def->source) < 0) + return -1; + + virBufferAddLit(buf, "<target>\n"); + virBufferAdjustIndent(buf, 2); + + virBufferEscapeString(buf, "<path>%s</path>\n", def->target.path); + + if (def->target.perms.mode != (mode_t) -1 || + def->target.perms.uid != (uid_t) -1 || + def->target.perms.gid != (gid_t) -1 || + def->target.perms.label) { + virBufferAddLit(buf, "<permissions>\n"); + virBufferAdjustIndent(buf, 2); + if (def->target.perms.mode != (mode_t) -1) + virBufferAsprintf(buf, "<mode>0%o</mode>\n", + def->target.perms.mode); + if (def->target.perms.uid != (uid_t) -1) + virBufferAsprintf(buf, "<owner>%d</owner>\n", + (int) def->target.perms.uid); + if (def->target.perms.gid != (gid_t) -1) + virBufferAsprintf(buf, "<group>%d</group>\n", + (int) def->target.perms.gid); + virBufferEscapeString(buf, "<label>%s</label>\n", + def->target.perms.label); + + virBufferAdjustIndent(buf, -2); + virBufferAddLit(buf, "</permissions>\n"); + } + + virBufferAdjustIndent(buf, -2); + virBufferAddLit(buf, "</target>\n"); + + virBufferAdjustIndent(buf, -2); + virBufferAddLit(buf, "</fspool>\n"); + + return 0; +} + +char * +virFSPoolDefFormat(virFSPoolDefPtr def) +{ + virBuffer buf = VIR_BUFFER_INITIALIZER; + + if (virFSPoolDefFormatBuf(&buf, def) < 0) + goto error; + + if (virBufferCheckError(&buf) < 0) + goto error; + + return virBufferContentAndReset(&buf); + + error: + virBufferFreeAndReset(&buf); + return NULL; +} + + +static int +virFSSize(const char *unit, + const char *val, + unsigned long long *ret) +{ + if (virStrToLong_ull(val, NULL, 10, ret) < 0) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("malformed capacity element")); + return -1; + } + /* off_t is signed, so you cannot create a file larger than 2**63 + * bytes in the first place. */ + if (virScaleInteger(ret, unit, 1, LLONG_MAX) < 0) + return -1; + + return 0; +} + +static virFSItemDefPtr +virFSItemDefParseXML(virFSPoolDefPtr fspool, + xmlXPathContextPtr ctxt, + unsigned int flags) +{ + virFSItemDefPtr ret; + virFSItemOptionsPtr options; + char *type = NULL; + char *allocation = NULL; + char *capacity = NULL; + char *unit = NULL; + xmlNodePtr *nodes = NULL; + + virCheckFlags(VIR_ITEM_XML_PARSE_NO_CAPACITY | + VIR_ITEM_XML_PARSE_OPT_CAPACITY, NULL); + + options = virFSItemOptionsForPoolType(fspool->type); + if (options == NULL) + return NULL; + + if (VIR_ALLOC(ret) < 0) + return NULL; + + ret->name = virXPathString("string(./name)", ctxt); + if (ret->name == NULL) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("missing item name element")); + goto error; + } + + /* Normally generated by pool refresh, but useful for unit tests */ + ret->key = virXPathString("string(./key)", ctxt); + + /* Technically overridden by pool refresh, but useful for unit tests */ + type = virXPathString("string(./@type)", ctxt); + if (type) { + if ((ret->type = virFSItemTypeFromString(type)) < 0) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("unknown item type '%s'"), type); + goto error; + } + } + + capacity = virXPathString("string(./capacity)", ctxt); + unit = virXPathString("string(./capacity/@unit)", ctxt); + if (capacity) { + if (virFSSize(unit, capacity, &ret->target.capacity) < 0) + goto error; + } else if (!(flags & VIR_ITEM_XML_PARSE_NO_CAPACITY) && + !((flags & VIR_ITEM_XML_PARSE_OPT_CAPACITY))) { + virReportError(VIR_ERR_XML_ERROR, "%s", _("missing capacity element")); + goto error; + } + VIR_FREE(unit); + + allocation = virXPathString("string(./allocation)", ctxt); + if (allocation) { + unit = virXPathString("string(./allocation/@unit)", ctxt); + if (virFSSize(unit, allocation, &ret->target.allocation) < 0) + goto error; + } else { + ret->target.allocation = ret->target.capacity; + } + + ret->target.path = virXPathString("string(./target/path)", ctxt); + + if (VIR_ALLOC(ret->target.perms) < 0) + goto error; + if (virFSDefParsePerms(ctxt, ret->target.perms, + "./target/permissions") < 0) + goto error; + + cleanup: + VIR_FREE(nodes); + VIR_FREE(allocation); + VIR_FREE(capacity); + VIR_FREE(unit); + VIR_FREE(type); + return ret; + + error: + virFSItemDefFree(ret); + ret = NULL; + goto cleanup; +} + +virFSItemDefPtr +virFSItemDefParseNode(virFSPoolDefPtr fspool, + xmlDocPtr xml, + xmlNodePtr root, + unsigned int flags) +{ + xmlXPathContextPtr ctxt = NULL; + virFSItemDefPtr def = NULL; + + if (!xmlStrEqual(root->name, BAD_CAST "item")) { + virReportError(VIR_ERR_XML_ERROR, + _("unexpected root element <%s>, " + "expecting <item>"), + root->name); + goto cleanup; + } + + ctxt = xmlXPathNewContext(xml); + if (ctxt == NULL) { + virReportOOMError(); + goto cleanup; + } + + ctxt->node = root; + def = virFSItemDefParseXML(fspool, ctxt, flags); + cleanup: + xmlXPathFreeContext(ctxt); + return def; +} + +static virFSItemDefPtr +virFSItemDefParse(virFSPoolDefPtr fspool, + const char *xmlStr, + const char *filename, + unsigned int flags) +{ + virFSItemDefPtr ret = NULL; + xmlDocPtr xml; + + if ((xml = virXMLParse(filename, xmlStr, _("(fspool_item_definition)")))) { + ret = virFSItemDefParseNode(fspool, xml, xmlDocGetRootElement(xml), flags); + xmlFreeDoc(xml); + } + + return ret; +} + +virFSItemDefPtr +virFSItemDefParseString(virFSPoolDefPtr fspool, + const char *xmlStr, + unsigned int flags) +{ + return virFSItemDefParse(fspool, xmlStr, NULL, flags); +} + +virFSItemDefPtr +virFSItemDefParseFile(virFSPoolDefPtr fspool, + const char *filename, + unsigned int flags) +{ + return virFSItemDefParse(fspool, NULL, filename, flags); +} + +static int +virFSItemTargetDefFormat(virFSItemOptionsPtr options ATTRIBUTE_UNUSED, + virBufferPtr buf, + virFSSourcePtr def, + const char *type) +{ + virBufferAsprintf(buf, "<%s>\n", type); + virBufferAdjustIndent(buf, 2); + + if (def->perms && + (def->perms->mode != (mode_t) -1 || + def->perms->uid != (uid_t) -1 || + def->perms->gid != (gid_t) -1 || + def->perms->label)) { + virBufferAddLit(buf, "<permissions>\n"); + virBufferAdjustIndent(buf, 2); + + if (def->perms->mode != (mode_t) -1) + virBufferAsprintf(buf, "<mode>0%o</mode>\n", + def->perms->mode); + if (def->perms->uid != (uid_t) -1) + virBufferAsprintf(buf, "<owner>%d</owner>\n", + (int) def->perms->uid); + if (def->perms->gid != (gid_t) -1) + virBufferAsprintf(buf, "<group>%d</group>\n", + (int) def->perms->gid); + + virBufferEscapeString(buf, "<label>%s</label>\n", + def->perms->label); + + virBufferAdjustIndent(buf, -2); + virBufferAddLit(buf, "</permissions>\n"); + } + + virBufferAdjustIndent(buf, -2); + virBufferAsprintf(buf, "</%s>\n", type); + return 0; +} + +char * +virFSItemDefFormat(virFSPoolDefPtr fspool, + virFSItemDefPtr def) +{ + virFSItemOptionsPtr options; + virBuffer buf = VIR_BUFFER_INITIALIZER; + + options = virFSItemOptionsForPoolType(fspool->type); + if (options == NULL) + return NULL; + + virBufferAddLit(&buf, "<item>\n"); + virBufferAdjustIndent(&buf, 2); + + virBufferEscapeString(&buf, "<name>%s</name>\n", def->name); + virBufferEscapeString(&buf, "<key>%s</key>\n", def->key); + + virBufferAsprintf(&buf, "<capacity unit='bytes'>%llu</capacity>\n", + def->target.capacity); + virBufferAsprintf(&buf, "<allocation unit='bytes'>%llu</allocation>\n", + def->target.allocation); + + if (virFSItemTargetDefFormat(options, &buf, + &def->target, "target") < 0) + goto cleanup; + + virBufferAdjustIndent(&buf, -2); + virBufferAddLit(&buf, "</item>\n"); + + if (virBufferCheckError(&buf) < 0) + goto cleanup; + + return virBufferContentAndReset(&buf); + + cleanup: + virBufferFreeAndReset(&buf); + return NULL; +} + + +virFSPoolObjPtr +virFSPoolObjFindByUUID(virFSPoolObjListPtr fspools, + const unsigned char *uuid) +{ + size_t i; + + for (i = 0; i < fspools->count; i++) { + virFSPoolObjLock(fspools->objs[i]); + if (!memcmp(fspools->objs[i]->def->uuid, uuid, VIR_UUID_BUFLEN)) + return fspools->objs[i]; + virFSPoolObjUnlock(fspools->objs[i]); + } + + return NULL; +} + +virFSPoolObjPtr +virFSPoolObjFindByName(virFSPoolObjListPtr fspools, + const char *name) +{ + size_t i; + + for (i = 0; i < fspools->count; i++) { + virFSPoolObjLock(fspools->objs[i]); + if (STREQ(fspools->objs[i]->def->name, name)) + return fspools->objs[i]; + virFSPoolObjUnlock(fspools->objs[i]); + } + + return NULL; +} + + +void +virFSPoolObjClearItems(virFSPoolObjPtr fspool) +{ + size_t i; + for (i = 0; i < fspool->items.count; i++) + virFSItemDefFree(fspool->items.objs[i]); + + VIR_FREE(fspool->items.objs); + fspool->items.count = 0; +} + +virFSItemDefPtr +virFSItemDefFindByKey(virFSPoolObjPtr fspool, + const char *key) +{ + size_t i; + + for (i = 0; i < fspool->items.count; i++) + if (STREQ(fspool->items.objs[i]->key, key)) + return fspool->items.objs[i]; + + return NULL; +} + +virFSItemDefPtr +virFSItemDefFindByPath(virFSPoolObjPtr fspool, + const char *path) +{ + size_t i; + + for (i = 0; i < fspool->items.count; i++) + if (STREQ(fspool->items.objs[i]->target.path, path)) + return fspool->items.objs[i]; + + return NULL; +} + +virFSItemDefPtr +virFSItemDefFindByName(virFSPoolObjPtr fspool, + const char *name) +{ + size_t i; + + for (i = 0; i < fspool->items.count; i++) + if (STREQ(fspool->items.objs[i]->name, name)) + return fspool->items.objs[i]; + + return NULL; +} + +virFSPoolObjPtr +virFSPoolObjAssignDef(virFSPoolObjListPtr fspools, + virFSPoolDefPtr def) +{ + virFSPoolObjPtr fspool; + + if ((fspool = virFSPoolObjFindByName(fspools, def->name))) { + if (!virFSPoolObjIsActive(fspool)) { + virFSPoolDefFree(fspool->def); + fspool->def = def; + } else { + virFSPoolDefFree(fspool->newDef); + fspool->newDef = def; + } + return fspool; + } + + if (VIR_ALLOC(fspool) < 0) + return NULL; + + if (virMutexInit(&fspool->lock) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("cannot initialize mutex")); + VIR_FREE(fspool); + return NULL; + } + virFSPoolObjLock(fspool); + fspool->active = 0; + + if (VIR_APPEND_ELEMENT_COPY(fspools->objs, fspools->count, fspool) < 0) { + virFSPoolObjUnlock(fspool); + virFSPoolObjFree(fspool); + return NULL; + } + fspool->def = def; + + return fspool; +} + +static virFSPoolObjPtr +virFSPoolObjLoad(virFSPoolObjListPtr fspools, + const char *file, + const char *path, + const char *autostartLink) +{ + virFSPoolDefPtr def; + virFSPoolObjPtr fspool; + + if (!(def = virFSPoolDefParseFile(path))) + return NULL; + + if (!virFileMatchesNameSuffix(file, def->name, ".xml")) { + virReportError(VIR_ERR_XML_ERROR, + _("Storage fspool config filename '%s' does " + "not match fspool name '%s'"), + path, def->name); + virFSPoolDefFree(def); + return NULL; + } + + if (!(fspool = virFSPoolObjAssignDef(fspools, def))) { + virFSPoolDefFree(def); + return NULL; + } + + VIR_FREE(fspool->configFile); /* for driver reload */ + if (VIR_STRDUP(fspool->configFile, path) < 0) { + virFSPoolObjRemove(fspools, fspool); + return NULL; + } + VIR_FREE(fspool->autostartLink); /* for driver reload */ + if (VIR_STRDUP(fspool->autostartLink, autostartLink) < 0) { + virFSPoolObjRemove(fspools, fspool); + return NULL; + } + + fspool->autostart = virFileLinkPointsTo(fspool->autostartLink, + fspool->configFile); + + return fspool; +} + + +virFSPoolObjPtr +virFSPoolLoadState(virFSPoolObjListPtr fspools, + const char *stateDir, + const char *name) +{ + char *stateFile = NULL; + virFSPoolDefPtr def = NULL; + virFSPoolObjPtr fspool = NULL; + xmlDocPtr xml = NULL; + xmlXPathContextPtr ctxt = NULL; + xmlNodePtr node = NULL; + + if (!(stateFile = virFileBuildPath(stateDir, name, ".xml"))) + goto error; + + if (!(xml = virXMLParseCtxt(stateFile, NULL, _("(fspool state)"), &ctxt))) + goto error; + + if (!(node = virXPathNode("//fspool", ctxt))) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Could not find any 'fspool' element in state file")); + goto error; + } + + ctxt->node = node; + if (!(def = virFSPoolDefParseXML(ctxt))) + goto error; + + if (STRNEQ(name, def->name)) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Storage fspool state file '%s' does not match " + "fspool name '%s'"), + stateFile, def->name); + goto error; + } + + /* create the object */ + if (!(fspool = virFSPoolObjAssignDef(fspools, def))) + goto error; + + /* XXX: future handling of some additional useful status data, + * for now, if a status file for a fspool exists, the fspool will be marked + * as active + */ + + fspool->active = 1; + + cleanup: + VIR_FREE(stateFile); + xmlFreeDoc(xml); + xmlXPathFreeContext(ctxt); + return fspool; + + error: + virFSPoolDefFree(def); + goto cleanup; +} + + +int +virFSPoolLoadAllState(virFSPoolObjListPtr fspools, + const char *stateDir) +{ + DIR *dir; + struct dirent *entry; + int ret = -1; + int rc; + + if ((rc = virDirOpenIfExists(&dir, stateDir)) <= 0) + return rc; + + while ((ret = virDirRead(dir, &entry, stateDir)) > 0) { + virFSPoolObjPtr fspool; + + if (!virFileStripSuffix(entry->d_name, ".xml")) + continue; + + if (!(fspool = virFSPoolLoadState(fspools, stateDir, entry->d_name))) + continue; + virFSPoolObjUnlock(fspool); + } + + VIR_DIR_CLOSE(dir); + return ret; +} + + +int +virFSPoolLoadAllConfigs(virFSPoolObjListPtr fspools, + const char *configDir, + const char *autostartDir) +{ + DIR *dir; + struct dirent *entry; + int ret; + int rc; + + if ((rc = virDirOpenIfExists(&dir, configDir)) <= 0) + return rc; + + while ((ret = virDirRead(dir, &entry, configDir)) > 0) { + char *path; + char *autostartLink; + virFSPoolObjPtr fspool; + + 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; + } + + fspool = virFSPoolObjLoad(fspools, entry->d_name, path, + autostartLink); + if (fspool) + virFSPoolObjUnlock(fspool); + + VIR_FREE(path); + VIR_FREE(autostartLink); + } + + VIR_DIR_CLOSE(dir); + return ret; +} + + +static int virFSPoolSaveXML(const char *path, + virFSPoolDefPtr def, + const char *xml) +{ + char uuidstr[VIR_UUID_STRING_BUFLEN]; + int ret = -1; + + virUUIDFormat(def->uuid, uuidstr); + ret = virXMLSaveFile(path, + virXMLPickShellSafeComment(def->name, uuidstr), + "fspool-edit", xml); + + return ret; +} + + +int +virFSPoolSaveState(const char *stateFile, + virFSPoolDefPtr def) +{ + virBuffer buf = VIR_BUFFER_INITIALIZER; + int ret = -1; + char *xml; + + virBufferAddLit(&buf, "<fspoolstate>\n"); + virBufferAdjustIndent(&buf, 2); + + if (virFSPoolDefFormatBuf(&buf, def) < 0) + goto error; + + virBufferAdjustIndent(&buf, -2); + virBufferAddLit(&buf, "</fspoolstate>\n"); + + if (virBufferCheckError(&buf) < 0) + goto error; + + if (!(xml = virBufferContentAndReset(&buf))) + goto error; + + if (virFSPoolSaveXML(stateFile, def, xml)) + goto error; + + ret = 0; + + error: + VIR_FREE(xml); + return ret; +} + + +int +virFSPoolSaveConfig(const char *configFile, + virFSPoolDefPtr def) +{ + char *xml; + int ret = -1; + + if (!(xml = virFSPoolDefFormat(def))) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("failed to generate XML")); + return -1; + } + + if (virFSPoolSaveXML(configFile, def, xml)) + goto cleanup; + + ret = 0; + cleanup: + VIR_FREE(xml); + return ret; +} + +int +virFSPoolObjSaveDef(virFSDriverStatePtr driver, + virFSPoolObjPtr fspool, + virFSPoolDefPtr def) +{ + if (!fspool->configFile) { + if (virFileMakePath(driver->configDir) < 0) { + virReportSystemError(errno, + _("cannot create config directory %s"), + driver->configDir); + return -1; + } + + if (!(fspool->configFile = virFileBuildPath(driver->configDir, + def->name, ".xml"))) { + return -1; + } + + if (!(fspool->autostartLink = virFileBuildPath(driver->autostartDir, + def->name, ".xml"))) { + VIR_FREE(fspool->configFile); + return -1; + } + } + + return virFSPoolSaveConfig(fspool->configFile, def); +} + +int +virFSPoolObjDeleteDef(virFSPoolObjPtr fspool) +{ + if (!fspool->configFile) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("no config file for %s"), fspool->def->name); + return -1; + } + + if (unlink(fspool->configFile) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("cannot remove config for %s"), + fspool->def->name); + return -1; + } + + return 0; +} + +virFSPoolSourcePtr +virFSPoolSourceListNewSource(virFSPoolSourceListPtr list) +{ + virFSPoolSourcePtr source; + + if (VIR_REALLOC_N(list->sources, list->nsources + 1) < 0) + return NULL; + + source = &list->sources[list->nsources++]; + memset(source, 0, sizeof(*source)); + + return source; +} + +char * +virFSPoolSourceListFormat(virFSPoolSourceListPtr def) +{ + virFSPoolOptionsPtr options; + virBuffer buf = VIR_BUFFER_INITIALIZER; + const char *type; + size_t i; + + options = virFSPoolOptionsForPoolType(def->type); + if (options == NULL) + return NULL; + + type = virFSPoolTypeToString(def->type); + if (!type) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("unexpected fspool type")); + goto cleanup; + } + + virBufferAddLit(&buf, "<sources>\n"); + virBufferAdjustIndent(&buf, 2); + + for (i = 0; i < def->nsources; i++) + virFSPoolSourceFormat(&buf, options, &def->sources[i]); + + virBufferAdjustIndent(&buf, -2); + virBufferAddLit(&buf, "</sources>\n"); + + if (virBufferCheckError(&buf) < 0) + goto cleanup; + + return virBufferContentAndReset(&buf); + + cleanup: + virBufferFreeAndReset(&buf); + return NULL; +} + + +/* + * virFSPoolObjIsDuplicate: + * @doms : virFSPoolObjListPtr to search + * @def : virFSPoolDefPtr definition of fspool to lookup + * @check_active: If true, ensure that fspool is not active + * + * Returns: -1 on error + * 0 if fspool is new + * 1 if fspool is a duplicate + */ +int +virFSPoolObjIsDuplicate(virFSPoolObjListPtr fspools, + virFSPoolDefPtr def, + unsigned int check_active) +{ + int ret = -1; + virFSPoolObjPtr fspool = NULL; + + /* See if a Pool with matching UUID already exists */ + fspool = virFSPoolObjFindByUUID(fspools, def->uuid); + if (fspool) { + /* UUID matches, but if names don't match, refuse it */ + if (STRNEQ(fspool->def->name, def->name)) { + char uuidstr[VIR_UUID_STRING_BUFLEN]; + virUUIDFormat(fspool->def->uuid, uuidstr); + virReportError(VIR_ERR_OPERATION_FAILED, + _("fspool '%s' is already defined with uuid %s"), + fspool->def->name, uuidstr); + goto cleanup; + } + + if (check_active) { + /* UUID & name match, but if Pool is already active, refuse it */ + if (virFSPoolObjIsActive(fspool)) { + virReportError(VIR_ERR_OPERATION_INVALID, + _("fspool is already active as '%s'"), + fspool->def->name); + goto cleanup; + } + } + + ret = 1; + } else { + /* UUID does not match, but if a name matches, refuse it */ + fspool = virFSPoolObjFindByName(fspools, def->name); + if (fspool) { + char uuidstr[VIR_UUID_STRING_BUFLEN]; + virUUIDFormat(fspool->def->uuid, uuidstr); + virReportError(VIR_ERR_OPERATION_FAILED, + _("fspool '%s' already exists with uuid %s"), + def->name, uuidstr); + goto cleanup; + } + ret = 0; + } + + cleanup: + if (fspool) + virFSPoolObjUnlock(fspool); + return ret; +} + +int +virFSPoolSourceFindDuplicate(virConnectPtr conn ATTRIBUTE_UNUSED, + virFSPoolObjListPtr fspools, + virFSPoolDefPtr def) +{ + size_t i; + int ret = 1; + virFSPoolObjPtr fspool = NULL; + virFSPoolObjPtr matchfspool = NULL; + + /* Check the fspool list for duplicate underlying storage */ + for (i = 0; i < fspools->count; i++) { + fspool = fspools->objs[i]; + if (def->type != fspool->def->type) + continue; + + /* Don't mach against ourself if re-defining existing fspool ! */ + if (STREQ(fspool->def->name, def->name)) + continue; + + virFSPoolObjLock(fspool); + + switch ((virFSPoolType)fspool->def->type) { + case VIR_FSPOOL_DIR: + if (STREQ(fspool->def->target.path, def->target.path)) + matchfspool = fspool; + break; + + case VIR_FSPOOL_LAST: + break; + } + virFSPoolObjUnlock(fspool); + + if (matchfspool) + break; + } + + if (matchfspool) { + virReportError(VIR_ERR_OPERATION_FAILED, + _("FS source conflict with fspool: '%s'"), + matchfspool->def->name); + ret = -1; + } + return ret; +} + +void +virFSPoolObjLock(virFSPoolObjPtr obj) +{ + virMutexLock(&obj->lock); +} + +void +virFSPoolObjUnlock(virFSPoolObjPtr obj) +{ + virMutexUnlock(&obj->lock); +} + +#define MATCH(FLAG) (flags & (FLAG)) +static bool +virFSPoolMatch(virFSPoolObjPtr fspoolobj, + unsigned int flags) +{ + /* filter by active state */ + if (MATCH(VIR_CONNECT_LIST_FSPOOLS_FILTERS_ACTIVE) && + !((MATCH(VIR_CONNECT_LIST_FSPOOLS_ACTIVE) && + virFSPoolObjIsActive(fspoolobj)) || + (MATCH(VIR_CONNECT_LIST_FSPOOLS_INACTIVE) && + !virFSPoolObjIsActive(fspoolobj)))) + return false; + + /* filter by persistence */ + if (MATCH(VIR_CONNECT_LIST_FSPOOLS_FILTERS_PERSISTENT) && + !((MATCH(VIR_CONNECT_LIST_FSPOOLS_PERSISTENT) && + fspoolobj->configFile) || + (MATCH(VIR_CONNECT_LIST_FSPOOLS_TRANSIENT) && + !fspoolobj->configFile))) + return false; + + /* filter by autostart option */ + if (MATCH(VIR_CONNECT_LIST_FSPOOLS_FILTERS_AUTOSTART) && + !((MATCH(VIR_CONNECT_LIST_FSPOOLS_AUTOSTART) && + fspoolobj->autostart) || + (MATCH(VIR_CONNECT_LIST_FSPOOLS_NO_AUTOSTART) && + !fspoolobj->autostart))) + return false; + + /* filter by fspool type */ + if (MATCH(VIR_CONNECT_LIST_FSPOOLS_FILTERS_POOL_TYPE)) { + if (!(MATCH(VIR_CONNECT_LIST_FSPOOLS_DIR) && + (fspoolobj->def->type == VIR_FSPOOL_DIR))) + return false; + } + + return true; +} +#undef MATCH + +int +virFSPoolObjListExport(virConnectPtr conn, + virFSPoolObjList fspoolobjs, + virFSPoolPtr **fspools, + virFSPoolObjListFilter filter, + unsigned int flags) +{ + virFSPoolPtr *tmp_fspools = NULL; + virFSPoolPtr fspool = NULL; + int nfspools = 0; + int ret = -1; + size_t i; + + if (fspools && VIR_ALLOC_N(tmp_fspools, fspoolobjs.count + 1) < 0) + goto cleanup; + + for (i = 0; i < fspoolobjs.count; i++) { + virFSPoolObjPtr fspoolobj = fspoolobjs.objs[i]; + virFSPoolObjLock(fspoolobj); + if ((!filter || filter(conn, fspoolobj->def)) && + virFSPoolMatch(fspoolobj, flags)) { + if (fspools) { + if (!(fspool = virGetFSPool(conn, + fspoolobj->def->name, + fspoolobj->def->uuid, + NULL, NULL))) { + virFSPoolObjUnlock(fspoolobj); + goto cleanup; + } + tmp_fspools[nfspools] = fspool; + } + nfspools++; + } + virFSPoolObjUnlock(fspoolobj); + } + + if (tmp_fspools) { + /* trim the array to the final size */ + ignore_value(VIR_REALLOC_N(tmp_fspools, nfspools + 1)); + *fspools = tmp_fspools; + tmp_fspools = NULL; + } + + ret = nfspools; + + cleanup: + if (tmp_fspools) { + for (i = 0; i < nfspools; i++) + virObjectUnref(tmp_fspools[i]); + } + + VIR_FREE(tmp_fspools); + return ret; +} diff --git a/src/conf/fs_conf.h b/src/conf/fs_conf.h new file mode 100644 index 0000000..9c8e9f1 --- /dev/null +++ b/src/conf/fs_conf.h @@ -0,0 +1,323 @@ +/* + * fs_conf.h: config handling for fs driver + * Author: Olga Krishtal <okrishtal@xxxxxxxxxxxxx> + * + * Copyright (C) 2016 Parallels IP Holdings GmbH + * + * 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, see + * <http://www.gnu.org/licenses/>. + */ + +#ifndef __VIR_FS_CONF_H__ +# define __VIR_FS_CONF_H__ + +# include "internal.h" +# include "virbitmap.h" +# include "virthread.h" +# include "virutil.h" + +# include <libxml/tree.h> + +# define VIR_CONNECT_LIST_FSPOOLS_FILTERS_POOL_TYPE \ + VIR_CONNECT_LIST_FSPOOLS_DIR + +# define VIR_CONNECT_LIST_FSPOOLS_FILTERS_ACTIVE \ + (VIR_CONNECT_LIST_FSPOOLS_ACTIVE | \ + VIR_CONNECT_LIST_FSPOOLS_INACTIVE) + +# define VIR_CONNECT_LIST_FSPOOLS_FILTERS_PERSISTENT \ + (VIR_CONNECT_LIST_FSPOOLS_PERSISTENT | \ + VIR_CONNECT_LIST_FSPOOLS_TRANSIENT) + +# define VIR_CONNECT_LIST_FSPOOLS_FILTERS_AUTOSTART \ + (VIR_CONNECT_LIST_FSPOOLS_AUTOSTART | \ + VIR_CONNECT_LIST_FSPOOLS_NO_AUTOSTART) + +# define VIR_CONNECT_LIST_FSPOOLS_FILTERS_ALL \ + (VIR_CONNECT_LIST_FSPOOLS_FILTERS_ACTIVE | \ + VIR_CONNECT_LIST_FSPOOLS_FILTERS_PERSISTENT | \ + VIR_CONNECT_LIST_FSPOOLS_FILTERS_AUTOSTART | \ + VIR_CONNECT_LIST_FSPOOLS_FILTERS_POOL_TYPE) + +VIR_ENUM_DECL(virFSItem) +VIR_ENUM_DECL(virFS) + +typedef struct _virFSPerms virFSPerms; +typedef virFSPerms *virFSPermsPtr; +struct _virFSPerms { + mode_t mode; + uid_t uid; + gid_t gid; + char *label; +}; + +typedef struct _virFSSourcePoolDef virFSSourcePoolDef; +struct _virFSSourcePoolDef { + char *pool; /* pool name */ + char *item; /* item name */ + int itemtype; /* virFSItemType, internal only */ + int pooltype; /* virFSPoolType internal only */ +}; +typedef virFSSourcePoolDef *virFSSourcePoolDefPtr; + +typedef struct _virFSSource virFSSource; +typedef virFSSource *virFSSourcePtr; + +struct _virFSSource { + int type; /* virFSType */ + char *path; + virFSSourcePoolDefPtr srcpool; + char *driverName; + virFSPermsPtr perms; + unsigned long long capacity; /* in bytes, 0 if unknown */ + unsigned long long allocation; /* in bytes, 0 if unknown */ +}; + +typedef enum { + VIR_FSPOOL_DIR, /* Local directory */ + VIR_FSPOOL_LAST, +} virFSPoolType; + +VIR_ENUM_DECL(virFSPool) + +typedef struct _virFSItemDef virFSItemDef; +typedef virFSItemDef *virFSItemDefPtr; +struct _virFSItemDef { + char *name; + char *key; + int type; /* virFSItemType */ + + bool building; + unsigned int in_use; + + virFSSource target; +}; + +typedef struct _virFSItemDefList virFSItemDefList; +typedef virFSItemDefList *virFSItemDefListPtr; +struct _virFSItemDefList { + size_t count; + virFSItemDefPtr *objs; +}; + +typedef struct _virFSPoolSource virFSPoolSource; +typedef virFSPoolSource *virFSPoolSourcePtr; +struct _virFSPoolSource { + /* An optional (maybe multiple) host(s) */ + + /* Or a directory */ + char *dir; + + /* Or a name */ + char *name; + + /* Product name of the source*/ + char *product; + + /* Pool type specific format such as filesystem type, + * or lvm version, etc. + */ + int format; +}; + +typedef struct _virFSPoolTarget virFSPoolTarget; +typedef virFSPoolTarget *virFSPoolTargetPtr; +struct _virFSPoolTarget { + char *path; /* Optional local filesystem mapping */ + virFSPerms perms; /* Default permissions for volumes */ +}; + +typedef struct _virFSPoolDef virFSPoolDef; +typedef virFSPoolDef *virFSPoolDefPtr; +struct _virFSPoolDef { + char *name; + unsigned char uuid[VIR_UUID_BUFLEN]; + int type; /* virFSPoolType */ + + unsigned long long allocation; /* bytes */ + unsigned long long capacity; /* bytes */ + unsigned long long available; /* bytes */ + + virFSPoolSource source; + virFSPoolTarget target; +}; + +typedef struct _virFSPoolObj virFSPoolObj; +typedef virFSPoolObj *virFSPoolObjPtr; + +struct _virFSPoolObj { + virMutex lock; + + char *configFile; + char *autostartLink; + bool active; + int autostart; + unsigned int asyncjobs; + + virFSPoolDefPtr def; + virFSPoolDefPtr newDef; + + virFSItemDefList items; +}; + +typedef struct _virFSPoolObjList virFSPoolObjList; +typedef virFSPoolObjList *virFSPoolObjListPtr; +struct _virFSPoolObjList { + size_t count; + virFSPoolObjPtr *objs; +}; + +typedef struct _virFSDriverState virFSDriverState; +typedef virFSDriverState *virFSDriverStatePtr; + +struct _virFSDriverState { + virMutex lock; + + virFSPoolObjList fspools; + + char *configDir; + char *autostartDir; + char *stateDir; + bool privileged; +}; + +typedef struct _virFSPoolSourceList virFSPoolSourceList; +typedef virFSPoolSourceList *virFSPoolSourceListPtr; +struct _virFSPoolSourceList { + int type; + unsigned int nsources; + virFSPoolSourcePtr sources; +}; + +typedef bool (*virFSPoolObjListFilter)(virConnectPtr conn, + virFSPoolDefPtr def); + +static inline int +virFSPoolObjIsActive(virFSPoolObjPtr fspool) +{ + return fspool->active; +} + +int virFSPoolLoadAllConfigs(virFSPoolObjListPtr fspools, + const char *configDir, + const char *autostartDir); + +int virFSPoolLoadAllState(virFSPoolObjListPtr fspools, + const char *stateDir); + +virFSPoolObjPtr +virFSPoolLoadState(virFSPoolObjListPtr fspools, + const char *stateDir, + const char *name); +virFSPoolObjPtr +virFSPoolObjFindByUUID(virFSPoolObjListPtr fspools, + const unsigned char *uuid); +virFSPoolObjPtr +virFSPoolObjFindByName(virFSPoolObjListPtr fspools, + const char *name); + +virFSItemDefPtr +virFSItemDefFindByKey(virFSPoolObjPtr fspool, + const char *key); +virFSItemDefPtr +virFSItemDefFindByPath(virFSPoolObjPtr fspool, + const char *path); +virFSItemDefPtr +virFSItemDefFindByName(virFSPoolObjPtr fspool, + const char *name); + +void virFSPoolObjClearItems(virFSPoolObjPtr fspool); + +virFSPoolDefPtr virFSPoolDefParseString(const char *xml); +virFSPoolDefPtr virFSPoolDefParseFile(const char *filename); +virFSPoolDefPtr virFSPoolDefParseNode(xmlDocPtr xml, + xmlNodePtr root); +char *virFSPoolDefFormat(virFSPoolDefPtr def); + +typedef enum { + /* do not require volume capacity at all */ + VIR_ITEM_XML_PARSE_NO_CAPACITY = 1 << 0, + /* do not require volume capacity if the volume has a backing store */ + VIR_ITEM_XML_PARSE_OPT_CAPACITY = 1 << 1, +} virFSItemDefParseFlags; + +virFSItemDefPtr +virFSItemDefParseString(virFSPoolDefPtr fspool, + const char *xml, + unsigned int flags); +virFSItemDefPtr +virFSItemDefParseFile(virFSPoolDefPtr fspool, + const char *filename, + unsigned int flags); +virFSItemDefPtr +virFSItemDefParseNode(virFSPoolDefPtr fspool, + xmlDocPtr xml, + xmlNodePtr root, + unsigned int flags); +char *virFSItemDefFormat(virFSPoolDefPtr fspool, + virFSItemDefPtr def); + +virFSPoolObjPtr +virFSPoolObjAssignDef(virFSPoolObjListPtr fspools, + virFSPoolDefPtr def); + +int virFSPoolSaveState(const char *stateFile, + virFSPoolDefPtr def); +int virFSPoolSaveConfig(const char *configFile, + virFSPoolDefPtr def); +int virFSPoolObjSaveDef(virFSDriverStatePtr driver, + virFSPoolObjPtr fspool, + virFSPoolDefPtr def); +int virFSPoolObjDeleteDef(virFSPoolObjPtr fspool); + +void virFSItemDefFree(virFSItemDefPtr def); +void virFSPoolSourceClear(virFSPoolSourcePtr source); +void virFSPoolSourceFree(virFSPoolSourcePtr source); +void virFSPoolDefFree(virFSPoolDefPtr def); +void virFSPoolObjFree(virFSPoolObjPtr fspool); +void virFSPoolObjListFree(virFSPoolObjListPtr fspools); +void virFSPoolObjRemove(virFSPoolObjListPtr fspools, + virFSPoolObjPtr fspool); + +virFSPoolSourcePtr +virFSPoolDefParseSourceString(const char *srcSpec, + int fspool_type); +virFSPoolSourcePtr +virFSPoolSourceListNewSource(virFSPoolSourceListPtr list); +char *virFSPoolSourceListFormat(virFSPoolSourceListPtr def); + +int virFSPoolObjIsDuplicate(virFSPoolObjListPtr fspools, + virFSPoolDefPtr def, + unsigned int check_active); + +char *virFSPoolGetVhbaSCSIHostParent(virConnectPtr conn, + const char *name) + ATTRIBUTE_NONNULL(1); + +int virFSPoolSourceFindDuplicate(virConnectPtr conn, + virFSPoolObjListPtr fspools, + virFSPoolDefPtr def); + +void virFSPoolObjLock(virFSPoolObjPtr obj); +void virFSPoolObjUnlock(virFSPoolObjPtr obj); + +int virFSPoolObjListExport(virConnectPtr conn, + virFSPoolObjList fspoolobjs, + virFSPoolPtr **fspools, + virFSPoolObjListFilter filter, + unsigned int flags); + + + +#endif /* __VIR_FS_CONF_H__ */ diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index db8d337..900818d 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -567,6 +567,48 @@ virDomainConfNWFilterTeardown; virDomainConfVMNWFilterTeardown; +# conf/fs_conf.h +virFSItemDefFindByKey; +virFSItemDefFindByName; +virFSItemDefFindByPath; +virFSItemDefFormat; +virFSItemDefFree; +virFSItemDefParseFile; +virFSItemDefParseNode; +virFSItemDefParseString; +virFSItemTypeFromString; +virFSItemTypeToString; +virFSPoolDefFormat; +virFSPoolDefFree; +virFSPoolDefParseFile; +virFSPoolDefParseNode; +virFSPoolDefParseSourceString; +virFSPoolDefParseString; +virFSPoolLoadAllConfigs; +virFSPoolLoadAllState; +virFSPoolObjAssignDef; +virFSPoolObjClearItems; +virFSPoolObjDeleteDef; +virFSPoolObjFindByName; +virFSPoolObjFindByUUID; +virFSPoolObjIsDuplicate; +virFSPoolObjListExport; +virFSPoolObjListFree; +virFSPoolObjLock; +virFSPoolObjRemove; +virFSPoolObjSaveDef; +virFSPoolObjUnlock; +virFSPoolSaveConfig; +virFSPoolSaveState; +virFSPoolSourceClear; +virFSPoolSourceFindDuplicate; +virFSPoolSourceFree; +virFSPoolSourceListFormat; +virFSPoolSourceListNewSource; +virFSPoolTypeFromString; +virFSPoolTypeToString; + + # conf/interface_conf.h virInterfaceAssignDef; virInterfaceDefFormat; -- 1.8.3.1 -- libvir-list mailing list libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list