/* * * Embedded Linux library * * Copyright (C) 2011-2014 Intel Corporation. All rights reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * */ #ifdef HAVE_CONFIG_H #include #endif #define _GNU_SOURCE #include #include #include #include #include #include "private.h" #include "useful.h" #include "util.h" #include "queue.h" #include "string.h" #include "log.h" #include "dbus.h" #include "dbus-private.h" #include "gvariant-private.h" static const char *simple_types = "sogybnqiuxtdh"; static const char *variable_types = "sogav"; static const char *fixed_types = "bynqhiuxtd"; /* * The alignment of a container type is equal to the largest alignment of * any potential child of that container. This means that, even if an array * of 32-bit integers is empty, it still must be aligned to the nearest * multiple of 4 bytes. It also means that the variant type (described below) * has an alignment of 8 (since it could potentially contain a value of any * other type and the maximum alignment is 8). */ static int get_basic_alignment(const char type) { switch (type) { case 'b': return 1; case 'y': return 1; case 'n': case 'q': return 2; case 'i': case 'u': return 4; case 'x': case 't': case 'd': return 8; case 's': case 'g': case 'o': return 1; case 'h': return 4; case 'v': return 8; default: return 0; } } static int get_basic_fixed_size(const char type) { switch (type) { case 'b': return 1; case 'y': return 1; case 'n': case 'q': return 2; case 'i': case 'u': return 4; case 'x': case 't': case 'd': return 8; case 'h': return 4; default: return 0; } } static const char *validate_next_type(const char *sig, int *out_alignment) { char s = *sig; int alignment; if (s == '\0') return NULL; if (strchr(simple_types, s) || s == 'v') { *out_alignment = get_basic_alignment(s); return sig + 1; } switch (s) { case 'a': return validate_next_type(++sig, out_alignment); case '{': s = *++sig; /* Dictionary keys can only be simple types */ if (!strchr(simple_types, s)) return NULL; alignment = get_basic_alignment(s); sig = validate_next_type(sig + 1, out_alignment); if (!sig) return NULL; if (*sig != '}') return NULL; if (alignment > *out_alignment) *out_alignment = alignment; return sig + 1; case '(': { int max_alignment = 1, alignment; sig++; while (sig && *sig != ')') { sig = validate_next_type(sig, &alignment); if (alignment > max_alignment) max_alignment = alignment; } if (!sig) return NULL; if (*sig != ')') return NULL; *out_alignment = max_alignment; return sig + 1; } } return NULL; } bool _gvariant_valid_signature(const char *sig) { const char *s = sig; int a; if (strlen(sig) > 255) return false; do { s = validate_next_type(s, &a); if (!s) return false; } while (*s); return true; } int _gvariant_num_children(const char *sig) { const char *s = sig; int a; int num_children = 0; if (strlen(sig) > 255) return false; do { s = validate_next_type(s, &a); if (!s) return -1; num_children += 1; } while (*s); return num_children; } int _gvariant_get_alignment(const char *sig) { int max_alignment = 1, alignment; const char *s = sig; /* 8 is the largest alignment possible, so quit if we reach it */ while (*s && max_alignment != 8) { s = validate_next_type(s, &alignment); if (!s) return 0; if (alignment > max_alignment) max_alignment = alignment; } return max_alignment; } bool _gvariant_is_fixed_size(const char *sig) { while (*sig != 0) { if (strchr(variable_types, sig[0])) return false; sig += 1; } return true; } int _gvariant_get_fixed_size(const char *sig) { const char *s = sig; const char *p; int size = 0; int alignment; int max_alignment = 1; int r; while (*s) { if (strchr(variable_types, *s)) return 0; if (strchr(fixed_types, *s)) { alignment = get_basic_alignment(*s); if (alignment > max_alignment) max_alignment = alignment; size = align_len(size, alignment); size += get_basic_fixed_size(*s); s++; continue; } if (*s == '}' || *s == ')') break; p = validate_next_type(s, &alignment); if (!p) return 0; if (alignment > max_alignment) max_alignment = alignment; size = align_len(size, alignment); /* Handle special case of unit type */ if (s[0] == '(' && s[1] == ')') r = 1; else r = _gvariant_get_fixed_size(s + 1); if (r == 0) return 0; size += r; s = p; } size = align_len(size, max_alignment); return size; } static inline size_t offset_length(size_t size, size_t n_offsets) { if (size + n_offsets <= 0xff) return 1; if (size + n_offsets * 2 <= 0xffff) return 2; if (size + n_offsets * 4 <= 0xffffffff) return 4; return 8; } static inline size_t read_word_le(const void *p, size_t sz) { union { uint16_t u16; uint32_t u32; uint64_t u64; } x; if (sz == 1) return *(uint8_t *) p; memcpy(&x, p, sz); if (sz == 2) return le16toh(x.u16); if (sz == 4) return le32toh(x.u32); return le64toh(x.u64); } static inline void write_word_le(void *p, size_t value, size_t sz) { union { uint16_t u16; uint32_t u32; uint64_t u64; } x; if (sz == 1) { *(uint8_t *) p = value; return; } if (sz == 2) x.u16 = htole16((uint16_t) value); else if (sz == 4) x.u32 = htole32((uint32_t) value); else x.u64 = htole64((uint64_t) value); memcpy(p, &x, sz); } static bool gvariant_iter_init_internal(struct l_dbus_message_iter *iter, struct l_dbus_message *message, enum dbus_container_type type, const char *sig_start, const char *sig_end, const void *data, size_t len) { const char *p; int i; int v; char subsig[256]; unsigned int num_variable = 0; unsigned int offset_len = offset_length(len, 0); size_t last_offset; struct gvariant_type_info { uint8_t sig_start; uint8_t sig_end; bool fixed_size : 1; unsigned int alignment : 4; size_t end; /* Index past the end of the type */ } *children; int n_children; if (sig_end) { size_t len = sig_end - sig_start; memcpy(subsig, sig_start, len); subsig[len] = '\0'; } else strcpy(subsig, sig_start); iter->message = message; iter->sig_start = sig_start; iter->sig_len = strlen(subsig); iter->sig_pos = 0; iter->data = data; iter->len = len; iter->pos = 0; if (subsig[0] != '\0') { n_children = _gvariant_num_children(subsig); if (n_children < 0) return false; children = l_new(struct gvariant_type_info, n_children); } else { n_children = 0; children = NULL; } for (p = sig_start, i = 0; i < n_children; i++) { int alignment; size_t size; size_t len; children[i].sig_start = p - sig_start; p = validate_next_type(p, &alignment); children[i].sig_end = p - sig_start; len = children[i].sig_end - children[i].sig_start; memcpy(subsig, sig_start + children[i].sig_start, len); subsig[len] = '\0'; children[i].alignment = alignment; children[i].fixed_size = _gvariant_is_fixed_size(subsig); if (children[i].fixed_size) { size = _gvariant_get_fixed_size(subsig); children[i].end = size; } else if (i + 1 < n_children) num_variable += 1; } if (len < num_variable * offset_len) goto fail; last_offset = len - num_variable * offset_len; if (num_variable > 0) iter->offsets = iter->data + len - offset_len; else iter->offsets = NULL; for (i = 0, v = 0; i < n_children; i++) { size_t o; if (children[i].fixed_size) { if (i == 0) continue; o = align_len(children[i-1].end, children[i].alignment); children[i].end += o; if (children[i].end > len) goto fail; continue; } if (num_variable == 0) { children[i].end = last_offset; continue; } v += 1; children[i].end = read_word_le(data + len - offset_len * v, offset_len); num_variable -= 1; if (children[i].end > len) goto fail; } iter->container_type = type; if (type == DBUS_CONTAINER_TYPE_ARRAY && !children[0].fixed_size) { size_t offset = read_word_le(iter->data + iter->len - offset_len, offset_len); iter->offsets = iter->data + offset; } l_free(children); return true; fail: l_free(children); return false; } bool _gvariant_iter_init(struct l_dbus_message_iter *iter, struct l_dbus_message *message, const char *sig_start, const char *sig_end, const void *data, size_t len) { return gvariant_iter_init_internal(iter, message, DBUS_CONTAINER_TYPE_STRUCT, sig_start, sig_end, data, len); } static const void *next_item(struct l_dbus_message_iter *iter, size_t *out_item_size) { const void *start; const char *p; char sig[256]; int alignment; bool fixed_size; bool last_member; unsigned int sig_len; unsigned int offset_len; memcpy(sig, iter->sig_start + iter->sig_pos, iter->sig_len - iter->sig_pos); sig[iter->sig_len - iter->sig_pos] = '\0'; /* * Find the next type and make a note whether it is the last in the * structure. Arrays will always have a single complete type, so * last_member will always be true. */ p = validate_next_type(sig, &alignment); if (!p) return NULL; sig_len = p - sig; last_member = *p == '\0'; sig[sig_len] = '\0'; fixed_size = _gvariant_is_fixed_size(sig); if (iter->container_type != DBUS_CONTAINER_TYPE_ARRAY) iter->sig_pos += sig_len; iter->pos = align_len(iter->pos, alignment); if (fixed_size) { *out_item_size = _gvariant_get_fixed_size(sig); goto done; } if (iter->container_type != DBUS_CONTAINER_TYPE_ARRAY && last_member) { unsigned int len = iter->len; offset_len = offset_length(iter->len, 0); if (iter->offsets && iter->offsets + offset_len < iter->data + len) len = iter->offsets + offset_len - iter->data; *out_item_size = len - iter->pos; goto done; } if (iter->offsets >= iter->data + iter->len) return NULL; offset_len = offset_length(iter->len, 0); *out_item_size = read_word_le(iter->offsets, offset_len) - iter->pos; /* In structures the offsets are in reverse order */ if (iter->container_type == DBUS_CONTAINER_TYPE_ARRAY) iter->offsets += offset_len; else iter->offsets -= offset_len; done: start = iter->data + iter->pos; if (start >= iter->data + iter->len) return NULL; iter->pos += *out_item_size; return start; } bool _gvariant_iter_next_entry_basic(struct l_dbus_message_iter *iter, char type, void *out) { size_t item_size = 0; const void *start; uint8_t uint8_val; uint16_t uint16_val; uint32_t uint32_val; uint64_t uint64_val; int16_t int16_val; int32_t int32_val; int64_t int64_val; if (iter->pos >= iter->len) return false; if (iter->sig_start[iter->sig_pos] != type) return false; start = next_item(iter, &item_size); if (!start) return false; switch (type) { case 'o': case 's': case 'g': { const void *end = memchr(start, 0, item_size); if (!end) return false; *(const char**) out = start; break; } case 'b': uint8_val = l_get_u8(start); *(bool *) out = !!uint8_val; break; case 'y': uint8_val = l_get_u8(start); *(uint8_t *) out = uint8_val; break; case 'n': int16_val = l_get_s16(start); *(int16_t *) out = int16_val; break; case 'q': uint16_val = l_get_u16(start); *(uint16_t *) out = uint16_val; break; case 'i': int32_val = l_get_s32(start); *(int32_t *) out = int32_val; break; case 'h': case 'u': uint32_val = l_get_u32(start); *(uint32_t *) out = uint32_val; break; case 'x': int64_val = l_get_s64(start); *(int64_t *) out = int64_val; break; case 't': uint64_val = l_get_u64(start); *(uint64_t *) out = uint64_val; break; case 'd': uint64_val = l_get_u64(start); *(uint64_t *) out = uint64_val; break; } return true; } bool _gvariant_iter_enter_struct(struct l_dbus_message_iter *iter, struct l_dbus_message_iter *structure) { bool is_dict = iter->sig_start[iter->sig_pos] == '{'; bool is_struct = iter->sig_start[iter->sig_pos] == '('; const char *sig_start = iter->sig_start + iter->sig_pos + 1; const char *sig_end; const void *start; size_t item_size; enum dbus_container_type type; if (!is_dict && !is_struct) return false; start = next_item(iter, &item_size); if (!start) return false; /* For ARRAY containers the sig_pos is never incremented */ if (iter->container_type == DBUS_CONTAINER_TYPE_ARRAY) sig_end = iter->sig_start + iter->sig_len - 1; else sig_end = iter->sig_start + iter->sig_pos - 1; type = is_dict ? DBUS_CONTAINER_TYPE_DICT_ENTRY : DBUS_CONTAINER_TYPE_STRUCT; return gvariant_iter_init_internal(structure, iter->message, type, sig_start, sig_end, start, item_size); } bool _gvariant_iter_enter_variant(struct l_dbus_message_iter *iter, struct l_dbus_message_iter *variant) { size_t item_size; const void *start, *end, *nul; char signature[256]; if (iter->sig_start[iter->sig_pos] != 'v') return false; start = next_item(iter, &item_size); if (!start) return false; /* Find the signature */ end = start + item_size; nul = memrchr(start, 0, end - start); if (!nul) return false; if (end - nul - 1 > 255) return false; memcpy(signature, nul + 1, end - nul - 1); signature[end - nul - 1] = '\0'; if (_gvariant_num_children(signature) != 1) return false; return gvariant_iter_init_internal(variant, iter->message, DBUS_CONTAINER_TYPE_VARIANT, nul + 1, end, start, nul - start); } bool _gvariant_iter_enter_array(struct l_dbus_message_iter *iter, struct l_dbus_message_iter *array) { const char *sig_start; const char *sig_end; size_t item_size; const void *start; if (iter->sig_start[iter->sig_pos] != 'a') return false; sig_start = iter->sig_start + iter->sig_pos + 1; start = next_item(iter, &item_size); if (!start) return false; /* For ARRAY containers the sig_pos is never incremented */ if (iter->container_type == DBUS_CONTAINER_TYPE_ARRAY) sig_end = iter->sig_start + iter->sig_len; else sig_end = iter->sig_start + iter->sig_pos; return gvariant_iter_init_internal(array, iter->message, DBUS_CONTAINER_TYPE_ARRAY, sig_start, sig_end, start, item_size); } bool _gvariant_iter_skip_entry(struct l_dbus_message_iter *iter) { size_t size; if (!next_item(iter, &size)) return false; return true; } struct dbus_builder { struct l_string *signature; void *body; size_t body_size; size_t body_pos; struct l_queue *containers; struct { struct container *container; int sig_end; size_t body_pos; size_t offset_index; bool variable_is_last : 1; } mark; }; struct container { size_t *offsets; size_t offsets_size; size_t offset_index; size_t start; bool variable_is_last : 1; enum dbus_container_type type; char signature[256]; uint8_t sigindex; }; static inline size_t grow_body(struct dbus_builder *builder, size_t len, unsigned int alignment) { size_t size = align_len(builder->body_pos, alignment); if (size + len > builder->body_size) { builder->body = l_realloc(builder->body, size + len); builder->body_size = size + len; } if (size - builder->body_pos > 0) memset(builder->body + builder->body_pos, 0, size - builder->body_pos); builder->body_pos = size + len; return size; } static inline bool grow_offsets(struct container *container) { size_t needed; if (container->offset_index < container->offsets_size) return true; needed = container->offsets_size * 2; if (needed > USHRT_MAX) return false; if (needed == 0) needed = 8; container->offsets = l_realloc(container->offsets, needed * sizeof(size_t)); container->offsets_size = needed; return true; } static struct container *container_new(enum dbus_container_type type, const char *signature, size_t start) { struct container *ret; ret = l_new(struct container, 1); ret->type = type; strcpy(ret->signature, signature); ret->start = start; return ret; } static void container_free(struct container *container) { l_free(container->offsets); l_free(container); } static void container_append_struct_offsets(struct container *container, struct dbus_builder *builder) { size_t offset_size; int i; size_t start; if (container->variable_is_last) container->offset_index -= 1; if (container->offset_index == 0) return; offset_size = offset_length(builder->body_pos, container->offset_index); i = container->offset_index - 1; start = grow_body(builder, offset_size * container->offset_index, 1); for (i = container->offset_index - 1; i >= 0; i--) { write_word_le(builder->body + start, container->offsets[i], offset_size); start += offset_size; } } static void container_append_array_offsets(struct container *container, struct dbus_builder *builder) { size_t offset_size; unsigned int i; size_t start; if (container->offset_index == 0) return; offset_size = offset_length(builder->body_pos, container->offset_index); start = grow_body(builder, offset_size * container->offset_index, 1); for (i = 0; i < container->offset_index; i++) { write_word_le(builder->body + start, container->offsets[i], offset_size); start += offset_size; } } struct dbus_builder *_gvariant_builder_new(void *body, size_t body_size) { struct dbus_builder *builder; struct container *root; builder = l_new(struct dbus_builder, 1); builder->signature = l_string_new(63); builder->containers = l_queue_new(); root = container_new(DBUS_CONTAINER_TYPE_STRUCT, "", 0); l_queue_push_head(builder->containers, root); builder->body = body; builder->body_size = body_size; builder->body_pos = body_size; builder->mark.container = root; return builder; } void _gvariant_builder_free(struct dbus_builder *builder) { if (unlikely(!builder)) return; l_string_free(builder->signature); l_queue_destroy(builder->containers, (l_queue_destroy_func_t) container_free); l_free(builder->body); l_free(builder); } static bool enter_struct_dict_common(struct dbus_builder *builder, const char *signature, enum dbus_container_type type, const char open, const char close) { size_t qlen = l_queue_length(builder->containers); struct container *container = l_queue_peek_head(builder->containers); int alignment; size_t start; if (qlen == 1) { if (l_string_length(builder->signature) + strlen(signature) + 2 > 255) return false; } else { /* Verify Signatures Match */ char expect[256]; const char *start; const char *end; start = container->signature + container->sigindex; end = validate_next_type(start, &alignment) - 1; if (*start != open || *end != close) return false; memcpy(expect, start + 1, end - start - 1); expect[end - start - 1] = '\0'; if (strcmp(expect, signature)) return false; } alignment = _gvariant_get_alignment(signature); start = grow_body(builder, 0, alignment); container = container_new(type, signature, start); l_queue_push_head(builder->containers, container); return true; } bool _gvariant_builder_enter_struct(struct dbus_builder *builder, const char *signature) { if (signature[0] && !_gvariant_valid_signature(signature)) return false; return enter_struct_dict_common(builder, signature, DBUS_CONTAINER_TYPE_STRUCT, '(', ')'); } bool _gvariant_builder_enter_dict(struct dbus_builder *builder, const char *signature) { if (_gvariant_num_children(signature) != 2) return false; if (!strchr(simple_types, signature[0])) return false; return enter_struct_dict_common(builder, signature, DBUS_CONTAINER_TYPE_DICT_ENTRY, '{', '}'); } static bool leave_struct_dict_common(struct dbus_builder *builder, enum dbus_container_type type, const char open, const char close) { struct container *container = l_queue_peek_head(builder->containers); size_t qlen = l_queue_length(builder->containers); struct container *parent; if (unlikely(qlen <= 1)) return false; if (unlikely(container->type != type)) return false; l_queue_pop_head(builder->containers); qlen -= 1; parent = l_queue_peek_head(builder->containers); if (_gvariant_is_fixed_size(container->signature)) { int alignment = _gvariant_get_alignment(container->signature); grow_body(builder, 0, alignment); /* Empty struct or "unit type" is encoded as a zero byte */ if (container->signature[0] == '\0') { size_t start = grow_body(builder, 1, 1); memset(builder->body + start, 0, 1); } parent->variable_is_last = false; } else { size_t offset; if (!grow_offsets(parent)) return false; container_append_struct_offsets(container, builder); offset = builder->body_pos - parent->start; parent->offsets[parent->offset_index++] = offset; parent->variable_is_last = true; } if (qlen == 1) l_string_append_printf(builder->signature, "%c%s%c", open, container->signature, close); else if (parent->type != DBUS_CONTAINER_TYPE_ARRAY) parent->sigindex += strlen(container->signature) + 2; container_free(container); return true; } bool _gvariant_builder_leave_struct(struct dbus_builder *builder) { return leave_struct_dict_common(builder, DBUS_CONTAINER_TYPE_STRUCT, '(', ')'); } bool _gvariant_builder_leave_dict(struct dbus_builder *builder) { return leave_struct_dict_common(builder, DBUS_CONTAINER_TYPE_DICT_ENTRY, '{', '}'); } bool _gvariant_builder_enter_variant(struct dbus_builder *builder, const char *signature) { size_t qlen = l_queue_length(builder->containers); struct container *container = l_queue_peek_head(builder->containers); size_t start; if (_gvariant_num_children(signature) != 1) return false; if (qlen == 1) { if (l_string_length(builder->signature) + 1 > 255) return false; } else if (container->signature[container->sigindex] != 'v') return false; start = grow_body(builder, 0, 8); container = container_new(DBUS_CONTAINER_TYPE_VARIANT, signature, start); l_queue_push_head(builder->containers, container); return true; } bool _gvariant_builder_leave_variant(struct dbus_builder *builder) { struct container *container = l_queue_peek_head(builder->containers); size_t qlen = l_queue_length(builder->containers); struct container *parent; size_t start; size_t siglen; size_t offset; if (unlikely(qlen <= 1)) return false; if (unlikely(container->type != DBUS_CONTAINER_TYPE_VARIANT)) return false; l_queue_pop_head(builder->containers); qlen -= 1; parent = l_queue_peek_head(builder->containers); siglen = strlen(container->signature); start = grow_body(builder, siglen + 1, 1); memset(builder->body + start, 0, 1); memcpy(builder->body + start + 1, container->signature, siglen); if (!grow_offsets(parent)) return false; offset = builder->body_pos - parent->start; parent->offsets[parent->offset_index++] = offset; parent->variable_is_last = true; if (qlen == 1) l_string_append_c(builder->signature, 'v'); else if (parent->type != DBUS_CONTAINER_TYPE_ARRAY) parent->sigindex += 1; container_free(container); return true; } bool _gvariant_builder_enter_array(struct dbus_builder *builder, const char *signature) { size_t qlen = l_queue_length(builder->containers); struct container *container = l_queue_peek_head(builder->containers); size_t start; int alignment; if (_gvariant_num_children(signature) != 1) return false; if (qlen == 1) { if (l_string_length(builder->signature) + strlen(signature) + 1 > 255) return false; } else { /* Verify Signatures Match */ char expect[256]; const char *start; const char *end; start = container->signature + container->sigindex; end = validate_next_type(start, &alignment); if (*start != 'a') return false; memcpy(expect, start + 1, end - start - 1); expect[end - start - 1] = '\0'; if (strcmp(expect, signature)) return false; } alignment = _gvariant_get_alignment(signature); start = grow_body(builder, 0, alignment); container = container_new(DBUS_CONTAINER_TYPE_ARRAY, signature, start); l_queue_push_head(builder->containers, container); return true; } bool _gvariant_builder_leave_array(struct dbus_builder *builder) { struct container *container = l_queue_peek_head(builder->containers); size_t qlen = l_queue_length(builder->containers); struct container *parent; size_t offset; if (unlikely(qlen <= 1)) return false; if (unlikely(container->type != DBUS_CONTAINER_TYPE_ARRAY)) return false; l_queue_pop_head(builder->containers); qlen -= 1; parent = l_queue_peek_head(builder->containers); if (!_gvariant_is_fixed_size(container->signature)) container_append_array_offsets(container, builder); if (!grow_offsets(parent)) return false; offset = builder->body_pos - parent->start; parent->offsets[parent->offset_index++] = offset; parent->variable_is_last = true; if (qlen == 1) l_string_append_printf(builder->signature, "a%s", container->signature); else if (parent->type != DBUS_CONTAINER_TYPE_ARRAY) parent->sigindex += strlen(container->signature) + 1; container_free(container); return true; } bool _gvariant_builder_append_basic(struct dbus_builder *builder, char type, const void *value) { struct container *container = l_queue_peek_head(builder->containers); size_t start; unsigned int alignment; size_t len; size_t offset; if (unlikely(!builder)) return false; if (unlikely(!strchr(simple_types, type))) return false; alignment = get_basic_alignment(type); if (!alignment) return false; if (l_queue_length(builder->containers) == 1) l_string_append_c(builder->signature, type); else if (container->signature[container->sigindex] != type) return false; len = get_basic_fixed_size(type); if (len) { start = grow_body(builder, len, alignment); memcpy(builder->body + start, value, len); container->variable_is_last = false; if (container->type != DBUS_CONTAINER_TYPE_ARRAY) container->sigindex += 1; return true; } if (!grow_offsets(container)) return false; len = strlen(value) + 1; start = grow_body(builder, len, alignment); memcpy(builder->body + start, value, len); offset = builder->body_pos - container->start; container->offsets[container->offset_index++] = offset; container->variable_is_last = true; if (container->type != DBUS_CONTAINER_TYPE_ARRAY) container->sigindex += 1; return true; } bool _gvariant_builder_mark(struct dbus_builder *builder) { struct container *container = l_queue_peek_head(builder->containers); builder->mark.container = container; if (l_queue_length(builder->containers) == 1) builder->mark.sig_end = l_string_length(builder->signature); else builder->mark.sig_end = container->sigindex; builder->mark.body_pos = builder->body_pos; builder->mark.offset_index = container->offset_index; builder->mark.variable_is_last = container->variable_is_last; return true; } bool _gvariant_builder_rewind(struct dbus_builder *builder) { struct container *container; while ((container = l_queue_peek_head(builder->containers)) != builder->mark.container) { container_free(container); l_queue_pop_head(builder->containers); } builder->body_pos = builder->mark.body_pos; container->offset_index = builder->mark.offset_index; container->variable_is_last = builder->mark.variable_is_last; if (l_queue_length(builder->containers) == 1) l_string_truncate(builder->signature, builder->mark.sig_end); else container->sigindex = builder->mark.sig_end; return true; } char *_gvariant_builder_finish(struct dbus_builder *builder, void **body, size_t *body_size) { char *signature; struct container *root; uint8_t *variant_buf; size_t size; if (unlikely(!builder)) return NULL; if (unlikely(l_queue_length(builder->containers) != 1)) return NULL; root = l_queue_peek_head(builder->containers); signature = l_string_unwrap(builder->signature); builder->signature = NULL; if (_gvariant_is_fixed_size(signature)) { int alignment = _gvariant_get_alignment(signature); grow_body(builder, 0, alignment); /* Empty struct or "unit type" is encoded as a zero byte */ if (signature[0] == '\0') { size_t start = grow_body(builder, 1, 1); memset(builder->body + start, 0, 1); } } else container_append_struct_offsets(root, builder); /* * Make sure there's enough space after the body for the variant * signature written here but not included in the body size and * one framing offset value to be written in * _gvariant_message_finalize. */ size = 3 + strlen(signature) + 8; if (builder->body_pos + size > builder->body_size) builder->body = l_realloc(builder->body, builder->body_pos + size); variant_buf = builder->body + builder->body_pos; *variant_buf++ = 0; *variant_buf++ = '('; variant_buf = mempcpy(variant_buf, signature, strlen(signature)); *variant_buf++ = ')'; *body = builder->body; *body_size = builder->body_pos; builder->body = NULL; builder->body_size = 0; return signature; } /* * Write the header's framing offset after the body variant which is the * last piece of data in the message after the header, the padding and * the builder has written the message body. */ size_t _gvariant_message_finalize(size_t header_end, void *body, size_t body_size, const char *signature) { size_t offset_start; size_t offset_size; offset_start = body_size + 3 + strlen(signature); offset_size = offset_length(align_len(header_end, 8) + offset_start, 1); write_word_le(body + offset_start, header_end, offset_size); return align_len(header_end, 8) + offset_start + offset_size; }