bool thread_is_rcu_registered = false;
int ret = 0, retval = 0;
void *status;
+ char *unlinked_file_directory_path = NULL, *output_path = NULL;
/* Parse environment variables */
ret = parse_env_options();
rcu_register_thread();
thread_is_rcu_registered = true;
- the_fd_tracker = fd_tracker_create(lttng_opt_fd_pool_size);
+ output_path = create_output_path("");
+ if (!output_path) {
+ ERR("Failed to get output path");
+ retval = -1;
+ goto exit_options;
+ }
+ ret = asprintf(&unlinked_file_directory_path, "%s/%s", output_path,
+ DEFAULT_UNLINKED_FILES_DIRECTORY);
+ free(output_path);
+ if (ret < 0) {
+ ERR("Failed to format unlinked file directory path");
+ retval = -1;
+ goto exit_options;
+ }
+ the_fd_tracker = fd_tracker_create(
+ unlinked_file_directory_path, lttng_opt_fd_pool_size);
+ free(unlinked_file_directory_path);
if (!the_fd_tracker) {
retval = -1;
goto exit_options;
#define DEFAULT_CHUNK_TMP_OLD_DIRECTORY ".tmp_old_chunk"
#define DEFAULT_CHUNK_TMP_NEW_DIRECTORY ".tmp_new_chunk"
#define DEFAULT_ARCHIVED_TRACE_CHUNKS_DIRECTORY "archives"
+#define DEFAULT_UNLINKED_FILES_DIRECTORY ".unlinked"
/*
* Default timer value in usec for the rotate pending polling check on the
struct cds_list_head suspended_handles;
struct cds_lfht *unsuspendable_fds;
struct lttng_inode_registry *inode_registry;
+ /* Unlinked files are moved in this directory under a unique name. */
+ struct lttng_directory_handle *unlink_directory_handle;
+ struct lttng_unlinked_file_pool *unlinked_file_pool;
};
struct open_properties {
static void unsuspendable_fd_destroy(struct unsuspendable_fd *entry);
static struct unsuspendable_fd *unsuspendable_fd_create(
const char *name, int fd);
-static int open_from_properties(
+static int open_from_properties(const struct lttng_directory_handle *dir_handle,
const char *path, struct open_properties *properties);
static void fs_handle_tracked_log(struct fs_handle_tracked *handle);
static int fd_tracker_restore_handle(
struct fd_tracker *tracker, struct fs_handle_tracked *handle);
-static const struct fs_handle fs_handle_tracked_callbacks = {
- .get_fd = fs_handle_tracked_get_fd,
- .put_fd = fs_handle_tracked_put_fd,
- .unlink = fs_handle_tracked_unlink,
- .close = fs_handle_tracked_close,
-};
-
/* Match function of the tracker's unsuspendable_fds hash table. */
static int match_fd(struct cds_lfht_node *node, const void *key)
{
const char *path;
pthread_mutex_lock(&handle->lock);
- path = lttng_inode_get_path(handle->inode);
+ lttng_inode_get_location(handle->inode, NULL, &path);
if (handle->fd >= 0) {
DBG_NO_LOC(" %s [active, fd %d%s]", path, handle->fd,
int ret = 0;
struct stat fs_stat;
const char *path;
+ const struct lttng_directory_handle *node_directory_handle;
pthread_mutex_lock(&handle->lock);
- path = lttng_inode_get_path(handle->inode);
+ lttng_inode_get_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. */
goto end;
}
- ret = stat(path, &fs_stat);
+ ret = lttng_directory_handle_stat(
+ node_directory_handle, path, &fs_stat);
if (ret) {
PERROR("Filesystem handle to %s cannot be suspended as stat() failed",
path);
static int fs_handle_tracked_restore(struct fs_handle_tracked *handle)
{
int ret, fd = -1;
- const char *path = lttng_inode_get_path(handle->inode);
+ const char *path;
+ const struct lttng_directory_handle *node_directory_handle;
+
+ lttng_inode_get_location(handle->inode, &node_directory_handle, &path);
assert(handle->fd == -1);
assert(path);
- ret = open_from_properties(path, &handle->properties);
+ ret = open_from_properties(
+ node_directory_handle, path, &handle->properties);
if (ret < 0) {
PERROR("Failed to restore filesystem handle to %s, open() failed",
path);
return ret;
}
-static int open_from_properties(
+static int open_from_properties(const struct lttng_directory_handle *dir_handle,
const char *path, struct open_properties *properties)
{
int ret;
* thus it is ignored here.
*/
if ((properties->flags & O_CREAT) && properties->mode.is_set) {
- ret = open(path, properties->flags, properties->mode.value);
+ ret = lttng_directory_handle_open_file(dir_handle, path,
+ properties->flags, properties->mode.value);
} else {
- ret = open(path, properties->flags);
+ ret = lttng_directory_handle_open_file(dir_handle, path,
+ properties->flags, 0);
}
/*
* Some flags should not be used beyond the initial open() of a
return ret;
}
-struct fd_tracker *fd_tracker_create(unsigned int capacity)
+struct fd_tracker *fd_tracker_create(const char *unlinked_file_path,
+ unsigned int capacity)
{
struct fd_tracker *tracker = zmalloc(sizeof(struct fd_tracker));
ERR("Failed to create fd-tracker's inode registry");
goto error;
}
+
+ tracker->unlinked_file_pool =
+ lttng_unlinked_file_pool_create(unlinked_file_path);
+ if (!tracker->unlinked_file_pool) {
+ goto error;
+ }
DBG("File descriptor tracker created with a limit of %u simultaneously-opened FDs",
capacity);
end:
}
lttng_inode_registry_destroy(tracker->inode_registry);
+ lttng_unlinked_file_pool_destroy(tracker->unlinked_file_pool);
pthread_mutex_destroy(&tracker->lock);
free(tracker);
end:
}
struct fs_handle *fd_tracker_open_fs_handle(struct fd_tracker *tracker,
+ struct lttng_directory_handle *directory,
const char *path,
int flags,
mode_t *mode)
if (!handle) {
goto end;
}
- handle->parent = fs_handle_tracked_callbacks;
+ handle->parent = (typeof(handle->parent)) {
+ .get_fd = fs_handle_tracked_get_fd,
+ .put_fd = fs_handle_tracked_put_fd,
+ .unlink = fs_handle_tracked_unlink,
+ .close = fs_handle_tracked_close,
+ };
+
handle->tracker = tracker;
ret = pthread_mutex_init(&handle->lock, NULL);
goto error_mutex_init;
}
- handle->fd = open_from_properties(path, &properties);
+ handle->fd = open_from_properties(directory, path, &properties);
if (handle->fd < 0) {
PERROR("Failed to open fs handle to %s, open() returned", path);
goto error;
handle->properties = properties;
- handle->inode = lttng_inode_registry_get_inode(
- tracker->inode_registry, handle->fd, path);
+ handle->inode = lttng_inode_registry_get_inode(tracker->inode_registry,
+ directory, path, handle->fd,
+ tracker->unlinked_file_pool);
if (!handle->inode) {
ERR("Failed to get lttng_inode corresponding to file %s", path);
goto error;
struct fd_tracker *tracker, unsigned int count)
{
unsigned int left_to_close = count;
+ unsigned int attempts_left = tracker->count.suspendable.active;
struct fs_handle_tracked *handle, *tmp;
cds_list_for_each_entry_safe(handle, tmp, &tracker->active_handles,
if (!ret) {
left_to_close--;
}
+ attempts_left--;
- if (!left_to_close) {
+ if (left_to_close == 0 || attempts_left == 0) {
break;
}
}
pthread_mutex_lock(&handle->tracker->lock);
pthread_mutex_lock(&handle->lock);
- ret = lttng_inode_defer_unlink(handle->inode);
+ ret = lttng_inode_unlink(handle->inode);
pthread_mutex_unlock(&handle->lock);
pthread_mutex_unlock(&handle->tracker->lock);
return ret;
pthread_mutex_lock(&handle->tracker->lock);
pthread_mutex_lock(&handle->lock);
if (handle->inode) {
- path = lttng_inode_get_path(handle->inode);
+ lttng_inode_get_location(handle->inode, NULL, &path);
}
fd_tracker_untrack(handle->tracker, handle);
if (handle->fd >= 0) {
#ifndef FD_TRACKER_H
#define FD_TRACKER_H
+#include <common/compat/directory-handle.h>
#include <stdint.h>
#include <sys/types.h>
* Set the maximal number of fds that the process should be allowed to open at
* any given time. This function must be called before any other of this
* interface.
+ *
+ * The unlinked_file_path is an absolute path (which does not need to exist)
+ * under which unlinked files will be stored for as long as a reference to them
+ * is held.
*/
-struct fd_tracker *fd_tracker_create(unsigned int capacity);
+struct fd_tracker *fd_tracker_create(const char *unlinked_file_path,
+ unsigned int capacity);
/* Returns an error if file descriptors are leaked. */
int fd_tracker_destroy(struct fd_tracker *tracker);
* open.
*/
struct fs_handle *fd_tracker_open_fs_handle(struct fd_tracker *tracker,
+ struct lttng_directory_handle *directory,
const char *path,
int flags,
mode_t *mode);
/*
- * Copyright (C) 2018 - Jérémie Galarneau <jeremie.galarneau@efficios.com>
+ * Copyright (C) 2020 - Jérémie Galarneau <jeremie.galarneau@efficios.com>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License, version 2 only, as
#include <common/error.h>
#include <common/hashtable/utils.h>
#include <common/macros.h>
+#include <common/optional.h>
+#include <common/string-utils/format.h>
+#include <common/utils.h>
#include <inttypes.h>
#include <lttng/constant.h>
#include <sys/stat.h>
struct lttng_inode {
struct inode_id id;
- char *path;
- bool unlink_pending;
/* Node in the lttng_inode_registry's ht. */
struct cds_lfht_node registry_node;
/* Weak reference to ht containing the node. */
struct cds_lfht *registry_ht;
struct urcu_ref ref;
struct rcu_head rcu_head;
+ /* Location from which this file can be opened. */
+ struct {
+ struct lttng_directory_handle *directory_handle;
+ char *path;
+ } location;
+ /* Unlink the underlying file at the release of the inode. */
+ bool unlink_pending;
+ LTTNG_OPTIONAL(unsigned int) unlinked_id;
+ /* Weak reference. */
+ struct lttng_unlinked_file_pool *unlinked_file_pool;
+};
+
+struct lttng_unlinked_file_pool {
+ struct lttng_directory_handle *unlink_directory_handle;
+ char *unlink_directory_path;
+ unsigned int file_count;
+ unsigned int next_id;
};
static struct {
.lock = PTHREAD_MUTEX_INITIALIZER,
};
-static unsigned long lttng_inode_id_hash(struct inode_id *id)
+static unsigned long lttng_inode_id_hash(const struct inode_id *id)
{
uint64_t device = id->device, inode_no = id->inode;
static int lttng_inode_match(struct cds_lfht_node *node, const void *key)
{
const struct inode_id *id = key;
- struct lttng_inode *inode = caa_container_of(
+ const struct lttng_inode *inode = caa_container_of(
node, struct lttng_inode, registry_node);
return inode->id.device == id->device && inode->id.inode == id->inode;
}
-static void lttng_inode_delete(struct rcu_head *head)
+static void lttng_inode_free(struct rcu_head *head)
{
struct lttng_inode *inode =
caa_container_of(head, struct lttng_inode, rcu_head);
- free(inode->path);
free(inode);
}
+static int lttng_unlinked_file_pool_add_inode(
+ struct lttng_unlinked_file_pool *pool,
+ struct lttng_inode *inode)
+{
+ int ret;
+ const unsigned int unlinked_id = pool->next_id++;
+ char *inode_unlinked_name;
+ bool reference_acquired;
+
+ DBG("Adding inode of %s to unlinked file pool as id %u",
+ inode->location.path, unlinked_id);
+ ret = asprintf(&inode_unlinked_name, "%u", unlinked_id);
+ if (ret < 0) {
+ ERR("Failed to format unlinked inode name");
+ ret = -1;
+ goto end;
+ }
+
+ if (pool->file_count == 0) {
+ DBG("Creating unlinked files directory at %s",
+ pool->unlink_directory_path);
+ assert(!pool->unlink_directory_handle);
+ ret = utils_mkdir(pool->unlink_directory_path,
+ S_IRWXU | S_IRWXG, -1, -1);
+ if (ret) {
+ if (errno == EEXIST) {
+ /*
+ * Unexpected (previous crash?), but not an
+ * error.
+ */
+ DBG("Unlinked file directory \"%s\" already exists",
+ pool->unlink_directory_path);
+ } else {
+ PERROR("Failed to create unlinked files directory at %s",
+ pool->unlink_directory_path);
+ goto end;
+ }
+ }
+ pool->unlink_directory_handle = lttng_directory_handle_create(
+ pool->unlink_directory_path);
+ }
+
+ ret = lttng_directory_handle_rename(inode->location.directory_handle,
+ inode->location.path, pool->unlink_directory_handle,
+ inode_unlinked_name);
+ if (ret) {
+ goto end;
+ }
+
+ lttng_directory_handle_put(inode->location.directory_handle);
+ inode->location.directory_handle = NULL;
+ reference_acquired = lttng_directory_handle_get(
+ pool->unlink_directory_handle);
+ assert(reference_acquired);
+ inode->location.directory_handle = pool->unlink_directory_handle;
+
+ free(inode->location.path);
+ inode->location.path = inode_unlinked_name;
+ inode_unlinked_name = NULL;
+ LTTNG_OPTIONAL_SET(&inode->unlinked_id, unlinked_id);
+ pool->file_count++;
+end:
+ free(inode_unlinked_name);
+ return ret;
+}
+
+static int lttng_unlinked_file_pool_remove_inode(
+ struct lttng_unlinked_file_pool *pool,
+ struct lttng_inode *inode)
+{
+ int ret;
+
+ DBG("Removing inode with unlinked id %u from unlinked file pool",
+ LTTNG_OPTIONAL_GET(inode->unlinked_id));
+
+ ret = lttng_directory_handle_unlink_file(
+ inode->location.directory_handle, inode->location.path);
+ if (ret) {
+ PERROR("Failed to unlink file %s from unlinked file directory",
+ inode->location.path);
+ goto end;
+ }
+ free(inode->location.path);
+ inode->location.path = NULL;
+ lttng_directory_handle_put(inode->location.directory_handle);
+ inode->location.directory_handle = NULL;
+
+ pool->file_count--;
+ if (pool->file_count == 0) {
+ ret = utils_recursive_rmdir(pool->unlink_directory_path);
+ if (ret) {
+ /*
+ * There is nothing the caller can do, don't report an
+ * error except through logging.
+ */
+ PERROR("Failed to remove unlinked files directory at %s",
+ pool->unlink_directory_path);
+ }
+ lttng_directory_handle_put(pool->unlink_directory_handle);
+ pool->unlink_directory_handle = NULL;
+ }
+end:
+ return ret;
+}
+
static void lttng_inode_destroy(struct lttng_inode *inode)
{
if (!inode) {
return;
}
+
+ rcu_read_lock();
+ cds_lfht_del(inode->registry_ht, &inode->registry_node);
+ rcu_read_unlock();
+
if (inode->unlink_pending) {
- int ret = unlink(inode->path);
+ int ret;
- DBG("Unlinking %s during lttng_inode destruction", inode->path);
+ assert(inode->location.directory_handle);
+ assert(inode->location.path);
+ DBG("Removing %s from unlinked file pool",
+ inode->location.path);
+ ret = lttng_unlinked_file_pool_remove_inode(inode->unlinked_file_pool, inode);
if (ret) {
- PERROR("Failed to unlink %s", inode->path);
+ PERROR("Failed to unlink %s", inode->location.path);
}
}
- rcu_read_lock();
- cds_lfht_del(inode->registry_ht, &inode->registry_node);
- rcu_read_unlock();
- call_rcu(&inode->rcu_head, lttng_inode_delete);
+
+ lttng_directory_handle_put(
+ inode->location.directory_handle);
+ inode->location.directory_handle = NULL;
+ free(inode->location.path);
+ inode->location.path = NULL;
+ call_rcu(&inode->rcu_head, lttng_inode_free);
}
static void lttng_inode_release(struct urcu_ref *ref)
urcu_ref_get(&inode->ref);
}
+struct lttng_unlinked_file_pool *lttng_unlinked_file_pool_create(
+ const char *path)
+{
+ struct lttng_unlinked_file_pool *pool = zmalloc(sizeof(*pool));
+
+ if (!path || *path != '/') {
+ ERR("Unlinked file pool must be created with an absolute path, path = \"%s\"",
+ path ? path : "NULL");
+ goto error;
+ }
+
+ pool->unlink_directory_path = strdup(path);
+ if (!pool->unlink_directory_path) {
+ PERROR("Failed to allocation unlinked file pool path");
+ goto error;
+ }
+ DBG("Unlinked file pool created at: %s", path);
+ return pool;
+error:
+ lttng_unlinked_file_pool_destroy(pool);
+ return NULL;
+}
+
+void lttng_unlinked_file_pool_destroy(
+ struct lttng_unlinked_file_pool *pool)
+{
+ if (!pool) {
+ return;
+ }
+
+ assert(pool->file_count == 0);
+ lttng_directory_handle_put(pool->unlink_directory_handle);
+ free(pool->unlink_directory_path);
+ free(pool);
+}
+
void lttng_inode_put(struct lttng_inode *inode)
{
urcu_ref_put(&inode->ref, lttng_inode_release);
}
-const char *lttng_inode_get_path(const struct lttng_inode *inode)
+void lttng_inode_get_location(struct lttng_inode *inode,
+ const struct lttng_directory_handle **out_directory_handle,
+ const char **out_path)
{
- return inode->path;
+ if (out_directory_handle) {
+ *out_directory_handle = inode->location.directory_handle;
+ }
+ if (out_path) {
+ *out_path = inode->location.path;
+ }
}
int lttng_inode_rename(
- struct lttng_inode *inode, const char *new_path, bool overwrite)
+ struct lttng_inode *inode,
+ struct lttng_directory_handle *old_directory_handle,
+ const char *old_path,
+ struct lttng_directory_handle *new_directory_handle,
+ const char *new_path,
+ bool overwrite)
{
int ret = 0;
- char *new_path_copy = NULL;
+ char *new_path_copy = strdup(new_path);
+ bool reference_acquired;
+
+ DBG("Performing rename of inode from %s to %s with %s directory handles",
+ old_path, new_path,
+ lttng_directory_handle_equals(old_directory_handle,
+ new_directory_handle) ?
+ "identical" :
+ "different");
+
+ if (!new_path_copy) {
+ ret = -ENOMEM;
+ goto end;
+ }
if (inode->unlink_pending) {
- WARN("An attempt to rename an unlinked file, %s to %s, has been performed",
- inode->path, new_path);
+ WARN("An attempt to rename an unlinked file from %s to %s has been performed",
+ old_path, new_path);
ret = -ENOENT;
goto end;
}
if (!overwrite) {
+ /* Verify that file doesn't exist. */
struct stat statbuf;
- ret = stat(new_path, &statbuf);
+ ret = lttng_directory_handle_stat(
+ new_directory_handle, new_path, &statbuf);
if (ret == 0) {
+ ERR("Refusing to rename %s as the destination already exists",
+ old_path);
ret = -EEXIST;
goto end;
} else if (ret < 0 && errno != ENOENT) {
}
}
- new_path_copy = strdup(new_path);
- if (!new_path_copy) {
- ERR("Failed to allocate storage for path %s", new_path);
- ret = -ENOMEM;
- goto end;
- }
-
- ret = rename(inode->path, new_path);
+ ret = lttng_directory_handle_rename(old_directory_handle, old_path,
+ new_directory_handle, new_path);
if (ret) {
- PERROR("Failed to rename %s to %s", inode->path, new_path);
+ PERROR("Failed to rename file %s to %s", old_path, new_path);
ret = -errno;
goto end;
}
- free(inode->path);
- inode->path = new_path_copy;
+ reference_acquired = lttng_directory_handle_get(new_directory_handle);
+ assert(reference_acquired);
+ lttng_directory_handle_put(inode->location.directory_handle);
+ free(inode->location.path);
+ inode->location.directory_handle = new_directory_handle;
+ /* Ownership transferred. */
+ inode->location.path = new_path_copy;
new_path_copy = NULL;
end:
free(new_path_copy);
return ret;
}
-int lttng_inode_defer_unlink(struct lttng_inode *inode)
+int lttng_inode_unlink(struct lttng_inode *inode)
{
int ret = 0;
- uint16_t i = 0;
- char suffix[sizeof("-deleted-65535")] = "-deleted";
- char new_path[LTTNG_PATH_MAX];
- size_t original_path_len = strlen(inode->path);
+
+ DBG("Attempting unlink of inode %s", inode->location.path);
if (inode->unlink_pending) {
WARN("An attempt to re-unlink %s has been performed, ignoring.",
- inode->path);
+ inode->location.path);
ret = -ENOENT;
goto end;
}
- ret = lttng_strncpy(new_path, inode->path, sizeof(new_path));
- if (ret < 0) {
- ret = -ENAMETOOLONG;
+ /*
+ * Move to the temporary "deleted" directory until all
+ * references are released.
+ */
+ ret = lttng_unlinked_file_pool_add_inode(
+ inode->unlinked_file_pool, inode);
+ if (ret) {
+ PERROR("Failed to add inode \"%s\" to the unlinked file pool",
+ inode->location.path);
goto end;
}
-
- for (i = 0; i < UINT16_MAX; i++) {
- int p_ret;
-
- if (i != 0) {
- p_ret = snprintf(suffix, sizeof(suffix),
- "-deleted-%" PRIu16, i);
-
- if (p_ret < 0) {
- PERROR("Failed to form suffix to rename file %s",
- inode->path);
- ret = -errno;
- goto end;
- }
- assert(p_ret != sizeof(suffix));
- } else {
- /* suffix is initialy set to '-deleted'. */
- p_ret = strlen(suffix);
- }
-
- if (original_path_len + p_ret + 1 >= sizeof(new_path)) {
- ret = -ENAMETOOLONG;
- goto end;
- }
-
- strcat(&new_path[original_path_len], suffix);
- ret = lttng_inode_rename(inode, new_path, false);
- if (ret != -EEXIST) {
- break;
- }
- new_path[original_path_len] = '\0';
- }
- if (!ret) {
- inode->unlink_pending = true;
- }
+ inode->unlink_pending = true;
end:
return ret;
}
static struct lttng_inode *lttng_inode_create(const struct inode_id *id,
- const char *path,
- struct cds_lfht *ht)
+ struct cds_lfht *ht,
+ struct lttng_unlinked_file_pool *unlinked_file_pool,
+ struct lttng_directory_handle *directory_handle,
+ const char *path)
{
- struct lttng_inode *inode = zmalloc(sizeof(*inode));
+ struct lttng_inode *inode = NULL;
+ char *path_copy;
+ bool reference_acquired;
+
+ path_copy = strdup(path);
+ if (!path_copy) {
+ goto end;
+ }
+ reference_acquired = lttng_directory_handle_get(directory_handle);
+ assert(reference_acquired);
+
+ inode = zmalloc(sizeof(*inode));
if (!inode) {
goto end;
}
urcu_ref_init(&inode->ref);
cds_lfht_node_init(&inode->registry_node);
inode->id = *id;
- inode->path = strdup(path);
inode->registry_ht = ht;
- if (!inode->path) {
- goto error;
- }
+ inode->unlinked_file_pool = unlinked_file_pool;
+ /* Ownership of path copy is transferred to inode. */
+ inode->location.path = path_copy;
+ path_copy = NULL;
+ inode->location.directory_handle = directory_handle;
end:
+ free(path_copy);
return inode;
-error:
- lttng_inode_destroy(inode);
- return NULL;
}
struct lttng_inode_registry *lttng_inode_registry_create(void)
}
struct lttng_inode *lttng_inode_registry_get_inode(
- struct lttng_inode_registry *registry, int fd, const char *path)
+ struct lttng_inode_registry *registry,
+ struct lttng_directory_handle *handle,
+ const char *path,
+ int fd,
+ struct lttng_unlinked_file_pool *unlinked_file_pool)
{
int ret;
struct stat statbuf;
ret = fstat(fd, &statbuf);
if (ret < 0) {
- PERROR("stat() failed on file %s, fd = %i", path, fd);
+ PERROR("stat() failed on fd %i", fd);
goto end;
}
if (node) {
inode = caa_container_of(
node, struct lttng_inode, registry_node);
- /* Renames should happen through the fs-handle interface. */
- assert(!strcmp(path, inode->path));
lttng_inode_get(inode);
goto end_unlock;
}
- inode = lttng_inode_create(&id, path, registry->inodes);
+ inode = lttng_inode_create(&id, registry->inodes, unlinked_file_pool,
+ handle, path);
node = cds_lfht_add_unique(registry->inodes,
lttng_inode_id_hash(&inode->id), lttng_inode_match,
&inode->id, &inode->registry_node);
/*
- * Copyright (C) 2018 - Jérémie Galarneau <jeremie.galarneau@efficios.com>
+ * Copyright (C) 2020 - Jérémie Galarneau <jeremie.galarneau@efficios.com>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License, version 2 only, as
#ifndef FD_TRACKER_INODE_H
#define FD_TRACKER_INODE_H
+#include <common/compat/directory-handle.h>
#include <stdbool.h>
struct lttng_inode;
struct lttng_inode_registry;
+struct lttng_unlinked_file_directory;
+
+/*
+ * The unlinked file pool is protected by the fd-tracker's lock.
+ *
+ * NOTE: the unlinked file pool can use a single file desriptor when unlinked
+ * files are present in the pool. This file descriptor is not accounted-for
+ * by the fd-tracker. Users of the fd-tracker should account for this extra
+ * file descriptor.
+ */
+struct lttng_unlinked_file_pool *lttng_unlinked_file_pool_create(
+ const char *path);
+
+void lttng_unlinked_file_pool_destroy(
+ struct lttng_unlinked_file_pool *pool);
/* The inode registry is protected by the fd-tracker's lock. */
struct lttng_inode_registry *lttng_inode_registry_create(void);
struct lttng_inode *lttng_inode_registry_get_inode(
struct lttng_inode_registry *registry,
+ struct lttng_directory_handle *handle,
+ const char *path,
int fd,
- const char *path);
+ struct lttng_unlinked_file_pool *pool);
void lttng_inode_registry_destroy(struct lttng_inode_registry *registry);
-const char *lttng_inode_get_path(const struct lttng_inode *inode);
+void lttng_inode_get_location(struct lttng_inode *inode,
+ const struct lttng_directory_handle **out_directory_handle,
+ const char **out_path);
+
int lttng_inode_rename(struct lttng_inode *inode,
+ struct lttng_directory_handle *old_directory_handle,
+ const char *old_path,
+ struct lttng_directory_handle *new_directory_handle,
const char *new_path,
bool overwrite);
-int lttng_inode_defer_unlink(struct lttng_inode *inode);
+
+int lttng_inode_unlink(struct lttng_inode *inode);
+
void lttng_inode_put(struct lttng_inode *inode);
#endif /* FD_TRACKER_INODE_H */
#include <urcu.h>
-#include <common/fd-tracker/fd-tracker.h>
+#include <common/compat/directory-handle.h>
#include <common/error.h>
+#include <common/fd-tracker/fd-tracker.h>
/* For error.h */
int lttng_opt_quiet = 1;
int lttng_opt_mi;
/* Number of TAP tests in this file */
-#define NUM_TESTS 49
+#define NUM_TESTS 61
/* 3 for stdin, stdout, and stderr */
#define STDIO_FD_COUNT 3
#define TRACKER_FD_LIMIT 50
#define TMP_DIR_PATTERN "/tmp/fd-tracker-XXXXXX"
+#define TEST_UNLINK_DIRECTORY_NAME "unlinked_files"
/*
* Count of fds, beyond stdin, stderr, stdout that were open
"Landjaeger tri-tip salami leberkas ball tip, ham hock chuck sausage "
"flank jerky cupim. Pig bacon chuck pancetta andouille.";
+void get_temporary_directories(char **_test_directory, char **_unlink_directory)
+{
+ int ret;
+ char tmp_path_pattern[] = TMP_DIR_PATTERN;
+ char *output_dir;
+
+ output_dir = mkdtemp(tmp_path_pattern);
+ if (!output_dir) {
+ diag("Failed to create temporary path of the form %s",
+ TMP_DIR_PATTERN);
+ assert(0);
+ }
+
+ *_test_directory = strdup(output_dir);
+ assert(*_test_directory);
+ ret = asprintf(_unlink_directory, "%s/%s", output_dir,
+ TEST_UNLINK_DIRECTORY_NAME);
+ if (ret < 0) {
+ assert(0);
+ }
+}
+
int fd_count(void)
{
DIR *dir;
static
void test_unsuspendable_basic(void)
{
+ int ret;
struct fd_tracker *tracker;
+ char *test_directory = NULL, *unlinked_files_directory = NULL;
+
+ get_temporary_directories(&test_directory, &unlinked_files_directory);
- tracker = fd_tracker_create(TRACKER_FD_LIMIT);
+ tracker = fd_tracker_create(unlinked_files_directory, TRACKER_FD_LIMIT);
ok(tracker, "Created an fd tracker with a limit of %d simulateously opened file descriptors",
TRACKER_FD_LIMIT);
if (!tracker) {
untrack_std_fds(tracker);
fd_tracker_destroy(tracker);
+ ret = rmdir(test_directory);
+ ok(ret == 0, "Test directory is empty");
+ free(test_directory);
+ free(unlinked_files_directory);
}
static
int ret, stdout_fd = fileno(stdout), out_fd = 42;
struct fd_tracker *tracker;
int expected_error = -ENETDOWN;
+ char *test_directory = NULL, *unlinked_files_directory = NULL;
- tracker = fd_tracker_create(TRACKER_FD_LIMIT);
+ get_temporary_directories(&test_directory, &unlinked_files_directory);
+
+ tracker = fd_tracker_create(test_directory, TRACKER_FD_LIMIT);
assert(tracker);
/* The error_open callback should fail and return 'expected_error'. */
assert(!ret);
fd_tracker_destroy(tracker);
+ ret = rmdir(test_directory);
+ ok(ret == 0, "Test directory is empty");
+ free(test_directory);
+ free(unlinked_files_directory);
}
/*
{
int ret, stdout_fd = fileno(stdout), out_fd;
struct fd_tracker *tracker;
+ char *test_directory = NULL, *unlinked_files_directory = NULL;
+
+ get_temporary_directories(&test_directory, &unlinked_files_directory);
- tracker = fd_tracker_create(TRACKER_FD_LIMIT);
+ tracker = fd_tracker_create(unlinked_files_directory, TRACKER_FD_LIMIT);
assert(tracker);
ret = fd_tracker_open_unsuspendable_fd(tracker, &out_fd,
assert(!ret);
fd_tracker_destroy(tracker);
+ ret = rmdir(test_directory);
+ ok(ret == 0, "Test directory is empty");
+ free(test_directory);
+ free(unlinked_files_directory);
}
static
struct fd_tracker *tracker;
int ret, stdout_fd = fileno(stdout), out_fd;
int fds[TRACKER_FD_LIMIT];
+ char *test_directory = NULL, *unlinked_files_directory = NULL;
+
+ get_temporary_directories(&test_directory, &unlinked_files_directory);
/* This test assumes TRACKER_FD_LIMIT is a multiple of 2. */
assert((TRACKER_FD_LIMIT % 2 == 0) && TRACKER_FD_LIMIT);
- tracker = fd_tracker_create(TRACKER_FD_LIMIT);
+ tracker = fd_tracker_create(unlinked_files_directory, TRACKER_FD_LIMIT);
assert(tracker);
ret = fd_tracker_open_unsuspendable_fd(tracker, fds,
assert(!ret);
fd_tracker_destroy(tracker);
+ ret = rmdir(test_directory);
+ ok(ret == 0, "Test directory is empty");
+ free(test_directory);
+ free(unlinked_files_directory);
}
/*
{
int ret, stdout_fd = fileno(stdout), unknown_fds[2], out_fd;
struct fd_tracker *tracker;
+ char *test_directory = NULL, *unlinked_files_directory = NULL;
- tracker = fd_tracker_create(TRACKER_FD_LIMIT);
+ get_temporary_directories(&test_directory, &unlinked_files_directory);
+
+ tracker = fd_tracker_create(unlinked_files_directory, TRACKER_FD_LIMIT);
if (!tracker) {
return;
}
assert(!ret);
fd_tracker_destroy(tracker);
+ ret = rmdir(test_directory);
+ ok(ret == 0, "Test directory is empty");
+ free(test_directory);
+ free(unlinked_files_directory);
}
-static
-int open_files(struct fd_tracker *tracker, const char *dir, unsigned int count,
- struct fs_handle **handles, char **file_paths)
+static int open_files(struct fd_tracker *tracker,
+ struct lttng_directory_handle *directory,
+ unsigned int count,
+ struct fs_handle **handles,
+ char **file_paths)
{
int ret = 0;
unsigned int i;
struct fs_handle *handle;
mode_t mode = S_IWUSR | S_IRUSR;
- p_ret = asprintf(&file_path, "%s/file-%u", dir, i);
+ p_ret = asprintf(&file_path, "file-%u", i);
assert(p_ret >= 0);
file_paths[i] = file_path;
- handle = fd_tracker_open_fs_handle(tracker, file_path,
+ handle = fd_tracker_open_fs_handle(tracker, directory, file_path,
O_RDWR | O_CREAT, &mode);
if (!handle) {
ret = -1;
return ret;
}
-static
-int open_same_file(struct fd_tracker *tracker, const char *file_path,
- unsigned int count, struct fs_handle **handles)
+static int open_same_file(struct fd_tracker *tracker,
+ struct lttng_directory_handle *directory,
+ const char *file,
+ unsigned int count,
+ struct fs_handle **handles)
{
int ret = 0;
unsigned int i;
struct fs_handle *handle;
mode_t mode = S_IWUSR | S_IRUSR;
- handle = fd_tracker_open_fs_handle(tracker, file_path,
+ handle = fd_tracker_open_fs_handle(tracker, directory, file,
O_RDWR | O_CREAT, &mode);
if (!handle) {
ret = -1;
if (!file_path) {
break;
}
- (void) unlink(file_path);
- free(file_path);
+ if (fs_handle_unlink(handles[i])) {
+ diag("Failed to unlink fs_handle to file %s", file_path);
+ ret = -1;
+ }
if (fs_handle_close(handles[i])) {
+ diag("Failed to close fs_handle to file %s", file_path);
ret = -1;
}
+ free(file_path);
}
return ret;
}
int ret;
const int files_to_create = TRACKER_FD_LIMIT * 10;
struct fd_tracker *tracker;
- char tmp_path_pattern[] = TMP_DIR_PATTERN;
- const char *output_dir;
+ char *test_directory = NULL, *unlinked_files_directory = NULL;
char *output_files[files_to_create];
struct fs_handle *handles[files_to_create];
+ struct lttng_directory_handle *dir_handle = NULL;
+ int dir_handle_fd_count;
memset(output_files, 0, sizeof(output_files));
memset(handles, 0, sizeof(handles));
- tracker = fd_tracker_create(TRACKER_FD_LIMIT);
+ get_temporary_directories(&test_directory, &unlinked_files_directory);
+
+ tracker = fd_tracker_create(unlinked_files_directory, TRACKER_FD_LIMIT);
if (!tracker) {
return;
}
- output_dir = mkdtemp(tmp_path_pattern);
- if (!output_dir) {
- diag("Failed to create temporary path of the form %s",
- TMP_DIR_PATTERN);
- assert(0);
- }
+ dir_handle = lttng_directory_handle_create(test_directory);
+ assert(dir_handle);
+ dir_handle_fd_count = !!lttng_directory_handle_uses_fd(dir_handle);
- ret = open_files(tracker, output_dir, files_to_create, handles,
+ ret = open_files(tracker, dir_handle, files_to_create, handles,
output_files);
ok(!ret, "Created %d files with a limit of %d simultaneously-opened file descriptor",
files_to_create, TRACKER_FD_LIMIT);
- check_fd_count(TRACKER_FD_LIMIT + STDIO_FD_COUNT + unknown_fds_count);
+ check_fd_count(TRACKER_FD_LIMIT + STDIO_FD_COUNT + unknown_fds_count +
+ dir_handle_fd_count);
- ret = cleanup_files(tracker, output_dir, files_to_create, handles,
+ ret = cleanup_files(tracker, test_directory, files_to_create, handles,
output_files);
ok(!ret, "Close all opened filesystem handles");
- (void) rmdir(output_dir);
+ ret = rmdir(test_directory);
+ ok(ret == 0, "Test directory is empty");
fd_tracker_destroy(tracker);
+ lttng_directory_handle_put(dir_handle);
+ free(test_directory);
+ free(unlinked_files_directory);
}
static
int ret;
const int files_to_create = TRACKER_FD_LIMIT;
struct fd_tracker *tracker;
- char tmp_path_pattern[] = TMP_DIR_PATTERN;
- const char *output_dir;
+ char *test_directory = NULL, *unlinked_files_directory = NULL;
char *output_files[files_to_create];
struct fs_handle *handles[files_to_create];
+ struct lttng_directory_handle *dir_handle = NULL;
+ int dir_handle_fd_count;
memset(output_files, 0, sizeof(output_files));
memset(handles, 0, sizeof(handles));
- tracker = fd_tracker_create(TRACKER_FD_LIMIT);
+ get_temporary_directories(&test_directory, &unlinked_files_directory);
+
+ tracker = fd_tracker_create(unlinked_files_directory, TRACKER_FD_LIMIT);
if (!tracker) {
return;
}
- output_dir = mkdtemp(tmp_path_pattern);
- if (!output_dir) {
- diag("Failed to create temporary path of the form %s",
- TMP_DIR_PATTERN);
- assert(0);
- }
+ dir_handle = lttng_directory_handle_create(test_directory);
+ assert(dir_handle);
+ dir_handle_fd_count = !!lttng_directory_handle_uses_fd(dir_handle);
- ret = open_files(tracker, output_dir, files_to_create, handles,
+ ret = open_files(tracker, dir_handle, files_to_create, handles,
output_files);
ok(!ret, "Created %d files with a limit of %d simultaneously-opened file descriptor",
files_to_create, TRACKER_FD_LIMIT);
diag("Check file descriptor count after opening %u files", files_to_create);
- check_fd_count(TRACKER_FD_LIMIT + STDIO_FD_COUNT + unknown_fds_count);
+ check_fd_count(TRACKER_FD_LIMIT + STDIO_FD_COUNT + unknown_fds_count +
+ dir_handle_fd_count);
/*
* Open unsuspendable fds (stdin, stdout, stderr) and verify that the fd
diag("Check file descriptor count after adding %d unsuspendable fds",
STDIO_FD_COUNT);
track_std_fds(tracker);
- check_fd_count(TRACKER_FD_LIMIT + unknown_fds_count);
+ check_fd_count(TRACKER_FD_LIMIT + unknown_fds_count +
+ dir_handle_fd_count);
diag("Untrack unsuspendable file descriptors");
untrack_std_fds(tracker);
- check_fd_count(TRACKER_FD_LIMIT + unknown_fds_count);
+ check_fd_count(TRACKER_FD_LIMIT + unknown_fds_count +
+ dir_handle_fd_count);
- ret = cleanup_files(tracker, output_dir, files_to_create, handles,
+ ret = cleanup_files(tracker, test_directory, files_to_create, handles,
output_files);
ok(!ret, "Close all opened filesystem handles");
- (void) rmdir(output_dir);
+ ret = rmdir(test_directory);
+ ok(ret == 0, "Test directory is empty");
fd_tracker_destroy(tracker);
+ lttng_directory_handle_put(dir_handle);
+ free(test_directory);
+ free(unlinked_files_directory);
}
/*
int ret;
const int files_to_create = TRACKER_FD_LIMIT * 10;
struct fd_tracker *tracker;
- char tmp_path_pattern[] = TMP_DIR_PATTERN;
- const char *output_dir;
char *output_files[files_to_create];
struct fs_handle *handles[files_to_create];
size_t content_index;
bool write_success = true;
bool fd_cap_respected = true;
bool content_ok = true;
+ struct lttng_directory_handle *dir_handle = NULL;
+ int dir_handle_fd_count;
+ char *test_directory = NULL, *unlinked_files_directory = NULL;
memset(output_files, 0, sizeof(output_files));
memset(handles, 0, sizeof(handles));
- tracker = fd_tracker_create(TRACKER_FD_LIMIT);
+ get_temporary_directories(&test_directory, &unlinked_files_directory);
+
+ tracker = fd_tracker_create(unlinked_files_directory, TRACKER_FD_LIMIT);
if (!tracker) {
return;
}
- output_dir = mkdtemp(tmp_path_pattern);
- if (!output_dir) {
- diag("Failed to create temporary path of the form %s",
- TMP_DIR_PATTERN);
- assert(0);
- }
+ dir_handle = lttng_directory_handle_create(test_directory);
+ assert(dir_handle);
+ dir_handle_fd_count = !!lttng_directory_handle_uses_fd(dir_handle);
- ret = open_files(tracker, output_dir, files_to_create, handles,
+ ret = open_files(tracker, dir_handle, files_to_create, handles,
output_files);
ok(!ret, "Created %d files with a limit of %d simultaneously-opened file descriptor",
files_to_create, TRACKER_FD_LIMIT);
diag("Check file descriptor count after opening %u files", files_to_create);
- check_fd_count(TRACKER_FD_LIMIT + STDIO_FD_COUNT + unknown_fds_count);
+ check_fd_count(TRACKER_FD_LIMIT + STDIO_FD_COUNT + unknown_fds_count +
+ dir_handle_fd_count);
for (content_index = 0; content_index < sizeof(file_contents); content_index++) {
for (handle_index = 0; handle_index < files_to_create; handle_index++) {
goto skip_write;
}
- if (fd_count() >
- (TRACKER_FD_LIMIT + STDIO_FD_COUNT +
- unknown_fds_count)) {
+ if (fd_count() > (TRACKER_FD_LIMIT + STDIO_FD_COUNT +
+ unknown_fds_count +
+ dir_handle_fd_count)) {
fd_cap_respected = false;
}
- fs_handle_put_fd(handle);
+ fs_handle_put_fd(handle);
}
}
skip_write:
size_t to_read = sizeof(read_buf);
int fd;
- fd = open(path, O_RDONLY);
+ fd = lttng_directory_handle_open_file(
+ dir_handle, path, O_RDONLY, 0);
assert(fd >= 0);
ret = fstat(fd, &fd_stat);
assert(!ret);
(void) close(fd);
}
ok(content_ok, "Files contain the expected content");
- ret = cleanup_files(tracker, output_dir, files_to_create, handles,
+ ret = cleanup_files(tracker, test_directory, files_to_create, handles,
output_files);
ok(!ret, "Close all opened filesystem handles");
- (void) rmdir(output_dir);
- fd_tracker_destroy(tracker);
+ ret = rmdir(test_directory);
+ ok(ret == 0, "Test directory is empty");
+ fd_tracker_destroy(tracker);
+ lttng_directory_handle_put(dir_handle);
+ free(test_directory);
+ free(unlinked_files_directory);
}
static
int ret;
struct fd_tracker *tracker;
const int handles_to_open = 2;
- char tmp_path_pattern[] = TMP_DIR_PATTERN;
- const char *output_dir;
struct fs_handle *handles[handles_to_open];
struct fs_handle *new_handle = NULL;
- char *file_path;
- char *unlinked_file_path;
- char *unlinked_file_path_suffix;
struct stat statbuf;
-
- tracker = fd_tracker_create(1);
+ struct lttng_directory_handle *dir_handle = NULL;
+ const char file_name[] = "my_file";
+ char *test_directory = NULL, *unlinked_files_directory = NULL;
+ char *unlinked_file_zero = NULL, *unlinked_file_one = NULL;
+ int fd;
+
+ get_temporary_directories(&test_directory, &unlinked_files_directory);
+ ret = asprintf(&unlinked_file_zero, "%s/%u", unlinked_files_directory,
+ 0);
+ assert(ret > 0);
+ ret = asprintf(&unlinked_file_one, "%s/%u", unlinked_files_directory,
+ 1);
+ assert(ret > 0);
+
+ tracker = fd_tracker_create(unlinked_files_directory, 1);
if (!tracker) {
return;
}
- output_dir = mkdtemp(tmp_path_pattern);
- if (!output_dir) {
- diag("Failed to create temporary path of the form %s",
- TMP_DIR_PATTERN);
- return;
- }
- ret = asprintf(&file_path, "%s/my_file", output_dir);
- if (ret < 0) {
- diag("Failed to allocate path string");
- return;
- }
- ret = asprintf(&unlinked_file_path, "%s-deleted", file_path);
- if (ret < 0) {
- diag("Failed to allocate path string");
- return;
- }
- ret = asprintf(&unlinked_file_path_suffix, "%s-1", unlinked_file_path);
- if (ret < 0) {
- diag("Failed to allocate path string");
- return;
- }
+
+ dir_handle = lttng_directory_handle_create(test_directory);
+ assert(dir_handle);
/* Open two handles to the same file. */
- ret = open_same_file(tracker, file_path, handles_to_open, handles);
- ok(!ret, "Successfully opened %i handles to %s", handles_to_open,
- file_path);
+ ret = open_same_file(tracker, dir_handle, file_name, handles_to_open,
+ handles);
+ ok(!ret, "Successfully opened %i handles to %s/%s", handles_to_open,
+ test_directory, file_name);
if (ret) {
return;
}
/*
* Unlinking the first handle should cause the file to be renamed
- * to 'my_file-deleted'.
+ * to '0'.
*/
ret = fs_handle_unlink(handles[0]);
- ok(!ret, "Successfully unlinked the first handle to %s", file_path);
+ ok(!ret, "Successfully unlinked the first handle to %s/%s",
+ test_directory, file_name);
/*
* The original file should no longer exist on the file system, and a
- * new file, with the '-deleted' suffix should exist.
+ * new file named '0' should exist.
*/
- ok(stat(file_path, &statbuf) == -1 && errno == ENOENT, "%s no longer present on file system after unlink", file_path);
- ok(stat(unlinked_file_path, &statbuf) == 0, "%s exists on file system after unlink", unlinked_file_path);
+ ok(lttng_directory_handle_stat(dir_handle, file_name, &statbuf) == -1 &&
+ errno == ENOENT,
+ "%s no longer present on file system after unlink",
+ file_name);
+ ok(lttng_directory_handle_stat(
+ dir_handle, unlinked_file_zero, &statbuf) == 0,
+ "%s exists on file system after unlink",
+ unlinked_file_zero);
+
+ /*
+ * It should be possible to use the file descriptors of both handles.
+ * Since only one file descriptor can be opened at once, this should
+ * force the fd_tracker to suspend and restore the handles.
+ */
+ fd = fs_handle_get_fd(handles[0]);
+ ok(fd >= 0, "Got fd from first handle");
+
+ fd = fs_handle_get_fd(handles[1]);
+ ok (fd < 0, "fd tracker does not allow two fds to be used at once");
+
+ fs_handle_put_fd(handles[0]);
+ fd = fs_handle_get_fd(handles[1]);
+ ok(fd >= 0, "Got fd from second handle");
+ fs_handle_put_fd(handles[1]);
/* The second unlink should fail with -ENOENT. */
ret = fs_handle_unlink(handles[1]);
- ok(ret == -ENOENT, "ENOENT is reported when attempting to unlink the second handle to %s", file_path);
+ ok(ret == -ENOENT,
+ "ENOENT is reported when attempting to unlink the second handle to %s/%s",
+ test_directory, file_name);
/*
* Opening a new handle to 'my_file' should succeed.
*/
- ret = open_same_file(tracker, file_path, 1, &new_handle);
- ok(!ret, "Successfully opened a new handle to previously unlinked file %s", file_path);
+ ret = open_same_file(tracker, dir_handle, file_name, 1, &new_handle);
+ ok(!ret, "Successfully opened a new handle to previously unlinked file %s/%s",
+ test_directory, file_name);
assert(new_handle);
/*
* Unlinking the new handle should cause the file to be renamed
- * to 'my_file-deleted-1' since 'my_file-deleted' already exists.
+ * to '1' since '0' already exists.
*/
ret = fs_handle_unlink(new_handle);
- ok(!ret, "Successfully unlinked the new handle handle to %s", file_path);
- ok(stat(unlinked_file_path_suffix, &statbuf) == 0, "%s exists on file system after unlink", unlinked_file_path_suffix);
+ ok(!ret, "Successfully unlinked the new handle handle to %s/%s",
+ test_directory, file_name);
+ ok(stat(unlinked_file_one, &statbuf) == 0,
+ "%s exists on file system after unlink",
+ unlinked_file_one);
ret = fs_handle_close(handles[0]);
ok(!ret, "Successfully closed the first handle");
ret = fs_handle_close(new_handle);
ok(!ret, "Successfully closed the third handle");
- ok(stat(file_path, &statbuf) == -1 && errno == ENOENT, "%s no longer present on file system after handle close", file_path);
- ok(stat(unlinked_file_path, &statbuf) == -1 && errno == ENOENT, "%s no longer present on file system after handle close", unlinked_file_path);
- ok(stat(unlinked_file_path_suffix, &statbuf) == -1 && errno == ENOENT, "%s no longer present on file system after handle close", unlinked_file_path_suffix);
-
- (void) rmdir(output_dir);
+ ok(lttng_directory_handle_stat(dir_handle, file_name, &statbuf) == -1 &&
+ errno == ENOENT,
+ "%s no longer present on file system after handle close",
+ file_name);
+ ok(lttng_directory_handle_stat(
+ dir_handle, unlinked_file_zero, &statbuf) == -1 &&
+ errno == ENOENT,
+ "%s no longer present on file system after handle close",
+ unlinked_file_zero);
+ ok(lttng_directory_handle_stat(dir_handle, unlinked_file_one,
+ &statbuf) == -1 &&
+ errno == ENOENT,
+ "%s no longer present on file system after handle close",
+ unlinked_file_one);
+
+ ret = rmdir(test_directory);
+ ok(ret == 0, "Test directory is empty");
fd_tracker_destroy(tracker);
+ free(test_directory);
+ free(unlinked_files_directory);
+ free(unlinked_file_zero);
+ free(unlinked_file_one);
+ lttng_directory_handle_put(dir_handle);
}
int main(int argc, char **argv)
diag("Suspendable - Unlinking test");
test_unlink();
+ rcu_barrier();
rcu_unregister_thread();
return exit_status();
}