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@xxxxxxxxxx> --- 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 -- libvir-list mailing list libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list