/*
- * Copyright (C) 2019 - Jérémie Galarneau <jeremie.galarneau@efficios.com>
+ * Copyright (C) 2019 Jérémie Galarneau <jeremie.galarneau@efficios.com>
*
- * This library is free software; you can redistribute it and/or modify it
- * under the terms of the GNU Lesser General Public License, version 2.1 only,
- * as published by the Free Software Foundation.
+ * SPDX-License-Identifier: LGPL-2.1-only
*
- * This library is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
- * for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this library; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
-#include <lttng/constant.h>
-#include <common/string-utils/format.h>
-#include <common/trace-chunk.h>
-#include <common/trace-chunk-registry.h>
-#include <common/hashtable/utils.h>
-#include <common/hashtable/hashtable.h>
-#include <common/error.h>
-#include <common/utils.h>
-#include <common/time.h>
-#include <common/optional.h>
#include <common/compat/directory-handle.h>
#include <common/credentials.h>
#include <common/defaults.h>
#include <common/dynamic-array.h>
+#include <common/error.h>
+#include <common/fd-tracker/fd-tracker.h>
+#include <common/fd-tracker/utils.h>
+#include <common/fs-handle-internal.h>
+#include <common/hashtable/hashtable.h>
+#include <common/hashtable/utils.h>
+#include <common/optional.h>
+#include <common/string-utils/format.h>
+#include <common/time.h>
+#include <common/trace-chunk-registry.h>
+#include <common/trace-chunk.h>
+#include <common/utils.h>
+#include <lttng/constant.h>
-#include <urcu/ref.h>
-#include <urcu/rculfhash.h>
-#include <sys/stat.h>
#include <inttypes.h>
#include <pthread.h>
#include <stdio.h>
+#include <sys/stat.h>
+#include <urcu/rculfhash.h>
+#include <urcu/ref.h>
/*
* Two ISO 8601-compatible timestamps, separated by a hypen, followed an
struct lttng_directory_handle *session_output_directory;
struct lttng_directory_handle *chunk_directory;
LTTNG_OPTIONAL(enum lttng_trace_chunk_command_type) close_command;
+ /*
+ * fd_tracker instance through which file descriptors should be
+ * created/closed.
+ *
+ * An fd_tracker always outlives any trace chunk; there is no
+ * need to perform any reference counting of that object.
+ */
+ struct fd_tracker *fd_tracker;
};
/* A trace chunk is uniquely identified by its (session id, chunk id) tuple. */
struct cds_lfht *ht;
};
+struct fs_handle_untracked {
+ struct fs_handle parent;
+ int fd;
+ struct {
+ struct lttng_directory_handle *directory_handle;
+ char *path;
+ } location;
+};
+
+static
+int fs_handle_untracked_get_fd(struct fs_handle *handle);
+static
+void fs_handle_untracked_put_fd(struct fs_handle *handle);
+static
+int fs_handle_untracked_unlink(struct fs_handle *handle);
+static
+int fs_handle_untracked_close(struct fs_handle *handle);
+
static const
char *close_command_names[] = {
[LTTNG_TRACE_CHUNK_COMMAND_TYPE_MOVE_TO_COMPLETED] =
lttng_trace_chunk_delete_post_release,
};
+static
+struct fs_handle *fs_handle_untracked_create(
+ struct lttng_directory_handle *directory_handle,
+ const char *path,
+ int fd)
+{
+ struct fs_handle_untracked *handle = NULL;
+ bool reference_acquired;
+ char *path_copy = strdup(path);
+
+ assert(fd >= 0);
+ if (!path_copy) {
+ PERROR("Failed to copy file path while creating untracked filesystem handle");
+ goto end;
+ }
+
+ handle = zmalloc(sizeof(typeof(*handle)));
+ if (!handle) {
+ PERROR("Failed to allocate untracked filesystem handle");
+ goto end;
+ }
+
+ handle->parent = (typeof(handle->parent)) {
+ .get_fd = fs_handle_untracked_get_fd,
+ .put_fd = fs_handle_untracked_put_fd,
+ .unlink = fs_handle_untracked_unlink,
+ .close = fs_handle_untracked_close,
+ };
+
+ handle->fd = fd;
+ reference_acquired = lttng_directory_handle_get(directory_handle);
+ assert(reference_acquired);
+ handle->location.directory_handle = directory_handle;
+ /* Ownership is transferred. */
+ handle->location.path = path_copy;
+ path_copy = NULL;
+end:
+ free(path_copy);
+ return handle ? &handle->parent : NULL;
+}
+
+static
+int fs_handle_untracked_get_fd(struct fs_handle *_handle)
+{
+ struct fs_handle_untracked *handle = container_of(
+ _handle, struct fs_handle_untracked, parent);
+
+ return handle->fd;
+}
+
+static
+void fs_handle_untracked_put_fd(struct fs_handle *_handle)
+{
+ /* no-op. */
+}
+
+static
+int fs_handle_untracked_unlink(struct fs_handle *_handle)
+{
+ struct fs_handle_untracked *handle = container_of(
+ _handle, struct fs_handle_untracked, parent);
+
+ return lttng_directory_handle_unlink_file(
+ handle->location.directory_handle,
+ handle->location.path);
+}
+
+static
+void fs_handle_untracked_destroy(struct fs_handle_untracked *handle)
+{
+ lttng_directory_handle_put(handle->location.directory_handle);
+ free(handle->location.path);
+ free(handle);
+}
+
+static
+int fs_handle_untracked_close(struct fs_handle *_handle)
+{
+ struct fs_handle_untracked *handle = container_of(
+ _handle, struct fs_handle_untracked, parent);
+ int ret = close(handle->fd);
+
+ fs_handle_untracked_destroy(handle);
+ return ret;
+}
+
static
bool lttng_trace_chunk_registry_element_equals(
const struct lttng_trace_chunk_registry_element *a,
return NULL;
}
+LTTNG_HIDDEN
+void lttng_trace_chunk_set_fd_tracker(struct lttng_trace_chunk *chunk,
+ struct fd_tracker *fd_tracker)
+{
+ assert(!chunk->session_output_directory);
+ assert(!chunk->chunk_directory);
+ assert(lttng_dynamic_pointer_array_get_count(&chunk->files) == 0);
+ chunk->fd_tracker = fd_tracker;
+}
+
LTTNG_HIDDEN
struct lttng_trace_chunk *lttng_trace_chunk_copy(
struct lttng_trace_chunk *source_chunk)
new_chunk->chunk_directory = source_chunk->chunk_directory;
}
new_chunk->close_command = source_chunk->close_command;
+ new_chunk->fd_tracker = source_chunk->fd_tracker;
pthread_mutex_unlock(&source_chunk->lock);
end:
return new_chunk;
status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
goto end;
}
- rename_directory = lttng_directory_handle_create_from_handle(
- path,
- chunk->session_output_directory);
+ 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;
}
chunk_directory_handle =
- lttng_directory_handle_create_from_handle(
- chunk->path,
- session_output_directory);
+ chunk->fd_tracker ?
+ fd_tracker_create_directory_handle_from_handle(
+ chunk->fd_tracker,
+ session_output_directory,
+ chunk->path) :
+ lttng_directory_handle_create_from_handle(
+ chunk->path,
+ session_output_directory);
if (!chunk_directory_handle) {
/* The function already logs on all error paths. */
status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
assert(!ret);
}
-LTTNG_HIDDEN
-enum lttng_trace_chunk_status lttng_trace_chunk_open_file(
- struct lttng_trace_chunk *chunk, const char *file_path,
- int flags, mode_t mode, int *out_fd, bool expect_no_file)
+static
+enum lttng_trace_chunk_status _lttng_trace_chunk_open_fs_handle_locked(
+ struct lttng_trace_chunk *chunk,
+ const char *file_path,
+ int flags,
+ mode_t mode,
+ struct fs_handle **out_handle,
+ bool expect_no_file)
{
int ret;
enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
DBG("Opening trace chunk file \"%s\"", file_path);
- pthread_mutex_lock(&chunk->lock);
if (!chunk->credentials.is_set) {
/*
* Fatal error, credentials must be set before a
if (status != LTTNG_TRACE_CHUNK_STATUS_OK) {
goto end;
}
- ret = lttng_directory_handle_open_file_as_user(
- chunk->chunk_directory, file_path, flags, mode,
- chunk->credentials.value.use_current_user ?
- NULL : &chunk->credentials.value.user);
+ if (chunk->fd_tracker) {
+ assert(chunk->credentials.value.use_current_user);
+ *out_handle = fd_tracker_open_fs_handle(chunk->fd_tracker,
+ chunk->chunk_directory, file_path, flags, &mode);
+ ret = *out_handle ? 0 : -1;
+ } else {
+ ret = lttng_directory_handle_open_file_as_user(
+ chunk->chunk_directory, file_path, flags, mode,
+ chunk->credentials.value.use_current_user ?
+ NULL :
+ &chunk->credentials.value.user);
+ if (ret >= 0) {
+ *out_handle = fs_handle_untracked_create(
+ chunk->chunk_directory, file_path, ret);
+ if (!*out_handle) {
+ status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
+ goto end;
+ }
+ }
+ }
if (ret < 0) {
if (errno == ENOENT && expect_no_file) {
status = LTTNG_TRACE_CHUNK_STATUS_NO_FILE;
lttng_trace_chunk_remove_file(chunk, file_path);
goto end;
}
- *out_fd = ret;
end:
+ return status;
+}
+
+LTTNG_HIDDEN
+enum lttng_trace_chunk_status lttng_trace_chunk_open_fs_handle(
+ struct lttng_trace_chunk *chunk,
+ const char *file_path,
+ int flags,
+ mode_t mode,
+ struct fs_handle **out_handle,
+ bool expect_no_file)
+{
+ enum lttng_trace_chunk_status status;
+
+ pthread_mutex_lock(&chunk->lock);
+ status = _lttng_trace_chunk_open_fs_handle_locked(chunk, file_path,
+ flags, mode, out_handle, expect_no_file);
+ pthread_mutex_unlock(&chunk->lock);
+ return status;
+}
+
+LTTNG_HIDDEN
+enum lttng_trace_chunk_status lttng_trace_chunk_open_file(
+ struct lttng_trace_chunk *chunk,
+ const char *file_path,
+ int flags,
+ mode_t mode,
+ int *out_fd,
+ bool expect_no_file)
+{
+ enum lttng_trace_chunk_status status;
+ struct fs_handle *fs_handle;
+
+ pthread_mutex_lock(&chunk->lock);
+ /*
+ * Using this method is never valid when an fd_tracker is being
+ * used since the resulting file descriptor would not be tracked.
+ */
+ assert(!chunk->fd_tracker);
+ status = _lttng_trace_chunk_open_fs_handle_locked(chunk, file_path,
+ flags, mode, &fs_handle, expect_no_file);
pthread_mutex_unlock(&chunk->lock);
+
+ if (status == LTTNG_TRACE_CHUNK_STATUS_OK) {
+ *out_fd = fs_handle_get_fd(fs_handle);
+ /*
+ * Does not close the fd; we just "unbox" it from the fs_handle.
+ */
+ fs_handle_untracked_destroy(container_of(
+ fs_handle, struct fs_handle_untracked, parent));
+ }
+
return status;
}
goto end;
}
- archived_chunks_directory = lttng_directory_handle_create_from_handle(
- DEFAULT_ARCHIVED_TRACE_CHUNKS_DIRECTORY,
- trace_chunk->session_output_directory);
+ archived_chunks_directory = trace_chunk->fd_tracker ?
+ fd_tracker_create_directory_handle_from_handle(
+ trace_chunk->fd_tracker,
+ trace_chunk->session_output_directory,
+ DEFAULT_ARCHIVED_TRACE_CHUNKS_DIRECTORY) :
+ lttng_directory_handle_create_from_handle(
+ DEFAULT_ARCHIVED_TRACE_CHUNKS_DIRECTORY,
+ trace_chunk->session_output_directory);
if (!archived_chunks_directory) {
PERROR("Failed to get handle to archived trace chunks directory");
ret = -1;
*/
chunk->name = NULL;
chunk->path = NULL;
+ element->chunk.fd_tracker = chunk->fd_tracker;
element->chunk.in_registry_element = true;
end:
return element;