[libvirt] [PATCH v8 7/8] parallels: add storage driver

Dmitry Guryanov dguryanov at parallels.com
Wed Jul 4 17:42:57 UTC 2012


PARALLELS has one serious discrepancy with libvirt: libvirt stores
domain configuration files in one place, and storage files
in other places (with the API of storage pools and storage volumes).
PARALLELS stores all domain data in a single directory, for example, you
may have domain with name fedora-15, which will be located in
'/var/parallels/fedora-15.pvm', and it's hard disk image will be
in '/var/parallels/fedora-15.pvm/harddisk1.hdd'.

I've decided to create storage driver, which produces pseudo-volumes
(xml files with volume description), and they will be 'converted' to
real disk images after attaching to a VM.

So if someone creates VM with one hard disk using virt-manager,
at first virt-manager creates a new volume, and then defines a
domain. We can lookup a volume by path in XML domain definition
and find out location of new domain and size of its hard disk.

Signed-off-by: Dmitry Guryanov <dguryanov at parallels.com>
---
 po/POTFILES.in                    |    1 +
 src/Makefile.am                   |    3 +-
 src/parallels/parallels_driver.c  |    6 +-
 src/parallels/parallels_driver.h  |    5 +
 src/parallels/parallels_storage.c | 1460 +++++++++++++++++++++++++++++++++++++
 src/parallels/parallels_utils.c   |   24 +
 6 files changed, 1496 insertions(+), 3 deletions(-)
 create mode 100644 src/parallels/parallels_storage.c

diff --git a/po/POTFILES.in b/po/POTFILES.in
index dcb0813..240becb 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -66,6 +66,7 @@ src/phyp/phyp_driver.c
 src/parallels/parallels_driver.c
 src/parallels/parallels_driver.h
 src/parallels/parallels_utils.c
+src/parallels/parallels_storage.c
 src/qemu/qemu_agent.c
 src/qemu/qemu_bridge_filter.c
 src/qemu/qemu_capabilities.c
diff --git a/src/Makefile.am b/src/Makefile.am
index 93989c8..6908bb2 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -482,7 +482,8 @@ HYPERV_DRIVER_EXTRA_DIST =							\
 PARALLELS_DRIVER_SOURCES =					\
 		parallels/parallels_driver.h			\
 		parallels/parallels_driver.c			\
-		parallels/parallels_utils.c
+		parallels/parallels_utils.c				\
+		parallels/parallels_storage.c
 
 NETWORK_DRIVER_SOURCES =					\
 		network/bridge_driver.h network/bridge_driver.c
diff --git a/src/parallels/parallels_driver.c b/src/parallels/parallels_driver.c
index 54bdc9a..8c8ddd8 100644
--- a/src/parallels/parallels_driver.c
+++ b/src/parallels/parallels_driver.c
@@ -65,13 +65,13 @@ static int parallelsStart(virDomainObjPtr privdom);
 static int parallelsKill(virDomainObjPtr privdom);
 static int parallelsStop(virDomainObjPtr privdom);
 
-static void
+void
 parallelsDriverLock(parallelsConnPtr driver)
 {
     virMutexLock(&driver->lock);
 }
 
-static void
+void
 parallelsDriverUnlock(parallelsConnPtr driver)
 {
     virMutexUnlock(&driver->lock);
@@ -1226,6 +1226,8 @@ parallelsRegister(void)
 
     if (virRegisterDriver(&parallelsDriver) < 0)
         return -1;
+    if (parallelsStorageRegister())
+        return -1;
 
     return 0;
 }
diff --git a/src/parallels/parallels_driver.h b/src/parallels/parallels_driver.h
index 2b3c956..6f06ac8 100644
--- a/src/parallels/parallels_driver.h
+++ b/src/parallels/parallels_driver.h
@@ -26,6 +26,7 @@
 
 # include "domain_conf.h"
 # include "storage_conf.h"
+# include "driver.h"
 # include "domain_event.h"
 
 # include "json.h"
@@ -59,8 +60,12 @@ typedef struct _parallelsConn parallelsConn;
 typedef struct _parallelsConn *parallelsConnPtr;
 
 int parallelsRegister(void);
+int parallelsStorageRegister(void);
 
 virJSONValuePtr parallelsParseOutput(const char *binary, ...) ATTRIBUTE_NONNULL(1) ATTRIBUTE_SENTINEL;
 int parallelsCmdRun(const char *binary, ...) ATTRIBUTE_NONNULL(1) ATTRIBUTE_SENTINEL;
+char * parallelsAddFileExt(const char *path, const char *ext);
+void parallelsDriverLock(parallelsConnPtr driver);
+void parallelsDriverUnlock(parallelsConnPtr driver);
 
 #endif
diff --git a/src/parallels/parallels_storage.c b/src/parallels/parallels_storage.c
new file mode 100644
index 0000000..5a5b366
--- /dev/null
+++ b/src/parallels/parallels_storage.c
@@ -0,0 +1,1460 @@
+/*
+ * parallels_storage.c: core driver functions for managing
+ * Parallels Virtuozzo Server hosts
+ *
+ * Copyright (C) 2012 Parallels, Inc.
+ *
+ * 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
+ *
+ */
+
+#include <config.h>
+
+#include <stdlib.h>
+#include <dirent.h>
+#include <sys/statvfs.h>
+
+#include "datatypes.h"
+#include "memory.h"
+#include "configmake.h"
+#include "storage_file.h"
+#include "virterror_internal.h"
+
+#include "parallels_driver.h"
+
+#define VIR_FROM_THIS VIR_FROM_PARALLELS
+
+static int parallelsStorageClose(virConnectPtr conn);
+static virStorageVolDefPtr parallelsStorageVolumeDefine(virStoragePoolObjPtr pool,
+                                                  const char *xmldesc,
+                                                  const char *xmlfile,
+                                                  bool is_new);
+static virStorageVolPtr parallelsStorageVolumeLookupByPathLocked(virConnectPtr
+                                                           conn,
+                                                           const char
+                                                           *path);
+static virStorageVolPtr parallelsStorageVolumeLookupByPath(virConnectPtr conn,
+                                                     const char *path);
+static int parallelsStoragePoolGetAlloc(virStoragePoolDefPtr def);
+
+static void
+parallelsStorageLock(virStorageDriverStatePtr driver)
+{
+    virMutexLock(&driver->lock);
+}
+
+static void
+parallelsStorageUnlock(virStorageDriverStatePtr driver)
+{
+    virMutexUnlock(&driver->lock);
+}
+
+static int
+parallelsFindVolumes(virStoragePoolObjPtr pool)
+{
+    DIR *dir;
+    struct dirent *ent;
+    char *path;
+
+    if (!(dir = opendir(pool->def->target.path))) {
+        virReportSystemError(errno,
+                             _("cannot open path '%s'"),
+                             pool->def->target.path);
+        goto cleanup;
+    }
+
+    while ((ent = readdir(dir)) != NULL) {
+        if (!virFileHasSuffix(ent->d_name, ".xml"))
+            continue;
+
+        if (!(path = virFileBuildPath(pool->def->target.path,
+                                      ent->d_name, NULL)))
+            goto no_memory;
+        if (!parallelsStorageVolumeDefine(pool, NULL, path, false))
+            goto cleanup;
+        VIR_FREE(path);
+    }
+
+    return 0;
+  no_memory:
+    virReportOOMError();
+  cleanup:
+    return -1;
+
+}
+
+static virDrvOpenStatus
+parallelsStorageOpen(virConnectPtr conn,
+               virConnectAuthPtr auth ATTRIBUTE_UNUSED, unsigned int flags)
+{
+    char *base = NULL;
+    virStorageDriverStatePtr storageState;
+    int privileged = (geteuid() == 0);
+    parallelsConnPtr privconn = conn->privateData;
+    virCheckFlags(VIR_CONNECT_RO, VIR_DRV_OPEN_ERROR);
+
+    if (STRNEQ(conn->driver->name, "PARALLELS"))
+        return VIR_DRV_OPEN_DECLINED;
+
+    if (VIR_ALLOC(storageState) < 0) {
+        virReportOOMError();
+        return VIR_DRV_OPEN_ERROR;
+    }
+
+    if (virMutexInit(&storageState->lock) < 0) {
+        VIR_FREE(storageState);
+        return VIR_DRV_OPEN_ERROR;
+    }
+    parallelsStorageLock(storageState);
+
+    if (privileged) {
+        if ((base = strdup(SYSCONFDIR "/libvirt")) == NULL)
+            goto out_of_memory;
+    } else {
+        char *userdir = virGetUserDirectory();
+
+        if (!userdir)
+            goto error;
+
+        if (virAsprintf(&base, "%s/.libvirt", userdir) == -1) {
+            VIR_FREE(userdir);
+            goto out_of_memory;
+        }
+        VIR_FREE(userdir);
+    }
+
+    /* Configuration paths are either ~/.libvirt/storage/... (session) or
+     * /etc/libvirt/storage/... (system).
+     */
+    if (virAsprintf(&storageState->configDir,
+                    "%s/parallels-storage", base) == -1)
+        goto out_of_memory;
+
+    if (virAsprintf(&storageState->autostartDir,
+                    "%s/parallels-storage/autostart", base) == -1)
+        goto out_of_memory;
+
+    VIR_FREE(base);
+
+    if (virStoragePoolLoadAllConfigs(&privconn->pools,
+                                     storageState->configDir,
+                                     storageState->autostartDir) < 0) {
+        parallelsError(VIR_ERR_INTERNAL_ERROR, _("Failed to load pool configs"));
+        goto error;
+    }
+
+    for (int i = 0; i < privconn->pools.count; i++) {
+        virStoragePoolObjLock(privconn->pools.objs[i]);
+        virStoragePoolObjPtr pool;
+
+        pool = privconn->pools.objs[i];
+        pool->active = 1;
+
+        if (parallelsStoragePoolGetAlloc(pool->def) < 0)
+            goto error;
+
+        if (parallelsFindVolumes(pool) < 0)
+            goto error;
+
+        virStoragePoolObjUnlock(privconn->pools.objs[i]);
+    }
+
+    parallelsStorageUnlock(storageState);
+
+    conn->storagePrivateData = storageState;
+
+    return VIR_DRV_OPEN_SUCCESS;
+
+  out_of_memory:
+    virReportOOMError();
+  error:
+    VIR_FREE(base);
+    parallelsStorageUnlock(storageState);
+    parallelsStorageClose(conn);
+    return -1;
+}
+
+static int
+parallelsStorageClose(virConnectPtr conn)
+{
+    parallelsConnPtr privconn = conn->privateData;
+
+    virStorageDriverStatePtr storageState = conn->storagePrivateData;
+    conn->storagePrivateData = NULL;
+
+    parallelsStorageLock(storageState);
+    virStoragePoolObjListFree(&privconn->pools);
+    VIR_FREE(storageState->configDir);
+    VIR_FREE(storageState->autostartDir);
+    parallelsStorageUnlock(storageState);
+    virMutexDestroy(&storageState->lock);
+    VIR_FREE(storageState);
+
+    return 0;
+}
+
+static int
+parallelsStorageNumPools(virConnectPtr conn)
+{
+    parallelsConnPtr privconn = conn->privateData;
+    int numActive = 0, i;
+
+    parallelsDriverLock(privconn);
+    for (i = 0; i < privconn->pools.count; i++)
+        if (virStoragePoolObjIsActive(privconn->pools.objs[i]))
+            numActive++;
+    parallelsDriverUnlock(privconn);
+
+    return numActive;
+}
+
+static int
+parallelsStorageListPools(virConnectPtr conn, char **const names, int nnames)
+{
+    parallelsConnPtr privconn = conn->privateData;
+    int n = 0, i;
+
+    parallelsDriverLock(privconn);
+    memset(names, 0, sizeof(*names) * nnames);
+    for (i = 0; i < privconn->pools.count && n < nnames; i++) {
+        virStoragePoolObjLock(privconn->pools.objs[i]);
+        if (virStoragePoolObjIsActive(privconn->pools.objs[i]) &&
+            !(names[n++] = strdup(privconn->pools.objs[i]->def->name))) {
+            virStoragePoolObjUnlock(privconn->pools.objs[i]);
+            goto no_memory;
+        }
+        virStoragePoolObjUnlock(privconn->pools.objs[i]);
+    }
+    parallelsDriverUnlock(privconn);
+
+    return n;
+
+  no_memory:
+    virReportOOMError();
+    for (n = 0; n < nnames; n++)
+        VIR_FREE(names[n]);
+    parallelsDriverUnlock(privconn);
+    return -1;
+}
+
+static int
+parallelsStorageNumDefinedPools(virConnectPtr conn)
+{
+    parallelsConnPtr privconn = conn->privateData;
+    int numInactive = 0, i;
+
+    parallelsDriverLock(privconn);
+    for (i = 0; i < privconn->pools.count; i++) {
+        virStoragePoolObjLock(privconn->pools.objs[i]);
+        if (!virStoragePoolObjIsActive(privconn->pools.objs[i]))
+            numInactive++;
+        virStoragePoolObjUnlock(privconn->pools.objs[i]);
+    }
+    parallelsDriverUnlock(privconn);
+
+    return numInactive;
+}
+
+static int
+parallelsStorageListDefinedPools(virConnectPtr conn,
+                           char **const names, int nnames)
+{
+    parallelsConnPtr privconn = conn->privateData;
+    int n = 0, i;
+
+    parallelsDriverLock(privconn);
+    memset(names, 0, sizeof(*names) * nnames);
+    for (i = 0; i < privconn->pools.count && n < nnames; i++) {
+        virStoragePoolObjLock(privconn->pools.objs[i]);
+        if (!virStoragePoolObjIsActive(privconn->pools.objs[i]) &&
+            !(names[n++] = strdup(privconn->pools.objs[i]->def->name))) {
+            virStoragePoolObjUnlock(privconn->pools.objs[i]);
+            goto no_memory;
+        }
+        virStoragePoolObjUnlock(privconn->pools.objs[i]);
+    }
+    parallelsDriverUnlock(privconn);
+
+    return n;
+
+  no_memory:
+    virReportOOMError();
+    for (n = 0; n < nnames; n++)
+        VIR_FREE(names[n]);
+    parallelsDriverUnlock(privconn);
+    return -1;
+}
+
+
+static int
+parallelsStoragePoolIsActive(virStoragePoolPtr pool)
+{
+    parallelsConnPtr privconn = pool->conn->privateData;
+    virStoragePoolObjPtr obj;
+    int ret = -1;
+
+    parallelsDriverLock(privconn);
+    obj = virStoragePoolObjFindByUUID(&privconn->pools, pool->uuid);
+    parallelsDriverUnlock(privconn);
+    if (!obj) {
+        parallelsError(VIR_ERR_NO_STORAGE_POOL, NULL);
+        goto cleanup;
+    }
+    ret = virStoragePoolObjIsActive(obj);
+
+  cleanup:
+    if (obj)
+        virStoragePoolObjUnlock(obj);
+    return ret;
+}
+
+static int
+parallelsStoragePoolIsPersistent(virStoragePoolPtr pool ATTRIBUTE_UNUSED)
+{
+    return 1;
+}
+
+static char *
+parallelsStorageFindPoolSources(virConnectPtr conn ATTRIBUTE_UNUSED,
+                          const char *type ATTRIBUTE_UNUSED,
+                          const char *srcSpec ATTRIBUTE_UNUSED,
+                          unsigned int flags)
+{
+    virCheckFlags(0, NULL);
+
+    return NULL;
+}
+
+static virStoragePoolPtr
+parallelsStoragePoolLookupByUUID(virConnectPtr conn, const unsigned char *uuid)
+{
+    parallelsConnPtr privconn = conn->privateData;
+    virStoragePoolObjPtr pool;
+    virStoragePoolPtr ret = NULL;
+
+    parallelsDriverLock(privconn);
+    pool = virStoragePoolObjFindByUUID(&privconn->pools, uuid);
+    parallelsDriverUnlock(privconn);
+
+    if (pool == NULL) {
+        parallelsError(VIR_ERR_NO_STORAGE_POOL, NULL);
+        goto cleanup;
+    }
+
+    ret = virGetStoragePool(conn, pool->def->name, pool->def->uuid);
+
+  cleanup:
+    if (pool)
+        virStoragePoolObjUnlock(pool);
+    return ret;
+}
+
+static virStoragePoolPtr
+parallelsStoragePoolLookupByName(virConnectPtr conn, const char *name)
+{
+    parallelsConnPtr privconn = conn->privateData;
+    virStoragePoolObjPtr pool;
+    virStoragePoolPtr ret = NULL;
+
+    parallelsDriverLock(privconn);
+    pool = virStoragePoolObjFindByName(&privconn->pools, name);
+    parallelsDriverUnlock(privconn);
+
+    if (pool == NULL) {
+        parallelsError(VIR_ERR_NO_STORAGE_POOL, NULL);
+        goto cleanup;
+    }
+
+    ret = virGetStoragePool(conn, pool->def->name, pool->def->uuid);
+
+  cleanup:
+    if (pool)
+        virStoragePoolObjUnlock(pool);
+    return ret;
+}
+
+static virStoragePoolPtr
+parallelsStoragePoolLookupByVolume(virStorageVolPtr vol)
+{
+    return parallelsStoragePoolLookupByName(vol->conn, vol->pool);
+}
+
+/*
+ * Fill capacity, available and allocation
+ * fields in pool definition.
+ */
+static int
+parallelsStoragePoolGetAlloc(virStoragePoolDefPtr def)
+{
+    struct statvfs sb;
+
+    if (statvfs(def->target.path, &sb) < 0) {
+        virReportSystemError(errno,
+                             _("cannot statvfs path '%s'"),
+                             def->target.path);
+        return -1;
+    }
+
+    def->capacity = ((unsigned long long)sb.f_frsize *
+                     (unsigned long long)sb.f_blocks);
+    def->available = ((unsigned long long)sb.f_bfree *
+                            (unsigned long long)sb.f_bsize);
+    def->allocation = def->capacity - def->available;
+
+    return 0;
+}
+
+static virStoragePoolPtr
+parallelsStoragePoolDefine(virConnectPtr conn,
+                     const char *xml, unsigned int flags)
+{
+    parallelsConnPtr privconn = conn->privateData;
+    virStoragePoolDefPtr def;
+    virStoragePoolObjPtr pool = NULL;
+    virStoragePoolPtr ret = NULL;
+
+    virCheckFlags(0, NULL);
+
+    parallelsDriverLock(privconn);
+    if (!(def = virStoragePoolDefParseString(xml)))
+        goto cleanup;
+
+    if (def->type != VIR_STORAGE_POOL_DIR) {
+        parallelsError(VIR_ERR_NO_SUPPORT, "%s",
+                 _("Only local directories are supported"));
+        goto cleanup;
+    }
+
+    if (virStoragePoolObjIsDuplicate(&privconn->pools, def, 0) < 0)
+        goto cleanup;
+
+    if (virStoragePoolSourceFindDuplicate(&privconn->pools, def) < 0)
+        goto cleanup;
+
+    if (parallelsStoragePoolGetAlloc(def))
+        goto cleanup;
+
+    if (!(pool = virStoragePoolObjAssignDef(&privconn->pools, def)))
+        goto cleanup;
+
+    if (virStoragePoolObjSaveDef(conn->storagePrivateData, pool, def) < 0) {
+        virStoragePoolObjRemove(&privconn->pools, pool);
+        def = NULL;
+        goto cleanup;
+    }
+    def = NULL;
+
+    pool->configFile = strdup("\0");
+    if (!pool->configFile) {
+        virReportOOMError();
+        goto cleanup;
+    }
+
+    ret = virGetStoragePool(conn, pool->def->name, pool->def->uuid);
+
+  cleanup:
+    virStoragePoolDefFree(def);
+    if (pool)
+        virStoragePoolObjUnlock(pool);
+    parallelsDriverUnlock(privconn);
+    return ret;
+}
+
+static int
+parallelsStoragePoolUndefine(virStoragePoolPtr pool)
+{
+    parallelsConnPtr privconn = pool->conn->privateData;
+    virStoragePoolObjPtr privpool;
+    int ret = -1;
+
+    parallelsDriverLock(privconn);
+    privpool = virStoragePoolObjFindByName(&privconn->pools, pool->name);
+
+    if (privpool == NULL) {
+        parallelsError(VIR_ERR_INVALID_ARG, __FUNCTION__);
+        goto cleanup;
+    }
+
+    if (virStoragePoolObjIsActive(privpool)) {
+        parallelsError(VIR_ERR_OPERATION_INVALID,
+                 _("storage pool '%s' is still active"), pool->name);
+        goto cleanup;
+    }
+
+    if (virStoragePoolObjDeleteDef(privpool) < 0)
+        goto cleanup;
+
+    VIR_FREE(privpool->configFile);
+
+    virStoragePoolObjRemove(&privconn->pools, privpool);
+    ret = 0;
+
+  cleanup:
+    if (privpool)
+        virStoragePoolObjUnlock(privpool);
+    parallelsDriverUnlock(privconn);
+    return ret;
+}
+
+static int
+parallelsStoragePoolBuild(virStoragePoolPtr pool, unsigned int flags)
+{
+    parallelsConnPtr privconn = pool->conn->privateData;
+    virStoragePoolObjPtr privpool;
+    int ret = -1;
+
+    virCheckFlags(0, -1);
+
+    parallelsDriverLock(privconn);
+    privpool = virStoragePoolObjFindByName(&privconn->pools, pool->name);
+    parallelsDriverUnlock(privconn);
+
+    if (privpool == NULL) {
+        parallelsError(VIR_ERR_INVALID_ARG, __FUNCTION__);
+        goto cleanup;
+    }
+
+    if (virStoragePoolObjIsActive(privpool)) {
+        parallelsError(VIR_ERR_OPERATION_INVALID,
+                 _("storage pool '%s' is already active"), pool->name);
+        goto cleanup;
+    }
+    ret = 0;
+
+  cleanup:
+    if (privpool)
+        virStoragePoolObjUnlock(privpool);
+    return ret;
+}
+
+static int
+parallelsStoragePoolStart(virStoragePoolPtr pool, unsigned int flags)
+{
+    parallelsConnPtr privconn = pool->conn->privateData;
+    virStoragePoolObjPtr privpool;
+    int ret = -1;
+
+    virCheckFlags(0, -1);
+
+    parallelsDriverLock(privconn);
+    privpool = virStoragePoolObjFindByName(&privconn->pools, pool->name);
+    parallelsDriverUnlock(privconn);
+
+    if (privpool == NULL) {
+        parallelsError(VIR_ERR_INVALID_ARG, __FUNCTION__);
+        goto cleanup;
+    }
+
+    if (virStoragePoolObjIsActive(privpool)) {
+        parallelsError(VIR_ERR_OPERATION_INVALID,
+                 _("storage pool '%s' is already active"), pool->name);
+        goto cleanup;
+    }
+
+    privpool->active = 1;
+    ret = 0;
+
+  cleanup:
+    if (privpool)
+        virStoragePoolObjUnlock(privpool);
+    return ret;
+}
+
+static int
+parallelsStoragePoolDestroy(virStoragePoolPtr pool)
+{
+    parallelsConnPtr privconn = pool->conn->privateData;
+    virStoragePoolObjPtr privpool;
+    int ret = -1;
+
+    parallelsDriverLock(privconn);
+    privpool = virStoragePoolObjFindByName(&privconn->pools, pool->name);
+
+    if (privpool == NULL) {
+        parallelsError(VIR_ERR_INVALID_ARG, __FUNCTION__);
+        goto cleanup;
+    }
+
+    if (!virStoragePoolObjIsActive(privpool)) {
+        parallelsError(VIR_ERR_OPERATION_INVALID,
+                 _("storage pool '%s' is not active"), pool->name);
+        goto cleanup;
+    }
+
+    if (privpool->configFile == NULL) {
+        virStoragePoolObjRemove(&privconn->pools, privpool);
+        privpool = NULL;
+    }
+    ret = 0;
+
+  cleanup:
+    if (privpool)
+        virStoragePoolObjUnlock(privpool);
+    parallelsDriverUnlock(privconn);
+    return ret;
+}
+
+
+static int
+parallelsStoragePoolDelete(virStoragePoolPtr pool, unsigned int flags)
+{
+    parallelsConnPtr privconn = pool->conn->privateData;
+    virStoragePoolObjPtr privpool;
+    int ret = -1;
+
+    virCheckFlags(0, -1);
+
+    parallelsDriverLock(privconn);
+    privpool = virStoragePoolObjFindByName(&privconn->pools, pool->name);
+    parallelsDriverUnlock(privconn);
+
+    if (privpool == NULL) {
+        parallelsError(VIR_ERR_INVALID_ARG, __FUNCTION__);
+        goto cleanup;
+    }
+
+    if (virStoragePoolObjIsActive(privpool)) {
+        parallelsError(VIR_ERR_OPERATION_INVALID,
+                 _("storage pool '%s' is already active"), pool->name);
+        goto cleanup;
+    }
+
+    ret = 0;
+
+  cleanup:
+    if (privpool)
+        virStoragePoolObjUnlock(privpool);
+    return ret;
+}
+
+
+static int
+parallelsStoragePoolRefresh(virStoragePoolPtr pool, unsigned int flags)
+{
+    parallelsConnPtr privconn = pool->conn->privateData;
+    virStoragePoolObjPtr privpool;
+    int ret = -1;
+
+    virCheckFlags(0, -1);
+
+    parallelsDriverLock(privconn);
+    privpool = virStoragePoolObjFindByName(&privconn->pools, pool->name);
+    parallelsDriverUnlock(privconn);
+
+    if (privpool == NULL) {
+        parallelsError(VIR_ERR_INVALID_ARG, __FUNCTION__);
+        goto cleanup;
+    }
+
+    if (!virStoragePoolObjIsActive(privpool)) {
+        parallelsError(VIR_ERR_OPERATION_INVALID,
+                 _("storage pool '%s' is not active"), pool->name);
+        goto cleanup;
+    }
+    ret = 0;
+
+  cleanup:
+    if (privpool)
+        virStoragePoolObjUnlock(privpool);
+    return ret;
+}
+
+
+static int
+parallelsStoragePoolGetInfo(virStoragePoolPtr pool, virStoragePoolInfoPtr info)
+{
+    parallelsConnPtr privconn = pool->conn->privateData;
+    virStoragePoolObjPtr privpool;
+    int ret = -1;
+
+    parallelsDriverLock(privconn);
+    privpool = virStoragePoolObjFindByName(&privconn->pools, pool->name);
+    parallelsDriverUnlock(privconn);
+
+    if (privpool == NULL) {
+        parallelsError(VIR_ERR_INVALID_ARG, __FUNCTION__);
+        goto cleanup;
+    }
+
+    memset(info, 0, sizeof(virStoragePoolInfo));
+    info->state = VIR_STORAGE_POOL_RUNNING;
+    info->capacity = privpool->def->capacity;
+    info->allocation = privpool->def->allocation;
+    info->available = privpool->def->available;
+    ret = 0;
+
+  cleanup:
+    if (privpool)
+        virStoragePoolObjUnlock(privpool);
+    return ret;
+}
+
+static char *
+parallelsStoragePoolGetXMLDesc(virStoragePoolPtr pool, unsigned int flags)
+{
+    parallelsConnPtr privconn = pool->conn->privateData;
+    virStoragePoolObjPtr privpool;
+    char *ret = NULL;
+
+    virCheckFlags(0, NULL);
+
+    parallelsDriverLock(privconn);
+    privpool = virStoragePoolObjFindByName(&privconn->pools, pool->name);
+    parallelsDriverUnlock(privconn);
+
+    if (privpool == NULL) {
+        parallelsError(VIR_ERR_INVALID_ARG, __FUNCTION__);
+        goto cleanup;
+    }
+
+    ret = virStoragePoolDefFormat(privpool->def);
+
+  cleanup:
+    if (privpool)
+        virStoragePoolObjUnlock(privpool);
+    return ret;
+}
+
+static int
+parallelsStoragePoolGetAutostart(virStoragePoolPtr pool, int *autostart)
+{
+    parallelsConnPtr privconn = pool->conn->privateData;
+    virStoragePoolObjPtr privpool;
+    int ret = -1;
+
+    parallelsDriverLock(privconn);
+    privpool = virStoragePoolObjFindByName(&privconn->pools, pool->name);
+    parallelsDriverUnlock(privconn);
+
+    if (privpool == NULL) {
+        parallelsError(VIR_ERR_INVALID_ARG, __FUNCTION__);
+        goto cleanup;
+    }
+
+    if (!privpool->configFile) {
+        *autostart = 0;
+    } else {
+        *autostart = privpool->autostart;
+    }
+    ret = 0;
+
+  cleanup:
+    if (privpool)
+        virStoragePoolObjUnlock(privpool);
+    return ret;
+}
+
+static int
+parallelsStoragePoolSetAutostart(virStoragePoolPtr pool, int autostart)
+{
+    parallelsConnPtr privconn = pool->conn->privateData;
+    virStoragePoolObjPtr privpool;
+    int ret = -1;
+
+    parallelsDriverLock(privconn);
+    privpool = virStoragePoolObjFindByName(&privconn->pools, pool->name);
+    parallelsDriverUnlock(privconn);
+
+    if (privpool == NULL) {
+        parallelsError(VIR_ERR_INVALID_ARG, __FUNCTION__);
+        goto cleanup;
+    }
+
+    if (!privpool->configFile) {
+        parallelsError(VIR_ERR_INVALID_ARG, "%s", _("pool has no config file"));
+        goto cleanup;
+    }
+
+    autostart = (autostart != 0);
+    privpool->autostart = autostart;
+    ret = 0;
+
+  cleanup:
+    if (privpool)
+        virStoragePoolObjUnlock(privpool);
+    return ret;
+}
+
+static int
+parallelsStoragePoolNumVolumes(virStoragePoolPtr pool)
+{
+    parallelsConnPtr privconn = pool->conn->privateData;
+    virStoragePoolObjPtr privpool;
+    int ret = -1;
+
+    parallelsDriverLock(privconn);
+    privpool = virStoragePoolObjFindByName(&privconn->pools, pool->name);
+    parallelsDriverUnlock(privconn);
+
+    if (privpool == NULL) {
+        parallelsError(VIR_ERR_INVALID_ARG, __FUNCTION__);
+        goto cleanup;
+    }
+
+    if (!virStoragePoolObjIsActive(privpool)) {
+        parallelsError(VIR_ERR_OPERATION_INVALID,
+                 _("storage pool '%s' is not active"), pool->name);
+        goto cleanup;
+    }
+
+    ret = privpool->volumes.count;
+
+  cleanup:
+    if (privpool)
+        virStoragePoolObjUnlock(privpool);
+    return ret;
+}
+
+static int
+parallelsStoragePoolListVolumes(virStoragePoolPtr pool,
+                          char **const names, int maxnames)
+{
+    parallelsConnPtr privconn = pool->conn->privateData;
+    virStoragePoolObjPtr privpool;
+    int i = 0, n = 0;
+
+    memset(names, 0, maxnames * sizeof(*names));
+
+    parallelsDriverLock(privconn);
+    privpool = virStoragePoolObjFindByName(&privconn->pools, pool->name);
+    parallelsDriverUnlock(privconn);
+
+    if (privpool == NULL) {
+        parallelsError(VIR_ERR_INVALID_ARG, __FUNCTION__);
+        goto cleanup;
+    }
+
+
+    if (!virStoragePoolObjIsActive(privpool)) {
+        parallelsError(VIR_ERR_OPERATION_INVALID,
+                 _("storage pool '%s' is not active"), pool->name);
+        goto cleanup;
+    }
+
+    for (i = 0; i < privpool->volumes.count && n < maxnames; i++) {
+        if ((names[n++] = strdup(privpool->volumes.objs[i]->name)) == NULL) {
+            virReportOOMError();
+            goto cleanup;
+        }
+    }
+
+    virStoragePoolObjUnlock(privpool);
+    return n;
+
+  cleanup:
+    for (n = 0; n < maxnames; n++)
+        VIR_FREE(names[i]);
+
+    memset(names, 0, maxnames * sizeof(*names));
+    if (privpool)
+        virStoragePoolObjUnlock(privpool);
+    return -1;
+}
+
+static virStorageVolPtr
+parallelsStorageVolumeLookupByName(virStoragePoolPtr pool,
+                             const char *name ATTRIBUTE_UNUSED)
+{
+    parallelsConnPtr privconn = pool->conn->privateData;
+    virStoragePoolObjPtr privpool;
+    virStorageVolDefPtr privvol;
+    virStorageVolPtr ret = NULL;
+
+    parallelsDriverLock(privconn);
+    privpool = virStoragePoolObjFindByName(&privconn->pools, pool->name);
+    parallelsDriverUnlock(privconn);
+
+    if (privpool == NULL) {
+        parallelsError(VIR_ERR_INVALID_ARG, __FUNCTION__);
+        goto cleanup;
+    }
+
+
+    if (!virStoragePoolObjIsActive(privpool)) {
+        parallelsError(VIR_ERR_OPERATION_INVALID,
+                 _("storage pool '%s' is not active"), pool->name);
+        goto cleanup;
+    }
+
+    privvol = virStorageVolDefFindByName(privpool, name);
+
+    if (!privvol) {
+        parallelsError(VIR_ERR_NO_STORAGE_VOL,
+                 _("no storage vol with matching name '%s'"), name);
+        goto cleanup;
+    }
+
+    ret = virGetStorageVol(pool->conn, privpool->def->name,
+                           privvol->name, privvol->key);
+
+  cleanup:
+    if (privpool)
+        virStoragePoolObjUnlock(privpool);
+    return ret;
+}
+
+
+static virStorageVolPtr
+parallelsStorageVolumeLookupByKey(virConnectPtr conn, const char *key)
+{
+    parallelsConnPtr privconn = conn->privateData;
+    unsigned int i;
+    virStorageVolPtr ret = NULL;
+
+    parallelsDriverLock(privconn);
+    for (i = 0; i < privconn->pools.count; i++) {
+        virStoragePoolObjLock(privconn->pools.objs[i]);
+        if (virStoragePoolObjIsActive(privconn->pools.objs[i])) {
+            virStorageVolDefPtr privvol =
+                virStorageVolDefFindByKey(privconn->pools.objs[i], key);
+
+            if (privvol) {
+                ret = virGetStorageVol(conn,
+                                       privconn->pools.objs[i]->def->name,
+                                       privvol->name, privvol->key);
+                virStoragePoolObjUnlock(privconn->pools.objs[i]);
+                break;
+            }
+        }
+        virStoragePoolObjUnlock(privconn->pools.objs[i]);
+    }
+    parallelsDriverUnlock(privconn);
+
+    if (!ret)
+        parallelsError(VIR_ERR_NO_STORAGE_VOL,
+                 _("no storage vol with matching key '%s'"), key);
+
+    return ret;
+}
+
+static virStorageVolPtr
+parallelsStorageVolumeLookupByPathLocked(virConnectPtr conn, const char *path)
+{
+    parallelsConnPtr privconn = conn->privateData;
+    unsigned int i;
+    virStorageVolPtr ret = NULL;
+
+    for (i = 0; i < privconn->pools.count; i++) {
+        virStoragePoolObjLock(privconn->pools.objs[i]);
+        if (virStoragePoolObjIsActive(privconn->pools.objs[i])) {
+            virStorageVolDefPtr privvol =
+                virStorageVolDefFindByPath(privconn->pools.objs[i], path);
+
+            if (privvol) {
+                ret = virGetStorageVol(conn,
+                                       privconn->pools.objs[i]->def->name,
+                                       privvol->name, privvol->key);
+                virStoragePoolObjUnlock(privconn->pools.objs[i]);
+                break;
+            }
+        }
+        virStoragePoolObjUnlock(privconn->pools.objs[i]);
+    }
+
+    if (!ret)
+        parallelsError(VIR_ERR_NO_STORAGE_VOL,
+                 _("no storage vol with matching path '%s'"), path);
+
+    return ret;
+}
+
+static virStorageVolPtr
+parallelsStorageVolumeLookupByPath(virConnectPtr conn, const char *path)
+{
+    parallelsConnPtr privconn = conn->privateData;
+    virStorageVolPtr ret = NULL;
+
+    parallelsDriverLock(privconn);
+    ret = parallelsStorageVolumeLookupByPathLocked(conn, path);
+    parallelsDriverUnlock(privconn);
+
+    return ret;
+}
+
+static virStorageVolDefPtr
+parallelsStorageVolumeDefine(virStoragePoolObjPtr pool,
+                       const char *xmldesc,
+                       const char *xmlfile, bool is_new)
+{
+    virStorageVolDefPtr privvol = NULL;
+    virStorageVolDefPtr ret = NULL;
+    char *xml_path = NULL;
+
+    if (xmlfile)
+        privvol = virStorageVolDefParseFile(pool->def, xmlfile);
+    else
+        privvol = virStorageVolDefParseString(pool->def, xmldesc);
+    if (privvol == NULL)
+        goto cleanup;
+
+    if (virStorageVolDefFindByName(pool, privvol->name)) {
+        parallelsError(VIR_ERR_OPERATION_FAILED,
+                 "%s", _("storage vol already exists"));
+        goto cleanup;
+    }
+
+    if (is_new) {
+        /* Make sure enough space */
+        if ((pool->def->allocation + privvol->allocation) >
+            pool->def->capacity) {
+            parallelsError(VIR_ERR_INTERNAL_ERROR,
+                     _("Not enough free space in pool for volume '%s'"),
+                     privvol->name);
+            goto cleanup;
+        }
+    }
+
+    if (VIR_REALLOC_N(pool->volumes.objs, pool->volumes.count + 1) < 0) {
+        virReportOOMError();
+        goto cleanup;
+    }
+
+    if (virAsprintf(&privvol->target.path, "%s/%s",
+                    pool->def->target.path, privvol->name) < 0) {
+        virReportOOMError();
+        goto cleanup;
+    }
+
+    privvol->key = strdup(privvol->target.path);
+    if (privvol->key == NULL) {
+        virReportOOMError();
+        goto cleanup;
+    }
+
+    if (is_new) {
+        xml_path = parallelsAddFileExt(privvol->target.path, ".xml");
+        if (!xml_path)
+            goto cleanup;
+
+        if (virXMLSaveFile
+            (xml_path, privvol->name, "volume-create", xmldesc)) {
+            parallelsError(VIR_ERR_OPERATION_FAILED,
+                     "Can't create file with volume description");
+            goto cleanup;
+        }
+
+        pool->def->allocation += privvol->allocation;
+        pool->def->available = (pool->def->capacity -
+                                pool->def->allocation);
+    }
+
+    pool->volumes.objs[pool->volumes.count++] = privvol;
+
+    ret = privvol;
+    privvol = NULL;
+
+  cleanup:
+    virStorageVolDefFree(privvol);
+    VIR_FREE(xml_path);
+    return ret;
+}
+
+static virStorageVolPtr
+parallelsStorageVolumeCreateXML(virStoragePoolPtr pool,
+                          const char *xmldesc, unsigned int flags)
+{
+    parallelsConnPtr privconn = pool->conn->privateData;
+    virStoragePoolObjPtr privpool;
+    virStorageVolPtr ret = NULL;
+    virStorageVolDefPtr privvol = NULL;
+
+    virCheckFlags(0, NULL);
+
+    parallelsDriverLock(privconn);
+    privpool = virStoragePoolObjFindByName(&privconn->pools, pool->name);
+    parallelsDriverUnlock(privconn);
+
+    if (privpool == NULL) {
+        parallelsError(VIR_ERR_INVALID_ARG, __FUNCTION__);
+        goto cleanup;
+    }
+
+    if (!virStoragePoolObjIsActive(privpool)) {
+        parallelsError(VIR_ERR_OPERATION_INVALID,
+                 _("storage pool '%s' is not active"), pool->name);
+        goto cleanup;
+    }
+
+    privvol = parallelsStorageVolumeDefine(privpool, xmldesc, NULL, true);
+    if (!privvol)
+        goto cleanup;
+
+    ret = virGetStorageVol(pool->conn, privpool->def->name,
+                           privvol->name, privvol->key);
+  cleanup:
+    if (privpool)
+        virStoragePoolObjUnlock(privpool);
+    return ret;
+}
+
+static virStorageVolPtr
+parallelsStorageVolumeCreateXMLFrom(virStoragePoolPtr pool,
+                              const char *xmldesc,
+                              virStorageVolPtr clonevol,
+                              unsigned int flags)
+{
+    parallelsConnPtr privconn = pool->conn->privateData;
+    virStoragePoolObjPtr privpool;
+    virStorageVolDefPtr privvol = NULL, origvol = NULL;
+    virStorageVolPtr ret = NULL;
+
+    virCheckFlags(0, NULL);
+
+    parallelsDriverLock(privconn);
+    privpool = virStoragePoolObjFindByName(&privconn->pools, pool->name);
+    parallelsDriverUnlock(privconn);
+
+    if (privpool == NULL) {
+        parallelsError(VIR_ERR_INVALID_ARG, __FUNCTION__);
+        goto cleanup;
+    }
+
+    if (!virStoragePoolObjIsActive(privpool)) {
+        parallelsError(VIR_ERR_OPERATION_INVALID,
+                 _("storage pool '%s' is not active"), pool->name);
+        goto cleanup;
+    }
+
+    privvol = virStorageVolDefParseString(privpool->def, xmldesc);
+    if (privvol == NULL)
+        goto cleanup;
+
+    if (virStorageVolDefFindByName(privpool, privvol->name)) {
+        parallelsError(VIR_ERR_OPERATION_FAILED,
+                 "%s", _("storage vol already exists"));
+        goto cleanup;
+    }
+
+    origvol = virStorageVolDefFindByName(privpool, clonevol->name);
+    if (!origvol) {
+        parallelsError(VIR_ERR_NO_STORAGE_VOL,
+                 _("no storage vol with matching name '%s'"),
+                 clonevol->name);
+        goto cleanup;
+    }
+
+    /* Make sure enough space */
+    if ((privpool->def->allocation + privvol->allocation) >
+        privpool->def->capacity) {
+        parallelsError(VIR_ERR_INTERNAL_ERROR,
+                 _("Not enough free space in pool for volume '%s'"),
+                 privvol->name);
+        goto cleanup;
+    }
+    privpool->def->available = (privpool->def->capacity -
+                                privpool->def->allocation);
+
+    if (VIR_REALLOC_N(privpool->volumes.objs,
+                      privpool->volumes.count + 1) < 0) {
+        virReportOOMError();
+        goto cleanup;
+    }
+
+    if (virAsprintf(&privvol->target.path, "%s/%s",
+                    privpool->def->target.path, privvol->name) == -1) {
+        virReportOOMError();
+        goto cleanup;
+    }
+
+    privvol->key = strdup(privvol->target.path);
+    if (privvol->key == NULL) {
+        virReportOOMError();
+        goto cleanup;
+    }
+
+    privpool->def->allocation += privvol->allocation;
+    privpool->def->available = (privpool->def->capacity -
+                                privpool->def->allocation);
+
+    privpool->volumes.objs[privpool->volumes.count++] = privvol;
+
+    ret = virGetStorageVol(pool->conn, privpool->def->name,
+                           privvol->name, privvol->key);
+    privvol = NULL;
+
+  cleanup:
+    virStorageVolDefFree(privvol);
+    if (privpool)
+        virStoragePoolObjUnlock(privpool);
+    return ret;
+}
+
+static int
+parallelsStorageVolumeDelete(virStorageVolPtr vol, unsigned int flags)
+{
+    parallelsConnPtr privconn = vol->conn->privateData;
+    virStoragePoolObjPtr privpool;
+    virStorageVolDefPtr privvol;
+    int i;
+    int ret = -1;
+    char *xml_path = NULL;
+
+    virCheckFlags(0, -1);
+
+    parallelsDriverLock(privconn);
+    privpool = virStoragePoolObjFindByName(&privconn->pools, vol->pool);
+    parallelsDriverUnlock(privconn);
+
+    if (privpool == NULL) {
+        parallelsError(VIR_ERR_INVALID_ARG, __FUNCTION__);
+        goto cleanup;
+    }
+
+
+    privvol = virStorageVolDefFindByName(privpool, vol->name);
+
+    if (privvol == NULL) {
+        parallelsError(VIR_ERR_NO_STORAGE_VOL,
+                 _("no storage vol with matching name '%s'"), vol->name);
+        goto cleanup;
+    }
+
+    if (!virStoragePoolObjIsActive(privpool)) {
+        parallelsError(VIR_ERR_OPERATION_INVALID,
+                 _("storage pool '%s' is not active"), vol->pool);
+        goto cleanup;
+    }
+
+
+    privpool->def->allocation -= privvol->allocation;
+    privpool->def->available = (privpool->def->capacity -
+                                privpool->def->allocation);
+
+    for (i = 0; i < privpool->volumes.count; i++) {
+        if (privpool->volumes.objs[i] == privvol) {
+            xml_path = parallelsAddFileExt(privvol->target.path, ".xml");
+            if (!xml_path)
+                goto cleanup;
+
+            if (unlink(xml_path)) {
+                parallelsError(VIR_ERR_OPERATION_FAILED,
+                         _("Can't remove file '%s'"), xml_path);
+                goto cleanup;
+            }
+
+            virStorageVolDefFree(privvol);
+
+            if (i < (privpool->volumes.count - 1))
+                memmove(privpool->volumes.objs + i,
+                        privpool->volumes.objs + i + 1,
+                        sizeof(*(privpool->volumes.objs)) *
+                        (privpool->volumes.count - (i + 1)));
+
+            if (VIR_REALLOC_N(privpool->volumes.objs,
+                              privpool->volumes.count - 1) < 0) {
+                ;               /* Failure to reduce memory allocation isn't fatal */
+            }
+            privpool->volumes.count--;
+
+            break;
+        }
+    }
+    ret = 0;
+
+  cleanup:
+    if (privpool)
+        virStoragePoolObjUnlock(privpool);
+    VIR_FREE(xml_path);
+    return ret;
+}
+
+
+static int
+parallelsStorageVolumeTypeForPool(int pooltype)
+{
+
+    switch (pooltype) {
+        case VIR_STORAGE_POOL_DIR:
+        case VIR_STORAGE_POOL_FS:
+        case VIR_STORAGE_POOL_NETFS:
+            return VIR_STORAGE_VOL_FILE;
+        default:
+            return VIR_STORAGE_VOL_BLOCK;
+    }
+}
+
+static int
+parallelsStorageVolumeGetInfo(virStorageVolPtr vol, virStorageVolInfoPtr info)
+{
+    parallelsConnPtr privconn = vol->conn->privateData;
+    virStoragePoolObjPtr privpool;
+    virStorageVolDefPtr privvol;
+    int ret = -1;
+
+    parallelsDriverLock(privconn);
+    privpool = virStoragePoolObjFindByName(&privconn->pools, vol->pool);
+    parallelsDriverUnlock(privconn);
+
+    if (privpool == NULL) {
+        parallelsError(VIR_ERR_INVALID_ARG, __FUNCTION__);
+        goto cleanup;
+    }
+
+    privvol = virStorageVolDefFindByName(privpool, vol->name);
+
+    if (privvol == NULL) {
+        parallelsError(VIR_ERR_NO_STORAGE_VOL,
+                 _("no storage vol with matching name '%s'"), vol->name);
+        goto cleanup;
+    }
+
+    if (!virStoragePoolObjIsActive(privpool)) {
+        parallelsError(VIR_ERR_OPERATION_INVALID,
+                 _("storage pool '%s' is not active"), vol->pool);
+        goto cleanup;
+    }
+
+    memset(info, 0, sizeof(*info));
+    info->type = parallelsStorageVolumeTypeForPool(privpool->def->type);
+    info->capacity = privvol->capacity;
+    info->allocation = privvol->allocation;
+    ret = 0;
+
+  cleanup:
+    if (privpool)
+        virStoragePoolObjUnlock(privpool);
+    return ret;
+}
+
+static char *
+parallelsStorageVolumeGetXMLDesc(virStorageVolPtr vol, unsigned int flags)
+{
+    parallelsConnPtr privconn = vol->conn->privateData;
+    virStoragePoolObjPtr privpool;
+    virStorageVolDefPtr privvol;
+    char *ret = NULL;
+
+    virCheckFlags(0, NULL);
+
+    parallelsDriverLock(privconn);
+    privpool = virStoragePoolObjFindByName(&privconn->pools, vol->pool);
+    parallelsDriverUnlock(privconn);
+
+    if (privpool == NULL) {
+        parallelsError(VIR_ERR_INVALID_ARG, __FUNCTION__);
+        goto cleanup;
+    }
+
+    privvol = virStorageVolDefFindByName(privpool, vol->name);
+
+    if (privvol == NULL) {
+        parallelsError(VIR_ERR_NO_STORAGE_VOL,
+                 _("no storage vol with matching name '%s'"), vol->name);
+        goto cleanup;
+    }
+
+    if (!virStoragePoolObjIsActive(privpool)) {
+        parallelsError(VIR_ERR_OPERATION_INVALID,
+                 _("storage pool '%s' is not active"), vol->pool);
+        goto cleanup;
+    }
+
+    ret = virStorageVolDefFormat(privpool->def, privvol);
+
+  cleanup:
+    if (privpool)
+        virStoragePoolObjUnlock(privpool);
+    return ret;
+}
+
+static char *
+parallelsStorageVolumeGetPath(virStorageVolPtr vol)
+{
+    parallelsConnPtr privconn = vol->conn->privateData;
+    virStoragePoolObjPtr privpool;
+    virStorageVolDefPtr privvol;
+    char *ret = NULL;
+
+    parallelsDriverLock(privconn);
+    privpool = virStoragePoolObjFindByName(&privconn->pools, vol->pool);
+    parallelsDriverUnlock(privconn);
+
+    if (privpool == NULL) {
+        parallelsError(VIR_ERR_INVALID_ARG, __FUNCTION__);
+        goto cleanup;
+    }
+
+    privvol = virStorageVolDefFindByName(privpool, vol->name);
+
+    if (privvol == NULL) {
+        parallelsError(VIR_ERR_NO_STORAGE_VOL,
+                 _("no storage vol with matching name '%s'"), vol->name);
+        goto cleanup;
+    }
+
+    if (!virStoragePoolObjIsActive(privpool)) {
+        parallelsError(VIR_ERR_OPERATION_INVALID,
+                 _("storage pool '%s' is not active"), vol->pool);
+        goto cleanup;
+    }
+
+    ret = strdup(privvol->target.path);
+    if (ret == NULL)
+        virReportOOMError();
+
+  cleanup:
+    if (privpool)
+        virStoragePoolObjUnlock(privpool);
+    return ret;
+}
+
+static virStorageDriver parallelsStorageDriver = {
+    .name = "PARALLELS",
+    .open = parallelsStorageOpen,     /* 0.10.0 */
+    .close = parallelsStorageClose,   /* 0.10.0 */
+
+    .numOfPools = parallelsStorageNumPools,   /* 0.10.0 */
+    .listPools = parallelsStorageListPools,   /* 0.10.0 */
+    .numOfDefinedPools = parallelsStorageNumDefinedPools,     /* 0.10.0 */
+    .listDefinedPools = parallelsStorageListDefinedPools,     /* 0.10.0 */
+    .findPoolSources = parallelsStorageFindPoolSources,       /* 0.10.0 */
+    .poolLookupByName = parallelsStoragePoolLookupByName,     /* 0.10.0 */
+    .poolLookupByUUID = parallelsStoragePoolLookupByUUID,     /* 0.10.0 */
+    .poolLookupByVolume = parallelsStoragePoolLookupByVolume, /* 0.10.0 */
+    .poolDefineXML = parallelsStoragePoolDefine,      /* 0.10.0 */
+    .poolBuild = parallelsStoragePoolBuild,   /* 0.10.0 */
+    .poolUndefine = parallelsStoragePoolUndefine,     /* 0.10.0 */
+    .poolCreate = parallelsStoragePoolStart,  /* 0.10.0 */
+    .poolDestroy = parallelsStoragePoolDestroy,       /* 0.10.0 */
+    .poolDelete = parallelsStoragePoolDelete, /* 0.10.0 */
+    .poolRefresh = parallelsStoragePoolRefresh,       /* 0.10.0 */
+    .poolGetInfo = parallelsStoragePoolGetInfo,       /* 0.10.0 */
+    .poolGetXMLDesc = parallelsStoragePoolGetXMLDesc, /* 0.10.0 */
+    .poolGetAutostart = parallelsStoragePoolGetAutostart,     /* 0.10.0 */
+    .poolSetAutostart = parallelsStoragePoolSetAutostart,     /* 0.10.0 */
+    .poolNumOfVolumes = parallelsStoragePoolNumVolumes,       /* 0.10.0 */
+    .poolListVolumes = parallelsStoragePoolListVolumes,       /* 0.10.0 */
+
+    .volLookupByName = parallelsStorageVolumeLookupByName,    /* 0.10.0 */
+    .volLookupByKey = parallelsStorageVolumeLookupByKey,      /* 0.10.0 */
+    .volLookupByPath = parallelsStorageVolumeLookupByPath,    /* 0.10.0 */
+    .volCreateXML = parallelsStorageVolumeCreateXML,  /* 0.10.0 */
+    .volCreateXMLFrom = parallelsStorageVolumeCreateXMLFrom,  /* 0.10.0 */
+    .volDelete = parallelsStorageVolumeDelete,        /* 0.10.0 */
+    .volGetInfo = parallelsStorageVolumeGetInfo,      /* 0.10.0 */
+    .volGetXMLDesc = parallelsStorageVolumeGetXMLDesc,        /* 0.10.0 */
+    .volGetPath = parallelsStorageVolumeGetPath,      /* 0.10.0 */
+    .poolIsActive = parallelsStoragePoolIsActive,     /* 0.10.0 */
+    .poolIsPersistent = parallelsStoragePoolIsPersistent,     /* 0.10.0 */
+};
+
+int
+parallelsStorageRegister(void)
+{
+    if (virRegisterStorageDriver(&parallelsStorageDriver) < 0)
+        return -1;
+
+    return 0;
+}
diff --git a/src/parallels/parallels_utils.c b/src/parallels/parallels_utils.c
index e4220e9..72178d9 100644
--- a/src/parallels/parallels_utils.c
+++ b/src/parallels/parallels_utils.c
@@ -30,6 +30,8 @@
 
 #include "parallels_driver.h"
 
+#define VIR_FROM_THIS VIR_FROM_PARALLELS
+
 static int
 parallelsDoCmdRun(char **outbuf, const char *binary, va_list list)
 {
@@ -105,3 +107,25 @@ parallelsCmdRun(const char *binary, ...)
 
     return ret;
 }
+
+/*
+ * Return new file path in malloced string created by
+ * concatenating first and second function arguments.
+ */
+char *
+parallelsAddFileExt(const char *path, const char *ext)
+{
+    char *new_path = NULL;
+    size_t len = strlen(path) + strlen(ext) + 1;
+
+    if (VIR_ALLOC_N(new_path, len) < 0) {
+        virReportOOMError();
+        return NULL;
+    }
+
+    if (!virStrcpy(new_path, path, len))
+        return NULL;
+    strcat(new_path, ext);
+
+    return new_path;
+}
-- 
1.7.1




More information about the libvir-list mailing list