/* * parallels_storage.c: core driver functions for managing * Parallels Cloud Server hosts * * Copyright (C) 2013 Red Hat, Inc. * 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, see * . * */ #include #include #include #include #include #include #include #include "datatypes.h" #include "dirname.h" #include "viralloc.h" #include "configmake.h" #include "virstoragefile.h" #include "virerror.h" #include "virfile.h" #include "parallels_utils.h" #include "virstring.h" #define VIR_FROM_THIS VIR_FROM_PARALLELS #define parallelsPoolNotFoundError(pool_name) \ virReportError(VIR_ERR_INVALID_ARG, \ _("pool '%s' not found"), pool_name); static virStorageVolDefPtr parallelsStorageVolDefineXML(virStoragePoolObjPtr pool, const char *xmldesc, const char *xmlfile, bool is_new); static virStorageVolPtr parallelsStorageVolLookupByPath(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 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 parallelsFindVolumes(virStoragePoolObjPtr pool) { DIR *dir; struct dirent *ent; char *path = NULL; int ret = -1; if (!(dir = opendir(pool->def->target.path))) { virReportSystemError(errno, _("cannot open path '%s'"), pool->def->target.path); return -1; } while ((ent = readdir(dir)) != NULL) { if (!virFileHasSuffix(ent->d_name, ".xml")) continue; if (!(path = virFileBuildPath(pool->def->target.path, ent->d_name, NULL))) goto cleanup; if (!parallelsStorageVolDefineXML(pool, NULL, path, false)) goto cleanup; VIR_FREE(path); } ret = 0; cleanup: VIR_FREE(path); closedir(dir); return ret; } /* * Generate unique pool name by path */ static char *parallelsMakePoolName(virConnectPtr conn, const char *path) { parallelsConnPtr privconn = conn->privateData; char *name; size_t i; for (i = 0; i < UINT_MAX; i++) { bool found = false; size_t j; if ((!i && VIR_STRDUP(name, path) < 0) || (i && virAsprintf(&name, "%s-%zu", path, i) < 0)) return NULL; for (j = 0; j < strlen(name); j++) if (name[j] == '/') name[j] = '-'; for (j = 0; j < privconn->pools.count; j++) { if (STREQ(name, privconn->pools.objs[j]->def->name)) { found = true; break; } } if (!found) return name; VIR_FREE(name); } return NULL; } static virStoragePoolObjPtr parallelsPoolCreateByPath(virConnectPtr conn, const char *path) { parallelsConnPtr privconn = conn->privateData; virStoragePoolObjListPtr pools = &privconn->pools; virStoragePoolDefPtr def; virStoragePoolObjPtr pool = NULL; if (VIR_ALLOC(def) < 0) goto error; if (!(def->name = parallelsMakePoolName(conn, path))) goto error; if (VIR_ALLOC_N(def->uuid, VIR_UUID_BUFLEN)) goto error; if (virUUIDGenerate(def->uuid)) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("Can't generate UUID")); goto error; } def->type = VIR_STORAGE_POOL_DIR; if (VIR_STRDUP(def->target.path, path) < 0) goto error; if (!(pool = virStoragePoolObjAssignDef(pools, def))) goto error; if (virStoragePoolObjSaveDef(conn->storagePrivateData, pool, def) < 0) { virStoragePoolObjRemove(pools, pool); goto error; } virStoragePoolObjUnlock(pool); return pool; error: virStoragePoolDefFree(def); if (pool) virStoragePoolObjUnlock(pool); return NULL; } /* * Create pool of type VIR_STORAGE_POOL_DIR with * path to the VM, if it's not exists. */ static virStoragePoolObjPtr parallelsPoolAddByDomain(virConnectPtr conn, virDomainObjPtr dom) { parallelsConnPtr privconn = conn->privateData; parallelsDomObjPtr pdom = dom->privateData; virStoragePoolObjListPtr pools = &privconn->pools; char *poolPath; virStoragePoolObjPtr pool = NULL; size_t j; poolPath = mdir_name(pdom->home); if (!poolPath) { virReportOOMError(); return NULL; } for (j = 0; j < pools->count; j++) { if (STREQ(poolPath, pools->objs[j]->def->target.path)) { pool = pools->objs[j]; break; } } if (!pool) pool = parallelsPoolCreateByPath(conn, poolPath); VIR_FREE(poolPath); return pool; } static int parallelsDiskDescParseNode(xmlDocPtr xml, xmlNodePtr root, virStorageVolDefPtr def) { xmlXPathContextPtr ctxt = NULL; int ret = -1; if (STRNEQ((const char *)root->name, "Parallels_disk_image")) { virReportError(VIR_ERR_XML_ERROR, "%s", _("unknown root element for storage pool")); goto cleanup; } ctxt = xmlXPathNewContext(xml); if (ctxt == NULL) { virReportOOMError(); goto cleanup; } ctxt->node = root; if (virXPathULongLong("string(./Disk_Parameters/Disk_size)", ctxt, &def->capacity) < 0) { virReportError(VIR_ERR_XML_ERROR, "%s", _("failed to get disk size from " "the disk descriptor xml")); goto cleanup; } def->capacity <<= 9; def->allocation = def->capacity; ret = 0; cleanup: xmlXPathFreeContext(ctxt); return ret; } static int parallelsDiskDescParse(const char *path, virStorageVolDefPtr def) { xmlDocPtr xml; int ret = -1; if (!(xml = virXMLParse(path, NULL, NULL))) return -1; ret = parallelsDiskDescParseNode(xml, xmlDocGetRootElement(xml), def); xmlFreeDoc(xml); return ret; } static int parallelsAddDiskVolume(virStoragePoolObjPtr pool, virDomainObjPtr dom, const char *diskName, const char *diskPath, const char *diskDescPath) { virStorageVolDefPtr def = NULL; if (VIR_ALLOC(def)) goto error; if (virAsprintf(&def->name, "%s-%s", dom->def->name, diskName) < 0) goto error; def->type = VIR_STORAGE_VOL_FILE; if (parallelsDiskDescParse(diskDescPath, def) < 0) goto error; if (!(def->target.path = realpath(diskPath, NULL))) goto no_memory; if (VIR_STRDUP(def->key, def->target.path) < 0) goto error; if (VIR_REALLOC_N(pool->volumes.objs, pool->volumes.count + 1) < 0) goto error; pool->volumes.objs[pool->volumes.count++] = def; return 0; no_memory: virReportOOMError(); error: virStorageVolDefFree(def); return -1; } static int parallelsFindVmVolumes(virStoragePoolObjPtr pool, virDomainObjPtr dom) { parallelsDomObjPtr pdom = dom->privateData; DIR *dir; struct dirent *ent; char *diskPath = NULL, *diskDescPath = NULL; struct stat sb; int ret = -1; if (!(dir = opendir(pdom->home))) { virReportSystemError(errno, _("cannot open path '%s'"), pdom->home); goto cleanup; } while ((ent = readdir(dir)) != NULL) { VIR_FREE(diskPath); VIR_FREE(diskDescPath); if (!(diskPath = virFileBuildPath(pdom->home, ent->d_name, NULL))) goto cleanup; if (lstat(diskPath, &sb) < 0) { virReportSystemError(errno, _("cannot stat path '%s'"), ent->d_name); goto cleanup; } if (!S_ISDIR(sb.st_mode)) continue; if (!(diskDescPath = virFileBuildPath(diskPath, "DiskDescriptor", ".xml"))) goto cleanup; if (access(diskDescPath, F_OK)) continue; /* here we know, that ent->d_name is a disk image directory */ if (parallelsAddDiskVolume(pool, dom, ent->d_name, diskPath, diskDescPath)) goto cleanup; } ret = 0; cleanup: VIR_FREE(diskPath); VIR_FREE(diskDescPath); closedir(dir); return ret; } static int parallelsPoolsAdd(virDomainObjPtr dom, void *opaque) { virConnectPtr conn = opaque; virStoragePoolObjPtr pool; if (!(pool = parallelsPoolAddByDomain(conn, dom))) return -1; if (parallelsFindVmVolumes(pool, dom)) return -1; return 0; } static int parallelsLoadPools(virConnectPtr conn) { parallelsConnPtr privconn = conn->privateData; virStorageDriverStatePtr storageState = conn->storagePrivateData; char *base = NULL; size_t i; if (VIR_STRDUP(base, SYSCONFDIR "/libvirt") < 0) goto error; /* Configuration path is /etc/libvirt/parallels-storage/... . */ if (virAsprintf(&storageState->configDir, "%s/parallels-storage", base) == -1) goto error; if (virAsprintf(&storageState->autostartDir, "%s/parallels-storage/autostart", base) == -1) goto error; VIR_FREE(base); if (virStoragePoolLoadAllConfigs(&privconn->pools, storageState->configDir, storageState->autostartDir) < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("Failed to load pool configs")); goto error; } if (virDomainObjListForEach(privconn->domains, parallelsPoolsAdd, conn) < 0) goto error; for (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]); } return 0; error: VIR_FREE(base); return -1; } static virDrvOpenStatus parallelsStorageOpen(virConnectPtr conn, virConnectAuthPtr auth ATTRIBUTE_UNUSED, unsigned int flags) { virStorageDriverStatePtr storageState; virCheckFlags(VIR_CONNECT_RO, VIR_DRV_OPEN_ERROR); if (STRNEQ(conn->driver->name, "Parallels")) return VIR_DRV_OPEN_DECLINED; if (VIR_ALLOC(storageState) < 0) return VIR_DRV_OPEN_ERROR; if (virMutexInit(&storageState->lock) < 0) { VIR_FREE(storageState); return VIR_DRV_OPEN_ERROR; } conn->storagePrivateData = storageState; parallelsStorageLock(storageState); if (parallelsLoadPools(conn)) goto error; parallelsStorageUnlock(storageState); return VIR_DRV_OPEN_SUCCESS; error: parallelsStorageUnlock(storageState); parallelsStorageClose(conn); return -1; } static int parallelsConnectNumOfStoragePools(virConnectPtr conn) { parallelsConnPtr privconn = conn->privateData; int numActive = 0; size_t i; parallelsDriverLock(privconn); for (i = 0; i < privconn->pools.count; i++) if (virStoragePoolObjIsActive(privconn->pools.objs[i])) numActive++; parallelsDriverUnlock(privconn); return numActive; } static int parallelsConnectListStoragePools(virConnectPtr conn, char **const names, int nnames) { parallelsConnPtr privconn = conn->privateData; int n = 0; size_t 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]) && VIR_STRDUP(names[n++], privconn->pools.objs[i]->def->name) < 0) { virStoragePoolObjUnlock(privconn->pools.objs[i]); goto error; } virStoragePoolObjUnlock(privconn->pools.objs[i]); } parallelsDriverUnlock(privconn); return n; error: for (n = 0; n < nnames; n++) VIR_FREE(names[n]); parallelsDriverUnlock(privconn); return -1; } static int parallelsConnectNumOfDefinedStoragePools(virConnectPtr conn) { parallelsConnPtr privconn = conn->privateData; int numInactive = 0; size_t 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 parallelsConnectListDefinedStoragePools(virConnectPtr conn, char **const names, int nnames) { parallelsConnPtr privconn = conn->privateData; int n = 0; size_t 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]) && VIR_STRDUP(names[n++], privconn->pools.objs[i]->def->name) < 0) { virStoragePoolObjUnlock(privconn->pools.objs[i]); goto error; } virStoragePoolObjUnlock(privconn->pools.objs[i]); } parallelsDriverUnlock(privconn); return n; error: 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) { virReportError(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 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) { virReportError(VIR_ERR_NO_STORAGE_POOL, NULL); goto cleanup; } ret = virGetStoragePool(conn, pool->def->name, pool->def->uuid, NULL, NULL); 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) { virReportError(VIR_ERR_NO_STORAGE_POOL, NULL); goto cleanup; } ret = virGetStoragePool(conn, pool->def->name, pool->def->uuid, NULL, NULL); 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_frsize); def->allocation = def->capacity - def->available; return 0; } static virStoragePoolPtr parallelsStoragePoolDefineXML(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) { virReportError(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; if (VIR_STRDUP(pool->configFile, "\0") < 0) goto cleanup; ret = virGetStoragePool(conn, pool->def->name, pool->def->uuid, NULL, NULL); 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) { parallelsPoolNotFoundError(pool->name); goto cleanup; } if (virStoragePoolObjIsActive(privpool)) { virReportError(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 parallelsStoragePoolCreate(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) { parallelsPoolNotFoundError(pool->name); goto cleanup; } if (virStoragePoolObjIsActive(privpool)) { virReportError(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) { parallelsPoolNotFoundError(pool->name); goto cleanup; } if (!virStoragePoolObjIsActive(privpool)) { virReportError(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 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) { parallelsPoolNotFoundError(pool->name); goto cleanup; } if (!virStoragePoolObjIsActive(privpool)) { virReportError(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) { parallelsPoolNotFoundError(pool->name); goto cleanup; } memset(info, 0, sizeof(virStoragePoolInfo)); if (privpool->active) info->state = VIR_STORAGE_POOL_RUNNING; else info->state = VIR_STORAGE_POOL_INACTIVE; 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) { parallelsPoolNotFoundError(pool->name); 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) { parallelsPoolNotFoundError(pool->name); 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) { parallelsPoolNotFoundError(pool->name); goto cleanup; } if (!privpool->configFile) { virReportError(VIR_ERR_INVALID_ARG, "%s", _("pool has no config file")); goto cleanup; } privpool->autostart = (autostart != 0); ret = 0; cleanup: if (privpool) virStoragePoolObjUnlock(privpool); return ret; } static int parallelsStoragePoolNumOfVolumes(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) { parallelsPoolNotFoundError(pool->name); goto cleanup; } if (!virStoragePoolObjIsActive(privpool)) { virReportError(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 n = 0; size_t i = 0; memset(names, 0, maxnames * sizeof(*names)); parallelsDriverLock(privconn); privpool = virStoragePoolObjFindByName(&privconn->pools, pool->name); parallelsDriverUnlock(privconn); if (privpool == NULL) { parallelsPoolNotFoundError(pool->name); goto error; } if (!virStoragePoolObjIsActive(privpool)) { virReportError(VIR_ERR_OPERATION_INVALID, _("storage pool '%s' is not active"), pool->name); goto error; } for (i = 0; i < privpool->volumes.count && n < maxnames; i++) { if (VIR_STRDUP(names[n++], privpool->volumes.objs[i]->name) < 0) goto error; } virStoragePoolObjUnlock(privpool); return n; error: for (n = 0; n < maxnames; n++) VIR_FREE(names[i]); if (privpool) virStoragePoolObjUnlock(privpool); return -1; } static virStorageVolPtr parallelsStorageVolLookupByName(virStoragePoolPtr pool, const char *name) { 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) { parallelsPoolNotFoundError(pool->name); goto cleanup; } if (!virStoragePoolObjIsActive(privpool)) { virReportError(VIR_ERR_OPERATION_INVALID, _("storage pool '%s' is not active"), pool->name); goto cleanup; } privvol = virStorageVolDefFindByName(privpool, name); if (!privvol) { virReportError(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, NULL, NULL); cleanup: if (privpool) virStoragePoolObjUnlock(privpool); return ret; } static virStorageVolPtr parallelsStorageVolLookupByKey(virConnectPtr conn, const char *key) { parallelsConnPtr privconn = conn->privateData; size_t 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, NULL, NULL); virStoragePoolObjUnlock(privconn->pools.objs[i]); break; } } virStoragePoolObjUnlock(privconn->pools.objs[i]); } parallelsDriverUnlock(privconn); if (!ret) virReportError(VIR_ERR_NO_STORAGE_VOL, _("no storage vol with matching key '%s'"), key); return ret; } virStorageVolPtr parallelsStorageVolLookupByPathLocked(virConnectPtr conn, const char *path) { parallelsConnPtr privconn = conn->privateData; size_t 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, NULL, NULL); virStoragePoolObjUnlock(privconn->pools.objs[i]); break; } } virStoragePoolObjUnlock(privconn->pools.objs[i]); } if (!ret) virReportError(VIR_ERR_NO_STORAGE_VOL, _("no storage vol with matching path '%s'"), path); return ret; } static virStorageVolPtr parallelsStorageVolLookupByPath(virConnectPtr conn, const char *path) { parallelsConnPtr privconn = conn->privateData; virStorageVolPtr ret = NULL; parallelsDriverLock(privconn); ret = parallelsStorageVolLookupByPathLocked(conn, path); parallelsDriverUnlock(privconn); return ret; } static virStorageVolDefPtr parallelsStorageVolDefineXML(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)) { virReportError(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) { virReportError(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) goto cleanup; if (virAsprintf(&privvol->target.path, "%s/%s", pool->def->target.path, privvol->name) < 0) goto cleanup; if (VIR_STRDUP(privvol->key, privvol->target.path) < 0) goto cleanup; if (is_new) { xml_path = parallelsAddFileExt(privvol->target.path, ".xml"); if (!xml_path) goto cleanup; if (virXMLSaveFile(xml_path, NULL, "volume-create", xmldesc)) { virReportError(VIR_ERR_OPERATION_FAILED, "%s", _("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 parallelsStorageVolCreateXML(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) { parallelsPoolNotFoundError(pool->name); goto cleanup; } if (!virStoragePoolObjIsActive(privpool)) { virReportError(VIR_ERR_OPERATION_INVALID, _("storage pool '%s' is not active"), pool->name); goto cleanup; } privvol = parallelsStorageVolDefineXML(privpool, xmldesc, NULL, true); if (!privvol) goto cleanup; ret = virGetStorageVol(pool->conn, privpool->def->name, privvol->name, privvol->key, NULL, NULL); cleanup: if (privpool) virStoragePoolObjUnlock(privpool); return ret; } static virStorageVolPtr parallelsStorageVolCreateXMLFrom(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) { parallelsPoolNotFoundError(pool->name); goto cleanup; } if (!virStoragePoolObjIsActive(privpool)) { virReportError(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)) { virReportError(VIR_ERR_OPERATION_FAILED, "%s", _("storage vol already exists")); goto cleanup; } origvol = virStorageVolDefFindByName(privpool, clonevol->name); if (!origvol) { virReportError(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) { virReportError(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) goto cleanup; if (virAsprintf(&privvol->target.path, "%s/%s", privpool->def->target.path, privvol->name) == -1) goto cleanup; if (VIR_STRDUP(privvol->key, privvol->target.path) < 0) 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, NULL, NULL); privvol = NULL; cleanup: virStorageVolDefFree(privvol); if (privpool) virStoragePoolObjUnlock(privpool); return ret; } int parallelsStorageVolDefRemove(virStoragePoolObjPtr privpool, virStorageVolDefPtr privvol) { int ret = -1; char *xml_path = NULL; size_t i; 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)) { virReportError(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: VIR_FREE(xml_path); return ret; } static int parallelsStorageVolDelete(virStorageVolPtr vol, unsigned int flags) { parallelsConnPtr privconn = vol->conn->privateData; virStoragePoolObjPtr privpool; virStorageVolDefPtr privvol; int ret = -1; virCheckFlags(0, -1); parallelsDriverLock(privconn); privpool = virStoragePoolObjFindByName(&privconn->pools, vol->pool); parallelsDriverUnlock(privconn); if (privpool == NULL) { parallelsPoolNotFoundError(vol->pool); goto cleanup; } privvol = virStorageVolDefFindByName(privpool, vol->name); if (privvol == NULL) { virReportError(VIR_ERR_NO_STORAGE_VOL, _("no storage vol with matching name '%s'"), vol->name); goto cleanup; } if (!virStoragePoolObjIsActive(privpool)) { virReportError(VIR_ERR_OPERATION_INVALID, _("storage pool '%s' is not active"), vol->pool); goto cleanup; } if (parallelsStorageVolDefRemove(privpool, privvol)) goto cleanup; ret = 0; cleanup: if (privpool) virStoragePoolObjUnlock(privpool); return ret; } static int parallelsStorageVolTypeForPool(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 parallelsStorageVolGetInfo(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) { parallelsPoolNotFoundError(vol->pool); goto cleanup; } privvol = virStorageVolDefFindByName(privpool, vol->name); if (privvol == NULL) { virReportError(VIR_ERR_NO_STORAGE_VOL, _("no storage vol with matching name '%s'"), vol->name); goto cleanup; } if (!virStoragePoolObjIsActive(privpool)) { virReportError(VIR_ERR_OPERATION_INVALID, _("storage pool '%s' is not active"), vol->pool); goto cleanup; } memset(info, 0, sizeof(*info)); info->type = parallelsStorageVolTypeForPool(privpool->def->type); info->capacity = privvol->capacity; info->allocation = privvol->allocation; ret = 0; cleanup: if (privpool) virStoragePoolObjUnlock(privpool); return ret; } static char * parallelsStorageVolGetXMLDesc(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) { parallelsPoolNotFoundError(vol->pool); goto cleanup; } privvol = virStorageVolDefFindByName(privpool, vol->name); if (privvol == NULL) { virReportError(VIR_ERR_NO_STORAGE_VOL, _("no storage vol with matching name '%s'"), vol->name); goto cleanup; } if (!virStoragePoolObjIsActive(privpool)) { virReportError(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 * parallelsStorageVolGetPath(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) { parallelsPoolNotFoundError(vol->pool); goto cleanup; } privvol = virStorageVolDefFindByName(privpool, vol->name); if (privvol == NULL) { virReportError(VIR_ERR_NO_STORAGE_VOL, _("no storage vol with matching name '%s'"), vol->name); goto cleanup; } if (!virStoragePoolObjIsActive(privpool)) { virReportError(VIR_ERR_OPERATION_INVALID, _("storage pool '%s' is not active"), vol->pool); goto cleanup; } ignore_value(VIR_STRDUP(ret, privvol->target.path)); cleanup: if (privpool) virStoragePoolObjUnlock(privpool); return ret; } static virStorageDriver parallelsStorageDriver = { .name = "Parallels", .storageOpen = parallelsStorageOpen, /* 0.10.0 */ .storageClose = parallelsStorageClose, /* 0.10.0 */ .connectNumOfStoragePools = parallelsConnectNumOfStoragePools, /* 0.10.0 */ .connectListStoragePools = parallelsConnectListStoragePools, /* 0.10.0 */ .connectNumOfDefinedStoragePools = parallelsConnectNumOfDefinedStoragePools, /* 0.10.0 */ .connectListDefinedStoragePools = parallelsConnectListDefinedStoragePools, /* 0.10.0 */ .storagePoolLookupByName = parallelsStoragePoolLookupByName, /* 0.10.0 */ .storagePoolLookupByUUID = parallelsStoragePoolLookupByUUID, /* 0.10.0 */ .storagePoolLookupByVolume = parallelsStoragePoolLookupByVolume, /* 0.10.0 */ .storagePoolDefineXML = parallelsStoragePoolDefineXML, /* 0.10.0 */ .storagePoolUndefine = parallelsStoragePoolUndefine, /* 0.10.0 */ .storagePoolCreate = parallelsStoragePoolCreate, /* 0.10.0 */ .storagePoolDestroy = parallelsStoragePoolDestroy, /* 0.10.0 */ .storagePoolRefresh = parallelsStoragePoolRefresh, /* 0.10.0 */ .storagePoolGetInfo = parallelsStoragePoolGetInfo, /* 0.10.0 */ .storagePoolGetXMLDesc = parallelsStoragePoolGetXMLDesc, /* 0.10.0 */ .storagePoolGetAutostart = parallelsStoragePoolGetAutostart, /* 0.10.0 */ .storagePoolSetAutostart = parallelsStoragePoolSetAutostart, /* 0.10.0 */ .storagePoolNumOfVolumes = parallelsStoragePoolNumOfVolumes, /* 0.10.0 */ .storagePoolListVolumes = parallelsStoragePoolListVolumes, /* 0.10.0 */ .storageVolLookupByName = parallelsStorageVolLookupByName, /* 0.10.0 */ .storageVolLookupByKey = parallelsStorageVolLookupByKey, /* 0.10.0 */ .storageVolLookupByPath = parallelsStorageVolLookupByPath, /* 0.10.0 */ .storageVolCreateXML = parallelsStorageVolCreateXML, /* 0.10.0 */ .storageVolCreateXMLFrom = parallelsStorageVolCreateXMLFrom, /* 0.10.0 */ .storageVolDelete = parallelsStorageVolDelete, /* 0.10.0 */ .storageVolGetInfo = parallelsStorageVolGetInfo, /* 0.10.0 */ .storageVolGetXMLDesc = parallelsStorageVolGetXMLDesc, /* 0.10.0 */ .storageVolGetPath = parallelsStorageVolGetPath, /* 0.10.0 */ .storagePoolIsActive = parallelsStoragePoolIsActive, /* 0.10.0 */ .storagePoolIsPersistent = parallelsStoragePoolIsPersistent, /* 0.10.0 */ }; int parallelsStorageRegister(void) { if (virRegisterStorageDriver(¶llelsStorageDriver) < 0) return -1; return 0; }