On 07/04/12 19:42, Dmitry Guryanov wrote:
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@xxxxxxxxxxxxx> ---
Comments inline.
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
Looks like these should be squashed into one of the previous patches, but I'm not sure if you'll need this.
parallelsDriverUnlock(parallelsConnPtr driver) { virMutexUnlock(&driver->lock); @@ -1226,6 +1226,8 @@ parallelsRegister(void) if (virRegisterDriver(¶llelsDriver) < 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);
Please include these in some kind of private header file, so you don't expose them into other drivers.
#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"));
As danpb comented on one of the previous patches, you'll need to change parallelsError for the global macro virReportError.
+ 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; +}
Hm, is there a reason to have a function like this?
+ +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__);
This is a very bad error message we're trying to get rid of now. The first reason for this is that the error reporting macro already includes __FUNCTION__ in it's message. Also this gives only a little clue what argument is actually missing. I'd suggest
virReportError(VIR_ERR_INVALID_ARG, _("pool '%s' not found") pool->name);
+ 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__);
Same problem with the message.
+ goto cleanup; + } + + if (virStoragePoolObjIsActive(privpool)) { + parallelsError(VIR_ERR_OPERATION_INVALID, + _("storage pool '%s' is already active"), pool->name); + goto cleanup; + }
Huh, this function doesn't do anything useful?
+ 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__);
Same errror message issue.
+ 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__);
Error message.
+ 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__);
Error message.
+ goto cleanup; + } + + if (virStoragePoolObjIsActive(privpool)) { + parallelsError(VIR_ERR_OPERATION_INVALID, + _("storage pool '%s' is already active"), pool->name); + goto cleanup; + }
Also this function doesn't appear to be doing what it is supposed to do.
+ + 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;
Same here.
+ + 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__);
Error message. Here and all other ocurences of this pattern.
+ goto cleanup; + } + + memset(info, 0, sizeof(virStoragePoolInfo)); + info->state = VIR_STORAGE_POOL_RUNNING;
Value of this should be filled according to state of privpool->active.
+ 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;
write this as: privpool->autostart = (autostart != 0);
+ 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:
This label should be named "error" as normal control flow isn't reaching it.
+ for (n = 0; n < maxnames; n++) + VIR_FREE(names[i]); + + memset(names, 0, maxnames * sizeof(*names));
Also, this memset isn't needed, VIR_FREE sets the freed pointers to NULL by itself.
+ if (privpool) + virStoragePoolObjUnlock(privpool); + return -1; +} + +static virStorageVolPtr +parallelsStorageVolumeLookupByName(virStoragePoolPtr pool, + const char *name ATTRIBUTE_UNUSED)
You're using "name" in the code below, so it shouldn't be 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);
Put a newline here to separate if statements.
+ 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
Please no whitespace between function symbol and the opening brace.
+ (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;
This function copies the volume definition but does not actualy copy data, is that ok?
+ + 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; + } + }
Same here. You remove the definition but you don't touch the data.
+ 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(¶llelsStorageDriver) < 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; +}
Peter -- libvir-list mailing list libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list