+ int64_t stream_instance_id = -1;
+ int64_t begin_ns = -1;
+ struct ctf_fs_ds_file_group *ds_file_group = NULL;
+ bool add_group = false;
+ int ret;
+ size_t i;
+ struct ctf_fs_ds_file *ds_file = NULL;
+ struct ctf_fs_ds_index *index = NULL;
+ struct bt_msg_iter *msg_iter = NULL;
+ struct ctf_stream_class *sc = NULL;
+ struct bt_msg_iter_packet_properties props;
+
+ msg_iter = bt_msg_iter_create(ctf_fs_trace->metadata->tc,
+ bt_common_get_page_size() * 8, ctf_fs_ds_file_medops, NULL);
+ if (!msg_iter) {
+ BT_LOGE_STR("Cannot create a CTF message iterator.");
+ goto error;
+ }
+
+ ds_file = ctf_fs_ds_file_create(ctf_fs_trace, NULL, msg_iter,
+ NULL, path);
+ if (!ds_file) {
+ goto error;
+ }
+
+ ret = bt_msg_iter_get_packet_properties(ds_file->msg_iter, &props);
+ if (ret) {
+ BT_LOGE("Cannot get stream file's first packet's header and context fields (`%s`).",
+ path);
+ goto error;
+ }
+
+ sc = ctf_trace_class_borrow_stream_class_by_id(ds_file->metadata->tc,
+ props.stream_class_id);
+ BT_ASSERT(sc);
+ stream_instance_id = props.data_stream_id;
+
+ if (props.snapshots.beginning_clock != UINT64_C(-1)) {
+ BT_ASSERT(sc->default_clock_class);
+ ret = bt_util_clock_cycles_to_ns_from_origin(
+ props.snapshots.beginning_clock,
+ sc->default_clock_class->frequency,
+ sc->default_clock_class->offset_seconds,
+ sc->default_clock_class->offset_cycles, &begin_ns);
+ if (ret) {
+ BT_LOGE("Cannot convert clock cycles to nanoseconds from origin (`%s`).",
+ path);
+ goto error;
+ }
+ }
+
+ index = ctf_fs_ds_file_build_index(ds_file);
+ if (!index) {
+ BT_LOGW("Failed to index CTF stream file \'%s\'",
+ ds_file->file->path->str);
+ }
+
+ if (begin_ns == -1) {
+ /*
+ * No beggining timestamp to sort the stream files
+ * within a stream file group, so consider that this
+ * file must be the only one within its group.
+ */
+ stream_instance_id = -1;
+ }
+
+ if (stream_instance_id == -1) {
+ /*
+ * No stream instance ID or no beginning timestamp:
+ * create a unique stream file group for this stream
+ * file because, even if there's a stream instance ID,
+ * there's no timestamp to order the file within its
+ * group.
+ */
+ ds_file_group = ctf_fs_ds_file_group_create(ctf_fs_trace,
+ sc, UINT64_C(-1));
+ if (!ds_file_group) {
+ goto error;
+ }
+
+ ret = ctf_fs_ds_file_group_add_ds_file_info(ds_file_group,
+ path, begin_ns, index);
+ /* Ownership of index is transferred. */
+ index = NULL;
+ if (ret) {
+ goto error;
+ }
+
+ add_group = true;
+ goto end;
+ }
+
+ BT_ASSERT(stream_instance_id != -1);
+ BT_ASSERT(begin_ns != -1);
+
+ /* Find an existing stream file group with this ID */
+ for (i = 0; i < ctf_fs_trace->ds_file_groups->len; i++) {
+ ds_file_group = g_ptr_array_index(
+ ctf_fs_trace->ds_file_groups, i);
+
+ if (ds_file_group->sc == sc &&
+ ds_file_group->stream_id ==
+ stream_instance_id) {
+ break;
+ }
+
+ ds_file_group = NULL;
+ }
+
+ if (!ds_file_group) {
+ ds_file_group = ctf_fs_ds_file_group_create(ctf_fs_trace,
+ sc, stream_instance_id);
+ if (!ds_file_group) {
+ goto error;
+ }
+
+ add_group = true;
+ }
+
+ ret = ctf_fs_ds_file_group_add_ds_file_info(ds_file_group, path,
+ begin_ns, index);
+ index = NULL;
+ if (ret) {
+ goto error;
+ }
+
+ goto end;
+
+error:
+ ctf_fs_ds_file_group_destroy(ds_file_group);
+ ret = -1;
+
+end:
+ if (add_group && ds_file_group) {
+ g_ptr_array_add(ctf_fs_trace->ds_file_groups, ds_file_group);
+ }
+
+ ctf_fs_ds_file_destroy(ds_file);
+
+ if (msg_iter) {
+ bt_msg_iter_destroy(msg_iter);
+ }
+
+ ctf_fs_ds_index_destroy(index);
+ return ret;
+}
+
+static
+int create_ds_file_groups(struct ctf_fs_trace *ctf_fs_trace)
+{
+ int ret = 0;
+ const char *basename;
+ GError *error = NULL;
+ GDir *dir = NULL;
+
+ /* Check each file in the path directory, except specific ones */
+ dir = g_dir_open(ctf_fs_trace->path->str, 0, &error);
+ if (!dir) {
+ BT_LOGE("Cannot open directory `%s`: %s (code %d)",
+ ctf_fs_trace->path->str, error->message,
+ error->code);
+ goto error;
+ }
+
+ while ((basename = g_dir_read_name(dir))) {
+ struct ctf_fs_file *file;
+
+ if (!strcmp(basename, CTF_FS_METADATA_FILENAME)) {
+ /* Ignore the metadata stream. */
+ BT_LOGD("Ignoring metadata file `%s" G_DIR_SEPARATOR_S "%s`",
+ ctf_fs_trace->path->str, basename);
+ continue;
+ }
+
+ if (basename[0] == '.') {
+ BT_LOGD("Ignoring hidden file `%s" G_DIR_SEPARATOR_S "%s`",
+ ctf_fs_trace->path->str, basename);
+ continue;
+ }
+
+ /* Create the file. */
+ file = ctf_fs_file_create();
+ if (!file) {
+ BT_LOGE("Cannot create stream file object for file `%s" G_DIR_SEPARATOR_S "%s`",
+ ctf_fs_trace->path->str, basename);
+ goto error;
+ }
+
+ /* Create full path string. */
+ g_string_append_printf(file->path, "%s" G_DIR_SEPARATOR_S "%s",
+ ctf_fs_trace->path->str, basename);
+ if (!g_file_test(file->path->str, G_FILE_TEST_IS_REGULAR)) {
+ BT_LOGD("Ignoring non-regular file `%s`",
+ file->path->str);
+ ctf_fs_file_destroy(file);
+ file = NULL;
+ continue;
+ }
+
+ ret = ctf_fs_file_open(file, "rb");
+ if (ret) {
+ BT_LOGE("Cannot open stream file `%s`", file->path->str);
+ goto error;
+ }
+
+ if (file->size == 0) {
+ /* Skip empty stream. */
+ BT_LOGD("Ignoring empty file `%s`", file->path->str);
+ ctf_fs_file_destroy(file);
+ continue;
+ }
+
+ ret = add_ds_file_to_ds_file_group(ctf_fs_trace,
+ file->path->str);
+ if (ret) {
+ BT_LOGE("Cannot add stream file `%s` to stream file group",
+ file->path->str);
+ ctf_fs_file_destroy(file);
+ goto error;
+ }
+
+ ctf_fs_file_destroy(file);
+ }
+
+ goto end;
+
+error:
+ ret = -1;
+
+end:
+ if (dir) {
+ g_dir_close(dir);
+ dir = NULL;
+ }
+
+ if (error) {
+ g_error_free(error);
+ }
+
+ return ret;
+}
+
+static
+int set_trace_name(bt_trace *trace, const char *name_suffix)
+{