/* * storage_driver.c: core driver for storage APIs * * Copyright (C) 2006-2009 Red Hat, Inc. * Copyright (C) 2006-2008 Daniel P. Berrange * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Author: Daniel P. Berrange */ #include #include #include #include #if HAVE_PWD_H #include #endif #include #include #include "virterror_internal.h" #include "datatypes.h" #include "driver.h" #include "util.h" #include "storage_driver.h" #include "storage_conf.h" #include "memory.h" #include "storage_backend.h" #include "logging.h" #define VIR_FROM_THIS VIR_FROM_STORAGE static virStorageDriverStatePtr driverState; static int storageDriverShutdown(void); static void storageDriverLock(virStorageDriverStatePtr driver) { virMutexLock(&driver->lock); } static void storageDriverUnlock(virStorageDriverStatePtr driver) { virMutexUnlock(&driver->lock); } static void storageDriverAutostart(virStorageDriverStatePtr driver) { unsigned int i; for (i = 0 ; i < driver->pools.count ; i++) { virStoragePoolObjPtr pool = driver->pools.objs[i]; virStoragePoolObjLock(pool); if (pool->autostart && !virStoragePoolObjIsActive(pool)) { virStorageBackendPtr backend; if ((backend = virStorageBackendForType(pool->def->type)) == NULL) { VIR_ERROR("Missing backend %d", pool->def->type); virStoragePoolObjUnlock(pool); continue; } if (backend->startPool && backend->startPool(NULL, pool) < 0) { virErrorPtr err = virGetLastError(); VIR_ERROR("Failed to autostart storage pool '%s': %s", pool->def->name, err ? err->message : "no error message found"); virStoragePoolObjUnlock(pool); continue; } if (backend->refreshPool(NULL, pool) < 0) { virErrorPtr err = virGetLastError(); if (backend->stopPool) backend->stopPool(NULL, pool); VIR_ERROR("Failed to autostart storage pool '%s': %s", pool->def->name, err ? err->message : "no error message found"); virStoragePoolObjUnlock(pool); continue; } pool->active = 1; } virStoragePoolObjUnlock(pool); } } /** * virStorageStartup: * * Initialization function for the QEmu daemon */ static int storageDriverStartup(int privileged) { char *base = NULL; char driverConf[PATH_MAX]; if (VIR_ALLOC(driverState) < 0) return -1; if (virMutexInit(&driverState->lock) < 0) { VIR_FREE(driverState); return -1; } storageDriverLock(driverState); if (privileged) { if ((base = strdup (SYSCONF_DIR "/libvirt")) == NULL) goto out_of_memory; } else { uid_t uid = geteuid(); char *userdir = virGetUserDirectory(uid); 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 (snprintf (driverConf, sizeof(driverConf), "%s/storage.conf", base) == -1) goto out_of_memory; driverConf[sizeof(driverConf)-1] = '\0'; if (virAsprintf(&driverState->configDir, "%s/storage", base) == -1) goto out_of_memory; if (virAsprintf(&driverState->autostartDir, "%s/storage/autostart", base) == -1) goto out_of_memory; VIR_FREE(base); /* if (virStorageLoadDriverConfig(driver, driverConf) < 0) { virStorageDriverShutdown(); return -1; } */ if (virStoragePoolLoadAllConfigs(&driverState->pools, driverState->configDir, driverState->autostartDir) < 0) goto error; storageDriverAutostart(driverState); storageDriverUnlock(driverState); return 0; out_of_memory: virReportOOMError(); error: VIR_FREE(base); storageDriverUnlock(driverState); storageDriverShutdown(); return -1; } /** * virStorageReload: * * Function to restart the storage driver, it will recheck the configuration * files and update its state */ static int storageDriverReload(void) { if (!driverState) return -1; storageDriverLock(driverState); virStoragePoolLoadAllConfigs(&driverState->pools, driverState->configDir, driverState->autostartDir); storageDriverAutostart(driverState); storageDriverUnlock(driverState); return 0; } /** * virStorageActive: * * Checks if the storage driver is active, i.e. has an active pool * * Returns 1 if active, 0 otherwise */ static int storageDriverActive(void) { unsigned int i; int active = 0; if (!driverState) return 0; storageDriverLock(driverState); for (i = 0 ; i < driverState->pools.count ; i++) { virStoragePoolObjLock(driverState->pools.objs[i]); if (virStoragePoolObjIsActive(driverState->pools.objs[i])) active = 1; virStoragePoolObjUnlock(driverState->pools.objs[i]); } storageDriverUnlock(driverState); return active; } /** * virStorageShutdown: * * Shutdown the storage driver, it will stop all active storage pools */ static int storageDriverShutdown(void) { if (!driverState) return -1; storageDriverLock(driverState); /* free inactive pools */ virStoragePoolObjListFree(&driverState->pools); VIR_FREE(driverState->configDir); VIR_FREE(driverState->autostartDir); storageDriverUnlock(driverState); virMutexDestroy(&driverState->lock); VIR_FREE(driverState); return 0; } static virStoragePoolPtr storagePoolLookupByUUID(virConnectPtr conn, const unsigned char *uuid) { virStorageDriverStatePtr driver = conn->storagePrivateData; virStoragePoolObjPtr pool; virStoragePoolPtr ret = NULL; storageDriverLock(driver); pool = virStoragePoolObjFindByUUID(&driver->pools, uuid); storageDriverUnlock(driver); if (!pool) { virStorageReportError(VIR_ERR_NO_STORAGE_POOL, "%s", _("no pool with matching uuid")); goto cleanup; } ret = virGetStoragePool(conn, pool->def->name, pool->def->uuid); cleanup: if (pool) virStoragePoolObjUnlock(pool); return ret; } static virStoragePoolPtr storagePoolLookupByName(virConnectPtr conn, const char *name) { virStorageDriverStatePtr driver = conn->storagePrivateData; virStoragePoolObjPtr pool; virStoragePoolPtr ret = NULL; storageDriverLock(driver); pool = virStoragePoolObjFindByName(&driver->pools, name); storageDriverUnlock(driver); if (!pool) { virStorageReportError(VIR_ERR_NO_STORAGE_POOL, _("no pool with matching name '%s'"), name); goto cleanup; } ret = virGetStoragePool(conn, pool->def->name, pool->def->uuid); cleanup: if (pool) virStoragePoolObjUnlock(pool); return ret; } static virStoragePoolPtr storagePoolLookupByVolume(virStorageVolPtr vol) { return storagePoolLookupByName(vol->conn, vol->pool); } static virDrvOpenStatus storageOpen(virConnectPtr conn, virConnectAuthPtr auth ATTRIBUTE_UNUSED, int flags ATTRIBUTE_UNUSED) { if (!driverState) return VIR_DRV_OPEN_DECLINED; conn->storagePrivateData = driverState; return VIR_DRV_OPEN_SUCCESS; } static int storageClose(virConnectPtr conn) { conn->storagePrivateData = NULL; return 0; } static int storageNumPools(virConnectPtr conn) { virStorageDriverStatePtr driver = conn->storagePrivateData; unsigned int i, nactive = 0; storageDriverLock(driver); for (i = 0 ; i < driver->pools.count ; i++) { virStoragePoolObjLock(driver->pools.objs[i]); if (virStoragePoolObjIsActive(driver->pools.objs[i])) nactive++; virStoragePoolObjUnlock(driver->pools.objs[i]); } storageDriverUnlock(driver); return nactive; } static int storageListPools(virConnectPtr conn, char **const names, int nnames) { virStorageDriverStatePtr driver = conn->storagePrivateData; int got = 0, i; storageDriverLock(driver); for (i = 0 ; i < driver->pools.count && got < nnames ; i++) { virStoragePoolObjLock(driver->pools.objs[i]); if (virStoragePoolObjIsActive(driver->pools.objs[i])) { if (!(names[got] = strdup(driver->pools.objs[i]->def->name))) { virStoragePoolObjUnlock(driver->pools.objs[i]); virReportOOMError(); goto cleanup; } got++; } virStoragePoolObjUnlock(driver->pools.objs[i]); } storageDriverUnlock(driver); return got; cleanup: storageDriverUnlock(driver); for (i = 0 ; i < got ; i++) VIR_FREE(names[i]); memset(names, 0, nnames * sizeof(*names)); return -1; } static int storageNumDefinedPools(virConnectPtr conn) { virStorageDriverStatePtr driver = conn->storagePrivateData; unsigned int i, nactive = 0; storageDriverLock(driver); for (i = 0 ; i < driver->pools.count ; i++) { virStoragePoolObjLock(driver->pools.objs[i]); if (!virStoragePoolObjIsActive(driver->pools.objs[i])) nactive++; virStoragePoolObjUnlock(driver->pools.objs[i]); } storageDriverUnlock(driver); return nactive; } static int storageListDefinedPools(virConnectPtr conn, char **const names, int nnames) { virStorageDriverStatePtr driver = conn->storagePrivateData; int got = 0, i; storageDriverLock(driver); for (i = 0 ; i < driver->pools.count && got < nnames ; i++) { virStoragePoolObjLock(driver->pools.objs[i]); if (!virStoragePoolObjIsActive(driver->pools.objs[i])) { if (!(names[got] = strdup(driver->pools.objs[i]->def->name))) { virStoragePoolObjUnlock(driver->pools.objs[i]); virReportOOMError(); goto cleanup; } got++; } virStoragePoolObjUnlock(driver->pools.objs[i]); } storageDriverUnlock(driver); return got; cleanup: storageDriverUnlock(driver); for (i = 0 ; i < got ; i++) { VIR_FREE(names[i]); } memset(names, 0, nnames * sizeof(*names)); return -1; } /* This method is required to be re-entrant / thread safe, so uses no driver lock */ static char * storageFindPoolSources(virConnectPtr conn, const char *type, const char *srcSpec, unsigned int flags) { int backend_type; virStorageBackendPtr backend; char *ret = NULL; backend_type = virStoragePoolTypeFromString(type); if (backend_type < 0) { virStorageReportError(VIR_ERR_INTERNAL_ERROR, _("unknown storage pool type %s"), type); goto cleanup; } backend = virStorageBackendForType(backend_type); if (backend == NULL) goto cleanup; if (!backend->findPoolSources) { virStorageReportError(VIR_ERR_NO_SUPPORT, _("pool type '%s' does not support source " "discovery"), type); goto cleanup; } ret = backend->findPoolSources(conn, srcSpec, flags); cleanup: return ret; } static int storagePoolIsActive(virStoragePoolPtr pool) { virStorageDriverStatePtr driver = pool->conn->storagePrivateData; virStoragePoolObjPtr obj; int ret = -1; storageDriverLock(driver); obj = virStoragePoolObjFindByUUID(&driver->pools, pool->uuid); storageDriverUnlock(driver); if (!obj) { virStorageReportError(VIR_ERR_NO_STORAGE_POOL, NULL); goto cleanup; } ret = virStoragePoolObjIsActive(obj); cleanup: if (obj) virStoragePoolObjUnlock(obj); return ret; } static int storagePoolIsPersistent(virStoragePoolPtr pool) { virStorageDriverStatePtr driver = pool->conn->storagePrivateData; virStoragePoolObjPtr obj; int ret = -1; storageDriverLock(driver); obj = virStoragePoolObjFindByUUID(&driver->pools, pool->uuid); storageDriverUnlock(driver); if (!obj) { virStorageReportError(VIR_ERR_NO_STORAGE_POOL, NULL); goto cleanup; } ret = obj->configFile ? 1 : 0; cleanup: if (obj) virStoragePoolObjUnlock(obj); return ret; } static virStoragePoolPtr storagePoolCreate(virConnectPtr conn, const char *xml, unsigned int flags ATTRIBUTE_UNUSED) { virStorageDriverStatePtr driver = conn->storagePrivateData; virStoragePoolDefPtr def; virStoragePoolObjPtr pool = NULL; virStoragePoolPtr ret = NULL; virStorageBackendPtr backend; storageDriverLock(driver); if (!(def = virStoragePoolDefParseString(xml))) goto cleanup; pool = virStoragePoolObjFindByUUID(&driver->pools, def->uuid); if (!pool) pool = virStoragePoolObjFindByName(&driver->pools, def->name); if (pool) { virStorageReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("storage pool already exists")); virStoragePoolObjUnlock(pool); pool = NULL; goto cleanup; } if ((backend = virStorageBackendForType(def->type)) == NULL) goto cleanup; if (!(pool = virStoragePoolObjAssignDef(&driver->pools, def))) goto cleanup; def = NULL; if (backend->startPool && backend->startPool(conn, pool) < 0) { virStoragePoolObjRemove(&driver->pools, pool); pool = NULL; goto cleanup; } if (backend->refreshPool(conn, pool) < 0) { if (backend->stopPool) backend->stopPool(conn, pool); virStoragePoolObjRemove(&driver->pools, pool); pool = NULL; goto cleanup; } pool->active = 1; ret = virGetStoragePool(conn, pool->def->name, pool->def->uuid); cleanup: virStoragePoolDefFree(def); if (pool) virStoragePoolObjUnlock(pool); storageDriverUnlock(driver); return ret; } static virStoragePoolPtr storagePoolDefine(virConnectPtr conn, const char *xml, unsigned int flags ATTRIBUTE_UNUSED) { virStorageDriverStatePtr driver = conn->storagePrivateData; virStoragePoolDefPtr def; virStoragePoolObjPtr pool = NULL; virStoragePoolPtr ret = NULL; storageDriverLock(driver); if (!(def = virStoragePoolDefParseString(xml))) goto cleanup; if (virStorageBackendForType(def->type) == NULL) goto cleanup; if (!(pool = virStoragePoolObjAssignDef(&driver->pools, def))) goto cleanup; if (virStoragePoolObjSaveDef(driver, pool, def) < 0) { virStoragePoolObjRemove(&driver->pools, pool); def = NULL; goto cleanup; } def = NULL; ret = virGetStoragePool(conn, pool->def->name, pool->def->uuid); cleanup: virStoragePoolDefFree(def); if (pool) virStoragePoolObjUnlock(pool); storageDriverUnlock(driver); return ret; } static int storagePoolUndefine(virStoragePoolPtr obj) { virStorageDriverStatePtr driver = obj->conn->storagePrivateData; virStoragePoolObjPtr pool; int ret = -1; storageDriverLock(driver); pool = virStoragePoolObjFindByUUID(&driver->pools, obj->uuid); if (!pool) { virStorageReportError(VIR_ERR_INVALID_STORAGE_POOL, "%s", _("no storage pool with matching uuid")); goto cleanup; } if (virStoragePoolObjIsActive(pool)) { virStorageReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("pool is still active")); goto cleanup; } if (pool->asyncjobs > 0) { virStorageReportError(VIR_ERR_INTERNAL_ERROR, _("pool '%s' has asynchronous jobs running."), pool->def->name); goto cleanup; } if (virStoragePoolObjDeleteDef(pool) < 0) goto cleanup; if (unlink(pool->autostartLink) < 0 && errno != ENOENT && errno != ENOTDIR) { char ebuf[1024]; VIR_ERROR("Failed to delete autostart link '%s': %s", pool->autostartLink, virStrerror(errno, ebuf, sizeof ebuf)); } VIR_FREE(pool->configFile); VIR_FREE(pool->autostartLink); virStoragePoolObjRemove(&driver->pools, pool); pool = NULL; ret = 0; cleanup: if (pool) virStoragePoolObjUnlock(pool); storageDriverUnlock(driver); return ret; } static int storagePoolStart(virStoragePoolPtr obj, unsigned int flags ATTRIBUTE_UNUSED) { virStorageDriverStatePtr driver = obj->conn->storagePrivateData; virStoragePoolObjPtr pool; virStorageBackendPtr backend; int ret = -1; storageDriverLock(driver); pool = virStoragePoolObjFindByUUID(&driver->pools, obj->uuid); storageDriverUnlock(driver); if (!pool) { virStorageReportError(VIR_ERR_INVALID_STORAGE_POOL, "%s", _("no storage pool with matching uuid")); goto cleanup; } if ((backend = virStorageBackendForType(pool->def->type)) == NULL) goto cleanup; if (virStoragePoolObjIsActive(pool)) { virStorageReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("pool already active")); goto cleanup; } if (backend->startPool && backend->startPool(obj->conn, pool) < 0) goto cleanup; if (backend->refreshPool(obj->conn, pool) < 0) { if (backend->stopPool) backend->stopPool(obj->conn, pool); goto cleanup; } pool->active = 1; ret = 0; cleanup: if (pool) virStoragePoolObjUnlock(pool); return ret; } static int storagePoolBuild(virStoragePoolPtr obj, unsigned int flags) { virStorageDriverStatePtr driver = obj->conn->storagePrivateData; virStoragePoolObjPtr pool; virStorageBackendPtr backend; int ret = -1; storageDriverLock(driver); pool = virStoragePoolObjFindByUUID(&driver->pools, obj->uuid); storageDriverUnlock(driver); if (!pool) { virStorageReportError(VIR_ERR_INVALID_STORAGE_POOL, "%s", _("no storage pool with matching uuid")); goto cleanup; } if ((backend = virStorageBackendForType(pool->def->type)) == NULL) goto cleanup; if (virStoragePoolObjIsActive(pool)) { virStorageReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("storage pool is already active")); goto cleanup; } if (backend->buildPool && backend->buildPool(obj->conn, pool, flags) < 0) goto cleanup; ret = 0; cleanup: if (pool) virStoragePoolObjUnlock(pool); return ret; } static int storagePoolDestroy(virStoragePoolPtr obj) { virStorageDriverStatePtr driver = obj->conn->storagePrivateData; virStoragePoolObjPtr pool; virStorageBackendPtr backend; int ret = -1; storageDriverLock(driver); pool = virStoragePoolObjFindByUUID(&driver->pools, obj->uuid); if (!pool) { virStorageReportError(VIR_ERR_INVALID_STORAGE_POOL, "%s", _("no storage pool with matching uuid")); goto cleanup; } if ((backend = virStorageBackendForType(pool->def->type)) == NULL) goto cleanup; if (!virStoragePoolObjIsActive(pool)) { virStorageReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("storage pool is not active")); goto cleanup; } if (pool->asyncjobs > 0) { virStorageReportError(VIR_ERR_INTERNAL_ERROR, _("pool '%s' has asynchronous jobs running."), pool->def->name); goto cleanup; } if (backend->stopPool && backend->stopPool(obj->conn, pool) < 0) goto cleanup; virStoragePoolObjClearVols(pool); pool->active = 0; if (pool->configFile == NULL) { virStoragePoolObjRemove(&driver->pools, pool); pool = NULL; } ret = 0; cleanup: if (pool) virStoragePoolObjUnlock(pool); storageDriverUnlock(driver); return ret; } static int storagePoolDelete(virStoragePoolPtr obj, unsigned int flags) { virStorageDriverStatePtr driver = obj->conn->storagePrivateData; virStoragePoolObjPtr pool; virStorageBackendPtr backend; int ret = -1; storageDriverLock(driver); pool = virStoragePoolObjFindByUUID(&driver->pools, obj->uuid); storageDriverUnlock(driver); if (!pool) { virStorageReportError(VIR_ERR_INVALID_STORAGE_POOL, "%s", _("no storage pool with matching uuid")); goto cleanup; } if ((backend = virStorageBackendForType(pool->def->type)) == NULL) goto cleanup; if (virStoragePoolObjIsActive(pool)) { virStorageReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("storage pool is still active")); goto cleanup; } if (pool->asyncjobs > 0) { virStorageReportError(VIR_ERR_INTERNAL_ERROR, _("pool '%s' has asynchronous jobs running."), pool->def->name); goto cleanup; } if (!backend->deletePool) { virStorageReportError(VIR_ERR_NO_SUPPORT, "%s", _("pool does not support pool deletion")); goto cleanup; } if (backend->deletePool(obj->conn, pool, flags) < 0) goto cleanup; ret = 0; cleanup: if (pool) virStoragePoolObjUnlock(pool); return ret; } static int storagePoolRefresh(virStoragePoolPtr obj, unsigned int flags ATTRIBUTE_UNUSED) { virStorageDriverStatePtr driver = obj->conn->storagePrivateData; virStoragePoolObjPtr pool; virStorageBackendPtr backend; int ret = -1; storageDriverLock(driver); pool = virStoragePoolObjFindByUUID(&driver->pools, obj->uuid); if (!pool) { virStorageReportError(VIR_ERR_INVALID_STORAGE_POOL, "%s", _("no storage pool with matching uuid")); goto cleanup; } if ((backend = virStorageBackendForType(pool->def->type)) == NULL) goto cleanup; if (!virStoragePoolObjIsActive(pool)) { virStorageReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("storage pool is not active")); goto cleanup; } if (pool->asyncjobs > 0) { virStorageReportError(VIR_ERR_INTERNAL_ERROR, _("pool '%s' has asynchronous jobs running."), pool->def->name); goto cleanup; } virStoragePoolObjClearVols(pool); if (backend->refreshPool(obj->conn, pool) < 0) { if (backend->stopPool) backend->stopPool(obj->conn, pool); pool->active = 0; if (pool->configFile == NULL) { virStoragePoolObjRemove(&driver->pools, pool); pool = NULL; } goto cleanup; } ret = 0; cleanup: if (pool) virStoragePoolObjUnlock(pool); storageDriverUnlock(driver); return ret; } static int storagePoolGetInfo(virStoragePoolPtr obj, virStoragePoolInfoPtr info) { virStorageDriverStatePtr driver = obj->conn->storagePrivateData; virStoragePoolObjPtr pool; int ret = -1; storageDriverLock(driver); pool = virStoragePoolObjFindByUUID(&driver->pools, obj->uuid); storageDriverUnlock(driver); if (!pool) { virStorageReportError(VIR_ERR_INVALID_STORAGE_POOL, "%s", _("no storage pool with matching uuid")); goto cleanup; } if (virStorageBackendForType(pool->def->type) == NULL) goto cleanup; memset(info, 0, sizeof(virStoragePoolInfo)); if (pool->active) info->state = VIR_STORAGE_POOL_RUNNING; else info->state = VIR_STORAGE_POOL_INACTIVE; info->capacity = pool->def->capacity; info->allocation = pool->def->allocation; info->available = pool->def->available; ret = 0; cleanup: if (pool) virStoragePoolObjUnlock(pool); return ret; } static char * storagePoolDumpXML(virStoragePoolPtr obj, unsigned int flags ATTRIBUTE_UNUSED) { virStorageDriverStatePtr driver = obj->conn->storagePrivateData; virStoragePoolObjPtr pool; char *ret = NULL; storageDriverLock(driver); pool = virStoragePoolObjFindByUUID(&driver->pools, obj->uuid); storageDriverUnlock(driver); if (!pool) { virStorageReportError(VIR_ERR_INVALID_STORAGE_POOL, "%s", _("no storage pool with matching uuid")); goto cleanup; } ret = virStoragePoolDefFormat(pool->def); cleanup: if (pool) virStoragePoolObjUnlock(pool); return ret; } static int storagePoolGetAutostart(virStoragePoolPtr obj, int *autostart) { virStorageDriverStatePtr driver = obj->conn->storagePrivateData; virStoragePoolObjPtr pool; int ret = -1; storageDriverLock(driver); pool = virStoragePoolObjFindByUUID(&driver->pools, obj->uuid); storageDriverUnlock(driver); if (!pool) { virStorageReportError(VIR_ERR_INVALID_STORAGE_POOL, "%s", _("no pool with matching uuid")); goto cleanup; } if (!pool->configFile) { *autostart = 0; } else { *autostart = pool->autostart; } ret = 0; cleanup: if (pool) virStoragePoolObjUnlock(pool); return ret; } static int storagePoolSetAutostart(virStoragePoolPtr obj, int autostart) { virStorageDriverStatePtr driver = obj->conn->storagePrivateData; virStoragePoolObjPtr pool; int ret = -1; storageDriverLock(driver); pool = virStoragePoolObjFindByUUID(&driver->pools, obj->uuid); if (!pool) { virStorageReportError(VIR_ERR_INVALID_STORAGE_POOL, "%s", _("no pool with matching uuid")); goto cleanup; } if (!pool->configFile) { virStorageReportError(VIR_ERR_INVALID_ARG, "%s", _("pool has no config file")); goto cleanup; } autostart = (autostart != 0); if (pool->autostart != autostart) { if (autostart) { int err; if ((err = virFileMakePath(driver->autostartDir))) { virReportSystemError(err, _("cannot create autostart directory %s"), driver->autostartDir); goto cleanup; } if (symlink(pool->configFile, pool->autostartLink) < 0) { virReportSystemError(errno, _("Failed to create symlink '%s' to '%s'"), pool->autostartLink, pool->configFile); goto cleanup; } } else { if (unlink(pool->autostartLink) < 0 && errno != ENOENT && errno != ENOTDIR) { virReportSystemError(errno, _("Failed to delete symlink '%s'"), pool->autostartLink); goto cleanup; } } pool->autostart = autostart; } ret = 0; cleanup: if (pool) virStoragePoolObjUnlock(pool); storageDriverUnlock(driver); return ret; } static int storagePoolNumVolumes(virStoragePoolPtr obj) { virStorageDriverStatePtr driver = obj->conn->storagePrivateData; virStoragePoolObjPtr pool; int ret = -1; storageDriverLock(driver); pool = virStoragePoolObjFindByUUID(&driver->pools, obj->uuid); storageDriverUnlock(driver); if (!pool) { virStorageReportError(VIR_ERR_INVALID_STORAGE_POOL, "%s", _("no storage pool with matching uuid")); goto cleanup; } if (!virStoragePoolObjIsActive(pool)) { virStorageReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("storage pool is not active")); goto cleanup; } ret = pool->volumes.count; cleanup: if (pool) virStoragePoolObjUnlock(pool); return ret; } static int storagePoolListVolumes(virStoragePoolPtr obj, char **const names, int maxnames) { virStorageDriverStatePtr driver = obj->conn->storagePrivateData; virStoragePoolObjPtr pool; int i, n = 0; memset(names, 0, maxnames * sizeof(*names)); storageDriverLock(driver); pool = virStoragePoolObjFindByUUID(&driver->pools, obj->uuid); storageDriverUnlock(driver); if (!pool) { virStorageReportError(VIR_ERR_INVALID_STORAGE_POOL, "%s", _("no storage pool with matching uuid")); goto cleanup; } if (!virStoragePoolObjIsActive(pool)) { virStorageReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("storage pool is not active")); goto cleanup; } for (i = 0 ; i < pool->volumes.count && n < maxnames ; i++) { if ((names[n++] = strdup(pool->volumes.objs[i]->name)) == NULL) { virReportOOMError(); goto cleanup; } } virStoragePoolObjUnlock(pool); return n; cleanup: if (pool) virStoragePoolObjUnlock(pool); for (n = 0 ; n < maxnames ; n++) VIR_FREE(names[n]); memset(names, 0, maxnames * sizeof(*names)); return -1; } static virStorageVolPtr storageVolumeLookupByName(virStoragePoolPtr obj, const char *name) { virStorageDriverStatePtr driver = obj->conn->storagePrivateData; virStoragePoolObjPtr pool; virStorageVolDefPtr vol; virStorageVolPtr ret = NULL; storageDriverLock(driver); pool = virStoragePoolObjFindByUUID(&driver->pools, obj->uuid); storageDriverUnlock(driver); if (!pool) { virStorageReportError(VIR_ERR_INVALID_STORAGE_POOL, "%s", _("no storage pool with matching uuid")); goto cleanup; } if (!virStoragePoolObjIsActive(pool)) { virStorageReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("storage pool is not active")); goto cleanup; } vol = virStorageVolDefFindByName(pool, name); if (!vol) { virStorageReportError(VIR_ERR_NO_STORAGE_VOL, _("no storage vol with matching name '%s'"), name); goto cleanup; } ret = virGetStorageVol(obj->conn, pool->def->name, vol->name, vol->key); cleanup: if (pool) virStoragePoolObjUnlock(pool); return ret; } static virStorageVolPtr storageVolumeLookupByKey(virConnectPtr conn, const char *key) { virStorageDriverStatePtr driver = conn->storagePrivateData; unsigned int i; virStorageVolPtr ret = NULL; storageDriverLock(driver); for (i = 0 ; i < driver->pools.count && !ret ; i++) { virStoragePoolObjLock(driver->pools.objs[i]); if (virStoragePoolObjIsActive(driver->pools.objs[i])) { virStorageVolDefPtr vol = virStorageVolDefFindByKey(driver->pools.objs[i], key); if (vol) ret = virGetStorageVol(conn, driver->pools.objs[i]->def->name, vol->name, vol->key); } virStoragePoolObjUnlock(driver->pools.objs[i]); } storageDriverUnlock(driver); if (!ret) virStorageReportError(VIR_ERR_INVALID_STORAGE_VOL, "%s", _("no storage vol with matching key")); return ret; } static virStorageVolPtr storageVolumeLookupByPath(virConnectPtr conn, const char *path) { virStorageDriverStatePtr driver = conn->storagePrivateData; unsigned int i; virStorageVolPtr ret = NULL; storageDriverLock(driver); for (i = 0 ; i < driver->pools.count && !ret ; i++) { virStoragePoolObjLock(driver->pools.objs[i]); if (virStoragePoolObjIsActive(driver->pools.objs[i])) { virStorageVolDefPtr vol; const char *stable_path; stable_path = virStorageBackendStablePath(driver->pools.objs[i], path); /* * virStorageBackendStablePath already does * virStorageReportError if it fails; we just need to keep * propagating the return code */ if (stable_path == NULL) { virStoragePoolObjUnlock(driver->pools.objs[i]); goto cleanup; } vol = virStorageVolDefFindByPath(driver->pools.objs[i], stable_path); VIR_FREE(stable_path); if (vol) ret = virGetStorageVol(conn, driver->pools.objs[i]->def->name, vol->name, vol->key); } virStoragePoolObjUnlock(driver->pools.objs[i]); } if (!ret) virStorageReportError(VIR_ERR_INVALID_STORAGE_VOL, "%s", _("no storage vol with matching path")); cleanup: storageDriverUnlock(driver); return ret; } static int storageVolumeDelete(virStorageVolPtr obj, unsigned int flags); static virStorageVolPtr storageVolumeCreateXML(virStoragePoolPtr obj, const char *xmldesc, unsigned int flags ATTRIBUTE_UNUSED) { virStorageDriverStatePtr driver = obj->conn->storagePrivateData; virStoragePoolObjPtr pool; virStorageBackendPtr backend; virStorageVolDefPtr voldef = NULL; virStorageVolPtr ret = NULL, volobj = NULL; storageDriverLock(driver); pool = virStoragePoolObjFindByUUID(&driver->pools, obj->uuid); storageDriverUnlock(driver); if (!pool) { virStorageReportError(VIR_ERR_INVALID_STORAGE_POOL, "%s", _("no storage pool with matching uuid")); goto cleanup; } if (!virStoragePoolObjIsActive(pool)) { virStorageReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("storage pool is not active")); goto cleanup; } if ((backend = virStorageBackendForType(pool->def->type)) == NULL) goto cleanup; voldef = virStorageVolDefParseString(pool->def, xmldesc); if (voldef == NULL) goto cleanup; if (virStorageVolDefFindByName(pool, voldef->name)) { virStorageReportError(VIR_ERR_INVALID_STORAGE_POOL, "%s", _("storage vol already exists")); goto cleanup; } if (VIR_REALLOC_N(pool->volumes.objs, pool->volumes.count+1) < 0) { virReportOOMError(); goto cleanup; } if (!backend->createVol) { virStorageReportError(VIR_ERR_NO_SUPPORT, "%s", _("storage pool does not support volume " "creation")); goto cleanup; } if (backend->createVol(obj->conn, pool, voldef) < 0) { goto cleanup; } pool->volumes.objs[pool->volumes.count++] = voldef; volobj = virGetStorageVol(obj->conn, pool->def->name, voldef->name, voldef->key); if (volobj && backend->buildVol) { int buildret; virStorageVolDefPtr buildvoldef = NULL; if (VIR_ALLOC(buildvoldef) < 0) { virReportOOMError(); voldef = NULL; goto cleanup; } /* Make a shallow copy of the 'defined' volume definition, since the * original allocation value will change as the user polls 'info', * but we only need the initial requested values */ memcpy(buildvoldef, voldef, sizeof(*voldef)); /* Drop the pool lock during volume allocation */ pool->asyncjobs++; voldef->building = 1; virStoragePoolObjUnlock(pool); buildret = backend->buildVol(obj->conn, pool, buildvoldef); storageDriverLock(driver); virStoragePoolObjLock(pool); storageDriverUnlock(driver); voldef->building = 0; pool->asyncjobs--; voldef = NULL; VIR_FREE(buildvoldef); if (buildret < 0) { virStoragePoolObjUnlock(pool); storageVolumeDelete(volobj, 0); pool = NULL; goto cleanup; } } ret = volobj; volobj = NULL; voldef = NULL; cleanup: if (volobj) virUnrefStorageVol(volobj); virStorageVolDefFree(voldef); if (pool) virStoragePoolObjUnlock(pool); return ret; } static virStorageVolPtr storageVolumeCreateXMLFrom(virStoragePoolPtr obj, const char *xmldesc, virStorageVolPtr vobj, unsigned int flags ATTRIBUTE_UNUSED) { virStorageDriverStatePtr driver = obj->conn->storagePrivateData; virStoragePoolObjPtr pool, origpool = NULL; virStorageBackendPtr backend; virStorageVolDefPtr origvol = NULL, newvol = NULL; virStorageVolPtr ret = NULL, volobj = NULL; int buildret; storageDriverLock(driver); pool = virStoragePoolObjFindByUUID(&driver->pools, obj->uuid); if (pool && STRNEQ(obj->name, vobj->pool)) { virStoragePoolObjUnlock(pool); origpool = virStoragePoolObjFindByName(&driver->pools, vobj->pool); virStoragePoolObjLock(pool); } storageDriverUnlock(driver); if (!pool) { virStorageReportError(VIR_ERR_INVALID_STORAGE_POOL, "%s", _("no storage pool with matching uuid")); goto cleanup; } if (STRNEQ(obj->name, vobj->pool) && !origpool) { virStorageReportError(VIR_ERR_NO_STORAGE_POOL, _("no storage pool with matching name '%s'"), vobj->pool); goto cleanup; } if (!virStoragePoolObjIsActive(pool)) { virStorageReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("storage pool is not active")); goto cleanup; } if (origpool && !virStoragePoolObjIsActive(origpool)) { virStorageReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("storage pool is not active")); goto cleanup; } if ((backend = virStorageBackendForType(pool->def->type)) == NULL) goto cleanup; origvol = virStorageVolDefFindByName(origpool ? origpool : pool, vobj->name); if (!origvol) { virStorageReportError(VIR_ERR_NO_STORAGE_VOL, _("no storage vol with matching name '%s'"), vobj->name); goto cleanup; } newvol = virStorageVolDefParseString(pool->def, xmldesc); if (newvol == NULL) goto cleanup; if (virStorageVolDefFindByName(pool, newvol->name)) { virStorageReportError(VIR_ERR_INVALID_STORAGE_POOL, _("storage volume name '%s' already in use."), newvol->name); goto cleanup; } /* Is there ever a valid case for this? */ if (newvol->capacity < origvol->capacity) newvol->capacity = origvol->capacity; /* Make sure allocation is at least as large as the destination cap, * to make absolutely sure we copy all possible contents */ if (newvol->allocation < origvol->capacity) newvol->allocation = origvol->capacity; if (!backend->buildVolFrom) { virStorageReportError(VIR_ERR_NO_SUPPORT, "%s", _("storage pool does not support volume creation from an existing volume")); goto cleanup; } if (origvol->building) { virStorageReportError(VIR_ERR_INTERNAL_ERROR, _("volume '%s' is still being allocated."), origvol->name); goto cleanup; } if (backend->refreshVol && backend->refreshVol(obj->conn, pool, origvol) < 0) goto cleanup; if (VIR_REALLOC_N(pool->volumes.objs, pool->volumes.count+1) < 0) { virReportOOMError(); goto cleanup; } /* 'Define' the new volume so we get async progress reporting */ if (backend->createVol(obj->conn, pool, newvol) < 0) { goto cleanup; } pool->volumes.objs[pool->volumes.count++] = newvol; volobj = virGetStorageVol(obj->conn, pool->def->name, newvol->name, newvol->key); /* Drop the pool lock during volume allocation */ pool->asyncjobs++; origvol->building = 1; newvol->building = 1; virStoragePoolObjUnlock(pool); if (origpool) { origpool->asyncjobs++; virStoragePoolObjUnlock(origpool); } buildret = backend->buildVolFrom(obj->conn, pool, newvol, origvol, flags); storageDriverLock(driver); virStoragePoolObjLock(pool); if (origpool) virStoragePoolObjLock(origpool); storageDriverUnlock(driver); origvol->building = 0; newvol->building = 0; newvol = NULL; pool->asyncjobs--; if (origpool) { origpool->asyncjobs--; virStoragePoolObjUnlock(origpool); origpool = NULL; } if (buildret < 0) { virStoragePoolObjUnlock(pool); storageVolumeDelete(volobj, 0); pool = NULL; goto cleanup; } ret = volobj; volobj = NULL; cleanup: if (volobj) virUnrefStorageVol(volobj); virStorageVolDefFree(newvol); if (pool) virStoragePoolObjUnlock(pool); if (origpool) virStoragePoolObjUnlock(origpool); return ret; } static int storageVolumeDelete(virStorageVolPtr obj, unsigned int flags) { virStorageDriverStatePtr driver = obj->conn->storagePrivateData; virStoragePoolObjPtr pool; virStorageBackendPtr backend; virStorageVolDefPtr vol = NULL; unsigned int i; int ret = -1; storageDriverLock(driver); pool = virStoragePoolObjFindByName(&driver->pools, obj->pool); storageDriverUnlock(driver); if (!pool) { virStorageReportError(VIR_ERR_INVALID_STORAGE_POOL, "%s", _("no storage pool with matching uuid")); goto cleanup; } if (!virStoragePoolObjIsActive(pool)) { virStorageReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("storage pool is not active")); goto cleanup; } if ((backend = virStorageBackendForType(pool->def->type)) == NULL) goto cleanup; vol = virStorageVolDefFindByName(pool, obj->name); if (!vol) { virStorageReportError(VIR_ERR_NO_STORAGE_VOL, _("no storage vol with matching name '%s'"), obj->name); goto cleanup; } if (vol->building) { virStorageReportError(VIR_ERR_INTERNAL_ERROR, _("volume '%s' is still being allocated."), vol->name); goto cleanup; } if (!backend->deleteVol) { virStorageReportError(VIR_ERR_NO_SUPPORT, "%s", _("storage pool does not support vol deletion")); goto cleanup; } if (backend->deleteVol(obj->conn, pool, vol, flags) < 0) goto cleanup; for (i = 0 ; i < pool->volumes.count ; i++) { if (pool->volumes.objs[i] == vol) { virStorageVolDefFree(vol); vol = NULL; if (i < (pool->volumes.count - 1)) memmove(pool->volumes.objs + i, pool->volumes.objs + i + 1, sizeof(*(pool->volumes.objs)) * (pool->volumes.count - (i + 1))); if (VIR_REALLOC_N(pool->volumes.objs, pool->volumes.count - 1) < 0) { ; /* Failure to reduce memory allocation isn't fatal */ } pool->volumes.count--; break; } } ret = 0; cleanup: if (pool) virStoragePoolObjUnlock(pool); return ret; } static int storageVolumeGetInfo(virStorageVolPtr obj, virStorageVolInfoPtr info) { virStorageDriverStatePtr driver = obj->conn->storagePrivateData; virStoragePoolObjPtr pool; virStorageBackendPtr backend; virStorageVolDefPtr vol; int ret = -1; storageDriverLock(driver); pool = virStoragePoolObjFindByName(&driver->pools, obj->pool); storageDriverUnlock(driver); if (!pool) { virStorageReportError(VIR_ERR_INVALID_STORAGE_POOL, "%s", _("no storage pool with matching uuid")); goto cleanup; } if (!virStoragePoolObjIsActive(pool)) { virStorageReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("storage pool is not active")); goto cleanup; } vol = virStorageVolDefFindByName(pool, obj->name); if (!vol) { virStorageReportError(VIR_ERR_NO_STORAGE_VOL, _("no storage vol with matching name '%s'"), obj->name); goto cleanup; } if ((backend = virStorageBackendForType(pool->def->type)) == NULL) goto cleanup; if (backend->refreshVol && backend->refreshVol(obj->conn, pool, vol) < 0) goto cleanup; memset(info, 0, sizeof(*info)); info->type = vol->type; info->capacity = vol->capacity; info->allocation = vol->allocation; ret = 0; cleanup: if (pool) virStoragePoolObjUnlock(pool); return ret; } static char * storageVolumeGetXMLDesc(virStorageVolPtr obj, unsigned int flags ATTRIBUTE_UNUSED) { virStorageDriverStatePtr driver = obj->conn->storagePrivateData; virStoragePoolObjPtr pool; virStorageBackendPtr backend; virStorageVolDefPtr vol; char *ret = NULL; storageDriverLock(driver); pool = virStoragePoolObjFindByName(&driver->pools, obj->pool); storageDriverUnlock(driver); if (!pool) { virStorageReportError(VIR_ERR_INVALID_STORAGE_POOL, "%s", _("no storage pool with matching uuid")); goto cleanup; } if (!virStoragePoolObjIsActive(pool)) { virStorageReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("storage pool is not active")); goto cleanup; } vol = virStorageVolDefFindByName(pool, obj->name); if (!vol) { virStorageReportError(VIR_ERR_NO_STORAGE_VOL, _("no storage vol with matching name '%s'"), obj->name); goto cleanup; } if ((backend = virStorageBackendForType(pool->def->type)) == NULL) goto cleanup; if (backend->refreshVol && backend->refreshVol(obj->conn, pool, vol) < 0) goto cleanup; ret = virStorageVolDefFormat(pool->def, vol); cleanup: if (pool) virStoragePoolObjUnlock(pool); return ret; } static char * storageVolumeGetPath(virStorageVolPtr obj) { virStorageDriverStatePtr driver = obj->conn->storagePrivateData; virStoragePoolObjPtr pool; virStorageVolDefPtr vol; char *ret = NULL; storageDriverLock(driver); pool = virStoragePoolObjFindByName(&driver->pools, obj->pool); storageDriverUnlock(driver); if (!pool) { virStorageReportError(VIR_ERR_INVALID_STORAGE_POOL, "%s", _("no storage pool with matching uuid")); goto cleanup; } if (!virStoragePoolObjIsActive(pool)) { virStorageReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("storage pool is not active")); goto cleanup; } vol = virStorageVolDefFindByName(pool, obj->name); if (!vol) { virStorageReportError(VIR_ERR_NO_STORAGE_VOL, _("no storage vol with matching name '%s'"), obj->name); goto cleanup; } ret = strdup(vol->target.path); if (ret == NULL) virReportOOMError(); cleanup: if (pool) virStoragePoolObjUnlock(pool); return ret; } static virStorageDriver storageDriver = { .name = "storage", .open = storageOpen, .close = storageClose, .numOfPools = storageNumPools, .listPools = storageListPools, .numOfDefinedPools = storageNumDefinedPools, .listDefinedPools = storageListDefinedPools, .findPoolSources = storageFindPoolSources, .poolLookupByName = storagePoolLookupByName, .poolLookupByUUID = storagePoolLookupByUUID, .poolLookupByVolume = storagePoolLookupByVolume, .poolCreateXML = storagePoolCreate, .poolDefineXML = storagePoolDefine, .poolBuild = storagePoolBuild, .poolUndefine = storagePoolUndefine, .poolCreate = storagePoolStart, .poolDestroy = storagePoolDestroy, .poolDelete = storagePoolDelete, .poolRefresh = storagePoolRefresh, .poolGetInfo = storagePoolGetInfo, .poolGetXMLDesc = storagePoolDumpXML, .poolGetAutostart = storagePoolGetAutostart, .poolSetAutostart = storagePoolSetAutostart, .poolNumOfVolumes = storagePoolNumVolumes, .poolListVolumes = storagePoolListVolumes, .volLookupByName = storageVolumeLookupByName, .volLookupByKey = storageVolumeLookupByKey, .volLookupByPath = storageVolumeLookupByPath, .volCreateXML = storageVolumeCreateXML, .volCreateXMLFrom = storageVolumeCreateXMLFrom, .volDelete = storageVolumeDelete, .volGetInfo = storageVolumeGetInfo, .volGetXMLDesc = storageVolumeGetXMLDesc, .volGetPath = storageVolumeGetPath, .poolIsActive = storagePoolIsActive, .poolIsPersistent = storagePoolIsPersistent, }; static virStateDriver stateDriver = { .name = "Storage", .initialize = storageDriverStartup, .cleanup = storageDriverShutdown, .reload = storageDriverReload, .active = storageDriverActive, }; int storageRegister(void) { virRegisterStorageDriver(&storageDriver); virRegisterStateDriver(&stateDriver); return 0; }