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 <smcv@collabora.com>
Reviewed-by: Philip Withnall <withnall@endlessm.com>
Bug: https://bugs.freedesktop.org/show_bug.cgi?id=101568
This commit is contained in:
Simon McVittie 2017-06-02 20:19:56 +01:00
parent 3444ac4007
commit aa5fdac600
2 changed files with 257 additions and 0 deletions

View file

@ -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);

View file

@ -5230,6 +5230,248 @@ 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;
}
_dbus_string_free (&contained_signature);
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 */