libvirt/src/storage/storage_driver.c
Osier Yang 05e2fc51d1 storage: Do not break the whole vol lookup process in the middle
* src/storage/storage_driver.c: As virStorageVolLookupByPath lookups
all the pool objs of the drivers, breaking when failing on getting
the stable path of the pool will just breaks the whole lookup process,
it can cause the API fails even if the vol exists indeed. It won't get
any benefit. This patch is to fix it.
2011-09-27 08:38:12 +08:00

2200 lines
62 KiB
C

/*
* storage_driver.c: core driver for storage APIs
*
* Copyright (C) 2006-2011 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 <berrange@redhat.com>
*/
#include <config.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/param.h>
#include <fcntl.h>
#if HAVE_PWD_H
# include <pwd.h>
#endif
#include <errno.h>
#include <string.h>
#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"
#include "virfile.h"
#include "fdstream.h"
#include "configmake.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];
virStorageBackendPtr backend;
bool started = false;
virStoragePoolObjLock(pool);
if ((backend = virStorageBackendForType(pool->def->type)) == NULL) {
VIR_ERROR(_("Missing backend %d"), pool->def->type);
virStoragePoolObjUnlock(pool);
continue;
}
if (backend->checkPool &&
backend->checkPool(NULL, pool, &started) < 0) {
virErrorPtr err = virGetLastError();
VIR_ERROR(_("Failed to initialize storage pool '%s': %s"),
pool->def->name, err ? err->message :
_("no error message found"));
virStoragePoolObjUnlock(pool);
continue;
}
if (!started &&
pool->autostart &&
!virStoragePoolObjIsActive(pool)) {
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;
}
started = true;
}
if (started) {
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;
if (VIR_ALLOC(driverState) < 0)
return -1;
if (virMutexInit(&driverState->lock) < 0) {
VIR_FREE(driverState);
return -1;
}
storageDriverLock(driverState);
if (privileged) {
if ((base = strdup (SYSCONFDIR "/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 (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 (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,
unsigned int flags)
{
virCheckFlags(VIR_CONNECT_RO, VIR_DRV_OPEN_ERROR);
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)
{
virStorageDriverStatePtr driver = conn->storagePrivateData;
virStoragePoolDefPtr def;
virStoragePoolObjPtr pool = NULL;
virStoragePoolPtr ret = NULL;
virStorageBackendPtr backend;
virCheckFlags(0, NULL);
storageDriverLock(driver);
if (!(def = virStoragePoolDefParseString(xml)))
goto cleanup;
if (virStoragePoolObjIsDuplicate(&driver->pools, def, 1) < 0)
goto cleanup;
if (virStoragePoolSourceFindDuplicate(&driver->pools, def) < 0)
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;
}
VIR_INFO("Creating storage pool '%s'", pool->def->name);
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)
{
virStorageDriverStatePtr driver = conn->storagePrivateData;
virStoragePoolDefPtr def;
virStoragePoolObjPtr pool = NULL;
virStoragePoolPtr ret = NULL;
virCheckFlags(0, NULL);
storageDriverLock(driver);
if (!(def = virStoragePoolDefParseString(xml)))
goto cleanup;
if (virStoragePoolObjIsDuplicate(&driver->pools, def, 0) < 0)
goto cleanup;
if (virStoragePoolSourceFindDuplicate(&driver->pools, def) < 0)
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;
VIR_INFO("Defining storage pool '%s'", pool->def->name);
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_NO_STORAGE_POOL,
"%s", _("no storage pool with matching uuid"));
goto cleanup;
}
if (virStoragePoolObjIsActive(pool)) {
virStorageReportError(VIR_ERR_OPERATION_INVALID,
"%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);
VIR_INFO("Undefining storage pool '%s'", pool->def->name);
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)
{
virStorageDriverStatePtr driver = obj->conn->storagePrivateData;
virStoragePoolObjPtr pool;
virStorageBackendPtr backend;
int ret = -1;
virCheckFlags(0, -1);
storageDriverLock(driver);
pool = virStoragePoolObjFindByUUID(&driver->pools, obj->uuid);
storageDriverUnlock(driver);
if (!pool) {
virStorageReportError(VIR_ERR_NO_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_OPERATION_INVALID,
"%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;
}
VIR_INFO("Starting up storage pool '%s'", pool->def->name);
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_NO_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_OPERATION_INVALID,
"%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_NO_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_OPERATION_INVALID,
"%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;
VIR_INFO("Shutting down storage pool '%s'", pool->def->name);
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_NO_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_OPERATION_INVALID,
"%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;
VIR_INFO("Deleting storage pool '%s'", pool->def->name);
ret = 0;
cleanup:
if (pool)
virStoragePoolObjUnlock(pool);
return ret;
}
static int
storagePoolRefresh(virStoragePoolPtr obj,
unsigned int flags)
{
virStorageDriverStatePtr driver = obj->conn->storagePrivateData;
virStoragePoolObjPtr pool;
virStorageBackendPtr backend;
int ret = -1;
virCheckFlags(0, -1);
storageDriverLock(driver);
pool = virStoragePoolObjFindByUUID(&driver->pools, obj->uuid);
if (!pool) {
virStorageReportError(VIR_ERR_NO_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_OPERATION_INVALID,
"%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_NO_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 *
storagePoolGetXMLDesc(virStoragePoolPtr obj,
unsigned int flags)
{
virStorageDriverStatePtr driver = obj->conn->storagePrivateData;
virStoragePoolObjPtr pool;
char *ret = NULL;
virCheckFlags(0, NULL);
storageDriverLock(driver);
pool = virStoragePoolObjFindByUUID(&driver->pools, obj->uuid);
storageDriverUnlock(driver);
if (!pool) {
virStorageReportError(VIR_ERR_NO_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_NO_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_NO_STORAGE_POOL,
"%s", _("no pool with matching uuid"));
goto cleanup;
}
if (!pool->configFile) {
virStorageReportError(VIR_ERR_INTERNAL_ERROR,
"%s", _("pool has no config file"));
goto cleanup;
}
autostart = (autostart != 0);
if (pool->autostart != autostart) {
if (autostart) {
if (virFileMakePath(driver->autostartDir) < 0) {
virReportSystemError(errno,
_("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_NO_STORAGE_POOL,
"%s", _("no storage pool with matching uuid"));
goto cleanup;
}
if (!virStoragePoolObjIsActive(pool)) {
virStorageReportError(VIR_ERR_OPERATION_INVALID,
"%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_NO_STORAGE_POOL,
"%s", _("no storage pool with matching uuid"));
goto cleanup;
}
if (!virStoragePoolObjIsActive(pool)) {
virStorageReportError(VIR_ERR_OPERATION_INVALID,
"%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_NO_STORAGE_POOL,
"%s", _("no storage pool with matching uuid"));
goto cleanup;
}
if (!virStoragePoolObjIsActive(pool)) {
virStorageReportError(VIR_ERR_OPERATION_INVALID,
"%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_NO_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;
char *cleanpath;
cleanpath = virFileSanitizePath(path);
if (!cleanpath)
return 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],
cleanpath);
if (stable_path == NULL) {
/* Don't break the whole lookup process if it fails on
* getting the stable path for some of the pools.
*/
VIR_WARN("Failed to get stable path for pool '%s'",
driver->pools.objs[i]->def->name);
virStoragePoolObjUnlock(driver->pools.objs[i]);
continue;
}
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_NO_STORAGE_VOL,
"%s", _("no storage vol with matching path"));
VIR_FREE(cleanpath);
storageDriverUnlock(driver);
return ret;
}
static int storageVolumeDelete(virStorageVolPtr obj, unsigned int flags);
static virStorageVolPtr
storageVolumeCreateXML(virStoragePoolPtr obj,
const char *xmldesc,
unsigned int flags)
{
virStorageDriverStatePtr driver = obj->conn->storagePrivateData;
virStoragePoolObjPtr pool;
virStorageBackendPtr backend;
virStorageVolDefPtr voldef = NULL;
virStorageVolPtr ret = NULL, volobj = NULL;
virCheckFlags(0, NULL);
storageDriverLock(driver);
pool = virStoragePoolObjFindByUUID(&driver->pools, obj->uuid);
storageDriverUnlock(driver);
if (!pool) {
virStorageReportError(VIR_ERR_NO_STORAGE_POOL,
"%s", _("no storage pool with matching uuid"));
goto cleanup;
}
if (!virStoragePoolObjIsActive(pool)) {
virStorageReportError(VIR_ERR_OPERATION_INVALID,
"%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_NO_STORAGE_VOL,
"%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) {
pool->volumes.count--;
goto cleanup;
}
if (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;
}
}
VIR_INFO("Creating volume '%s' in storage pool '%s'",
volobj->name, pool->def->name);
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)
{
virStorageDriverStatePtr driver = obj->conn->storagePrivateData;
virStoragePoolObjPtr pool, origpool = NULL;
virStorageBackendPtr backend;
virStorageVolDefPtr origvol = NULL, newvol = NULL;
virStorageVolPtr ret = NULL, volobj = NULL;
int buildret;
virCheckFlags(0, NULL);
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_NO_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_OPERATION_INVALID,
"%s", _("storage pool is not active"));
goto cleanup;
}
if (origpool && !virStoragePoolObjIsActive(origpool)) {
virStorageReportError(VIR_ERR_OPERATION_INVALID,
"%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_INTERNAL_ERROR,
_("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_OPERATION_INVALID,
_("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;
}
VIR_INFO("Creating volume '%s' in storage pool '%s'",
volobj->name, pool->def->name);
ret = volobj;
volobj = NULL;
cleanup:
if (volobj)
virUnrefStorageVol(volobj);
virStorageVolDefFree(newvol);
if (pool)
virStoragePoolObjUnlock(pool);
if (origpool)
virStoragePoolObjUnlock(origpool);
return ret;
}
static int
storageVolumeDownload(virStorageVolPtr obj,
virStreamPtr stream,
unsigned long long offset,
unsigned long long length,
unsigned int flags)
{
virStorageDriverStatePtr driver = obj->conn->storagePrivateData;
virStoragePoolObjPtr pool = NULL;
virStorageVolDefPtr vol = NULL;
int ret = -1;
virCheckFlags(0, -1);
storageDriverLock(driver);
pool = virStoragePoolObjFindByName(&driver->pools, obj->pool);
storageDriverUnlock(driver);
if (!pool) {
virStorageReportError(VIR_ERR_NO_STORAGE_POOL,
"%s", _("no storage pool with matching uuid"));
goto out;
}
if (!virStoragePoolObjIsActive(pool)) {
virStorageReportError(VIR_ERR_OPERATION_INVALID,
"%s", _("storage pool is not active"));
goto out;
}
vol = virStorageVolDefFindByName(pool, obj->name);
if (vol == NULL) {
virStorageReportError(VIR_ERR_NO_STORAGE_VOL,
_("no storage vol with matching name '%s'"),
obj->name);
goto out;
}
if (vol->building) {
virStorageReportError(VIR_ERR_OPERATION_INVALID,
_("volume '%s' is still being allocated."),
vol->name);
goto out;
}
if (virFDStreamOpenFile(stream,
vol->target.path,
offset, length,
O_RDONLY) < 0)
goto out;
ret = 0;
out:
if (pool)
virStoragePoolObjUnlock(pool);
return ret;
}
static int
storageVolumeUpload(virStorageVolPtr obj,
virStreamPtr stream,
unsigned long long offset,
unsigned long long length,
unsigned int flags)
{
virStorageDriverStatePtr driver = obj->conn->storagePrivateData;
virStoragePoolObjPtr pool = NULL;
virStorageVolDefPtr vol = NULL;
int ret = -1;
virCheckFlags(0, -1);
storageDriverLock(driver);
pool = virStoragePoolObjFindByName(&driver->pools, obj->pool);
storageDriverUnlock(driver);
if (!pool) {
virStorageReportError(VIR_ERR_NO_STORAGE_POOL,
"%s", _("no storage pool with matching uuid"));
goto out;
}
if (!virStoragePoolObjIsActive(pool)) {
virStorageReportError(VIR_ERR_OPERATION_INVALID,
"%s", _("storage pool is not active"));
goto out;
}
vol = virStorageVolDefFindByName(pool, obj->name);
if (vol == NULL) {
virStorageReportError(VIR_ERR_NO_STORAGE_VOL,
_("no storage vol with matching name '%s'"),
obj->name);
goto out;
}
if (vol->building) {
virStorageReportError(VIR_ERR_OPERATION_INVALID,
_("volume '%s' is still being allocated."),
vol->name);
goto out;
}
/* Not using O_CREAT because the file is required to
* already exist at this point */
if (virFDStreamOpenFile(stream,
vol->target.path,
offset, length,
O_WRONLY) < 0)
goto out;
ret = 0;
out:
if (pool)
virStoragePoolObjUnlock(pool);
return ret;
}
/* If the volume we're wiping is already a sparse file, we simply
* truncate and extend it to its original size, filling it with
* zeroes. This behavior is guaranteed by POSIX:
*
* http://www.opengroup.org/onlinepubs/9699919799/functions/ftruncate.html
*
* If fildes refers to a regular file, the ftruncate() function shall
* cause the size of the file to be truncated to length. If the size
* of the file previously exceeded length, the extra data shall no
* longer be available to reads on the file. If the file previously
* was smaller than this size, ftruncate() shall increase the size of
* the file. If the file size is increased, the extended area shall
* appear as if it were zero-filled.
*/
static int
storageVolumeZeroSparseFile(virStorageVolDefPtr vol,
off_t size,
int fd)
{
int ret = -1;
ret = ftruncate(fd, 0);
if (ret == -1) {
virReportSystemError(errno,
_("Failed to truncate volume with "
"path '%s' to 0 bytes"),
vol->target.path);
goto out;
}
ret = ftruncate(fd, size);
if (ret == -1) {
virReportSystemError(errno,
_("Failed to truncate volume with "
"path '%s' to %ju bytes"),
vol->target.path, (uintmax_t)size);
}
out:
return ret;
}
static int
storageWipeExtent(virStorageVolDefPtr vol,
int fd,
off_t extent_start,
off_t extent_length,
char *writebuf,
size_t writebuf_length,
size_t *bytes_wiped)
{
int ret = -1, written = 0;
off_t remaining = 0;
size_t write_size = 0;
VIR_DEBUG("extent logical start: %ju len: %ju",
(uintmax_t)extent_start, (uintmax_t)extent_length);
if ((ret = lseek(fd, extent_start, SEEK_SET)) < 0) {
virReportSystemError(errno,
_("Failed to seek to position %ju in volume "
"with path '%s'"),
(uintmax_t)extent_start, vol->target.path);
goto out;
}
remaining = extent_length;
while (remaining > 0) {
write_size = (writebuf_length < remaining) ? writebuf_length : remaining;
written = safewrite(fd, writebuf, write_size);
if (written < 0) {
virReportSystemError(errno,
_("Failed to write %zu bytes to "
"storage volume with path '%s'"),
write_size, vol->target.path);
goto out;
}
*bytes_wiped += written;
remaining -= written;
}
if (fdatasync(fd) < 0) {
ret = -errno;
virReportSystemError(errno,
_("cannot sync data to volume with path '%s'"),
vol->target.path);
goto out;
}
VIR_DEBUG("Wrote %zu bytes to volume with path '%s'",
*bytes_wiped, vol->target.path);
ret = 0;
out:
return ret;
}
static int
storageVolumeWipeInternal(virStorageVolDefPtr def)
{
int ret = -1, fd = -1;
struct stat st;
char *writebuf = NULL;
size_t bytes_wiped = 0;
VIR_DEBUG("Wiping volume with path '%s'", def->target.path);
fd = open(def->target.path, O_RDWR);
if (fd == -1) {
virReportSystemError(errno,
_("Failed to open storage volume with path '%s'"),
def->target.path);
goto out;
}
if (fstat(fd, &st) == -1) {
virReportSystemError(errno,
_("Failed to stat storage volume with path '%s'"),
def->target.path);
goto out;
}
if (S_ISREG(st.st_mode) && st.st_blocks < (st.st_size / DEV_BSIZE)) {
ret = storageVolumeZeroSparseFile(def, st.st_size, fd);
} else {
if (VIR_ALLOC_N(writebuf, st.st_blksize) != 0) {
virReportOOMError();
goto out;
}
ret = storageWipeExtent(def,
fd,
0,
def->allocation,
writebuf,
st.st_blksize,
&bytes_wiped);
}
out:
VIR_FREE(writebuf);
VIR_FORCE_CLOSE(fd);
return ret;
}
static int
storageVolumeWipe(virStorageVolPtr obj,
unsigned int flags)
{
virStorageDriverStatePtr driver = obj->conn->storagePrivateData;
virStoragePoolObjPtr pool = NULL;
virStorageVolDefPtr vol = NULL;
int ret = -1;
virCheckFlags(0, -1);
storageDriverLock(driver);
pool = virStoragePoolObjFindByName(&driver->pools, obj->pool);
storageDriverUnlock(driver);
if (!pool) {
virStorageReportError(VIR_ERR_NO_STORAGE_POOL,
"%s", _("no storage pool with matching uuid"));
goto out;
}
if (!virStoragePoolObjIsActive(pool)) {
virStorageReportError(VIR_ERR_OPERATION_INVALID,
"%s", _("storage pool is not active"));
goto out;
}
vol = virStorageVolDefFindByName(pool, obj->name);
if (vol == NULL) {
virStorageReportError(VIR_ERR_NO_STORAGE_VOL,
_("no storage vol with matching name '%s'"),
obj->name);
goto out;
}
if (vol->building) {
virStorageReportError(VIR_ERR_OPERATION_INVALID,
_("volume '%s' is still being allocated."),
vol->name);
goto out;
}
if (storageVolumeWipeInternal(vol) == -1) {
goto out;
}
ret = 0;
out:
if (pool) {
virStoragePoolObjUnlock(pool);
}
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_NO_STORAGE_POOL,
"%s", _("no storage pool with matching uuid"));
goto cleanup;
}
if (!virStoragePoolObjIsActive(pool)) {
virStorageReportError(VIR_ERR_OPERATION_INVALID,
"%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_OPERATION_INVALID,
_("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) {
VIR_INFO("Deleting volume '%s' from storage pool '%s'",
vol->name, pool->def->name);
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_NO_STORAGE_POOL,
"%s", _("no storage pool with matching uuid"));
goto cleanup;
}
if (!virStoragePoolObjIsActive(pool)) {
virStorageReportError(VIR_ERR_OPERATION_INVALID,
"%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)
{
virStorageDriverStatePtr driver = obj->conn->storagePrivateData;
virStoragePoolObjPtr pool;
virStorageBackendPtr backend;
virStorageVolDefPtr vol;
char *ret = NULL;
virCheckFlags(0, NULL);
storageDriverLock(driver);
pool = virStoragePoolObjFindByName(&driver->pools, obj->pool);
storageDriverUnlock(driver);
if (!pool) {
virStorageReportError(VIR_ERR_NO_STORAGE_POOL,
"%s", _("no storage pool with matching uuid"));
goto cleanup;
}
if (!virStoragePoolObjIsActive(pool)) {
virStorageReportError(VIR_ERR_OPERATION_INVALID,
"%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_NO_STORAGE_POOL,
"%s", _("no storage pool with matching uuid"));
goto cleanup;
}
if (!virStoragePoolObjIsActive(pool)) {
virStorageReportError(VIR_ERR_OPERATION_INVALID,
"%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, /* 0.4.0 */
.close = storageClose, /* 0.4.0 */
.numOfPools = storageNumPools, /* 0.4.0 */
.listPools = storageListPools, /* 0.4.0 */
.numOfDefinedPools = storageNumDefinedPools, /* 0.4.0 */
.listDefinedPools = storageListDefinedPools, /* 0.4.0 */
.findPoolSources = storageFindPoolSources, /* 0.4.0 */
.poolLookupByName = storagePoolLookupByName, /* 0.4.0 */
.poolLookupByUUID = storagePoolLookupByUUID, /* 0.4.0 */
.poolLookupByVolume = storagePoolLookupByVolume, /* 0.4.0 */
.poolCreateXML = storagePoolCreate, /* 0.4.0 */
.poolDefineXML = storagePoolDefine, /* 0.4.0 */
.poolBuild = storagePoolBuild, /* 0.4.0 */
.poolUndefine = storagePoolUndefine, /* 0.4.0 */
.poolCreate = storagePoolStart, /* 0.4.0 */
.poolDestroy = storagePoolDestroy, /* 0.4.0 */
.poolDelete = storagePoolDelete, /* 0.4.0 */
.poolRefresh = storagePoolRefresh, /* 0.4.0 */
.poolGetInfo = storagePoolGetInfo, /* 0.4.0 */
.poolGetXMLDesc = storagePoolGetXMLDesc, /* 0.4.0 */
.poolGetAutostart = storagePoolGetAutostart, /* 0.4.0 */
.poolSetAutostart = storagePoolSetAutostart, /* 0.4.0 */
.poolNumOfVolumes = storagePoolNumVolumes, /* 0.4.0 */
.poolListVolumes = storagePoolListVolumes, /* 0.4.0 */
.volLookupByName = storageVolumeLookupByName, /* 0.4.0 */
.volLookupByKey = storageVolumeLookupByKey, /* 0.4.0 */
.volLookupByPath = storageVolumeLookupByPath, /* 0.4.0 */
.volCreateXML = storageVolumeCreateXML, /* 0.4.0 */
.volCreateXMLFrom = storageVolumeCreateXMLFrom, /* 0.6.4 */
.volDownload = storageVolumeDownload, /* 0.9.0 */
.volUpload = storageVolumeUpload, /* 0.9.0 */
.volDelete = storageVolumeDelete, /* 0.4.0 */
.volWipe = storageVolumeWipe, /* 0.8.0 */
.volGetInfo = storageVolumeGetInfo, /* 0.4.0 */
.volGetXMLDesc = storageVolumeGetXMLDesc, /* 0.4.0 */
.volGetPath = storageVolumeGetPath, /* 0.4.0 */
.poolIsActive = storagePoolIsActive, /* 0.7.3 */
.poolIsPersistent = storagePoolIsPersistent, /* 0.7.3 */
};
static virStateDriver stateDriver = {
.name = "Storage",
.initialize = storageDriverStartup,
.cleanup = storageDriverShutdown,
.reload = storageDriverReload,
.active = storageDriverActive,
};
int storageRegister(void) {
virRegisterStorageDriver(&storageDriver);
virRegisterStateDriver(&stateDriver);
return 0;
}