Now that we are able to dump internal state into a JSON
file/string, we should be able to reverse the process and
reconstruct the internal state from a JSON file/string.
Signed-off-by: Michal Privoznik <mprivozn(a)redhat.com>
---
src/libvirt_private.syms | 2 +
src/util/virudev.c | 159 +++++++++++++++++++++++++++++++++++++++++++++++
src/util/virudev.h | 2 +
tests/virudevtest.c | 45 ++++++++++++++
4 files changed, 208 insertions(+)
diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
index ca64c80..0f08018 100644
--- a/src/libvirt_private.syms
+++ b/src/libvirt_private.syms
@@ -2582,6 +2582,8 @@ virUdevMgrAddLabel;
virUdevMgrDumpFile;
virUdevMgrDumpStr;
virUdevMgrNew;
+virUdevMgrNewFromFile;
+virUdevMgrNewFromStr;
virUdevMgrRemoveAllLabels;
diff --git a/src/util/virudev.c b/src/util/virudev.c
index 7f52149..d60fbf9 100644
--- a/src/util/virudev.c
+++ b/src/util/virudev.c
@@ -28,6 +28,7 @@
#include "virhash.h"
#include "virjson.h"
#include "virobject.h"
+#include "virstring.h"
#include "virudev.h"
#define VIR_FROM_THIS VIR_FROM_SECURITY
@@ -176,6 +177,49 @@ udevSeclabelsDump(void *payload,
}
+static udevSeclabelPtr
+udevSeclabelRestore(virJSONValuePtr labels)
+{
+ udevSeclabelPtr ret = NULL;
+ size_t i;
+
+ if (VIR_ALLOC(ret) < 0)
+ goto error;
+
+ for (i = 0; i < virJSONValueArraySize(labels); i++) {
+ virJSONValuePtr labelJSON = virJSONValueArrayGet(labels, i);
+ virSecurityDeviceLabelDefPtr seclabel;
+ const char *model;
+ const char *label;
+
+ if (!(model = virJSONValueObjectGetString(labelJSON, "model"))) {
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("seclabel missing model in JSON"));
+ goto error;
+ }
+
+ if (!(label = virJSONValueObjectGetString(labelJSON, "label"))) {
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("seclabel missing label in JSON"));
+ goto error;
+ }
+
+ if (!(seclabel = virSecurityDeviceLabelDefNewLabel(model, label)) ||
+ udevSeclabelAppend(ret, seclabel) < 0) {
+ virSecurityDeviceLabelDefFree(seclabel);
+ goto error;
+ }
+ virSecurityDeviceLabelDefFree(seclabel);
+ }
+
+ return ret;
+
+ error:
+ udevSeclabelFree(ret, NULL);
+ return NULL;
+}
+
+
static void
virUdevMgrDispose(void *obj)
{
@@ -359,3 +403,118 @@ virUdevMgrDumpFile(virUdevMgrPtr mgr,
VIR_FREE(state);
return ret;
}
+
+
+static int
+virUdevRestoreLabels(virUdevMgrPtr mgr ATTRIBUTE_UNUSED,
+ virJSONValuePtr labelsArray)
+{
+ int ret = -1;
+ size_t i;
+ udevSeclabelPtr list = NULL;
+
+ for (i = 0; i < virJSONValueArraySize(labelsArray); i++) {
+ virJSONValuePtr deviceJSON = virJSONValueArrayGet(labelsArray, i);
+ virJSONValuePtr labels;
+ const char *device;
+
+ if (!(device = virJSONValueObjectGetString(deviceJSON, "device"))) {
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("Missing device name"));
+ goto cleanup;
+ }
+
+ if (!(labels = virJSONValueObjectGetArray(deviceJSON, "labels"))) {
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("Missing device labels array"));
+ goto cleanup;
+ }
+
+ if (!(list = udevSeclabelRestore(labels))) {
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ _("Malformed seclabels for device %s"), device);
+ goto cleanup;
+ }
+
+ if (virHashAddEntry(mgr->labels, device, list) < 0)
+ goto cleanup;
+ list = NULL;
+ }
+
+ ret = 0;
+ cleanup:
+ udevSeclabelFree(list, NULL);
+ return ret;
+}
+
+
+static int
+virUdevMgrNewFromStrInternal(virUdevMgrPtr mgr,
+ const char *state)
+{
+ virJSONValuePtr object;
+ virJSONValuePtr child;
+ int ret = -1;
+
+ if (!(object = virJSONValueFromString(state)))
+ goto cleanup;
+
+ if (!(child = virJSONValueObjectGetArray(object, "labels"))) {
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("Missing 'labels' object in JSON document"));
+ goto cleanup;
+ }
+
+ if (virUdevRestoreLabels(mgr, child) < 0)
+ goto cleanup;
+
+ ret = 0;
+ cleanup:
+ virJSONValueFree(object);
+ return ret;
+}
+
+
+virUdevMgrPtr
+virUdevMgrNewFromStr(const char *str)
+{
+ virUdevMgrPtr mgr;
+
+ if (!(mgr = virUdevMgrNew()))
+ goto error;
+
+ if (virUdevMgrNewFromStrInternal(mgr, str) < 0)
+ goto error;
+
+ return mgr;
+ error:
+ virObjectUnref(mgr);
+ return NULL;
+}
+
+
+virUdevMgrPtr
+virUdevMgrNewFromFile(const char *filename)
+{
+ virUdevMgrPtr mgr;
+ char *state = NULL;
+
+ if (!(mgr = virUdevMgrNew()))
+ goto error;
+
+ if (virFileReadAll(filename,
+ 1024 * 1024 * 10, /* 10 MB */
+ &state) < 0)
+ goto error;
+
+ if (virUdevMgrNewFromStrInternal(mgr, state) < 0)
+ goto error;
+
+ VIR_FREE(state);
+
+ return mgr;
+ error:
+ virObjectUnref(mgr);
+ VIR_FREE(state);
+ return NULL;
+}
diff --git a/src/util/virudev.h b/src/util/virudev.h
index 1d5a23e..82b2b4f 100644
--- a/src/util/virudev.h
+++ b/src/util/virudev.h
@@ -29,6 +29,8 @@ typedef struct _virUdevMgr virUdevMgr;
typedef virUdevMgr *virUdevMgrPtr;
virUdevMgrPtr virUdevMgrNew(void);
+virUdevMgrPtr virUdevMgrNewFromStr(const char *str);
+virUdevMgrPtr virUdevMgrNewFromFile(const char *filename);
int virUdevMgrAddLabel(virUdevMgrPtr mgr,
const char *device,
diff --git a/tests/virudevtest.c b/tests/virudevtest.c
index 883d751..c395741 100644
--- a/tests/virudevtest.c
+++ b/tests/virudevtest.c
@@ -93,6 +93,37 @@ testDump(const void *opaque)
static int
+testParse(const void *opaque)
+{
+ const struct testUdevData *data = opaque;
+ virUdevMgrPtr mgr = NULL;
+ char *filename = NULL;
+ char *state = NULL;
+ int ret = -1;
+
+ if (virAsprintf(&filename, "%s/virudevtestdata/%s.json",
+ abs_srcdir, data->file) < 0)
+ goto cleanup;
+
+ if (!(mgr = virUdevMgrNewFromFile(filename)))
+ goto cleanup;
+
+ if (!(state = virUdevMgrDumpStr(mgr)))
+ goto cleanup;
+
+ if (virTestCompareToFile(state, filename))
+ goto cleanup;
+
+ ret = 0;
+ cleanup:
+ VIR_FREE(state);
+ VIR_FREE(filename);
+ virObjectUnref(mgr);
+ return ret;
+}
+
+
+static int
mymain(void)
{
int ret = 0;
@@ -107,6 +138,15 @@ mymain(void)
ret = -1; \
} while (0)
+#define DO_TEST_PARSE(filename) \
+ do { \
+ struct testUdevData data = { \
+ .file = filename, \
+ }; \
+ if (virTestRun("Parse " filename, testParse, &data) < 0) \
+ ret = -1; \
+ } while (0)
+
DO_TEST_DUMP("empty", NULL);
DO_TEST_DUMP("simple-selinux",
"/dev/sda", "selinux",
"someSELinuxLabel");
@@ -118,6 +158,11 @@ mymain(void)
"/dev/sdb", "dac", "otherDACLabel",
"/dev/sdb", "selinux",
"otherSELinuxLabel");
+ DO_TEST_PARSE("empty");
+ DO_TEST_PARSE("simple-selinux");
+ DO_TEST_PARSE("simple-dac");
+ DO_TEST_PARSE("complex");
+
return ret == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
}
--
2.8.4