mirror of
https://gitlab.com/libvirt/libvirt.git
synced 2024-10-13 17:49:16 +00:00
5e752513d8
This functions tries to add a domain moment (love the name!) onto a list of domain moments. Firstly, it checks if another moment with the same name already exists. Then, it creates an empty moment (without initializing its definition) and tries to add the moment onto the list dereferencing moment definition in that process. If it succeeds (which it never can), only after that it sets moment->def. Signed-off-by: Michal Privoznik <mprivozn@redhat.com> Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
527 lines
15 KiB
C
527 lines
15 KiB
C
/*
|
|
* virdomainmomentobjlist.c: handle snapshot/checkpoint objects
|
|
* (derived from snapshot_conf.c)
|
|
*
|
|
* Copyright (C) 2006-2019 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
|
|
* <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include <config.h>
|
|
|
|
#include "internal.h"
|
|
#include "virdomainmomentobjlist.h"
|
|
#include "virlog.h"
|
|
#include "virerror.h"
|
|
#include "virstring.h"
|
|
#include "moment_conf.h"
|
|
|
|
/* FIXME: using virObject would allow us to not need this */
|
|
#include "snapshot_conf.h"
|
|
#include "virdomainsnapshotobjlist.h"
|
|
|
|
#define VIR_FROM_THIS VIR_FROM_DOMAIN
|
|
|
|
VIR_LOG_INIT("conf.virdomainmomentobjlist");
|
|
|
|
/* Opaque struct */
|
|
struct _virDomainMomentObjList {
|
|
/* name string -> virDomainMomentObj mapping
|
|
* for O(1), lockless lookup-by-name */
|
|
virHashTable *objs;
|
|
|
|
virDomainMomentObj metaroot; /* Special parent of all root moments */
|
|
virDomainMomentObjPtr current; /* The current moment, if any */
|
|
};
|
|
|
|
|
|
/* Run iter(data) on all direct children of moment, while ignoring all
|
|
* other entries in moments. Return the number of children
|
|
* visited. No particular ordering is guaranteed. */
|
|
int
|
|
virDomainMomentForEachChild(virDomainMomentObjPtr moment,
|
|
virHashIterator iter,
|
|
void *data)
|
|
{
|
|
virDomainMomentObjPtr child = moment->first_child;
|
|
|
|
while (child) {
|
|
virDomainMomentObjPtr next = child->sibling;
|
|
(iter)(child, child->def->name, data);
|
|
child = next;
|
|
}
|
|
|
|
return moment->nchildren;
|
|
}
|
|
|
|
struct moment_act_on_descendant {
|
|
int number;
|
|
virHashIterator iter;
|
|
void *data;
|
|
};
|
|
|
|
static int
|
|
virDomainMomentActOnDescendant(void *payload,
|
|
const void *name,
|
|
void *data)
|
|
{
|
|
virDomainMomentObjPtr obj = payload;
|
|
struct moment_act_on_descendant *curr = data;
|
|
|
|
(curr->iter)(payload, name, curr->data);
|
|
curr->number += 1 + virDomainMomentForEachDescendant(obj,
|
|
curr->iter,
|
|
curr->data);
|
|
return 0;
|
|
}
|
|
|
|
/* Run iter(data) on all descendants of moment, while ignoring all
|
|
* other entries in moments. Return the number of descendants
|
|
* visited. The visit is guaranteed to be topological, but no
|
|
* particular order between siblings is guaranteed. */
|
|
int
|
|
virDomainMomentForEachDescendant(virDomainMomentObjPtr moment,
|
|
virHashIterator iter,
|
|
void *data)
|
|
{
|
|
struct moment_act_on_descendant act;
|
|
|
|
act.number = 0;
|
|
act.iter = iter;
|
|
act.data = data;
|
|
virDomainMomentForEachChild(moment,
|
|
virDomainMomentActOnDescendant, &act);
|
|
|
|
return act.number;
|
|
}
|
|
|
|
|
|
/* Prepare to reparent or delete moment, by removing it from its
|
|
* current listed parent. Note that when bulk removing all children
|
|
* of a parent, it is faster to just 0 the count rather than calling
|
|
* this function on each child. */
|
|
void
|
|
virDomainMomentDropParent(virDomainMomentObjPtr moment)
|
|
{
|
|
virDomainMomentObjPtr prev = NULL;
|
|
virDomainMomentObjPtr curr = NULL;
|
|
|
|
moment->parent->nchildren--;
|
|
curr = moment->parent->first_child;
|
|
while (curr != moment) {
|
|
if (!curr) {
|
|
VIR_WARN("inconsistent moment relations");
|
|
return;
|
|
}
|
|
prev = curr;
|
|
curr = curr->sibling;
|
|
}
|
|
if (prev)
|
|
prev->sibling = moment->sibling;
|
|
else
|
|
moment->parent->first_child = moment->sibling;
|
|
moment->parent = NULL;
|
|
moment->sibling = NULL;
|
|
}
|
|
|
|
|
|
/* Update @moment to no longer have children. */
|
|
void
|
|
virDomainMomentDropChildren(virDomainMomentObjPtr moment)
|
|
{
|
|
moment->nchildren = 0;
|
|
moment->first_child = NULL;
|
|
}
|
|
|
|
|
|
/* Add @moment to @parent's list of children. */
|
|
void
|
|
virDomainMomentSetParent(virDomainMomentObjPtr moment,
|
|
virDomainMomentObjPtr parent)
|
|
{
|
|
moment->parent = parent;
|
|
parent->nchildren++;
|
|
moment->sibling = parent->first_child;
|
|
parent->first_child = moment;
|
|
}
|
|
|
|
|
|
/* Take all children of @from and convert them into children of @to. */
|
|
void
|
|
virDomainMomentMoveChildren(virDomainMomentObjPtr from,
|
|
virDomainMomentObjPtr to)
|
|
{
|
|
virDomainMomentObjPtr child;
|
|
virDomainMomentObjPtr last;
|
|
|
|
if (!from->first_child)
|
|
return;
|
|
for (child = from->first_child; child; child = child->sibling) {
|
|
child->parent = to;
|
|
if (!child->sibling)
|
|
last = child;
|
|
}
|
|
to->nchildren += from->nchildren;
|
|
last->sibling = to->first_child;
|
|
to->first_child = from->first_child;
|
|
from->nchildren = 0;
|
|
from->first_child = NULL;
|
|
}
|
|
|
|
|
|
static virDomainMomentObjPtr
|
|
virDomainMomentObjNew(void)
|
|
{
|
|
virDomainMomentObjPtr moment;
|
|
|
|
if (VIR_ALLOC(moment) < 0)
|
|
return NULL;
|
|
|
|
VIR_DEBUG("obj=%p", moment);
|
|
|
|
return moment;
|
|
}
|
|
|
|
|
|
static void
|
|
virDomainMomentObjFree(virDomainMomentObjPtr moment)
|
|
{
|
|
if (!moment)
|
|
return;
|
|
|
|
VIR_DEBUG("obj=%p", moment);
|
|
|
|
/* FIXME: Make this polymorphic by inheriting from virObject */
|
|
virDomainSnapshotDefFree(virDomainSnapshotObjGetDef(moment));
|
|
VIR_FREE(moment);
|
|
}
|
|
|
|
|
|
/* Add def to the list and return a matching object, or NULL on error */
|
|
virDomainMomentObjPtr
|
|
virDomainMomentAssignDef(virDomainMomentObjListPtr moments,
|
|
virDomainMomentDefPtr def)
|
|
{
|
|
virDomainMomentObjPtr moment;
|
|
|
|
if (virHashLookup(moments->objs, def->name) != NULL) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
_("unexpected domain moment %s already exists"),
|
|
def->name);
|
|
return NULL;
|
|
}
|
|
|
|
if (!(moment = virDomainMomentObjNew()))
|
|
return NULL;
|
|
|
|
if (virHashAddEntry(moments->objs, def->name, moment) < 0) {
|
|
VIR_FREE(moment);
|
|
return NULL;
|
|
}
|
|
moment->def = def;
|
|
|
|
return moment;
|
|
}
|
|
|
|
|
|
static void
|
|
virDomainMomentObjListDataFree(void *payload,
|
|
const void *name ATTRIBUTE_UNUSED)
|
|
{
|
|
virDomainMomentObjPtr obj = payload;
|
|
|
|
virDomainMomentObjFree(obj);
|
|
}
|
|
|
|
|
|
virDomainMomentObjListPtr
|
|
virDomainMomentObjListNew(void)
|
|
{
|
|
virDomainMomentObjListPtr moments;
|
|
|
|
if (VIR_ALLOC(moments) < 0)
|
|
return NULL;
|
|
moments->objs = virHashCreate(50, virDomainMomentObjListDataFree);
|
|
if (!moments->objs) {
|
|
VIR_FREE(moments);
|
|
return NULL;
|
|
}
|
|
return moments;
|
|
}
|
|
|
|
void
|
|
virDomainMomentObjListFree(virDomainMomentObjListPtr moments)
|
|
{
|
|
if (!moments)
|
|
return;
|
|
virHashFree(moments->objs);
|
|
VIR_FREE(moments);
|
|
}
|
|
|
|
|
|
/* Struct and callback for collecting a list of names of moments that
|
|
* meet a particular filter. */
|
|
struct virDomainMomentNameData {
|
|
char **const names;
|
|
int maxnames;
|
|
unsigned int flags;
|
|
int count;
|
|
bool error;
|
|
virDomainMomentObjListFilter filter;
|
|
};
|
|
|
|
|
|
static int virDomainMomentObjListCopyNames(void *payload,
|
|
const void *name ATTRIBUTE_UNUSED,
|
|
void *opaque)
|
|
{
|
|
virDomainMomentObjPtr obj = payload;
|
|
struct virDomainMomentNameData *data = opaque;
|
|
|
|
if (data->error)
|
|
return 0;
|
|
/* Caller already sanitized flags. Filtering on DESCENDANTS was
|
|
* done by choice of iteration in the caller. */
|
|
/* TODO: Create VIR_DOMAIN_MOMENT_LIST names */
|
|
if ((data->flags & VIR_DOMAIN_SNAPSHOT_LIST_LEAVES) && obj->nchildren)
|
|
return 0;
|
|
if ((data->flags & VIR_DOMAIN_SNAPSHOT_LIST_NO_LEAVES) && !obj->nchildren)
|
|
return 0;
|
|
|
|
if (!data->filter(obj, data->flags))
|
|
return 0;
|
|
|
|
if (data->names && data->count < data->maxnames &&
|
|
VIR_STRDUP(data->names[data->count], obj->def->name) < 0) {
|
|
data->error = true;
|
|
return 0;
|
|
}
|
|
data->count++;
|
|
return 0;
|
|
}
|
|
|
|
|
|
int
|
|
virDomainMomentObjListGetNames(virDomainMomentObjListPtr moments,
|
|
virDomainMomentObjPtr from,
|
|
char **const names,
|
|
int maxnames,
|
|
unsigned int flags,
|
|
virDomainMomentObjListFilter filter)
|
|
{
|
|
struct virDomainMomentNameData data = { names, maxnames, flags, 0,
|
|
false, filter };
|
|
size_t i;
|
|
|
|
if (!from) {
|
|
/* LIST_ROOTS and LIST_DESCENDANTS have the same bit value,
|
|
* but opposite semantics. Toggle here to get the correct
|
|
* traversal on the metaroot. */
|
|
/* TODO: Create VIR_DOMAIN_MOMENT_LIST names */
|
|
flags ^= VIR_DOMAIN_SNAPSHOT_LIST_ROOTS;
|
|
from = &moments->metaroot;
|
|
}
|
|
|
|
/* We handle LIST_ROOT/LIST_DESCENDANTS and LIST_TOPOLOGICAL directly,
|
|
* mask those bits out to determine when we must use the filter callback. */
|
|
data.flags &= ~(VIR_DOMAIN_SNAPSHOT_LIST_DESCENDANTS |
|
|
VIR_DOMAIN_SNAPSHOT_LIST_TOPOLOGICAL);
|
|
|
|
/* If this common code is being used, we assume that all moments
|
|
* have metadata, and thus can handle METADATA up front as an
|
|
* all-or-none filter. XXX This might not always be true, if we
|
|
* add the ability to track qcow2 internal snapshots without the
|
|
* use of metadata, in which case this check should move into the
|
|
* filter callback. */
|
|
if ((data.flags & VIR_DOMAIN_SNAPSHOT_FILTERS_METADATA) ==
|
|
VIR_DOMAIN_SNAPSHOT_LIST_NO_METADATA)
|
|
return 0;
|
|
data.flags &= ~VIR_DOMAIN_SNAPSHOT_FILTERS_METADATA;
|
|
|
|
if (flags & VIR_DOMAIN_SNAPSHOT_LIST_DESCENDANTS) {
|
|
/* We could just always do a topological visit; but it is
|
|
* possible to optimize for less stack usage and time when a
|
|
* simpler full hashtable visit or counter will do. */
|
|
if (from->def || (names &&
|
|
(flags & VIR_DOMAIN_SNAPSHOT_LIST_TOPOLOGICAL)))
|
|
virDomainMomentForEachDescendant(from,
|
|
virDomainMomentObjListCopyNames,
|
|
&data);
|
|
else if (names || data.flags)
|
|
virHashForEach(moments->objs, virDomainMomentObjListCopyNames,
|
|
&data);
|
|
else
|
|
data.count = virHashSize(moments->objs);
|
|
} else if (names || data.flags) {
|
|
virDomainMomentForEachChild(from,
|
|
virDomainMomentObjListCopyNames, &data);
|
|
} else {
|
|
data.count = from->nchildren;
|
|
}
|
|
|
|
if (data.error) {
|
|
for (i = 0; i < data.count; i++)
|
|
VIR_FREE(names[i]);
|
|
return -1;
|
|
}
|
|
|
|
return data.count;
|
|
}
|
|
|
|
|
|
virDomainMomentObjPtr
|
|
virDomainMomentFindByName(virDomainMomentObjListPtr moments,
|
|
const char *name)
|
|
{
|
|
return name ? virHashLookup(moments->objs, name) : &moments->metaroot;
|
|
}
|
|
|
|
|
|
/* Return the current moment, or NULL */
|
|
virDomainMomentObjPtr
|
|
virDomainMomentGetCurrent(virDomainMomentObjListPtr moments)
|
|
{
|
|
return moments->current;
|
|
}
|
|
|
|
|
|
/* Return the current moment's name, or NULL */
|
|
const char *
|
|
virDomainMomentGetCurrentName(virDomainMomentObjListPtr moments)
|
|
{
|
|
if (moments->current)
|
|
return moments->current->def->name;
|
|
return NULL;
|
|
}
|
|
|
|
|
|
/* Return true if name matches the current moment */
|
|
bool
|
|
virDomainMomentIsCurrentName(virDomainMomentObjListPtr moments,
|
|
const char *name)
|
|
{
|
|
return moments->current && STREQ(moments->current->def->name, name);
|
|
}
|
|
|
|
|
|
/* Update the current moment, using NULL if no current remains */
|
|
void
|
|
virDomainMomentSetCurrent(virDomainMomentObjListPtr moments,
|
|
virDomainMomentObjPtr moment)
|
|
{
|
|
moments->current = moment;
|
|
}
|
|
|
|
|
|
/* Return the number of moments in the list */
|
|
int
|
|
virDomainMomentObjListSize(virDomainMomentObjListPtr moments)
|
|
{
|
|
return virHashSize(moments->objs);
|
|
}
|
|
|
|
|
|
/* Remove moment from the list; return true if it was current */
|
|
bool
|
|
virDomainMomentObjListRemove(virDomainMomentObjListPtr moments,
|
|
virDomainMomentObjPtr moment)
|
|
{
|
|
bool ret = moments->current == moment;
|
|
|
|
virHashRemoveEntry(moments->objs, moment->def->name);
|
|
if (ret)
|
|
moments->current = NULL;
|
|
return ret;
|
|
}
|
|
|
|
|
|
/* Remove all moments tracked in the list */
|
|
void
|
|
virDomainMomentObjListRemoveAll(virDomainMomentObjListPtr moments)
|
|
{
|
|
virHashRemoveAll(moments->objs);
|
|
virDomainMomentDropChildren(&moments->metaroot);
|
|
}
|
|
|
|
|
|
/* Call iter on each member of the list, in unspecified order */
|
|
int
|
|
virDomainMomentForEach(virDomainMomentObjListPtr moments,
|
|
virHashIterator iter,
|
|
void *data)
|
|
{
|
|
return virHashForEach(moments->objs, iter, data);
|
|
}
|
|
|
|
|
|
/* Struct and callback function used as a hash table callback; each call
|
|
* inspects the pre-existing moment->def->parent field, and adjusts
|
|
* the moment->parent field as well as the parent's child fields to
|
|
* wire up the hierarchical relations for the given moment. The error
|
|
* indicator gets set if a parent is missing or a requested parent would
|
|
* cause a circular parent chain. */
|
|
struct moment_set_relation {
|
|
virDomainMomentObjListPtr moments;
|
|
int err;
|
|
};
|
|
static int
|
|
virDomainMomentSetRelations(void *payload,
|
|
const void *name ATTRIBUTE_UNUSED,
|
|
void *data)
|
|
{
|
|
virDomainMomentObjPtr obj = payload;
|
|
struct moment_set_relation *curr = data;
|
|
virDomainMomentObjPtr tmp;
|
|
virDomainMomentObjPtr parent;
|
|
|
|
parent = virDomainMomentFindByName(curr->moments, obj->def->parent);
|
|
if (!parent) {
|
|
curr->err = -1;
|
|
parent = &curr->moments->metaroot;
|
|
VIR_WARN("moment %s lacks parent", obj->def->name);
|
|
} else {
|
|
tmp = parent;
|
|
while (tmp && tmp->def) {
|
|
if (tmp == obj) {
|
|
curr->err = -1;
|
|
parent = &curr->moments->metaroot;
|
|
VIR_WARN("moment %s in circular chain", obj->def->name);
|
|
break;
|
|
}
|
|
tmp = tmp->parent;
|
|
}
|
|
}
|
|
virDomainMomentSetParent(obj, parent);
|
|
return 0;
|
|
}
|
|
|
|
|
|
/* Populate parent link and child count of all moments, with all
|
|
* assigned defs having relations starting as 0/NULL. Return 0 on
|
|
* success, -1 if a parent is missing or if a circular relationship
|
|
* was requested. */
|
|
int
|
|
virDomainMomentUpdateRelations(virDomainMomentObjListPtr moments)
|
|
{
|
|
struct moment_set_relation act = { moments, 0 };
|
|
|
|
virDomainMomentDropChildren(&moments->metaroot);
|
|
virHashForEach(moments->objs, virDomainMomentSetRelations, &act);
|
|
if (act.err)
|
|
moments->current = NULL;
|
|
return act.err;
|
|
}
|