The most interesting part where all the hard work takes place.
For storing triplets of strings a two dimensional table would be
the most intuitive approach. Or, if the triplet is rearranged
into pair of one string and a pair, a hash table can be used. But
hey, we already have those! Excellent. In other words,
<path, model, seclabel>
is turned into:
<path, <model, seclabel>>
The @path is then the key that the hash table is filled with. The
inner pair is turned into linked list (hash table in a hash table
would be awful really). Moreover, we need to store yet another
item: reference counter.
While virtlockd holds internal state in a JSON file between, this
feature is honored too.
Signed-off-by: Michal Privoznik <mprivozn(a)redhat.com>
---
src/locking/lock_daemon.c | 375 ++++++++++++++++++++++++++++++++++++++++++++--
1 file changed, 361 insertions(+), 14 deletions(-)
diff --git a/src/locking/lock_daemon.c b/src/locking/lock_daemon.c
index b39a63a..66fbe03 100644
--- a/src/locking/lock_daemon.c
+++ b/src/locking/lock_daemon.c
@@ -58,12 +58,30 @@
VIR_LOG_INIT("locking.lock_daemon");
#define VIR_LOCK_DAEMON_NUM_LOCKSPACES 3
+#define VIR_LOCK_DAEMON_NUM_SECLABELS 8
struct _virLockDaemon {
virMutex lock;
virNetServerPtr srv;
virHashTablePtr lockspaces;
virLockSpacePtr defaultLockspace;
+ virHashTablePtr seclabels;
+};
+
+typedef struct _virSeclabel virSeclabel;
+typedef virSeclabel *virSeclabelPtr;
+struct _virSeclabel {
+ char *model;
+ char *seclabel;
+ unsigned int refcount;
+};
+
+typedef struct _virSeclabelList virSeclabelList;
+typedef virSeclabelList *virSeclabelListPtr;
+struct _virSeclabelList {
+ virSeclabelPtr item;
+ virSeclabelListPtr prev;
+ virSeclabelListPtr next;
};
virLockDaemonPtr lockDaemon = NULL;
@@ -121,6 +139,7 @@ virLockDaemonFree(virLockDaemonPtr lockd)
virObjectUnref(lockd->srv);
virHashFree(lockd->lockspaces);
virLockSpaceFree(lockd->defaultLockspace);
+ virHashFree(lockd->seclabels);
VIR_FREE(lockd);
}
@@ -132,6 +151,178 @@ static void virLockDaemonLockSpaceDataFree(void *data,
virLockSpaceFree(data);
}
+
+static void
+virSeclabelFree(virSeclabelPtr label)
+{
+ if (!label)
+ return;
+
+ VIR_FREE(label->model);
+ VIR_FREE(label->seclabel);
+ VIR_FREE(label);
+}
+
+
+static void
+virSeclabelListFree(virSeclabelListPtr list)
+{
+ while (list) {
+ virSeclabelListPtr tmp = list->next;
+ virSeclabelFree(list->item);
+ VIR_FREE(list);
+ list = tmp;
+ }
+}
+
+
+static virSeclabelListPtr
+virSeclabelListFind(virSeclabelListPtr list,
+ const char *model)
+{
+ while (list && list->item) {
+ if (STREQ(list->item->model, model))
+ return list;
+ list = list->next;
+ }
+ return NULL;
+}
+
+
+static virSeclabelListPtr
+virSeclabelListAdd(virSeclabelListPtr list,
+ const char *model,
+ const char *seclabel)
+{
+ virSeclabelPtr tmp = NULL;
+ virSeclabelListPtr item = NULL;
+ virSeclabelListPtr ret = NULL;
+
+ if (!list)
+ return NULL;
+
+ while (list && list->next) {
+ if (STREQ(list->item->model, model))
+ return list;
+ list = list->next;
+ }
+
+ /* The model does not exist yet. Append at the end of the list */
+ if (VIR_ALLOC(tmp) < 0 ||
+ VIR_STRDUP(tmp->model, model) < 0 ||
+ VIR_STRDUP(tmp->seclabel, seclabel) < 0)
+ goto cleanup;
+
+ if (!list->item) {
+ /* Handle special case */
+ list->item = tmp;
+ item = list;
+ } else {
+ if (VIR_ALLOC(item) < 0)
+ goto cleanup;
+
+ item->item = tmp;
+ list->next = item;
+ item->prev = list;
+ }
+
+ ret = item;
+ item = NULL;
+ tmp = NULL;
+
+ cleanup:
+ virSeclabelListFree(item);
+ virSeclabelFree(tmp);
+ return ret;
+}
+
+
+static void
+virLockDaemonSeclabelDataFree(void *data,
+ const void *key ATTRIBUTE_UNUSED)
+{
+ virSeclabelListFree(data);
+}
+
+
+static virJSONValuePtr
+virSeclabelPreExecRestart(virSeclabelListPtr list)
+{
+ virJSONValuePtr object = virJSONValueNewArray();
+
+ if (!object)
+ return NULL;
+
+ while (list) {
+ virSeclabelPtr seclabel = list->item;
+ virJSONValuePtr child = virJSONValueNewObject();
+
+ if (!child ||
+ virJSONValueArrayAppend(object, child) < 0) {
+ virJSONValueFree(child);
+ goto error;
+ }
+
+ if (virJSONValueObjectAppendString(child, "model", seclabel->model)
< 0 ||
+ virJSONValueObjectAppendString(child, "seclabel",
seclabel->seclabel) < 0 ||
+ virJSONValueObjectAppendNumberUint(child, "refcount",
seclabel->refcount) < 0)
+ goto error;
+
+ list = list->next;
+ }
+
+ return object;
+
+ error:
+ virJSONValueFree(object);
+ return NULL;
+}
+
+
+static virSeclabelListPtr
+virSeclabelPostExecRestart(virJSONValuePtr object)
+{
+ virSeclabelListPtr list;
+ ssize_t i, n;
+
+ if (VIR_ALLOC(list) < 0)
+ goto error;
+
+ if ((n = virJSONValueArraySize(object)) < 0) {
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("Malformed seclabel list"));
+ goto error;
+ }
+
+ for (i = 0; i < n; i++) {
+ virJSONValuePtr child = virJSONValueArrayGet(object, i);
+ const char *model;
+ const char *seclabel;
+ unsigned int refcount;
+ virSeclabelListPtr item;
+
+ if (!(model = virJSONValueObjectGetString(child, "model")) ||
+ !(seclabel = virJSONValueObjectGetString(child, "seclabel")) ||
+ virJSONValueObjectGetNumberUint(child, "refcount", &refcount)
< 0) {
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("Malformed seclabel list"));
+ goto error;
+ }
+
+ if (!(item = virSeclabelListAdd(list, model, seclabel)))
+ goto error;
+
+ item->item->refcount = refcount;
+ }
+
+ return list;
+
+ error:
+ virSeclabelListFree(list);
+ return NULL;
+}
+
+
static virLockDaemonPtr
virLockDaemonNew(virLockDaemonConfigPtr config, bool privileged)
{
@@ -163,6 +354,10 @@ virLockDaemonNew(virLockDaemonConfigPtr config, bool privileged)
if (!(lockd->defaultLockspace = virLockSpaceNew(NULL)))
goto error;
+ if (!(lockd->seclabels = virHashCreate(VIR_LOCK_DAEMON_NUM_SECLABELS,
+ virLockDaemonSeclabelDataFree)))
+ goto error;
+
return lockd;
error:
@@ -176,7 +371,7 @@ virLockDaemonNewPostExecRestart(virJSONValuePtr object, bool
privileged)
{
virLockDaemonPtr lockd;
virJSONValuePtr child;
- virJSONValuePtr lockspaces;
+ virJSONValuePtr lockspaces, seclabels;
size_t i;
int n;
@@ -194,6 +389,10 @@ virLockDaemonNewPostExecRestart(virJSONValuePtr object, bool
privileged)
virLockDaemonLockSpaceDataFree)))
goto error;
+ if (!(lockd->seclabels = virHashCreate(VIR_LOCK_DAEMON_NUM_SECLABELS,
+ virLockDaemonSeclabelDataFree)))
+ goto error;
+
if (!(child = virJSONValueObjectGet(object, "defaultLockspace"))) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("Missing defaultLockspace data from JSON file"));
@@ -231,6 +430,38 @@ virLockDaemonNewPostExecRestart(virJSONValuePtr object, bool
privileged)
}
}
+ if ((seclabels = virJSONValueObjectGet(object, "seclabels"))) {
+ /* Parse iff seclabels attribute is present */
+ if ((n = virJSONValueArraySize(seclabels)) < 0) {
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("Malformed seclabels data from JSON file"));
+ goto error;
+ }
+
+ for (i = 0; i < n; i++) {
+ virSeclabelListPtr list;
+ virJSONValuePtr seclabel_list;
+ const char *path;
+
+ child = virJSONValueArrayGet(seclabels, i);
+
+ if (!(path = virJSONValueObjectGetString(child, "path")) ||
+ !(seclabel_list = virJSONValueObjectGet(child, "seclabel"))) {
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("Malformed seclabels data from JSON file"));
+ goto error;
+ }
+
+ if (!(list = virSeclabelPostExecRestart(seclabel_list)))
+ goto error;
+
+ if (virHashAddEntry(lockd->seclabels, path, list) < 0) {
+ virSeclabelListFree(list);
+ goto error;
+ }
+ }
+ }
+
if (!(child = virJSONValueObjectGet(object, "server"))) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("Missing server data from JSON file"));
@@ -279,24 +510,109 @@ virLockSpacePtr virLockDaemonFindLockSpace(virLockDaemonPtr lockd,
int
-virLockDaemonRememberSeclabel(virLockDaemonPtr lockd ATTRIBUTE_UNUSED,
- const char *path ATTRIBUTE_UNUSED,
- const char *model ATTRIBUTE_UNUSED,
- const char *seclabel ATTRIBUTE_UNUSED)
+virLockDaemonRememberSeclabel(virLockDaemonPtr lockd,
+ const char *path,
+ const char *model,
+ const char *seclabel)
{
- /* Implement me */
- return -1;
+ int ret = -1;
+ virSeclabelListPtr list = NULL;
+ virSeclabelListPtr tmp = NULL;
+ bool list_created = false;
+
+ virMutexLock(&lockd->lock);
+
+ if (!(list = virHashLookup(lockd->seclabels, path))) {
+ if (VIR_ALLOC(list) < 0)
+ goto cleanup;
+
+ if (virHashAddEntry(lockd->seclabels, path, list) < 0)
+ goto cleanup;
+
+ list_created = true;
+ }
+
+ tmp = list;
+ if (!(tmp = virSeclabelListFind(list, model))) {
+ /* seclabel for @model doesn't exit yet */
+ if (!(tmp = virSeclabelListAdd(list, model, seclabel)))
+ goto cleanup;
+ }
+
+ /* Update refcount */
+ ret = ++tmp->item->refcount;
+
+ cleanup:
+ virMutexUnlock(&lockd->lock);
+ if (ret < 0 && list_created) {
+ virHashRemoveEntry(lockd->seclabels, path);
+ virSeclabelListFree(list);
+ }
+ return ret;
}
int
-virLockDaemonRecallSeclabel(virLockDaemonPtr lockd ATTRIBUTE_UNUSED,
- const char *path ATTRIBUTE_UNUSED,
- const char *model ATTRIBUTE_UNUSED,
- char **seclabel ATTRIBUTE_UNUSED)
+virLockDaemonRecallSeclabel(virLockDaemonPtr lockd,
+ const char *path,
+ const char *model,
+ char **seclabel)
{
- /* Implement me */
- return -1;
+ int ret = -1;
+ virSeclabelListPtr list = NULL;
+ virSeclabelPtr label = NULL;
+
+ virMutexLock(&lockd->lock);
+
+ if (!(list = virHashLookup(lockd->seclabels, path))) {
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ _("unable to find path '%s' in seclabels
hashtable"),
+ path);
+ goto cleanup;
+ }
+
+ if (!(list = virSeclabelListFind(list, model))) {
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ _("unable to find entry for path "
+ "'%s' and security model '%s'"),
+ path, model);
+ goto cleanup;
+ }
+
+ label = list->item;
+
+ if (seclabel && VIR_STRDUP(*seclabel, label->seclabel) < 0)
+ goto cleanup;
+
+ /* Now decrement refcount */
+ if (!label->refcount) {
+ /* Huh funny. This should never happen (TM) */
+ VIR_WARN("path '%s' had no refcounts", path);
+ } else {
+ label->refcount--;
+ }
+
+ if (!(ret = label->refcount)) {
+ /* Okay, this is the last reference, adjust the linked list of
+ * seclabels */
+ if (list->prev)
+ list->prev->next = list->next;
+ if (list->next)
+ list->next->prev = list->prev;
+
+ if (!list->next && !list->prev) {
+ /* The last seclabel for @path, remove it. */
+ virHashRemoveEntry(lockd->seclabels, path);
+ } else {
+ /* There's another seclabel for @path, remove only this item. */
+ list->next = list->prev = NULL;
+ virSeclabelListFree(list);
+ }
+ }
+
+ cleanup:
+ virMutexUnlock(&lockd->lock);
+ return ret;
}
@@ -1034,7 +1350,7 @@ virLockDaemonPreExecRestart(const char *state_file,
virJSONValuePtr object;
char *magic;
virHashKeyValuePairPtr pairs = NULL, tmp;
- virJSONValuePtr lockspaces;
+ virJSONValuePtr lockspaces, seclabels;
VIR_DEBUG("Running pre-restart exec");
@@ -1080,6 +1396,37 @@ virLockDaemonPreExecRestart(const char *state_file,
tmp++;
}
+ if (!(seclabels = virJSONValueNewArray()) ||
+ virJSONValueObjectAppend(object, "seclabels", seclabels) < 0) {
+ virJSONValueFree(seclabels);
+ goto cleanup;
+ }
+
+ VIR_FREE(pairs);
+ tmp = pairs = virHashGetItems(lockDaemon->seclabels, NULL);
+ while (tmp && tmp->key) {
+ virSeclabelListPtr list = (virSeclabelListPtr)tmp->value;
+ virJSONValuePtr seclabel_list;
+
+ if (!(seclabel_list = virSeclabelPreExecRestart(list)))
+ goto cleanup;
+
+ if (!(child = virJSONValueNewObject()) ||
+ virJSONValueObjectAppendString(child, "path", tmp->key) < 0
||
+ virJSONValueObjectAppend(child, "seclabel", seclabel_list) < 0)
{
+ virJSONValueFree(seclabel_list);
+ virJSONValueFree(child);
+ goto cleanup;
+ }
+
+ if (virJSONValueArrayAppend(seclabels, child) < 0) {
+ virJSONValueFree(child);
+ goto cleanup;
+ }
+
+ tmp++;
+ }
+
if (!(magic = virLockDaemonGetExecRestartMagic()))
goto cleanup;
--
1.8.5.5