/* * 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 * . */ #include #include #include "datatypes.h" #include "node_device_conf.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 virClassPtr virStoragePoolObjClass; static virClassPtr virStoragePoolObjListClass; static void virStoragePoolObjDispose(void *opaque); static void virStoragePoolObjListDispose(void *opaque); struct _virStorageVolDefList { size_t count; virStorageVolDefPtr *objs; }; struct _virStoragePoolObj { virObjectLockable parent; char *configFile; char *autostartLink; bool active; bool autostart; unsigned int asyncjobs; virStoragePoolDefPtr def; virStoragePoolDefPtr newDef; virStorageVolDefList volumes; }; struct _virStoragePoolObjList { virObjectRWLockable parent; /* uuid string -> virStoragePoolObj mapping * for (1), lockless lookup-by-uuid */ virHashTable *objs; /* name string -> virStoragePoolObj mapping * for (1), lockless lookup-by-name */ virHashTable *objsName; }; static int virStoragePoolObjOnceInit(void) { if (!(virStoragePoolObjClass = virClassNew(virClassForObjectLockable(), "virStoragePoolObj", sizeof(virStoragePoolObj), virStoragePoolObjDispose))) return -1; if (!(virStoragePoolObjListClass = virClassNew(virClassForObjectRWLockable(), "virStoragePoolObjList", sizeof(virStoragePoolObjList), virStoragePoolObjListDispose))) return -1; return 0; } VIR_ONCE_GLOBAL_INIT(virStoragePoolObj) virStoragePoolObjPtr virStoragePoolObjNew(void) { virStoragePoolObjPtr obj; if (virStoragePoolObjInitialize() < 0) return NULL; if (!(obj = virObjectLockableNew(virStoragePoolObjClass))) return NULL; virObjectLock(obj); obj->active = false; return obj; } void virStoragePoolObjEndAPI(virStoragePoolObjPtr *obj) { if (!*obj) return; virObjectUnlock(*obj); virObjectUnref(*obj); *obj = NULL; } virStoragePoolDefPtr virStoragePoolObjGetDef(virStoragePoolObjPtr obj) { return obj->def; } void virStoragePoolObjSetDef(virStoragePoolObjPtr obj, virStoragePoolDefPtr def) { virStoragePoolDefFree(obj->def); obj->def = def; } virStoragePoolDefPtr virStoragePoolObjGetNewDef(virStoragePoolObjPtr obj) { return obj->newDef; } void virStoragePoolObjDefUseNewDef(virStoragePoolObjPtr obj) { virStoragePoolDefFree(obj->def); obj->def = obj->newDef; obj->newDef = NULL; } const char * virStoragePoolObjGetConfigFile(virStoragePoolObjPtr obj) { return obj->configFile; } void virStoragePoolObjSetConfigFile(virStoragePoolObjPtr obj, char *configFile) { VIR_FREE(obj->configFile); obj->configFile = configFile; } const char * virStoragePoolObjGetAutostartLink(virStoragePoolObjPtr obj) { return obj->autostartLink; } bool virStoragePoolObjIsActive(virStoragePoolObjPtr obj) { return obj->active; } void virStoragePoolObjSetActive(virStoragePoolObjPtr obj, bool active) { obj->active = active; } bool virStoragePoolObjIsAutostart(virStoragePoolObjPtr obj) { if (!obj->configFile) return 0; return obj->autostart; } void virStoragePoolObjSetAutostart(virStoragePoolObjPtr obj, bool autostart) { obj->autostart = autostart; } unsigned int virStoragePoolObjGetAsyncjobs(virStoragePoolObjPtr obj) { return obj->asyncjobs; } void virStoragePoolObjIncrAsyncjobs(virStoragePoolObjPtr obj) { obj->asyncjobs++; } void virStoragePoolObjDecrAsyncjobs(virStoragePoolObjPtr obj) { obj->asyncjobs--; } void virStoragePoolObjDispose(void *opaque) { virStoragePoolObjPtr obj = opaque; if (!obj) return; virStoragePoolObjClearVols(obj); virStoragePoolDefFree(obj->def); virStoragePoolDefFree(obj->newDef); VIR_FREE(obj->configFile); VIR_FREE(obj->autostartLink); } void virStoragePoolObjListDispose(void *opaque) { virStoragePoolObjListPtr pools = opaque; virHashFree(pools->objs); virHashFree(pools->objsName); } virStoragePoolObjListPtr virStoragePoolObjListNew(void) { virStoragePoolObjListPtr pools; if (virStoragePoolObjInitialize() < 0) return NULL; if (!(pools = virObjectRWLockableNew(virStoragePoolObjListClass))) return NULL; if (!(pools->objs = virHashCreate(20, virObjectFreeHashData)) || !(pools->objsName = virHashCreate(20, virObjectFreeHashData))) { virObjectUnref(pools); return NULL; } return pools; } struct _virStoragePoolObjListForEachData { virStoragePoolObjListIterator iter; const void *opaque; }; static int virStoragePoolObjListForEachCb(void *payload, const void *name ATTRIBUTE_UNUSED, void *opaque) { virStoragePoolObjPtr obj = payload; struct _virStoragePoolObjListForEachData *data = opaque; virObjectLock(obj); data->iter(obj, data->opaque); virObjectUnlock(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. */ void virStoragePoolObjListForEach(virStoragePoolObjListPtr pools, virStoragePoolObjListIterator iter, const void *opaque) { struct _virStoragePoolObjListForEachData data = { .iter = iter, .opaque = opaque }; virObjectRWLockRead(pools); virHashForEach(pools->objs, virStoragePoolObjListForEachCb, &data); virObjectRWUnlock(pools); } struct _virStoragePoolObjListSearchData { virStoragePoolObjListSearcher searcher; const void *opaque; }; static int virStoragePoolObjListSearchCb(const void *payload, const void *name ATTRIBUTE_UNUSED, const void *opaque) { virStoragePoolObjPtr obj = (virStoragePoolObjPtr) 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 */ virStoragePoolObjPtr virStoragePoolObjListSearch(virStoragePoolObjListPtr pools, virStoragePoolObjListSearcher searcher, const void *opaque) { virStoragePoolObjPtr 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(virStoragePoolObjListPtr pools, virStoragePoolObjPtr 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); virObjectUnlock(obj); virObjectUnref(obj); virObjectRWUnlock(pools); } static virStoragePoolObjPtr virStoragePoolObjFindByUUIDLocked(virStoragePoolObjListPtr 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 */ virStoragePoolObjPtr virStoragePoolObjFindByUUID(virStoragePoolObjListPtr pools, const unsigned char *uuid) { virStoragePoolObjPtr obj; virObjectRWLockRead(pools); obj = virStoragePoolObjFindByUUIDLocked(pools, uuid); virObjectRWUnlock(pools); if (obj) virObjectLock(obj); return obj; } static virStoragePoolObjPtr virStoragePoolObjFindByNameLocked(virStoragePoolObjListPtr 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 */ virStoragePoolObjPtr virStoragePoolObjFindByName(virStoragePoolObjListPtr pools, const char *name) { virStoragePoolObjPtr obj; virObjectRWLockRead(pools); obj = virStoragePoolObjFindByNameLocked(pools, name); virObjectRWUnlock(pools); if (obj) virObjectLock(obj); return obj; } static virStoragePoolObjPtr virStoragePoolSourceFindDuplicateDevices(virStoragePoolObjPtr obj, virStoragePoolDefPtr 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(virStoragePoolObjPtr obj) { size_t i; for (i = 0; i < obj->volumes.count; i++) virStorageVolDefFree(obj->volumes.objs[i]); VIR_FREE(obj->volumes.objs); obj->volumes.count = 0; } int virStoragePoolObjAddVol(virStoragePoolObjPtr obj, virStorageVolDefPtr voldef) { if (VIR_APPEND_ELEMENT(obj->volumes.objs, obj->volumes.count, voldef) < 0) return -1; return 0; } void virStoragePoolObjRemoveVol(virStoragePoolObjPtr obj, virStorageVolDefPtr voldef) { virStoragePoolDefPtr def = virStoragePoolObjGetDef(obj); size_t i; for (i = 0; i < obj->volumes.count; i++) { if (obj->volumes.objs[i] == voldef) { VIR_INFO("Deleting volume '%s' from storage pool '%s'", voldef->name, def->name); virStorageVolDefFree(voldef); VIR_DELETE_ELEMENT(obj->volumes.objs, i, obj->volumes.count); return; } } } size_t virStoragePoolObjGetVolumesCount(virStoragePoolObjPtr obj) { return obj->volumes.count; } int virStoragePoolObjForEachVolume(virStoragePoolObjPtr obj, virStorageVolObjListIterator iter, const void *opaque) { size_t i; for (i = 0; i < obj->volumes.count; i++) { if (iter(obj->volumes.objs[i], opaque) < 0) return -1; } return 0; } virStorageVolDefPtr virStoragePoolObjSearchVolume(virStoragePoolObjPtr obj, virStorageVolObjListSearcher iter, const void *opaque) { size_t i; for (i = 0; i < obj->volumes.count; i++) { if (iter(obj->volumes.objs[i], opaque)) return obj->volumes.objs[i]; } return NULL; } virStorageVolDefPtr virStorageVolDefFindByKey(virStoragePoolObjPtr obj, const char *key) { size_t i; for (i = 0; i < obj->volumes.count; i++) if (STREQ(obj->volumes.objs[i]->key, key)) return obj->volumes.objs[i]; return NULL; } virStorageVolDefPtr virStorageVolDefFindByPath(virStoragePoolObjPtr obj, const char *path) { size_t i; for (i = 0; i < obj->volumes.count; i++) if (STREQ(obj->volumes.objs[i]->target.path, path)) return obj->volumes.objs[i]; return NULL; } virStorageVolDefPtr virStorageVolDefFindByName(virStoragePoolObjPtr obj, const char *name) { size_t i; for (i = 0; i < obj->volumes.count; i++) if (STREQ(obj->volumes.objs[i]->name, name)) return obj->volumes.objs[i]; return NULL; } int virStoragePoolObjNumOfVolumes(virStoragePoolObjPtr obj, virConnectPtr conn, virStoragePoolVolumeACLFilter filter) { virStoragePoolDefPtr pooldef = obj->def; virStorageVolDefListPtr volumes = &obj->volumes; int nvolumes = 0; size_t i; for (i = 0; i < volumes->count; i++) { virStorageVolDefPtr def = volumes->objs[i]; if (filter && !filter(conn, pooldef, def)) continue; nvolumes++; } return nvolumes; } int virStoragePoolObjVolumeGetNames(virStoragePoolObjPtr obj, virConnectPtr conn, virStoragePoolVolumeACLFilter filter, char **const names, int maxnames) { virStoragePoolDefPtr pooldef = obj->def; virStorageVolDefListPtr volumes = &obj->volumes; int nnames = 0; size_t i; for (i = 0; i < volumes->count && nnames < maxnames; i++) { virStorageVolDefPtr def = volumes->objs[i]; if (filter && !filter(conn, pooldef, def)) continue; if (VIR_STRDUP(names[nnames], def->name) < 0) goto failure; nnames++; } return nnames; failure: while (--nnames >= 0) VIR_FREE(names[nnames]); return -1; } int virStoragePoolObjVolumeListExport(virConnectPtr conn, virStoragePoolObjPtr obj, virStorageVolPtr **vols, virStoragePoolVolumeACLFilter filter) { virStoragePoolDefPtr pooldef = obj->def; virStorageVolDefListPtr volumes = &obj->volumes; int ret = -1; size_t i; virStorageVolPtr *tmp_vols = NULL; virStorageVolPtr vol = NULL; int nvols = 0; /* Just returns the volumes count */ if (!vols) { ret = volumes->count; goto cleanup; } if (VIR_ALLOC_N(tmp_vols, volumes->count + 1) < 0) goto cleanup; for (i = 0; i < volumes->count; i++) { virStorageVolDefPtr def = volumes->objs[i]; if (filter && !filter(conn, pooldef, def)) continue; if (!(vol = virGetStorageVol(conn, pooldef->name, def->name, def->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++) virObjectUnref(tmp_vols[i]); VIR_FREE(tmp_vols); } return ret; } /** * virStoragePoolObjAssignDef: * @pools: Storage Pool object list pointer * @def: Storage pool definition to add or update * * Lookup the @def to see if it already exists in the @pools in order * to either update or add if it does not exist. * * Returns locked and reffed object pointer or NULL on error */ virStoragePoolObjPtr virStoragePoolObjAssignDef(virStoragePoolObjListPtr pools, virStoragePoolDefPtr def) { virStoragePoolObjPtr obj; char uuidstr[VIR_UUID_STRING_BUFLEN]; virObjectRWLockWrite(pools); if ((obj = virStoragePoolObjFindByNameLocked(pools, def->name))) { virObjectLock(obj); if (!virStoragePoolObjIsActive(obj)) { virStoragePoolDefFree(obj->def); obj->def = def; } else { virStoragePoolDefFree(obj->newDef); obj->newDef = def; } virObjectRWUnlock(pools); return obj; } if (!(obj = virStoragePoolObjNew())) return NULL; 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 = def; virObjectRWUnlock(pools); return obj; error: virStoragePoolObjEndAPI(&obj); virObjectRWUnlock(pools); return NULL; } static virStoragePoolObjPtr virStoragePoolObjLoad(virStoragePoolObjListPtr pools, const char *file, const char *path, const char *autostartLink) { virStoragePoolDefPtr def; virStoragePoolObjPtr obj; if (!(def = virStoragePoolDefParseFile(path))) return NULL; if (!virFileMatchesNameSuffix(file, def->name, ".xml")) { virReportError(VIR_ERR_XML_ERROR, _("Storage pool config filename '%s' does " "not match pool name '%s'"), path, def->name); virStoragePoolDefFree(def); return NULL; } if (!(obj = virStoragePoolObjAssignDef(pools, def))) { virStoragePoolDefFree(def); return NULL; } VIR_FREE(obj->configFile); /* for driver reload */ if (VIR_STRDUP(obj->configFile, path) < 0) { virStoragePoolObjRemove(pools, obj); virObjectUnref(obj); return NULL; } VIR_FREE(obj->autostartLink); /* for driver reload */ if (VIR_STRDUP(obj->autostartLink, autostartLink) < 0) { virStoragePoolObjRemove(pools, obj); virObjectUnref(obj); return NULL; } obj->autostart = virFileLinkPointsTo(obj->autostartLink, obj->configFile); return obj; } static virStoragePoolObjPtr virStoragePoolObjLoadState(virStoragePoolObjListPtr pools, const char *stateDir, const char *name) { char *stateFile = NULL; virStoragePoolDefPtr def = NULL; virStoragePoolObjPtr obj = NULL; xmlDocPtr xml = NULL; xmlXPathContextPtr ctxt = NULL; xmlNodePtr node = NULL; if (!(stateFile = virFileBuildPath(stateDir, name, ".xml"))) goto error; if (!(xml = virXMLParseCtxt(stateFile, NULL, _("(pool state)"), &ctxt))) goto error; if (!(node = virXPathNode("//pool", ctxt))) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("Could not find any 'pool' element in state file")); goto error; } ctxt->node = node; if (!(def = virStoragePoolDefParseXML(ctxt))) goto error; if (STRNEQ(name, def->name)) { virReportError(VIR_ERR_INTERNAL_ERROR, _("Storage pool state file '%s' does not match " "pool name '%s'"), stateFile, def->name); goto error; } /* create the object */ if (!(obj = virStoragePoolObjAssignDef(pools, def))) goto error; /* 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; cleanup: VIR_FREE(stateFile); xmlFreeDoc(xml); xmlXPathFreeContext(ctxt); return obj; error: virStoragePoolDefFree(def); goto cleanup; } int virStoragePoolObjLoadAllState(virStoragePoolObjListPtr pools, const char *stateDir) { DIR *dir; struct dirent *entry; int ret = -1; int rc; if ((rc = virDirOpenIfExists(&dir, stateDir)) <= 0) return rc; while ((ret = virDirRead(dir, &entry, stateDir)) > 0) { virStoragePoolObjPtr obj; if (!virFileStripSuffix(entry->d_name, ".xml")) continue; if (!(obj = virStoragePoolObjLoadState(pools, stateDir, entry->d_name))) continue; virStoragePoolObjEndAPI(&obj); } VIR_DIR_CLOSE(dir); return ret; } int virStoragePoolObjLoadAllConfigs(virStoragePoolObjListPtr pools, const char *configDir, const char *autostartDir) { DIR *dir; struct dirent *entry; int ret; int rc; if ((rc = virDirOpenIfExists(&dir, configDir)) <= 0) return rc; while ((ret = virDirRead(dir, &entry, configDir)) > 0) { char *path; char *autostartLink; virStoragePoolObjPtr obj; if (!virFileHasSuffix(entry->d_name, ".xml")) continue; if (!(path = virFileBuildPath(configDir, entry->d_name, NULL))) continue; if (!(autostartLink = virFileBuildPath(autostartDir, entry->d_name, NULL))) { VIR_FREE(path); continue; } obj = virStoragePoolObjLoad(pools, entry->d_name, path, autostartLink); virStoragePoolObjEndAPI(&obj); VIR_FREE(path); VIR_FREE(autostartLink); } VIR_DIR_CLOSE(dir); return ret; } int virStoragePoolObjSaveDef(virStorageDriverStatePtr driver, virStoragePoolObjPtr obj, virStoragePoolDefPtr def) { if (!obj->configFile) { if (virFileMakePath(driver->configDir) < 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(virStoragePoolObjPtr 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 void *name ATTRIBUTE_UNUSED, void *opaque) { virStoragePoolObjPtr 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(virStoragePoolObjListPtr 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 void *name ATTRIBUTE_UNUSED, void *opaque) { virStoragePoolObjPtr 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) { if (VIR_STRDUP(data->names[data->nnames], obj->def->name) < 0) { data->error = true; goto cleanup; } } data->nnames++; cleanup: virObjectUnlock(obj); return 0; } int virStoragePoolObjGetNames(virStoragePoolObjListPtr 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; } /* * virStoragePoolObjIsDuplicate: * @doms : virStoragePoolObjListPtr to search * @def : virStoragePoolDefPtr definition of pool to lookup * @check_active: If true, ensure that pool is not active * * Returns: -1 on error * 0 if pool is new * 1 if pool is a duplicate */ int virStoragePoolObjIsDuplicate(virStoragePoolObjListPtr pools, virStoragePoolDefPtr def, unsigned int check_active) { int ret = -1; virStoragePoolObjPtr obj = NULL; /* See if a Pool with matching UUID already exists */ obj = virStoragePoolObjFindByUUID(pools, def->uuid); if (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; } } ret = 1; } else { /* UUID does not match, but if a name matches, refuse it */ obj = virStoragePoolObjFindByName(pools, def->name); if (obj) { char uuidstr[VIR_UUID_STRING_BUFLEN]; 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(virStorageAdapterSCSIHostPtr scsi_host, unsigned int *hostnum) { int ret = -1; unsigned int num; char *name = NULL; if (scsi_host->has_parent) { virPCIDeviceAddressPtr 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: * * @conn: Connection pointer * @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(virConnectPtr conn, virStorageAdapterFCHostPtr fchost, unsigned int scsi_hostnum) { 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 (conn && !fchost->parent) { if (virAsprintf(&scsi_host_name, "scsi_%s", name) < 0) goto cleanup; 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 availabilty. 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); return ret; } static bool matchSCSIAdapterParent(virStorageAdapterSCSIHostPtr pool_scsi_host, virStorageAdapterSCSIHostPtr def_scsi_host) { virPCIDeviceAddressPtr pooladdr = &pool_scsi_host->parentaddr; virPCIDeviceAddressPtr 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(virStoragePoolSourcePtr poolsrc, virStoragePoolSourcePtr 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(virStoragePoolObjPtr obj, virStoragePoolDefPtr def) { virStoragePoolSourcePtr poolsrc = &obj->def->source; virStoragePoolSourcePtr 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 virStoragePoolObjPtr virStoragePoolObjSourceMatchTypeDIR(virStoragePoolObjPtr obj, virStoragePoolDefPtr 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 virStoragePoolObjPtr virStoragePoolObjSourceMatchTypeISCSI(virStoragePoolObjPtr obj, virStoragePoolDefPtr def, virConnectPtr conn) { virStorageAdapterPtr pool_adapter = &obj->def->source.adapter; virStorageAdapterPtr def_adapter = &def->source.adapter; virStorageAdapterSCSIHostPtr pool_scsi_host; virStorageAdapterSCSIHostPtr def_scsi_host; virStorageAdapterFCHostPtr pool_fchost; virStorageAdapterFCHostPtr 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(conn, 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(conn, def_fchost, scsi_hostnum)) return obj; } return NULL; } static virStoragePoolObjPtr virStoragePoolObjSourceMatchTypeDEVICE(virStoragePoolObjPtr obj, virStoragePoolDefPtr def) { virStoragePoolObjPtr 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 { virConnectPtr conn; virStoragePoolDefPtr def; }; static int virStoragePoolObjSourceFindDuplicateCb(const void *payload, const void *name ATTRIBUTE_UNUSED, const void *opaque) { virStoragePoolObjPtr obj = (virStoragePoolObjPtr) 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, data->conn)) 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_RBD: case VIR_STORAGE_POOL_LAST: break; } return 0; } int virStoragePoolObjSourceFindDuplicate(virConnectPtr conn, virStoragePoolObjListPtr pools, virStoragePoolDefPtr def) { struct _virStoragePoolObjFindDuplicateData data = { .conn = conn, .def = def }; virStoragePoolObjPtr obj = NULL; virObjectRWLockRead(pools); obj = virHashSearch(pools->objs, virStoragePoolObjSourceFindDuplicateCb, &data, NULL); virObjectRWUnlock(pools); if (obj) { virReportError(VIR_ERR_OPERATION_FAILED, _("Storage source conflict with pool: '%s'"), obj->def->name); return -1; } return 0; } #define MATCH(FLAG) (flags & (FLAG)) static bool virStoragePoolMatch(virStoragePoolObjPtr 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)))) return false; } return true; } #undef MATCH struct _virStoragePoolExportData { virConnectPtr conn; virStoragePoolObjListACLFilter filter; bool checkActive; bool wantActive; bool checkMatch; unsigned int flags; bool error; int nPools; virStoragePoolPtr *pools; }; static int virStoragePoolObjListExportCb(void *payload, const void *name ATTRIBUTE_UNUSED, void *opaque) { virStoragePoolObjPtr obj = payload; struct _virStoragePoolExportData *data = opaque; virStoragePoolPtr pool = NULL; if (data->error) return 0; virObjectLock(obj); if (data->filter && !data->filter(data->conn, obj->def)) goto cleanup; if (!virStoragePoolMatch(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, virStoragePoolObjListPtr poolobjs, virStoragePoolPtr **pools, virStoragePoolObjListFilter filter, unsigned int flags) { struct _virStoragePoolExportData data = { .conn = conn, .filter = filter, .flags = flags, .error = false, .nPools = 0, .pools = NULL }; virObjectRWLockRead(poolobjs); if (pools && VIR_ALLOC_N(data.pools, virHashSize(poolobjs->objs) + 1) < 0) goto error; virHashForEach(poolobjs->objs, virStoragePoolObjListExportCb, &data); virObjectRWUnlock(poolobjs); if (data.error) goto error; if (data.pools) { /* trim the array to the final size */ ignore_value(VIR_REALLOC_N(data.pools, data.nPools + 1)); *pools = data.pools; } return data.nPools; error: virObjectListFree(data.pools); return -1; }