Commit 9dea0253 authored by Allison Karlitskaya's avatar Allison Karlitskaya

GVariant: add loading, byteswapping, normalisation

parent 1ac590b7
......@@ -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
......
......@@ -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
......
......@@ -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"
......@@ -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"
......
......@@ -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 >*/
......
......@@ -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 ();
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment