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_size
|
||||||
g_variant_get_data
|
g_variant_get_data
|
||||||
g_variant_store
|
g_variant_store
|
||||||
|
g_variant_new_from_data
|
||||||
|
g_variant_byteswap
|
||||||
|
g_variant_get_normal_form
|
||||||
|
g_variant_is_normal_form
|
||||||
|
|
||||||
<SUBSECTION>
|
<SUBSECTION>
|
||||||
g_variant_hash
|
g_variant_hash
|
||||||
|
@ -1705,6 +1705,7 @@ g_variant_get_child_value
|
|||||||
g_variant_get_size
|
g_variant_get_size
|
||||||
g_variant_get_data
|
g_variant_get_data
|
||||||
g_variant_store
|
g_variant_store
|
||||||
|
g_variant_is_normal_form
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if IN_FILE(__G_VARIANT_C__)
|
#if IN_FILE(__G_VARIANT_C__)
|
||||||
@ -1788,6 +1789,10 @@ g_variant_builder_add
|
|||||||
g_variant_get_child
|
g_variant_get_child
|
||||||
g_variant_iter_next
|
g_variant_iter_next
|
||||||
g_variant_iter_loop
|
g_variant_iter_loop
|
||||||
|
|
||||||
|
g_variant_new_from_data
|
||||||
|
g_variant_get_normal_form
|
||||||
|
g_variant_byteswap
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -709,10 +709,9 @@ g_variant_get_size (GVariant *value)
|
|||||||
* @returns: the serialised form of @value, or %NULL
|
* @returns: the serialised form of @value, or %NULL
|
||||||
*
|
*
|
||||||
* Returns a pointer to the serialised form of a #GVariant instance.
|
* 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
|
* The returned data may not be in fully-normalised form if read from an
|
||||||
* fully-normalised form if read from an untrusted source. The returned
|
* untrusted source. The returned data must not be freed; it remains
|
||||||
* data must not be freed; it remains valid for as long as @value
|
* valid for as long as @value exists.
|
||||||
* exists.
|
|
||||||
*
|
*
|
||||||
* If @value is a fixed-sized value that was deserialised from a
|
* If @value is a fixed-sized value that was deserialised from a
|
||||||
* corrupted serialised container then %NULL may be returned. In this
|
* 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_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__
|
#define __G_VARIANT_CORE_C__
|
||||||
#include "galiasdef.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;
|
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 */
|
/* Epilogue {{{1 */
|
||||||
#define __G_VARIANT_C__
|
#define __G_VARIANT_C__
|
||||||
#include "galiasdef.c"
|
#include "galiasdef.c"
|
||||||
|
@ -139,6 +139,16 @@ guint g_variant_hash (gconstp
|
|||||||
gboolean g_variant_equal (gconstpointer one,
|
gboolean g_variant_equal (gconstpointer one,
|
||||||
gconstpointer two);
|
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;
|
typedef struct _GVariantIter GVariantIter;
|
||||||
struct _GVariantIter {
|
struct _GVariantIter {
|
||||||
/*< private >*/
|
/*< private >*/
|
||||||
|
@ -3385,6 +3385,77 @@ test_hashing (void)
|
|||||||
g_variant_type_info_assert_no_infos ();
|
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
|
int
|
||||||
main (int argc, char **argv)
|
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/valist", test_valist);
|
||||||
g_test_add_func ("/gvariant/builder-memory", test_builder_memory);
|
g_test_add_func ("/gvariant/builder-memory", test_builder_memory);
|
||||||
g_test_add_func ("/gvariant/hashing", test_hashing);
|
g_test_add_func ("/gvariant/hashing", test_hashing);
|
||||||
|
g_test_add_func ("/gvariant/byteswap", test_gv_byteswap);
|
||||||
|
|
||||||
return g_test_run ();
|
return g_test_run ();
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user