libvirt/src/storage/storage_driver.c

2201 lines
62 KiB
C
Raw Normal View History

/*
* 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"
maint: use gnulib configmake rather than open-coding things * bootstrap.conf (gnulib_modules): Add configmake. * daemon/Makefile.am (libvirtd_CFLAGS): Drop defines provided by gnulib. * src/Makefile.am (INCLUDES): Likewise. * tests/Makefile.am (INCLUDES): Likewise. * tools/Makefile.am (virsh_CFLAGS): Likewise. * daemon/libvirtd.c (qemudInitPaths, usage, main): Update clients. * src/cpu/cpu_map.c (CPUMAPFILE): Likewise. * src/driver.c (DEFAULT_DRIVER_DIR): Likewise. * src/internal.h (_): Likewise. * src/libvirt.c (virInitialize): Likewise. * src/lxc/lxc_conf.h (LXC_CONFIG_DIR, LXC_STATE_DIR, LXC_LOG_DIR): Likewise. * src/lxc/lxc_conf.c (lxcCapsInit, lxcLoadDriverConfig): Likewise. * src/network/bridge_driver.c (NETWORK_PID_DIR) (NETWORK_STATE_DIR, DNSMASQ_STATE_DIR, networkStartup): Likewise. * src/nwfilter/nwfilter_driver.c (nwfilterDriverStartup): Likewise. * src/qemu/qemu_conf.c (qemudLoadDriverConfig): Likewise. * src/qemu/qemu_driver.c (qemudStartup): Likewise. * src/remote/remote_driver.h (LIBVIRTD_PRIV_UNIX_SOCKET) (LIBVIRTD_PRIV_UNIX_SOCKET_RO, LIBVIRTD_CONFIGURATION_FILE) (LIBVIRT_PKI_DIR): Likewise. * src/secret/secret_driver.c (secretDriverStartup): Likewise. * src/security/security_apparmor.c (VIRT_AA_HELPER): Likewise. * src/security/virt-aa-helper.c (main): Likewise. * src/storage/storage_backend_disk.c (PARTHELPER): Likewise. * src/storage/storage_driver.c (storageDriverStartup): Likewise. * src/uml/uml_driver.c (TEMPDIR, umlStartup): Likewise. * src/util/hooks.c (LIBVIRT_HOOK_DIR): Likewise. * tools/virsh.c (main): Likewise. * docs/hooks.html.in: Likewise.
2010-11-16 14:54:17 +00:00
#include "configmake.h"
#define VIR_FROM_THIS VIR_FROM_STORAGE
static virStorageDriverStatePtr driverState;
static int storageDriverShutdown(void);
static void storageDriverLock(virStorageDriverStatePtr driver)
{
2009-01-15 19:56:05 +00:00
virMutexLock(&driver->lock);
}
static void storageDriverUnlock(virStorageDriverStatePtr driver)
{
2009-01-15 19:56:05 +00:00
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;
2009-01-15 19:56:05 +00:00
if (virMutexInit(&driverState->lock) < 0) {
VIR_FREE(driverState);
return -1;
}
storageDriverLock(driverState);
if (privileged) {
maint: use gnulib configmake rather than open-coding things * bootstrap.conf (gnulib_modules): Add configmake. * daemon/Makefile.am (libvirtd_CFLAGS): Drop defines provided by gnulib. * src/Makefile.am (INCLUDES): Likewise. * tests/Makefile.am (INCLUDES): Likewise. * tools/Makefile.am (virsh_CFLAGS): Likewise. * daemon/libvirtd.c (qemudInitPaths, usage, main): Update clients. * src/cpu/cpu_map.c (CPUMAPFILE): Likewise. * src/driver.c (DEFAULT_DRIVER_DIR): Likewise. * src/internal.h (_): Likewise. * src/libvirt.c (virInitialize): Likewise. * src/lxc/lxc_conf.h (LXC_CONFIG_DIR, LXC_STATE_DIR, LXC_LOG_DIR): Likewise. * src/lxc/lxc_conf.c (lxcCapsInit, lxcLoadDriverConfig): Likewise. * src/network/bridge_driver.c (NETWORK_PID_DIR) (NETWORK_STATE_DIR, DNSMASQ_STATE_DIR, networkStartup): Likewise. * src/nwfilter/nwfilter_driver.c (nwfilterDriverStartup): Likewise. * src/qemu/qemu_conf.c (qemudLoadDriverConfig): Likewise. * src/qemu/qemu_driver.c (qemudStartup): Likewise. * src/remote/remote_driver.h (LIBVIRTD_PRIV_UNIX_SOCKET) (LIBVIRTD_PRIV_UNIX_SOCKET_RO, LIBVIRTD_CONFIGURATION_FILE) (LIBVIRT_PKI_DIR): Likewise. * src/secret/secret_driver.c (secretDriverStartup): Likewise. * src/security/security_apparmor.c (VIRT_AA_HELPER): Likewise. * src/security/virt-aa-helper.c (main): Likewise. * src/storage/storage_backend_disk.c (PARTHELPER): Likewise. * src/storage/storage_driver.c (storageDriverStartup): Likewise. * src/uml/uml_driver.c (TEMPDIR, umlStartup): Likewise. * src/util/hooks.c (LIBVIRT_HOOK_DIR): Likewise. * tools/virsh.c (main): Likewise. * docs/hooks.html.in: Likewise.
2010-11-16 14:54:17 +00:00
if ((base = strdup (SYSCONFDIR "/libvirt")) == NULL)
goto out_of_memory;
} else {
uid_t uid = geteuid();
char *userdir = virGetUserDirectory(uid);
2009-01-22 19:41:48 +00:00
if (!userdir)
goto error;
2009-01-22 19:41:48 +00:00
if (virAsprintf(&base, "%s/.libvirt", userdir) == -1) {
VIR_FREE(userdir);
goto out_of_memory;
}
2009-01-22 19:41:48 +00:00
VIR_FREE(userdir);
}
/* Configuration paths are either ~/.libvirt/storage/... (session) or
* /etc/libvirt/storage/... (system).
*/
2008-12-23 13:03:29 +00:00
if (virAsprintf(&driverState->configDir,
"%s/storage", base) == -1)
goto out_of_memory;
2008-12-23 13:03:29 +00:00
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);
2009-01-15 19:56:05 +00:00
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;
2008-03-17 15:10:48 +00:00
if (backend->startPool &&
backend->startPool(conn, pool) < 0) {
virStoragePoolObjRemove(&driver->pools, pool);
pool = NULL;
goto cleanup;
}
2008-03-17 15:10:48 +00:00
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);
2008-12-22 16:30:57 +00:00
def = NULL;
goto cleanup;
}
2008-12-22 16:30:57 +00:00
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);
/*
* 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_NO_STORAGE_VOL,
"%s", _("no storage vol with matching path"));
cleanup:
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;
}