[glib] GVariant: add loading, byteswapping, normalisation
- From: Ryan Lortie <ryanl src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [glib] GVariant: add loading, byteswapping, normalisation
- Date: Sun, 14 Mar 2010 19:56:44 +0000 (UTC)
commit 9dea0253a3ded0e32daa33f11850797109018326
Author: Ryan Lortie <desrt desrt ca>
Date: Sun Mar 14 15:55:48 2010 -0400
GVariant: add loading, byteswapping, normalisation
docs/reference/glib/glib-sections.txt | 4 +
glib/glib.symbols | 5 +
glib/gvariant-core.c | 61 +++++++++-
glib/gvariant.c | 207 +++++++++++++++++++++++++++++++++
glib/gvariant.h | 10 ++
glib/tests/gvariant.c | 72 ++++++++++++
6 files changed, 355 insertions(+), 4 deletions(-)
---
diff --git a/docs/reference/glib/glib-sections.txt b/docs/reference/glib/glib-sections.txt
index 5ae7413..05b06cd 100644
--- a/docs/reference/glib/glib-sections.txt
+++ b/docs/reference/glib/glib-sections.txt
@@ -2857,6 +2857,10 @@ g_variant_get_fixed_array
g_variant_get_size
g_variant_get_data
g_variant_store
+g_variant_new_from_data
+g_variant_byteswap
+g_variant_get_normal_form
+g_variant_is_normal_form
<SUBSECTION>
g_variant_hash
diff --git a/glib/glib.symbols b/glib/glib.symbols
index 9d0c2a1..ee9da31 100644
--- a/glib/glib.symbols
+++ b/glib/glib.symbols
@@ -1705,6 +1705,7 @@ g_variant_get_child_value
g_variant_get_size
g_variant_get_data
g_variant_store
+g_variant_is_normal_form
#endif
#if IN_FILE(__G_VARIANT_C__)
@@ -1788,6 +1789,10 @@ g_variant_builder_add
g_variant_get_child
g_variant_iter_next
g_variant_iter_loop
+
+g_variant_new_from_data
+g_variant_get_normal_form
+g_variant_byteswap
#endif
#endif
diff --git a/glib/gvariant-core.c b/glib/gvariant-core.c
index 51e1fac..183fa09 100644
--- a/glib/gvariant-core.c
+++ b/glib/gvariant-core.c
@@ -709,10 +709,9 @@ g_variant_get_size (GVariant *value)
* @returns: the serialised form of @value, or %NULL
*
* Returns a pointer to the serialised form of a #GVariant instance.
- * The returned data is in machine native byte order but may not be in
- * fully-normalised form if read from an untrusted source. The returned
- * data must not be freed; it remains valid for as long as @value
- * exists.
+ * The returned data may not be in fully-normalised form if read from an
+ * untrusted source. The returned data must not be freed; it remains
+ * valid for as long as @value exists.
*
* If @value is a fixed-sized value that was deserialised from a
* corrupted serialised container then %NULL may be returned. In this
@@ -875,5 +874,59 @@ g_variant_store (GVariant *value,
g_variant_unlock (value);
}
+/**
+ * g_variant_is_normal_form:
+ * @value: a #GVariant instance
+ * @returns: %TRUE if @value is in normal form
+ *
+ * Checks if @value is in normal form.
+ *
+ * The main reason to do this is to detect if a given chunk of
+ * serialised data is in normal form: load the data into a #GVariant
+ * using g_variant_create_from_data() and then use this function to
+ * check.
+ *
+ * If @value is found to be in normal form then it will be marked as
+ * being trusted. If the value was already marked as being trusted then
+ * this function will immediately return %TRUE.
+ *
+ * Since: 2.24
+ **/
+gboolean
+g_variant_is_normal_form (GVariant *value)
+{
+ if (value->state & STATE_TRUSTED)
+ return TRUE;
+
+ g_variant_lock (value);
+
+ if (value->state & STATE_SERIALISED)
+ {
+ GVariantSerialised serialised = {
+ value->type_info,
+ (gpointer) value->contents.serialised.data,
+ value->size
+ };
+
+ if (g_variant_serialised_is_normal (serialised))
+ value->state |= STATE_TRUSTED;
+ }
+ else
+ {
+ gboolean normal = TRUE;
+ gsize i;
+
+ for (i = 0; i < value->contents.tree.n_children; i++)
+ normal &= g_variant_is_normal_form (value->contents.tree.children[i]);
+
+ if (normal)
+ value->state |= STATE_TRUSTED;
+ }
+
+ g_variant_unlock (value);
+
+ return (value->state & STATE_TRUSTED) != 0;
+}
+
#define __G_VARIANT_CORE_C__
#include "galiasdef.c"
diff --git a/glib/gvariant.c b/glib/gvariant.c
index 1cacb63..342eeeb 100644
--- a/glib/gvariant.c
+++ b/glib/gvariant.c
@@ -3912,6 +3912,213 @@ g_variant_iter_loop (GVariantIter *iter,
return value != NULL;
}
+/* Serialised data {{{1 */
+static GVariant *
+g_variant_deep_copy (GVariant *value)
+{
+ switch (g_variant_classify (value))
+ {
+ case G_VARIANT_CLASS_MAYBE:
+ case G_VARIANT_CLASS_ARRAY:
+ case G_VARIANT_CLASS_TUPLE:
+ case G_VARIANT_CLASS_DICT_ENTRY:
+ case G_VARIANT_CLASS_VARIANT:
+ {
+ GVariantBuilder builder;
+ GVariantIter iter;
+ GVariant *child;
+
+ g_variant_builder_init (&builder, g_variant_get_type (value));
+ g_variant_iter_init (&iter, value);
+
+ while ((child = g_variant_iter_next_value (&iter)))
+ {
+ g_variant_builder_add_value (&builder, g_variant_deep_copy (child));
+ g_variant_unref (child);
+ }
+
+ return g_variant_builder_end (&builder);
+ }
+
+ case G_VARIANT_CLASS_BOOLEAN:
+ return g_variant_new_boolean (g_variant_get_boolean (value));
+
+ case G_VARIANT_CLASS_BYTE:
+ return g_variant_new_byte (g_variant_get_byte (value));
+
+ case G_VARIANT_CLASS_INT16:
+ return g_variant_new_int16 (g_variant_get_int16 (value));
+
+ case G_VARIANT_CLASS_UINT16:
+ return g_variant_new_uint16 (g_variant_get_uint16 (value));
+
+ case G_VARIANT_CLASS_INT32:
+ return g_variant_new_int32 (g_variant_get_int32 (value));
+
+ case G_VARIANT_CLASS_UINT32:
+ return g_variant_new_uint32 (g_variant_get_uint32 (value));
+
+ case G_VARIANT_CLASS_INT64:
+ return g_variant_new_int64 (g_variant_get_int64 (value));
+
+ case G_VARIANT_CLASS_UINT64:
+ return g_variant_new_uint64 (g_variant_get_uint64 (value));
+
+ case G_VARIANT_CLASS_HANDLE:
+ return g_variant_new_handle (g_variant_get_handle (value));
+
+ case G_VARIANT_CLASS_DOUBLE:
+ return g_variant_new_double (g_variant_get_double (value));
+
+ case G_VARIANT_CLASS_STRING:
+ return g_variant_new_string (g_variant_get_string (value, NULL));
+
+ case G_VARIANT_CLASS_OBJECT_PATH:
+ return g_variant_new_object_path (g_variant_get_string (value, NULL));
+
+ case G_VARIANT_CLASS_SIGNATURE:
+ return g_variant_new_signature (g_variant_get_string (value, NULL));
+ }
+
+ g_assert_not_reached ();
+}
+
+/**
+ * g_variant_get_normal_form:
+ * @value: a #GVariant
+ * @returns: a trusted #GVariant
+ *
+ * Gets a #GVariant instance that has the same value as @value and is
+ * trusted to be in normal form.
+ *
+ * If @value is already trusted to be in normal form then a new
+ * reference to @value is returned.
+ *
+ * If @value is not already trusted, then it is scanned to check if it
+ * is in normal form. If it is found to be in normal form then it is
+ * marked as trusted and a new reference to it is returned.
+ *
+ * If @value is found not to be in normal form then a new trusted
+ * #GVariant is created with the same value as @value.
+ *
+ * It makes sense to call this function if you've received #GVariant
+ * data from untrusted sources and you want to ensure your serialised
+ * output is definitely in normal form.
+ *
+ * Since: 2.24
+ **/
+GVariant *
+g_variant_get_normal_form (GVariant *value)
+{
+ GVariant *trusted;
+
+ if (g_variant_is_normal_form (value))
+ return g_variant_ref (value);
+
+ trusted = g_variant_deep_copy (value);
+ g_assert (g_variant_is_trusted (trusted));
+
+ return g_variant_ref_sink (trusted);
+}
+
+/**
+ * g_variant_byteswap:
+ * @value: a #GVariant
+ * @returns: the byteswapped form of @value
+ *
+ * Performs a byteswapping operation on the contents of @value. The
+ * result is that all multi-byte numeric data contained in @value is
+ * byteswapped. That includes 16, 32, and 64bit signed and unsigned
+ * integers as well as file handles and double precision floating point
+ * values.
+ *
+ * This function is an identity mapping on any value that does not
+ * contain multi-byte numeric data. That include strings, booleans,
+ * bytes and containers containing only these things (recursively).
+ *
+ * The returned value is always in normal form and is marked as trusted.
+ *
+ * Since: 2.24
+ **/
+GVariant *
+g_variant_byteswap (GVariant *value)
+{
+ GVariantSerialised serialised;
+ GVariant *trusted;
+ GBuffer *buffer;
+ GVariant *new;
+
+ trusted = g_variant_get_normal_form (value);
+ serialised.type_info = g_variant_get_type_info (trusted);
+ serialised.size = g_variant_get_size (trusted);
+ serialised.data = g_malloc (serialised.size);
+ g_variant_store (trusted, serialised.data);
+ g_variant_unref (trusted);
+
+ g_variant_serialised_byteswap (serialised);
+
+ buffer = g_buffer_new_take_data (serialised.data, serialised.size);
+ new = g_variant_new_from_buffer (g_variant_get_type (value), buffer, TRUE);
+ g_buffer_unref (buffer);
+
+ return g_variant_ref_sink (new);
+}
+
+/**
+ * g_variant_new_from_data:
+ * @type: a #GVariantType
+ * @data: the serialised data
+ * @size: the size of @data
+ * @trusted: %TRUE if @data is definitely in normal form
+ * @notify: function to call when @data is no longer needed
+ * @user_data: data for @notify
+ * @returns: a new floating #GVariant of type @type
+ *
+ * Creates a new #GVariant instance from serialised data.
+ *
+ * @type is the type of #GVariant instance that will be constructed.
+ * The interpretation of @data depends on knowing the type.
+ *
+ * @data is not modified by this function and must remain valid with an
+ * unchanging value until such a time as @notify is called with
+ * @user_data. If the contents of @data change before that time then
+ * the result is undefined.
+ *
+ * If @data is trusted to be serialised data in normal form then
+ * @trusted should be %TRUE. This applies to serialised data created
+ * within this process or read from a trusted location on the disk (such
+ * as a file installed in /usr/lib alongside your application). You
+ * should set trusted to %FALSE if @data is read from the network, a
+ * file in the user's home directory, etc.
+ *
+ * @notify will be called with @user_data when @data is no longer
+ * needed. The exact time of this call is unspecified and might even be
+ * before this function returns.
+ *
+ * Since: 2.24
+ **/
+GVariant *
+g_variant_new_from_data (const GVariantType *type,
+ gconstpointer data,
+ gsize size,
+ gboolean trusted,
+ GDestroyNotify notify,
+ gpointer user_data)
+{
+ GVariant *value;
+ GBuffer *buffer;
+
+ if (notify)
+ buffer = g_buffer_new_from_pointer (data, size, notify, user_data);
+ else
+ buffer = g_buffer_new_from_static_data (data, size);
+
+ value = g_variant_new_from_buffer (type, buffer, trusted);
+ g_buffer_unref (buffer);
+
+ return value;
+}
+
/* Epilogue {{{1 */
#define __G_VARIANT_C__
#include "galiasdef.c"
diff --git a/glib/gvariant.h b/glib/gvariant.h
index abf8102..19c57c6 100644
--- a/glib/gvariant.h
+++ b/glib/gvariant.h
@@ -139,6 +139,16 @@ guint g_variant_hash (gconstp
gboolean g_variant_equal (gconstpointer one,
gconstpointer two);
+GVariant * g_variant_get_normal_form (GVariant *variant);
+gboolean g_variant_is_normal_form (GVariant *variant);
+GVariant * g_variant_byteswap (GVariant *variant);
+GVariant * g_variant_new_from_data (const GVariantType *type,
+ gconstpointer data,
+ gsize size,
+ gboolean trusted,
+ GDestroyNotify notify,
+ gpointer user_data);
+
typedef struct _GVariantIter GVariantIter;
struct _GVariantIter {
/*< private >*/
diff --git a/glib/tests/gvariant.c b/glib/tests/gvariant.c
index af648a7..550fdac 100644
--- a/glib/tests/gvariant.c
+++ b/glib/tests/gvariant.c
@@ -3385,6 +3385,77 @@ test_hashing (void)
g_variant_type_info_assert_no_infos ();
}
+static void
+test_gv_byteswap ()
+{
+#if G_BYTE_ORDER == G_LITTLE_ENDIAN
+# define native16(x) x, 0
+# define swapped16(x) 0, x
+#else
+# define native16(x) 0, x
+# define swapped16(x) x, 0
+#endif
+ /* all kinds of of crazy randomised testing already performed on the
+ * byteswapper in the /gvariant/serialiser/byteswap test and all kinds
+ * of crazy randomised testing performed against the serialiser
+ * normalisation functions in the /gvariant/serialiser/fuzz/ tests.
+ *
+ * just test a few simple cases here to make sure they each work
+ */
+ guchar valid_data[] = { 'a', '\0', swapped16(66), 2,
+ 0,
+ 'b', '\0', swapped16(77), 2,
+ 5, 11 };
+ guchar corrupt_data[] = { 'a', '\0', swapped16(66), 2,
+ 0,
+ 'b', '\0', swapped16(77), 2,
+ 6, 11 };
+ GVariant *value, *swapped, *reswapped;
+ gchar *string, *string2;
+
+
+ /* trusted */
+ value = g_variant_new_from_data (G_VARIANT_TYPE ("a(sn)"),
+ valid_data, sizeof valid_data, TRUE,
+ NULL, NULL);
+ swapped = g_variant_byteswap (value);
+ g_variant_unref (value);
+ g_assert (g_variant_get_size (swapped) == 13);
+ string = g_variant_print (swapped, FALSE);
+ g_variant_unref (swapped);
+ g_assert_cmpstr (string, ==, "[('a', 66), ('b', 77)]");
+ g_free (string);
+
+ /* untrusted but valid */
+ value = g_variant_new_from_data (G_VARIANT_TYPE ("a(sn)"),
+ valid_data, sizeof valid_data, FALSE,
+ NULL, NULL);
+ swapped = g_variant_byteswap (value);
+ g_variant_unref (value);
+ g_assert (g_variant_get_size (swapped) == 13);
+ string = g_variant_print (swapped, FALSE);
+ g_variant_unref (swapped);
+ g_assert_cmpstr (string, ==, "[('a', 66), ('b', 77)]");
+ g_free (string);
+
+ /* untrusted, invalid */
+ value = g_variant_new_from_data (G_VARIANT_TYPE ("a(sn)"),
+ corrupt_data, sizeof corrupt_data, FALSE,
+ NULL, NULL);
+ string = g_variant_print (value, FALSE);
+ swapped = g_variant_byteswap (value);
+ g_variant_unref (value);
+ g_assert (g_variant_get_size (swapped) == 13);
+ value = g_variant_byteswap (swapped);
+ g_variant_unref (swapped);
+ string2 = g_variant_print (value, FALSE);
+ g_assert (g_variant_get_size (value) == 13);
+ g_variant_unref (value);
+ g_assert_cmpstr (string, ==, string2);
+ g_free (string2);
+ g_free (string);
+}
+
int
main (int argc, char **argv)
{
@@ -3418,6 +3489,7 @@ main (int argc, char **argv)
g_test_add_func ("/gvariant/valist", test_valist);
g_test_add_func ("/gvariant/builder-memory", test_builder_memory);
g_test_add_func ("/gvariant/hashing", test_hashing);
+ g_test_add_func ("/gvariant/byteswap", test_gv_byteswap);
return g_test_run ();
}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]