X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=gio%2Fgdbusmessage.c;h=bfe91e68452009526165cfa2f45a46116355e456;hb=853692bdfd9f8a87aed70d21f643dc13b57c92d1;hp=b41f6497b3da7f3091f5dcc11dfa595e06c6bef4;hpb=d108ada4b98cb50fb1517f55c4f09acdaf3de471;p=platform%2Fupstream%2Fglib.git diff --git a/gio/gdbusmessage.c b/gio/gdbusmessage.c index b41f649..bfe91e6 100644 --- a/gio/gdbusmessage.c +++ b/gio/gdbusmessage.c @@ -49,6 +49,7 @@ #include "gdbusprivate.h" #ifdef G_OS_UNIX +#include "gkdbus.h" #include "gunixfdlist.h" #endif @@ -204,12 +205,12 @@ g_memory_buffer_read_uint64 (GMemoryBuffer *mbuf) #define MIN_ARRAY_SIZE 128 -static gint -g_nearest_pow (gint num) +static gsize +g_nearest_pow (gsize num) { - gint n = 1; + gsize n = 1; - while (n < num) + while (n < num && n > 0) n <<= 1; return n; @@ -261,12 +262,10 @@ g_memory_buffer_write (GMemoryBuffer *mbuf, TODO: This wastes a lot of memory at large buffer sizes. Figure out a more rational allocation strategy. */ new_size = g_nearest_pow (mbuf->pos + count); - /* Check for overflow again. We have only checked if - pos + count > G_MAXSIZE, but it only catches the case of writing - more than 4GiB total on a 32-bit system. There's still the problem - of g_nearest_pow overflowing above 0x7fffffff, so we're - effectively limited to 2GiB. */ - if (new_size < mbuf->len) + /* Check for overflow again. We have checked if + pos + count > G_MAXSIZE, but now check if g_nearest_pow () has + overflowed */ + if (new_size == 0) return FALSE; new_size = MAX (new_size, MIN_ARRAY_SIZE); @@ -974,6 +973,38 @@ g_dbus_message_set_serial (GDBusMessage *message, /* ---------------------------------------------------------------------------------------------------- */ +/** + * _g_dbus_message_get_protocol_ver: + * To remove - more info [1] + * [1] https://bugzilla.gnome.org/show_bug.cgi?id=721861 + */ +guint32 +_g_dbus_message_get_protocol_ver (GDBusMessage *message) +{ + g_return_val_if_fail (G_IS_DBUS_MESSAGE (message), 0); + return message->major_protocol_version; +} + +/** + * _g_dbus_message_set_protocol_ver: + * To remove - more info [1] + * [1] https://bugzilla.gnome.org/show_bug.cgi?id=721861 + */ +void +_g_dbus_message_set_protocol_ver (GDBusMessage *message, + guint32 protocol_ver) +{ + g_return_if_fail (G_IS_DBUS_MESSAGE (message)); + + if (message->locked) + { + g_warning ("%s: Attempted to modify a locked message", G_STRFUNC); + return; + } + + message->major_protocol_version = protocol_ver; +} + /* TODO: need GI annotations to specify that any guchar value goes for header_field */ /** @@ -1205,6 +1236,27 @@ g_dbus_message_set_unix_fd_list (GDBusMessage *message, /* ---------------------------------------------------------------------------------------------------- */ +static guint +get_type_fixed_size (const GVariantType *type) +{ + /* NB: we do not treat 'b' as fixed-size here because GVariant and + * D-Bus disagree about the size. + */ + switch (*g_variant_type_peek_string (type)) + { + case 'y': + return 1; + case 'n': case 'q': + return 2; + case 'i': case 'u': case 'h': + return 4; + case 'x': case 't': case 'd': + return 8; + default: + return 0; + } +} + static gboolean validate_headers (GDBusMessage *message, GError **error) @@ -1325,7 +1377,7 @@ read_string (GMemoryBuffer *mbuf, gchar *str; const gchar *end_valid; - if (mbuf->pos + len >= mbuf->valid_len || mbuf->pos + len < mbuf->pos) + if G_UNLIKELY (mbuf->pos + len >= mbuf->valid_len || mbuf->pos + len < mbuf->pos) { mbuf->pos = mbuf->valid_len; /* G_GSIZE_FORMAT doesn't work with gettext, so we use %lu */ @@ -1341,7 +1393,7 @@ read_string (GMemoryBuffer *mbuf, return NULL; } - if (mbuf->data[mbuf->pos + len] != '\0') + if G_UNLIKELY (mbuf->data[mbuf->pos + len] != '\0') { str = g_strndup (mbuf->data + mbuf->pos, len); g_set_error (error, @@ -1357,7 +1409,7 @@ read_string (GMemoryBuffer *mbuf, str = mbuf->data + mbuf->pos; mbuf->pos += len + 1; - if (!g_utf8_validate (str, -1, &end_valid)) + if G_UNLIKELY (!g_utf8_validate (str, -1, &end_valid)) { gint offset; gchar *valid_str; @@ -1378,6 +1430,35 @@ read_string (GMemoryBuffer *mbuf, return str; } +static gconstpointer +read_bytes (GMemoryBuffer *mbuf, + gsize len, + GError **error) +{ + gconstpointer result; + + if G_UNLIKELY (mbuf->pos + len > mbuf->valid_len || mbuf->pos + len < mbuf->pos) + { + mbuf->pos = mbuf->valid_len; + /* G_GSIZE_FORMAT doesn't work with gettext, so we use %lu */ + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_ARGUMENT, + g_dngettext (GETTEXT_PACKAGE, + "Wanted to read %lu byte but only got %lu", + "Wanted to read %lu bytes but only got %lu", + (gulong)len), + (gulong)len, + (gulong)(mbuf->valid_len - mbuf->pos)); + return NULL; + } + + result = mbuf->data + mbuf->pos; + mbuf->pos += len; + + return result; +} + /* if just_align==TRUE, don't read a value, just align the input stream wrt padding */ /* returns a non-floating GVariant! */ @@ -1588,10 +1669,8 @@ parse_value_from_blob (GMemoryBuffer *buf, if (!just_align) { guint32 array_len; - goffset offset; - goffset target; const GVariantType *element_type; - GVariantBuilder builder; + guint fixed_size; array_len = g_memory_buffer_read_uint32 (buf); @@ -1614,44 +1693,82 @@ parse_value_from_blob (GMemoryBuffer *buf, goto fail; } - g_variant_builder_init (&builder, type); element_type = g_variant_type_element (type); + fixed_size = get_type_fixed_size (element_type); - if (array_len == 0) + /* Fast-path the cases like 'ay', etc. */ + if (fixed_size != 0) { - GVariant *item; - item = parse_value_from_blob (buf, - element_type, - TRUE, - indent + 2, - NULL); - g_assert (item == NULL); + gconstpointer array_data; + + if (array_len % fixed_size != 0) + { + g_set_error (&local_error, + G_IO_ERROR, + G_IO_ERROR_INVALID_ARGUMENT, + _("Encountered array of type 'a%c', expected to have a length a multiple " + "of %u bytes, but found to be %u bytes in length"), + g_variant_type_peek_string (element_type)[0], fixed_size, array_len); + goto fail; + } + + ensure_input_padding (buf, fixed_size); + array_data = read_bytes (buf, array_len, &local_error); + if (array_data == NULL) + goto fail; + + ret = g_variant_new_fixed_array (element_type, array_data, array_len / fixed_size, fixed_size); + + if (g_memory_buffer_is_byteswapped (buf)) + { + GVariant *tmp = g_variant_ref_sink (ret); + ret = g_variant_byteswap (tmp); + g_variant_unref (tmp); + } } else { - /* TODO: optimize array of primitive types */ - offset = buf->pos; - target = offset + array_len; - while (offset < target) + GVariantBuilder builder; + goffset offset; + goffset target; + + g_variant_builder_init (&builder, type); + + if (array_len == 0) { GVariant *item; item = parse_value_from_blob (buf, element_type, - FALSE, + TRUE, indent + 2, - &local_error); - if (item == NULL) + NULL); + g_assert (item == NULL); + } + else + { + offset = buf->pos; + target = offset + array_len; + while (offset < target) { - g_variant_builder_clear (&builder); - goto fail; + GVariant *item; + item = parse_value_from_blob (buf, + element_type, + FALSE, + indent + 2, + &local_error); + if (item == NULL) + { + g_variant_builder_clear (&builder); + goto fail; + } + g_variant_builder_add_value (&builder, item); + g_variant_unref (item); + offset = buf->pos; } - g_variant_builder_add_value (&builder, item); - g_variant_unref (item); - offset = buf->pos; } - } - ret = g_variant_builder_end (&builder); + ret = g_variant_builder_end (&builder); + } } break; @@ -1812,12 +1929,9 @@ parse_value_from_blob (GMemoryBuffer *buf, is_leaf = is_leaf; /* To avoid -Wunused-but-set-variable */ #endif /* DEBUG_SERIALIZER */ - /* sink the reference */ + /* sink the reference, if floating */ if (ret != NULL) - { - g_assert (g_variant_is_floating (ret)); - g_variant_ref_sink (ret); - } + g_variant_take_ref (ret); return ret; fail: @@ -2111,6 +2225,219 @@ g_dbus_message_new_from_blob (guchar *blob, /* ---------------------------------------------------------------------------------------------------- */ +/* + * _g_dbus_message_new_from_kdbus_items: + * + * Single kdbus message may contain zero, one or more items + * (PAYLOAD_VEC or PAYLOAD_MEMFD), so we need this function + * (only for kdbus transport purposes) to parse them to GDBusMessage. + * kdbus_msg_items list contain list of pointer + data pair for each received item. + * + * TODO: Add support for two and more items + */ + +GDBusMessage * +_g_dbus_message_new_from_kdbus_items (GSList *kdbus_msg_items, + GError **error) +{ + gboolean ret; + GMemoryBuffer mbuf; + GDBusMessage *message; + guchar endianness; + guchar major_protocol_version; + guint32 message_body_len; + guint32 message_headers_len; + GVariant *headers; + GVariant *item; + GVariantIter iter; + GVariant *signature; + + ret = FALSE; + + g_return_val_if_fail (kdbus_msg_items != NULL, NULL); + g_return_val_if_fail (error == NULL || *error == NULL, NULL); + + message = g_dbus_message_new (); + memset (&mbuf, 0, sizeof (mbuf)); + + /* + * MESSAGE HEADER + * message header in its entirety must be contained in a first single item + */ + mbuf.data = ((msg_part*)kdbus_msg_items->data)->data; + mbuf.len = mbuf.valid_len = ((msg_part*)kdbus_msg_items->data)->size; + + endianness = g_memory_buffer_read_byte (&mbuf); + switch (endianness) + { + case 'l': + mbuf.byte_order = G_DATA_STREAM_BYTE_ORDER_LITTLE_ENDIAN; + message->byte_order = G_DBUS_MESSAGE_BYTE_ORDER_LITTLE_ENDIAN; + break; + case 'B': + mbuf.byte_order = G_DATA_STREAM_BYTE_ORDER_BIG_ENDIAN; + message->byte_order = G_DBUS_MESSAGE_BYTE_ORDER_BIG_ENDIAN; + break; + default: + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_ARGUMENT, + _("Invalid endianness value. Expected 0x6c ('l') or 0x42 ('B') but found value 0x%02x"), + endianness); + goto out; + } + + message->type = g_memory_buffer_read_byte (&mbuf); + message->flags = g_memory_buffer_read_byte (&mbuf); + major_protocol_version = g_memory_buffer_read_byte (&mbuf); + + if (major_protocol_version != 2) + { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_ARGUMENT, + _("Invalid major protocol version. Expected 2 but found %d"), + major_protocol_version); + goto out; + } + + message_body_len = g_memory_buffer_read_uint32 (&mbuf); + message->serial = g_memory_buffer_read_uint32 (&mbuf); + + message_headers_len = g_memory_buffer_read_uint32 (&mbuf); + headers = g_variant_new_from_data (G_VARIANT_TYPE ("a{yv}"), + mbuf.data + mbuf.pos, + message_headers_len, + TRUE, + NULL, + NULL); + mbuf.pos += message_headers_len; + + if (headers == NULL) + goto out; + g_variant_iter_init (&iter, headers); + while ((item = g_variant_iter_next_value (&iter)) != NULL) + { + guchar header_field; + GVariant *value; + g_variant_get (item, + "{yv}", + &header_field, + &value); + g_dbus_message_set_header (message, header_field, value); + g_variant_unref (value); + g_variant_unref (item); + } + g_variant_unref (headers); + + signature = g_dbus_message_get_header (message, G_DBUS_MESSAGE_HEADER_FIELD_SIGNATURE); + if (signature != NULL) + { + const gchar *signature_str; + gsize signature_str_len; + + signature_str = g_variant_get_string (signature, &signature_str_len); + + if (signature_str_len > 0) + { + GVariantType *variant_type; + gchar *tupled_signature_str; + + gchar *data = NULL; + gsize size = NULL; + + if (!g_variant_is_signature (signature_str)) + { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_ARGUMENT, + _("Parsed value '%s' is not a valid D-Bus signature (for body)"), + signature_str); + goto out; + } + tupled_signature_str = g_strdup_printf ("(%s)", signature_str); + variant_type = g_variant_type_new (tupled_signature_str); + g_free (tupled_signature_str); + + /* + * MESSAGE BODY + */ + + if (g_slist_length(kdbus_msg_items) == 1) + { + /* if kdbus_msg_items has only one element, head and body are + contained in a single PAYLOAD_VEC item */ + ensure_input_padding (&mbuf,8); + data = mbuf.data + mbuf.pos; + size = message_body_len; + } + else if (g_slist_length(kdbus_msg_items) > 1) + { + /* message consists two or more items + TODO: Add support for three and more items */ + data = ((msg_part*)g_slist_next(kdbus_msg_items)->data)->data; + size = ((msg_part*)g_slist_next(kdbus_msg_items)->data)->size; + } + else + { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_ARGUMENT, + _("[KDBUS] Received message is not valid")); + goto out; + } + + message->body = g_variant_new_from_data (variant_type, + data, + size, + TRUE, + NULL, + NULL); + + g_variant_type_free (variant_type); + if (message->body == NULL) + goto out; + } + } + else + { + /* no signature, this is only OK if the body is empty */ + if (message_body_len != 0) + { + /* G_GUINT32_FORMAT doesn't work with gettext, just use %u */ + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_ARGUMENT, + g_dngettext (GETTEXT_PACKAGE, + "No signature header in message but the message body is %u byte", + "No signature header in message but the message body is %u bytes", + message_body_len), + message_body_len); + goto out; + } + } + + if (!validate_headers (message, error)) + { + g_prefix_error (error, _("Cannot deserialize message: ")); + goto out; + } + + ret = TRUE; + + out: + if (ret) + { + return message; + } + else + { + if (message != NULL) + g_object_unref (message); + return NULL; + } +} + static gsize ensure_output_padding (GMemoryBuffer *mbuf, gsize padding_size) @@ -2283,12 +2610,14 @@ append_value_to_blob (GVariant *value, case 'a': /* G_VARIANT_TYPE_ARRAY */ { + const GVariantType *element_type; GVariant *item; GVariantIter iter; goffset array_len_offset; goffset array_payload_begin_offset; goffset cur_offset; gsize array_len; + guint fixed_size; padding_added = ensure_output_padding (mbuf, 4); if (value != NULL) @@ -2312,17 +2641,35 @@ append_value_to_blob (GVariant *value, */ array_payload_begin_offset = mbuf->valid_len; + element_type = g_variant_type_element (type); + fixed_size = get_type_fixed_size (element_type); + if (g_variant_n_children (value) == 0) { gsize padding_added_for_item; if (!append_value_to_blob (NULL, - g_variant_type_element (type), + element_type, mbuf, &padding_added_for_item, error)) goto fail; array_payload_begin_offset += padding_added_for_item; } + else if (fixed_size != 0) + { + GVariant *use_value; + + if (g_memory_buffer_is_byteswapped (mbuf)) + use_value = g_variant_byteswap (value); + else + use_value = g_variant_ref (value); + + array_payload_begin_offset += ensure_output_padding (mbuf, fixed_size); + + array_len = g_variant_get_size (use_value); + g_memory_buffer_write (mbuf, g_variant_get_data (use_value), array_len); + g_variant_unref (use_value); + } else { guint n; @@ -2466,6 +2813,12 @@ append_body_to_blob (GVariant *value, /* ---------------------------------------------------------------------------------------------------- */ +/* [KDBUS] + * g_dbus_message_to_blob() will be replaced by new function only for kdbus transport + * purposes (this function will be able to create blob directly/unconditionally in memfd + * object, without making copy) + */ + /** * g_dbus_message_to_blob: * @message: A #GDBusMessage. @@ -2494,7 +2847,11 @@ g_dbus_message_to_blob (GDBusMessage *message, goffset body_len_offset; goffset body_start_offset; gsize body_size; + gconstpointer message_body_data; + gsize message_body_size; GVariant *header_fields; + gsize header_fields_size; + gconstpointer header_fields_data; GVariantBuilder builder; GHashTableIter hash_iter; gpointer key; @@ -2512,6 +2869,20 @@ g_dbus_message_to_blob (GDBusMessage *message, g_return_val_if_fail (out_size != NULL, NULL); g_return_val_if_fail (error == NULL || *error == NULL, NULL); + /* temporary solution */ + if (!message->major_protocol_version) + g_error ("message->major_protocol_version is not set"); + + if (message->major_protocol_version != 1 && message->major_protocol_version != 2) + { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_ARGUMENT, + _("Invalid major protocol version. Expected 1 or 2 but found %d"), + message->major_protocol_version); + goto out; + } + memset (&mbuf, 0, sizeof (mbuf)); mbuf.len = MIN_ARRAY_SIZE; mbuf.data = g_malloc (mbuf.len); @@ -2531,7 +2902,10 @@ g_dbus_message_to_blob (GDBusMessage *message, g_memory_buffer_put_byte (&mbuf, (guchar) message->byte_order); g_memory_buffer_put_byte (&mbuf, message->type); g_memory_buffer_put_byte (&mbuf, message->flags); - g_memory_buffer_put_byte (&mbuf, 1); /* major protocol version */ + + /* major protocol version */ + g_memory_buffer_put_byte (&mbuf, message->major_protocol_version); + body_len_offset = mbuf.valid_len; /* body length - will be filled in later */ g_memory_buffer_put_uint32 (&mbuf, 0xF00DFACE); @@ -2571,15 +2945,30 @@ g_dbus_message_to_blob (GDBusMessage *message, } header_fields = g_variant_builder_end (&builder); - if (!append_value_to_blob (header_fields, - g_variant_get_type (header_fields), - &mbuf, - NULL, - error)) + /* header - dbus1 marshaliling */ + if (message->major_protocol_version == 1) { - g_variant_unref (header_fields); - goto out; + if (!append_value_to_blob (header_fields, + g_variant_get_type (header_fields), + &mbuf, + NULL, + error)) + { + g_variant_unref (header_fields); + goto out; + } + + } + /* header - GVariant marshalling */ + else if (message->major_protocol_version == 2) + { + header_fields_data = g_variant_get_data (header_fields); + header_fields_size = g_variant_get_size (header_fields); + + g_memory_buffer_put_uint32 (&mbuf, header_fields_size); + g_memory_buffer_write (&mbuf, header_fields_data, header_fields_size); } + g_variant_unref (header_fields); /* header size must be a multiple of 8 */ @@ -2616,8 +3005,21 @@ g_dbus_message_to_blob (GDBusMessage *message, goto out; } g_free (tupled_signature_str); - if (!append_body_to_blob (message->body, &mbuf, error)) - goto out; + + /* body - dbus1 marshaliling */ + if (message->major_protocol_version == 1) + { + if (!append_body_to_blob (message->body, &mbuf, error)) + goto out; + } + /* body - GVariant marshalling */ + else if (message->major_protocol_version == 2) + { + message_body_data = g_variant_get_data (message->body); + message_body_size = g_variant_get_size (message->body); + + g_memory_buffer_write (&mbuf, message_body_data, message_body_size); + } } else {