From 58ec725313d5fe2c4e4f686d6bfc980a08ea92b6 Mon Sep 17 00:00:00 2001 From: Mathieu Desnoyers Date: Mon, 12 Aug 2019 17:25:19 -0400 Subject: [PATCH] Fix: rmdir recursive: skip non-empty directories with flag MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Restore the behavior of LTTng 2.10 with a flag. Signed-off-by: Mathieu Desnoyers Signed-off-by: Jérémie Galarneau --- src/bin/lttng-sessiond/ust-registry.c | 3 +- src/common/compat/directory-handle.c | 194 ++++++++++++++++--------- src/common/compat/directory-handle.h | 12 +- src/common/runas.c | 10 +- src/common/runas.h | 4 +- src/common/ust-consumer/ust-consumer.c | 6 +- tests/unit/test_directory_handle.c | 2 +- 7 files changed, 154 insertions(+), 77 deletions(-) diff --git a/src/bin/lttng-sessiond/ust-registry.c b/src/bin/lttng-sessiond/ust-registry.c index a8db79ea6..7345bcbdd 100644 --- a/src/bin/lttng-sessiond/ust-registry.c +++ b/src/bin/lttng-sessiond/ust-registry.c @@ -1031,7 +1031,8 @@ void ust_registry_session_destroy(struct ust_registry_session *reg) * Try deleting the directory hierarchy. */ (void) run_as_rmdir_recursive(reg->root_shm_path, - reg->uid, reg->gid); + reg->uid, reg->gid, + LTTNG_DIRECTORY_HANDLE_SKIP_NON_EMPTY_FLAG); } /* Destroy the enum hash table */ if (reg->enums) { diff --git a/src/common/compat/directory-handle.c b/src/common/compat/directory-handle.c index d6fb7037b..4dad7b757 100644 --- a/src/common/compat/directory-handle.c +++ b/src/common/compat/directory-handle.c @@ -85,7 +85,7 @@ int _run_as_rmdir(const struct lttng_directory_handle *handle, static int _run_as_rmdir_recursive( const struct lttng_directory_handle *handle, const char *name, - uid_t uid, gid_t gid); + uid_t uid, gid_t gid, int flags); static void lttng_directory_handle_invalidate(struct lttng_directory_handle *handle); @@ -305,9 +305,9 @@ int _run_as_rmdir(const struct lttng_directory_handle *handle, static int _run_as_rmdir_recursive( const struct lttng_directory_handle *handle, const char *name, - uid_t uid, gid_t gid) + uid_t uid, gid_t gid, int flags) { - return run_as_rmdirat_recursive(handle->dirfd, name, uid, gid); + return run_as_rmdirat_recursive(handle->dirfd, name, uid, gid, flags); } #else /* COMPAT_DIRFD */ @@ -759,7 +759,7 @@ end: static int _run_as_rmdir_recursive( const struct lttng_directory_handle *handle, const char *name, - uid_t uid, gid_t gid) + uid_t uid, gid_t gid, int flags) { int ret; char fullpath[LTTNG_PATH_MAX]; @@ -770,7 +770,7 @@ int _run_as_rmdir_recursive( goto end; } - ret = run_as_rmdir_recursive(fullpath, uid, gid); + ret = run_as_rmdir_recursive(fullpath, uid, gid, flags); end: return ret; } @@ -1059,7 +1059,9 @@ int lttng_directory_handle_remove_subdirectory_as_user( } struct rmdir_frame { + ssize_t parent_frame_idx; DIR *dir; + bool empty; /* Size including '\0'. */ size_t path_size; }; @@ -1074,13 +1076,15 @@ void rmdir_frame_fini(void *data) static int remove_directory_recursive(const struct lttng_directory_handle *handle, - const char *path) + const char *path, int flags) { int ret; struct lttng_dynamic_array frames; size_t current_frame_idx = 0; struct rmdir_frame initial_frame = { + .parent_frame_idx = -1, .dir = lttng_directory_handle_opendir(handle, path), + .empty = true, .path_size = strlen(path) + 1, }; struct lttng_dynamic_buffer current_path; @@ -1088,7 +1092,27 @@ int remove_directory_recursive(const struct lttng_directory_handle *handle, lttng_dynamic_buffer_init(¤t_path); lttng_dynamic_array_init(&frames, sizeof(struct rmdir_frame), - rmdir_frame_fini); + rmdir_frame_fini); + + if (flags & ~(LTTNG_DIRECTORY_HANDLE_SKIP_NON_EMPTY_FLAG | + LTTNG_DIRECTORY_HANDLE_FAIL_NON_EMPTY_FLAG)) { + ERR("Unknown flags %d", flags); + ret = -1; + goto end; + } + + if (!initial_frame.dir) { + if (flags & LTTNG_DIRECTORY_HANDLE_SKIP_NON_EMPTY_FLAG && + errno == ENOENT) { + DBG("Cannot rmdir \"%s\": root does not exist", path); + ret = 0; + goto end; + } else { + PERROR("Failed to rmdir \"%s\"", path); + ret = -1; + goto end; + } + } ret = lttng_dynamic_array_add_element(&frames, &initial_frame); if (ret) { @@ -1097,42 +1121,37 @@ int remove_directory_recursive(const struct lttng_directory_handle *handle, goto end; } - ret = lttng_dynamic_buffer_append(¤t_path, path, - initial_frame.path_size); - if (ret) { + ret = lttng_dynamic_buffer_append( + ¤t_path, path, initial_frame.path_size); + if (ret) { ERR("Failed to set initial path during recursive directory removal"); ret = -1; goto end; - } + } - while (lttng_dynamic_array_get_count(&frames) > 0) { + while (lttng_dynamic_array_get_count(&frames) > 0) { struct dirent *entry; struct rmdir_frame *current_frame = - lttng_dynamic_array_get_element(&frames, - current_frame_idx); + lttng_dynamic_array_get_element( + &frames, current_frame_idx); - if (!current_frame->dir) { - PERROR("Failed to open directory stream during recursive directory removal"); - ret = -1; - goto end; - } - ret = lttng_dynamic_buffer_set_size(¤t_path, - current_frame->path_size); + assert(current_frame->dir); + ret = lttng_dynamic_buffer_set_size( + ¤t_path, current_frame->path_size); assert(!ret); current_path.data[current_path.size - 1] = '\0'; while ((entry = readdir(current_frame->dir))) { struct stat st; - struct rmdir_frame new_frame; - if (!strcmp(entry->d_name, ".") - || !strcmp(entry->d_name, "..")) { + if (!strcmp(entry->d_name, ".") || + !strcmp(entry->d_name, "..")) { continue; } /* Set current_path to the entry's path. */ - ret = lttng_dynamic_buffer_set_size(¤t_path, - current_path.size - 1); + ret = lttng_dynamic_buffer_set_size( + ¤t_path, current_path.size - 1); assert(!ret); ret = lttng_dynamic_buffer_append(¤t_path, &separator, sizeof(separator)); @@ -1146,8 +1165,12 @@ int remove_directory_recursive(const struct lttng_directory_handle *handle, goto end; } - if (lttng_directory_handle_stat(handle, - current_path.data, &st)) { + if (lttng_directory_handle_stat( + handle, current_path.data, &st)) { + if ((flags & LTTNG_DIRECTORY_HANDLE_SKIP_NON_EMPTY_FLAG) && + errno == ENOENT) { + break; + } PERROR("Failed to stat \"%s\"", current_path.data); ret = -1; @@ -1155,42 +1178,81 @@ int remove_directory_recursive(const struct lttng_directory_handle *handle, } if (!S_ISDIR(st.st_mode)) { - /* Not empty, abort. */ - DBG("Directory \"%s\" is not empty; refusing to remove directory", - current_path.data); - ret = -1; - goto end; + if (flags & LTTNG_DIRECTORY_HANDLE_SKIP_NON_EMPTY_FLAG) { + current_frame->empty = false; + break; + } else { + /* Not empty, abort. */ + DBG("Directory \"%s\" is not empty; refusing to remove directory", + current_path.data); + ret = -1; + goto end; + } + } else { + struct rmdir_frame new_frame = { + .path_size = current_path.size, + .dir = lttng_directory_handle_opendir( + handle, + current_path.data), + .empty = true, + .parent_frame_idx = current_frame_idx, + }; + + if (!new_frame.dir) { + if (flags & LTTNG_DIRECTORY_HANDLE_SKIP_NON_EMPTY_FLAG && + errno == ENOENT) { + DBG("Non-existing directory stream during recursive directory removal"); + break; + } else { + PERROR("Failed to open directory stream during recursive directory removal"); + ret = -1; + goto end; + } + } + ret = lttng_dynamic_array_add_element( + &frames, &new_frame); + if (ret) { + ERR("Failed to push context frame during recursive directory removal"); + rmdir_frame_fini(&new_frame); + goto end; + } + current_frame_idx++; + /* We break iteration on readdir. */ + break; } + } + if (entry) { + continue; + } - new_frame.path_size = current_path.size; - new_frame.dir = lttng_directory_handle_opendir(handle, - current_path.data); - ret = lttng_dynamic_array_add_element(&frames, - &new_frame); - if (ret) { - ERR("Failed to push context frame during recursive directory removal"); - rmdir_frame_fini(&new_frame); - goto end; - } - current_frame_idx++; - break; - } - if (!entry) { - ret = lttng_directory_handle_rmdir(handle, - current_path.data); - if (ret) { - PERROR("Failed to remove \"%s\" during recursive directory removal", - current_path.data); - goto end; - } - ret = lttng_dynamic_array_remove_element(&frames, - current_frame_idx); + /* Pop rmdir frame. */ + if (current_frame->empty) { + ret = lttng_directory_handle_rmdir( + handle, current_path.data); if (ret) { - ERR("Failed to pop context frame during recursive directory removal"); - goto end; + if ((flags & LTTNG_DIRECTORY_HANDLE_FAIL_NON_EMPTY_FLAG) || + errno != ENOENT) { + PERROR("Failed to remove \"%s\" during recursive directory removal", + current_path.data); + goto end; + } + DBG("Non-existing directory stream during recursive directory removal"); } - current_frame_idx--; - } + } else if (current_frame->parent_frame_idx >= 0) { + struct rmdir_frame *parent_frame; + + parent_frame = lttng_dynamic_array_get_element(&frames, + current_frame->parent_frame_idx); + assert(parent_frame); + parent_frame->empty = false; + } + ret = lttng_dynamic_array_remove_element( + &frames, current_frame_idx); + if (ret) { + ERR("Failed to pop context frame during recursive directory removal"); + goto end; + } + current_frame_idx--; } end: lttng_dynamic_array_reset(&frames); @@ -1201,26 +1263,28 @@ end: LTTNG_HIDDEN int lttng_directory_handle_remove_subdirectory_recursive( const struct lttng_directory_handle *handle, - const char *name) + const char *name, + int flags) { return lttng_directory_handle_remove_subdirectory_recursive_as_user( - handle, name, NULL); + handle, name, NULL, flags); } LTTNG_HIDDEN int lttng_directory_handle_remove_subdirectory_recursive_as_user( const struct lttng_directory_handle *handle, const char *name, - const struct lttng_credentials *creds) + const struct lttng_credentials *creds, + int flags) { int ret; if (!creds) { /* Run as current user. */ - ret = remove_directory_recursive(handle, name); + ret = remove_directory_recursive(handle, name, flags); } else { ret = _run_as_rmdir_recursive(handle, name, creds->uid, - creds->gid); + creds->gid, flags); } return ret; } diff --git a/src/common/compat/directory-handle.h b/src/common/compat/directory-handle.h index 6f2df1ba1..26da76bcc 100644 --- a/src/common/compat/directory-handle.h +++ b/src/common/compat/directory-handle.h @@ -21,6 +21,11 @@ #include #include +enum lttng_directory_handle_rmdir_recursive_flags { + LTTNG_DIRECTORY_HANDLE_FAIL_NON_EMPTY_FLAG = (1U << 0), + LTTNG_DIRECTORY_HANDLE_SKIP_NON_EMPTY_FLAG = (1U << 1), +}; + /* * Some platforms, such as Solaris 10, do not support directory file descriptors * and their associated functions (*at(...)), which are defined in POSIX.2008. @@ -233,20 +238,23 @@ int lttng_directory_handle_remove_subdirectory_as_user( /* * Remove a subdirectory and remove its contents if it only * consists in empty directories. + * @flags: enum lttng_directory_handle_rmdir_recursive_flags */ LTTNG_HIDDEN int lttng_directory_handle_remove_subdirectory_recursive( const struct lttng_directory_handle *handle, - const char *name); + const char *name, int flags); /* * Remove a subdirectory and remove its contents if it only * consists in empty directories as a given user. + * @flags: enum lttng_directory_handle_rmdir_recursive_flags */ LTTNG_HIDDEN int lttng_directory_handle_remove_subdirectory_recursive_as_user( const struct lttng_directory_handle *handle, const char *name, - const struct lttng_credentials *creds); + const struct lttng_credentials *creds, + int flags); #endif /* _COMPAT_PATH_HANDLE_H */ diff --git a/src/common/runas.c b/src/common/runas.c index f8263793d..cddddb9ec 100644 --- a/src/common/runas.c +++ b/src/common/runas.c @@ -90,6 +90,7 @@ struct run_as_unlink_data { struct run_as_rmdir_data { int dirfd; char path[LTTNG_PATH_MAX]; + int flags; /* enum lttng_directory_handle_rmdir_recursive_flags */ } LTTNG_PACKED; struct run_as_extract_elf_symbol_offset_data { @@ -471,7 +472,7 @@ int _rmdir_recursive(struct run_as_data *data, struct run_as_ret *ret_value) data->u.rmdir.dirfd = -1; ret_value->u.ret = lttng_directory_handle_remove_subdirectory_recursive( - &handle, data->u.rmdir.path); + &handle, data->u.rmdir.path, data->u.rmdir.flags); ret_value->_errno = errno; ret_value->_error = (ret_value->u.ret) ? true : false; lttng_directory_handle_fini(&handle); @@ -1538,13 +1539,13 @@ error: } LTTNG_HIDDEN -int run_as_rmdir_recursive(const char *path, uid_t uid, gid_t gid) +int run_as_rmdir_recursive(const char *path, uid_t uid, gid_t gid, int flags) { - return run_as_rmdirat_recursive(AT_FDCWD, path, uid, gid); + return run_as_rmdirat_recursive(AT_FDCWD, path, uid, gid, flags); } LTTNG_HIDDEN -int run_as_rmdirat_recursive(int dirfd, const char *path, uid_t uid, gid_t gid) +int run_as_rmdirat_recursive(int dirfd, const char *path, uid_t uid, gid_t gid, int flags) { int ret; struct run_as_data data = {}; @@ -1559,6 +1560,7 @@ int run_as_rmdirat_recursive(int dirfd, const char *path, uid_t uid, gid_t gid) goto error; } data.u.rmdir.dirfd = dirfd; + data.u.rmdir.flags = flags; run_as(dirfd == AT_FDCWD ? RUN_AS_RMDIR_RECURSIVE : RUN_AS_RMDIRAT_RECURSIVE, &data, &run_as_ret, uid, gid); errno = run_as_ret._errno; diff --git a/src/common/runas.h b/src/common/runas.h index d3a9523bf..a6eb46f7e 100644 --- a/src/common/runas.h +++ b/src/common/runas.h @@ -62,11 +62,11 @@ int run_as_unlinkat(int dirfd, const char *filename, uid_t uid, gid_t gid); LTTNG_HIDDEN int run_as_rmdir(const char *path, uid_t uid, gid_t gid); LTTNG_HIDDEN -int run_as_rmdir_recursive(const char *path, uid_t uid, gid_t gid); +int run_as_rmdir_recursive(const char *path, uid_t uid, gid_t gid, int flags); LTTNG_HIDDEN int run_as_rmdirat(int dirfd, const char *path, uid_t uid, gid_t gid); LTTNG_HIDDEN -int run_as_rmdirat_recursive(int dirfd, const char *path, uid_t uid, gid_t gid); +int run_as_rmdirat_recursive(int dirfd, const char *path, uid_t uid, gid_t gid, int flags); LTTNG_HIDDEN int run_as_rename(const char *old, const char *new, uid_t uid, gid_t gid); LTTNG_HIDDEN diff --git a/src/common/ust-consumer/ust-consumer.c b/src/common/ust-consumer/ust-consumer.c index 0d61b866f..4a7efad3e 100644 --- a/src/common/ust-consumer/ust-consumer.c +++ b/src/common/ust-consumer/ust-consumer.c @@ -519,7 +519,8 @@ error_open: if (channel->root_shm_path[0]) { (void) run_as_rmdir_recursive(channel->root_shm_path, channel->buffer_credentials.value.uid, - channel->buffer_credentials.value.gid); + channel->buffer_credentials.value.gid, + LTTNG_DIRECTORY_HANDLE_SKIP_NON_EMPTY_FLAG); } free(stream_fds); error_alloc: @@ -2349,7 +2350,8 @@ void lttng_ustconsumer_free_channel(struct lttng_consumer_channel *chan) if (chan->root_shm_path[0]) { (void) run_as_rmdir_recursive(chan->root_shm_path, chan->buffer_credentials.value.uid, - chan->buffer_credentials.value.gid); + chan->buffer_credentials.value.gid, + LTTNG_DIRECTORY_HANDLE_SKIP_NON_EMPTY_FLAG); } free(chan->stream_fds); } diff --git a/tests/unit/test_directory_handle.c b/tests/unit/test_directory_handle.c index 13db3f4bb..6d3028a35 100644 --- a/tests/unit/test_directory_handle.c +++ b/tests/unit/test_directory_handle.c @@ -80,7 +80,7 @@ int main(int argc, char **argv) ok(dir_exists(created_dir), "Folder %s exists", created_dir); ret = lttng_directory_handle_remove_subdirectory_recursive( - &test_dir_handle, "a"); + &test_dir_handle, "a", LTTNG_DIRECTORY_HANDLE_FAIL_NON_EMPTY_FLAG); ok(ret == 0, "Recursively removed directory hierarchy %s by removing %s", DIR_HIERARCHY, "a"); end: -- 2.34.1