From 12353891a44da3fe5bbe22c1ec7691292f2ae0ce Mon Sep 17 00:00:00 2001 From: Simon McVittie Date: Fri, 2 Jun 2017 20:19:56 +0100 Subject: [PATCH 1/7] message: Add DBusVariant, a way to copy a single message item For #100344, we will need a way to store the metadata from the original method call, and copy them back into arbitrarily many messages later. This would be easy in GDBus, which has GVariant as a first-class object. However, libdbus doesn't have an object for message items, only messages. We could copy the message's content, but it will carry file descriptors, which we don't want to copy. Instead, introduce an internal object representing a message item in a small buffer. It is stored as a variant (D-Bus type 'v') so that it naturally carries its own type. Signed-off-by: Simon McVittie --- Use _dbus_string_copy_len() instead of reinventing it, for better range-checking. Move the basic type case to the end, and assert that the type is in fact basic. --- dbus/dbus-message-internal.h | 15 +++ dbus/dbus-message.c | 241 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 256 insertions(+) diff --git a/dbus/dbus-message-internal.h b/dbus/dbus-message-internal.h index c9120420..6f113169 100644 --- a/dbus/dbus-message-internal.h +++ b/dbus/dbus-message-internal.h @@ -119,6 +119,21 @@ void _dbus_message_loader_set_pending_fds_function (DBusMessageLoa void (* callback) (void *), void *data); +typedef struct DBusVariant DBusVariant; +DBUS_PRIVATE_EXPORT +DBusVariant *_dbus_variant_read (DBusMessageIter *reader); +DBUS_PRIVATE_EXPORT +dbus_bool_t _dbus_variant_write (DBusVariant *self, + DBusMessageIter *writer); +DBUS_PRIVATE_EXPORT +void _dbus_variant_free (DBusVariant *self); +DBUS_PRIVATE_EXPORT +int _dbus_variant_get_length (DBusVariant *self); +DBUS_PRIVATE_EXPORT +const DBusString *_dbus_variant_peek (DBusVariant *self); +DBUS_PRIVATE_EXPORT +const char *_dbus_variant_get_signature (DBusVariant *self); + typedef struct DBusInitialFDs DBusInitialFDs; DBusInitialFDs *_dbus_check_fdleaks_enter (void); void _dbus_check_fdleaks_leave (DBusInitialFDs *fds); diff --git a/dbus/dbus-message.c b/dbus/dbus-message.c index 0a27f529..db7cbc3d 100644 --- a/dbus/dbus-message.c +++ b/dbus/dbus-message.c @@ -5083,6 +5083,247 @@ dbus_message_get_allow_interactive_authorization (DBusMessage *message) DBUS_HEADER_FLAG_ALLOW_INTERACTIVE_AUTHORIZATION); } +/** + * An opaque data structure containing the serialized form of any single + * D-Bus message item, whose signature is a single complete type. + * + * (Implementation detail: It's serialized as a single variant.) + */ +struct DBusVariant +{ + DBusString data; +}; + +/** + * Copy a single D-Bus message item from reader into a + * newly-allocated #DBusVariant. + * + * For example, if a message contains three string arguments, and reader points + * to the second string, the resulting DBusVariant will have signature + * #DBUS_TYPE_STRING_AS_STRING and contain only that second string. + * + * @param reader An iterator over message items, pointing to one item to copy + * @returns The variant, or #NULL if out of memory + */ +DBusVariant * +_dbus_variant_read (DBusMessageIter *reader) +{ + DBusVariant *self = NULL; + /* Points to the single item we will read from the reader */ + DBusMessageRealIter *real_reader = (DBusMessageRealIter *) reader; + /* The position in self at which we will write a single variant + * (it is position 0) */ + DBusTypeWriter items_writer; + /* The position in self at which we will write a copy of reader + * (it is inside the variant) */ + DBusTypeWriter variant_writer; + /* 'v' */ + DBusString variant_signature; + /* Whatever is the signature of the item we will copy from the reader */ + DBusString contained_signature; + /* TRUE if self->data needs to be freed */ + dbus_bool_t data_inited = FALSE; + /* The type of the item we will read from the reader */ + int type; + /* The string, start position within that string, and length of the signature + * of the single complete type of the item reader points to */ + const DBusString *sig; + int start, len; + + _dbus_assert (_dbus_message_iter_check (real_reader)); + _dbus_assert (real_reader->iter_type == DBUS_MESSAGE_ITER_TYPE_READER); + _dbus_string_init_const (&variant_signature, DBUS_TYPE_VARIANT_AS_STRING); + type = dbus_message_iter_get_arg_type (reader); + _dbus_type_reader_get_signature (&real_reader->u.reader, &sig, &start, &len); + + if (!_dbus_string_init (&contained_signature)) + return NULL; + + if (!_dbus_string_copy_len (sig, start, len, &contained_signature, 0)) + goto oom; + + self = dbus_new0 (DBusVariant, 1); + + if (self == NULL) + goto oom; + + if (!_dbus_string_init (&self->data)) + goto oom; + + data_inited = TRUE; + + _dbus_type_writer_init_values_only (&items_writer, DBUS_COMPILER_BYTE_ORDER, + &variant_signature, 0, &self->data, 0); + + if (!_dbus_type_writer_recurse (&items_writer, DBUS_TYPE_VARIANT, + &contained_signature, 0, &variant_writer)) + goto oom; + + if (type == DBUS_TYPE_ARRAY) + { + /* Points to each item in turn inside the array we are copying */ + DBusMessageIter array_reader; + /* Same as array_reader */ + DBusMessageRealIter *real_array_reader = (DBusMessageRealIter *) &array_reader; + /* The position inside the copied array at which we will write + * the copy of array_reader */ + DBusTypeWriter array_writer; + + dbus_message_iter_recurse (reader, &array_reader); + + if (!_dbus_type_writer_recurse (&variant_writer, type, + &contained_signature, 1, &array_writer)) + goto oom; + + if (!_dbus_type_writer_write_reader (&array_writer, + &real_array_reader->u.reader)) + goto oom; + + if (!_dbus_type_writer_unrecurse (&variant_writer, &array_writer)) + goto oom; + } + else if (type == DBUS_TYPE_DICT_ENTRY || type == DBUS_TYPE_VARIANT || + type == DBUS_TYPE_STRUCT) + { + /* Points to each item in turn inside the container we are copying */ + DBusMessageIter inner_reader; + /* Same as inner_reader */ + DBusMessageRealIter *real_inner_reader = (DBusMessageRealIter *) &inner_reader; + /* The position inside the copied container at which we will write the + * copy of inner_reader */ + DBusTypeWriter inner_writer; + + dbus_message_iter_recurse (reader, &inner_reader); + + if (!_dbus_type_writer_recurse (&variant_writer, type, NULL, 0, + &inner_writer)) + goto oom; + + if (!_dbus_type_writer_write_reader (&inner_writer, + &real_inner_reader->u.reader)) + goto oom; + + if (!_dbus_type_writer_unrecurse (&variant_writer, &inner_writer)) + goto oom; + } + else + { + DBusBasicValue value; + + /* We eliminated all the container types above */ + _dbus_assert (dbus_type_is_basic (type)); + + dbus_message_iter_get_basic (reader, &value); + + if (!_dbus_type_writer_write_basic (&variant_writer, type, &value)) + goto oom; + } + + return self; + +oom: + if (self != NULL) + { + if (data_inited) + _dbus_string_free (&self->data); + + dbus_free (self); + } + + _dbus_string_free (&contained_signature); + return NULL; +} + +/** + * Return the signature of the item stored in self. It is a single complete + * type. + * + * @param self the variant + */ +const char * +_dbus_variant_get_signature (DBusVariant *self) +{ + unsigned char len; + const char *ret; + + _dbus_assert (self != NULL); + + /* Here we make use of the fact that the serialization of a variant starts + * with the 1-byte length, then that many bytes of signature, then \0. */ + len = _dbus_string_get_byte (&self->data, 0); + ret = _dbus_string_get_const_data_len (&self->data, 1, len); + _dbus_assert (strlen (ret) == len); + return ret; +} + +/** + * Copy the single D-Bus message item from self into writer. + * + * For example, if writer points into the body of an empty message and self has + * signature #DBUS_TYPE_STRING_AS_STRING, then the message will + * have signature #DBUS_TYPE_STRING_AS_STRING after this function returns + * + * @param self the variant + * @param writer the place to write the contents of the variant + * @returns #TRUE on success, #FALSE if out of memory + */ +dbus_bool_t +_dbus_variant_write (DBusVariant *self, + DBusMessageIter *writer) +{ + /* 'v' */ + DBusString variant_signature; + /* Points to the single item in self */ + DBusTypeReader variant_reader; + /* Points to the single item (of whatever type) inside the variant */ + DBusTypeReader reader; + /* The position at which we will copy reader */ + DBusMessageRealIter *real_writer = (DBusMessageRealIter *) writer; + dbus_bool_t ret; + + _dbus_assert (self != NULL); + _dbus_assert (_dbus_message_iter_append_check (real_writer)); + _dbus_assert (real_writer->iter_type == DBUS_MESSAGE_ITER_TYPE_WRITER); + + _dbus_string_init_const (&variant_signature, DBUS_TYPE_VARIANT_AS_STRING); + _dbus_type_reader_init (&reader, DBUS_COMPILER_BYTE_ORDER, + &variant_signature, 0, &self->data, 0); + _dbus_type_reader_recurse (&reader, &variant_reader); + + if (!_dbus_message_iter_open_signature (real_writer)) + return FALSE; + + ret = _dbus_type_writer_write_reader (&real_writer->u.writer, + &variant_reader); + + if (!_dbus_message_iter_close_signature (real_writer)) + return FALSE; + + return ret; +} + +int +_dbus_variant_get_length (DBusVariant *self) +{ + _dbus_assert (self != NULL); + return _dbus_string_get_length (&self->data); +} + +const DBusString * +_dbus_variant_peek (DBusVariant *self) +{ + _dbus_assert (self != NULL); + return &self->data; +} + +void +_dbus_variant_free (DBusVariant *self) +{ + _dbus_assert (self != NULL); + _dbus_string_free (&self->data); + dbus_free (self); +} + /** @} */ /* tests in dbus-message-util.c */ -- 2.13.2