From: Jérémie Galarneau Date: Wed, 29 Jan 2020 04:53:49 +0000 (-0500) Subject: fd-tracker: restore suspended handles from their inode's path X-Git-Url: http://git.efficios.com/?p=lttng-tools.git;a=commitdiff_plain;h=f7c3ffd79ddcece895eb0de616001d549aced5fc fd-tracker: restore suspended handles from their inode's path In order to support session rotations and, to a lesser degree, the "clear" command, the fd-tracker internals (lttng_inode and fs_handle) need to recover from files being renamed while the handle to it is suspended. This refactor introduces a "location" to the lttng_inode which is updated anytime an unlink or rename is performed on an fs_handle. Keep in mind that multiple independent fs_handles can refer to the same lttng_inode (a unique tuple of device id and inode number). This location is used to restore the fs_handle whenever it is needed. Moreover, since the session rotation/clear operations sometimes rely on directories having been emptied after rename/unlink, the current scheme of renaming unlinked files to the form "filename-deleted-suffix" no longer works. The renaming scheme is replaced by a new "unlinked file pool", which is an hidden directory at the base of the output directory which contains the files which were unlinked to which references (fs_handles) are still being held. The API of the fd-tracker itself had to be changed slightly and the tests are adapted as a consequence. New tests targeting this new behaviour are also added. Signed-off-by: Jérémie Galarneau Change-Id: I9b1344da1966c85bdd6b51838507d3208e1d9a42 --- diff --git a/src/bin/lttng-relayd/main.c b/src/bin/lttng-relayd/main.c index 25a32a70b..63927bf9f 100644 --- a/src/bin/lttng-relayd/main.c +++ b/src/bin/lttng-relayd/main.c @@ -4109,6 +4109,7 @@ int main(int argc, char **argv) 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(); @@ -4199,7 +4200,23 @@ int main(int argc, char **argv) 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; diff --git a/src/common/defaults.h b/src/common/defaults.h index 56a7c9449..06d6bf7b7 100644 --- a/src/common/defaults.h +++ b/src/common/defaults.h @@ -371,6 +371,7 @@ #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 diff --git a/src/common/fd-tracker/fd-tracker.c b/src/common/fd-tracker/fd-tracker.c index e79351388..88f53c741 100644 --- a/src/common/fd-tracker/fd-tracker.c +++ b/src/common/fd-tracker/fd-tracker.c @@ -85,6 +85,9 @@ struct fd_tracker { 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 { @@ -143,7 +146,7 @@ static int match_fd(struct cds_lfht_node *node, const void *key); 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); @@ -163,13 +166,6 @@ static int fd_tracker_suspend_handles( 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) { @@ -224,7 +220,7 @@ static void fs_handle_tracked_log(struct fs_handle_tracked *handle) 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, @@ -241,9 +237,10 @@ static int fs_handle_tracked_suspend(struct fs_handle_tracked *handle) 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. */ @@ -251,7 +248,8 @@ static int fs_handle_tracked_suspend(struct fs_handle_tracked *handle) 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); @@ -297,11 +295,15 @@ end: 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); @@ -329,7 +331,7 @@ end: 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; @@ -341,9 +343,11 @@ static int open_from_properties( * 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 @@ -364,7 +368,8 @@ end: 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)); @@ -393,6 +398,12 @@ struct fd_tracker *fd_tracker_create(unsigned int capacity) 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: @@ -475,6 +486,7 @@ int fd_tracker_destroy(struct fd_tracker *tracker) } lttng_inode_registry_destroy(tracker->inode_registry); + lttng_unlinked_file_pool_destroy(tracker->unlinked_file_pool); pthread_mutex_destroy(&tracker->lock); free(tracker); end: @@ -482,6 +494,7 @@ 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) @@ -518,7 +531,13 @@ struct fs_handle *fd_tracker_open_fs_handle(struct fd_tracker *tracker, 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); @@ -527,7 +546,7 @@ struct fs_handle *fd_tracker_open_fs_handle(struct fd_tracker *tracker, 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; @@ -535,8 +554,9 @@ struct fs_handle *fd_tracker_open_fs_handle(struct fd_tracker *tracker, 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; @@ -568,6 +588,7 @@ static int fd_tracker_suspend_handles( 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, @@ -580,8 +601,9 @@ static int fd_tracker_suspend_handles( if (!ret) { left_to_close--; } + attempts_left--; - if (!left_to_close) { + if (left_to_close == 0 || attempts_left == 0) { break; } } @@ -858,7 +880,7 @@ static int fs_handle_tracked_unlink(struct fs_handle *_handle) 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; @@ -879,7 +901,7 @@ static int fs_handle_tracked_close(struct fs_handle *_handle) 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) { diff --git a/src/common/fd-tracker/fd-tracker.h b/src/common/fd-tracker/fd-tracker.h index d321181ae..2ed385a2a 100644 --- a/src/common/fd-tracker/fd-tracker.h +++ b/src/common/fd-tracker/fd-tracker.h @@ -18,6 +18,7 @@ #ifndef FD_TRACKER_H #define FD_TRACKER_H +#include #include #include @@ -52,8 +53,13 @@ typedef int (*fd_close_cb)(void *, int *in_fds); * 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); @@ -81,6 +87,7 @@ 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); diff --git a/src/common/fd-tracker/inode.c b/src/common/fd-tracker/inode.c index 2eab2d615..28825b106 100644 --- a/src/common/fd-tracker/inode.c +++ b/src/common/fd-tracker/inode.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018 - Jérémie Galarneau + * Copyright (C) 2020 - Jérémie Galarneau * * 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 @@ -19,6 +19,9 @@ #include #include #include +#include +#include +#include #include #include #include @@ -42,14 +45,29 @@ struct lttng_inode_registry { 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 { @@ -60,7 +78,7 @@ 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; @@ -71,38 +89,154 @@ static unsigned long lttng_inode_id_hash(struct inode_id *id) 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) @@ -115,34 +249,99 @@ static void lttng_inode_get(struct lttng_inode *inode) 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) { @@ -152,93 +351,75 @@ int lttng_inode_rename( } } - 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; } @@ -246,16 +427,15 @@ static struct lttng_inode *lttng_inode_create(const struct inode_id *id, 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) @@ -299,7 +479,11 @@ void lttng_inode_registry_destroy(struct lttng_inode_registry *registry) } 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; @@ -310,7 +494,7 @@ struct lttng_inode *lttng_inode_registry_get_inode( 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; } @@ -324,13 +508,12 @@ struct lttng_inode *lttng_inode_registry_get_inode( 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); diff --git a/src/common/fd-tracker/inode.h b/src/common/fd-tracker/inode.h index 41e947011..d3c0ed2bf 100644 --- a/src/common/fd-tracker/inode.h +++ b/src/common/fd-tracker/inode.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018 - Jérémie Galarneau + * Copyright (C) 2020 - Jérémie Galarneau * * 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 @@ -18,26 +18,52 @@ #ifndef FD_TRACKER_INODE_H #define FD_TRACKER_INODE_H +#include #include 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 */ diff --git a/tests/unit/test_fd_tracker.c b/tests/unit/test_fd_tracker.c index 94f54769d..99b0dae2c 100644 --- a/tests/unit/test_fd_tracker.c +++ b/tests/unit/test_fd_tracker.c @@ -33,8 +33,9 @@ #include -#include +#include #include +#include /* For error.h */ int lttng_opt_quiet = 1; @@ -42,11 +43,12 @@ int lttng_opt_verbose; 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 @@ -62,6 +64,28 @@ const char file_contents[] = "Bacon ipsum dolor amet jerky drumstick sirloin " "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; @@ -163,9 +187,13 @@ void untrack_std_fds(struct fd_tracker *tracker) 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) { @@ -176,6 +204,10 @@ void test_unsuspendable_basic(void) 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 @@ -200,8 +232,11 @@ void test_unsuspendable_cb_return(void) 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'. */ @@ -227,6 +262,10 @@ void test_unsuspendable_cb_return(void) assert(!ret); fd_tracker_destroy(tracker); + ret = rmdir(test_directory); + ok(ret == 0, "Test directory is empty"); + free(test_directory); + free(unlinked_files_directory); } /* @@ -238,8 +277,11 @@ void test_unsuspendable_duplicate(void) { 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, @@ -254,6 +296,10 @@ void test_unsuspendable_duplicate(void) 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 @@ -298,11 +344,14 @@ void test_unsuspendable_limit(void) 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, @@ -319,6 +368,10 @@ void test_unsuspendable_limit(void) assert(!ret); fd_tracker_destroy(tracker); + ret = rmdir(test_directory); + ok(ret == 0, "Test directory is empty"); + free(test_directory); + free(unlinked_files_directory); } /* @@ -330,8 +383,11 @@ void test_unsuspendable_close_untracked(void) { 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; } @@ -354,11 +410,17 @@ void test_unsuspendable_close_untracked(void) 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; @@ -369,11 +431,11 @@ int open_files(struct fd_tracker *tracker, const char *dir, unsigned int count, 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; @@ -384,9 +446,11 @@ int open_files(struct fd_tracker *tracker, const char *dir, unsigned int count, 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; @@ -395,7 +459,7 @@ int open_same_file(struct fd_tracker *tracker, const char *file_path, 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; @@ -420,11 +484,15 @@ int cleanup_files(struct fd_tracker *tracker, const char *dir, 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; } @@ -435,37 +503,42 @@ void test_suspendable_limit(void) 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 @@ -474,32 +547,33 @@ void test_mixed_limit(void) 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 @@ -508,16 +582,22 @@ void test_mixed_limit(void) 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); } /* @@ -534,8 +614,6 @@ void test_suspendable_restore(void) 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; @@ -543,28 +621,31 @@ void test_suspendable_restore(void) 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++) { @@ -590,13 +671,13 @@ void test_suspendable_restore(void) 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: @@ -613,7 +694,8 @@ 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); @@ -650,11 +732,15 @@ skip_write: (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 @@ -663,81 +749,101 @@ void test_unlink(void) 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"); @@ -746,12 +852,29 @@ void test_unlink(void) 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) @@ -786,6 +909,7 @@ int main(int argc, char **argv) diag("Suspendable - Unlinking test"); test_unlink(); + rcu_barrier(); rcu_unregister_thread(); return exit_status(); }