mirror of
https://gitlab.gnome.org/GNOME/glib.git
synced 2024-12-25 15:06:14 +01:00
GVariant: add loading, byteswapping, normalisation
This commit is contained in:
parent
1ac590b7f7
commit
9dea0253a3
@ -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"
|
||||
|
207
glib/gvariant.c
207
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"
|
||||
|
@ -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 ();
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user