X-Git-Url: https://git.lttng.org/?p=lttng-tools.git;a=blobdiff_plain;f=src%2Fcommon%2Ffd-tracker%2Finode.c;h=16dd49a6ff2c95230efd1aba28fe2788d11c5722;hp=f02f0a812d67d516df48c7702b72f42f1be35ddb;hb=640b9481e1805d314256361ed49d55dcc35d6172;hpb=e0e72660f16b5cba852b9dc2dddd4ecaf96c42f1 diff --git a/src/common/fd-tracker/inode.c b/src/common/fd-tracker/inode.c index f02f0a812..16dd49a6f 100644 --- a/src/common/fd-tracker/inode.c +++ b/src/common/fd-tracker/inode.c @@ -1,32 +1,25 @@ /* - * 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 - * published by the Free Software Foundation. + * SPDX-License-Identifier: GPL-2.0-only * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., 51 - * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ -#include +#include +#include +#include +#include +#include +#include +#include +#include +#include #include +#include #include -#include #include -#include #include -#include -#include -#include -#include -#include +#include #include "inode.h" @@ -42,14 +35,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 { @@ -57,98 +65,291 @@ static struct { bool initialized; unsigned long value; } seed = { - .lock = PTHREAD_MUTEX_INITIALIZER, + .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; - return hash_key_u64(&device, seed.value) ^ - hash_key_u64(&inode_no, seed.value); + return hash_key_u64(&device, seed.value) ^ + hash_key_u64(&inode_no, seed.value); } -static -int lttng_inode_match(struct cds_lfht_node *node, const void *key) +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(node, struct lttng_inode, - registry_node); + 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); + struct lttng_inode *inode = + caa_container_of(head, struct lttng_inode, rcu_head); - free(inode->path); free(inode); } -static -void lttng_inode_destroy(struct lttng_inode *inode) +static int lttng_unlinked_file_pool_add_inode( + struct lttng_unlinked_file_pool *pool, + struct lttng_inode *inode) { - if (!inode) { - return; + 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); + if (!pool->unlink_directory_handle) { + ERR("Failed to create directory handle to unlinked file pool at %s", + pool->unlink_directory_path); + ret = -1; + goto end; + } } - if (inode->unlink_pending) { - int ret = unlink(inode->path); - DBG("Unlinking %s during lttng_inode destruction", inode->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) { - PERROR("Failed to unlink %s", inode->path); + /* + * 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(); - call_rcu(&inode->rcu_head, lttng_inode_delete); + + if (inode->unlink_pending) { + int ret; + + 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->location.path); + } + } + + 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) +static void lttng_inode_release(struct urcu_ref *ref) { - lttng_inode_destroy(caa_container_of(ref, struct lttng_inode, ref)); + lttng_inode_destroy(caa_container_of(ref, struct lttng_inode, ref)); } -static -void lttng_inode_get(struct lttng_inode *inode) +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) +struct lttng_directory_handle *lttng_inode_get_location_directory_handle( + struct lttng_inode *inode) +{ + if (inode->location.directory_handle) { + const bool reference_acquired = lttng_directory_handle_get( + inode->location.directory_handle); + + assert(reference_acquired); + } + return inode->location.directory_handle; +} + +void lttng_inode_borrow_location(struct lttng_inode *inode, + const struct lttng_directory_handle **out_directory_handle, + const char **out_path) { - 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) +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 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) { @@ -158,92 +359,75 @@ int lttng_inode_rename(struct lttng_inode *inode, const char *new_path, } } - 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) +static struct lttng_inode *lttng_inode_create(const struct inode_id *id, + 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; } @@ -251,16 +435,15 @@ 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) @@ -305,7 +488,10 @@ 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_directory_handle *handle, + const char *path, + int fd, + struct lttng_unlinked_file_pool *unlinked_file_pool) { int ret; struct stat statbuf; @@ -316,7 +502,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,26 +510,25 @@ struct lttng_inode *lttng_inode_registry_get_inode( id.inode = statbuf.st_ino; rcu_read_lock(); - cds_lfht_lookup(registry->inodes, - lttng_inode_id_hash(&id), - lttng_inode_match, - &id, - &iter); + cds_lfht_lookup(registry->inodes, lttng_inode_id_hash(&id), + lttng_inode_match, &id, &iter); node = cds_lfht_iter_get_node(&iter); 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)); + inode = caa_container_of( + node, struct lttng_inode, registry_node); 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); + if (!inode) { + goto end_unlock; + } + node = cds_lfht_add_unique(registry->inodes, - lttng_inode_id_hash(&inode->id), - lttng_inode_match, - &inode->id, - &inode->registry_node); + lttng_inode_id_hash(&inode->id), lttng_inode_match, + &inode->id, &inode->registry_node); assert(node == &inode->registry_node); end_unlock: rcu_read_unlock();