#define _LGPL_SOURCE
#include <common/common.h>
-#include <common/uuid.h>
+#include <common/compat/path.h>
+#include <common/fd-tracker/utils.h>
#include <common/time.h>
#include <common/utils.h>
#include <common/uuid.h>
-#include <common/compat/path.h>
#include <urcu/rculist.h>
#include <sys/stat.h>
goto end;
}
- handle = lttng_directory_handle_create(full_session_path);
+ handle = fd_tracker_create_directory_handle(the_fd_tracker, full_session_path);
end:
pthread_mutex_unlock(&session->lock);
free(full_session_path);
struct lttng_directory_handle *handle =
container_of(ref, struct lttng_directory_handle, ref);
+ if (handle->destroy_cb) {
+ handle->destroy_cb(handle, handle->destroy_cb_data);
+ }
+
if (handle->dirfd == AT_FDCWD || handle->dirfd == -1) {
goto end;
}
* or a directory file descriptors, depending on the platform's capabilities.
*/
#ifdef COMPAT_DIRFD
+
+struct lttng_directory_handle;
+
+typedef void (*lttng_directory_handle_destroy_cb)(
+ struct lttng_directory_handle *handle, void *data);
+
struct lttng_directory_handle {
struct urcu_ref ref;
ino_t directory_inode;
int dirfd;
+ lttng_directory_handle_destroy_cb destroy_cb;
+ void *destroy_cb_data;
};
static inline
const char *path;
pthread_mutex_lock(&handle->lock);
- lttng_inode_get_location(handle->inode, NULL, &path);
+ lttng_inode_borrow_location(handle->inode, NULL, &path);
if (handle->fd >= 0) {
DBG_NO_LOC(" %s [active, fd %d%s]", path, handle->fd,
const struct lttng_directory_handle *node_directory_handle;
pthread_mutex_lock(&handle->lock);
- lttng_inode_get_location(handle->inode, &node_directory_handle, &path);
+ lttng_inode_borrow_location(
+ handle->inode, &node_directory_handle, &path);
assert(handle->fd >= 0);
if (handle->in_use) {
/* This handle can't be suspended as it is currently in use. */
const char *path;
const struct lttng_directory_handle *node_directory_handle;
- lttng_inode_get_location(handle->inode, &node_directory_handle, &path);
+ lttng_inode_borrow_location(
+ handle->inode, &node_directory_handle, &path);
assert(handle->fd == -1);
assert(path);
const char *path = NULL;
struct fs_handle_tracked *handle =
container_of(_handle, struct fs_handle_tracked, parent);
+ struct lttng_directory_handle *inode_directory_handle = NULL;
if (!handle) {
ret = -EINVAL;
pthread_mutex_lock(&handle->tracker->lock);
pthread_mutex_lock(&handle->lock);
if (handle->inode) {
- lttng_inode_get_location(handle->inode, NULL, &path);
+ lttng_inode_borrow_location(handle->inode, NULL, &path);
+ /*
+ * Here a reference to the inode's directory handle is acquired
+ * to prevent the last reference to it from being released while
+ * the tracker's lock is taken.
+ *
+ * If this wasn't done, the directory handle could attempt to
+ * close its underlying directory file descriptor, which would
+ * attempt to lock the tracker's lock, resulting in a deadlock.
+ *
+ * Since a new reference to the directory handle is taken within
+ * the scope of this function, it is not possible for the last
+ * reference to the inode's location directory handle to be
+ * released during the call to lttng_inode_put().
+ *
+ * We wait until the tracker's lock is released to release the
+ * reference. Hence, the call to the tracker is delayed just
+ * enough to not attempt to recursively acquire the tracker's
+ * lock twice.
+ */
+ inode_directory_handle =
+ lttng_inode_get_location_directory_handle(
+ handle->inode);
}
fd_tracker_untrack(handle->tracker, handle);
if (handle->fd >= 0) {
pthread_mutex_destroy(&handle->lock);
pthread_mutex_unlock(&handle->tracker->lock);
free(handle);
+ lttng_directory_handle_put(inode_directory_handle);
end:
return ret;
}
urcu_ref_put(&inode->ref, lttng_inode_release);
}
-void lttng_inode_get_location(struct lttng_inode *inode,
+struct lttng_directory_handle *lttng_inode_get_location_directory_handle(
+ struct lttng_inode *inode)
+{
+ if (inode->location.directory_handle) {
+ const bool reference_acquired = lttng_directory_handle_get(
+ inode->location.directory_handle);
+
+ assert(reference_acquired);
+ }
+ return inode->location.directory_handle;
+}
+
+void lttng_inode_borrow_location(struct lttng_inode *inode,
const struct lttng_directory_handle **out_directory_handle,
const char **out_path)
{
struct lttng_inode;
struct lttng_inode_registry;
struct lttng_unlinked_file_directory;
+struct lttng_directory_handle;
/*
* The unlinked file pool is protected by the fd-tracker's lock.
void lttng_inode_registry_destroy(struct lttng_inode_registry *registry);
-void lttng_inode_get_location(struct lttng_inode *inode,
+void lttng_inode_borrow_location(struct lttng_inode *inode,
const struct lttng_directory_handle **out_directory_handle,
const char **out_path);
+/* Returns a new reference to the inode's location directory handle. */
+struct lttng_directory_handle *lttng_inode_get_location_directory_handle(
+ struct lttng_inode *inode);
+
int lttng_inode_rename(struct lttng_inode *inode,
struct lttng_directory_handle *old_directory_handle,
const char *old_path,
*
*/
+#include <common/error.h>
#include <common/fd-tracker/utils.h>
#include <common/utils.h>
+#include <lttng/constant.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
-static int open_pipe_cloexec(void *data, int *fds)
+static
+int open_pipe_cloexec(void *data, int *fds)
{
return utils_create_pipe_cloexec(fds);
}
-static int close_pipe(void *data, int *pipe)
+static
+int close_pipe(void *data, int *pipe)
{
utils_close_pipe(pipe);
pipe[0] = pipe[1] = -1;
return 0;
}
+LTTNG_HIDDEN
int fd_tracker_util_close_fd(void *unused, int *fd)
{
return close(*fd);
}
+LTTNG_HIDDEN
int fd_tracker_util_pipe_open_cloexec(
struct fd_tracker *tracker, const char *name, int *pipe)
{
return ret;
}
+LTTNG_HIDDEN
int fd_tracker_util_pipe_close(struct fd_tracker *tracker, int *pipe)
{
return fd_tracker_close_unsuspendable_fd(
tracker, pipe, 2, close_pipe, NULL);
}
+
+struct open_directory_handle_args {
+ const struct lttng_directory_handle *in_handle;
+ struct lttng_directory_handle *ret_handle;
+ const char *path;
+};
+
+static
+int open_directory_handle(void *_args, int *out_fds)
+{
+ int ret = 0;
+ struct open_directory_handle_args *args = _args;
+ struct lttng_directory_handle *new_handle = NULL;
+
+ new_handle = args->in_handle ?
+ lttng_directory_handle_create_from_handle(
+ args->path, args->in_handle) :
+ lttng_directory_handle_create(args->path);
+ if (!new_handle) {
+ ret = -errno;
+ goto end;
+ }
+
+ args->ret_handle = new_handle;
+
+ /*
+ * Reserved to indicate that the handle does not use a handle; there is
+ * nothing to track. We want to indicate an error to the fd-tracker so
+ * that it doesn't attempt to track the file descriptor, but also want
+ * the caller to retrieve the newly-created handle.
+ *
+ * Calling this a hack is a fair assessment.
+ */
+ if (!lttng_directory_handle_uses_fd(new_handle)) {
+ ret = ENOTSUP;
+ } else {
+#ifdef COMPAT_DIRFD
+ *out_fds = new_handle->dirfd;
+#else
+ abort();
+#endif
+
+ }
+end:
+ return ret;
+}
+
+#ifdef COMPAT_DIRFD
+static
+int fd_close(void *unused, int *in_fds)
+{
+ const int ret = close(in_fds[0]);
+
+ in_fds[0] = -1;
+ return ret;
+}
+
+static
+void directory_handle_destroy(
+ struct lttng_directory_handle *handle, void *data)
+{
+ struct fd_tracker *tracker = data;
+ const int ret = fd_tracker_close_unsuspendable_fd(
+ tracker, &handle->dirfd, 1, fd_close, NULL);
+
+ if (ret) {
+ ERR("Failed to untrack directory handle file descriptor");
+ }
+}
+#endif
+
+LTTNG_HIDDEN
+struct lttng_directory_handle *fd_tracker_create_directory_handle(
+ struct fd_tracker *tracker, const char *path)
+{
+ return fd_tracker_create_directory_handle_from_handle(
+ tracker, NULL, path);
+}
+
+LTTNG_HIDDEN
+struct lttng_directory_handle *fd_tracker_create_directory_handle_from_handle(
+ struct fd_tracker *tracker,
+ struct lttng_directory_handle *in_handle,
+ const char *path)
+{
+ int ret;
+ int dirfd = -1;
+ char *handle_name = NULL;
+ char cwd_path[LTTNG_PATH_MAX] = "working directory";
+ struct lttng_directory_handle *new_handle = NULL;
+ struct open_directory_handle_args open_args = {
+ .in_handle = in_handle,
+ .path = path,
+ };
+
+ if (!path) {
+ if (!getcwd(cwd_path, sizeof(cwd_path))) {
+ PERROR("Failed to get current working directory to name directory handle");
+ goto end;
+ }
+ }
+
+ ret = asprintf(&handle_name, "Directory handle to %s",
+ path ? path : cwd_path);
+ if (ret < 0) {
+ PERROR("Failed to format directory handle name");
+ goto end;
+ }
+
+ ret = fd_tracker_open_unsuspendable_fd(tracker, &dirfd,
+ (const char **) &handle_name, 1, open_directory_handle,
+ &open_args);
+ if (ret && ret != ENOTSUP) {
+ ERR("Failed to open directory handle to %s through the fd tracker", path ? path : cwd_path);
+ }
+ new_handle = open_args.ret_handle;
+
+#ifdef COMPAT_DIRFD
+ new_handle->destroy_cb = directory_handle_destroy;
+ new_handle->destroy_cb_data = tracker;
+#endif
+end:
+ free(handle_name);
+ return new_handle;
+}
#ifndef FD_TRACKER_UTILS_H
#define FD_TRACKER_UTILS_H
+#include <common/compat/directory-handle.h>
#include <common/fd-tracker/fd-tracker.h>
+#include <common/macros.h>
struct lttng_poll_event;
* Utility implementing a close_fd callback which receives one file descriptor
* and closes it, returning close()'s return value.
*/
+LTTNG_HIDDEN
int fd_tracker_util_close_fd(void *, int *fd);
/*
* Create a pipe and track its underlying fds.
*/
+LTTNG_HIDDEN
int fd_tracker_util_pipe_open_cloexec(
struct fd_tracker *tracker, const char *name, int *pipe);
+LTTNG_HIDDEN
int fd_tracker_util_pipe_close(struct fd_tracker *tracker, int *pipe);
/*
* Create a poll event and track its underlying fd, if applicable.
*/
+LTTNG_HIDDEN
int fd_tracker_util_poll_create(struct fd_tracker *tracker,
const char *name,
struct lttng_poll_event *events,
int size,
int flags);
+LTTNG_HIDDEN
int fd_tracker_util_poll_clean(
struct fd_tracker *tracker, struct lttng_poll_event *events);
+LTTNG_HIDDEN
+struct lttng_directory_handle *fd_tracker_create_directory_handle(
+ struct fd_tracker *tracker, const char *path);
+
+LTTNG_HIDDEN
+struct lttng_directory_handle *fd_tracker_create_directory_handle_from_handle(
+ struct fd_tracker *tracker,
+ struct lttng_directory_handle *handle,
+ const char *path);
+
#endif /* FD_TRACKER_UTILS_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>
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;
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;