1
0
mirror of https://gitlab.com/libvirt/libvirt.git synced 2025-03-20 07:59:00 +00:00
libvirt/src/conf/virstorageobj.c
Daniel P. Berrangé ec8e185cd0 conf: remove misleading comments about access being 'lockless'
For the various structs storing lists of objects, the access
to the hash tables is not lockless. The mutex on the object
owning the hash table must be held.

Reviewed-by: Ján Tomko <jtomko@redhat.com>
Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
2022-03-10 08:05:29 +00:00

2078 lines
55 KiB
C

/*
* virstorageobj.c: internal storage pool and volume objects handling
* (derived from storage_conf.c)
*
* 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
* <http://www.gnu.org/licenses/>.
*/
#include <config.h>
#include <dirent.h>
#include "datatypes.h"
#include "node_device_conf.h"
#include "node_device_util.h"
#include "virstorageobj.h"
#include "viralloc.h"
#include "virerror.h"
#include "virfile.h"
#include "virhash.h"
#include "virlog.h"
#include "virscsihost.h"
#include "virstring.h"
#include "virvhba.h"
#define VIR_FROM_THIS VIR_FROM_STORAGE
VIR_LOG_INIT("conf.virstorageobj");
static virClass *virStoragePoolObjClass;
static virClass *virStoragePoolObjListClass;
static virClass *virStorageVolObjClass;
static virClass *virStorageVolObjListClass;
static void
virStoragePoolObjDispose(void *opaque);
static void
virStoragePoolObjListDispose(void *opaque);
static void
virStorageVolObjDispose(void *opaque);
static void
virStorageVolObjListDispose(void *opaque);
typedef struct _virStorageVolObj virStorageVolObj;
struct _virStorageVolObj {
virObjectLockable parent;
virStorageVolDef *voldef;
};
typedef struct _virStorageVolObjList virStorageVolObjList;
struct _virStorageVolObjList {
virObjectRWLockable parent;
/* key string -> virStorageVolObj mapping
* for (1), lookup-by-key */
GHashTable *objsKey;
/* name string -> virStorageVolObj mapping
* for (1), lookup-by-name */
GHashTable *objsName;
/* path string -> virStorageVolObj mapping
* for (1), lookup-by-path */
GHashTable *objsPath;
};
struct _virStoragePoolObj {
virObjectLockable parent;
char *configFile;
char *autostartLink;
bool active;
bool starting;
bool autostart;
unsigned int asyncjobs;
virStoragePoolDef *def;
virStoragePoolDef *newDef;
virStorageVolObjList *volumes;
};
struct _virStoragePoolObjList {
virObjectRWLockable parent;
/* uuid string -> virStoragePoolObj mapping
* for (1), lookup-by-uuid */
GHashTable *objs;
/* name string -> virStoragePoolObj mapping
* for (1), lookup-by-name */
GHashTable *objsName;
};
static int
virStorageVolObjOnceInit(void)
{
if (!VIR_CLASS_NEW(virStorageVolObj, virClassForObjectLockable()))
return -1;
if (!VIR_CLASS_NEW(virStorageVolObjList, virClassForObjectRWLockable()))
return -1;
return 0;
}
VIR_ONCE_GLOBAL_INIT(virStorageVolObj);
static virStorageVolObj *
virStorageVolObjNew(void)
{
virStorageVolObj *obj;
if (virStorageVolObjInitialize() < 0)
return NULL;
if (!(obj = virObjectLockableNew(virStorageVolObjClass)))
return NULL;
virObjectLock(obj);
return obj;
}
static void
virStorageVolObjEndAPI(virStorageVolObj **obj)
{
if (!*obj)
return;
virObjectUnlock(*obj);
g_clear_pointer(obj, virObjectUnref);
}
static void
virStorageVolObjDispose(void *opaque)
{
virStorageVolObj *obj = opaque;
virStorageVolDefFree(obj->voldef);
}
static virStorageVolObjList *
virStorageVolObjListNew(void)
{
virStorageVolObjList *vols;
if (virStorageVolObjInitialize() < 0)
return NULL;
if (!(vols = virObjectRWLockableNew(virStorageVolObjListClass)))
return NULL;
vols->objsKey = virHashNew(virObjectFreeHashData);
vols->objsName = virHashNew(virObjectFreeHashData);
vols->objsPath = virHashNew(virObjectFreeHashData);
return vols;
}
static void
virStorageVolObjListDispose(void *opaque)
{
virStorageVolObjList *vols = opaque;
g_clear_pointer(&vols->objsKey, g_hash_table_unref);
g_clear_pointer(&vols->objsName, g_hash_table_unref);
g_clear_pointer(&vols->objsPath, g_hash_table_unref);
}
static int
virStoragePoolObjOnceInit(void)
{
if (!VIR_CLASS_NEW(virStoragePoolObj, virClassForObjectLockable()))
return -1;
if (!VIR_CLASS_NEW(virStoragePoolObjList, virClassForObjectRWLockable()))
return -1;
return 0;
}
VIR_ONCE_GLOBAL_INIT(virStoragePoolObj);
virStoragePoolObj *
virStoragePoolObjNew(void)
{
virStoragePoolObj *obj;
if (virStoragePoolObjInitialize() < 0)
return NULL;
if (!(obj = virObjectLockableNew(virStoragePoolObjClass)))
return NULL;
if (!(obj->volumes = virStorageVolObjListNew())) {
virObjectUnref(obj);
return NULL;
}
virObjectLock(obj);
obj->active = false;
return obj;
}
void
virStoragePoolObjEndAPI(virStoragePoolObj **obj)
{
if (!*obj)
return;
virObjectUnlock(*obj);
g_clear_pointer(obj, virObjectUnref);
}
virStoragePoolDef *
virStoragePoolObjGetDef(virStoragePoolObj *obj)
{
return obj->def;
}
void
virStoragePoolObjSetDef(virStoragePoolObj *obj,
virStoragePoolDef *def)
{
virStoragePoolDefFree(obj->def);
obj->def = def;
}
virStoragePoolDef *
virStoragePoolObjGetNewDef(virStoragePoolObj *obj)
{
return obj->newDef;
}
void
virStoragePoolObjDefUseNewDef(virStoragePoolObj *obj)
{
virStoragePoolDefFree(obj->def);
obj->def = g_steal_pointer(&obj->newDef);
}
const char *
virStoragePoolObjGetConfigFile(virStoragePoolObj *obj)
{
return obj->configFile;
}
void
virStoragePoolObjSetConfigFile(virStoragePoolObj *obj,
char *configFile)
{
VIR_FREE(obj->configFile);
obj->configFile = configFile;
}
const char *
virStoragePoolObjGetAutostartLink(virStoragePoolObj *obj)
{
return obj->autostartLink;
}
bool
virStoragePoolObjIsActive(virStoragePoolObj *obj)
{
return obj->active;
}
void
virStoragePoolObjSetActive(virStoragePoolObj *obj,
bool active)
{
obj->active = active;
}
void
virStoragePoolObjSetStarting(virStoragePoolObj *obj,
bool starting)
{
obj->starting = starting;
}
bool
virStoragePoolObjIsStarting(virStoragePoolObj *obj)
{
return obj->starting;
}
bool
virStoragePoolObjIsAutostart(virStoragePoolObj *obj)
{
if (!obj->configFile)
return false;
return obj->autostart;
}
void
virStoragePoolObjSetAutostart(virStoragePoolObj *obj,
bool autostart)
{
obj->autostart = autostart;
}
unsigned int
virStoragePoolObjGetAsyncjobs(virStoragePoolObj *obj)
{
return obj->asyncjobs;
}
void
virStoragePoolObjIncrAsyncjobs(virStoragePoolObj *obj)
{
obj->asyncjobs++;
}
void
virStoragePoolObjDecrAsyncjobs(virStoragePoolObj *obj)
{
obj->asyncjobs--;
}
void
virStoragePoolObjDispose(void *opaque)
{
virStoragePoolObj *obj = opaque;
virStoragePoolObjClearVols(obj);
virObjectUnref(obj->volumes);
virStoragePoolDefFree(obj->def);
virStoragePoolDefFree(obj->newDef);
g_free(obj->configFile);
g_free(obj->autostartLink);
}
void
virStoragePoolObjListDispose(void *opaque)
{
virStoragePoolObjList *pools = opaque;
g_clear_pointer(&pools->objs, g_hash_table_unref);
g_clear_pointer(&pools->objsName, g_hash_table_unref);
}
virStoragePoolObjList *
virStoragePoolObjListNew(void)
{
virStoragePoolObjList *pools;
if (virStoragePoolObjInitialize() < 0)
return NULL;
if (!(pools = virObjectRWLockableNew(virStoragePoolObjListClass)))
return NULL;
pools->objs = virHashNew(virObjectFreeHashData);
pools->objsName = virHashNew(virObjectFreeHashData);
return pools;
}
struct _virStoragePoolObjListForEachData {
virStoragePoolObjListIterator iter;
const void *opaque;
};
static int
virStoragePoolObjListForEachCb(void *payload,
const char *name G_GNUC_UNUSED,
void *opaque)
{
virStoragePoolObj *obj = payload;
struct _virStoragePoolObjListForEachData *data = opaque;
/* Grab a reference so that we don't rely only on references grabbed by
* hash table earlier. Remember, an iterator can remove object from the
* hash table. */
virObjectRef(obj);
virObjectLock(obj);
data->iter(obj, data->opaque);
virStoragePoolObjEndAPI(&obj);
return 0;
}
/**
* virStoragePoolObjListForEach
* @pools: Pointer to pools object
* @iter: Callback iteration helper
* @opaque: Opaque data to use as argument to helper
*
* For each object in @pools, call the @iter helper using @opaque as
* an argument. This function doesn't care whether the @iter fails or
* not as it's being used for Autostart and UpdateAllState callers
* that want to iterate over all the @pools objects not stopping if
* one happens to fail.
*
* NB: We cannot take the Storage Pool lock here because it's possible
* that some action as part of Autostart or UpdateAllState will need
* to modify/destroy a transient pool. Since these paths only occur
* during periods in which the storageDriverLock is held (Initialization,
* AutoStart, or Reload) this is OK.
*/
void
virStoragePoolObjListForEach(virStoragePoolObjList *pools,
virStoragePoolObjListIterator iter,
const void *opaque)
{
struct _virStoragePoolObjListForEachData data = { .iter = iter,
.opaque = opaque };
virHashForEachSafe(pools->objs, virStoragePoolObjListForEachCb, &data);
}
struct _virStoragePoolObjListSearchData {
virStoragePoolObjListSearcher searcher;
const void *opaque;
};
static int
virStoragePoolObjListSearchCb(const void *payload,
const char *name G_GNUC_UNUSED,
const void *opaque)
{
virStoragePoolObj *obj = (virStoragePoolObj *) payload;
struct _virStoragePoolObjListSearchData *data =
(struct _virStoragePoolObjListSearchData *)opaque;
virObjectLock(obj);
if (data->searcher(obj, data->opaque))
return 1;
virObjectUnlock(obj);
return 0;
}
/**
* virStoragePoolObjListSearch
* @pools: Pointer to pools object
* @search: Callback searcher helper
* @opaque: Opaque data to use as argument to helper
*
* Search through the @pools objects calling the @search helper using
* the @opaque data in order to find an object that matches some criteria
* and return that object locked.
*
* Returns a locked and reffed object when found and NULL when not found
*/
virStoragePoolObj *
virStoragePoolObjListSearch(virStoragePoolObjList *pools,
virStoragePoolObjListSearcher searcher,
const void *opaque)
{
virStoragePoolObj *obj = NULL;
struct _virStoragePoolObjListSearchData data = { .searcher = searcher,
.opaque = opaque };
virObjectRWLockRead(pools);
obj = virHashSearch(pools->objs, virStoragePoolObjListSearchCb, &data, NULL);
virObjectRWUnlock(pools);
return virObjectRef(obj);
}
void
virStoragePoolObjRemove(virStoragePoolObjList *pools,
virStoragePoolObj *obj)
{
char uuidstr[VIR_UUID_STRING_BUFLEN];
virUUIDFormat(obj->def->uuid, uuidstr);
virObjectRef(obj);
virObjectUnlock(obj);
virObjectRWLockWrite(pools);
virObjectLock(obj);
virHashRemoveEntry(pools->objs, uuidstr);
virHashRemoveEntry(pools->objsName, obj->def->name);
virObjectUnref(obj);
virObjectRWUnlock(pools);
}
static virStoragePoolObj *
virStoragePoolObjFindByUUIDLocked(virStoragePoolObjList *pools,
const unsigned char *uuid)
{
char uuidstr[VIR_UUID_STRING_BUFLEN];
virUUIDFormat(uuid, uuidstr);
return virObjectRef(virHashLookup(pools->objs, uuidstr));
}
/**
* virStoragePoolObjFindByUUID
* @pools: Storage pool object list pointer
* @uuid: Storage object uuid to find
*
* Lock the @pools and lookup the object by @uuid
*
* Returns: Locked and reffed storage pool object or NULL if not found
*/
virStoragePoolObj *
virStoragePoolObjFindByUUID(virStoragePoolObjList *pools,
const unsigned char *uuid)
{
virStoragePoolObj *obj;
virObjectRWLockRead(pools);
obj = virStoragePoolObjFindByUUIDLocked(pools, uuid);
virObjectRWUnlock(pools);
if (obj)
virObjectLock(obj);
return obj;
}
static virStoragePoolObj *
virStoragePoolObjFindByNameLocked(virStoragePoolObjList *pools,
const char *name)
{
return virObjectRef(virHashLookup(pools->objsName, name));
}
/**
* virStoragePoolObjFindByName
* @pools: Storage pool object list pointer
* @name: Storage object name to find
*
* Lock the @pools and lookup the object by @name
*
* Returns: Locked and reffed storage pool object or NULL if not found
*/
virStoragePoolObj *
virStoragePoolObjFindByName(virStoragePoolObjList *pools,
const char *name)
{
virStoragePoolObj *obj;
virObjectRWLockRead(pools);
obj = virStoragePoolObjFindByNameLocked(pools, name);
virObjectRWUnlock(pools);
if (obj)
virObjectLock(obj);
return obj;
}
static virStoragePoolObj *
virStoragePoolSourceFindDuplicateDevices(virStoragePoolObj *obj,
virStoragePoolDef *def)
{
size_t i, j;
for (i = 0; i < obj->def->source.ndevice; i++) {
for (j = 0; j < def->source.ndevice; j++) {
if (STREQ(obj->def->source.devices[i].path, def->source.devices[j].path))
return obj;
}
}
return NULL;
}
void
virStoragePoolObjClearVols(virStoragePoolObj *obj)
{
if (!obj->volumes)
return;
virHashRemoveAll(obj->volumes->objsKey);
virHashRemoveAll(obj->volumes->objsName);
virHashRemoveAll(obj->volumes->objsPath);
}
int
virStoragePoolObjAddVol(virStoragePoolObj *obj,
virStorageVolDef *voldef)
{
virStorageVolObj *volobj = NULL;
virStorageVolObjList *volumes = obj->volumes;
virObjectRWLockWrite(volumes);
if (!(volobj = virStorageVolObjNew()))
goto error;
if (virHashAddEntry(volumes->objsKey, voldef->key, volobj) < 0)
goto error;
virObjectRef(volobj);
if (virHashAddEntry(volumes->objsName, voldef->name, volobj) < 0) {
virHashRemoveEntry(volumes->objsKey, voldef->key);
goto error;
}
virObjectRef(volobj);
if (virHashAddEntry(volumes->objsPath, voldef->target.path, volobj) < 0) {
virHashRemoveEntry(volumes->objsKey, voldef->key);
virHashRemoveEntry(volumes->objsName, voldef->name);
goto error;
}
virObjectRef(volobj);
volobj->voldef = voldef;
virObjectRWUnlock(volumes);
virStorageVolObjEndAPI(&volobj);
return 0;
error:
virStorageVolObjEndAPI(&volobj);
virObjectRWUnlock(volumes);
return -1;
}
void
virStoragePoolObjRemoveVol(virStoragePoolObj *obj,
virStorageVolDef *voldef)
{
virStorageVolObjList *volumes = obj->volumes;
virStorageVolObj *volobj;
virObjectRWLockWrite(volumes);
volobj = virHashLookup(volumes->objsName, voldef->name);
if (!volobj) {
VIR_INFO("Cannot find volume '%s' from storage pool '%s'",
voldef->name, obj->def->name);
virObjectRWUnlock(volumes);
return;
}
VIR_INFO("Deleting volume '%s' from storage pool '%s'",
voldef->name, obj->def->name);
virObjectRef(volobj);
virObjectLock(volobj);
virHashRemoveEntry(volumes->objsKey, voldef->key);
virHashRemoveEntry(volumes->objsName, voldef->name);
virHashRemoveEntry(volumes->objsPath, voldef->target.path);
virStorageVolObjEndAPI(&volobj);
virObjectRWUnlock(volumes);
}
size_t
virStoragePoolObjGetVolumesCount(virStoragePoolObj *obj)
{
size_t nbElems;
virObjectRWLockRead(obj->volumes);
nbElems = virHashSize(obj->volumes->objsKey);
virObjectRWUnlock(obj->volumes);
return nbElems;
}
struct _virStoragePoolObjForEachVolData {
virStorageVolObjListIterator iter;
const void *opaque;
};
static int
virStoragePoolObjForEachVolumeCb(void *payload,
const char *name G_GNUC_UNUSED,
void *opaque)
{
int ret = 0;
virStorageVolObj *volobj = payload;
struct _virStoragePoolObjForEachVolData *data = opaque;
virObjectLock(volobj);
if (data->iter(volobj->voldef, data->opaque) < 0)
ret = -1;
virObjectUnlock(volobj);
return ret;
}
int
virStoragePoolObjForEachVolume(virStoragePoolObj *obj,
virStorageVolObjListIterator iter,
const void *opaque)
{
struct _virStoragePoolObjForEachVolData data = {
.iter = iter, .opaque = opaque };
virObjectRWLockRead(obj->volumes);
virHashForEachSafe(obj->volumes->objsKey, virStoragePoolObjForEachVolumeCb,
&data);
virObjectRWUnlock(obj->volumes);
return 0;
}
struct _virStoragePoolObjSearchVolData {
virStorageVolObjListSearcher iter;
const void *opaque;
};
static int
virStoragePoolObjSearchVolumeCb(const void *payload,
const char *name G_GNUC_UNUSED,
const void *opaque)
{
virStorageVolObj *volobj = (virStorageVolObj *) payload;
struct _virStoragePoolObjSearchVolData *data =
(struct _virStoragePoolObjSearchVolData *) opaque;
int found = 0;
virObjectLock(volobj);
if (data->iter(volobj->voldef, data->opaque))
found = 1;
virObjectUnlock(volobj);
return found;
}
virStorageVolDef *
virStoragePoolObjSearchVolume(virStoragePoolObj *obj,
virStorageVolObjListSearcher iter,
const void *opaque)
{
virStorageVolObj *volobj;
struct _virStoragePoolObjSearchVolData data = {
.iter = iter, .opaque = opaque };
virObjectRWLockRead(obj->volumes);
volobj = virHashSearch(obj->volumes->objsKey,
virStoragePoolObjSearchVolumeCb,
&data, NULL);
virObjectRWUnlock(obj->volumes);
if (volobj)
return volobj->voldef;
return NULL;
}
virStorageVolDef *
virStorageVolDefFindByKey(virStoragePoolObj *obj,
const char *key)
{
virStorageVolObj *volobj;
virObjectRWLockRead(obj->volumes);
volobj = virHashLookup(obj->volumes->objsKey, key);
virObjectRWUnlock(obj->volumes);
if (volobj)
return volobj->voldef;
return NULL;
}
virStorageVolDef *
virStorageVolDefFindByPath(virStoragePoolObj *obj,
const char *path)
{
virStorageVolObj *volobj;
virObjectRWLockRead(obj->volumes);
volobj = virHashLookup(obj->volumes->objsPath, path);
virObjectRWUnlock(obj->volumes);
if (volobj)
return volobj->voldef;
return NULL;
}
virStorageVolDef *
virStorageVolDefFindByName(virStoragePoolObj *obj,
const char *name)
{
virStorageVolObj *volobj;
virObjectRWLockRead(obj->volumes);
volobj = virHashLookup(obj->volumes->objsName, name);
virObjectRWUnlock(obj->volumes);
if (volobj)
return volobj->voldef;
return NULL;
}
struct _virStorageVolObjCountData {
virConnectPtr conn;
virStoragePoolVolumeACLFilter filter;
virStoragePoolDef *pooldef;
int count;
};
static int
virStoragePoolObjNumOfVolumesCb(void *payload,
const char *name G_GNUC_UNUSED,
void *opaque)
{
virStorageVolObj *volobj = payload;
struct _virStorageVolObjCountData *data = opaque;
virObjectLock(volobj);
if (data->filter &&
!data->filter(data->conn, data->pooldef, volobj->voldef))
goto cleanup;
data->count++;
cleanup:
virObjectUnlock(volobj);
return 0;
}
int
virStoragePoolObjNumOfVolumes(virStoragePoolObj *obj,
virConnectPtr conn,
virStoragePoolVolumeACLFilter filter)
{
virStorageVolObjList *volumes = obj->volumes;
struct _virStorageVolObjCountData data = {
.conn = conn, .filter = filter, .pooldef = obj->def, .count = 0 };
virObjectRWLockRead(volumes);
virHashForEach(volumes->objsName, virStoragePoolObjNumOfVolumesCb, &data);
virObjectRWUnlock(volumes);
return data.count;
}
struct _virStorageVolObjNameData {
virConnectPtr conn;
virStoragePoolVolumeACLFilter filter;
virStoragePoolDef *pooldef;
bool error;
int nnames;
int maxnames;
char **const names;
};
static int
virStoragePoolObjVolumeGetNamesCb(void *payload,
const char *name G_GNUC_UNUSED,
void *opaque)
{
virStorageVolObj *volobj = payload;
struct _virStorageVolObjNameData *data = opaque;
if (data->error)
return 0;
if (data->maxnames >= 0 && data->nnames == data->maxnames)
return 0;
virObjectLock(volobj);
if (data->filter &&
!data->filter(data->conn, data->pooldef, volobj->voldef))
goto cleanup;
if (data->names)
data->names[data->nnames] = g_strdup(volobj->voldef->name);
data->nnames++;
cleanup:
virObjectUnlock(volobj);
return 0;
}
int
virStoragePoolObjVolumeGetNames(virStoragePoolObj *obj,
virConnectPtr conn,
virStoragePoolVolumeACLFilter filter,
char **const names,
int maxnames)
{
virStorageVolObjList *volumes = obj->volumes;
struct _virStorageVolObjNameData data = {
.conn = conn, .filter = filter, .pooldef = obj->def, .error = false,
.nnames = 0, .maxnames = maxnames, .names = names };
virObjectRWLockRead(volumes);
virHashForEach(volumes->objsName, virStoragePoolObjVolumeGetNamesCb, &data);
virObjectRWUnlock(volumes);
if (data.error)
goto error;
return data.nnames;
error:
while (--data.nnames)
VIR_FREE(data.names[data.nnames]);
return -1;
}
typedef struct _virStoragePoolObjVolumeListExportData virStoragePoolObjVolumeListExportData;
struct _virStoragePoolObjVolumeListExportData {
virConnectPtr conn;
virStoragePoolVolumeACLFilter filter;
virStoragePoolDef *pooldef;
bool error;
int nvols;
virStorageVolPtr *vols;
};
static int
virStoragePoolObjVolumeListExportCallback(void *payload,
const char *name G_GNUC_UNUSED,
void *opaque)
{
virStorageVolObj *volobj = payload;
virStoragePoolObjVolumeListExportData *data = opaque;
virStorageVolPtr vol = NULL;
if (data->error)
return 0;
virObjectLock(volobj);
if (data->filter &&
!data->filter(data->conn, data->pooldef, volobj->voldef))
goto cleanup;
if (data->vols) {
if (!(vol = virGetStorageVol(data->conn, data->pooldef->name,
volobj->voldef->name, volobj->voldef->key,
NULL, NULL))) {
data->error = true;
goto cleanup;
}
data->vols[data->nvols] = vol;
}
data->nvols++;
cleanup:
virObjectUnlock(volobj);
return 0;
}
int
virStoragePoolObjVolumeListExport(virConnectPtr conn,
virStoragePoolObj *obj,
virStorageVolPtr **vols,
virStoragePoolVolumeACLFilter filter)
{
virStorageVolObjList *volumes = obj->volumes;
virStoragePoolObjVolumeListExportData data = {
.conn = conn, .filter = filter, .pooldef = obj->def, .error = false,
.nvols = 0, .vols = NULL };
virObjectRWLockRead(volumes);
if (!vols) {
int ret = virHashSize(volumes->objsName);
virObjectRWUnlock(volumes);
return ret;
}
data.vols = g_new0(virStorageVolPtr, virHashSize(volumes->objsName) + 1);
virHashForEach(volumes->objsName, virStoragePoolObjVolumeListExportCallback, &data);
virObjectRWUnlock(volumes);
if (data.error)
goto error;
*vols = data.vols;
return data.nvols;
error:
virObjectListFree(data.vols);
return -1;
}
/*
* virStoragePoolObjIsDuplicate:
* @doms : virStoragePoolObjList * to search
* @def : virStoragePoolDef * definition of pool to lookup
* @check_active: If true, ensure that pool is not active
* @objRet: returned pool object
*
* Assumes @pools is locked by caller already.
*
* Returns: -1 on error (name/uuid mismatch or check_active failure)
* 0 if pool is new
* 1 if pool is a duplicate (name and UUID match)
*/
static int
virStoragePoolObjIsDuplicate(virStoragePoolObjList *pools,
virStoragePoolDef *def,
bool check_active,
virStoragePoolObj **objRet)
{
int ret = -1;
virStoragePoolObj *obj = NULL;
/* See if a Pool with matching UUID already exists */
obj = virStoragePoolObjFindByUUIDLocked(pools, def->uuid);
if (obj) {
virObjectLock(obj);
/* UUID matches, but if names don't match, refuse it */
if (STRNEQ(obj->def->name, def->name)) {
char uuidstr[VIR_UUID_STRING_BUFLEN];
virUUIDFormat(obj->def->uuid, uuidstr);
virReportError(VIR_ERR_OPERATION_FAILED,
_("pool '%s' is already defined with uuid %s"),
obj->def->name, uuidstr);
goto cleanup;
}
if (check_active) {
/* UUID & name match, but if Pool is already active, refuse it */
if (virStoragePoolObjIsActive(obj)) {
virReportError(VIR_ERR_OPERATION_INVALID,
_("pool is already active as '%s'"),
obj->def->name);
goto cleanup;
}
if (virStoragePoolObjIsStarting(obj)) {
virReportError(VIR_ERR_OPERATION_INVALID,
_("pool '%s' is starting up"),
obj->def->name);
goto cleanup;
}
}
*objRet = g_steal_pointer(&obj);
ret = 1;
} else {
/* UUID does not match, but if a name matches, refuse it */
obj = virStoragePoolObjFindByNameLocked(pools, def->name);
if (obj) {
char uuidstr[VIR_UUID_STRING_BUFLEN];
virObjectLock(obj);
virUUIDFormat(obj->def->uuid, uuidstr);
virReportError(VIR_ERR_OPERATION_FAILED,
_("pool '%s' already exists with uuid %s"),
def->name, uuidstr);
goto cleanup;
}
ret = 0;
}
cleanup:
virStoragePoolObjEndAPI(&obj);
return ret;
}
static int
getSCSIHostNumber(virStorageAdapterSCSIHost *scsi_host,
unsigned int *hostnum)
{
int ret = -1;
unsigned int num;
char *name = NULL;
if (scsi_host->has_parent) {
virPCIDeviceAddress *addr = &scsi_host->parentaddr;
unsigned int unique_id = scsi_host->unique_id;
if (!(name = virSCSIHostGetNameByParentaddr(addr->domain,
addr->bus,
addr->slot,
addr->function,
unique_id)))
goto cleanup;
if (virSCSIHostGetNumber(name, &num) < 0)
goto cleanup;
} else {
if (virSCSIHostGetNumber(scsi_host->name, &num) < 0)
goto cleanup;
}
*hostnum = num;
ret = 0;
cleanup:
VIR_FREE(name);
return ret;
}
static bool
virStorageIsSameHostnum(const char *name,
unsigned int scsi_hostnum)
{
unsigned int fc_hostnum;
if (virSCSIHostGetNumber(name, &fc_hostnum) == 0 &&
scsi_hostnum == fc_hostnum)
return true;
return false;
}
/*
* matchFCHostToSCSIHost:
*
* @fchost: fc_host adapter ptr (either def or pool->def)
* @scsi_hostnum: Already determined "scsi_pool" hostnum
*
* Returns true/false whether there is a match between the incoming
* fc_adapter host# and the scsi_host host#
*/
static bool
matchFCHostToSCSIHost(virStorageAdapterFCHost *fchost,
unsigned int scsi_hostnum)
{
virConnectPtr conn = NULL;
bool ret = false;
char *name = NULL;
char *scsi_host_name = NULL;
char *parent_name = NULL;
/* If we have a parent defined, get its hostnum, and compare to the
* scsi_hostnum. If they are the same, then we have a match
*/
if (fchost->parent &&
virStorageIsSameHostnum(fchost->parent, scsi_hostnum))
return true;
/* If we find an fc adapter name, then either libvirt created a vHBA
* for this fc_host or a 'virsh nodedev-create' generated a vHBA.
*/
if ((name = virVHBAGetHostByWWN(NULL, fchost->wwnn, fchost->wwpn))) {
/* Get the scsi_hostN for the vHBA in order to see if it
* matches our scsi_hostnum
*/
if (virStorageIsSameHostnum(name, scsi_hostnum)) {
ret = true;
goto cleanup;
}
/* We weren't provided a parent, so we have to query the node
* device driver in order to ascertain the parent of the vHBA.
* If the parent fc_hostnum is the same as the scsi_hostnum, we
* have a match.
*/
if (!fchost->parent &&
(conn = virGetConnectNodeDev())) {
scsi_host_name = g_strdup_printf("scsi_%s", name);
if ((parent_name = virNodeDeviceGetParentName(conn,
scsi_host_name))) {
if (virStorageIsSameHostnum(parent_name, scsi_hostnum)) {
ret = true;
goto cleanup;
}
} else {
/* Throw away the error and fall through */
virResetLastError();
VIR_DEBUG("Could not determine parent vHBA");
}
}
}
/* NB: Lack of a name means that this vHBA hasn't yet been created,
* which means our scsi_host cannot be using the vHBA. Furthermore,
* lack of a provided parent means libvirt is going to choose the
* "best" fc_host capable adapter based on availability. That could
* conflict with an existing scsi_host definition, but there's no
* way to know that now.
*/
cleanup:
VIR_FREE(name);
VIR_FREE(parent_name);
VIR_FREE(scsi_host_name);
virConnectClose(conn);
return ret;
}
static bool
matchSCSIAdapterParent(virStorageAdapterSCSIHost *pool_scsi_host,
virStorageAdapterSCSIHost *def_scsi_host)
{
virPCIDeviceAddress *pooladdr = &pool_scsi_host->parentaddr;
virPCIDeviceAddress *defaddr = &def_scsi_host->parentaddr;
if (pooladdr->domain == defaddr->domain &&
pooladdr->bus == defaddr->bus &&
pooladdr->slot == defaddr->slot &&
pooladdr->function == defaddr->function &&
pool_scsi_host->unique_id == def_scsi_host->unique_id)
return true;
return false;
}
static bool
virStoragePoolSourceMatchSingleHost(virStoragePoolSource *poolsrc,
virStoragePoolSource *defsrc)
{
if (poolsrc->nhost != 1 && defsrc->nhost != 1)
return false;
if (defsrc->hosts[0].port &&
poolsrc->hosts[0].port != defsrc->hosts[0].port)
return false;
return STREQ(poolsrc->hosts[0].name, defsrc->hosts[0].name);
}
static bool
virStoragePoolSourceISCSIMatch(virStoragePoolObj *obj,
virStoragePoolDef *def)
{
virStoragePoolSource *poolsrc = &obj->def->source;
virStoragePoolSource *defsrc = &def->source;
/* NB: Do not check the source host name */
if (STRNEQ_NULLABLE(poolsrc->initiator.iqn, defsrc->initiator.iqn))
return false;
return true;
}
static virStoragePoolObj *
virStoragePoolObjSourceMatchTypeDIR(virStoragePoolObj *obj,
virStoragePoolDef *def)
{
if (obj->def->type == VIR_STORAGE_POOL_DIR) {
if (STREQ(obj->def->target.path, def->target.path))
return obj;
} else if (obj->def->type == VIR_STORAGE_POOL_GLUSTER) {
if (STREQ(obj->def->source.name, def->source.name) &&
STREQ_NULLABLE(obj->def->source.dir, def->source.dir) &&
virStoragePoolSourceMatchSingleHost(&obj->def->source,
&def->source))
return obj;
} else if (obj->def->type == VIR_STORAGE_POOL_NETFS) {
if (STREQ(obj->def->source.dir, def->source.dir) &&
virStoragePoolSourceMatchSingleHost(&obj->def->source,
&def->source))
return obj;
}
return NULL;
}
static virStoragePoolObj *
virStoragePoolObjSourceMatchTypeISCSI(virStoragePoolObj *obj,
virStoragePoolDef *def)
{
virStorageAdapter *pool_adapter = &obj->def->source.adapter;
virStorageAdapter *def_adapter = &def->source.adapter;
virStorageAdapterSCSIHost *pool_scsi_host;
virStorageAdapterSCSIHost *def_scsi_host;
virStorageAdapterFCHost *pool_fchost;
virStorageAdapterFCHost *def_fchost;
unsigned int pool_hostnum;
unsigned int def_hostnum;
unsigned int scsi_hostnum;
if (pool_adapter->type == VIR_STORAGE_ADAPTER_TYPE_FC_HOST &&
def_adapter->type == VIR_STORAGE_ADAPTER_TYPE_FC_HOST) {
pool_fchost = &pool_adapter->data.fchost;
def_fchost = &def_adapter->data.fchost;
if (STREQ(pool_fchost->wwnn, def_fchost->wwnn) &&
STREQ(pool_fchost->wwpn, def_fchost->wwpn))
return obj;
} else if (pool_adapter->type == VIR_STORAGE_ADAPTER_TYPE_SCSI_HOST &&
def_adapter->type == VIR_STORAGE_ADAPTER_TYPE_SCSI_HOST) {
pool_scsi_host = &pool_adapter->data.scsi_host;
def_scsi_host = &def_adapter->data.scsi_host;
if (pool_scsi_host->has_parent &&
def_scsi_host->has_parent &&
matchSCSIAdapterParent(pool_scsi_host, def_scsi_host))
return obj;
if (getSCSIHostNumber(pool_scsi_host, &pool_hostnum) < 0 ||
getSCSIHostNumber(def_scsi_host, &def_hostnum) < 0)
return NULL;
if (pool_hostnum == def_hostnum)
return obj;
} else if (pool_adapter->type == VIR_STORAGE_ADAPTER_TYPE_FC_HOST &&
def_adapter->type == VIR_STORAGE_ADAPTER_TYPE_SCSI_HOST) {
pool_fchost = &pool_adapter->data.fchost;
def_scsi_host = &def_adapter->data.scsi_host;
/* Get the scsi_hostN for the scsi_host source adapter def */
if (getSCSIHostNumber(def_scsi_host, &scsi_hostnum) < 0)
return NULL;
if (matchFCHostToSCSIHost(pool_fchost, scsi_hostnum))
return obj;
} else if (pool_adapter->type == VIR_STORAGE_ADAPTER_TYPE_SCSI_HOST &&
def_adapter->type == VIR_STORAGE_ADAPTER_TYPE_FC_HOST) {
pool_scsi_host = &pool_adapter->data.scsi_host;
def_fchost = &def_adapter->data.fchost;
if (getSCSIHostNumber(pool_scsi_host, &scsi_hostnum) < 0)
return NULL;
if (matchFCHostToSCSIHost(def_fchost, scsi_hostnum))
return obj;
}
return NULL;
}
static virStoragePoolObj *
virStoragePoolObjSourceMatchTypeDEVICE(virStoragePoolObj *obj,
virStoragePoolDef *def)
{
virStoragePoolObj *matchobj = NULL;
if (obj->def->type == VIR_STORAGE_POOL_ISCSI) {
if (def->type != VIR_STORAGE_POOL_ISCSI)
return NULL;
if ((matchobj = virStoragePoolSourceFindDuplicateDevices(obj, def))) {
if (!virStoragePoolSourceISCSIMatch(matchobj, def))
return NULL;
}
return matchobj;
}
if (def->type == VIR_STORAGE_POOL_ISCSI)
return NULL;
/* VIR_STORAGE_POOL_FS
* VIR_STORAGE_POOL_LOGICAL
* VIR_STORAGE_POOL_DISK
* VIR_STORAGE_POOL_ZFS */
return virStoragePoolSourceFindDuplicateDevices(obj, def);
}
struct _virStoragePoolObjFindDuplicateData {
virStoragePoolDef *def;
};
static int
virStoragePoolObjSourceFindDuplicateCb(const void *payload,
const char *name G_GNUC_UNUSED,
const void *opaque)
{
virStoragePoolObj *obj = (virStoragePoolObj *) payload;
struct _virStoragePoolObjFindDuplicateData *data =
(struct _virStoragePoolObjFindDuplicateData *) opaque;
/* Don't match against ourself if re-defining existing pool ! */
if (STREQ(obj->def->name, data->def->name))
return 0;
switch ((virStoragePoolType)obj->def->type) {
case VIR_STORAGE_POOL_DIR:
case VIR_STORAGE_POOL_GLUSTER:
case VIR_STORAGE_POOL_NETFS:
if (data->def->type == obj->def->type &&
virStoragePoolObjSourceMatchTypeDIR(obj, data->def))
return 1;
break;
case VIR_STORAGE_POOL_SCSI:
if (data->def->type == obj->def->type &&
virStoragePoolObjSourceMatchTypeISCSI(obj, data->def))
return 1;
break;
case VIR_STORAGE_POOL_ISCSI:
case VIR_STORAGE_POOL_FS:
case VIR_STORAGE_POOL_LOGICAL:
case VIR_STORAGE_POOL_DISK:
case VIR_STORAGE_POOL_ZFS:
if ((data->def->type == VIR_STORAGE_POOL_ISCSI ||
data->def->type == VIR_STORAGE_POOL_FS ||
data->def->type == VIR_STORAGE_POOL_LOGICAL ||
data->def->type == VIR_STORAGE_POOL_DISK ||
data->def->type == VIR_STORAGE_POOL_ZFS) &&
virStoragePoolObjSourceMatchTypeDEVICE(obj, data->def))
return 1;
break;
case VIR_STORAGE_POOL_SHEEPDOG:
if (data->def->type == obj->def->type &&
virStoragePoolSourceMatchSingleHost(&obj->def->source,
&data->def->source))
return 1;
break;
case VIR_STORAGE_POOL_MPATH:
/* Only one mpath pool is valid per host */
if (data->def->type == obj->def->type)
return 1;
break;
case VIR_STORAGE_POOL_VSTORAGE:
if (data->def->type == obj->def->type &&
STREQ(obj->def->source.name, data->def->source.name))
return 1;
break;
case VIR_STORAGE_POOL_ISCSI_DIRECT:
case VIR_STORAGE_POOL_RBD:
case VIR_STORAGE_POOL_LAST:
break;
}
return 0;
}
static int
virStoragePoolObjSourceFindDuplicate(virStoragePoolObjList *pools,
virStoragePoolDef *def)
{
struct _virStoragePoolObjFindDuplicateData data = {.def = def};
virStoragePoolObj *obj = NULL;
obj = virHashSearch(pools->objs, virStoragePoolObjSourceFindDuplicateCb,
&data, NULL);
if (obj) {
virReportError(VIR_ERR_OPERATION_FAILED,
_("Storage source conflict with pool: '%s'"),
obj->def->name);
return -1;
}
return 0;
}
static void
virStoragePoolObjAssignDef(virStoragePoolObj *obj,
virStoragePoolDef **def,
unsigned int flags)
{
if (virStoragePoolObjIsActive(obj) ||
virStoragePoolObjIsStarting(obj)) {
virStoragePoolDefFree(obj->newDef);
obj->newDef = g_steal_pointer(def);
} else {
if (!obj->newDef &&
flags & VIR_STORAGE_POOL_OBJ_LIST_ADD_LIVE)
obj->newDef = g_steal_pointer(&obj->def);
virStoragePoolDefFree(obj->def);
obj->def = g_steal_pointer(def);
}
}
/**
* virStoragePoolObjListAdd:
* @pools: Storage Pool object list pointer
* @def: Storage pool definition to add or update
* @flags: bitwise-OR of VIR_STORAGE_POOL_OBJ_LIST_* flags
*
* Lookup the @def to see if it already exists in the @pools in order
* to either update or add if it does not exist.
*
* Use VIR_STORAGE_POOL_OBJ_LIST_ADD_LIVE to denote that @def
* refers to an active definition and thus any possible inactive
* definition found should be saved to ->newDef (in case of
* future restore).
*
* If VIR_STORAGE_POOL_OBJ_LIST_ADD_CHECK_LIVE is set in @flags
* then this will fail if the pool exists and is active.
*
* Upon successful return the virStoragePool object is the owner
* of @def and callers should use virStoragePoolObjGetDef() or
* virStoragePoolObjGetNewDef() if they need to access the
* definition as @def is set to NULL.
*
* Returns locked and reffed object pointer or NULL on error
*/
virStoragePoolObj *
virStoragePoolObjListAdd(virStoragePoolObjList *pools,
virStoragePoolDef **def,
unsigned int flags)
{
virStoragePoolObj *obj = NULL;
char uuidstr[VIR_UUID_STRING_BUFLEN];
int rc;
virObjectRWLockWrite(pools);
if (virStoragePoolObjSourceFindDuplicate(pools, *def) < 0)
goto error;
rc = virStoragePoolObjIsDuplicate(pools, *def,
!!(flags & VIR_STORAGE_POOL_OBJ_LIST_ADD_CHECK_LIVE),
&obj);
if (rc < 0)
goto error;
if (rc > 0) {
virStoragePoolObjAssignDef(obj, def, flags);
virObjectRWUnlock(pools);
return obj;
}
if (!(obj = virStoragePoolObjNew()))
goto error;
virUUIDFormat((*def)->uuid, uuidstr);
if (virHashAddEntry(pools->objs, uuidstr, obj) < 0)
goto error;
virObjectRef(obj);
if (virHashAddEntry(pools->objsName, (*def)->name, obj) < 0) {
virHashRemoveEntry(pools->objs, uuidstr);
goto error;
}
virObjectRef(obj);
obj->def = g_steal_pointer(def);
virObjectRWUnlock(pools);
return obj;
error:
virStoragePoolObjEndAPI(&obj);
virObjectRWUnlock(pools);
return NULL;
}
static virStoragePoolObj *
virStoragePoolObjLoad(virStoragePoolObjList *pools,
const char *file,
const char *path,
const char *autostartLink)
{
virStoragePoolObj *obj;
g_autoptr(virStoragePoolDef) def = NULL;
VIR_DEBUG("loading storage pool config XML '%s'", path);
if (!(def = virStoragePoolDefParseFile(path)))
return NULL;
if (!virStringMatchesNameSuffix(file, def->name, ".xml")) {
virReportError(VIR_ERR_XML_ERROR,
_("Storage pool config filename '%s' does "
"not match pool name '%s'"),
path, def->name);
return NULL;
}
if (!(obj = virStoragePoolObjListAdd(pools, &def, 0)))
return NULL;
VIR_FREE(obj->configFile); /* for driver reload */
obj->configFile = g_strdup(path);
VIR_FREE(obj->autostartLink); /* for driver reload */
obj->autostartLink = g_strdup(autostartLink);
obj->autostart = virFileLinkPointsTo(obj->autostartLink,
obj->configFile);
return obj;
}
static virStoragePoolObj *
virStoragePoolObjLoadState(virStoragePoolObjList *pools,
const char *stateDir,
const char *name)
{
g_autofree char *stateFile = NULL;
virStoragePoolObj *obj = NULL;
g_autoptr(xmlDoc) xml = NULL;
g_autoptr(xmlXPathContext) ctxt = NULL;
xmlNodePtr node = NULL;
g_autoptr(virStoragePoolDef) def = NULL;
if (!(stateFile = virFileBuildPath(stateDir, name, ".xml")))
return NULL;
VIR_DEBUG("loading storage pool state XML '%s'", stateFile);
if (!(xml = virXMLParseCtxt(stateFile, NULL, _("(pool state)"), &ctxt)))
return NULL;
if (!(node = virXPathNode("//pool", ctxt))) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("Could not find any 'pool' element in state file"));
return NULL;
}
ctxt->node = node;
if (!(def = virStoragePoolDefParseXML(ctxt)))
return NULL;
if (STRNEQ(name, def->name)) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Storage pool state file '%s' does not match "
"pool name '%s'"),
stateFile, def->name);
return NULL;
}
/* create the object */
if (!(obj = virStoragePoolObjListAdd(pools, &def,
VIR_STORAGE_POOL_OBJ_LIST_ADD_CHECK_LIVE)))
return NULL;
/* XXX: future handling of some additional useful status data,
* for now, if a status file for a pool exists, the pool will be marked
* as active
*/
obj->active = true;
return obj;
}
int
virStoragePoolObjLoadAllState(virStoragePoolObjList *pools,
const char *stateDir)
{
g_autoptr(DIR) dir = NULL;
struct dirent *entry;
int ret = -1;
int rc;
if ((rc = virDirOpenIfExists(&dir, stateDir)) <= 0)
return rc;
while ((ret = virDirRead(dir, &entry, stateDir)) > 0) {
virStoragePoolObj *obj;
if (!virStringStripSuffix(entry->d_name, ".xml"))
continue;
if (!(obj = virStoragePoolObjLoadState(pools, stateDir, entry->d_name)))
continue;
virStoragePoolObjEndAPI(&obj);
}
return ret;
}
int
virStoragePoolObjLoadAllConfigs(virStoragePoolObjList *pools,
const char *configDir,
const char *autostartDir)
{
g_autoptr(DIR) dir = NULL;
struct dirent *entry;
int ret;
int rc;
if ((rc = virDirOpenIfExists(&dir, configDir)) <= 0)
return rc;
while ((ret = virDirRead(dir, &entry, configDir)) > 0) {
g_autofree char *path = virFileBuildPath(configDir, entry->d_name, NULL);
g_autofree char *autostartLink = virFileBuildPath(autostartDir, entry->d_name, NULL);
virStoragePoolObj *obj;
if (!virStringHasSuffix(entry->d_name, ".xml"))
continue;
obj = virStoragePoolObjLoad(pools, entry->d_name, path, autostartLink);
virStoragePoolObjEndAPI(&obj);
}
return ret;
}
int
virStoragePoolObjSaveDef(virStorageDriverState *driver,
virStoragePoolObj *obj,
virStoragePoolDef *def)
{
if (!obj->configFile) {
if (g_mkdir_with_parents(driver->configDir, 0777) < 0) {
virReportSystemError(errno,
_("cannot create config directory %s"),
driver->configDir);
return -1;
}
if (!(obj->configFile = virFileBuildPath(driver->configDir,
def->name, ".xml"))) {
return -1;
}
if (!(obj->autostartLink = virFileBuildPath(driver->autostartDir,
def->name, ".xml"))) {
VIR_FREE(obj->configFile);
return -1;
}
}
return virStoragePoolSaveConfig(obj->configFile, def);
}
int
virStoragePoolObjDeleteDef(virStoragePoolObj *obj)
{
if (!obj->configFile) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("no config file for %s"), obj->def->name);
return -1;
}
if (unlink(obj->configFile) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("cannot remove config for %s"),
obj->def->name);
return -1;
}
return 0;
}
struct _virStoragePoolCountData {
virConnectPtr conn;
virStoragePoolObjListACLFilter filter;
bool wantActive;
int count;
};
static int
virStoragePoolObjNumOfStoragePoolsCb(void *payload,
const char *name G_GNUC_UNUSED,
void *opaque)
{
virStoragePoolObj *obj = payload;
struct _virStoragePoolCountData *data = opaque;
virObjectLock(obj);
if (data->filter && !data->filter(data->conn, obj->def))
goto cleanup;
if (data->wantActive != virStoragePoolObjIsActive(obj))
goto cleanup;
data->count++;
cleanup:
virObjectUnlock(obj);
return 0;
}
int
virStoragePoolObjNumOfStoragePools(virStoragePoolObjList *pools,
virConnectPtr conn,
bool wantActive,
virStoragePoolObjListACLFilter filter)
{
struct _virStoragePoolCountData data = {
.conn = conn, .filter = filter, .wantActive = wantActive, .count = 0 };
virObjectRWLockRead(pools);
virHashForEach(pools->objs, virStoragePoolObjNumOfStoragePoolsCb, &data);
virObjectRWUnlock(pools);
return data.count;
}
struct _virStoragePoolNameData {
virConnectPtr conn;
virStoragePoolObjListACLFilter filter;
bool wantActive;
bool error;
int nnames;
int maxnames;
char **const names;
};
static int
virStoragePoolObjGetNamesCb(void *payload,
const char *name G_GNUC_UNUSED,
void *opaque)
{
virStoragePoolObj *obj = payload;
struct _virStoragePoolNameData *data = opaque;
if (data->error)
return 0;
if (data->maxnames >= 0 && data->nnames == data->maxnames)
return 0;
virObjectLock(obj);
if (data->filter && !data->filter(data->conn, obj->def))
goto cleanup;
if (data->wantActive != virStoragePoolObjIsActive(obj))
goto cleanup;
if (data->names)
data->names[data->nnames] = g_strdup(obj->def->name);
data->nnames++;
cleanup:
virObjectUnlock(obj);
return 0;
}
int
virStoragePoolObjGetNames(virStoragePoolObjList *pools,
virConnectPtr conn,
bool wantActive,
virStoragePoolObjListACLFilter filter,
char **const names,
int maxnames)
{
struct _virStoragePoolNameData data = {
.conn = conn, .filter = filter, .wantActive = wantActive,
.error = false, .nnames = 0, .maxnames = maxnames, .names = names };
virObjectRWLockRead(pools);
virHashForEach(pools->objs, virStoragePoolObjGetNamesCb, &data);
virObjectRWUnlock(pools);
if (data.error)
goto error;
return data.nnames;
error:
while (data.nnames)
VIR_FREE(data.names[--data.nnames]);
return -1;
}
#define MATCH(FLAG) (flags & (FLAG))
static bool
virStoragePoolObjMatch(virStoragePoolObj *obj,
unsigned int flags)
{
/* filter by active state */
if (MATCH(VIR_CONNECT_LIST_STORAGE_POOLS_FILTERS_ACTIVE) &&
!((MATCH(VIR_CONNECT_LIST_STORAGE_POOLS_ACTIVE) &&
virStoragePoolObjIsActive(obj)) ||
(MATCH(VIR_CONNECT_LIST_STORAGE_POOLS_INACTIVE) &&
!virStoragePoolObjIsActive(obj))))
return false;
/* filter by persistence */
if (MATCH(VIR_CONNECT_LIST_STORAGE_POOLS_FILTERS_PERSISTENT) &&
!((MATCH(VIR_CONNECT_LIST_STORAGE_POOLS_PERSISTENT) &&
obj->configFile) ||
(MATCH(VIR_CONNECT_LIST_STORAGE_POOLS_TRANSIENT) &&
!obj->configFile)))
return false;
/* filter by autostart option */
if (MATCH(VIR_CONNECT_LIST_STORAGE_POOLS_FILTERS_AUTOSTART) &&
!((MATCH(VIR_CONNECT_LIST_STORAGE_POOLS_AUTOSTART) &&
obj->autostart) ||
(MATCH(VIR_CONNECT_LIST_STORAGE_POOLS_NO_AUTOSTART) &&
!obj->autostart)))
return false;
/* filter by pool type */
if (MATCH(VIR_CONNECT_LIST_STORAGE_POOLS_FILTERS_POOL_TYPE)) {
if (!((MATCH(VIR_CONNECT_LIST_STORAGE_POOLS_DIR) &&
(obj->def->type == VIR_STORAGE_POOL_DIR)) ||
(MATCH(VIR_CONNECT_LIST_STORAGE_POOLS_FS) &&
(obj->def->type == VIR_STORAGE_POOL_FS)) ||
(MATCH(VIR_CONNECT_LIST_STORAGE_POOLS_NETFS) &&
(obj->def->type == VIR_STORAGE_POOL_NETFS)) ||
(MATCH(VIR_CONNECT_LIST_STORAGE_POOLS_LOGICAL) &&
(obj->def->type == VIR_STORAGE_POOL_LOGICAL)) ||
(MATCH(VIR_CONNECT_LIST_STORAGE_POOLS_DISK) &&
(obj->def->type == VIR_STORAGE_POOL_DISK)) ||
(MATCH(VIR_CONNECT_LIST_STORAGE_POOLS_ISCSI) &&
(obj->def->type == VIR_STORAGE_POOL_ISCSI)) ||
(MATCH(VIR_CONNECT_LIST_STORAGE_POOLS_SCSI) &&
(obj->def->type == VIR_STORAGE_POOL_SCSI)) ||
(MATCH(VIR_CONNECT_LIST_STORAGE_POOLS_MPATH) &&
(obj->def->type == VIR_STORAGE_POOL_MPATH)) ||
(MATCH(VIR_CONNECT_LIST_STORAGE_POOLS_RBD) &&
(obj->def->type == VIR_STORAGE_POOL_RBD)) ||
(MATCH(VIR_CONNECT_LIST_STORAGE_POOLS_SHEEPDOG) &&
(obj->def->type == VIR_STORAGE_POOL_SHEEPDOG)) ||
(MATCH(VIR_CONNECT_LIST_STORAGE_POOLS_GLUSTER) &&
(obj->def->type == VIR_STORAGE_POOL_GLUSTER)) ||
(MATCH(VIR_CONNECT_LIST_STORAGE_POOLS_ZFS) &&
(obj->def->type == VIR_STORAGE_POOL_ZFS)) ||
(MATCH(VIR_CONNECT_LIST_STORAGE_POOLS_VSTORAGE) &&
(obj->def->type == VIR_STORAGE_POOL_VSTORAGE)) ||
(MATCH(VIR_CONNECT_LIST_STORAGE_POOLS_ISCSI_DIRECT) &&
(obj->def->type == VIR_STORAGE_POOL_ISCSI_DIRECT))))
return false;
}
return true;
}
#undef MATCH
typedef struct _virStoragePoolObjListExportData virStoragePoolObjListExportData;
struct _virStoragePoolObjListExportData {
virConnectPtr conn;
virStoragePoolObjListACLFilter filter;
bool checkActive;
bool wantActive;
bool checkMatch;
unsigned int flags;
bool error;
int nPools;
virStoragePoolPtr *pools;
};
static int
virStoragePoolObjListExportCallback(void *payload,
const char *name G_GNUC_UNUSED,
void *opaque)
{
virStoragePoolObj *obj = payload;
virStoragePoolObjListExportData *data = opaque;
virStoragePoolPtr pool = NULL;
if (data->error)
return 0;
virObjectLock(obj);
if (data->filter && !data->filter(data->conn, obj->def))
goto cleanup;
if (!virStoragePoolObjMatch(obj, data->flags))
goto cleanup;
if (data->pools) {
if (!(pool = virGetStoragePool(data->conn, obj->def->name,
obj->def->uuid, NULL, NULL))) {
data->error = true;
goto cleanup;
}
data->pools[data->nPools] = pool;
}
data->nPools++;
cleanup:
virObjectUnlock(obj);
return 0;
}
int
virStoragePoolObjListExport(virConnectPtr conn,
virStoragePoolObjList *poolobjs,
virStoragePoolPtr **pools,
virStoragePoolObjListFilter filter,
unsigned int flags)
{
virStoragePoolObjListExportData data = {
.conn = conn, .filter = filter, .flags = flags, .error = false,
.nPools = 0, .pools = NULL };
virObjectRWLockRead(poolobjs);
if (!pools) {
int ret = virHashSize(poolobjs->objs);
virObjectRWUnlock(poolobjs);
return ret;
}
data.pools = g_new0(virStoragePoolPtr, virHashSize(poolobjs->objs) + 1);
virHashForEach(poolobjs->objs, virStoragePoolObjListExportCallback, &data);
virObjectRWUnlock(poolobjs);
if (data.error)
goto error;
if (data.pools) {
/* trim the array to the final size */
VIR_REALLOC_N(data.pools, data.nPools + 1);
*pools = data.pools;
}
return data.nPools;
error:
virObjectListFree(data.pools);
return -1;
}