QEMU >= 2.10 started to do mandatory locking. This checks the QMP
schema to see if we are using that version of qemu. (Note it is
sometimes disabled in downstream builds, and it was also enabled in
upstream prereleases with version 2.9.9x, so we cannot just check the
version number).
---
lib/guestfs-internal.h | 1 +
lib/qemu.c | 60 ++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 61 insertions(+)
diff --git a/lib/guestfs-internal.h b/lib/guestfs-internal.h
index 2ca258cb3..ab839de4d 100644
--- a/lib/guestfs-internal.h
+++ b/lib/guestfs-internal.h
@@ -983,6 +983,7 @@ extern struct qemu_data *guestfs_int_test_qemu (guestfs_h *g);
extern struct version guestfs_int_qemu_version (guestfs_h *g, struct qemu_data *);
extern int guestfs_int_qemu_supports (guestfs_h *g, const struct qemu_data *, const char
*option);
extern int guestfs_int_qemu_supports_device (guestfs_h *g, const struct qemu_data *,
const char *device_name);
+extern int guestfs_int_qemu_mandatory_locking (guestfs_h *g, const struct qemu_data
*data);
extern char *guestfs_int_drive_source_qemu_param (guestfs_h *g, const struct drive_source
*src);
extern bool guestfs_int_discard_possible (guestfs_h *g, struct drive *drv, const struct
version *qemu_version);
extern char *guestfs_int_qemu_escape_param (guestfs_h *g, const char *param);
diff --git a/lib/qemu.c b/lib/qemu.c
index b438b7bc8..59b3fc3b1 100644
--- a/lib/qemu.c
+++ b/lib/qemu.c
@@ -576,6 +576,66 @@ guestfs_int_qemu_supports_device (guestfs_h *g,
return strstr (data->qemu_devices, device_name) != NULL;
}
+/* GCC can't work out that the YAJL_IS_<foo> test is sufficient to
+ * ensure that YAJL_GET_<foo> later doesn't return NULL.
+ */
+#pragma GCC diagnostic push
+#if defined(__GNUC__) && __GNUC__ >= 6 /* gcc >= 6 */
+#pragma GCC diagnostic ignored "-Wnull-dereference"
+#endif
+
+/**
+ * Test if the QMP schema contains a particular C<(key, value)> pair
+ * (anywhere). QMP is almost impossible to parse sanely so this is
+ * the minimum we need to detect if C<"locking"> is supported.
+ */
+static int
+qmp_schema_contains (yajl_val tree, const char *key, const char *value)
+{
+ size_t i;
+
+ if (YAJL_IS_OBJECT (tree)) {
+ for (i = 0; i < YAJL_GET_OBJECT(tree)->len; ++i) {
+ const char *k;
+ yajl_val v;
+
+ k = YAJL_GET_OBJECT(tree)->keys[i];
+ v = YAJL_GET_OBJECT(tree)->values[i];
+
+ if (STREQ (k, key) &&
+ YAJL_IS_STRING (v) && STREQ (YAJL_GET_STRING (v), value))
+ return 1;
+ if (qmp_schema_contains (v, key, value))
+ return 1;
+ }
+ }
+ else if (YAJL_IS_ARRAY (tree)) {
+ for (i = 0; i < YAJL_GET_ARRAY(tree)->len; ++i) {
+ yajl_val v;
+
+ v = YAJL_GET_ARRAY(tree)->values[i];
+
+ if (qmp_schema_contains (v, key, value))
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+#pragma GCC diagnostic pop
+
+/**
+ * Test if the qemu binary uses mandatory file locking, added in
+ * QEMU >= 2.10 (but sometimes disabled).
+ */
+int
+guestfs_int_qemu_mandatory_locking (guestfs_h *g,
+ const struct qemu_data *data)
+{
+ return qmp_schema_contains (data->qmp_schema_tree, "name",
"locking");
+}
+
/**
* Escape a qemu parameter.
*
--
2.13.2