+ if (!BUS_MESSAGE_IS_GVARIANT(m)) {
+ a = message_extend_body(m, 4, 4, false);
+ if (!a)
+ return -ENOMEM;
+
+ *(uint32_t*) a = size - 1;
+ }
+
+ part = message_append_part(m);
+ if (!part)
+ return -ENOMEM;
+
+ part->memfd = copy_fd;
+ part->sealed = true;
+ part->size = size;
+ copy_fd = -1;
+
+ m->header->body_size += size;
+ message_extend_containers(m, size);
+
+ if (BUS_MESSAGE_IS_GVARIANT(m)) {
+ r = message_add_offset(m, m->header->body_size);
+ if (r < 0) {
+ m->poisoned = true;
+ return -ENOMEM;
+ }
+ }
+
+ if (c->enclosing != SD_BUS_TYPE_ARRAY)
+ c->index++;
+
+ return 0;
+}
+
+_public_ int sd_bus_message_append_strv(sd_bus_message *m, char **l) {
+ char **i;
+ int r;
+
+ assert_return(m, -EINVAL);
+ assert_return(!m->sealed, -EPERM);
+ assert_return(!m->poisoned, -ESTALE);
+
+ r = sd_bus_message_open_container(m, 'a', "s");
+ if (r < 0)
+ return r;
+
+ STRV_FOREACH(i, l) {
+ r = sd_bus_message_append_basic(m, 's', *i);
+ if (r < 0)
+ return r;
+ }
+
+ return sd_bus_message_close_container(m);
+}
+
+static int bus_message_close_header(sd_bus_message *m) {
+ uint8_t *a;
+ size_t sz, i;
+
+ assert(m);
+
+ if (!BUS_MESSAGE_IS_GVARIANT(m))
+ return 0;
+
+ if (m->n_header_offsets < 1)
+ return 0;
+
+ assert(m->header->fields_size == m->header_offsets[m->n_header_offsets-1]);
+
+ sz = determine_word_size(m->header->fields_size, m->n_header_offsets);
+
+ a = message_extend_fields(m, 1, sz * m->n_header_offsets, false);
+ if (!a)
+ return -ENOMEM;
+
+ for (i = 0; i < m->n_header_offsets; i++)
+ write_word_le(a + sz*i, sz, m->header_offsets[i]);
+
+ return 0;
+}
+
+int bus_message_seal(sd_bus_message *m, uint64_t serial, usec_t timeout) {
+ struct bus_body_part *part;
+ size_t l, a;
+ unsigned i;
+ int r;
+
+ assert(m);
+
+ if (m->sealed)
+ return -EPERM;
+
+ if (m->n_containers > 0)
+ return -EBADMSG;
+
+ if (m->poisoned)
+ return -ESTALE;
+
+ /* In vtables the return signature of method calls is listed,
+ * let's check if they match if this is a response */
+ if (m->header->type == SD_BUS_MESSAGE_METHOD_RETURN &&
+ m->enforced_reply_signature &&
+ !streq(strempty(m->root_container.signature), m->enforced_reply_signature))
+ return -ENOMSG;
+
+ /* If gvariant marshalling is used we need to close the body structure */
+ r = bus_message_close_struct(m, &m->root_container, false);
+ if (r < 0)
+ return r;
+
+ /* If there's a non-trivial signature set, then add it in here */
+ if (!isempty(m->root_container.signature)) {
+ r = message_append_field_signature(m, BUS_MESSAGE_HEADER_SIGNATURE, m->root_container.signature, NULL);
+ if (r < 0)
+ return r;
+ }
+
+ if (m->n_fds > 0) {
+ r = message_append_field_uint32(m, BUS_MESSAGE_HEADER_UNIX_FDS, m->n_fds);
+ if (r < 0)
+ return r;
+ }
+
+ r = bus_message_close_header(m);
+ if (r < 0)
+ return r;
+
+ m->header->serial = serial;
+ m->timeout = m->header->flags & BUS_MESSAGE_NO_REPLY_EXPECTED ? 0 : timeout;
+
+ /* Add padding at the end of the fields part, since we know
+ * the body needs to start at an 8 byte alignment. We made
+ * sure we allocated enough space for this, so all we need to
+ * do here is to zero it out. */
+ l = BUS_MESSAGE_FIELDS_SIZE(m);
+ a = ALIGN8(l) - l;
+ if (a > 0)
+ memset((uint8_t*) BUS_MESSAGE_FIELDS(m) + l, 0, a);
+
+ /* If this is something we can send as memfd, then let's seal
+ the memfd now. Note that we can send memfds as payload only
+ for directed messages, and not for broadcasts. */
+ if (m->destination && m->bus && m->bus->use_memfd) {
+ MESSAGE_FOREACH_PART(part, i, m)
+ if (part->memfd >= 0 && !part->sealed && (part->size > MEMFD_MIN_SIZE || m->bus->use_memfd < 0)) {
+ uint64_t sz;
+
+ /* Try to seal it if that makes
+ * sense. First, unmap our own map to
+ * make sure we don't keep it busy. */
+ bus_body_part_unmap(part);
+
+ /* Then, sync up real memfd size */
+ sz = part->size;
+ if (ioctl(part->memfd, KDBUS_CMD_MEMFD_SIZE_SET, &sz) < 0)
+ return -errno;
+
+ /* Finally, try to seal */
+ if (ioctl(part->memfd, KDBUS_CMD_MEMFD_SEAL_SET, 1) >= 0)
+ part->sealed = true;
+ }
+ }
+
+ m->root_container.end = BUS_MESSAGE_BODY_SIZE(m);
+ m->root_container.index = 0;
+ m->root_container.offset_index = 0;
+ m->root_container.item_size = m->root_container.n_offsets > 0 ? m->root_container.offsets[0] : 0;
+
+ m->sealed = true;
+
+ return 0;
+}
+
+int bus_body_part_map(struct bus_body_part *part) {
+ void *p;
+ size_t psz;
+
+ assert_se(part);
+
+ if (part->data)
+ return 0;
+
+ if (part->size <= 0)
+ return 0;
+
+ /* For smaller zero parts (as used for padding) we don't need to map anything... */
+ if (part->memfd < 0 && part->is_zero && part->size < 8) {
+ static const uint8_t zeroes[7] = { };
+ part->data = (void*) zeroes;
+ return 0;
+ }
+
+ psz = PAGE_ALIGN(part->size);
+
+ if (part->memfd >= 0)
+ p = mmap(NULL, psz, PROT_READ, MAP_SHARED, part->memfd, 0);
+ else if (part->is_zero)
+ p = mmap(NULL, psz, PROT_READ, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
+ else
+ return -EINVAL;
+
+ if (p == MAP_FAILED)
+ return -errno;
+
+ part->mapped = psz;
+ part->data = p;
+ part->munmap_this = true;
+
+ return 0;
+}
+
+void bus_body_part_unmap(struct bus_body_part *part) {
+
+ assert_se(part);
+
+ if (part->memfd < 0)
+ return;
+
+ if (!part->data)
+ return;
+
+ if (!part->munmap_this)
+ return;
+
+ assert_se(munmap(part->data, part->mapped) == 0);
+
+ part->data = NULL;
+ part->mapped = 0;
+ part->munmap_this = false;
+
+ return;
+}
+
+static int buffer_peek(const void *p, uint32_t sz, size_t *rindex, size_t align, size_t nbytes, void **r) {
+ size_t k, start, end;
+
+ assert(rindex);
+ assert(align > 0);
+
+ start = ALIGN_TO((size_t) *rindex, align);
+ end = start + nbytes;
+
+ if (end > sz)
+ return -EBADMSG;
+
+ /* Verify that padding is 0 */
+ for (k = *rindex; k < start; k++)
+ if (((const uint8_t*) p)[k] != 0)
+ return -EBADMSG;
+
+ if (r)
+ *r = (uint8_t*) p + start;
+
+ *rindex = end;
+
+ return 1;
+}
+
+static bool message_end_of_signature(sd_bus_message *m) {
+ struct bus_container *c;
+
+ assert(m);
+
+ c = message_get_container(m);
+ return !c->signature || c->signature[c->index] == 0;
+}
+
+static bool message_end_of_array(sd_bus_message *m, size_t index) {
+ struct bus_container *c;
+
+ assert(m);
+
+ c = message_get_container(m);
+ if (c->enclosing != SD_BUS_TYPE_ARRAY)
+ return false;
+
+ if (BUS_MESSAGE_IS_GVARIANT(m))
+ return index >= c->end;
+ else {
+ assert(c->array_size);
+ return index >= c->begin + BUS_MESSAGE_BSWAP32(m, *c->array_size);
+ }
+}
+
+_public_ int sd_bus_message_at_end(sd_bus_message *m, int complete) {
+ assert_return(m, -EINVAL);
+ assert_return(m->sealed, -EPERM);
+
+ if (complete && m->n_containers > 0)
+ return false;
+
+ if (message_end_of_signature(m))
+ return true;
+
+ if (message_end_of_array(m, m->rindex))
+ return true;
+
+ return false;
+}
+
+static struct bus_body_part* find_part(sd_bus_message *m, size_t index, size_t sz, void **p) {
+ struct bus_body_part *part;
+ size_t begin;
+ int r;
+
+ assert(m);
+
+ if (m->cached_rindex_part && index >= m->cached_rindex_part_begin) {
+ part = m->cached_rindex_part;
+ begin = m->cached_rindex_part_begin;
+ } else {
+ part = &m->body;
+ begin = 0;
+ }
+
+ while (part) {
+ if (index < begin)
+ return NULL;
+
+ if (index + sz <= begin + part->size) {
+
+ r = bus_body_part_map(part);
+ if (r < 0)
+ return NULL;
+
+ if (p)
+ *p = (uint8_t*) part->data + index - begin;
+
+ m->cached_rindex_part = part;
+ m->cached_rindex_part_begin = begin;
+
+ return part;
+ }
+
+ begin += part->size;
+ part = part->next;
+ }
+
+ return NULL;
+}
+
+static int container_next_item(sd_bus_message *m, struct bus_container *c, size_t *rindex) {
+ int r;
+
+ assert(m);
+ assert(c);
+ assert(rindex);
+
+ if (!BUS_MESSAGE_IS_GVARIANT(m))
+ return 0;
+
+ if (c->enclosing == SD_BUS_TYPE_ARRAY) {
+ int sz;
+
+ sz = bus_gvariant_get_size(c->signature);
+ if (sz < 0) {
+ int alignment;
+
+ if (c->offset_index+1 >= c->n_offsets)
+ goto end;
+
+ /* Variable-size array */
+
+ alignment = bus_gvariant_get_alignment(c->signature);
+ assert(alignment > 0);
+
+ *rindex = ALIGN_TO(c->offsets[c->offset_index], alignment);
+ c->item_size = c->offsets[c->offset_index+1] - *rindex;
+ } else {
+
+ if (c->offset_index+1 >= (c->end-c->begin)/sz)
+ goto end;
+
+ /* Fixed-size array */
+ *rindex = c->begin + (c->offset_index+1) * sz;
+ c->item_size = sz;
+ }
+
+ c->offset_index++;
+
+ } else if (c->enclosing == 0 ||
+ c->enclosing == SD_BUS_TYPE_STRUCT ||
+ c->enclosing == SD_BUS_TYPE_DICT_ENTRY) {
+
+ int alignment;
+ size_t n, j;
+
+ if (c->offset_index+1 >= c->n_offsets)
+ goto end;
+
+ r = signature_element_length(c->signature + c->index, &n);
+ if (r < 0)
+ return r;
+
+ r = signature_element_length(c->signature + c->index + n, &j);
+ if (r < 0)
+ return r;
+ else {
+ char t[j+1];
+ memcpy(t, c->signature + c->index + n, j);
+ t[j] = 0;
+
+ alignment = bus_gvariant_get_alignment(t);
+ }
+
+ assert(alignment > 0);
+
+ *rindex = ALIGN_TO(c->offsets[c->offset_index], alignment);
+ c->item_size = c->offsets[c->offset_index+1] - *rindex;
+
+ c->offset_index++;
+
+ } else if (c->enclosing == SD_BUS_TYPE_VARIANT)
+ goto end;
+ else
+ assert_not_reached("Unknown container type");
+
+ return 0;
+
+end:
+ /* Reached the end */
+ *rindex = c->end;
+ c->item_size = 0;
+ return 0;
+}
+
+
+static int message_peek_body(
+ sd_bus_message *m,
+ size_t *rindex,
+ size_t align,
+ size_t nbytes,
+ void **ret) {
+
+ size_t k, start, end, padding;
+ struct bus_body_part *part;
+ uint8_t *q;
+
+ assert(m);
+ assert(rindex);
+ assert(align > 0);
+
+ start = ALIGN_TO((size_t) *rindex, align);
+ padding = start - *rindex;
+ end = start + nbytes;
+
+ if (end > BUS_MESSAGE_BODY_SIZE(m))
+ return -EBADMSG;
+
+ part = find_part(m, *rindex, padding, (void**) &q);
+ if (!part)
+ return -EBADMSG;
+
+ if (q) {
+ /* Verify padding */
+ for (k = 0; k < padding; k++)
+ if (q[k] != 0)
+ return -EBADMSG;
+ }
+
+ part = find_part(m, start, nbytes, (void**) &q);
+ if (!part || (nbytes > 0 && !q))
+ return -EBADMSG;
+
+ *rindex = end;
+
+ if (ret)
+ *ret = q;
+
+ return 0;
+}
+
+static bool validate_nul(const char *s, size_t l) {
+
+ /* Check for NUL chars in the string */
+ if (memchr(s, 0, l))
+ return false;
+
+ /* Check for NUL termination */
+ if (s[l] != 0)
+ return false;
+
+ return true;
+}
+
+static bool validate_string(const char *s, size_t l) {
+
+ if (!validate_nul(s, l))
+ return false;
+
+ /* Check if valid UTF8 */
+ if (!utf8_is_valid(s))
+ return false;
+
+ return true;
+}
+
+static bool validate_signature(const char *s, size_t l) {
+
+ if (!validate_nul(s, l))
+ return false;
+
+ /* Check if valid signature */
+ if (!signature_is_valid(s, true))
+ return false;
+
+ return true;
+}
+
+static bool validate_object_path(const char *s, size_t l) {
+
+ if (!validate_nul(s, l))
+ return false;
+
+ if (!object_path_is_valid(s))
+ return false;
+
+ return true;
+}
+
+_public_ int sd_bus_message_read_basic(sd_bus_message *m, char type, void *p) {
+ struct bus_container *c;
+ size_t rindex;
+ void *q;
+ int r;
+
+ assert_return(m, -EINVAL);
+ assert_return(m->sealed, -EPERM);
+ assert_return(bus_type_is_basic(type), -EINVAL);
+
+ if (message_end_of_signature(m))
+ return -ENXIO;
+
+ if (message_end_of_array(m, m->rindex))
+ return 0;
+
+ c = message_get_container(m);
+ if (c->signature[c->index] != type)
+ return -ENXIO;
+
+ rindex = m->rindex;
+
+ if (BUS_MESSAGE_IS_GVARIANT(m)) {
+
+ if (IN_SET(type, SD_BUS_TYPE_STRING, SD_BUS_TYPE_OBJECT_PATH, SD_BUS_TYPE_SIGNATURE)) {
+ bool ok;
+
+ r = message_peek_body(m, &rindex, 1, c->item_size, &q);
+ if (r < 0)
+ return r;
+
+ if (type == SD_BUS_TYPE_STRING)
+ ok = validate_string(q, c->item_size-1);
+ else if (type == SD_BUS_TYPE_OBJECT_PATH)
+ ok = validate_object_path(q, c->item_size-1);
+ else
+ ok = validate_signature(q, c->item_size-1);
+
+ if (!ok)
+ return -EBADMSG;
+
+ if (p)
+ *(const char**) p = q;
+ } else {
+ int sz, align;
+
+ sz = bus_gvariant_get_size(CHAR_TO_STR(type));
+ assert(sz > 0);
+ if ((size_t) sz != c->item_size)
+ return -EBADMSG;
+
+ align = bus_gvariant_get_alignment(CHAR_TO_STR(type));
+ assert(align > 0);
+
+ r = message_peek_body(m, &rindex, align, c->item_size, &q);
+ if (r < 0)
+ return r;
+
+ switch (type) {
+
+ case SD_BUS_TYPE_BYTE:
+ if (p)
+ *(uint8_t*) p = *(uint8_t*) q;
+ break;
+
+ case SD_BUS_TYPE_BOOLEAN:
+ if (p)
+ *(int*) p = !!*(uint8_t*) q;
+ break;
+
+ case SD_BUS_TYPE_INT16:
+ case SD_BUS_TYPE_UINT16:
+ if (p)
+ *(uint16_t*) p = BUS_MESSAGE_BSWAP16(m, *(uint16_t*) q);
+ break;