clang-tidy: add Chrome-inspired checks
[lttng-tools.git] / src / common / trace-chunk.cpp
CommitLineData
2c5ff4e4 1/*
ab5be9fa 2 * Copyright (C) 2019 Jérémie Galarneau <jeremie.galarneau@efficios.com>
2c5ff4e4 3 *
ab5be9fa 4 * SPDX-License-Identifier: LGPL-2.1-only
2c5ff4e4 5 *
2c5ff4e4
JG
6 */
7
c9e313bc
SM
8#include <common/compat/directory-handle.hpp>
9#include <common/credentials.hpp>
10#include <common/defaults.hpp>
11#include <common/dynamic-array.hpp>
12#include <common/error.hpp>
13#include <common/fd-tracker/fd-tracker.hpp>
14#include <common/fd-tracker/utils.hpp>
c9e313bc 15#include <common/fs-handle-internal.hpp>
28ab034a 16#include <common/fs-handle.hpp>
c9e313bc
SM
17#include <common/hashtable/hashtable.hpp>
18#include <common/hashtable/utils.hpp>
19#include <common/optional.hpp>
20#include <common/string-utils/format.hpp>
21#include <common/time.hpp>
22#include <common/trace-chunk-registry.hpp>
23#include <common/trace-chunk.hpp>
24#include <common/utils.hpp>
28ab034a 25
b2621f79 26#include <lttng/constant.h>
2c5ff4e4 27
2c5ff4e4
JG
28#include <inttypes.h>
29#include <pthread.h>
30#include <stdio.h>
b2621f79
JG
31#include <sys/stat.h>
32#include <urcu/rculfhash.h>
33#include <urcu/ref.h>
2c5ff4e4
JG
34
35/*
36 * Two ISO 8601-compatible timestamps, separated by a hypen, followed an
37 * index, i.e. <start-iso-8601>-<end-iso-8601>-<id-uint64_t>.
38 */
39#define GENERATED_CHUNK_NAME_LEN (2 * sizeof("YYYYmmddTHHMMSS+HHMM") + MAX_INT_DEC_LEN(uint64_t))
28ab034a 40#define DIR_CREATION_MODE (S_IRWXU | S_IRWXG)
2c5ff4e4
JG
41
42enum trace_chunk_mode {
43 TRACE_CHUNK_MODE_USER,
44 TRACE_CHUNK_MODE_OWNER,
45};
46
47/*
48 * Callback to invoke on release of a trace chunk. Note that there is no
49 * need to 'lock' the trace chunk during the execution of these callbacks
50 * since only one thread may access a chunk during its destruction (the last
51 * to release its reference to the chunk).
52 */
e665dfbc 53using chunk_command = int (*)(struct lttng_trace_chunk *);
2c5ff4e4
JG
54
55/* Move a completed trace chunk to the 'completed' trace archive folder. */
28ab034a 56static int lttng_trace_chunk_move_to_completed_post_release(struct lttng_trace_chunk *trace_chunk);
8ced4811 57/* Empty callback. */
28ab034a 58static int lttng_trace_chunk_no_operation(struct lttng_trace_chunk *trace_chunk);
8ced4811 59/* Unlink old chunk files. */
28ab034a
JG
60static int lttng_trace_chunk_delete_post_release(struct lttng_trace_chunk *trace_chunk);
61static enum lttng_trace_chunk_status
62lttng_trace_chunk_rename_path_no_lock(struct lttng_trace_chunk *chunk, const char *path);
2c5ff4e4 63
f1494934 64namespace {
2c5ff4e4
JG
65struct chunk_credentials {
66 bool use_current_user;
67 struct lttng_credentials user;
68};
f1494934 69} /* namespace */
2c5ff4e4 70
a7ceb342
MD
71/*
72 * NOTE: Make sure to update:
73 * - lttng_trace_chunk_copy(),
74 * - lttng_trace_chunk_registry_element_create_from_chunk()
75 * if you modify this structure.
76 */
2c5ff4e4
JG
77struct lttng_trace_chunk {
78 pthread_mutex_t lock;
79 struct urcu_ref ref;
80 LTTNG_OPTIONAL(enum trace_chunk_mode) mode;
81 /*
82 * First-level directories created within the trace chunk.
83 * Elements are of type 'char *'.
1a414e3a
JG
84 *
85 * Only used by _owner_ mode chunks.
2c5ff4e4
JG
86 */
87 struct lttng_dynamic_pointer_array top_level_directories;
6cb32e5a
MD
88 /*
89 * All files contained within the trace chunk.
90 * Array of paths (char *).
91 */
92 struct lttng_dynamic_pointer_array files;
2c5ff4e4
JG
93 /* Is contained within an lttng_trace_chunk_registry_element? */
94 bool in_registry_element;
913a542b 95 bool name_overridden;
2c5ff4e4 96 char *name;
a7ceb342 97 char *path;
2c5ff4e4
JG
98 /* An unset id means the chunk is anonymous. */
99 LTTNG_OPTIONAL(uint64_t) id;
4b050fdd
JR
100
101 /*
102 * The creation and close timestamps are NOT monotonic.
103 * They must not be used in context were monotonicity is required.
104 */
2c5ff4e4
JG
105 LTTNG_OPTIONAL(time_t) timestamp_creation;
106 LTTNG_OPTIONAL(time_t) timestamp_close;
4b050fdd 107
2c5ff4e4 108 LTTNG_OPTIONAL(struct chunk_credentials) credentials;
cbf53d23
JG
109 struct lttng_directory_handle *session_output_directory;
110 struct lttng_directory_handle *chunk_directory;
2c5ff4e4 111 LTTNG_OPTIONAL(enum lttng_trace_chunk_command_type) close_command;
b2621f79
JG
112 /*
113 * fd_tracker instance through which file descriptors should be
114 * created/closed.
115 *
116 * An fd_tracker always outlives any trace chunk; there is no
117 * need to perform any reference counting of that object.
118 */
119 struct fd_tracker *fd_tracker;
2c5ff4e4
JG
120};
121
f1494934 122namespace {
2c5ff4e4
JG
123/* A trace chunk is uniquely identified by its (session id, chunk id) tuple. */
124struct lttng_trace_chunk_registry_element {
2c5ff4e4 125 struct lttng_trace_chunk chunk;
1f2292f6 126 uint64_t session_id;
2c5ff4e4
JG
127 /* Weak and only set when added. */
128 struct lttng_trace_chunk_registry *registry;
129 struct cds_lfht_node trace_chunk_registry_ht_node;
130 /* call_rcu delayed reclaim. */
131 struct rcu_head rcu_node;
132};
f1494934 133} /* namespace */
2c5ff4e4
JG
134
135struct lttng_trace_chunk_registry {
136 struct cds_lfht *ht;
137};
138
f1494934 139namespace {
8bb66c3c
JG
140struct fs_handle_untracked {
141 struct fs_handle parent;
142 int fd;
143 struct {
144 struct lttng_directory_handle *directory_handle;
145 char *path;
146 } location;
147};
f1494934 148} /* namespace */
8bb66c3c 149
28ab034a
JG
150static int fs_handle_untracked_get_fd(struct fs_handle *handle);
151static void fs_handle_untracked_put_fd(struct fs_handle *handle);
152static int fs_handle_untracked_unlink(struct fs_handle *handle);
153static int fs_handle_untracked_close(struct fs_handle *handle);
154
155static const char *lttng_trace_chunk_command_type_str(lttng_trace_chunk_command_type type)
156{
a6bc4ca9
SM
157 switch (type) {
158 case LTTNG_TRACE_CHUNK_COMMAND_TYPE_MOVE_TO_COMPLETED:
159 return "move to completed chunk folder";
160 case LTTNG_TRACE_CHUNK_COMMAND_TYPE_NO_OPERATION:
161 return "no operation";
162 case LTTNG_TRACE_CHUNK_COMMAND_TYPE_DELETE:
163 return "delete";
164 case LTTNG_TRACE_CHUNK_COMMAND_TYPE_MAX:
165 abort();
166 }
167
168 abort();
2c5ff4e4
JG
169};
170
28ab034a
JG
171static chunk_command close_command_get_post_release_func(lttng_trace_chunk_command_type type)
172{
a6bc4ca9
SM
173 switch (type) {
174 case LTTNG_TRACE_CHUNK_COMMAND_TYPE_MOVE_TO_COMPLETED:
175 return lttng_trace_chunk_move_to_completed_post_release;
176 case LTTNG_TRACE_CHUNK_COMMAND_TYPE_NO_OPERATION:
177 return lttng_trace_chunk_no_operation;
178 case LTTNG_TRACE_CHUNK_COMMAND_TYPE_DELETE:
179 return lttng_trace_chunk_delete_post_release;
180 case LTTNG_TRACE_CHUNK_COMMAND_TYPE_MAX:
181 abort();
182 }
183
184 abort();
2c5ff4e4
JG
185};
186
28ab034a
JG
187static struct fs_handle *fs_handle_untracked_create(struct lttng_directory_handle *directory_handle,
188 const char *path,
189 int fd)
8bb66c3c 190{
cd9adb8b 191 struct fs_handle_untracked *handle = nullptr;
8bb66c3c
JG
192 bool reference_acquired;
193 char *path_copy = strdup(path);
194
a0377dfe 195 LTTNG_ASSERT(fd >= 0);
8bb66c3c
JG
196 if (!path_copy) {
197 PERROR("Failed to copy file path while creating untracked filesystem handle");
198 goto end;
199 }
200
64803277 201 handle = zmalloc<fs_handle_untracked>();
8bb66c3c
JG
202 if (!handle) {
203 PERROR("Failed to allocate untracked filesystem handle");
204 goto end;
205 }
206
28ab034a 207 handle->parent = (typeof(handle->parent)){
8bb66c3c
JG
208 .get_fd = fs_handle_untracked_get_fd,
209 .put_fd = fs_handle_untracked_put_fd,
210 .unlink = fs_handle_untracked_unlink,
211 .close = fs_handle_untracked_close,
212 };
213
214 handle->fd = fd;
215 reference_acquired = lttng_directory_handle_get(directory_handle);
a0377dfe 216 LTTNG_ASSERT(reference_acquired);
8bb66c3c
JG
217 handle->location.directory_handle = directory_handle;
218 /* Ownership is transferred. */
219 handle->location.path = path_copy;
cd9adb8b 220 path_copy = nullptr;
8bb66c3c
JG
221end:
222 free(path_copy);
cd9adb8b 223 return handle ? &handle->parent : nullptr;
8bb66c3c
JG
224}
225
28ab034a 226static int fs_handle_untracked_get_fd(struct fs_handle *_handle)
8bb66c3c 227{
28ab034a
JG
228 struct fs_handle_untracked *handle =
229 lttng::utils::container_of(_handle, &fs_handle_untracked::parent);
8bb66c3c
JG
230
231 return handle->fd;
232}
233
28ab034a 234static void fs_handle_untracked_put_fd(struct fs_handle *_handle __attribute__((unused)))
8bb66c3c
JG
235{
236 /* no-op. */
237}
238
28ab034a 239static int fs_handle_untracked_unlink(struct fs_handle *_handle)
8bb66c3c 240{
28ab034a
JG
241 struct fs_handle_untracked *handle =
242 lttng::utils::container_of(_handle, &fs_handle_untracked::parent);
8bb66c3c 243
28ab034a
JG
244 return lttng_directory_handle_unlink_file(handle->location.directory_handle,
245 handle->location.path);
8bb66c3c
JG
246}
247
28ab034a 248static void fs_handle_untracked_destroy(struct fs_handle_untracked *handle)
8bb66c3c
JG
249{
250 lttng_directory_handle_put(handle->location.directory_handle);
251 free(handle->location.path);
252 free(handle);
253}
254
28ab034a 255static int fs_handle_untracked_close(struct fs_handle *_handle)
8bb66c3c 256{
28ab034a
JG
257 struct fs_handle_untracked *handle =
258 lttng::utils::container_of(_handle, &fs_handle_untracked::parent);
8bb66c3c
JG
259 int ret = close(handle->fd);
260
261 fs_handle_untracked_destroy(handle);
262 return ret;
263}
264
28ab034a
JG
265static bool
266lttng_trace_chunk_registry_element_equals(const struct lttng_trace_chunk_registry_element *a,
267 const struct lttng_trace_chunk_registry_element *b)
2c5ff4e4
JG
268{
269 if (a->session_id != b->session_id) {
270 goto not_equal;
271 }
272 if (a->chunk.id.is_set != b->chunk.id.is_set) {
273 goto not_equal;
274 }
275 if (a->chunk.id.is_set && a->chunk.id.value != b->chunk.id.value) {
276 goto not_equal;
277 }
278 return true;
279not_equal:
280 return false;
281}
282
28ab034a 283static int lttng_trace_chunk_registry_element_match(struct cds_lfht_node *node, const void *key)
2c5ff4e4
JG
284{
285 const struct lttng_trace_chunk_registry_element *element_a, *element_b;
286
287 element_a = (const struct lttng_trace_chunk_registry_element *) key;
28ab034a 288 element_b = caa_container_of(node, typeof(*element_b), trace_chunk_registry_ht_node);
2c5ff4e4
JG
289 return lttng_trace_chunk_registry_element_equals(element_a, element_b);
290}
291
28ab034a
JG
292static unsigned long
293lttng_trace_chunk_registry_element_hash(const struct lttng_trace_chunk_registry_element *element)
2c5ff4e4 294{
28ab034a 295 unsigned long hash = hash_key_u64(&element->session_id, lttng_ht_seed);
2c5ff4e4
JG
296
297 if (element->chunk.id.is_set) {
298 hash ^= hash_key_u64(&element->chunk.id.value, lttng_ht_seed);
299 }
300
301 return hash;
302}
303
28ab034a
JG
304static char *
305generate_chunk_name(uint64_t chunk_id, time_t creation_timestamp, const time_t *close_timestamp)
2c5ff4e4
JG
306{
307 int ret = 0;
cd9adb8b 308 char *new_name = nullptr;
6e7e5048
JG
309 char start_datetime[ISO8601_STR_LEN] = {};
310 /* Add 1 for a '-' prefix. */
311 char end_datetime_suffix[ISO8601_STR_LEN + 1] = {};
2c5ff4e4 312
28ab034a 313 ret = time_to_iso8601_str(creation_timestamp, start_datetime, sizeof(start_datetime));
2c5ff4e4
JG
314 if (ret) {
315 ERR("Failed to format trace chunk start date time");
316 goto error;
317 }
318 if (close_timestamp) {
319 *end_datetime_suffix = '-';
320 ret = time_to_iso8601_str(
28ab034a 321 *close_timestamp, end_datetime_suffix + 1, sizeof(end_datetime_suffix) - 1);
2c5ff4e4
JG
322 if (ret) {
323 ERR("Failed to format trace chunk end date time");
324 goto error;
325 }
326 }
64803277 327 new_name = calloc<char>(GENERATED_CHUNK_NAME_LEN);
2c5ff4e4
JG
328 if (!new_name) {
329 ERR("Failed to allocate buffer for automatically-generated trace chunk name");
330 goto error;
331 }
28ab034a
JG
332 ret = snprintf(new_name,
333 GENERATED_CHUNK_NAME_LEN,
334 "%s%s-%" PRIu64,
335 start_datetime,
336 end_datetime_suffix,
337 chunk_id);
2c5ff4e4
JG
338 if (ret >= GENERATED_CHUNK_NAME_LEN || ret == -1) {
339 ERR("Failed to format trace chunk name");
340 goto error;
341 }
342
343 return new_name;
344error:
345 free(new_name);
cd9adb8b 346 return nullptr;
2c5ff4e4
JG
347}
348
28ab034a 349static void lttng_trace_chunk_init(struct lttng_trace_chunk *chunk)
2c5ff4e4
JG
350{
351 urcu_ref_init(&chunk->ref);
cd9adb8b 352 pthread_mutex_init(&chunk->lock, nullptr);
93bed9fe 353 lttng_dynamic_pointer_array_init(&chunk->top_level_directories, free);
6cb32e5a 354 lttng_dynamic_pointer_array_init(&chunk->files, free);
2c5ff4e4
JG
355}
356
28ab034a 357static void lttng_trace_chunk_fini(struct lttng_trace_chunk *chunk)
2c5ff4e4 358{
cbf53d23 359 if (chunk->session_output_directory) {
28ab034a 360 lttng_directory_handle_put(chunk->session_output_directory);
cd9adb8b 361 chunk->session_output_directory = nullptr;
2c5ff4e4 362 }
cbf53d23
JG
363 if (chunk->chunk_directory) {
364 lttng_directory_handle_put(chunk->chunk_directory);
cd9adb8b 365 chunk->chunk_directory = nullptr;
2c5ff4e4
JG
366 }
367 free(chunk->name);
cd9adb8b 368 chunk->name = nullptr;
a7ceb342 369 free(chunk->path);
cd9adb8b 370 chunk->path = nullptr;
93bed9fe 371 lttng_dynamic_pointer_array_reset(&chunk->top_level_directories);
6cb32e5a 372 lttng_dynamic_pointer_array_reset(&chunk->files);
2c5ff4e4
JG
373 pthread_mutex_destroy(&chunk->lock);
374}
375
cd9adb8b 376static struct lttng_trace_chunk *lttng_trace_chunk_allocate()
2c5ff4e4 377{
cd9adb8b 378 struct lttng_trace_chunk *chunk = nullptr;
2c5ff4e4 379
64803277 380 chunk = zmalloc<lttng_trace_chunk>();
2c5ff4e4
JG
381 if (!chunk) {
382 ERR("Failed to allocate trace chunk");
383 goto end;
384 }
385 lttng_trace_chunk_init(chunk);
386end:
387 return chunk;
388}
389
cd9adb8b 390struct lttng_trace_chunk *lttng_trace_chunk_create_anonymous()
2c5ff4e4
JG
391{
392 DBG("Creating anonymous trace chunk");
393 return lttng_trace_chunk_allocate();
394}
395
28ab034a
JG
396struct lttng_trace_chunk *
397lttng_trace_chunk_create(uint64_t chunk_id, time_t chunk_creation_time, const char *path)
2c5ff4e4
JG
398{
399 struct lttng_trace_chunk *chunk;
420acd90 400 char chunk_creation_datetime_buf[16] = {};
2c5ff4e4 401 const char *chunk_creation_datetime_str = "(formatting error)";
420acd90 402 struct tm timeinfo_buf, *timeinfo;
2c5ff4e4
JG
403
404 timeinfo = localtime_r(&chunk_creation_time, &timeinfo_buf);
405 if (timeinfo) {
406 size_t strftime_ret;
407
408 /* Don't fail because of this; it is only used for logging. */
409 strftime_ret = strftime(chunk_creation_datetime_buf,
28ab034a
JG
410 sizeof(chunk_creation_datetime_buf),
411 "%Y%m%d-%H%M%S",
412 timeinfo);
2c5ff4e4 413 if (strftime_ret) {
28ab034a 414 chunk_creation_datetime_str = chunk_creation_datetime_buf;
2c5ff4e4
JG
415 }
416 }
417
418 DBG("Creating trace chunk: chunk_id = %" PRIu64 ", creation time = %s",
28ab034a
JG
419 chunk_id,
420 chunk_creation_datetime_str);
2c5ff4e4
JG
421 chunk = lttng_trace_chunk_allocate();
422 if (!chunk) {
423 goto end;
424 }
425
426 LTTNG_OPTIONAL_SET(&chunk->id, chunk_id);
427 LTTNG_OPTIONAL_SET(&chunk->timestamp_creation, chunk_creation_time);
428 if (chunk_id != 0) {
cd9adb8b 429 chunk->name = generate_chunk_name(chunk_id, chunk_creation_time, nullptr);
2c5ff4e4
JG
430 if (!chunk->name) {
431 ERR("Failed to allocate trace chunk name storage");
432 goto error;
433 }
420acd90 434 }
a7ceb342
MD
435 if (path) {
436 chunk->path = strdup(path);
437 if (!chunk->path) {
438 goto error;
439 }
440 } else {
441 if (chunk->name) {
442 chunk->path = strdup(chunk->name);
443 if (!chunk->path) {
444 goto error;
445 }
446 }
447 }
2c5ff4e4 448
28ab034a 449 DBG("Chunk name set to \"%s\"", chunk->name ?: "(none)");
2c5ff4e4
JG
450end:
451 return chunk;
452error:
453 lttng_trace_chunk_put(chunk);
cd9adb8b 454 return nullptr;
2c5ff4e4
JG
455}
456
b2621f79 457void lttng_trace_chunk_set_fd_tracker(struct lttng_trace_chunk *chunk,
28ab034a 458 struct fd_tracker *fd_tracker)
b2621f79 459{
a0377dfe
FD
460 LTTNG_ASSERT(!chunk->session_output_directory);
461 LTTNG_ASSERT(!chunk->chunk_directory);
462 LTTNG_ASSERT(lttng_dynamic_pointer_array_get_count(&chunk->files) == 0);
b2621f79
JG
463 chunk->fd_tracker = fd_tracker;
464}
465
28ab034a 466struct lttng_trace_chunk *lttng_trace_chunk_copy(struct lttng_trace_chunk *source_chunk)
1a414e3a
JG
467{
468 struct lttng_trace_chunk *new_chunk = lttng_trace_chunk_allocate();
469
470 if (!new_chunk) {
471 goto end;
472 }
473
474 pthread_mutex_lock(&source_chunk->lock);
475 /*
476 * A new chunk is always a user; it shall create no new trace
477 * subdirectories.
478 */
28ab034a 479 new_chunk->mode = (typeof(new_chunk->mode)){
1a414e3a
JG
480 .is_set = true,
481 .value = TRACE_CHUNK_MODE_USER,
482 };
483 /*
484 * top_level_directories is not copied as it is never used
485 * by _user_ mode chunks.
486 */
487 /* The new chunk is not part of a registry (yet, at least). */
488 new_chunk->in_registry_element = false;
489 new_chunk->name_overridden = source_chunk->name_overridden;
490 if (source_chunk->name) {
491 new_chunk->name = strdup(source_chunk->name);
492 if (!new_chunk->name) {
28ab034a 493 ERR("Failed to copy source trace chunk name in %s()", __FUNCTION__);
1a414e3a
JG
494 goto error_unlock;
495 }
496 }
a7ceb342
MD
497 if (source_chunk->path) {
498 new_chunk->path = strdup(source_chunk->path);
499 if (!new_chunk->path) {
28ab034a 500 ERR("Failed to copy source trace chunk path in %s()", __FUNCTION__);
a7ceb342
MD
501 }
502 }
1a414e3a
JG
503 new_chunk->id = source_chunk->id;
504 new_chunk->timestamp_creation = source_chunk->timestamp_creation;
505 new_chunk->timestamp_close = source_chunk->timestamp_close;
506 new_chunk->credentials = source_chunk->credentials;
cbf53d23 507 if (source_chunk->session_output_directory) {
28ab034a
JG
508 const bool reference_acquired =
509 lttng_directory_handle_get(source_chunk->session_output_directory);
cbf53d23 510
a0377dfe 511 LTTNG_ASSERT(reference_acquired);
28ab034a 512 new_chunk->session_output_directory = source_chunk->session_output_directory;
1a414e3a 513 }
cbf53d23 514 if (source_chunk->chunk_directory) {
28ab034a
JG
515 const bool reference_acquired =
516 lttng_directory_handle_get(source_chunk->chunk_directory);
cbf53d23 517
a0377dfe 518 LTTNG_ASSERT(reference_acquired);
cbf53d23 519 new_chunk->chunk_directory = source_chunk->chunk_directory;
1a414e3a
JG
520 }
521 new_chunk->close_command = source_chunk->close_command;
b2621f79 522 new_chunk->fd_tracker = source_chunk->fd_tracker;
1a414e3a
JG
523 pthread_mutex_unlock(&source_chunk->lock);
524end:
525 return new_chunk;
526error_unlock:
527 pthread_mutex_unlock(&source_chunk->lock);
41d2ab71 528 lttng_trace_chunk_put(new_chunk);
cd9adb8b 529 return nullptr;
1a414e3a
JG
530}
531
28ab034a
JG
532enum lttng_trace_chunk_status lttng_trace_chunk_get_id(struct lttng_trace_chunk *chunk,
533 uint64_t *id)
2c5ff4e4
JG
534{
535 enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
536
537 pthread_mutex_lock(&chunk->lock);
538 if (chunk->id.is_set) {
539 *id = chunk->id.value;
540 } else {
541 status = LTTNG_TRACE_CHUNK_STATUS_NONE;
542 }
543 pthread_mutex_unlock(&chunk->lock);
544 return status;
545}
546
28ab034a
JG
547enum lttng_trace_chunk_status
548lttng_trace_chunk_get_creation_timestamp(struct lttng_trace_chunk *chunk, time_t *creation_ts)
2c5ff4e4
JG
549
550{
551 enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
552
553 pthread_mutex_lock(&chunk->lock);
554 if (chunk->timestamp_creation.is_set) {
555 *creation_ts = chunk->timestamp_creation.value;
556 } else {
557 status = LTTNG_TRACE_CHUNK_STATUS_NONE;
558 }
559 pthread_mutex_unlock(&chunk->lock);
560 return status;
561}
562
28ab034a
JG
563enum lttng_trace_chunk_status lttng_trace_chunk_get_close_timestamp(struct lttng_trace_chunk *chunk,
564 time_t *close_ts)
2c5ff4e4
JG
565{
566 enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
567
568 pthread_mutex_lock(&chunk->lock);
569 if (chunk->timestamp_close.is_set) {
570 *close_ts = chunk->timestamp_close.value;
571 } else {
572 status = LTTNG_TRACE_CHUNK_STATUS_NONE;
573 }
574 pthread_mutex_unlock(&chunk->lock);
575 return status;
576}
577
28ab034a
JG
578enum lttng_trace_chunk_status lttng_trace_chunk_set_close_timestamp(struct lttng_trace_chunk *chunk,
579 time_t close_ts)
2c5ff4e4
JG
580{
581 enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
582
583 pthread_mutex_lock(&chunk->lock);
584 if (!chunk->timestamp_creation.is_set) {
585 ERR("Failed to set trace chunk close timestamp: creation timestamp is unset");
586 status = LTTNG_TRACE_CHUNK_STATUS_INVALID_OPERATION;
587 goto end;
588 }
4b050fdd
JR
589
590 /*
591 * Note: we do not enforce that the closing timestamp be greater or
592 * equal to the begin timestamp. These timestamps are used for
593 * generating the chunk name and should only be used in context where
594 * the monotonicity of time is not important. The source of those
595 * timestamps is NOT monotonic and represent the system calendar time,
596 * also know as the wall time.
597 */
2c5ff4e4 598 if (chunk->timestamp_creation.value > close_ts) {
4b050fdd 599 WARN("Set trace chunk close timestamp: close timestamp is before creation timestamp, begin : %ld, close : %ld",
28ab034a
JG
600 chunk->timestamp_creation.value,
601 close_ts);
2c5ff4e4 602 }
4b050fdd 603
2c5ff4e4 604 LTTNG_OPTIONAL_SET(&chunk->timestamp_close, close_ts);
ecd1a12f
MD
605 if (!chunk->name_overridden) {
606 free(chunk->name);
607 chunk->name = generate_chunk_name(LTTNG_OPTIONAL_GET(chunk->id),
28ab034a
JG
608 LTTNG_OPTIONAL_GET(chunk->timestamp_creation),
609 &close_ts);
ecd1a12f
MD
610 if (!chunk->name) {
611 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
612 }
2c5ff4e4
JG
613 }
614end:
615 pthread_mutex_unlock(&chunk->lock);
616 return status;
617}
618
28ab034a
JG
619enum lttng_trace_chunk_status lttng_trace_chunk_get_name(struct lttng_trace_chunk *chunk,
620 const char **name,
621 bool *name_overridden)
2c5ff4e4
JG
622{
623 enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
624
625 pthread_mutex_lock(&chunk->lock);
420acd90 626 if (name_overridden) {
913a542b 627 *name_overridden = chunk->name_overridden;
420acd90
JG
628 }
629 if (!chunk->name) {
2c5ff4e4
JG
630 status = LTTNG_TRACE_CHUNK_STATUS_NONE;
631 goto end;
632 }
633 *name = chunk->name;
634end:
635 pthread_mutex_unlock(&chunk->lock);
636 return status;
637}
638
0e2d816a
MD
639bool lttng_trace_chunk_get_name_overridden(struct lttng_trace_chunk *chunk)
640{
641 bool name_overridden;
642
643 pthread_mutex_lock(&chunk->lock);
644 name_overridden = chunk->name_overridden;
645 pthread_mutex_unlock(&chunk->lock);
646 return name_overridden;
647}
648
28ab034a 649static bool is_valid_chunk_name(const char *name)
84fa4db5
JG
650{
651 size_t len;
652
653 if (!name) {
654 return false;
655 }
656
f7399c50 657 len = lttng_strnlen(name, LTTNG_NAME_MAX);
84fa4db5
JG
658 if (len == 0 || len == LTTNG_NAME_MAX) {
659 return false;
660 }
661
662 if (strchr(name, '/') || strchr(name, '.')) {
663 return false;
664 }
665
666 return true;
667}
668
28ab034a
JG
669enum lttng_trace_chunk_status lttng_trace_chunk_override_name(struct lttng_trace_chunk *chunk,
670 const char *name)
2c5ff4e4
JG
671
672{
2c5ff4e4 673 enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
a7ceb342 674 char *new_name, *new_path;
2c5ff4e4 675
a7ceb342 676 DBG("Override trace chunk name from %s to %s", chunk->name, name);
84fa4db5 677 if (!is_valid_chunk_name(name)) {
28ab034a 678 ERR("Attempted to set an invalid name on a trace chunk: name = %s", name ?: "NULL");
2c5ff4e4
JG
679 status = LTTNG_TRACE_CHUNK_STATUS_INVALID_ARGUMENT;
680 goto end;
681 }
682
683 pthread_mutex_lock(&chunk->lock);
684 if (!chunk->id.is_set) {
685 ERR("Attempted to set an override name on an anonymous trace chunk: name = %s",
28ab034a 686 name);
2c5ff4e4
JG
687 status = LTTNG_TRACE_CHUNK_STATUS_INVALID_OPERATION;
688 goto end_unlock;
689 }
a7ceb342 690
2c5ff4e4
JG
691 new_name = strdup(name);
692 if (!new_name) {
693 ERR("Failed to allocate new trace chunk name");
694 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
695 goto end_unlock;
696 }
697 free(chunk->name);
698 chunk->name = new_name;
a7ceb342
MD
699
700 new_path = strdup(name);
701 if (!new_path) {
702 ERR("Failed to allocate new trace chunk path");
703 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
704 goto end_unlock;
705 }
706 free(chunk->path);
707 chunk->path = new_path;
708
913a542b 709 chunk->name_overridden = true;
a7ceb342 710end_unlock:
2c5ff4e4
JG
711 pthread_mutex_unlock(&chunk->lock);
712end:
713 return status;
714}
715
28ab034a
JG
716static enum lttng_trace_chunk_status
717lttng_trace_chunk_rename_path_no_lock(struct lttng_trace_chunk *chunk, const char *path)
a7ceb342
MD
718
719{
720 enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
cd9adb8b 721 struct lttng_directory_handle *rename_directory = nullptr;
a7ceb342
MD
722 char *new_path, *old_path;
723 int ret;
724
725 if (chunk->name_overridden) {
726 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
727 goto end;
728 }
729
730 old_path = chunk->path;
731 DBG("lttng_trace_chunk_rename_path from %s to %s", old_path, path);
732
28ab034a 733 if ((!old_path && !path) || (old_path && path && !strcmp(old_path, path))) {
a7ceb342
MD
734 goto end;
735 }
736 /*
737 * Use chunk name as path if NULL path is specified.
738 */
739 if (!path) {
740 path = chunk->name;
741 }
742
743 /* Renaming from "" to "" is not accepted. */
744 if (path[0] == '\0' && old_path[0] == '\0') {
745 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
746 goto end;
747 }
748
749 /*
750 * If a rename is performed on a chunk for which the chunk_directory
751 * is not set (yet), or the session_output_directory is not set
752 * (interacting with a relay daemon), there is no rename to perform.
753 */
28ab034a 754 if (!chunk->chunk_directory || !chunk->session_output_directory) {
a7ceb342
MD
755 goto skip_move;
756 }
757
83fa31bf 758 if (old_path && old_path[0] != '\0' && path[0] != '\0') {
a7ceb342
MD
759 /* Rename chunk directory. */
760 ret = lttng_directory_handle_rename_as_user(
761 chunk->session_output_directory,
762 old_path,
763 chunk->session_output_directory,
764 path,
765 LTTNG_OPTIONAL_GET(chunk->credentials).use_current_user ?
cd9adb8b 766 nullptr :
a7ceb342
MD
767 &chunk->credentials.value.user);
768 if (ret) {
769 PERROR("Failed to move trace chunk directory \"%s\" to \"%s\"",
28ab034a
JG
770 old_path,
771 path);
a7ceb342
MD
772 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
773 goto end;
774 }
dd95933f 775 rename_directory = chunk->fd_tracker ?
28ab034a
JG
776 fd_tracker_create_directory_handle_from_handle(
777 chunk->fd_tracker, chunk->session_output_directory, path) :
778 lttng_directory_handle_create_from_handle(path,
779 chunk->session_output_directory);
a7ceb342
MD
780 if (!rename_directory) {
781 ERR("Failed to get handle to trace chunk rename directory");
782 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
783 goto end;
784 }
785
786 /* Release old handle. */
787 lttng_directory_handle_put(chunk->chunk_directory);
788 /*
789 * Transfer new handle reference to chunk as the current chunk
790 * handle.
791 */
792 chunk->chunk_directory = rename_directory;
cd9adb8b 793 rename_directory = nullptr;
83fa31bf 794 } else if (old_path && old_path[0] == '\0') {
28ab034a
JG
795 size_t i,
796 count = lttng_dynamic_pointer_array_get_count(
a7ceb342
MD
797 &chunk->top_level_directories);
798
799 ret = lttng_directory_handle_create_subdirectory_as_user(
28ab034a
JG
800 chunk->session_output_directory,
801 path,
802 DIR_CREATION_MODE,
803 LTTNG_OPTIONAL_GET(chunk->credentials).use_current_user ?
cd9adb8b 804 nullptr :
28ab034a 805 &chunk->credentials.value.user);
a7ceb342 806 if (ret) {
28ab034a 807 PERROR("Failed to create trace chunk rename directory \"%s\"", path);
a7ceb342
MD
808 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
809 goto end;
810 }
811
812 rename_directory = lttng_directory_handle_create_from_handle(
28ab034a 813 path, chunk->session_output_directory);
a7ceb342
MD
814 if (!rename_directory) {
815 ERR("Failed to get handle to trace chunk rename directory");
816 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
817 goto end;
818 }
819
820 /* Move toplevel directories. */
821 for (i = 0; i < count; i++) {
822 const char *top_level_name =
a6bc4ca9 823 (const char *) lttng_dynamic_pointer_array_get_pointer(
a7ceb342
MD
824 &chunk->top_level_directories, i);
825
826 ret = lttng_directory_handle_rename_as_user(
28ab034a
JG
827 chunk->chunk_directory,
828 top_level_name,
829 rename_directory,
830 top_level_name,
831 LTTNG_OPTIONAL_GET(chunk->credentials).use_current_user ?
cd9adb8b 832 nullptr :
28ab034a 833 &chunk->credentials.value.user);
a7ceb342
MD
834 if (ret) {
835 PERROR("Failed to move \"%s\" to trace chunk rename directory",
28ab034a 836 top_level_name);
a7ceb342
MD
837 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
838 goto end;
839 }
840 }
841 /* Release old handle. */
842 lttng_directory_handle_put(chunk->chunk_directory);
843 /*
844 * Transfer new handle reference to chunk as the current chunk
845 * handle.
846 */
847 chunk->chunk_directory = rename_directory;
cd9adb8b 848 rename_directory = nullptr;
a3a75bf4 849 } else if (old_path) {
28ab034a
JG
850 size_t i,
851 count = lttng_dynamic_pointer_array_get_count(
a7ceb342 852 &chunk->top_level_directories);
28ab034a
JG
853 const bool reference_acquired =
854 lttng_directory_handle_get(chunk->session_output_directory);
a7ceb342 855
a0377dfe 856 LTTNG_ASSERT(reference_acquired);
a7ceb342
MD
857 rename_directory = chunk->session_output_directory;
858
859 /* Move toplevel directories. */
860 for (i = 0; i < count; i++) {
861 const char *top_level_name =
a6bc4ca9 862 (const char *) lttng_dynamic_pointer_array_get_pointer(
a7ceb342
MD
863 &chunk->top_level_directories, i);
864
865 ret = lttng_directory_handle_rename_as_user(
28ab034a
JG
866 chunk->chunk_directory,
867 top_level_name,
868 rename_directory,
869 top_level_name,
870 LTTNG_OPTIONAL_GET(chunk->credentials).use_current_user ?
cd9adb8b 871 nullptr :
28ab034a 872 &chunk->credentials.value.user);
a7ceb342
MD
873 if (ret) {
874 PERROR("Failed to move \"%s\" to trace chunk rename directory",
28ab034a 875 top_level_name);
a7ceb342
MD
876 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
877 goto end;
878 }
879 }
880 /* Release old handle. */
881 lttng_directory_handle_put(chunk->chunk_directory);
882 /*
883 * Transfer new handle reference to chunk as the current chunk
884 * handle.
885 */
886 chunk->chunk_directory = rename_directory;
cd9adb8b 887 rename_directory = nullptr;
a7ceb342
MD
888
889 /* Remove old directory. */
a6bc4ca9 890 status = (lttng_trace_chunk_status) lttng_directory_handle_remove_subdirectory(
28ab034a 891 chunk->session_output_directory, old_path);
a7ceb342 892 if (status != LTTNG_TRACE_CHUNK_STATUS_OK) {
28ab034a 893 ERR("Error removing subdirectory '%s' file when deleting chunk", old_path);
a7ceb342
MD
894 goto end;
895 }
a3a75bf4
JG
896 } else {
897 /* Unexpected !old_path && !path. */
898 status = LTTNG_TRACE_CHUNK_STATUS_INVALID_ARGUMENT;
899 goto end;
a7ceb342
MD
900 }
901
902skip_move:
f3ce6f5d
JG
903 new_path = strdup(path);
904 if (!new_path) {
905 ERR("Failed to allocate new trace chunk path");
906 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
907 goto end;
a7ceb342
MD
908 }
909 free(chunk->path);
910 chunk->path = new_path;
911end:
912 lttng_directory_handle_put(rename_directory);
913 return status;
914}
915
28ab034a
JG
916enum lttng_trace_chunk_status lttng_trace_chunk_rename_path(struct lttng_trace_chunk *chunk,
917 const char *path)
a7ceb342
MD
918
919{
920 enum lttng_trace_chunk_status status;
921
922 pthread_mutex_lock(&chunk->lock);
923 status = lttng_trace_chunk_rename_path_no_lock(chunk, path);
924 pthread_mutex_unlock(&chunk->lock);
925
926 return status;
927}
928
28ab034a
JG
929enum lttng_trace_chunk_status
930lttng_trace_chunk_get_credentials(struct lttng_trace_chunk *chunk,
931 struct lttng_credentials *credentials)
2c5ff4e4
JG
932{
933 enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
934
935 pthread_mutex_lock(&chunk->lock);
936 if (chunk->credentials.is_set) {
937 if (chunk->credentials.value.use_current_user) {
ff588497
JR
938 LTTNG_OPTIONAL_SET(&credentials->uid, geteuid());
939 LTTNG_OPTIONAL_SET(&credentials->gid, getegid());
2c5ff4e4
JG
940 } else {
941 *credentials = chunk->credentials.value.user;
942 }
943 } else {
944 status = LTTNG_TRACE_CHUNK_STATUS_NONE;
945 }
946 pthread_mutex_unlock(&chunk->lock);
947 return status;
948}
949
28ab034a
JG
950enum lttng_trace_chunk_status
951lttng_trace_chunk_set_credentials(struct lttng_trace_chunk *chunk,
952 const struct lttng_credentials *user_credentials)
2c5ff4e4
JG
953{
954 enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
955 const struct chunk_credentials credentials = {
2c5ff4e4 956 .use_current_user = false,
a6bc4ca9 957 .user = *user_credentials,
2c5ff4e4
JG
958 };
959
960 pthread_mutex_lock(&chunk->lock);
961 if (chunk->credentials.is_set) {
962 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
963 goto end;
964 }
965 LTTNG_OPTIONAL_SET(&chunk->credentials, credentials);
966end:
967 pthread_mutex_unlock(&chunk->lock);
968 return status;
969}
970
28ab034a
JG
971enum lttng_trace_chunk_status
972lttng_trace_chunk_set_credentials_current_user(struct lttng_trace_chunk *chunk)
2c5ff4e4
JG
973{
974 enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
975 const struct chunk_credentials credentials = {
976 .use_current_user = true,
1c9a0b0e 977 .user = {},
2c5ff4e4
JG
978 };
979
980 pthread_mutex_lock(&chunk->lock);
981 if (chunk->credentials.is_set) {
982 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
983 goto end;
984 }
985 LTTNG_OPTIONAL_SET(&chunk->credentials, credentials);
986end:
987 pthread_mutex_unlock(&chunk->lock);
988 return status;
989}
990
28ab034a
JG
991enum lttng_trace_chunk_status
992lttng_trace_chunk_set_as_owner(struct lttng_trace_chunk *chunk,
993 struct lttng_directory_handle *session_output_directory)
2c5ff4e4
JG
994{
995 int ret;
996 enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
cd9adb8b 997 struct lttng_directory_handle *chunk_directory_handle = nullptr;
cbf53d23 998 bool reference_acquired;
2c5ff4e4
JG
999
1000 pthread_mutex_lock(&chunk->lock);
1001 if (chunk->mode.is_set) {
1002 status = LTTNG_TRACE_CHUNK_STATUS_INVALID_OPERATION;
1003 goto end;
1004 }
1005 if (!chunk->credentials.is_set) {
1006 /*
1007 * Fatal error, credentials must be set before a
1008 * directory is created.
1009 */
1010 ERR("Credentials of trace chunk are unset: refusing to set session output directory");
1011 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
1012 goto end;
1013 }
d7a20fcf 1014 if (chunk->path && chunk->path[0] != '\0') {
2c5ff4e4 1015 ret = lttng_directory_handle_create_subdirectory_as_user(
28ab034a
JG
1016 session_output_directory,
1017 chunk->path,
1018 DIR_CREATION_MODE,
1019 !chunk->credentials.value.use_current_user ?
1020 &chunk->credentials.value.user :
cd9adb8b 1021 nullptr);
2c5ff4e4 1022 if (ret) {
28ab034a 1023 PERROR("Failed to create chunk output directory \"%s\"", chunk->path);
2c5ff4e4
JG
1024 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
1025 goto end;
1026 }
28ab034a
JG
1027 chunk_directory_handle = chunk->fd_tracker ?
1028 fd_tracker_create_directory_handle_from_handle(
1029 chunk->fd_tracker, session_output_directory, chunk->path) :
1030 lttng_directory_handle_create_from_handle(chunk->path,
1031 session_output_directory);
a7ceb342
MD
1032 if (!chunk_directory_handle) {
1033 /* The function already logs on all error paths. */
1034 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
1035 goto end;
1036 }
1037 } else {
1038 /*
1039 * A nameless chunk does not need its own output directory.
1040 * The session's output directory will be used.
1041 */
28ab034a 1042 reference_acquired = lttng_directory_handle_get(session_output_directory);
a7ceb342 1043
a0377dfe 1044 LTTNG_ASSERT(reference_acquired);
a7ceb342 1045 chunk_directory_handle = session_output_directory;
2c5ff4e4 1046 }
cbf53d23 1047 chunk->chunk_directory = chunk_directory_handle;
cd9adb8b 1048 chunk_directory_handle = nullptr;
28ab034a 1049 reference_acquired = lttng_directory_handle_get(session_output_directory);
a0377dfe 1050 LTTNG_ASSERT(reference_acquired);
cbf53d23 1051 chunk->session_output_directory = session_output_directory;
2c5ff4e4
JG
1052 LTTNG_OPTIONAL_SET(&chunk->mode, TRACE_CHUNK_MODE_OWNER);
1053end:
1054 pthread_mutex_unlock(&chunk->lock);
1055 return status;
1056}
1057
28ab034a
JG
1058enum lttng_trace_chunk_status
1059lttng_trace_chunk_set_as_user(struct lttng_trace_chunk *chunk,
1060 struct lttng_directory_handle *chunk_directory)
2c5ff4e4
JG
1061{
1062 enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
cbf53d23 1063 bool reference_acquired;
2c5ff4e4
JG
1064
1065 pthread_mutex_lock(&chunk->lock);
1066 if (chunk->mode.is_set) {
1067 status = LTTNG_TRACE_CHUNK_STATUS_INVALID_OPERATION;
1068 goto end;
1069 }
1070 if (!chunk->credentials.is_set) {
1071 ERR("Credentials of trace chunk are unset: refusing to set chunk output directory");
1072 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
1073 goto end;
1074 }
cbf53d23 1075 reference_acquired = lttng_directory_handle_get(chunk_directory);
a0377dfe 1076 LTTNG_ASSERT(reference_acquired);
cbf53d23 1077 chunk->chunk_directory = chunk_directory;
2c5ff4e4
JG
1078 LTTNG_OPTIONAL_SET(&chunk->mode, TRACE_CHUNK_MODE_USER);
1079end:
1080 pthread_mutex_unlock(&chunk->lock);
1081 return status;
1082}
1083
7ceefac4 1084enum lttng_trace_chunk_status
28ab034a
JG
1085lttng_trace_chunk_get_session_output_directory_handle(struct lttng_trace_chunk *chunk,
1086 struct lttng_directory_handle **handle)
7ceefac4
JG
1087{
1088 enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
1089
1090 pthread_mutex_lock(&chunk->lock);
1091 if (!chunk->session_output_directory) {
1092 status = LTTNG_TRACE_CHUNK_STATUS_NONE;
cd9adb8b 1093 *handle = nullptr;
7ceefac4
JG
1094 goto end;
1095 } else {
28ab034a
JG
1096 const bool reference_acquired =
1097 lttng_directory_handle_get(chunk->session_output_directory);
7ceefac4 1098
a0377dfe 1099 LTTNG_ASSERT(reference_acquired);
7ceefac4
JG
1100 *handle = chunk->session_output_directory;
1101 }
1102end:
1103 pthread_mutex_unlock(&chunk->lock);
1104 return status;
1105}
1106
28ab034a
JG
1107enum lttng_trace_chunk_status
1108lttng_trace_chunk_borrow_chunk_directory_handle(struct lttng_trace_chunk *chunk,
1109 const struct lttng_directory_handle **handle)
2c5ff4e4
JG
1110{
1111 enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
1112
1113 pthread_mutex_lock(&chunk->lock);
cbf53d23 1114 if (!chunk->chunk_directory) {
2c5ff4e4
JG
1115 status = LTTNG_TRACE_CHUNK_STATUS_NONE;
1116 goto end;
1117 }
1118
cbf53d23 1119 *handle = chunk->chunk_directory;
2c5ff4e4
JG
1120end:
1121 pthread_mutex_unlock(&chunk->lock);
1122 return status;
1123}
1124
1125/* Add a top-level directory to the trace chunk if it was previously unknown. */
28ab034a 1126static int add_top_level_directory_unique(struct lttng_trace_chunk *chunk, const char *new_path)
2c5ff4e4
JG
1127{
1128 int ret = 0;
1129 bool found = false;
28ab034a 1130 size_t i, count = lttng_dynamic_pointer_array_get_count(&chunk->top_level_directories);
2c5ff4e4 1131 const char *new_path_separator_pos = strchr(new_path, '/');
28ab034a
JG
1132 const ptrdiff_t new_path_top_level_len =
1133 new_path_separator_pos ? new_path_separator_pos - new_path : strlen(new_path);
2c5ff4e4
JG
1134
1135 for (i = 0; i < count; i++) {
a6bc4ca9 1136 const char *path = (const char *) lttng_dynamic_pointer_array_get_pointer(
28ab034a 1137 &chunk->top_level_directories, i);
2c5ff4e4
JG
1138 const ptrdiff_t path_top_level_len = strlen(path);
1139
1140 if (path_top_level_len != new_path_top_level_len) {
1141 continue;
1142 }
1143 if (!strncmp(path, new_path, path_top_level_len)) {
1144 found = true;
1145 break;
1146 }
1147 }
1148
1149 if (!found) {
c36a763b 1150 char *copy = lttng_strndup(new_path, new_path_top_level_len);
2c5ff4e4
JG
1151
1152 DBG("Adding new top-level directory \"%s\" to trace chunk \"%s\"",
28ab034a
JG
1153 new_path,
1154 chunk->name ?: "(unnamed)");
2c5ff4e4
JG
1155 if (!copy) {
1156 PERROR("Failed to copy path");
1157 ret = -1;
1158 goto end;
1159 }
28ab034a 1160 ret = lttng_dynamic_pointer_array_add_pointer(&chunk->top_level_directories, copy);
2c5ff4e4
JG
1161 if (ret) {
1162 ERR("Allocation failure while adding top-level directory entry to a trace chunk");
1163 free(copy);
1164 goto end;
1165 }
1166 }
1167end:
1168 return ret;
1169}
1170
28ab034a
JG
1171enum lttng_trace_chunk_status lttng_trace_chunk_create_subdirectory(struct lttng_trace_chunk *chunk,
1172 const char *path)
2c5ff4e4
JG
1173{
1174 int ret;
1175 enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
1176
1177 DBG("Creating trace chunk subdirectory \"%s\"", path);
1178 pthread_mutex_lock(&chunk->lock);
1179 if (!chunk->credentials.is_set) {
1180 /*
1181 * Fatal error, credentials must be set before a
1182 * directory is created.
1183 */
1184 ERR("Credentials of trace chunk are unset: refusing to create subdirectory \"%s\"",
28ab034a 1185 path);
2c5ff4e4
JG
1186 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
1187 goto end;
1188 }
28ab034a 1189 if (!chunk->mode.is_set || chunk->mode.value != TRACE_CHUNK_MODE_OWNER) {
2c5ff4e4 1190 ERR("Attempted to create trace chunk subdirectory \"%s\" through a non-owner chunk",
28ab034a 1191 path);
2c5ff4e4
JG
1192 status = LTTNG_TRACE_CHUNK_STATUS_INVALID_OPERATION;
1193 goto end;
1194 }
cbf53d23 1195 if (!chunk->chunk_directory) {
2c5ff4e4 1196 ERR("Attempted to create trace chunk subdirectory \"%s\" before setting the chunk output directory",
28ab034a 1197 path);
2c5ff4e4
JG
1198 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
1199 goto end;
1200 }
1201 if (*path == '/') {
28ab034a 1202 ERR("Refusing to create absolute trace chunk directory \"%s\"", path);
2c5ff4e4
JG
1203 status = LTTNG_TRACE_CHUNK_STATUS_INVALID_ARGUMENT;
1204 goto end;
1205 }
1206 ret = lttng_directory_handle_create_subdirectory_recursive_as_user(
28ab034a
JG
1207 chunk->chunk_directory,
1208 path,
1209 DIR_CREATION_MODE,
cd9adb8b
JG
1210 chunk->credentials.value.use_current_user ? nullptr :
1211 &chunk->credentials.value.user);
2c5ff4e4 1212 if (ret) {
28ab034a 1213 PERROR("Failed to create trace chunk subdirectory \"%s\"", path);
2c5ff4e4
JG
1214 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
1215 goto end;
1216 }
1217 ret = add_top_level_directory_unique(chunk, path);
1218 if (ret) {
1219 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
1220 goto end;
1221 }
1222end:
1223 pthread_mutex_unlock(&chunk->lock);
1224 return status;
1225}
1226
6cb32e5a
MD
1227/*
1228 * TODO: Implement O(1) lookup.
1229 */
28ab034a
JG
1230static bool
1231lttng_trace_chunk_find_file(struct lttng_trace_chunk *chunk, const char *path, size_t *index)
6cb32e5a
MD
1232{
1233 size_t i, count;
1234
1235 count = lttng_dynamic_pointer_array_get_count(&chunk->files);
1236 for (i = 0; i < count; i++) {
1237 const char *iter_path =
28ab034a 1238 (const char *) lttng_dynamic_pointer_array_get_pointer(&chunk->files, i);
6cb32e5a
MD
1239 if (!strcmp(iter_path, path)) {
1240 if (index) {
1241 *index = i;
1242 }
1243 return true;
1244 }
1245 }
1246 return false;
1247}
1248
28ab034a
JG
1249static enum lttng_trace_chunk_status lttng_trace_chunk_add_file(struct lttng_trace_chunk *chunk,
1250 const char *path)
6cb32e5a
MD
1251{
1252 char *copy;
1253 int ret;
1254 enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
1255
cd9adb8b 1256 if (lttng_trace_chunk_find_file(chunk, path, nullptr)) {
6cb32e5a
MD
1257 return LTTNG_TRACE_CHUNK_STATUS_OK;
1258 }
28ab034a 1259 DBG("Adding new file \"%s\" to trace chunk \"%s\"", path, chunk->name ?: "(unnamed)");
6cb32e5a
MD
1260 copy = strdup(path);
1261 if (!copy) {
1262 PERROR("Failed to copy path");
1263 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
1264 goto end;
1265 }
28ab034a 1266 ret = lttng_dynamic_pointer_array_add_pointer(&chunk->files, copy);
6cb32e5a
MD
1267 if (ret) {
1268 ERR("Allocation failure while adding file to a trace chunk");
1269 free(copy);
1270 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
1271 goto end;
1272 }
1273end:
1274 return status;
1275}
1276
28ab034a 1277static void lttng_trace_chunk_remove_file(struct lttng_trace_chunk *chunk, const char *path)
6cb32e5a
MD
1278{
1279 size_t index;
1280 bool found;
1281 int ret;
1282
1283 found = lttng_trace_chunk_find_file(chunk, path, &index);
1284 if (!found) {
1285 return;
1286 }
28ab034a 1287 ret = lttng_dynamic_pointer_array_remove_pointer(&chunk->files, index);
a0377dfe 1288 LTTNG_ASSERT(!ret);
6cb32e5a
MD
1289}
1290
28ab034a
JG
1291static enum lttng_trace_chunk_status
1292_lttng_trace_chunk_open_fs_handle_locked(struct lttng_trace_chunk *chunk,
1293 const char *file_path,
1294 int flags,
1295 mode_t mode,
1296 struct fs_handle **out_handle,
1297 bool expect_no_file)
2c5ff4e4
JG
1298{
1299 int ret;
1300 enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
1301
1302 DBG("Opening trace chunk file \"%s\"", file_path);
2c5ff4e4
JG
1303 if (!chunk->credentials.is_set) {
1304 /*
1305 * Fatal error, credentials must be set before a
1306 * file is created.
1307 */
1308 ERR("Credentials of trace chunk are unset: refusing to open file \"%s\"",
28ab034a 1309 file_path);
2c5ff4e4
JG
1310 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
1311 goto end;
1312 }
cbf53d23 1313 if (!chunk->chunk_directory) {
2c5ff4e4 1314 ERR("Attempted to open trace chunk file \"%s\" before setting the chunk output directory",
28ab034a 1315 file_path);
2c5ff4e4
JG
1316 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
1317 goto end;
1318 }
6cb32e5a
MD
1319 status = lttng_trace_chunk_add_file(chunk, file_path);
1320 if (status != LTTNG_TRACE_CHUNK_STATUS_OK) {
1321 goto end;
1322 }
8bb66c3c 1323 if (chunk->fd_tracker) {
a0377dfe 1324 LTTNG_ASSERT(chunk->credentials.value.use_current_user);
28ab034a
JG
1325 *out_handle = fd_tracker_open_fs_handle(
1326 chunk->fd_tracker, chunk->chunk_directory, file_path, flags, &mode);
8bb66c3c
JG
1327 ret = *out_handle ? 0 : -1;
1328 } else {
1329 ret = lttng_directory_handle_open_file_as_user(
28ab034a
JG
1330 chunk->chunk_directory,
1331 file_path,
1332 flags,
1333 mode,
cd9adb8b 1334 chunk->credentials.value.use_current_user ? nullptr :
28ab034a 1335 &chunk->credentials.value.user);
8bb66c3c 1336 if (ret >= 0) {
28ab034a
JG
1337 *out_handle =
1338 fs_handle_untracked_create(chunk->chunk_directory, file_path, ret);
8bb66c3c
JG
1339 if (!*out_handle) {
1340 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
1341 goto end;
1342 }
1343 }
1344 }
2c5ff4e4 1345 if (ret < 0) {
3ff5c5db
MD
1346 if (errno == ENOENT && expect_no_file) {
1347 status = LTTNG_TRACE_CHUNK_STATUS_NO_FILE;
1348 } else {
1349 PERROR("Failed to open file relative to trace chunk file_path = \"%s\", flags = %d, mode = %d",
28ab034a
JG
1350 file_path,
1351 flags,
1352 (int) mode);
3ff5c5db
MD
1353 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
1354 }
6cb32e5a 1355 lttng_trace_chunk_remove_file(chunk, file_path);
2c5ff4e4
JG
1356 goto end;
1357 }
2c5ff4e4 1358end:
8bb66c3c
JG
1359 return status;
1360}
1361
28ab034a
JG
1362enum lttng_trace_chunk_status lttng_trace_chunk_open_fs_handle(struct lttng_trace_chunk *chunk,
1363 const char *file_path,
1364 int flags,
1365 mode_t mode,
1366 struct fs_handle **out_handle,
1367 bool expect_no_file)
8bb66c3c
JG
1368{
1369 enum lttng_trace_chunk_status status;
1370
1371 pthread_mutex_lock(&chunk->lock);
28ab034a
JG
1372 status = _lttng_trace_chunk_open_fs_handle_locked(
1373 chunk, file_path, flags, mode, out_handle, expect_no_file);
8bb66c3c
JG
1374 pthread_mutex_unlock(&chunk->lock);
1375 return status;
1376}
1377
28ab034a
JG
1378enum lttng_trace_chunk_status lttng_trace_chunk_open_file(struct lttng_trace_chunk *chunk,
1379 const char *file_path,
1380 int flags,
1381 mode_t mode,
1382 int *out_fd,
1383 bool expect_no_file)
8bb66c3c
JG
1384{
1385 enum lttng_trace_chunk_status status;
1386 struct fs_handle *fs_handle;
1387
1388 pthread_mutex_lock(&chunk->lock);
1389 /*
1390 * Using this method is never valid when an fd_tracker is being
1391 * used since the resulting file descriptor would not be tracked.
1392 */
a0377dfe 1393 LTTNG_ASSERT(!chunk->fd_tracker);
28ab034a
JG
1394 status = _lttng_trace_chunk_open_fs_handle_locked(
1395 chunk, file_path, flags, mode, &fs_handle, expect_no_file);
2c5ff4e4 1396 pthread_mutex_unlock(&chunk->lock);
8bb66c3c
JG
1397
1398 if (status == LTTNG_TRACE_CHUNK_STATUS_OK) {
1399 *out_fd = fs_handle_get_fd(fs_handle);
1400 /*
1401 * Does not close the fd; we just "unbox" it from the fs_handle.
1402 */
28ab034a
JG
1403 fs_handle_untracked_destroy(
1404 lttng::utils::container_of(fs_handle, &fs_handle_untracked::parent));
8bb66c3c
JG
1405 }
1406
2c5ff4e4
JG
1407 return status;
1408}
1409
28ab034a 1410int lttng_trace_chunk_unlink_file(struct lttng_trace_chunk *chunk, const char *file_path)
2c5ff4e4
JG
1411{
1412 int ret;
1413 enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
1414
1415 DBG("Unlinking trace chunk file \"%s\"", file_path);
1416 pthread_mutex_lock(&chunk->lock);
1417 if (!chunk->credentials.is_set) {
1418 /*
1419 * Fatal error, credentials must be set before a
a7ceb342 1420 * file is unlinked.
2c5ff4e4
JG
1421 */
1422 ERR("Credentials of trace chunk are unset: refusing to unlink file \"%s\"",
28ab034a 1423 file_path);
2c5ff4e4
JG
1424 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
1425 goto end;
1426 }
cbf53d23 1427 if (!chunk->chunk_directory) {
2c5ff4e4 1428 ERR("Attempted to unlink trace chunk file \"%s\" before setting the chunk output directory",
28ab034a 1429 file_path);
2c5ff4e4
JG
1430 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
1431 goto end;
1432 }
cd9adb8b
JG
1433 ret = lttng_directory_handle_unlink_file_as_user(chunk->chunk_directory,
1434 file_path,
1435 chunk->credentials.value.use_current_user ?
1436 nullptr :
1437 &chunk->credentials.value.user);
2c5ff4e4
JG
1438 if (ret < 0) {
1439 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
1440 goto end;
1441 }
6cb32e5a 1442 lttng_trace_chunk_remove_file(chunk, file_path);
2c5ff4e4
JG
1443end:
1444 pthread_mutex_unlock(&chunk->lock);
1445 return status;
1446}
1447
28ab034a
JG
1448static int lttng_trace_chunk_remove_subdirectory_recursive(struct lttng_trace_chunk *chunk,
1449 const char *path)
2c5ff4e4
JG
1450{
1451 int ret;
a7ceb342
MD
1452 enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
1453
1454 DBG("Recursively removing trace chunk directory \"%s\"", path);
1455 pthread_mutex_lock(&chunk->lock);
1456 if (!chunk->credentials.is_set) {
1457 /*
1458 * Fatal error, credentials must be set before a
1459 * directory is removed.
1460 */
1461 ERR("Credentials of trace chunk are unset: refusing to recursively remove directory \"%s\"",
28ab034a 1462 path);
a7ceb342
MD
1463 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
1464 goto end;
1465 }
1466 if (!chunk->chunk_directory) {
1467 ERR("Attempted to recursively remove trace chunk directory \"%s\" before setting the chunk output directory",
28ab034a 1468 path);
a7ceb342
MD
1469 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
1470 goto end;
1471 }
1472 ret = lttng_directory_handle_remove_subdirectory_recursive_as_user(
28ab034a
JG
1473 chunk->chunk_directory,
1474 path,
cd9adb8b
JG
1475 chunk->credentials.value.use_current_user ? nullptr :
1476 &chunk->credentials.value.user,
28ab034a 1477 LTTNG_DIRECTORY_HANDLE_SKIP_NON_EMPTY_FLAG);
a7ceb342
MD
1478 if (ret < 0) {
1479 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
1480 goto end;
1481 }
1482end:
1483 pthread_mutex_unlock(&chunk->lock);
1484 return status;
1485}
1486
28ab034a 1487static int lttng_trace_chunk_move_to_completed_post_release(struct lttng_trace_chunk *trace_chunk)
a7ceb342
MD
1488{
1489 int ret = 0;
cd9adb8b 1490 char *archived_chunk_name = nullptr;
2c5ff4e4 1491 const uint64_t chunk_id = LTTNG_OPTIONAL_GET(trace_chunk->id);
28ab034a
JG
1492 const time_t creation_timestamp = LTTNG_OPTIONAL_GET(trace_chunk->timestamp_creation);
1493 const time_t close_timestamp = LTTNG_OPTIONAL_GET(trace_chunk->timestamp_close);
cd9adb8b 1494 struct lttng_directory_handle *archived_chunks_directory = nullptr;
a7ceb342 1495 enum lttng_trace_chunk_status status;
2c5ff4e4 1496
28ab034a
JG
1497 if (!trace_chunk->mode.is_set || trace_chunk->mode.value != TRACE_CHUNK_MODE_OWNER ||
1498 !trace_chunk->session_output_directory) {
bbc4768c
JG
1499 /*
1500 * This command doesn't need to run if the output is remote
1501 * or if the trace chunk is not owned by this process.
1502 */
1503 goto end;
1504 }
1505
a0377dfe
FD
1506 LTTNG_ASSERT(trace_chunk->mode.value == TRACE_CHUNK_MODE_OWNER);
1507 LTTNG_ASSERT(!trace_chunk->name_overridden);
1508 LTTNG_ASSERT(trace_chunk->path);
2c5ff4e4 1509
28ab034a 1510 archived_chunk_name = generate_chunk_name(chunk_id, creation_timestamp, &close_timestamp);
2c5ff4e4
JG
1511 if (!archived_chunk_name) {
1512 ERR("Failed to generate archived trace chunk name while renaming trace chunk");
a7ceb342 1513 ret = -1;
2c5ff4e4
JG
1514 goto end;
1515 }
1516
1517 ret = lttng_directory_handle_create_subdirectory_as_user(
28ab034a
JG
1518 trace_chunk->session_output_directory,
1519 DEFAULT_ARCHIVED_TRACE_CHUNKS_DIRECTORY,
1520 DIR_CREATION_MODE,
1521 !trace_chunk->credentials.value.use_current_user ?
1522 &trace_chunk->credentials.value.user :
cd9adb8b 1523 nullptr);
2c5ff4e4
JG
1524 if (ret) {
1525 PERROR("Failed to create \"" DEFAULT_ARCHIVED_TRACE_CHUNKS_DIRECTORY
28ab034a 1526 "\" directory for archived trace chunks");
2c5ff4e4
JG
1527 goto end;
1528 }
1529
dd95933f 1530 archived_chunks_directory = trace_chunk->fd_tracker ?
28ab034a
JG
1531 fd_tracker_create_directory_handle_from_handle(
1532 trace_chunk->fd_tracker,
1533 trace_chunk->session_output_directory,
1534 DEFAULT_ARCHIVED_TRACE_CHUNKS_DIRECTORY) :
1535 lttng_directory_handle_create_from_handle(DEFAULT_ARCHIVED_TRACE_CHUNKS_DIRECTORY,
1536 trace_chunk->session_output_directory);
cbf53d23 1537 if (!archived_chunks_directory) {
2c5ff4e4 1538 PERROR("Failed to get handle to archived trace chunks directory");
a7ceb342 1539 ret = -1;
2c5ff4e4
JG
1540 goto end;
1541 }
2c5ff4e4 1542
a7ceb342
MD
1543 /*
1544 * Make sure chunk is renamed to old directory if not already done by
1545 * the creation of the next chunk. This happens if a rotation is
1546 * performed while tracing is stopped.
1547 */
28ab034a 1548 if (!trace_chunk->path || strcmp(trace_chunk->path, DEFAULT_CHUNK_TMP_OLD_DIRECTORY)) {
a7ceb342 1549 status = lttng_trace_chunk_rename_path_no_lock(trace_chunk,
28ab034a 1550 DEFAULT_CHUNK_TMP_OLD_DIRECTORY);
a7ceb342
MD
1551 if (status != LTTNG_TRACE_CHUNK_STATUS_OK) {
1552 ERR("Failed to rename chunk to %s", DEFAULT_CHUNK_TMP_OLD_DIRECTORY);
1553 ret = -1;
1554 goto end;
1555 }
1556 }
1557
9de831f8 1558 ret = lttng_directory_handle_rename_as_user(
28ab034a
JG
1559 trace_chunk->session_output_directory,
1560 trace_chunk->path,
1561 archived_chunks_directory,
1562 archived_chunk_name,
1563 LTTNG_OPTIONAL_GET(trace_chunk->credentials).use_current_user ?
cd9adb8b 1564 nullptr :
28ab034a 1565 &trace_chunk->credentials.value.user);
2c5ff4e4
JG
1566 if (ret) {
1567 PERROR("Failed to rename folder \"%s\" to \"%s\"",
28ab034a
JG
1568 trace_chunk->path,
1569 archived_chunk_name);
2c5ff4e4
JG
1570 }
1571
1572end:
cbf53d23 1573 lttng_directory_handle_put(archived_chunks_directory);
2c5ff4e4 1574 free(archived_chunk_name);
a7ceb342 1575 return ret;
2c5ff4e4
JG
1576}
1577
28ab034a
JG
1578static int lttng_trace_chunk_no_operation(struct lttng_trace_chunk *trace_chunk
1579 __attribute__((unused)))
8ced4811
MD
1580{
1581 return 0;
1582}
1583
28ab034a 1584static int lttng_trace_chunk_delete_post_release_user(struct lttng_trace_chunk *trace_chunk)
8ced4811
MD
1585{
1586 int ret = 0;
1587
1588 DBG("Trace chunk \"delete\" close command post-release (User)");
1589
1590 /* Unlink all files. */
1591 while (lttng_dynamic_pointer_array_get_count(&trace_chunk->files) != 0) {
1592 enum lttng_trace_chunk_status status;
1593 const char *path;
1594
1595 /* Remove first. */
28ab034a
JG
1596 path = (const char *) lttng_dynamic_pointer_array_get_pointer(&trace_chunk->files,
1597 0);
8ced4811 1598 DBG("Unlink file: %s", path);
28ab034a
JG
1599 status =
1600 (lttng_trace_chunk_status) lttng_trace_chunk_unlink_file(trace_chunk, path);
8ced4811
MD
1601 if (status != LTTNG_TRACE_CHUNK_STATUS_OK) {
1602 ERR("Error unlinking file '%s' when deleting chunk", path);
1603 ret = -1;
1604 goto end;
1605 }
1606 }
1607end:
1608 return ret;
1609}
1610
28ab034a 1611static int lttng_trace_chunk_delete_post_release_owner(struct lttng_trace_chunk *trace_chunk)
8ced4811
MD
1612{
1613 enum lttng_trace_chunk_status status;
1614 size_t i, count;
1615 int ret = 0;
1616
1617 ret = lttng_trace_chunk_delete_post_release_user(trace_chunk);
1618 if (ret) {
1619 goto end;
1620 }
1621
1622 DBG("Trace chunk \"delete\" close command post-release (Owner)");
1623
a0377dfe
FD
1624 LTTNG_ASSERT(trace_chunk->session_output_directory);
1625 LTTNG_ASSERT(trace_chunk->chunk_directory);
8ced4811
MD
1626
1627 /* Remove empty directories. */
28ab034a 1628 count = lttng_dynamic_pointer_array_get_count(&trace_chunk->top_level_directories);
8ced4811
MD
1629
1630 for (i = 0; i < count; i++) {
28ab034a
JG
1631 const char *top_level_name = (const char *) lttng_dynamic_pointer_array_get_pointer(
1632 &trace_chunk->top_level_directories, i);
8ced4811 1633
28ab034a
JG
1634 status = (lttng_trace_chunk_status) lttng_trace_chunk_remove_subdirectory_recursive(
1635 trace_chunk, top_level_name);
8ced4811
MD
1636 if (status != LTTNG_TRACE_CHUNK_STATUS_OK) {
1637 ERR("Error recursively removing subdirectory '%s' file when deleting chunk",
28ab034a 1638 top_level_name);
8ced4811
MD
1639 ret = -1;
1640 break;
1641 }
1642 }
1643 if (!ret) {
1644 lttng_directory_handle_put(trace_chunk->chunk_directory);
cd9adb8b 1645 trace_chunk->chunk_directory = nullptr;
8ced4811
MD
1646
1647 if (trace_chunk->path && trace_chunk->path[0] != '\0') {
28ab034a
JG
1648 status = (lttng_trace_chunk_status)
1649 lttng_directory_handle_remove_subdirectory(
1650 trace_chunk->session_output_directory, trace_chunk->path);
8ced4811
MD
1651 if (status != LTTNG_TRACE_CHUNK_STATUS_OK) {
1652 ERR("Error removing subdirectory '%s' file when deleting chunk",
28ab034a 1653 trace_chunk->path);
8ced4811
MD
1654 ret = -1;
1655 }
1656 }
1657 }
1658 free(trace_chunk->path);
cd9adb8b 1659 trace_chunk->path = nullptr;
8ced4811
MD
1660end:
1661 return ret;
1662}
1663
1664/*
1665 * For local files, session and consumer daemons all run the delete hook. The
1666 * consumer daemons have the list of files to unlink, and technically the
1667 * session daemon is the owner of the chunk. Unlink all files owned by each
1668 * consumer daemon.
1669 */
28ab034a 1670static int lttng_trace_chunk_delete_post_release(struct lttng_trace_chunk *trace_chunk)
8ced4811
MD
1671{
1672 if (!trace_chunk->chunk_directory) {
1673 return 0;
1674 }
1675
1676 if (trace_chunk->mode.value == TRACE_CHUNK_MODE_OWNER) {
1677 return lttng_trace_chunk_delete_post_release_owner(trace_chunk);
1678 } else {
1679 return lttng_trace_chunk_delete_post_release_user(trace_chunk);
1680 }
1681}
1682
28ab034a
JG
1683enum lttng_trace_chunk_status
1684lttng_trace_chunk_get_close_command(struct lttng_trace_chunk *chunk,
1685 enum lttng_trace_chunk_command_type *command_type)
bbc4768c
JG
1686{
1687 enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
1688
1689 pthread_mutex_lock(&chunk->lock);
1690 if (chunk->close_command.is_set) {
1691 *command_type = chunk->close_command.value;
1692 status = LTTNG_TRACE_CHUNK_STATUS_OK;
1693 } else {
1694 status = LTTNG_TRACE_CHUNK_STATUS_NONE;
1695 }
1696 pthread_mutex_unlock(&chunk->lock);
1697 return status;
1698}
1699
28ab034a
JG
1700enum lttng_trace_chunk_status
1701lttng_trace_chunk_set_close_command(struct lttng_trace_chunk *chunk,
1702 enum lttng_trace_chunk_command_type close_command)
2c5ff4e4
JG
1703{
1704 enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
1705
1706 if (close_command < LTTNG_TRACE_CHUNK_COMMAND_TYPE_MOVE_TO_COMPLETED ||
28ab034a 1707 close_command >= LTTNG_TRACE_CHUNK_COMMAND_TYPE_MAX) {
2c5ff4e4 1708 status = LTTNG_TRACE_CHUNK_STATUS_INVALID_ARGUMENT;
6def6cd7 1709 goto end;
2c5ff4e4
JG
1710 }
1711
1712 pthread_mutex_lock(&chunk->lock);
1713 if (chunk->close_command.is_set) {
1714 DBG("Overriding trace chunk close command from \"%s\" to \"%s\"",
28ab034a
JG
1715 lttng_trace_chunk_command_type_str(chunk->close_command.value),
1716 lttng_trace_chunk_command_type_str(close_command));
420acd90 1717 } else {
2c5ff4e4 1718 DBG("Setting trace chunk close command to \"%s\"",
28ab034a 1719 lttng_trace_chunk_command_type_str(close_command));
420acd90 1720 }
343defc2
MD
1721 /*
1722 * Unset close command for no-op for backward compatibility with relayd
1723 * 2.11.
1724 */
1725 if (close_command != LTTNG_TRACE_CHUNK_COMMAND_TYPE_NO_OPERATION) {
1726 LTTNG_OPTIONAL_SET(&chunk->close_command, close_command);
1727 } else {
1728 LTTNG_OPTIONAL_UNSET(&chunk->close_command);
1729 }
2c5ff4e4 1730 pthread_mutex_unlock(&chunk->lock);
6def6cd7 1731end:
2c5ff4e4
JG
1732 return status;
1733}
1734
28ab034a 1735const char *lttng_trace_chunk_command_type_get_name(enum lttng_trace_chunk_command_type command)
bbc4768c
JG
1736{
1737 switch (command) {
1738 case LTTNG_TRACE_CHUNK_COMMAND_TYPE_MOVE_TO_COMPLETED:
1739 return "move to completed trace chunk folder";
343defc2
MD
1740 case LTTNG_TRACE_CHUNK_COMMAND_TYPE_NO_OPERATION:
1741 return "no operation";
1742 case LTTNG_TRACE_CHUNK_COMMAND_TYPE_DELETE:
1743 return "delete";
bbc4768c
JG
1744 default:
1745 abort();
1746 }
1747}
1748
ad8bec24 1749bool lttng_trace_chunk_ids_equal(const struct lttng_trace_chunk *chunk_a,
28ab034a 1750 const struct lttng_trace_chunk *chunk_b)
ad8bec24
JG
1751{
1752 bool equal = false;
1753
80516611
JG
1754 if (chunk_a == chunk_b) {
1755 equal = true;
1756 goto end;
1757 }
1758
1759 if (!!chunk_a ^ !!chunk_b) {
ad8bec24
JG
1760 goto end;
1761 }
1762
1763 if (chunk_a->id.is_set ^ chunk_a->id.is_set) {
1764 /* One id is set and not the other, thus they are not equal. */
1765 goto end;
1766 }
1767
1768 if (!chunk_a->id.is_set) {
1769 /* Both ids are unset. */
1770 equal = true;
1771 } else {
1772 equal = chunk_a->id.value == chunk_b->id.value;
1773 }
1774
1775end:
1776 return equal;
1777}
1778
2c5ff4e4
JG
1779bool lttng_trace_chunk_get(struct lttng_trace_chunk *chunk)
1780{
1781 return urcu_ref_get_unless_zero(&chunk->ref);
1782}
1783
28ab034a 1784static void free_lttng_trace_chunk_registry_element(struct rcu_head *node)
2c5ff4e4 1785{
28ab034a
JG
1786 struct lttng_trace_chunk_registry_element *element =
1787 lttng::utils::container_of(node, &lttng_trace_chunk_registry_element::rcu_node);
2c5ff4e4 1788
2c5ff4e4
JG
1789 free(element);
1790}
1791
28ab034a 1792static void lttng_trace_chunk_release(struct urcu_ref *ref)
2c5ff4e4 1793{
0114db0e 1794 struct lttng_trace_chunk *chunk = lttng::utils::container_of(ref, &lttng_trace_chunk::ref);
2c5ff4e4
JG
1795
1796 if (chunk->close_command.is_set) {
28ab034a
JG
1797 chunk_command func =
1798 close_command_get_post_release_func(chunk->close_command.value);
a6bc4ca9
SM
1799
1800 if (func(chunk)) {
a7ceb342 1801 ERR("Trace chunk post-release command %s has failed.",
28ab034a 1802 lttng_trace_chunk_command_type_str(chunk->close_command.value));
a7ceb342 1803 }
2c5ff4e4
JG
1804 }
1805
1806 if (chunk->in_registry_element) {
1807 struct lttng_trace_chunk_registry_element *element;
1808
c26d615c
JR
1809 /*
1810 * Release internal chunk attributes immediately and
1811 * only use the deferred `call_rcu` work to reclaim the
1812 * storage.
1813 *
1814 * This ensures that file handles are released as soon as
1815 * possible which works around a problem we encounter with PRAM fs
1816 * mounts (and possibly other non-POSIX compliant file systems):
1817 * directories that contain files which are open can't be
1818 * rmdir().
1819 *
1820 * This means that the recording of a snapshot could be
1821 * completed, but that it would be impossible for the user to
1822 * delete it until the deferred clean-up released the file
1823 * handles to its contents.
1824 */
1825 lttng_trace_chunk_fini(chunk);
1826
28ab034a
JG
1827 element = lttng::utils::container_of(chunk,
1828 &lttng_trace_chunk_registry_element::chunk);
2c5ff4e4
JG
1829 if (element->registry) {
1830 rcu_read_lock();
28ab034a 1831 cds_lfht_del(element->registry->ht, &element->trace_chunk_registry_ht_node);
2c5ff4e4 1832 rcu_read_unlock();
28ab034a 1833 call_rcu(&element->rcu_node, free_lttng_trace_chunk_registry_element);
2c5ff4e4
JG
1834 } else {
1835 /* Never published, can be free'd immediately. */
28ab034a 1836 free_lttng_trace_chunk_registry_element(&element->rcu_node);
2c5ff4e4
JG
1837 }
1838 } else {
1839 /* Not RCU-protected, free immediately. */
1840 lttng_trace_chunk_fini(chunk);
1841 free(chunk);
1842 }
1843}
1844
2c5ff4e4
JG
1845void lttng_trace_chunk_put(struct lttng_trace_chunk *chunk)
1846{
1847 if (!chunk) {
1848 return;
1849 }
a0377dfe 1850 LTTNG_ASSERT(chunk->ref.refcount);
2c5ff4e4
JG
1851 urcu_ref_put(&chunk->ref, lttng_trace_chunk_release);
1852}
1853
cd9adb8b 1854struct lttng_trace_chunk_registry *lttng_trace_chunk_registry_create()
2c5ff4e4
JG
1855{
1856 struct lttng_trace_chunk_registry *registry;
1857
64803277 1858 registry = zmalloc<lttng_trace_chunk_registry>();
2c5ff4e4
JG
1859 if (!registry) {
1860 goto end;
1861 }
1862
28ab034a 1863 registry->ht = cds_lfht_new(
cd9adb8b 1864 DEFAULT_HT_SIZE, 1, 0, CDS_LFHT_AUTO_RESIZE | CDS_LFHT_ACCOUNTING, nullptr);
2c5ff4e4
JG
1865 if (!registry->ht) {
1866 goto error;
1867 }
1868end:
1869 return registry;
1870error:
1871 lttng_trace_chunk_registry_destroy(registry);
cd9adb8b 1872 return nullptr;
2c5ff4e4
JG
1873}
1874
28ab034a 1875void lttng_trace_chunk_registry_destroy(struct lttng_trace_chunk_registry *registry)
2c5ff4e4
JG
1876{
1877 if (!registry) {
1878 return;
1879 }
1880 if (registry->ht) {
cd9adb8b 1881 int ret = cds_lfht_destroy(registry->ht, nullptr);
a0377dfe 1882 LTTNG_ASSERT(!ret);
2c5ff4e4
JG
1883 }
1884 free(registry);
1885}
1886
28ab034a
JG
1887static struct lttng_trace_chunk_registry_element *
1888lttng_trace_chunk_registry_element_create_from_chunk(struct lttng_trace_chunk *chunk,
1889 uint64_t session_id)
2c5ff4e4
JG
1890{
1891 struct lttng_trace_chunk_registry_element *element =
64803277 1892 zmalloc<lttng_trace_chunk_registry_element>();
2c5ff4e4
JG
1893
1894 if (!element) {
1895 goto end;
1896 }
1897 cds_lfht_node_init(&element->trace_chunk_registry_ht_node);
1898 element->session_id = session_id;
1899
1900 element->chunk = *chunk;
1901 lttng_trace_chunk_init(&element->chunk);
cbf53d23
JG
1902 if (chunk->session_output_directory) {
1903 /* Transferred ownership. */
28ab034a 1904 element->chunk.session_output_directory = chunk->session_output_directory;
cd9adb8b 1905 chunk->session_output_directory = nullptr;
cbf53d23
JG
1906 }
1907 if (chunk->chunk_directory) {
1908 /* Transferred ownership. */
1909 element->chunk.chunk_directory = chunk->chunk_directory;
cd9adb8b 1910 chunk->chunk_directory = nullptr;
2c5ff4e4
JG
1911 }
1912 /*
a7ceb342
MD
1913 * The original chunk becomes invalid; the name and path attributes are
1914 * transferred to the new chunk instance.
2c5ff4e4 1915 */
cd9adb8b
JG
1916 chunk->name = nullptr;
1917 chunk->path = nullptr;
b2621f79 1918 element->chunk.fd_tracker = chunk->fd_tracker;
2c5ff4e4
JG
1919 element->chunk.in_registry_element = true;
1920end:
1921 return element;
1922}
1923
2c5ff4e4 1924struct lttng_trace_chunk *
28ab034a
JG
1925lttng_trace_chunk_registry_publish_chunk(struct lttng_trace_chunk_registry *registry,
1926 uint64_t session_id,
1927 struct lttng_trace_chunk *chunk)
c5c79321
JG
1928{
1929 bool unused;
1930
28ab034a 1931 return lttng_trace_chunk_registry_publish_chunk(registry, session_id, chunk, &unused);
c5c79321
JG
1932}
1933
1934struct lttng_trace_chunk *
28ab034a
JG
1935lttng_trace_chunk_registry_publish_chunk(struct lttng_trace_chunk_registry *registry,
1936 uint64_t session_id,
1937 struct lttng_trace_chunk *chunk,
1938 bool *previously_published)
2c5ff4e4
JG
1939{
1940 struct lttng_trace_chunk_registry_element *element;
1941 unsigned long element_hash;
1942
1943 pthread_mutex_lock(&chunk->lock);
28ab034a 1944 element = lttng_trace_chunk_registry_element_create_from_chunk(chunk, session_id);
2c5ff4e4
JG
1945 pthread_mutex_unlock(&chunk->lock);
1946 if (!element) {
1947 goto end;
1948 }
1949 /*
1950 * chunk is now invalid, the only valid operation is a 'put' from the
1951 * caller.
1952 */
cd9adb8b 1953 chunk = nullptr;
2c5ff4e4
JG
1954 element_hash = lttng_trace_chunk_registry_element_hash(element);
1955
1956 rcu_read_lock();
cd9adb8b 1957 while (true) {
2c5ff4e4
JG
1958 struct cds_lfht_node *published_node;
1959 struct lttng_trace_chunk *published_chunk;
1960 struct lttng_trace_chunk_registry_element *published_element;
1961
1962 published_node = cds_lfht_add_unique(registry->ht,
28ab034a
JG
1963 element_hash,
1964 lttng_trace_chunk_registry_element_match,
1965 element,
1966 &element->trace_chunk_registry_ht_node);
2c5ff4e4
JG
1967 if (published_node == &element->trace_chunk_registry_ht_node) {
1968 /* Successfully published the new element. */
420acd90 1969 element->registry = registry;
2c5ff4e4
JG
1970 /* Acquire a reference for the caller. */
1971 if (lttng_trace_chunk_get(&element->chunk)) {
c5c79321 1972 *previously_published = false;
2c5ff4e4
JG
1973 break;
1974 } else {
1975 /*
1976 * Another thread concurrently unpublished the
1977 * trace chunk. This is currently unexpected.
1978 *
1979 * Re-attempt to publish.
1980 */
87cee602 1981 ERR("Attempt to publish a trace chunk to the chunk registry raced with a trace chunk deletion");
2c5ff4e4
JG
1982 continue;
1983 }
1984 }
1985
1986 /*
1987 * An equivalent trace chunk was published before this trace
1988 * chunk. Attempt to acquire a reference to the one that was
1989 * already published and release the reference to the copy we
1990 * created if successful.
1991 */
28ab034a
JG
1992 published_element = lttng::utils::container_of(
1993 published_node,
1994 &lttng_trace_chunk_registry_element::trace_chunk_registry_ht_node);
2c5ff4e4
JG
1995 published_chunk = &published_element->chunk;
1996 if (lttng_trace_chunk_get(published_chunk)) {
1997 lttng_trace_chunk_put(&element->chunk);
1998 element = published_element;
c5c79321 1999 *previously_published = true;
2c5ff4e4
JG
2000 break;
2001 }
2002 /*
2003 * A reference to the previously published trace chunk could not
a6bc4ca9 2004 * be acquired. Hence, retry to publish our copy of the trace
2c5ff4e4
JG
2005 * chunk.
2006 */
2007 }
2008 rcu_read_unlock();
2009end:
cd9adb8b 2010 return element ? &element->chunk : nullptr;
2c5ff4e4
JG
2011}
2012
2013/*
2014 * Note that the caller must be registered as an RCU thread.
2015 * However, it does not need to hold the RCU read lock. The RCU read lock is
2016 * acquired to perform the look-up in the registry's hash table and held until
2017 * after a reference to the "found" trace chunk is acquired.
2018 *
2019 * IOW, holding a reference guarantees the existence of the object for the
2020 * caller.
2021 */
28ab034a
JG
2022static struct lttng_trace_chunk *_lttng_trace_chunk_registry_find_chunk(
2023 const struct lttng_trace_chunk_registry *registry, uint64_t session_id, uint64_t *chunk_id)
2c5ff4e4 2024{
28ab034a 2025 lttng_trace_chunk_registry_element target_element{};
a6bc4ca9
SM
2026
2027 target_element.chunk.id.is_set = !!chunk_id;
2028 target_element.chunk.id.value = chunk_id ? *chunk_id : 0;
2029 target_element.session_id = session_id;
2030
28ab034a 2031 const unsigned long element_hash = lttng_trace_chunk_registry_element_hash(&target_element);
2c5ff4e4
JG
2032 struct cds_lfht_node *published_node;
2033 struct lttng_trace_chunk_registry_element *published_element;
cd9adb8b 2034 struct lttng_trace_chunk *published_chunk = nullptr;
2c5ff4e4
JG
2035 struct cds_lfht_iter iter;
2036
2037 rcu_read_lock();
2038 cds_lfht_lookup(registry->ht,
2039 element_hash,
2040 lttng_trace_chunk_registry_element_match,
2041 &target_element,
2042 &iter);
2043 published_node = cds_lfht_iter_get_node(&iter);
2044 if (!published_node) {
2045 goto end;
2046 }
2047
28ab034a
JG
2048 published_element = lttng::utils::container_of(
2049 published_node, &lttng_trace_chunk_registry_element::trace_chunk_registry_ht_node);
2c5ff4e4
JG
2050 if (lttng_trace_chunk_get(&published_element->chunk)) {
2051 published_chunk = &published_element->chunk;
2052 }
2053end:
2054 rcu_read_unlock();
2055 return published_chunk;
2056}
2057
28ab034a
JG
2058struct lttng_trace_chunk *lttng_trace_chunk_registry_find_chunk(
2059 const struct lttng_trace_chunk_registry *registry, uint64_t session_id, uint64_t chunk_id)
2c5ff4e4 2060{
28ab034a 2061 return _lttng_trace_chunk_registry_find_chunk(registry, session_id, &chunk_id);
2c5ff4e4
JG
2062}
2063
28ab034a
JG
2064int lttng_trace_chunk_registry_chunk_exists(const struct lttng_trace_chunk_registry *registry,
2065 uint64_t session_id,
2066 uint64_t chunk_id,
2067 bool *chunk_exists)
6b584c2e
JG
2068{
2069 int ret = 0;
a6bc4ca9
SM
2070 lttng_trace_chunk_registry_element target_element;
2071
2072 target_element.chunk.id.is_set = true;
2073 target_element.chunk.id.value = chunk_id;
2074 target_element.session_id = session_id;
2075
28ab034a 2076 const unsigned long element_hash = lttng_trace_chunk_registry_element_hash(&target_element);
6b584c2e
JG
2077 struct cds_lfht_node *published_node;
2078 struct cds_lfht_iter iter;
2079
2080 rcu_read_lock();
2081 cds_lfht_lookup(registry->ht,
2082 element_hash,
2083 lttng_trace_chunk_registry_element_match,
2084 &target_element,
2085 &iter);
2086 published_node = cds_lfht_iter_get_node(&iter);
2087 if (!published_node) {
2088 *chunk_exists = false;
2089 goto end;
2090 }
2091
2092 *chunk_exists = !cds_lfht_is_node_deleted(published_node);
2093end:
2094 rcu_read_unlock();
2095 return ret;
2096}
2097
2c5ff4e4 2098struct lttng_trace_chunk *
28ab034a
JG
2099lttng_trace_chunk_registry_find_anonymous_chunk(const struct lttng_trace_chunk_registry *registry,
2100 uint64_t session_id)
2c5ff4e4 2101{
cd9adb8b 2102 return _lttng_trace_chunk_registry_find_chunk(registry, session_id, nullptr);
2c5ff4e4 2103}
e10aec8f 2104
28ab034a
JG
2105unsigned int
2106lttng_trace_chunk_registry_put_each_chunk(const struct lttng_trace_chunk_registry *registry)
e10aec8f
MD
2107{
2108 struct cds_lfht_iter iter;
2109 struct lttng_trace_chunk_registry_element *chunk_element;
2110 unsigned int trace_chunks_left = 0;
2111
2112 DBG("Releasing trace chunk registry to all trace chunks");
2113 rcu_read_lock();
28ab034a 2114 cds_lfht_for_each_entry (registry->ht, &iter, chunk_element, trace_chunk_registry_ht_node) {
e10aec8f
MD
2115 const char *chunk_id_str = "none";
2116 char chunk_id_buf[MAX_INT_DEC_LEN(uint64_t)];
2117
2118 pthread_mutex_lock(&chunk_element->chunk.lock);
2119 if (chunk_element->chunk.id.is_set) {
2120 int fmt_ret;
2121
28ab034a
JG
2122 fmt_ret = snprintf(chunk_id_buf,
2123 sizeof(chunk_id_buf),
2124 "%" PRIu64,
2125 chunk_element->chunk.id.value);
e10aec8f
MD
2126 if (fmt_ret < 0 || fmt_ret >= sizeof(chunk_id_buf)) {
2127 chunk_id_str = "formatting error";
2128 } else {
2129 chunk_id_str = chunk_id_buf;
2130 }
2131 }
2132
2133 DBG("Releasing reference to trace chunk: session_id = %" PRIu64
28ab034a
JG
2134 "chunk_id = %s, name = \"%s\", status = %s",
2135 chunk_element->session_id,
2136 chunk_id_str,
2137 chunk_element->chunk.name ?: "none",
2138 chunk_element->chunk.close_command.is_set ? "open" : "closed");
e10aec8f
MD
2139 pthread_mutex_unlock(&chunk_element->chunk.lock);
2140 lttng_trace_chunk_put(&chunk_element->chunk);
2141 trace_chunks_left++;
2142 }
2143 rcu_read_unlock();
28ab034a 2144 DBG("Released reference to %u trace chunks in %s()", trace_chunks_left, __FUNCTION__);
e10aec8f
MD
2145
2146 return trace_chunks_left;
2147}
This page took 0.154007 seconds and 4 git commands to generate.