qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
From: Eric Blake <eblake@redhat.com>
To: qemu-devel@nongnu.org
Cc: armbru@redhat.com, Michael Roth <mdroth@linux.vnet.ibm.com>,
	Luiz Capitulino <lcapitulino@redhat.com>,
	Kevin Wolf <kwolf@redhat.com>, Max Reitz <mreitz@redhat.com>,
	"open list:Block layer core" <qemu-block@nongnu.org>
Subject: [Qemu-devel] [PATCH v4 29/28] qapi: Add strict mode to JSON output visitor
Date: Thu, 19 May 2016 10:52:23 -0600	[thread overview]
Message-ID: <1463676743-22133-1-git-send-email-eblake@redhat.com> (raw)
In-Reply-To: <1463632874-28559-1-git-send-email-eblake@redhat.com>

Let the caller decide whether output must be strict JSON (and
raise an error on an attempt to output an encoding error or
non-finite number), vs. the status quo of relaxed (encoding
errors are rewritten to use substitute U+fffd characters,
and non-finite numbers are output).

Adjust the testsuite to cover this: check-qobject-json checks
relaxed mode (since qobject_to_json() is unchanged in behavior),
test-qmp-output-visitor checks that QObject doesn't care about
JSON restrictions, and test-json-output-visitor is now in
strict mode and flags the errors.

Signed-off-by: Eric Blake <eblake@redhat.com>

---
v4: split out as new patch
[was parts of 04/18 and 07/18 in v3]
---
 include/qapi/json-output-visitor.h |  7 ++++-
 include/qapi/qmp/qobject-json.h    |  3 +-
 qapi/json-output-visitor.c         | 19 ++++++++----
 qemu-img.c                         |  6 ++--
 qobject/qobject-json.c             | 61 ++++++++++++++++++++++++--------------
 tests/check-qobject-json.c         | 15 ++++++++--
 tests/test-json-output-visitor.c   | 17 +++++++++--
 tests/test-qmp-output-visitor.c    | 17 +++++++++++
 8 files changed, 107 insertions(+), 38 deletions(-)

diff --git a/include/qapi/json-output-visitor.h b/include/qapi/json-output-visitor.h
index 414f91b..2ce2758 100644
--- a/include/qapi/json-output-visitor.h
+++ b/include/qapi/json-output-visitor.h
@@ -23,7 +23,12 @@ typedef struct JsonOutputVisitor JsonOutputVisitor;
  *
  * If @pretty, make the output legible with newlines and indentation;
  * otherwise the output uses a single line.
+ *
+ * If @strict, attempts to output invalid JSON (strings not properly
+ * encoded in UTF-8, or Infinity or NaN as a number) will be treated
+ * as an error; otherwise, output is best-effort even if not pure
+ * JSON.
  */
-Visitor *json_output_visitor_new(bool pretty, char **result);
+Visitor *json_output_visitor_new(bool pretty, bool strict, char **result);

 #endif
diff --git a/include/qapi/qmp/qobject-json.h b/include/qapi/qmp/qobject-json.h
index 28e44b2..30bc753 100644
--- a/include/qapi/qmp/qobject-json.h
+++ b/include/qapi/qmp/qobject-json.h
@@ -31,7 +31,8 @@ QString *qobject_to_json(const QObject *obj, bool pretty);
  * (ignored for a top-level visit, must be set if part of a
  * dictionary, should be NULL if part of a list).
  */
-void qobject_visit_output(Visitor *v, const char *name, const QObject *obj);
+void qobject_visit_output(Visitor *v, const char *name, const QObject *obj,
+                          Error **errp);

 int qstring_append_json_string(QString *qstring, const char *str);
 int qstring_append_json_number(QString *qstring, double number);
diff --git a/qapi/json-output-visitor.c b/qapi/json-output-visitor.c
index ba647d3..28e95ee 100644
--- a/qapi/json-output-visitor.c
+++ b/qapi/json-output-visitor.c
@@ -13,11 +13,13 @@
 #include "qapi/visitor-impl.h"
 #include "qapi/qmp/qstring.h"
 #include "qapi/qmp/qobject-json.h"
+#include "qapi/error.h"

 struct JsonOutputVisitor {
     Visitor visitor;
     QString *str;
     bool pretty;
+    bool strict;
     bool comma;
     unsigned int depth;
     char **result;
@@ -133,8 +135,10 @@ static void json_output_type_str(Visitor *v, const char *name, char **obj,

     assert(*obj);
     json_output_name(jov, name);
-    /* FIXME: report invalid UTF-8 encoding */
-    qstring_append_json_string(jov->str, *obj);
+    if (qstring_append_json_string(jov->str, *obj) < 0 && jov->strict) {
+        error_setg(errp, "string value of '%s' is not valid UTF-8",
+                   name ? name : "null");
+    }
 }

 static void json_output_type_number(Visitor *v, const char *name, double *obj,
@@ -143,14 +147,16 @@ static void json_output_type_number(Visitor *v, const char *name, double *obj,
     JsonOutputVisitor *jov = to_jov(v);

     json_output_name(jov, name);
-    /* FIXME: report Inf/NaN problems */
-    qstring_append_json_number(jov->str, *obj);
+    if (qstring_append_json_number(jov->str, *obj) < 0 && jov->strict) {
+        error_setg(errp, "number value of '%s' is not finite",
+                   name ? name : "null");
+    }
 }

 static void json_output_type_any(Visitor *v, const char *name, QObject **obj,
                                  Error **errp)
 {
-    qobject_visit_output(v, name, *obj);
+    qobject_visit_output(v, name, *obj, errp);
 }

 static void json_output_type_null(Visitor *v, const char *name, Error **errp)
@@ -181,12 +187,13 @@ static void json_output_free(Visitor *v)
     g_free(jov);
 }

-Visitor *json_output_visitor_new(bool pretty, char **result)
+Visitor *json_output_visitor_new(bool pretty, bool strict, char **result)
 {
     JsonOutputVisitor *v;

     v = g_malloc0(sizeof(*v));
     v->pretty = pretty;
+    v->strict = strict;
     v->result = result;
     *result = NULL;
     v->str = qstring_new();
diff --git a/qemu-img.c b/qemu-img.c
index d5dc19b..b0c6dd3 100644
--- a/qemu-img.c
+++ b/qemu-img.c
@@ -480,7 +480,7 @@ fail:
 static void dump_json_image_check(ImageCheck *check, bool quiet)
 {
     char *str;
-    Visitor *v = json_output_visitor_new(true, &str);
+    Visitor *v = json_output_visitor_new(true, true, &str);

     visit_type_ImageCheck(v, NULL, &check, &error_abort);
     visit_complete(v, &str);
@@ -2166,7 +2166,7 @@ static void dump_snapshots(BlockDriverState *bs)
 static void dump_json_image_info_list(ImageInfoList *list)
 {
     char *str;
-    Visitor *v = json_output_visitor_new(true, &str);
+    Visitor *v = json_output_visitor_new(true, true, &str);

     visit_type_ImageInfoList(v, NULL, &list, &error_abort);
     visit_complete(v, &str);
@@ -2178,7 +2178,7 @@ static void dump_json_image_info_list(ImageInfoList *list)
 static void dump_json_image_info(ImageInfo *info)
 {
     char *str;
-    Visitor *v = json_output_visitor_new(true, &str);
+    Visitor *v = json_output_visitor_new(true, true, &str);

     visit_type_ImageInfo(v, NULL, &info, &error_abort);
     visit_complete(v, &str);
diff --git a/qobject/qobject-json.c b/qobject/qobject-json.c
index 05b020a..cf81ab5 100644
--- a/qobject/qobject-json.c
+++ b/qobject/qobject-json.c
@@ -72,62 +72,81 @@ QObject *qobject_from_jsonf(const char *string, ...)
     return obj;
 }

+typedef struct ToJson {
+{
+    Visitor *v;
+    Error **errp;
+} ToJson;
+
 static void to_json_dict_iter(const char *key, QObject *obj, void *opaque)
 {
-    Visitor *v = opaque;
+    ToJson *s = opaque;

-    qobject_visit_output(v, key, obj);
+    if (!*s->errp) {
+        qobject_visit_output(s->v, key, obj, s->errp);
+    }
 }

 static void to_json_list_iter(QObject *obj, void *opaque)
 {
-    Visitor *v = opaque;
+    ToJson *s = opaque;

-    qobject_visit_output(v, NULL, obj);
+    if (!*s->errp) {
+        qobject_visit_output(s->v, NULL, obj, s->errp);
+    }
 }

-void qobject_visit_output(Visitor *v, const char *name, const QObject *obj)
+void qobject_visit_output(Visitor *v, const char *name, const QObject *obj,
+                          Error **errp)
 {
+    Error *err = NULL;
+    ToJson s = { .v = v, .errp = &err, };
+
     switch (qobject_type(obj)) {
     case QTYPE_QNULL:
-        visit_type_null(v, name, &error_abort);
+        visit_type_null(v, name, errp);
         break;
     case QTYPE_QINT: {
         int64_t val = qint_get_int(qobject_to_qint(obj));
-        visit_type_int64(v, name, &val, &error_abort);
+        visit_type_int64(v, name, &val, errp);
         break;
     }
     case QTYPE_QSTRING: {
         const char *str = qstring_get_str(qobject_to_qstring(obj));
-        /* FIXME: no way inform user if we modified the string to
-         * avoid encoding errors */
-        visit_type_str(v, name, (char **)&str, &error_abort);
+        visit_type_str(v, name, (char **)&str, errp);
         break;
     }
     case QTYPE_QDICT: {
         QDict *val = qobject_to_qdict(obj);
-        visit_start_struct(v, name, NULL, 0, &error_abort);
-        qdict_iter(val, to_json_dict_iter, v);
-        visit_check_struct(v, &error_abort);
+        visit_start_struct(v, name, NULL, 0, &err);
+        if (!err) {
+            qdict_iter(val, to_json_dict_iter, &s);
+        }
+        if (!err) {
+            visit_check_struct(v, &err);
+        }
+        error_propagate(errp, err);
         visit_end_struct(v, NULL);
         break;
     }
     case QTYPE_QLIST: {
         QList *val = qobject_to_qlist(obj);
-        visit_start_list(v, name, NULL, 0, &error_abort);
-        qlist_iter(val, to_json_list_iter, v);
+        visit_start_list(v, name, NULL, 0, &err);
+        if (!err) {
+            qlist_iter(val, to_json_list_iter, &s);
+        }
+        error_propagate(errp, err);
         visit_end_list(v, NULL);
         break;
     }
     case QTYPE_QFLOAT: {
         double val = qfloat_get_double(qobject_to_qfloat(obj));
-        /* FIXME: no way inform user if we generated invalid JSON */
-        visit_type_number(v, name, &val, NULL);
+        visit_type_number(v, name, &val, errp);
         break;
     }
     case QTYPE_QBOOL: {
         bool val = qbool_get_bool(qobject_to_qbool(obj));
-        visit_type_bool(v, name, &val, &error_abort);
+        visit_type_bool(v, name, &val, errp);
         break;
     }
     default:
@@ -138,9 +157,9 @@ void qobject_visit_output(Visitor *v, const char *name, const QObject *obj)
 QString *qobject_to_json(const QObject *obj, bool pretty)
 {
     char *str;
-    Visitor *v = json_output_visitor_new(pretty, &str);
+    Visitor *v = json_output_visitor_new(pretty, false, &str);

-    qobject_visit_output(v, NULL, obj);
+    qobject_visit_output(v, NULL, obj, &error_abort);
     visit_complete(v, &str);
     visit_free(v);
     return qstring_wrap_str(str);
@@ -222,8 +241,6 @@ int qstring_append_json_number(QString *qstring, double number)
     /* FIXME: snprintf() is locale dependent; but JSON requires
      * numbers to be formatted as if in the C locale. Dependence
      * on C locale is a pervasive issue in QEMU. */
-    /* FIXME: This risks printing Inf or NaN, which are not valid
-     * JSON values. */
     /* FIXME: the default precision of 6 for %f often causes
      * rounding errors; we should be using DBL_DECIMAL_DIG (17),
      * and only rounding to a shorter number if the result would
diff --git a/tests/check-qobject-json.c b/tests/check-qobject-json.c
index 267fc67..b4f3dc4 100644
--- a/tests/check-qobject-json.c
+++ b/tests/check-qobject-json.c
@@ -12,6 +12,7 @@
  */
 #include "qemu/osdep.h"
 #include <glib.h>
+#include <math.h>

 #include "qapi/qmp/types.h"
 #include "qapi/qmp/qobject-json.h"
@@ -920,6 +921,8 @@ static void simple_number(void)
 static void float_number(void)
 {
     int i;
+    QFloat *qfloat;
+    QString *str;
     struct {
         const char *encoded;
         double decoded;
@@ -934,7 +937,6 @@ static void float_number(void)

     for (i = 0; test_cases[i].encoded; i++) {
         QObject *obj;
-        QFloat *qfloat;

         obj = qobject_from_json(test_cases[i].encoded);
         g_assert(obj != NULL);
@@ -944,8 +946,6 @@ static void float_number(void)
         g_assert(qfloat_get_double(qfloat) == test_cases[i].decoded);

         if (test_cases[i].skip == 0) {
-            QString *str;
-
             str = qobject_to_json(obj, false);
             g_assert(strcmp(qstring_get_str(str), test_cases[i].encoded) == 0);
             QDECREF(str);
@@ -953,6 +953,15 @@ static void float_number(void)

         QDECREF(qfloat);
     }
+
+    /* Although Infinity/NaN are not valid JSON, we can generate them
+     * on output as an extension.
+     * FIXME: The parser doesn't handle infinity/NaN on input. */
+    qfloat = qfloat_from_double(INFINITY);
+    str = qobject_to_json(QOBJECT(qfloat), false);
+    g_assert_cmpstr(qstring_get_str(str), ==, "inf");
+    QDECREF(str);
+    QDECREF(qfloat);
 }

 static void vararg_number(void)
diff --git a/tests/test-json-output-visitor.c b/tests/test-json-output-visitor.c
index fd14e26..e0478bc 100644
--- a/tests/test-json-output-visitor.c
+++ b/tests/test-json-output-visitor.c
@@ -14,6 +14,7 @@

 #include "qemu/osdep.h"
 #include <glib.h>
+#include <math.h>

 #include "qemu-common.h"
 #include "qapi/json-output-visitor.h"
@@ -32,7 +33,7 @@ static void visitor_output_setup(TestOutputVisitorData *data,
                                  const void *arg)
 {
     const bool *pretty = arg;
-    data->ov = json_output_visitor_new(*pretty, &data->str);
+    data->ov = json_output_visitor_new(*pretty, true, &data->str);
     g_assert(data->ov);
     data->pretty = *pretty;
 }
@@ -89,13 +90,18 @@ static void test_visitor_out_number(TestOutputVisitorData *data,
 {
     double value = 3.14;
     const char *out;
+    Error *err = NULL;

     visit_type_number(data->ov, NULL, &value, &error_abort);

     out = visitor_get(data);
     g_assert_cmpstr(out, ==, "3.14");

-    /* FIXME: JSON requires finite numbers */
+    /* JSON requires finite numbers */
+    visitor_reset(data);
+    value = INFINITY;
+    visit_type_number(data->ov, NULL, &value, &err);
+    error_free_or_abort(&err);
 }

 static void test_visitor_out_string(TestOutputVisitorData *data,
@@ -103,11 +109,18 @@ static void test_visitor_out_string(TestOutputVisitorData *data,
 {
     char *string = (char *) "Q E M U";
     const char *out;
+    Error *err = NULL;

     visit_type_str(data->ov, NULL, &string, &error_abort);

     out = visitor_get(data);
     g_assert_cmpstr(out, ==, "\"Q E M U\"");
+
+    /* JSON requires valid UTF-8 encodings */
+    visitor_reset(data);
+    string = (char *) "\xfe";
+    visit_type_str(data->ov, NULL, &string, &err);
+    error_free_or_abort(&err);
 }

 static void test_visitor_out_enum(TestOutputVisitorData *data,
diff --git a/tests/test-qmp-output-visitor.c b/tests/test-qmp-output-visitor.c
index 0db0b19..68e074f 100644
--- a/tests/test-qmp-output-visitor.c
+++ b/tests/test-qmp-output-visitor.c
@@ -17,6 +17,7 @@

 #include "qemu/osdep.h"
 #include <glib.h>
+#include <math.h>

 #include "qemu-common.h"
 #include "qapi/error.h"
@@ -97,6 +98,14 @@ static void test_visitor_out_number(TestOutputVisitorData *data,
     obj = visitor_get(data);
     g_assert(qobject_type(obj) == QTYPE_QFLOAT);
     g_assert(qfloat_get_double(qobject_to_qfloat(obj)) == value);
+
+    /* While JSON requires finite values, QObject does not. */
+    visitor_reset(data);
+    value = INFINITY;
+    visit_type_number(data->ov, NULL, &value, &error_abort);
+    obj = visitor_get(data);
+    g_assert(qobject_type(obj) == QTYPE_QFLOAT);
+    g_assert(qfloat_get_double(qobject_to_qfloat(obj)) == INFINITY);
 }

 static void test_visitor_out_string(TestOutputVisitorData *data,
@@ -110,6 +119,14 @@ static void test_visitor_out_string(TestOutputVisitorData *data,
     obj = visitor_get(data);
     g_assert(qobject_type(obj) == QTYPE_QSTRING);
     g_assert_cmpstr(qstring_get_str(qobject_to_qstring(obj)), ==, string);
+
+    /* While JSON requires valid UTF-8 encoding, QObject does not. */
+    visitor_reset(data);
+    string = (char *) "\xfe";
+    visit_type_str(data->ov, NULL, &string, &error_abort);
+    obj = visitor_get(data);
+    g_assert(qobject_type(obj) == QTYPE_QSTRING);
+    g_assert_cmpstr(qstring_get_str(qobject_to_qstring(obj)), ==, string);
 }

 static void test_visitor_out_no_string(TestOutputVisitorData *data,
-- 
2.5.5

  parent reply	other threads:[~2016-05-19 16:52 UTC|newest]

Thread overview: 71+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2016-05-19  4:40 [Qemu-devel] [PATCH v4 00/28] Add qapi-to-JSON and clone visitors Eric Blake
2016-05-19  4:40 ` [Qemu-devel] [PATCH v4 01/28] qapi: Rename (one) qjson.h to qobject-json.h Eric Blake
2016-06-01 15:09   ` Markus Armbruster
2016-05-19  4:40 ` [Qemu-devel] [PATCH v4 02/28] qapi: Improve use of qmp/types.h Eric Blake
2016-05-19  4:40 ` [Qemu-devel] [PATCH v4 03/28] qemu-img: Don't leak errors when outputting JSON Eric Blake
2016-06-01 15:25   ` Markus Armbruster
2016-05-19  4:40 ` [Qemu-devel] [PATCH v4 04/28] qapi: Add parameter to visit_end_* Eric Blake
2016-06-01 15:36   ` Markus Armbruster
2016-06-07 23:20     ` Eric Blake
2016-05-19  4:40 ` [Qemu-devel] [PATCH v4 05/28] qapi: Add new visit_free() function Eric Blake
2016-06-01 16:03   ` Markus Armbruster
2016-06-03 11:46     ` Markus Armbruster
2016-05-19  4:40 ` [Qemu-devel] [PATCH v4 06/28] opts-visitor: Favor " Eric Blake
2016-06-01 16:06   ` Markus Armbruster
2016-05-19  4:40 ` [Qemu-devel] [PATCH v4 07/28] string-input-visitor: " Eric Blake
2016-06-01 16:13   ` Markus Armbruster
2016-05-19  4:40 ` [Qemu-devel] [PATCH v4 08/28] qmp-input-visitor: " Eric Blake
2016-06-01 16:19   ` Markus Armbruster
2016-05-19  4:40 ` [Qemu-devel] [PATCH v4 09/28] string-output-visitor: " Eric Blake
2016-05-19  4:40 ` [Qemu-devel] [PATCH v4 10/28] qmp-output-visitor: " Eric Blake
2016-05-19  4:40 ` [Qemu-devel] [PATCH v4 11/28] tests: Factor out common code in qapi output tests Eric Blake
2016-06-01 16:33   ` Markus Armbruster
2016-05-19  4:40 ` [Qemu-devel] [PATCH v4 12/28] qapi: Add new visit_complete() function Eric Blake
2016-06-01 17:02   ` Markus Armbruster
2016-05-19  4:40 ` [Qemu-devel] [PATCH v4 13/28] qapi: Add new clone visitor Eric Blake
2016-06-02 13:43   ` Markus Armbruster
2016-06-03 14:04     ` Markus Armbruster
2016-06-09  4:15     ` Eric Blake
2016-05-19  4:41 ` [Qemu-devel] [PATCH v4 14/28] sockets: Use new QAPI cloning Eric Blake
2016-05-19  4:41 ` [Qemu-devel] [PATCH v4 15/28] replay: " Eric Blake
2016-05-19  4:41 ` [Qemu-devel] [PATCH v4 16/28] qapi: Factor out JSON string escaping Eric Blake
2016-06-02 14:53   ` Markus Armbruster
2016-05-19  4:41 ` [Qemu-devel] [PATCH v4 17/28] qapi: Factor out JSON number formatting Eric Blake
2016-06-02 15:02   ` Markus Armbruster
2016-06-02 15:06     ` Eric Blake
2016-06-03  9:02       ` Markus Armbruster
2016-06-09 16:07         ` Eric Blake
2016-06-13  8:22           ` Markus Armbruster
2016-06-13 12:34             ` Eric Blake
2016-06-13 14:41               ` Markus Armbruster
2016-05-19  4:41 ` [Qemu-devel] [PATCH v4 18/28] qapi: Add qstring_append_printf() Eric Blake
2016-05-19  4:41 ` [Qemu-devel] [PATCH v4 19/28] qapi: Use qstring_append_chr() where appropriate Eric Blake
2016-05-19  4:41 ` [Qemu-devel] [PATCH v4 20/28] qstring: Add qstring_consume_str() Eric Blake
2016-05-19  4:41 ` [Qemu-devel] [PATCH v4 21/28] qstring: Add qstring_wrap_str() Eric Blake
2016-06-02 15:21   ` Markus Armbruster
2016-06-09 16:31     ` Eric Blake
2016-05-19  4:41 ` [Qemu-devel] [PATCH v4 22/28] qobject: Consolidate qobject_to_json() calls Eric Blake
2016-06-02 15:32   ` Markus Armbruster
2016-05-19  4:41 ` [Qemu-devel] [PATCH v4 23/28] tests: Test qobject_to_json() pretty formatting Eric Blake
2016-05-19  4:41 ` [Qemu-devel] [PATCH v4 24/28] qapi: Add JSON output visitor Eric Blake
2016-06-03  7:39   ` Markus Armbruster
2016-06-03 12:53     ` Eric Blake
2016-06-03 14:09       ` Markus Armbruster
2016-05-19  4:41 ` [Qemu-devel] [PATCH v4 25/28] qapi: Support pretty printing in " Eric Blake
2016-06-03  7:56   ` Markus Armbruster
2016-06-03 12:55     ` Eric Blake
2016-06-03 14:08       ` Markus Armbruster
2016-05-19  4:41 ` [Qemu-devel] [PATCH v4 26/28] qobject: Implement qobject_to_json() atop JSON visitor Eric Blake
2016-06-03  8:25   ` Markus Armbruster
2016-05-19  4:41 ` [Qemu-devel] [PATCH v4 27/28] qapi: Add 'any' support to JSON output Eric Blake
2016-06-03  8:29   ` Markus Armbruster
2016-05-19  4:41 ` [Qemu-devel] [PATCH v4 28/28] qemu-img: Use new JSON output formatter Eric Blake
2016-05-19 14:58 ` [Qemu-devel] [PATCH v4 00/28] Add qapi-to-JSON and clone visitors Eric Blake
2016-05-19 16:52 ` Eric Blake [this message]
2016-05-19 20:18   ` [Qemu-devel] [PATCH v4 29/28] qapi: Add strict mode to JSON output visitor Eric Blake
2016-06-03  8:36     ` Markus Armbruster
2016-06-03  9:21   ` Markus Armbruster
2016-05-19 17:05 ` [Qemu-devel] [PATCH v4 00/28] Add qapi-to-JSON and clone visitors Markus Armbruster
2016-06-03 12:09 ` Markus Armbruster
2016-06-09 16:16   ` Eric Blake
2016-06-13  8:26     ` Markus Armbruster

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1463676743-22133-1-git-send-email-eblake@redhat.com \
    --to=eblake@redhat.com \
    --cc=armbru@redhat.com \
    --cc=kwolf@redhat.com \
    --cc=lcapitulino@redhat.com \
    --cc=mdroth@linux.vnet.ibm.com \
    --cc=mreitz@redhat.com \
    --cc=qemu-block@nongnu.org \
    --cc=qemu-devel@nongnu.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).