/*
* storage_driver.c: core driver for storage APIs
*
* Copyright (C) 2006-2014 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, see
* .
*
* Author: Daniel P. Berrange
*/
#include
#include
#include
#include
#include
#include
#include
#if HAVE_PWD_H
# include
#endif
#include
#include
#include "virerror.h"
#include "datatypes.h"
#include "driver.h"
#include "storage_driver.h"
#include "storage_conf.h"
#include "viralloc.h"
#include "storage_backend.h"
#include "virlog.h"
#include "virfile.h"
#include "fdstream.h"
#include "configmake.h"
#include "virstring.h"
#include "viraccessapicheck.h"
#include "dirname.h"
#define VIR_FROM_THIS VIR_FROM_STORAGE
VIR_LOG_INIT("storage.storage_driver");
static virStorageDriverStatePtr driverState;
static int storageStateCleanup(void);
static void storageDriverLock(virStorageDriverStatePtr driver)
{
virMutexLock(&driver->lock);
}
static void storageDriverUnlock(virStorageDriverStatePtr driver)
{
virMutexUnlock(&driver->lock);
}
static void
storageDriverAutostart(virStorageDriverStatePtr driver)
{
size_t i;
virConnectPtr conn = NULL;
/* XXX Remove hardcoding of QEMU URI */
if (driverState->privileged)
conn = virConnectOpen("qemu:///system");
else
conn = virConnectOpen("qemu:///session");
/* Ignoring NULL conn - let backends decide */
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(conn, 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(conn, 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(conn, pool) < 0) {
virErrorPtr err = virGetLastError();
if (backend->stopPool)
backend->stopPool(conn, 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);
}
virObjectUnref(conn);
}
/**
* virStorageStartup:
*
* Initialization function for the QEmu daemon
*/
static int
storageStateInitialize(bool privileged,
virStateInhibitCallback callback ATTRIBUTE_UNUSED,
void *opaque ATTRIBUTE_UNUSED)
{
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 (VIR_STRDUP(base, SYSCONFDIR "/libvirt") < 0)
goto error;
} else {
base = virGetUserConfigDirectory();
if (!base)
goto error;
}
driverState->privileged = privileged;
/* Configuration paths are either $USER_CONFIG_HOME/libvirt/storage/... (session) or
* /etc/libvirt/storage/... (system).
*/
if (virAsprintf(&driverState->configDir,
"%s/storage", base) == -1)
goto error;
if (virAsprintf(&driverState->autostartDir,
"%s/storage/autostart", base) == -1)
goto error;
VIR_FREE(base);
if (virStoragePoolLoadAllConfigs(&driverState->pools,
driverState->configDir,
driverState->autostartDir) < 0)
goto error;
storageDriverUnlock(driverState);
return 0;
error:
VIR_FREE(base);
storageDriverUnlock(driverState);
storageStateCleanup();
return -1;
}
/**
* storageStateAutoStart:
*
* Function to auto start the storage driver
*/
static void
storageStateAutoStart(void)
{
if (!driverState)
return;
storageDriverLock(driverState);
storageDriverAutostart(driverState);
storageDriverUnlock(driverState);
}
/**
* storageStateReload:
*
* Function to restart the storage driver, it will recheck the configuration
* files and update its state
*/
static int
storageStateReload(void)
{
if (!driverState)
return -1;
storageDriverLock(driverState);
virStoragePoolLoadAllConfigs(&driverState->pools,
driverState->configDir,
driverState->autostartDir);
storageDriverAutostart(driverState);
storageDriverUnlock(driverState);
return 0;
}
/**
* storageStateCleanup
*
* Shutdown the storage driver, it will stop all active storage pools
*/
static int
storageStateCleanup(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) {
char uuidstr[VIR_UUID_STRING_BUFLEN];
virUUIDFormat(uuid, uuidstr);
virReportError(VIR_ERR_NO_STORAGE_POOL,
_("no storage pool with matching uuid '%s'"), uuidstr);
return NULL;
}
if (virStoragePoolLookupByUUIDEnsureACL(conn, pool->def) < 0)
goto cleanup;
ret = virGetStoragePool(conn, pool->def->name, pool->def->uuid,
NULL, NULL);
cleanup:
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) {
virReportError(VIR_ERR_NO_STORAGE_POOL,
_("no storage pool with matching name '%s'"), name);
return NULL;
}
if (virStoragePoolLookupByNameEnsureACL(conn, pool->def) < 0)
goto cleanup;
ret = virGetStoragePool(conn, pool->def->name, pool->def->uuid,
NULL, NULL);
cleanup:
virStoragePoolObjUnlock(pool);
return ret;
}
static virStoragePoolPtr
storagePoolLookupByVolume(virStorageVolPtr vol)
{
virStorageDriverStatePtr driver = vol->conn->storagePrivateData;
virStoragePoolObjPtr pool;
virStoragePoolPtr ret = NULL;
storageDriverLock(driver);
pool = virStoragePoolObjFindByName(&driver->pools, vol->pool);
storageDriverUnlock(driver);
if (!pool) {
virReportError(VIR_ERR_NO_STORAGE_POOL,
_("no storage pool with matching name '%s'"), vol->pool);
return NULL;
}
if (virStoragePoolLookupByVolumeEnsureACL(vol->conn, pool->def) < 0)
goto cleanup;
ret = virGetStoragePool(vol->conn, pool->def->name, pool->def->uuid,
NULL, NULL);
cleanup:
virStoragePoolObjUnlock(pool);
return ret;
}
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
storageConnectNumOfStoragePools(virConnectPtr conn)
{
virStorageDriverStatePtr driver = conn->storagePrivateData;
size_t i;
int nactive = 0;
if (virConnectNumOfStoragePoolsEnsureACL(conn) < 0)
return -1;
storageDriverLock(driver);
for (i = 0; i < driver->pools.count; i++) {
virStoragePoolObjPtr obj = driver->pools.objs[i];
virStoragePoolObjLock(obj);
if (virConnectNumOfStoragePoolsCheckACL(conn, obj->def) &&
virStoragePoolObjIsActive(obj))
nactive++;
virStoragePoolObjUnlock(obj);
}
storageDriverUnlock(driver);
return nactive;
}
static int
storageConnectListStoragePools(virConnectPtr conn,
char **const names,
int nnames)
{
virStorageDriverStatePtr driver = conn->storagePrivateData;
int got = 0;
size_t i;
if (virConnectListStoragePoolsEnsureACL(conn) < 0)
return -1;
storageDriverLock(driver);
for (i = 0; i < driver->pools.count && got < nnames; i++) {
virStoragePoolObjPtr obj = driver->pools.objs[i];
virStoragePoolObjLock(obj);
if (virConnectListStoragePoolsCheckACL(conn, obj->def) &&
virStoragePoolObjIsActive(obj)) {
if (VIR_STRDUP(names[got], obj->def->name) < 0) {
virStoragePoolObjUnlock(obj);
goto cleanup;
}
got++;
}
virStoragePoolObjUnlock(obj);
}
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
storageConnectNumOfDefinedStoragePools(virConnectPtr conn)
{
virStorageDriverStatePtr driver = conn->storagePrivateData;
size_t i;
int nactive = 0;
if (virConnectNumOfDefinedStoragePoolsEnsureACL(conn) < 0)
return -1;
storageDriverLock(driver);
for (i = 0; i < driver->pools.count; i++) {
virStoragePoolObjPtr obj = driver->pools.objs[i];
virStoragePoolObjLock(obj);
if (virConnectNumOfDefinedStoragePoolsCheckACL(conn, obj->def) &&
!virStoragePoolObjIsActive(obj))
nactive++;
virStoragePoolObjUnlock(obj);
}
storageDriverUnlock(driver);
return nactive;
}
static int
storageConnectListDefinedStoragePools(virConnectPtr conn,
char **const names,
int nnames)
{
virStorageDriverStatePtr driver = conn->storagePrivateData;
int got = 0;
size_t i;
if (virConnectListDefinedStoragePoolsEnsureACL(conn) < 0)
return -1;
storageDriverLock(driver);
for (i = 0; i < driver->pools.count && got < nnames; i++) {
virStoragePoolObjPtr obj = driver->pools.objs[i];
virStoragePoolObjLock(obj);
if (virConnectListDefinedStoragePoolsCheckACL(conn, obj->def) &&
!virStoragePoolObjIsActive(obj)) {
if (VIR_STRDUP(names[got], obj->def->name) < 0) {
virStoragePoolObjUnlock(obj);
goto cleanup;
}
got++;
}
virStoragePoolObjUnlock(obj);
}
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 *
storageConnectFindStoragePoolSources(virConnectPtr conn,
const char *type,
const char *srcSpec,
unsigned int flags)
{
int backend_type;
virStorageBackendPtr backend;
char *ret = NULL;
if (virConnectFindStoragePoolSourcesEnsureACL(conn) < 0)
return NULL;
backend_type = virStoragePoolTypeFromString(type);
if (backend_type < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("unknown storage pool type %s"), type);
goto cleanup;
}
backend = virStorageBackendForType(backend_type);
if (backend == NULL)
goto cleanup;
if (!backend->findPoolSources) {
virReportError(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 virStoragePoolObjPtr
virStoragePoolObjFromStoragePool(virStoragePoolPtr pool)
{
virStorageDriverStatePtr driver = pool->conn->storagePrivateData;
char uuidstr[VIR_UUID_STRING_BUFLEN];
virStoragePoolObjPtr ret;
storageDriverLock(driver);
if (!(ret = virStoragePoolObjFindByUUID(&driver->pools, pool->uuid))) {
virUUIDFormat(pool->uuid, uuidstr);
virReportError(VIR_ERR_NO_STORAGE_POOL,
_("no storage pool with matching uuid '%s' (%s)"),
uuidstr, pool->name);
}
storageDriverUnlock(driver);
return ret;
}
static int storagePoolIsActive(virStoragePoolPtr pool)
{
virStoragePoolObjPtr obj;
int ret = -1;
if (!(obj = virStoragePoolObjFromStoragePool(pool)))
return -1;
if (virStoragePoolIsActiveEnsureACL(pool->conn, obj->def) < 0)
goto cleanup;
ret = virStoragePoolObjIsActive(obj);
cleanup:
virStoragePoolObjUnlock(obj);
return ret;
}
static int storagePoolIsPersistent(virStoragePoolPtr pool)
{
virStoragePoolObjPtr obj;
int ret = -1;
if (!(obj = virStoragePoolObjFromStoragePool(pool)))
return -1;
if (virStoragePoolIsPersistentEnsureACL(pool->conn, obj->def) < 0)
goto cleanup;
ret = obj->configFile ? 1 : 0;
cleanup:
virStoragePoolObjUnlock(obj);
return ret;
}
static virStoragePoolPtr
storagePoolCreateXML(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 (virStoragePoolCreateXMLEnsureACL(conn, def) < 0)
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,
NULL, NULL);
cleanup:
virStoragePoolDefFree(def);
if (pool)
virStoragePoolObjUnlock(pool);
storageDriverUnlock(driver);
return ret;
}
static virStoragePoolPtr
storagePoolDefineXML(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 (virStoragePoolDefineXMLEnsureACL(conn, def) < 0)
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,
NULL, NULL);
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);
if (!(pool = virStoragePoolObjFindByUUID(&driver->pools, obj->uuid))) {
char uuidstr[VIR_UUID_STRING_BUFLEN];
virUUIDFormat(obj->uuid, uuidstr);
virReportError(VIR_ERR_NO_STORAGE_POOL,
_("no storage pool with matching uuid '%s' (%s)"),
uuidstr, obj->name);
goto cleanup;
}
if (virStoragePoolUndefineEnsureACL(obj->conn, pool->def) < 0)
goto cleanup;
if (virStoragePoolObjIsActive(pool)) {
virReportError(VIR_ERR_OPERATION_INVALID,
_("storage pool '%s' is still active"),
pool->def->name);
goto cleanup;
}
if (pool->asyncjobs > 0) {
virReportError(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
storagePoolCreate(virStoragePoolPtr obj,
unsigned int flags)
{
virStoragePoolObjPtr pool;
virStorageBackendPtr backend;
int ret = -1;
virCheckFlags(0, -1);
if (!(pool = virStoragePoolObjFromStoragePool(obj)))
return -1;
if (virStoragePoolCreateEnsureACL(obj->conn, pool->def) < 0)
goto cleanup;
if ((backend = virStorageBackendForType(pool->def->type)) == NULL)
goto cleanup;
if (virStoragePoolObjIsActive(pool)) {
virReportError(VIR_ERR_OPERATION_INVALID,
_("storage pool '%s' is already active"),
pool->def->name);
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:
virStoragePoolObjUnlock(pool);
return ret;
}
static int
storagePoolBuild(virStoragePoolPtr obj,
unsigned int flags)
{
virStoragePoolObjPtr pool;
virStorageBackendPtr backend;
int ret = -1;
if (!(pool = virStoragePoolObjFromStoragePool(obj)))
return -1;
if (virStoragePoolBuildEnsureACL(obj->conn, pool->def) < 0)
goto cleanup;
if ((backend = virStorageBackendForType(pool->def->type)) == NULL)
goto cleanup;
if (virStoragePoolObjIsActive(pool)) {
virReportError(VIR_ERR_OPERATION_INVALID,
_("storage pool '%s' is already active"),
pool->def->name);
goto cleanup;
}
if (backend->buildPool &&
backend->buildPool(obj->conn, pool, flags) < 0)
goto cleanup;
ret = 0;
cleanup:
virStoragePoolObjUnlock(pool);
return ret;
}
static int
storagePoolDestroy(virStoragePoolPtr obj)
{
virStorageDriverStatePtr driver = obj->conn->storagePrivateData;
virStoragePoolObjPtr pool;
virStorageBackendPtr backend;
int ret = -1;
storageDriverLock(driver);
if (!(pool = virStoragePoolObjFindByUUID(&driver->pools, obj->uuid))) {
char uuidstr[VIR_UUID_STRING_BUFLEN];
virUUIDFormat(obj->uuid, uuidstr);
virReportError(VIR_ERR_NO_STORAGE_POOL,
_("no storage pool with matching uuid '%s' (%s)"),
uuidstr, obj->name);
goto cleanup;
}
if (virStoragePoolDestroyEnsureACL(obj->conn, pool->def) < 0)
goto cleanup;
if ((backend = virStorageBackendForType(pool->def->type)) == NULL)
goto cleanup;
if (!virStoragePoolObjIsActive(pool)) {
virReportError(VIR_ERR_OPERATION_INVALID,
_("storage pool '%s' is not active"), pool->def->name);
goto cleanup;
}
if (pool->asyncjobs > 0) {
virReportError(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;
} else if (pool->newDef) {
virStoragePoolDefFree(pool->def);
pool->def = pool->newDef;
pool->newDef = NULL;
}
ret = 0;
cleanup:
if (pool)
virStoragePoolObjUnlock(pool);
storageDriverUnlock(driver);
return ret;
}
static int
storagePoolDelete(virStoragePoolPtr obj,
unsigned int flags)
{
virStoragePoolObjPtr pool;
virStorageBackendPtr backend;
int ret = -1;
if (!(pool = virStoragePoolObjFromStoragePool(obj)))
return -1;
if (virStoragePoolDeleteEnsureACL(obj->conn, pool->def) < 0)
goto cleanup;
if ((backend = virStorageBackendForType(pool->def->type)) == NULL)
goto cleanup;
if (virStoragePoolObjIsActive(pool)) {
virReportError(VIR_ERR_OPERATION_INVALID,
_("storage pool '%s' is still active"),
pool->def->name);
goto cleanup;
}
if (pool->asyncjobs > 0) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("pool '%s' has asynchronous jobs running."),
pool->def->name);
goto cleanup;
}
if (!backend->deletePool) {
virReportError(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:
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);
if (!(pool = virStoragePoolObjFindByUUID(&driver->pools, obj->uuid))) {
char uuidstr[VIR_UUID_STRING_BUFLEN];
virUUIDFormat(obj->uuid, uuidstr);
virReportError(VIR_ERR_NO_STORAGE_POOL,
_("no storage pool with matching uuid '%s' (%s)"),
uuidstr, obj->name);
goto cleanup;
}
if (virStoragePoolRefreshEnsureACL(obj->conn, pool->def) < 0)
goto cleanup;
if ((backend = virStorageBackendForType(pool->def->type)) == NULL)
goto cleanup;
if (!virStoragePoolObjIsActive(pool)) {
virReportError(VIR_ERR_OPERATION_INVALID,
_("storage pool '%s' is not active"), pool->def->name);
goto cleanup;
}
if (pool->asyncjobs > 0) {
virReportError(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)
{
virStoragePoolObjPtr pool;
int ret = -1;
if (!(pool = virStoragePoolObjFromStoragePool(obj)))
return -1;
if (virStoragePoolGetInfoEnsureACL(obj->conn, pool->def) < 0)
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:
virStoragePoolObjUnlock(pool);
return ret;
}
static char *
storagePoolGetXMLDesc(virStoragePoolPtr obj,
unsigned int flags)
{
virStoragePoolObjPtr pool;
virStoragePoolDefPtr def;
char *ret = NULL;
virCheckFlags(VIR_STORAGE_XML_INACTIVE, NULL);
if (!(pool = virStoragePoolObjFromStoragePool(obj)))
return NULL;
if (virStoragePoolGetXMLDescEnsureACL(obj->conn, pool->def) < 0)
goto cleanup;
if ((flags & VIR_STORAGE_XML_INACTIVE) && pool->newDef)
def = pool->newDef;
else
def = pool->def;
ret = virStoragePoolDefFormat(def);
cleanup:
virStoragePoolObjUnlock(pool);
return ret;
}
static int
storagePoolGetAutostart(virStoragePoolPtr obj,
int *autostart)
{
virStoragePoolObjPtr pool;
int ret = -1;
if (!(pool = virStoragePoolObjFromStoragePool(obj)))
return -1;
if (virStoragePoolGetAutostartEnsureACL(obj->conn, pool->def) < 0)
goto cleanup;
if (!pool->configFile) {
*autostart = 0;
} else {
*autostart = pool->autostart;
}
ret = 0;
cleanup:
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) {
char uuidstr[VIR_UUID_STRING_BUFLEN];
virUUIDFormat(obj->uuid, uuidstr);
virReportError(VIR_ERR_NO_STORAGE_POOL,
_("no storage pool with matching uuid '%s' (%s)"),
uuidstr, obj->name);
goto cleanup;
}
if (virStoragePoolSetAutostartEnsureACL(obj->conn, pool->def) < 0)
goto cleanup;
if (!pool->configFile) {
virReportError(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
storagePoolNumOfVolumes(virStoragePoolPtr obj)
{
virStoragePoolObjPtr pool;
int ret = -1;
size_t i;
if (!(pool = virStoragePoolObjFromStoragePool(obj)))
return -1;
if (virStoragePoolNumOfVolumesEnsureACL(obj->conn, pool->def) < 0)
goto cleanup;
if (!virStoragePoolObjIsActive(pool)) {
virReportError(VIR_ERR_OPERATION_INVALID,
_("storage pool '%s' is not active"), pool->def->name);
goto cleanup;
}
ret = 0;
for (i = 0; i < pool->volumes.count; i++) {
if (virStoragePoolNumOfVolumesCheckACL(obj->conn, pool->def,
pool->volumes.objs[i]))
ret++;
}
cleanup:
virStoragePoolObjUnlock(pool);
return ret;
}
static int
storagePoolListVolumes(virStoragePoolPtr obj,
char **const names,
int maxnames)
{
virStoragePoolObjPtr pool;
size_t i;
int n = 0;
memset(names, 0, maxnames * sizeof(*names));
if (!(pool = virStoragePoolObjFromStoragePool(obj)))
return -1;
if (virStoragePoolListVolumesEnsureACL(obj->conn, pool->def) < 0)
goto cleanup;
if (!virStoragePoolObjIsActive(pool)) {
virReportError(VIR_ERR_OPERATION_INVALID,
_("storage pool '%s' is not active"), pool->def->name);
goto cleanup;
}
for (i = 0; i < pool->volumes.count && n < maxnames; i++) {
if (!virStoragePoolListVolumesCheckACL(obj->conn, pool->def,
pool->volumes.objs[i]))
continue;
if (VIR_STRDUP(names[n++], pool->volumes.objs[i]->name) < 0)
goto cleanup;
}
virStoragePoolObjUnlock(pool);
return n;
cleanup:
virStoragePoolObjUnlock(pool);
for (n = 0; n < maxnames; n++)
VIR_FREE(names[n]);
memset(names, 0, maxnames * sizeof(*names));
return -1;
}
static int
storagePoolListAllVolumes(virStoragePoolPtr pool,
virStorageVolPtr **vols,
unsigned int flags)
{
virStoragePoolObjPtr obj;
size_t i;
virStorageVolPtr *tmp_vols = NULL;
virStorageVolPtr vol = NULL;
int nvols = 0;
int ret = -1;
virCheckFlags(0, -1);
if (!(obj = virStoragePoolObjFromStoragePool(pool)))
return -1;
if (virStoragePoolListAllVolumesEnsureACL(pool->conn, obj->def) < 0)
goto cleanup;
if (!virStoragePoolObjIsActive(obj)) {
virReportError(VIR_ERR_OPERATION_INVALID,
_("storage pool '%s' is not active"), obj->def->name);
goto cleanup;
}
/* Just returns the volumes count */
if (!vols) {
ret = obj->volumes.count;
goto cleanup;
}
if (VIR_ALLOC_N(tmp_vols, obj->volumes.count + 1) < 0)
goto cleanup;
for (i = 0; i < obj->volumes.count; i++) {
if (!virStoragePoolListAllVolumesCheckACL(pool->conn, obj->def,
obj->volumes.objs[i]))
continue;
if (!(vol = virGetStorageVol(pool->conn, obj->def->name,
obj->volumes.objs[i]->name,
obj->volumes.objs[i]->key,
NULL, NULL)))
goto cleanup;
tmp_vols[nvols++] = vol;
}
*vols = tmp_vols;
tmp_vols = NULL;
ret = nvols;
cleanup:
if (tmp_vols) {
for (i = 0; i < nvols; i++) {
if (tmp_vols[i])
virStorageVolFree(tmp_vols[i]);
}
VIR_FREE(tmp_vols);
}
virStoragePoolObjUnlock(obj);
return ret;
}
static virStorageVolPtr
storageVolLookupByName(virStoragePoolPtr obj,
const char *name)
{
virStoragePoolObjPtr pool;
virStorageVolDefPtr vol;
virStorageVolPtr ret = NULL;
if (!(pool = virStoragePoolObjFromStoragePool(obj)))
return NULL;
if (!virStoragePoolObjIsActive(pool)) {
virReportError(VIR_ERR_OPERATION_INVALID,
_("storage pool '%s' is not active"), pool->def->name);
goto cleanup;
}
vol = virStorageVolDefFindByName(pool, name);
if (!vol) {
virReportError(VIR_ERR_NO_STORAGE_VOL,
_("no storage vol with matching name '%s'"),
name);
goto cleanup;
}
if (virStorageVolLookupByNameEnsureACL(obj->conn, pool->def, vol) < 0)
goto cleanup;
ret = virGetStorageVol(obj->conn, pool->def->name, vol->name, vol->key,
NULL, NULL);
cleanup:
virStoragePoolObjUnlock(pool);
return ret;
}
static virStorageVolPtr
storageVolLookupByKey(virConnectPtr conn,
const char *key)
{
virStorageDriverStatePtr driver = conn->storagePrivateData;
size_t 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) {
if (virStorageVolLookupByKeyEnsureACL(conn, driver->pools.objs[i]->def, vol) < 0) {
virStoragePoolObjUnlock(driver->pools.objs[i]);
goto cleanup;
}
ret = virGetStorageVol(conn,
driver->pools.objs[i]->def->name,
vol->name,
vol->key,
NULL, NULL);
}
}
virStoragePoolObjUnlock(driver->pools.objs[i]);
}
if (!ret)
virReportError(VIR_ERR_NO_STORAGE_VOL,
_("no storage vol with matching key %s"), key);
cleanup:
storageDriverUnlock(driver);
return ret;
}
static virStorageVolPtr
storageVolLookupByPath(virConnectPtr conn,
const char *path)
{
virStorageDriverStatePtr driver = conn->storagePrivateData;
size_t i;
virStorageVolPtr ret = NULL;
char *cleanpath;
cleanpath = virFileSanitizePath(path);
if (!cleanpath)
return NULL;
storageDriverLock(driver);
for (i = 0; i < driver->pools.count && !ret; i++) {
virStoragePoolObjPtr pool = driver->pools.objs[i];
virStorageVolDefPtr vol;
char *stable_path = NULL;
virStoragePoolObjLock(pool);
if (!virStoragePoolObjIsActive(pool)) {
virStoragePoolObjUnlock(pool);
continue;
}
switch ((virStoragePoolType) pool->def->type) {
case VIR_STORAGE_POOL_DIR:
case VIR_STORAGE_POOL_FS:
case VIR_STORAGE_POOL_NETFS:
case VIR_STORAGE_POOL_LOGICAL:
case VIR_STORAGE_POOL_DISK:
case VIR_STORAGE_POOL_ISCSI:
case VIR_STORAGE_POOL_SCSI:
case VIR_STORAGE_POOL_MPATH:
stable_path = virStorageBackendStablePath(pool,
cleanpath,
false);
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'",
pool->def->name);
virStoragePoolObjUnlock(pool);
continue;
}
break;
case VIR_STORAGE_POOL_GLUSTER:
case VIR_STORAGE_POOL_RBD:
case VIR_STORAGE_POOL_SHEEPDOG:
case VIR_STORAGE_POOL_LAST:
if (VIR_STRDUP(stable_path, path) < 0) {
virStoragePoolObjUnlock(pool);
goto cleanup;
}
break;
}
vol = virStorageVolDefFindByPath(pool, stable_path);
VIR_FREE(stable_path);
if (vol) {
if (virStorageVolLookupByPathEnsureACL(conn, pool->def, vol) < 0) {
virStoragePoolObjUnlock(pool);
goto cleanup;
}
ret = virGetStorageVol(conn, pool->def->name,
vol->name, vol->key,
NULL, NULL);
}
virStoragePoolObjUnlock(pool);
}
if (!ret) {
if (STREQ(path, cleanpath)) {
virReportError(VIR_ERR_NO_STORAGE_VOL,
_("no storage vol with matching path '%s'"), path);
} else {
virReportError(VIR_ERR_NO_STORAGE_VOL,
_("no storage vol with matching path '%s' (%s)"),
path, cleanpath);
}
}
cleanup:
VIR_FREE(cleanpath);
storageDriverUnlock(driver);
return ret;
}
static int
storageVolDeleteInternal(virStorageVolPtr obj,
virStorageBackendPtr backend,
virStoragePoolObjPtr pool,
virStorageVolDefPtr vol,
unsigned int flags,
bool updateMeta)
{
size_t i;
int ret = -1;
if (!backend->deleteVol) {
virReportError(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;
/* Update pool metadata - don't update meta data from error paths
* in this module since the allocation/available weren't adjusted yet
*/
if (updateMeta) {
pool->def->allocation -= vol->target.allocation;
pool->def->available += vol->target.allocation;
}
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);
VIR_DELETE_ELEMENT(pool->volumes.objs, i, pool->volumes.count);
break;
}
}
ret = 0;
cleanup:
return ret;
}
static virStorageVolDefPtr
virStorageVolDefFromVol(virStorageVolPtr obj,
virStoragePoolObjPtr *pool,
virStorageBackendPtr *backend)
{
virStorageDriverStatePtr driver = obj->conn->storagePrivateData;
virStorageVolDefPtr vol = NULL;
*pool = NULL;
storageDriverLock(driver);
*pool = virStoragePoolObjFindByName(&driver->pools, obj->pool);
storageDriverUnlock(driver);
if (!*pool) {
virReportError(VIR_ERR_NO_STORAGE_POOL,
_("no storage pool with matching name '%s'"),
obj->pool);
return NULL;
}
if (!virStoragePoolObjIsActive(*pool)) {
virReportError(VIR_ERR_OPERATION_INVALID,
_("storage pool '%s' is not active"),
(*pool)->def->name);
goto error;
}
if (!(vol = virStorageVolDefFindByName(*pool, obj->name))) {
virReportError(VIR_ERR_NO_STORAGE_VOL,
_("no storage vol with matching name '%s'"),
obj->name);
goto error;
}
if (backend) {
if (!(*backend = virStorageBackendForType((*pool)->def->type)))
goto error;
}
return vol;
error:
virStoragePoolObjUnlock(*pool);
*pool = NULL;
return NULL;
}
static int
storageVolDelete(virStorageVolPtr obj,
unsigned int flags)
{
virStoragePoolObjPtr pool;
virStorageBackendPtr backend;
virStorageVolDefPtr vol = NULL;
int ret = -1;
if (!(vol = virStorageVolDefFromVol(obj, &pool, &backend)))
return -1;
if (virStorageVolDeleteEnsureACL(obj->conn, pool->def, vol) < 0)
goto cleanup;
if (vol->in_use) {
virReportError(VIR_ERR_OPERATION_INVALID,
_("volume '%s' is still in use."),
vol->name);
goto cleanup;
}
if (vol->building) {
virReportError(VIR_ERR_OPERATION_INVALID,
_("volume '%s' is still being allocated."),
vol->name);
goto cleanup;
}
if (storageVolDeleteInternal(obj, backend, pool, vol, flags, true) < 0)
goto cleanup;
ret = 0;
cleanup:
virStoragePoolObjUnlock(pool);
return ret;
}
static virStorageVolPtr
storageVolCreateXML(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;
virStorageVolDefPtr buildvoldef = NULL;
virCheckFlags(VIR_STORAGE_VOL_CREATE_PREALLOC_METADATA, NULL);
if (!(pool = virStoragePoolObjFromStoragePool(obj)))
return NULL;
if (!virStoragePoolObjIsActive(pool)) {
virReportError(VIR_ERR_OPERATION_INVALID,
_("storage pool '%s' is not active"), pool->def->name);
goto cleanup;
}
if ((backend = virStorageBackendForType(pool->def->type)) == NULL)
goto cleanup;
voldef = virStorageVolDefParseString(pool->def, xmldesc);
if (voldef == NULL)
goto cleanup;
if (virStorageVolCreateXMLEnsureACL(obj->conn, pool->def, voldef) < 0)
goto cleanup;
if (virStorageVolDefFindByName(pool, voldef->name)) {
virReportError(VIR_ERR_STORAGE_VOL_EXIST,
_("'%s'"), voldef->name);
goto cleanup;
}
if (VIR_REALLOC_N(pool->volumes.objs,
pool->volumes.count+1) < 0)
goto cleanup;
if (!backend->createVol) {
virReportError(VIR_ERR_NO_SUPPORT,
"%s", _("storage pool does not support volume "
"creation"));
goto cleanup;
}
/* Wipe any key the user may have suggested, as volume creation
* will generate the canonical key. */
VIR_FREE(voldef->key);
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, NULL, NULL);
if (!volobj) {
pool->volumes.count--;
goto cleanup;
}
if (VIR_ALLOC(buildvoldef) < 0) {
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));
if (backend->buildVol) {
int buildret;
/* Drop the pool lock during volume allocation */
pool->asyncjobs++;
voldef->building = 1;
virStoragePoolObjUnlock(pool);
buildret = backend->buildVol(obj->conn, pool, buildvoldef, flags);
storageDriverLock(driver);
virStoragePoolObjLock(pool);
storageDriverUnlock(driver);
voldef->building = 0;
pool->asyncjobs--;
if (buildret < 0) {
VIR_FREE(buildvoldef);
storageVolDeleteInternal(volobj, backend, pool, voldef,
0, false);
voldef = NULL;
goto cleanup;
}
}
/* Update pool metadata */
pool->def->allocation += buildvoldef->target.allocation;
pool->def->available -= buildvoldef->target.allocation;
VIR_INFO("Creating volume '%s' in storage pool '%s'",
volobj->name, pool->def->name);
ret = volobj;
volobj = NULL;
voldef = NULL;
cleanup:
virObjectUnref(volobj);
virStorageVolDefFree(voldef);
VIR_FREE(buildvoldef);
if (pool)
virStoragePoolObjUnlock(pool);
return ret;
}
static virStorageVolPtr
storageVolCreateXMLFrom(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;
unsigned long long allocation;
int buildret;
virCheckFlags(VIR_STORAGE_VOL_CREATE_PREALLOC_METADATA, 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) {
char uuidstr[VIR_UUID_STRING_BUFLEN];
virUUIDFormat(obj->uuid, uuidstr);
virReportError(VIR_ERR_NO_STORAGE_POOL,
_("no storage pool with matching uuid '%s' (%s)"),
uuidstr, obj->name);
goto cleanup;
}
if (STRNEQ(obj->name, vobj->pool) && !origpool) {
virReportError(VIR_ERR_NO_STORAGE_POOL,
_("no storage pool with matching name '%s'"),
vobj->pool);
goto cleanup;
}
if (!virStoragePoolObjIsActive(pool)) {
virReportError(VIR_ERR_OPERATION_INVALID,
_("storage pool '%s' is not active"), pool->def->name);
goto cleanup;
}
if (origpool && !virStoragePoolObjIsActive(origpool)) {
virReportError(VIR_ERR_OPERATION_INVALID,
_("storage pool '%s' is not active"),
origpool->def->name);
goto cleanup;
}
if ((backend = virStorageBackendForType(pool->def->type)) == NULL)
goto cleanup;
origvol = virStorageVolDefFindByName(origpool ? origpool : pool, vobj->name);
if (!origvol) {
virReportError(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 (virStorageVolCreateXMLFromEnsureACL(obj->conn, pool->def, newvol) < 0)
goto cleanup;
if (virStorageVolDefFindByName(pool, newvol->name)) {
virReportError(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->target.capacity < origvol->target.capacity)
newvol->target.capacity = origvol->target.capacity;
/* Make sure allocation is at least as large as the destination cap,
* to make absolutely sure we copy all possible contents */
if (newvol->target.allocation < origvol->target.capacity)
newvol->target.allocation = origvol->target.capacity;
if (!backend->buildVolFrom) {
virReportError(VIR_ERR_NO_SUPPORT,
"%s", _("storage pool does not support volume creation from an existing volume"));
goto cleanup;
}
if (origvol->building) {
virReportError(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)
goto cleanup;
/* 'Define' the new volume so we get async progress reporting.
* Wipe any key the user may have suggested, as volume creation
* will generate the canonical key. */
VIR_FREE(newvol->key);
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, NULL, NULL);
if (!volobj) {
pool->volumes.count--;
goto cleanup;
}
/* Drop the pool lock during volume allocation */
pool->asyncjobs++;
newvol->building = 1;
origvol->in_use++;
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->in_use--;
newvol->building = 0;
allocation = newvol->target.allocation;
pool->asyncjobs--;
if (origpool) {
origpool->asyncjobs--;
virStoragePoolObjUnlock(origpool);
origpool = NULL;
}
if (buildret < 0) {
storageVolDeleteInternal(volobj, backend, pool, newvol, 0, false);
newvol = NULL;
goto cleanup;
}
newvol = NULL;
/* Updating pool metadata */
pool->def->allocation += allocation;
pool->def->available -= allocation;
VIR_INFO("Creating volume '%s' in storage pool '%s'",
volobj->name, pool->def->name);
ret = volobj;
volobj = NULL;
cleanup:
virObjectUnref(volobj);
virStorageVolDefFree(newvol);
if (pool)
virStoragePoolObjUnlock(pool);
if (origpool)
virStoragePoolObjUnlock(origpool);
return ret;
}
static int
storageVolDownload(virStorageVolPtr obj,
virStreamPtr stream,
unsigned long long offset,
unsigned long long length,
unsigned int flags)
{
virStoragePoolObjPtr pool = NULL;
virStorageVolDefPtr vol = NULL;
int ret = -1;
virCheckFlags(0, -1);
if (!(vol = virStorageVolDefFromVol(obj, &pool, NULL)))
return -1;
if (virStorageVolDownloadEnsureACL(obj->conn, pool->def, vol) < 0)
goto cleanup;
if (vol->building) {
virReportError(VIR_ERR_OPERATION_INVALID,
_("volume '%s' is still being allocated."),
vol->name);
goto cleanup;
}
if (virFDStreamOpenFile(stream,
vol->target.path,
offset, length,
O_RDONLY) < 0)
goto cleanup;
ret = 0;
cleanup:
virStoragePoolObjUnlock(pool);
return ret;
}
static int
storageVolUpload(virStorageVolPtr obj,
virStreamPtr stream,
unsigned long long offset,
unsigned long long length,
unsigned int flags)
{
virStoragePoolObjPtr pool = NULL;
virStorageVolDefPtr vol = NULL;
int ret = -1;
virCheckFlags(0, -1);
if (!(vol = virStorageVolDefFromVol(obj, &pool, NULL)))
return -1;
if (virStorageVolUploadEnsureACL(obj->conn, pool->def, vol) < 0)
goto cleanup;
if (vol->in_use) {
virReportError(VIR_ERR_OPERATION_INVALID,
_("volume '%s' is still in use."),
vol->name);
goto cleanup;
}
if (vol->building) {
virReportError(VIR_ERR_OPERATION_INVALID,
_("volume '%s' is still being allocated."),
vol->name);
goto cleanup;
}
switch ((virStoragePoolType) pool->def->type) {
case VIR_STORAGE_POOL_DIR:
case VIR_STORAGE_POOL_FS:
case VIR_STORAGE_POOL_NETFS:
case VIR_STORAGE_POOL_LOGICAL:
case VIR_STORAGE_POOL_DISK:
case VIR_STORAGE_POOL_ISCSI:
case VIR_STORAGE_POOL_SCSI:
case VIR_STORAGE_POOL_MPATH:
/* 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 cleanup;
break;
case VIR_STORAGE_POOL_SHEEPDOG:
case VIR_STORAGE_POOL_RBD:
case VIR_STORAGE_POOL_GLUSTER:
case VIR_STORAGE_POOL_LAST:
virReportError(VIR_ERR_OPERATION_UNSUPPORTED,
_("volume upload is not supported with pools of type %s"),
virStoragePoolTypeToString(pool->def->type));
goto cleanup;
}
ret = 0;
cleanup:
virStoragePoolObjUnlock(pool);
return ret;
}
static int
storageVolResize(virStorageVolPtr obj,
unsigned long long capacity,
unsigned int flags)
{
virStorageBackendPtr backend;
virStoragePoolObjPtr pool = NULL;
virStorageVolDefPtr vol = NULL;
unsigned long long abs_capacity;
int ret = -1;
virCheckFlags(VIR_STORAGE_VOL_RESIZE_ALLOCATE |
VIR_STORAGE_VOL_RESIZE_DELTA |
VIR_STORAGE_VOL_RESIZE_SHRINK, -1);
if (!(vol = virStorageVolDefFromVol(obj, &pool, &backend)))
return -1;
if (virStorageVolResizeEnsureACL(obj->conn, pool->def, vol) < 0)
goto cleanup;
if (vol->in_use) {
virReportError(VIR_ERR_OPERATION_INVALID,
_("volume '%s' is still in use."),
vol->name);
goto cleanup;
}
if (vol->building) {
virReportError(VIR_ERR_OPERATION_INVALID,
_("volume '%s' is still being allocated."),
vol->name);
goto cleanup;
}
if (flags & VIR_STORAGE_VOL_RESIZE_DELTA) {
abs_capacity = vol->target.capacity + capacity;
flags &= ~VIR_STORAGE_VOL_RESIZE_DELTA;
} else {
abs_capacity = capacity;
}
if (abs_capacity < vol->target.allocation) {
virReportError(VIR_ERR_INVALID_ARG, "%s",
_("can't shrink capacity below "
"existing allocation"));
goto cleanup;
}
if (abs_capacity < vol->target.capacity &&
!(flags & VIR_STORAGE_VOL_RESIZE_SHRINK)) {
virReportError(VIR_ERR_INVALID_ARG, "%s",
_("Can't shrink capacity below current "
"capacity with shrink flag explicitly specified"));
goto cleanup;
}
if (abs_capacity > vol->target.capacity + pool->def->available) {
virReportError(VIR_ERR_OPERATION_FAILED, "%s",
_("Not enough space left on storage pool"));
goto cleanup;
}
if (!backend->resizeVol) {
virReportError(VIR_ERR_NO_SUPPORT, "%s",
_("storage pool does not support changing of "
"volume capacity"));
goto cleanup;
}
if (backend->resizeVol(obj->conn, pool, vol, abs_capacity, flags) < 0)
goto cleanup;
vol->target.capacity = abs_capacity;
if (flags & VIR_STORAGE_VOL_RESIZE_ALLOCATE)
vol->target.allocation = abs_capacity;
/* Update pool metadata */
pool->def->allocation += (abs_capacity - vol->target.capacity);
pool->def->available -= (abs_capacity - vol->target.capacity);
ret = 0;
cleanup:
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
storageVolZeroSparseFile(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);
return ret;
}
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);
}
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 cleanup;
}
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 cleanup;
}
*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 cleanup;
}
VIR_DEBUG("Wrote %zu bytes to volume with path '%s'",
*bytes_wiped, vol->target.path);
ret = 0;
cleanup:
return ret;
}
static int
storageVolWipeInternal(virStorageVolDefPtr def,
unsigned int algorithm)
{
int ret = -1, fd = -1;
struct stat st;
char *writebuf = NULL;
size_t bytes_wiped = 0;
virCommandPtr cmd = NULL;
VIR_DEBUG("Wiping volume with path '%s' and algorithm %u",
def->target.path, algorithm);
fd = open(def->target.path, O_RDWR);
if (fd == -1) {
virReportSystemError(errno,
_("Failed to open storage volume with path '%s'"),
def->target.path);
goto cleanup;
}
if (fstat(fd, &st) == -1) {
virReportSystemError(errno,
_("Failed to stat storage volume with path '%s'"),
def->target.path);
goto cleanup;
}
if (algorithm != VIR_STORAGE_VOL_WIPE_ALG_ZERO) {
const char *alg_char ATTRIBUTE_UNUSED = NULL;
switch (algorithm) {
case VIR_STORAGE_VOL_WIPE_ALG_NNSA:
alg_char = "nnsa";
break;
case VIR_STORAGE_VOL_WIPE_ALG_DOD:
alg_char = "dod";
break;
case VIR_STORAGE_VOL_WIPE_ALG_BSI:
alg_char = "bsi";
break;
case VIR_STORAGE_VOL_WIPE_ALG_GUTMANN:
alg_char = "gutmann";
break;
case VIR_STORAGE_VOL_WIPE_ALG_SCHNEIER:
alg_char = "schneier";
break;
case VIR_STORAGE_VOL_WIPE_ALG_PFITZNER7:
alg_char = "pfitzner7";
break;
case VIR_STORAGE_VOL_WIPE_ALG_PFITZNER33:
alg_char = "pfitzner33";
break;
case VIR_STORAGE_VOL_WIPE_ALG_RANDOM:
alg_char = "random";
break;
default:
virReportError(VIR_ERR_INVALID_ARG,
_("unsupported algorithm %d"),
algorithm);
}
cmd = virCommandNew(SCRUB);
virCommandAddArgList(cmd, "-f", "-p", alg_char,
def->target.path, NULL);
if (virCommandRun(cmd, NULL) < 0)
goto cleanup;
ret = 0;
goto cleanup;
} else {
if (S_ISREG(st.st_mode) && st.st_blocks < (st.st_size / DEV_BSIZE)) {
ret = storageVolZeroSparseFile(def, st.st_size, fd);
} else {
if (VIR_ALLOC_N(writebuf, st.st_blksize) < 0)
goto cleanup;
ret = storageWipeExtent(def,
fd,
0,
def->target.allocation,
writebuf,
st.st_blksize,
&bytes_wiped);
}
}
cleanup:
virCommandFree(cmd);
VIR_FREE(writebuf);
VIR_FORCE_CLOSE(fd);
return ret;
}
static int
storageVolWipePattern(virStorageVolPtr obj,
unsigned int algorithm,
unsigned int flags)
{
virStoragePoolObjPtr pool = NULL;
virStorageVolDefPtr vol = NULL;
int ret = -1;
virCheckFlags(0, -1);
if (algorithm >= VIR_STORAGE_VOL_WIPE_ALG_LAST) {
virReportError(VIR_ERR_INVALID_ARG,
_("wiping algorithm %d not supported"),
algorithm);
return -1;
}
if (!(vol = virStorageVolDefFromVol(obj, &pool, NULL)))
return -1;
if (virStorageVolWipePatternEnsureACL(obj->conn, pool->def, vol) < 0)
goto cleanup;
if (vol->in_use) {
virReportError(VIR_ERR_OPERATION_INVALID,
_("volume '%s' is still in use."),
vol->name);
goto cleanup;
}
if (vol->building) {
virReportError(VIR_ERR_OPERATION_INVALID,
_("volume '%s' is still being allocated."),
vol->name);
goto cleanup;
}
if (storageVolWipeInternal(vol, algorithm) == -1) {
goto cleanup;
}
ret = 0;
cleanup:
virStoragePoolObjUnlock(pool);
return ret;
}
static int
storageVolWipe(virStorageVolPtr obj,
unsigned int flags)
{
return storageVolWipePattern(obj, VIR_STORAGE_VOL_WIPE_ALG_ZERO, flags);
}
static int
storageVolGetInfo(virStorageVolPtr obj,
virStorageVolInfoPtr info)
{
virStoragePoolObjPtr pool;
virStorageBackendPtr backend;
virStorageVolDefPtr vol;
int ret = -1;
if (!(vol = virStorageVolDefFromVol(obj, &pool, &backend)))
return -1;
if (virStorageVolGetInfoEnsureACL(obj->conn, pool->def, vol) < 0)
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->target.capacity;
info->allocation = vol->target.allocation;
ret = 0;
cleanup:
virStoragePoolObjUnlock(pool);
return ret;
}
static char *
storageVolGetXMLDesc(virStorageVolPtr obj,
unsigned int flags)
{
virStoragePoolObjPtr pool;
virStorageBackendPtr backend;
virStorageVolDefPtr vol;
char *ret = NULL;
virCheckFlags(0, NULL);
if (!(vol = virStorageVolDefFromVol(obj, &pool, &backend)))
return NULL;
if (virStorageVolGetXMLDescEnsureACL(obj->conn, pool->def, vol) < 0)
goto cleanup;
if (backend->refreshVol &&
backend->refreshVol(obj->conn, pool, vol) < 0)
goto cleanup;
ret = virStorageVolDefFormat(pool->def, vol);
cleanup:
virStoragePoolObjUnlock(pool);
return ret;
}
static char *
storageVolGetPath(virStorageVolPtr obj)
{
virStoragePoolObjPtr pool;
virStorageVolDefPtr vol;
char *ret = NULL;
if (!(vol = virStorageVolDefFromVol(obj, &pool, NULL)))
return NULL;
if (virStorageVolGetPathEnsureACL(obj->conn, pool->def, vol) < 0)
goto cleanup;
ignore_value(VIR_STRDUP(ret, vol->target.path));
cleanup:
virStoragePoolObjUnlock(pool);
return ret;
}
static int
storageConnectListAllStoragePools(virConnectPtr conn,
virStoragePoolPtr **pools,
unsigned int flags)
{
virStorageDriverStatePtr driver = conn->storagePrivateData;
int ret = -1;
virCheckFlags(VIR_CONNECT_LIST_STORAGE_POOLS_FILTERS_ALL, -1);
if (virConnectListAllStoragePoolsEnsureACL(conn) < 0)
goto cleanup;
storageDriverLock(driver);
ret = virStoragePoolObjListExport(conn, driver->pools, pools,
virConnectListAllStoragePoolsCheckACL, flags);
storageDriverUnlock(driver);
cleanup:
return ret;
}
static virStorageDriver storageDriver = {
.name = "storage",
.storageOpen = storageOpen, /* 0.4.0 */
.storageClose = storageClose, /* 0.4.0 */
.connectNumOfStoragePools = storageConnectNumOfStoragePools, /* 0.4.0 */
.connectListStoragePools = storageConnectListStoragePools, /* 0.4.0 */
.connectNumOfDefinedStoragePools = storageConnectNumOfDefinedStoragePools, /* 0.4.0 */
.connectListDefinedStoragePools = storageConnectListDefinedStoragePools, /* 0.4.0 */
.connectListAllStoragePools = storageConnectListAllStoragePools, /* 0.10.2 */
.connectFindStoragePoolSources = storageConnectFindStoragePoolSources, /* 0.4.0 */
.storagePoolLookupByName = storagePoolLookupByName, /* 0.4.0 */
.storagePoolLookupByUUID = storagePoolLookupByUUID, /* 0.4.0 */
.storagePoolLookupByVolume = storagePoolLookupByVolume, /* 0.4.0 */
.storagePoolCreateXML = storagePoolCreateXML, /* 0.4.0 */
.storagePoolDefineXML = storagePoolDefineXML, /* 0.4.0 */
.storagePoolBuild = storagePoolBuild, /* 0.4.0 */
.storagePoolUndefine = storagePoolUndefine, /* 0.4.0 */
.storagePoolCreate = storagePoolCreate, /* 0.4.0 */
.storagePoolDestroy = storagePoolDestroy, /* 0.4.0 */
.storagePoolDelete = storagePoolDelete, /* 0.4.0 */
.storagePoolRefresh = storagePoolRefresh, /* 0.4.0 */
.storagePoolGetInfo = storagePoolGetInfo, /* 0.4.0 */
.storagePoolGetXMLDesc = storagePoolGetXMLDesc, /* 0.4.0 */
.storagePoolGetAutostart = storagePoolGetAutostart, /* 0.4.0 */
.storagePoolSetAutostart = storagePoolSetAutostart, /* 0.4.0 */
.storagePoolNumOfVolumes = storagePoolNumOfVolumes, /* 0.4.0 */
.storagePoolListVolumes = storagePoolListVolumes, /* 0.4.0 */
.storagePoolListAllVolumes = storagePoolListAllVolumes, /* 0.10.2 */
.storageVolLookupByName = storageVolLookupByName, /* 0.4.0 */
.storageVolLookupByKey = storageVolLookupByKey, /* 0.4.0 */
.storageVolLookupByPath = storageVolLookupByPath, /* 0.4.0 */
.storageVolCreateXML = storageVolCreateXML, /* 0.4.0 */
.storageVolCreateXMLFrom = storageVolCreateXMLFrom, /* 0.6.4 */
.storageVolDownload = storageVolDownload, /* 0.9.0 */
.storageVolUpload = storageVolUpload, /* 0.9.0 */
.storageVolDelete = storageVolDelete, /* 0.4.0 */
.storageVolWipe = storageVolWipe, /* 0.8.0 */
.storageVolWipePattern = storageVolWipePattern, /* 0.9.10 */
.storageVolGetInfo = storageVolGetInfo, /* 0.4.0 */
.storageVolGetXMLDesc = storageVolGetXMLDesc, /* 0.4.0 */
.storageVolGetPath = storageVolGetPath, /* 0.4.0 */
.storageVolResize = storageVolResize, /* 0.9.10 */
.storagePoolIsActive = storagePoolIsActive, /* 0.7.3 */
.storagePoolIsPersistent = storagePoolIsPersistent, /* 0.7.3 */
};
static virStateDriver stateDriver = {
.name = "Storage",
.stateInitialize = storageStateInitialize,
.stateAutoStart = storageStateAutoStart,
.stateCleanup = storageStateCleanup,
.stateReload = storageStateReload,
};
int storageRegister(void)
{
if (virRegisterStorageDriver(&storageDriver) < 0)
return -1;
if (virRegisterStateDriver(&stateDriver) < 0)
return -1;
return 0;
}
/* ----------- file handlers cooperating with storage driver --------------- */
static bool
virStorageFileIsInitialized(virStorageSourcePtr src)
{
return !!src->drv;
}
static bool
virStorageFileSupportsBackingChainTraversal(virStorageSourcePtr src)
{
int actualType = virStorageSourceGetActualType(src);
virStorageFileBackendPtr backend;
if (!src)
return false;
if (src->drv) {
backend = src->drv->backend;
} else {
if (!(backend = virStorageFileBackendForTypeInternal(actualType,
src->protocol,
false)))
return false;
}
return backend->storageFileGetUniqueIdentifier &&
backend->storageFileReadHeader &&
backend->storageFileAccess;
}
void
virStorageFileDeinit(virStorageSourcePtr src)
{
if (!virStorageFileIsInitialized(src))
return;
if (src->drv->backend &&
src->drv->backend->backendDeinit)
src->drv->backend->backendDeinit(src);
VIR_FREE(src->drv);
}
/**
* virStorageFileInitAs:
*
* @src: storage source definition
* @uid: uid used to access the file, or -1 for current uid
* @gid: gid used to access the file, or -1 for current gid
*
* Initialize a storage source to be used with storage driver. Use the provided
* uid and gid if possible for the operations.
*
* Returns 0 if the storage file was successfully initialized, -1 if the
* initialization failed. Libvirt error is reported.
*/
int
virStorageFileInitAs(virStorageSourcePtr src,
uid_t uid, gid_t gid)
{
int actualType = virStorageSourceGetActualType(src);
if (VIR_ALLOC(src->drv) < 0)
return -1;
if (uid == (uid_t) -1)
src->drv->uid = geteuid();
else
src->drv->uid = uid;
if (gid == (gid_t) -1)
src->drv->gid = getegid();
else
src->drv->gid = gid;
if (!(src->drv->backend = virStorageFileBackendForType(actualType,
src->protocol)))
goto error;
if (src->drv->backend->backendInit &&
src->drv->backend->backendInit(src) < 0)
goto error;
return 0;
error:
VIR_FREE(src->drv);
return -1;
}
/**
* virStorageFileInit:
*
* See virStorageFileInitAs. The file is initialized to be accessed by the
* current user.
*/
int
virStorageFileInit(virStorageSourcePtr src)
{
return virStorageFileInitAs(src, -1, -1);
}
/**
* virStorageFileCreate: Creates an empty storage file via storage driver
*
* @src: file structure pointing to the file
*
* Returns 0 on success, -2 if the function isn't supported by the backend,
* -1 on other failure. Errno is set in case of failure.
*/
int
virStorageFileCreate(virStorageSourcePtr src)
{
int ret;
if (!virStorageFileIsInitialized(src) ||
!src->drv->backend->storageFileCreate) {
errno = ENOSYS;
return -2;
}
ret = src->drv->backend->storageFileCreate(src);
VIR_DEBUG("created storage file %p: ret=%d, errno=%d",
src, ret, errno);
return ret;
}
/**
* virStorageFileUnlink: Unlink storage file via storage driver
*
* @src: file structure pointing to the file
*
* Unlinks the file described by the @file structure.
*
* Returns 0 on success, -2 if the function isn't supported by the backend,
* -1 on other failure. Errno is set in case of failure.
*/
int
virStorageFileUnlink(virStorageSourcePtr src)
{
int ret;
if (!virStorageFileIsInitialized(src) ||
!src->drv->backend->storageFileUnlink) {
errno = ENOSYS;
return -2;
}
ret = src->drv->backend->storageFileUnlink(src);
VIR_DEBUG("unlinked storage file %p: ret=%d, errno=%d",
src, ret, errno);
return ret;
}
/**
* virStorageFileStat: returns stat struct of a file via storage driver
*
* @src: file structure pointing to the file
* @stat: stat structure to return data
*
* Returns 0 on success, -2 if the function isn't supported by the backend,
* -1 on other failure. Errno is set in case of failure.
*/
int
virStorageFileStat(virStorageSourcePtr src,
struct stat *st)
{
int ret;
if (!virStorageFileIsInitialized(src) ||
!src->drv->backend->storageFileStat) {
errno = ENOSYS;
return -2;
}
ret = src->drv->backend->storageFileStat(src, st);
VIR_DEBUG("stat of storage file %p: ret=%d, errno=%d",
src, ret, errno);
return ret;
}
/**
* virStorageFileReadHeader: read the beginning bytes of a file into a buffer
*
* @src: file structure pointing to the file
* @max_len: maximum number of bytes read from the storage file
* @buf: buffer to read the data into. buffer shall be freed by caller)
*
* Returns the count of bytes read on success and -1 on failure, -2 if the
* function isn't supported by the backend. Libvirt error is reported on failure.
*/
ssize_t
virStorageFileReadHeader(virStorageSourcePtr src,
ssize_t max_len,
char **buf)
{
ssize_t ret;
if (!virStorageFileIsInitialized(src)) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("storage file backend not initialized"));
return -1;
}
if (!src->drv->backend->storageFileReadHeader) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("storage file header reading is not supported for "
"storage type %s (protocol: %s)"),
virStorageTypeToString(src->type),
virStorageNetProtocolTypeToString(src->protocol));
return -2;
}
ret = src->drv->backend->storageFileReadHeader(src, max_len, buf);
VIR_DEBUG("read of storage header %p: ret=%zd", src, ret);
return ret;
}
/*
* virStorageFileGetUniqueIdentifier: Get a unique string describing the volume
*
* @src: file structure pointing to the file
*
* Returns a string uniquely describing a single volume (canonical path).
* The string shall not be freed and is valid until the storage file is
* deinitialized. Returns NULL on error and sets a libvirt error code */
const char *
virStorageFileGetUniqueIdentifier(virStorageSourcePtr src)
{
if (!virStorageFileIsInitialized(src)) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("storage file backend not initialized"));
return NULL;
}
if (!src->drv->backend->storageFileGetUniqueIdentifier) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("unique storage file identifier not implemented for "
"storage type %s (protocol: %s)'"),
virStorageTypeToString(src->type),
virStorageNetProtocolTypeToString(src->protocol));
return NULL;
}
return src->drv->backend->storageFileGetUniqueIdentifier(src);
}
/**
* virStorageFileAccess: Check accessibility of a storage file
*
* @src: storage file to check access permissions
* @mode: accessibility check options (see man 2 access)
*
* Returns 0 on success, -1 on error and sets errno. No libvirt
* error is reported. Returns -2 if the operation isn't supported
* by libvirt storage backend.
*/
int
virStorageFileAccess(virStorageSourcePtr src,
int mode)
{
if (!virStorageFileIsInitialized(src) ||
!src->drv->backend->storageFileAccess) {
errno = ENOSYS;
return -2;
}
return src->drv->backend->storageFileAccess(src, mode);
}
/* Recursive workhorse for virStorageFileGetMetadata. */
static int
virStorageFileGetMetadataRecurse(virStorageSourcePtr src,
uid_t uid, gid_t gid,
bool allow_probe,
virHashTablePtr cycle)
{
int ret = -1;
const char *uniqueName;
char *buf = NULL;
ssize_t headerLen;
virStorageSourcePtr backingStore = NULL;
int backingFormat;
VIR_DEBUG("path=%s format=%d uid=%d gid=%d probe=%d",
src->path, src->format,
(int)uid, (int)gid, allow_probe);
/* exit if we can't load information about the current image */
if (!virStorageFileSupportsBackingChainTraversal(src))
return 0;
if (virStorageFileInitAs(src, uid, gid) < 0)
return -1;
if (virStorageFileAccess(src, F_OK) < 0) {
virReportSystemError(errno,
_("Cannot access backing file %s"),
src->path);
goto cleanup;
}
if (!(uniqueName = virStorageFileGetUniqueIdentifier(src)))
goto cleanup;
if (virHashLookup(cycle, uniqueName)) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("backing store for %s (%s) is self-referential"),
src->path, uniqueName);
goto cleanup;
}
if (virHashAddEntry(cycle, uniqueName, (void *)1) < 0)
goto cleanup;
if ((headerLen = virStorageFileReadHeader(src, VIR_STORAGE_MAX_HEADER,
&buf)) < 0)
goto cleanup;
if (virStorageFileGetMetadataInternal(src, buf, headerLen,
&backingFormat) < 0)
goto cleanup;
/* check whether we need to go deeper */
if (!src->backingStoreRaw) {
ret = 0;
goto cleanup;
}
if (!(backingStore = virStorageSourceNewFromBacking(src)))
goto cleanup;
if (backingFormat == VIR_STORAGE_FILE_AUTO && !allow_probe)
backingStore->format = VIR_STORAGE_FILE_RAW;
else if (backingFormat == VIR_STORAGE_FILE_AUTO_SAFE)
backingStore->format = VIR_STORAGE_FILE_AUTO;
else
backingStore->format = backingFormat;
if (virStorageFileGetMetadataRecurse(backingStore,
uid, gid, allow_probe,
cycle) < 0) {
/* if we fail somewhere midway, just accept and return a
* broken chain */
ret = 0;
goto cleanup;
}
src->backingStore = backingStore;
backingStore = NULL;
ret = 0;
cleanup:
VIR_FREE(buf);
virStorageFileDeinit(src);
virStorageSourceFree(backingStore);
return ret;
}
/**
* virStorageFileGetMetadata:
*
* Extract metadata about the storage volume with the specified
* image format. If image format is VIR_STORAGE_FILE_AUTO, it
* will probe to automatically identify the format. Recurses through
* the entire chain.
*
* Open files using UID and GID (or pass -1 for the current user/group).
* Treat any backing files without explicit type as raw, unless ALLOW_PROBE.
*
* Callers are advised never to use VIR_STORAGE_FILE_AUTO as a
* format, since a malicious guest can turn a raw file into any
* other non-raw format at will.
*
* Caller MUST free result after use via virStorageSourceFree.
*/
int
virStorageFileGetMetadata(virStorageSourcePtr src,
uid_t uid, gid_t gid,
bool allow_probe)
{
VIR_DEBUG("path=%s format=%d uid=%d gid=%d probe=%d",
src->path, src->format, (int)uid, (int)gid, allow_probe);
virHashTablePtr cycle = NULL;
char *canonPath = NULL;
int ret = -1;
if (!(cycle = virHashCreate(5, NULL)))
return -1;
if (src->format <= VIR_STORAGE_FILE_NONE)
src->format = allow_probe ? VIR_STORAGE_FILE_AUTO : VIR_STORAGE_FILE_RAW;
ret = virStorageFileGetMetadataRecurse(src, uid, gid,
allow_probe, cycle);
VIR_FREE(canonPath);
virHashFree(cycle);
return ret;
}