+static
+enum lttng_trace_chunk_status lttng_trace_chunk_rename_path_no_lock(
+ struct lttng_trace_chunk *chunk, const char *path)
+
+{
+ enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
+ struct lttng_directory_handle *rename_directory = NULL;
+ char *new_path, *old_path;
+ int ret;
+
+ if (chunk->name_overridden) {
+ status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
+ goto end;
+ }
+
+ old_path = chunk->path;
+ DBG("lttng_trace_chunk_rename_path from %s to %s", old_path, path);
+
+ if ((!old_path && !path) ||
+ (old_path && path && !strcmp(old_path, path))) {
+ goto end;
+ }
+ /*
+ * Use chunk name as path if NULL path is specified.
+ */
+ if (!path) {
+ path = chunk->name;
+ }
+
+ /* Renaming from "" to "" is not accepted. */
+ if (path[0] == '\0' && old_path[0] == '\0') {
+ status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
+ goto end;
+ }
+
+ /*
+ * If a rename is performed on a chunk for which the chunk_directory
+ * is not set (yet), or the session_output_directory is not set
+ * (interacting with a relay daemon), there is no rename to perform.
+ */
+ if (!chunk->chunk_directory ||
+ !chunk->session_output_directory) {
+ goto skip_move;
+ }
+
+ if (old_path && old_path[0] != '\0' && path[0] != '\0') {
+ /* Rename chunk directory. */
+ ret = lttng_directory_handle_rename_as_user(
+ chunk->session_output_directory,
+ old_path,
+ chunk->session_output_directory,
+ path,
+ LTTNG_OPTIONAL_GET(chunk->credentials).use_current_user ?
+ NULL :
+ &chunk->credentials.value.user);
+ if (ret) {
+ PERROR("Failed to move trace chunk directory \"%s\" to \"%s\"",
+ old_path, path);
+ status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
+ goto end;
+ }
+ rename_directory = chunk->fd_tracker ?
+ fd_tracker_create_directory_handle_from_handle(
+ chunk->fd_tracker,
+ chunk->session_output_directory,
+ path) :
+ lttng_directory_handle_create_from_handle(
+ path,
+ chunk->session_output_directory);
+ if (!rename_directory) {
+ ERR("Failed to get handle to trace chunk rename directory");
+ status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
+ goto end;
+ }
+
+ /* Release old handle. */
+ lttng_directory_handle_put(chunk->chunk_directory);
+ /*
+ * Transfer new handle reference to chunk as the current chunk
+ * handle.
+ */
+ chunk->chunk_directory = rename_directory;
+ rename_directory = NULL;
+ } else if (old_path && old_path[0] == '\0') {
+ size_t i, count = lttng_dynamic_pointer_array_get_count(
+ &chunk->top_level_directories);
+
+ ret = lttng_directory_handle_create_subdirectory_as_user(
+ chunk->session_output_directory,
+ path,
+ DIR_CREATION_MODE,
+ LTTNG_OPTIONAL_GET(chunk->credentials).use_current_user ?
+ NULL :
+ &chunk->credentials.value.user);
+ if (ret) {
+ PERROR("Failed to create trace chunk rename directory \"%s\"",
+ path);
+ status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
+ goto end;
+ }
+
+ rename_directory = lttng_directory_handle_create_from_handle(
+ path, chunk->session_output_directory);
+ if (!rename_directory) {
+ ERR("Failed to get handle to trace chunk rename directory");
+ status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
+ goto end;
+ }
+
+ /* Move toplevel directories. */
+ for (i = 0; i < count; i++) {
+ const char *top_level_name =
+ lttng_dynamic_pointer_array_get_pointer(
+ &chunk->top_level_directories, i);
+
+ ret = lttng_directory_handle_rename_as_user(
+ chunk->chunk_directory,
+ top_level_name,
+ rename_directory,
+ top_level_name,
+ LTTNG_OPTIONAL_GET(chunk->credentials).use_current_user ?
+ NULL :
+ &chunk->credentials.value.user);
+ if (ret) {
+ PERROR("Failed to move \"%s\" to trace chunk rename directory",
+ top_level_name);
+ status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
+ goto end;
+ }
+ }
+ /* Release old handle. */
+ lttng_directory_handle_put(chunk->chunk_directory);
+ /*
+ * Transfer new handle reference to chunk as the current chunk
+ * handle.
+ */
+ chunk->chunk_directory = rename_directory;
+ rename_directory = NULL;
+ } else if (old_path) {
+ size_t i, count = lttng_dynamic_pointer_array_get_count(
+ &chunk->top_level_directories);
+ const bool reference_acquired = lttng_directory_handle_get(
+ chunk->session_output_directory);
+
+ LTTNG_ASSERT(reference_acquired);
+ rename_directory = chunk->session_output_directory;
+
+ /* Move toplevel directories. */
+ for (i = 0; i < count; i++) {
+ const char *top_level_name =
+ lttng_dynamic_pointer_array_get_pointer(
+ &chunk->top_level_directories, i);
+
+ ret = lttng_directory_handle_rename_as_user(
+ chunk->chunk_directory,
+ top_level_name,
+ rename_directory,
+ top_level_name,
+ LTTNG_OPTIONAL_GET(chunk->credentials).use_current_user ?
+ NULL :
+ &chunk->credentials.value.user);
+ if (ret) {
+ PERROR("Failed to move \"%s\" to trace chunk rename directory",
+ top_level_name);
+ status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
+ goto end;
+ }
+ }
+ /* Release old handle. */
+ lttng_directory_handle_put(chunk->chunk_directory);
+ /*
+ * Transfer new handle reference to chunk as the current chunk
+ * handle.
+ */
+ chunk->chunk_directory = rename_directory;
+ rename_directory = NULL;
+
+ /* Remove old directory. */
+ status = lttng_directory_handle_remove_subdirectory(
+ chunk->session_output_directory,
+ old_path);
+ if (status != LTTNG_TRACE_CHUNK_STATUS_OK) {
+ ERR("Error removing subdirectory '%s' file when deleting chunk",
+ old_path);
+ goto end;
+ }
+ } else {
+ /* Unexpected !old_path && !path. */
+ status = LTTNG_TRACE_CHUNK_STATUS_INVALID_ARGUMENT;
+ goto end;
+ }
+
+skip_move:
+ new_path = strdup(path);
+ if (!new_path) {
+ ERR("Failed to allocate new trace chunk path");
+ status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
+ goto end;
+ }
+ free(chunk->path);
+ chunk->path = new_path;
+end:
+ lttng_directory_handle_put(rename_directory);
+ return status;
+}
+
+enum lttng_trace_chunk_status lttng_trace_chunk_rename_path(
+ struct lttng_trace_chunk *chunk, const char *path)
+
+{
+ enum lttng_trace_chunk_status status;
+
+ pthread_mutex_lock(&chunk->lock);
+ status = lttng_trace_chunk_rename_path_no_lock(chunk, path);
+ pthread_mutex_unlock(&chunk->lock);
+
+ return status;
+}
+