+ }
+
+ if ((header.content_size / CHAR_BIT) < header_sizeof(header))
+ return -EINVAL;
+
+ toread = (header.content_size / CHAR_BIT) - header_sizeof(header);
+
+ for (;;) {
+ readlen = fread(buf, sizeof(char), min(sizeof(buf) - 1, toread), in);
+ if (ferror(in)) {
+ ret = -EINVAL;
+ break;
+ }
+ if (babeltrace_debug) {
+ buf[readlen] = '\0';
+ fprintf(stderr, "[debug] metadata packet read: %s\n",
+ buf);
+ }
+
+ writelen = fwrite(buf, sizeof(char), readlen, out);
+ if (writelen < readlen) {
+ ret = -EIO;
+ break;
+ }
+ if (ferror(out)) {
+ ret = -EINVAL;
+ break;
+ }
+ toread -= readlen;
+ if (!toread) {
+ ret = 0; /* continue reading next packet */
+ goto read_padding;
+ }
+ }
+ return ret;
+
+read_padding:
+ toread = (header.packet_size - header.content_size) / CHAR_BIT;
+ ret = fseek(in, toread, SEEK_CUR);
+ if (ret < 0) {
+ fprintf(stderr, "[warning] Missing padding at end of file\n");
+ ret = 0;
+ }
+ return ret;
+}
+
+static
+int ctf_trace_metadata_stream_read(struct ctf_trace *td, FILE **fp,
+ char **buf)
+{
+ FILE *in, *out;
+ size_t size, buflen;
+ int ret;
+
+ in = *fp;
+ /*
+ * Using strlen on *buf instead of size of open_memstream
+ * because its size includes garbage at the end (after final
+ * \0). This is the allocated size, not the actual string size.
+ */
+ out = babeltrace_open_memstream(buf, &size);
+ if (out == NULL) {
+ perror("Metadata open_memstream");
+ return -errno;
+ }
+ for (;;) {
+ ret = ctf_trace_metadata_packet_read(td, in, out);
+ if (ret) {
+ break;
+ }
+ if (feof(in)) {
+ ret = 0;
+ break;
+ }
+ }
+ /* close to flush the buffer */
+ ret = babeltrace_close_memstream(buf, &size, out);
+ if (ret < 0) {
+ int closeret;
+
+ perror("babeltrace_flush_memstream");
+ ret = -errno;
+ closeret = fclose(in);
+ if (closeret) {
+ perror("Error in fclose");
+ }
+ return ret;
+ }
+ ret = fclose(in);
+ if (ret) {
+ perror("Error in fclose");
+ }
+ /* open for reading */
+ buflen = strlen(*buf);
+ if (!buflen) {
+ *fp = NULL;
+ return -ENOENT;
+ }
+ *fp = babeltrace_fmemopen(*buf, buflen, "rb");
+ if (!*fp) {
+ perror("Metadata fmemopen");
+ return -errno;
+ }
+ return 0;
+}
+
+static
+int ctf_trace_metadata_read(struct ctf_trace *td, FILE *metadata_fp,
+ struct ctf_scanner *scanner, int append)
+{
+ struct ctf_file_stream *metadata_stream;
+ FILE *fp;
+ char *buf = NULL;
+ int ret = 0, closeret;
+
+ metadata_stream = g_new0(struct ctf_file_stream, 1);
+ metadata_stream->pos.last_offset = LAST_OFFSET_POISON;
+
+ if (metadata_fp) {
+ fp = metadata_fp;
+ metadata_stream->pos.fd = -1;
+ } else {
+ td->metadata = &metadata_stream->parent;
+ metadata_stream->pos.fd = openat(td->dirfd, "metadata", O_RDONLY);
+ if (metadata_stream->pos.fd < 0) {
+ fprintf(stderr, "Unable to open metadata.\n");
+ ret = -1;
+ goto end_free;
+ }
+
+ fp = fdopen(metadata_stream->pos.fd, "r");
+ if (!fp) {
+ fprintf(stderr, "[error] Unable to open metadata stream.\n");
+ perror("Metadata stream open");
+ ret = -errno;
+ goto end_stream;
+ }
+ /* fd now belongs to fp */
+ metadata_stream->pos.fd = -1;
+ }
+ if (babeltrace_debug)
+ yydebug = 1;
+
+ if (packet_metadata(td, fp)) {
+ ret = ctf_trace_metadata_stream_read(td, &fp, &buf);
+ if (ret) {
+ goto end;
+ }
+ td->metadata_string = buf;
+ td->metadata_packetized = 1;
+ } else {
+ if (!append) {
+ unsigned int major, minor;
+ ssize_t nr_items;
+
+ td->byte_order = BYTE_ORDER;
+
+ /* Check text-only metadata header and version */
+ nr_items = fscanf(fp, "/* CTF %10u.%10u", &major, &minor);
+ if (nr_items < 2)
+ fprintf(stderr, "[warning] Ill-shapen or missing \"/* CTF x.y\" header for text-only metadata.\n");
+ if (check_version(major, minor) < 0) {
+ ret = -EINVAL;
+ goto end;
+ }
+ rewind(fp);
+ }
+ }
+
+ ret = ctf_scanner_append_ast(scanner, fp);
+ if (ret) {
+ fprintf(stderr, "[error] Error creating AST\n");
+ goto end;
+ }
+
+ if (babeltrace_debug) {
+ ret = ctf_visitor_print_xml(stderr, 0, &scanner->ast->root);
+ if (ret) {
+ fprintf(stderr, "[error] Error visiting AST for XML output\n");
+ goto end;
+ }
+ }
+
+ ret = ctf_visitor_semantic_check(stderr, 0, &scanner->ast->root);
+ if (ret) {
+ fprintf(stderr, "[error] Error in CTF semantic validation %d\n", ret);
+ goto end;
+ }
+ ret = ctf_visitor_construct_metadata(stderr, 0, &scanner->ast->root,
+ td, td->byte_order);
+ if (ret) {
+ fprintf(stderr, "[error] Error in CTF metadata constructor %d\n", ret);
+ goto end;
+ }
+end:
+ if (fp) {
+ closeret = fclose(fp);
+ if (closeret) {
+ perror("Error on fclose");
+ }
+ }
+end_stream:
+ if (metadata_stream->pos.fd >= 0) {
+ closeret = close(metadata_stream->pos.fd);
+ if (closeret) {
+ perror("Error on metadata stream fd close");
+ }
+ }
+end_free:
+ if (ret)
+ g_free(metadata_stream);
+ return ret;
+}
+
+static
+struct ctf_event_definition *create_event_definitions(struct ctf_trace *td,
+ struct ctf_stream_definition *stream,
+ struct ctf_event_declaration *event)
+{
+ struct ctf_event_definition *stream_event = g_new0(struct ctf_event_definition, 1);
+
+ if (event->context_decl) {
+ struct bt_definition *definition =
+ event->context_decl->p.definition_new(&event->context_decl->p,
+ stream->parent_def_scope, 0, 0, "event.context");
+ if (!definition) {
+ goto error;
+ }
+ stream_event->event_context = container_of(definition,
+ struct definition_struct, p);
+ stream->parent_def_scope = stream_event->event_context->p.scope;
+ }
+ if (event->fields_decl) {
+ struct bt_definition *definition =
+ event->fields_decl->p.definition_new(&event->fields_decl->p,
+ stream->parent_def_scope, 0, 0, "event.fields");
+ if (!definition) {
+ goto error;
+ }
+ stream_event->event_fields = container_of(definition,
+ struct definition_struct, p);
+ stream->parent_def_scope = stream_event->event_fields->p.scope;
+ }
+ stream_event->stream = stream;
+ return stream_event;
+
+error:
+ if (stream_event->event_fields)
+ bt_definition_unref(&stream_event->event_fields->p);
+ if (stream_event->event_context)
+ bt_definition_unref(&stream_event->event_context->p);
+ fprintf(stderr, "[error] Unable to create event definition for event \"%s\".\n",
+ g_quark_to_string(event->name));
+ return NULL;
+}
+
+static
+int copy_event_declarations_stream_class_to_stream(struct ctf_trace *td,
+ struct ctf_stream_declaration *stream_class,
+ struct ctf_stream_definition *stream)
+{
+ size_t def_size, class_size, i;
+ int ret = 0;
+
+ def_size = stream->events_by_id->len;
+ class_size = stream_class->events_by_id->len;
+
+ g_ptr_array_set_size(stream->events_by_id, class_size);
+ for (i = def_size; i < class_size; i++) {
+ struct ctf_event_declaration *event =
+ g_ptr_array_index(stream_class->events_by_id, i);
+ struct ctf_event_definition *stream_event;
+
+ if (!event)
+ continue;
+ stream_event = create_event_definitions(td, stream, event);
+ if (!stream_event) {
+ ret = -EINVAL;
+ goto error;
+ }
+ g_ptr_array_index(stream->events_by_id, i) = stream_event;
+ }
+error:
+ return ret;
+}
+
+static
+int create_stream_definitions(struct ctf_trace *td, struct ctf_stream_definition *stream)
+{
+ struct ctf_stream_declaration *stream_class;
+ int ret;
+ int i;
+
+ if (stream->stream_definitions_created)
+ return 0;
+
+ stream_class = stream->stream_class;
+
+ if (stream_class->packet_context_decl) {
+ struct bt_definition *definition =
+ stream_class->packet_context_decl->p.definition_new(&stream_class->packet_context_decl->p,
+ stream->parent_def_scope, 0, 0, "stream.packet.context");
+ if (!definition) {
+ ret = -EINVAL;
+ goto error;
+ }
+ stream->stream_packet_context = container_of(definition,
+ struct definition_struct, p);
+ stream->parent_def_scope = stream->stream_packet_context->p.scope;
+ }
+ if (stream_class->event_header_decl) {
+ struct bt_definition *definition =
+ stream_class->event_header_decl->p.definition_new(&stream_class->event_header_decl->p,
+ stream->parent_def_scope, 0, 0, "stream.event.header");
+ if (!definition) {
+ ret = -EINVAL;
+ goto error;
+ }
+ stream->stream_event_header =
+ container_of(definition, struct definition_struct, p);
+ stream->parent_def_scope = stream->stream_event_header->p.scope;
+ }
+ if (stream_class->event_context_decl) {
+ struct bt_definition *definition =
+ stream_class->event_context_decl->p.definition_new(&stream_class->event_context_decl->p,
+ stream->parent_def_scope, 0, 0, "stream.event.context");
+ if (!definition) {
+ ret = -EINVAL;
+ goto error;