From 93bed9fe8f48c11b7bb1224db36d82404cea080d Mon Sep 17 00:00:00 2001 From: =?utf8?q?J=C3=A9r=C3=A9mie=20Galarneau?= Date: Fri, 28 Jun 2019 17:14:11 -0400 Subject: [PATCH] Add rmdirat and renameat to run-as commands MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Add support for the rmdirat and renameat commands to the run-as infrastructure. These commands use the directory_handle compatibility layer to provide rename and directory deletion relative to a (or multiple) directory file descriptors. The rmdirat name is used even though there are no rmdirat syscall (at least on Linux). The functionality that would be provided by rmdirat() (vs rmdir) is provided through unlinkat(..., AT_REMOVEDIR). Signed-off-by: Jérémie Galarneau --- .gitignore | 1 + src/bin/lttng-sessiond/session.c | 5 +- src/common/compat/directory-handle.c | 527 ++++++++++++++- src/common/compat/directory-handle.h | 20 +- src/common/dynamic-array.c | 50 +- src/common/dynamic-array.h | 40 +- src/common/runas.c | 926 ++++++++++++++++++--------- src/common/runas.h | 11 + src/common/trace-chunk.c | 4 +- src/common/utils.c | 77 +-- tests/unit/Makefile.am | 7 +- tests/unit/test_directory_handle.c | 95 +++ 12 files changed, 1321 insertions(+), 442 deletions(-) create mode 100644 tests/unit/test_directory_handle.c diff --git a/.gitignore b/.gitignore index 02159fa7b..13ca8751c 100644 --- a/.gitignore +++ b/.gitignore @@ -83,6 +83,7 @@ compile_commands.json /tests/unit/test_utils_parse_time_suffix /tests/unit/test_utils_expand_path /tests/unit/test_notification +/tests/unit/test_directory_handle kernel_all_events_basic kernel_event_basic ust_global_event_wildcard diff --git a/src/bin/lttng-sessiond/session.c b/src/bin/lttng-sessiond/session.c index 4a15c9a9a..8424762f7 100644 --- a/src/bin/lttng-sessiond/session.c +++ b/src/bin/lttng-sessiond/session.c @@ -772,7 +772,7 @@ void session_release(struct urcu_ref *ref) ksess = session->kernel_session; session_notify_destruction(session); - lttng_dynamic_array_reset(&session->destroy_notifiers, NULL); + lttng_dynamic_array_reset(&session->destroy_notifiers); if (session->current_trace_chunk) { ret = session_close_trace_chunk(session, session->current_trace_chunk); if (ret) { @@ -977,7 +977,8 @@ enum lttng_error_code session_create(const char *name, uid_t uid, gid_t gid, } lttng_dynamic_array_init(&new_session->destroy_notifiers, - sizeof(struct ltt_session_destroy_notifier_element)); + sizeof(struct ltt_session_destroy_notifier_element), + NULL); urcu_ref_init(&new_session->ref); pthread_mutex_init(&new_session->lock, NULL); diff --git a/src/common/compat/directory-handle.c b/src/common/compat/directory-handle.c index 80e9e118b..3f35f9165 100644 --- a/src/common/compat/directory-handle.c +++ b/src/common/compat/directory-handle.c @@ -21,12 +21,14 @@ #include #include #include +#include #include #include #include #include #include +#include /* * This compatibility layer shares a common "base" that is implemented @@ -61,6 +63,30 @@ static int _run_as_unlink(const struct lttng_directory_handle *handle, const char *filename, uid_t uid, gid_t gid); static +int _lttng_directory_handle_rename( + const struct lttng_directory_handle *old_handle, + const char *old_name, + const struct lttng_directory_handle *new_handle, + const char *new_name); +static +int _run_as_rename(const struct lttng_directory_handle *old_handle, + const char *old_name, + const struct lttng_directory_handle *new_handle, + const char *new_name, uid_t uid, gid_t gid); +static +DIR *lttng_directory_handle_opendir(const struct lttng_directory_handle *handle, + const char *path); +static +int lttng_directory_handle_rmdir( + const struct lttng_directory_handle *handle, const char *name); +static +int _run_as_rmdir(const struct lttng_directory_handle *handle, + const char *name, uid_t uid, gid_t gid); +static +int _run_as_rmdir_recursive( + const struct lttng_directory_handle *handle, const char *name, + uid_t uid, gid_t gid); +static void lttng_directory_handle_invalidate(struct lttng_directory_handle *handle); #ifdef COMPAT_DIRFD @@ -213,6 +239,76 @@ int _run_as_mkdir_recursive(const struct lttng_directory_handle *handle, return run_as_mkdirat_recursive(handle->dirfd, path, mode, uid, gid); } +static +int _lttng_directory_handle_rename( + const struct lttng_directory_handle *old_handle, + const char *old_name, + const struct lttng_directory_handle *new_handle, + const char *new_name) +{ + return renameat(old_handle->dirfd, old_name, + new_handle->dirfd, new_name); +} + +static +int _run_as_rename(const struct lttng_directory_handle *old_handle, + const char *old_name, + const struct lttng_directory_handle *new_handle, + const char *new_name, uid_t uid, gid_t gid) +{ + return run_as_renameat(old_handle->dirfd, old_name, new_handle->dirfd, + new_name, uid, gid); +} + +static +DIR *lttng_directory_handle_opendir(const struct lttng_directory_handle *handle, + const char *path) +{ + DIR *dir_stream = NULL; + int fd = openat(handle->dirfd, path, O_RDONLY); + + if (fd < 0) { + goto end; + } + + dir_stream = fdopendir(fd); + if (!dir_stream) { + int ret; + + PERROR("Failed to open directory stream"); + ret = close(fd); + if (ret) { + PERROR("Failed to close file descriptor to %s", path); + } + goto end; + } + +end: + return dir_stream; +} + +static +int lttng_directory_handle_rmdir( + const struct lttng_directory_handle *handle, const char *name) +{ + return unlinkat(handle->dirfd, name, AT_REMOVEDIR); +} + +static +int _run_as_rmdir(const struct lttng_directory_handle *handle, + const char *name, uid_t uid, gid_t gid) +{ + return run_as_rmdirat(handle->dirfd, name, uid, gid); +} + +static +int _run_as_rmdir_recursive( + const struct lttng_directory_handle *handle, const char *name, + uid_t uid, gid_t gid) +{ + return run_as_rmdirat_recursive(handle->dirfd, name, uid, gid); +} + #else /* COMPAT_DIRFD */ static @@ -220,20 +316,29 @@ int get_full_path(const struct lttng_directory_handle *handle, const char *subdirectory, char *fullpath, size_t size) { int ret; - - subdirectory = subdirectory ? : ""; - /* - * Don't include the base path if subdirectory is absolute. - * This is the same behaviour than mkdirat. - */ - ret = snprintf(fullpath, size, "%s%s", - *subdirectory != '/' ? handle->base_path : "", - subdirectory); + const bool subdirectory_is_absolute = + subdirectory && *subdirectory == '/'; + const char * const base = subdirectory_is_absolute ? + subdirectory : handle->base_path; + const char * const end = subdirectory && !subdirectory_is_absolute ? + subdirectory : NULL; + const size_t base_len = strlen(base); + const size_t end_len = end ? strlen(end) : 0; + const bool add_separator_slash = end && base[base_len - 1] != '/'; + const bool add_trailing_slash = end && end[end_len - 1] != '/'; + + ret = snprintf(fullpath, size, "%s%s%s%s", + base, + add_separator_slash ? "/" : "", + end ? end : "", + add_trailing_slash ? "/" : ""); if (ret == -1 || ret >= size) { ERR("Failed to format subdirectory from directory handle"); ret = -1; + goto end; } ret = 0; +end: return ret; } @@ -242,39 +347,32 @@ int lttng_directory_handle_init(struct lttng_directory_handle *handle, const char *path) { int ret; - const char *cwd; + const char *cwd = ""; size_t cwd_len, path_len; char cwd_buf[LTTNG_PATH_MAX] = {}; char handle_buf[LTTNG_PATH_MAX] = {}; - bool add_cwd_slash, add_trailing_slash; + bool add_cwd_slash = false, add_trailing_slash = false; const struct lttng_directory_handle cwd_handle = { .base_path = handle_buf, }; - if (path && *path == '/') { - /* - * Creation of an handle to an absolute path; no need to sample - * the cwd. - */ - goto create; - } path_len = path ? strlen(path) : 0; - - cwd = getcwd(cwd_buf, sizeof(cwd_buf)); - if (!cwd) { - PERROR("Failed to initialize directory handle, can't get current working directory"); - ret = -1; - goto end; - } - cwd_len = strlen(cwd); - if (cwd_len == 0) { - ERR("Failed to initialize directory handle, current working directory path has a length of 0"); - ret = -1; - goto end; - } - - add_cwd_slash = cwd[cwd_len - 1] != '/'; add_trailing_slash = path && path[path_len - 1] != '/'; + if (!path || (path && *path != '/')) { + cwd = getcwd(cwd_buf, sizeof(cwd_buf)); + if (!cwd) { + PERROR("Failed to initialize directory handle, can't get current working directory"); + ret = -1; + goto end; + } + cwd_len = strlen(cwd); + if (cwd_len == 0) { + ERR("Failed to initialize directory handle, current working directory path has a length of 0"); + ret = -1; + goto end; + } + add_cwd_slash = cwd[cwd_len - 1] != '/'; + } ret = snprintf(handle_buf, sizeof(handle_buf), "%s%s%s%s", cwd, @@ -285,7 +383,7 @@ int lttng_directory_handle_init(struct lttng_directory_handle *handle, ERR("Failed to initialize directory handle, failed to format directory path"); goto end; } -create: + ret = lttng_directory_handle_init_from_handle(handle, path, &cwd_handle); end: @@ -545,8 +643,141 @@ end: return ret; } +static +int _lttng_directory_handle_rename( + const struct lttng_directory_handle *old_handle, + const char *old_name, + const struct lttng_directory_handle *new_handle, + const char *new_name) +{ + int ret; + char old_fullpath[LTTNG_PATH_MAX]; + char new_fullpath[LTTNG_PATH_MAX]; + + ret = get_full_path(old_handle, old_name, old_fullpath, + sizeof(old_fullpath)); + if (ret) { + errno = ENOMEM; + goto end; + } + ret = get_full_path(new_handle, new_name, new_fullpath, + sizeof(new_fullpath)); + if (ret) { + errno = ENOMEM; + goto end; + } + + ret = rename(old_fullpath, new_fullpath); +end: + return ret; +} + +static +int _run_as_rename(const struct lttng_directory_handle *old_handle, + const char *old_name, + const struct lttng_directory_handle *new_handle, + const char *new_name, uid_t uid, gid_t gid) +{ + int ret; + char old_fullpath[LTTNG_PATH_MAX]; + char new_fullpath[LTTNG_PATH_MAX]; + + ret = get_full_path(old_handle, old_name, old_fullpath, + sizeof(old_fullpath)); + if (ret) { + errno = ENOMEM; + goto end; + } + ret = get_full_path(new_handle, new_name, new_fullpath, + sizeof(new_fullpath)); + if (ret) { + errno = ENOMEM; + goto end; + } + + ret = run_as_rename(old_fullpath, new_fullpath, uid, gid); +end: + return ret; +} + +static +DIR *lttng_directory_handle_opendir(const struct lttng_directory_handle *handle, + const char *path) +{ + int ret; + DIR *dir_stream = NULL; + char fullpath[LTTNG_PATH_MAX]; + + ret = get_full_path(handle, path, fullpath, sizeof(fullpath)); + if (ret) { + errno = ENOMEM; + goto end; + } + + dir_stream = opendir(fullpath); +end: + return dir_stream; +} + +static +int lttng_directory_handle_rmdir( + const struct lttng_directory_handle *handle, const char *name) +{ + int ret; + char fullpath[LTTNG_PATH_MAX]; + + ret = get_full_path(handle, name, fullpath, sizeof(fullpath)); + if (ret) { + errno = ENOMEM; + goto end; + } + + ret = rmdir(fullpath); +end: + return ret; +} + +static +int _run_as_rmdir(const struct lttng_directory_handle *handle, + const char *name, uid_t uid, gid_t gid) +{ + int ret; + char fullpath[LTTNG_PATH_MAX]; + + ret = get_full_path(handle, name, fullpath, sizeof(fullpath)); + if (ret) { + errno = ENOMEM; + goto end; + } + + ret = run_as_rmdir(fullpath, uid, gid); +end: + return ret; +} + +static +int _run_as_rmdir_recursive( + const struct lttng_directory_handle *handle, const char *name, + uid_t uid, gid_t gid) +{ + int ret; + char fullpath[LTTNG_PATH_MAX]; + + ret = get_full_path(handle, name, fullpath, sizeof(fullpath)); + if (ret) { + errno = ENOMEM; + goto end; + } + + ret = run_as_rmdir_recursive(fullpath, uid, gid); +end: + return ret; +} + #endif /* COMPAT_DIRFD */ +/* Common implementation. */ + /* * On some filesystems (e.g. nfs), mkdir will validate access rights before * checking for the existence of the path element. This means that on a setup @@ -574,6 +805,8 @@ int create_directory_check_exists(const struct lttng_directory_handle *handle, ret = -1; goto end; } + } else if (errno != ENOENT) { + goto end; } /* @@ -585,7 +818,6 @@ end: return ret; } -/* Common implementation. */ LTTNG_HIDDEN struct lttng_directory_handle lttng_directory_handle_move(struct lttng_directory_handle *original) @@ -766,3 +998,228 @@ int lttng_directory_handle_unlink_file( return lttng_directory_handle_unlink_file_as_user(handle, filename, NULL); } + +LTTNG_HIDDEN +int lttng_directory_handle_rename( + const struct lttng_directory_handle *old_handle, + const char *old_name, + const struct lttng_directory_handle *new_handle, + const char *new_name) +{ + return lttng_directory_handle_rename_as_user(old_handle, old_name, + new_handle, new_name, NULL); +} + +LTTNG_HIDDEN +int lttng_directory_handle_rename_as_user( + const struct lttng_directory_handle *old_handle, + const char *old_name, + const struct lttng_directory_handle *new_handle, + const char *new_name, + const struct lttng_credentials *creds) +{ + int ret; + + if (!creds) { + /* Run as current user. */ + ret = _lttng_directory_handle_rename(old_handle, + old_name, new_handle, new_name); + } else { + ret = _run_as_rename(old_handle, old_name, new_handle, + new_name, creds->uid, creds->gid); + } + return ret; +} + +LTTNG_HIDDEN +int lttng_directory_handle_remove_subdirectory( + const struct lttng_directory_handle *handle, + const char *name) +{ + return lttng_directory_handle_remove_subdirectory_as_user(handle, name, + NULL); +} + +LTTNG_HIDDEN +int lttng_directory_handle_remove_subdirectory_as_user( + const struct lttng_directory_handle *handle, + const char *name, + const struct lttng_credentials *creds) +{ + int ret; + + if (!creds) { + /* Run as current user. */ + ret = lttng_directory_handle_rmdir(handle, name); + } else { + ret = _run_as_rmdir(handle, name, creds->uid, creds->gid); + } + return ret; +} + +struct rmdir_frame { + DIR *dir; + /* Size including '\0'. */ + size_t path_size; +}; + +static +void rmdir_frame_fini(void *data) +{ + struct rmdir_frame *frame = data; + + closedir(frame->dir); +} + +static +int remove_directory_recursive(const struct lttng_directory_handle *handle, + const char *path) +{ + int ret; + struct lttng_dynamic_array frames; + size_t current_frame_idx = 0; + struct rmdir_frame initial_frame = { + .dir = lttng_directory_handle_opendir(handle, path), + .path_size = strlen(path) + 1, + }; + struct lttng_dynamic_buffer current_path; + const char separator = '/'; + + lttng_dynamic_buffer_init(¤t_path); + lttng_dynamic_array_init(&frames, sizeof(struct rmdir_frame), + rmdir_frame_fini); + + ret = lttng_dynamic_array_add_element(&frames, &initial_frame); + if (ret) { + ERR("Failed to push context frame during recursive directory removal"); + rmdir_frame_fini(&initial_frame); + goto end; + } + + 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) { + struct dirent *entry; + struct rmdir_frame *current_frame = + 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(!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, "..")) { + continue; + } + + /* Set current_path to the entry's path. */ + ret = lttng_dynamic_buffer_set_size(¤t_path, + current_path.size - 1); + assert(!ret); + ret = lttng_dynamic_buffer_append(¤t_path, + &separator, sizeof(separator)); + if (ret) { + goto end; + } + ret = lttng_dynamic_buffer_append(¤t_path, + entry->d_name, + strlen(entry->d_name) + 1); + if (ret) { + goto end; + } + + if (lttng_directory_handle_stat(handle, + current_path.data, &st)) { + PERROR("Failed to stat \"%s\"", + current_path.data); + ret = -1; + goto end; + } + + 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; + } + + 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); + if (ret) { + ERR("Failed to pop context frame during recursive directory removal"); + goto end; + } + current_frame_idx--; + } + } +end: + lttng_dynamic_array_reset(&frames); + lttng_dynamic_buffer_reset(¤t_path); + return ret; +} + +LTTNG_HIDDEN +int lttng_directory_handle_remove_subdirectory_recursive( + const struct lttng_directory_handle *handle, + const char *name) +{ + return lttng_directory_handle_remove_subdirectory_recursive_as_user( + handle, name, NULL); +} + +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) +{ + int ret; + + if (!creds) { + /* Run as current user. */ + ret = remove_directory_recursive(handle, name); + } else { + ret = _run_as_rmdir_recursive(handle, name, creds->uid, + creds->gid); + } + return ret; +} diff --git a/src/common/compat/directory-handle.h b/src/common/compat/directory-handle.h index f50cef674..cc5c4cdd6 100644 --- a/src/common/compat/directory-handle.h +++ b/src/common/compat/directory-handle.h @@ -179,33 +179,37 @@ int lttng_directory_handle_unlink_file_as_user( LTTNG_HIDDEN int lttng_directory_handle_rename( - const struct lttng_directory_handle *handle, - const char *old, const char *new); + const struct lttng_directory_handle *old_handle, + const char *old_name, + const struct lttng_directory_handle *new_handle, + const char *new_name); LTTNG_HIDDEN int lttng_directory_handle_rename_as_user( - const struct lttng_directory_handle *handle, - const char *old, const char *new, + const struct lttng_directory_handle *old_handle, + const char *old_name, + const struct lttng_directory_handle *new_handle, + const char *new_name, const struct lttng_credentials *creds); LTTNG_HIDDEN -int lttng_directory_handle_rmdir( +int lttng_directory_handle_remove_subdirectory( const struct lttng_directory_handle *handle, const char *name); LTTNG_HIDDEN -int lttng_directory_handle_rmdir_as_user( +int lttng_directory_handle_remove_subdirectory_as_user( const struct lttng_directory_handle *handle, const char *name, const struct lttng_credentials *creds); LTTNG_HIDDEN -int lttng_directory_handle_rmdir_recursive( +int lttng_directory_handle_remove_subdirectory_recursive( const struct lttng_directory_handle *handle, const char *name); LTTNG_HIDDEN -int lttng_directory_handle_rmdir_recursive_as_user( +int lttng_directory_handle_remove_subdirectory_recursive_as_user( const struct lttng_directory_handle *handle, const char *name, const struct lttng_credentials *creds); diff --git a/src/common/dynamic-array.c b/src/common/dynamic-array.c index 69c9c614d..ac2a6d5aa 100644 --- a/src/common/dynamic-array.c +++ b/src/common/dynamic-array.c @@ -19,10 +19,13 @@ LTTNG_HIDDEN void lttng_dynamic_array_init(struct lttng_dynamic_array *array, - size_t element_size) + size_t element_size, + lttng_dynamic_array_element_destructor destructor) { lttng_dynamic_buffer_init(&array->buffer); array->element_size = element_size; + array->size = 0; + array->destructor = destructor; } LTTNG_HIDDEN @@ -47,14 +50,36 @@ end: } LTTNG_HIDDEN -void lttng_dynamic_array_reset(struct lttng_dynamic_array *array, - lttng_dynamic_array_element_destructor destructor) +int lttng_dynamic_array_remove_element(struct lttng_dynamic_array *array, + size_t element_index) { - if (destructor) { + void *element = lttng_dynamic_array_get_element(array, + element_index); + + if (array->destructor) { + array->destructor(element); + } + if (element_index != lttng_dynamic_array_get_count(array) - 1) { + void *next_element = lttng_dynamic_array_get_element(array, + element_index + 1); + + memmove(element, next_element, + (array->size - element_index - 1) * array->element_size); + } + array->size--; + return lttng_dynamic_buffer_set_size(&array->buffer, + array->buffer.size - array->element_size); +} + +LTTNG_HIDDEN +void lttng_dynamic_array_reset(struct lttng_dynamic_array *array) +{ + if (array->destructor) { size_t i; for (i = 0; i < lttng_dynamic_array_get_count(array); i++) { - destructor(lttng_dynamic_array_get_element(array, i)); + array->destructor(lttng_dynamic_array_get_element(array, + i)); } } @@ -64,25 +89,26 @@ void lttng_dynamic_array_reset(struct lttng_dynamic_array *array, LTTNG_HIDDEN void lttng_dynamic_pointer_array_init( - struct lttng_dynamic_pointer_array *array) + struct lttng_dynamic_pointer_array *array, + lttng_dynamic_pointer_array_destructor destructor) { - lttng_dynamic_array_init(&array->array, sizeof(void *)); + lttng_dynamic_array_init(&array->array, sizeof(void *), destructor); } /* Release any memory used by the dynamic array. */ LTTNG_HIDDEN void lttng_dynamic_pointer_array_reset( - struct lttng_dynamic_pointer_array *array, - lttng_dynamic_pointer_array_destructor destructor) + struct lttng_dynamic_pointer_array *array) { - if (destructor) { + if (array->array.destructor) { size_t i, count = lttng_dynamic_pointer_array_get_count(array); for (i = 0; i < count; i++) { void *ptr = lttng_dynamic_pointer_array_get_pointer( array, i); - destructor(ptr); + array->array.destructor(ptr); } + array->array.destructor = NULL; } - lttng_dynamic_array_reset(&array->array, NULL); + lttng_dynamic_array_reset(&array->array); } diff --git a/src/common/dynamic-array.h b/src/common/dynamic-array.h index a5e4f7fb5..e97b51d5d 100644 --- a/src/common/dynamic-array.h +++ b/src/common/dynamic-array.h @@ -21,26 +21,28 @@ #include #include +typedef void (*lttng_dynamic_array_element_destructor)(void *element); +typedef void (*lttng_dynamic_pointer_array_destructor)(void *ptr); + struct lttng_dynamic_array { struct lttng_dynamic_buffer buffer; size_t element_size; size_t size; + lttng_dynamic_array_element_destructor destructor; }; struct lttng_dynamic_pointer_array { struct lttng_dynamic_array array; }; -typedef void (*lttng_dynamic_array_element_destructor)(void *element); -typedef void (*lttng_dynamic_pointer_array_destructor)(void *ptr); - /* * Initialize a resizable array of fixed-size elements. This performs no * allocation and can't fail. */ LTTNG_HIDDEN void lttng_dynamic_array_init(struct lttng_dynamic_array *array, - size_t element_size); + size_t element_size, + lttng_dynamic_array_element_destructor destructor); /* * Returns the number of elements in the dynamic array. @@ -74,10 +76,18 @@ LTTNG_HIDDEN int lttng_dynamic_array_add_element(struct lttng_dynamic_array *array, const void *element); +/* + * Remove an element from the dynamic array. The array's element count is + * decreased by one and the following elements are shifted to take its place + * (when applicable). + */ +LTTNG_HIDDEN +int lttng_dynamic_array_remove_element(struct lttng_dynamic_array *array, + size_t element_index); + /* Release any memory used by the dynamic array. */ LTTNG_HIDDEN -void lttng_dynamic_array_reset(struct lttng_dynamic_array *array, - lttng_dynamic_array_element_destructor destructor); +void lttng_dynamic_array_reset(struct lttng_dynamic_array *array); /* @@ -93,7 +103,8 @@ void lttng_dynamic_array_reset(struct lttng_dynamic_array *array, */ LTTNG_HIDDEN void lttng_dynamic_pointer_array_init( - struct lttng_dynamic_pointer_array *array); + struct lttng_dynamic_pointer_array *array, + lttng_dynamic_pointer_array_destructor destructor); /* * Returns the number of pointers in the dynamic pointer array. @@ -130,10 +141,21 @@ int lttng_dynamic_pointer_array_add_pointer( return lttng_dynamic_array_add_element(&array->array, &pointer); } +/* + * Remove a pointer from a dynamic pointer array. The array's element + * count is decreased by one and the following pointers are shifted to + * take the place of the removed pointer (if applicable). + */ +static inline +int lttng_dynamic_pointer_array_remove_pointer( + struct lttng_dynamic_pointer_array *array, size_t index) +{ + return lttng_dynamic_array_remove_element(&array->array, index); +} + /* Release any memory used by the dynamic array. */ LTTNG_HIDDEN void lttng_dynamic_pointer_array_reset( - struct lttng_dynamic_pointer_array *array, - lttng_dynamic_pointer_array_destructor destructor); + struct lttng_dynamic_pointer_array *array); #endif /* LTTNG_DYNAMIC_ARRAY_H */ diff --git a/src/common/runas.c b/src/common/runas.c index ef169d320..9f30996e2 100644 --- a/src/common/runas.c +++ b/src/common/runas.c @@ -1,6 +1,7 @@ /* * Copyright (C) 2011 - David Goulet * Mathieu Desnoyers + * 2019 - 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, @@ -49,87 +50,96 @@ struct run_as_data; struct run_as_ret; typedef int (*run_as_fct)(struct run_as_data *data, struct run_as_ret *ret_value); -struct run_as_mkdirat_data { - char path[PATH_MAX]; - mode_t mode; +enum run_as_cmd { + RUN_AS_MKDIR, + RUN_AS_MKDIRAT, + RUN_AS_MKDIR_RECURSIVE, + RUN_AS_MKDIRAT_RECURSIVE, + RUN_AS_OPEN, + RUN_AS_OPENAT, + RUN_AS_UNLINK, + RUN_AS_UNLINKAT, + RUN_AS_RMDIR, + RUN_AS_RMDIRAT, + RUN_AS_RMDIR_RECURSIVE, + RUN_AS_RMDIRAT_RECURSIVE, + RUN_AS_RENAME, + RUN_AS_RENAMEAT, + RUN_AS_EXTRACT_ELF_SYMBOL_OFFSET, + RUN_AS_EXTRACT_SDT_PROBE_OFFSETS, }; +struct run_as_mkdir_data { + int dirfd; + char path[LTTNG_PATH_MAX]; + mode_t mode; +} LTTNG_PACKED; + struct run_as_open_data { - char path[PATH_MAX]; + int dirfd; + char path[LTTNG_PATH_MAX]; int flags; mode_t mode; -}; +} LTTNG_PACKED; struct run_as_unlink_data { - char path[PATH_MAX]; -}; + int dirfd; + char path[LTTNG_PATH_MAX]; +} LTTNG_PACKED; -struct run_as_rmdir_recursive_data { - char path[PATH_MAX]; -}; +struct run_as_rmdir_data { + int dirfd; + char path[LTTNG_PATH_MAX]; +} LTTNG_PACKED; struct run_as_extract_elf_symbol_offset_data { + int fd; char function[LTTNG_SYMBOL_NAME_LEN]; -}; +} LTTNG_PACKED; struct run_as_extract_sdt_probe_offsets_data { + int fd; char probe_name[LTTNG_SYMBOL_NAME_LEN]; char provider_name[LTTNG_SYMBOL_NAME_LEN]; -}; +} LTTNG_PACKED; -struct run_as_mkdirat_ret { - int ret; -}; +struct run_as_rename_data { + /* + * [0] = old_dirfd + * [1] = new_dirfd + */ + int dirfds[2]; + char old_path[LTTNG_PATH_MAX]; + char new_path[LTTNG_PATH_MAX]; +} LTTNG_PACKED; struct run_as_open_ret { - int ret; -}; - -struct run_as_unlink_ret { - int ret; -}; - -struct run_as_rmdir_recursive_ret { - int ret; -}; + int fd; +} LTTNG_PACKED; struct run_as_extract_elf_symbol_offset_ret { uint64_t offset; -}; +} LTTNG_PACKED; struct run_as_extract_sdt_probe_offsets_ret { uint32_t num_offset; uint64_t offsets[LTTNG_KERNEL_MAX_UPROBE_NUM]; -}; - -enum run_as_cmd { - RUN_AS_MKDIR, - RUN_AS_MKDIRAT, - RUN_AS_MKDIR_RECURSIVE, - RUN_AS_MKDIRAT_RECURSIVE, - RUN_AS_OPEN, - RUN_AS_OPENAT, - RUN_AS_UNLINK, - RUN_AS_UNLINKAT, - RUN_AS_RMDIR_RECURSIVE, - RUN_AS_EXTRACT_ELF_SYMBOL_OFFSET, - RUN_AS_EXTRACT_SDT_PROBE_OFFSETS, -}; +} LTTNG_PACKED; struct run_as_data { enum run_as_cmd cmd; - int fd; union { - struct run_as_mkdirat_data mkdirat; + struct run_as_mkdir_data mkdir; struct run_as_open_data open; struct run_as_unlink_data unlink; - struct run_as_rmdir_recursive_data rmdir_recursive; + struct run_as_rmdir_data rmdir; + struct run_as_rename_data rename; struct run_as_extract_elf_symbol_offset_data extract_elf_symbol_offset; struct run_as_extract_sdt_probe_offsets_data extract_sdt_probe_offsets; } u; uid_t uid; gid_t gid; -}; +} LTTNG_PACKED; /* * The run_as_ret structure holds the returned value and status of the command. @@ -147,17 +157,164 @@ struct run_as_data { * */ struct run_as_ret { - int fd; union { - struct run_as_mkdirat_ret mkdirat; + int ret; struct run_as_open_ret open; - struct run_as_unlink_ret unlink; - struct run_as_rmdir_recursive_ret rmdir_recursive; struct run_as_extract_elf_symbol_offset_ret extract_elf_symbol_offset; struct run_as_extract_sdt_probe_offsets_ret extract_sdt_probe_offsets; } u; int _errno; bool _error; +} LTTNG_PACKED; + +#define COMMAND_IN_FDS(data_ptr) ({ \ + int *fds = NULL; \ + if (command_properties[data_ptr->cmd].in_fds_offset != -1) { \ + fds = (int *) ((char *) data_ptr + command_properties[data_ptr->cmd].in_fds_offset); \ + } \ + fds; \ +}) + +#define COMMAND_OUT_FDS(cmd, ret_ptr) ({ \ + int *fds = NULL; \ + if (command_properties[cmd].out_fds_offset != -1) { \ + fds = (int *) ((char *) ret_ptr + command_properties[cmd].out_fds_offset); \ + } \ + fds; \ +}) + +#define COMMAND_IN_FD_COUNT(data_ptr) ({ \ + command_properties[data_ptr->cmd].in_fd_count; \ +}) + +#define COMMAND_OUT_FD_COUNT(cmd) ({ \ + command_properties[cmd].out_fd_count; \ +}) + +#define COMMAND_USE_CWD_FD(data_ptr) command_properties[data_ptr->cmd].use_cwd_fd + +struct run_as_command_properties { + /* Set to -1 when not applicable. */ + ptrdiff_t in_fds_offset, out_fds_offset; + unsigned int in_fd_count, out_fd_count; + bool use_cwd_fd; +}; + +static const struct run_as_command_properties command_properties[] = { + [RUN_AS_MKDIR] = { + .in_fds_offset = offsetof(struct run_as_data, u.mkdir.dirfd), + .in_fd_count = 1, + .out_fds_offset = -1, + .out_fd_count = 0, + .use_cwd_fd = true, + }, + [RUN_AS_MKDIRAT] = { + .in_fds_offset = offsetof(struct run_as_data, u.mkdir.dirfd), + .in_fd_count = 1, + .out_fds_offset = -1, + .out_fd_count = 0, + .use_cwd_fd = false, + }, + [RUN_AS_MKDIR_RECURSIVE] = { + .in_fds_offset = offsetof(struct run_as_data, u.mkdir.dirfd), + .in_fd_count = 1, + .out_fds_offset = -1, + .out_fd_count = 0, + .use_cwd_fd = true, + }, + [RUN_AS_MKDIRAT_RECURSIVE] = { + .in_fds_offset = offsetof(struct run_as_data, u.mkdir.dirfd), + .in_fd_count = 1, + .out_fds_offset = -1, + .out_fd_count = 0, + .use_cwd_fd = false, + }, + [RUN_AS_OPEN] = { + .in_fds_offset = offsetof(struct run_as_data, u.open.dirfd), + .in_fd_count = 1, + .out_fds_offset = offsetof(struct run_as_ret, u.open.fd), + .out_fd_count = 1, + .use_cwd_fd = true, + }, + [RUN_AS_OPENAT] = { + .in_fds_offset = offsetof(struct run_as_data, u.open.dirfd), + .in_fd_count = 1, + .out_fds_offset = offsetof(struct run_as_ret, u.open.fd), + .out_fd_count = 1, + .use_cwd_fd = false, + }, + [RUN_AS_UNLINK] = { + .in_fds_offset = offsetof(struct run_as_data, u.unlink.dirfd), + .in_fd_count = 1, + .out_fds_offset = -1, + .out_fd_count = 0, + .use_cwd_fd = true, + }, + [RUN_AS_UNLINKAT] = { + .in_fds_offset = offsetof(struct run_as_data, u.unlink.dirfd), + .in_fd_count = 1, + .out_fds_offset = -1, + .out_fd_count = 0, + .use_cwd_fd = false, + }, + [RUN_AS_RMDIR_RECURSIVE] = { + .in_fds_offset = offsetof(struct run_as_data, u.rmdir.dirfd), + .in_fd_count = 1, + .out_fds_offset = -1, + .out_fd_count = 0, + .use_cwd_fd = true, + }, + [RUN_AS_RMDIRAT_RECURSIVE] = { + .in_fds_offset = offsetof(struct run_as_data, u.rmdir.dirfd), + .in_fd_count = 1, + .out_fds_offset = -1, + .out_fd_count = 0, + .use_cwd_fd = false, + }, + [RUN_AS_RMDIR] = { + .in_fds_offset = offsetof(struct run_as_data, u.rmdir.dirfd), + .in_fd_count = 1, + .out_fds_offset = -1, + .out_fd_count = 0, + .use_cwd_fd = true, + }, + [RUN_AS_RMDIRAT] = { + .in_fds_offset = offsetof(struct run_as_data, u.rmdir.dirfd), + .in_fd_count = 1, + .out_fds_offset = -1, + .out_fd_count = 0, + .use_cwd_fd = false, + }, + [RUN_AS_RENAME] = { + .in_fds_offset = offsetof(struct run_as_data, u.rename.dirfds), + .in_fd_count = 2, + .out_fds_offset = -1, + .out_fd_count = 0, + .use_cwd_fd = true, + }, + [RUN_AS_RENAMEAT] = { + .in_fds_offset = offsetof(struct run_as_data, u.rename.dirfds), + .in_fd_count = 2, + .out_fds_offset = -1, + .out_fd_count = 0, + .use_cwd_fd = false, + }, + [RUN_AS_EXTRACT_ELF_SYMBOL_OFFSET] = { + .in_fds_offset = offsetof(struct run_as_data, + u.extract_elf_symbol_offset.fd), + .in_fd_count = 1, + .out_fds_offset = -1, + .out_fd_count = 0, + .use_cwd_fd = false, + }, + [RUN_AS_EXTRACT_SDT_PROBE_OFFSETS] = { + .in_fds_offset = offsetof(struct run_as_data, + u.extract_sdt_probe_offsets.fd), + .in_fd_count = 1, + .out_fds_offset = -1, + .out_fd_count = 0, + .use_cwd_fd = false, + }, }; struct run_as_worker { @@ -195,20 +352,21 @@ int _mkdirat_recursive(struct run_as_data *data, struct run_as_ret *ret_value) mode_t mode; struct lttng_directory_handle handle; - path = data->u.mkdirat.path; - mode = data->u.mkdirat.mode; + path = data->u.mkdir.path; + mode = data->u.mkdir.mode; - (void) lttng_directory_handle_init_from_dirfd(&handle, data->fd); + (void) lttng_directory_handle_init_from_dirfd(&handle, + data->u.mkdir.dirfd); /* Ownership of dirfd is transferred to the handle. */ - data->fd = -1; + data->u.mkdir.dirfd = -1; /* Safe to call as we have transitioned to the requested uid/gid. */ - ret_value->u.mkdirat.ret = + ret_value->u.ret = lttng_directory_handle_create_subdirectory_recursive( &handle, path, mode); ret_value->_errno = errno; - ret_value->_error = (ret_value->u.mkdirat.ret) ? true : false; + ret_value->_error = (ret_value->u.ret) ? true : false; lttng_directory_handle_fini(&handle); - return ret_value->u.mkdirat.ret; + return ret_value->u.ret; } static @@ -218,49 +376,133 @@ int _mkdirat(struct run_as_data *data, struct run_as_ret *ret_value) mode_t mode; struct lttng_directory_handle handle; - path = data->u.mkdirat.path; - mode = data->u.mkdirat.mode; + path = data->u.mkdir.path; + mode = data->u.mkdir.mode; - (void) lttng_directory_handle_init_from_dirfd(&handle, data->fd); + (void) lttng_directory_handle_init_from_dirfd(&handle, + data->u.mkdir.dirfd); /* Ownership of dirfd is transferred to the handle. */ - data->fd = -1; + data->u.mkdir.dirfd = -1; /* Safe to call as we have transitioned to the requested uid/gid. */ - ret_value->u.mkdirat.ret = + ret_value->u.ret = lttng_directory_handle_create_subdirectory( &handle, path, mode); ret_value->_errno = errno; - ret_value->_error = (ret_value->u.mkdirat.ret) ? true : false; + ret_value->_error = (ret_value->u.ret) ? true : false; lttng_directory_handle_fini(&handle); - return ret_value->u.mkdirat.ret; + return ret_value->u.ret; } static int _open(struct run_as_data *data, struct run_as_ret *ret_value) { - ret_value->u.open.ret = openat(data->fd, data->u.open.path, - data->u.open.flags, data->u.open.mode); - ret_value->fd = ret_value->u.open.ret; + int fd; + struct lttng_directory_handle handle; + + (void) lttng_directory_handle_init_from_dirfd(&handle, + data->u.open.dirfd); + /* Ownership of dirfd is transferred to the handle. */ + data->u.open.dirfd = -1; + + fd = lttng_directory_handle_open_file(&handle, + data->u.open.path, data->u.open.flags, + data->u.open.mode); + if (fd < 0) { + ret_value->u.ret = -1; + ret_value->u.open.fd = -1; + } else { + ret_value->u.ret = 0; + ret_value->u.open.fd = fd; + } + ret_value->_errno = errno; - ret_value->_error = ret_value->u.open.ret < 0; - return ret_value->u.open.ret; + ret_value->_error = fd < 0; + lttng_directory_handle_fini(&handle); + return ret_value->u.ret; } static int _unlink(struct run_as_data *data, struct run_as_ret *ret_value) { - ret_value->u.unlink.ret = unlinkat(data->fd, data->u.unlink.path, 0); + struct lttng_directory_handle handle; + + (void) lttng_directory_handle_init_from_dirfd(&handle, + data->u.unlink.dirfd); + + /* Ownership of dirfd is transferred to the handle. */ + data->u.unlink.dirfd = -1; + + ret_value->u.ret = lttng_directory_handle_unlink_file(&handle, + data->u.unlink.path); + ret_value->_errno = errno; + ret_value->_error = (ret_value->u.ret) ? true : false; + lttng_directory_handle_fini(&handle); + return ret_value->u.ret; +} + +static +int _rmdir(struct run_as_data *data, struct run_as_ret *ret_value) +{ + struct lttng_directory_handle handle; + + (void) lttng_directory_handle_init_from_dirfd(&handle, + data->u.rmdir.dirfd); + + /* Ownership of dirfd is transferred to the handle. */ + data->u.rmdir.dirfd = -1; + + ret_value->u.ret = lttng_directory_handle_remove_subdirectory( + &handle, data->u.rmdir.path); ret_value->_errno = errno; - ret_value->_error = (ret_value->u.unlink.ret) ? true : false; - return ret_value->u.unlink.ret; + ret_value->_error = (ret_value->u.ret) ? true : false; + lttng_directory_handle_fini(&handle); + return ret_value->u.ret; } static int _rmdir_recursive(struct run_as_data *data, struct run_as_ret *ret_value) { - ret_value->u.rmdir_recursive.ret = utils_recursive_rmdir(data->u.rmdir_recursive.path); + struct lttng_directory_handle handle; + + (void) lttng_directory_handle_init_from_dirfd(&handle, + data->u.rmdir.dirfd); + + /* Ownership of dirfd is transferred to the handle. */ + data->u.rmdir.dirfd = -1; + + ret_value->u.ret = lttng_directory_handle_remove_subdirectory_recursive( + &handle, data->u.rmdir.path); ret_value->_errno = errno; - ret_value->_error = (ret_value->u.rmdir_recursive.ret) ? true : false; - return ret_value->u.rmdir_recursive.ret; + ret_value->_error = (ret_value->u.ret) ? true : false; + lttng_directory_handle_fini(&handle); + return ret_value->u.ret; +} + +static +int _rename(struct run_as_data *data, struct run_as_ret *ret_value) +{ + const char *old_path, *new_path; + struct lttng_directory_handle old_handle, new_handle; + + old_path = data->u.rename.old_path; + new_path = data->u.rename.new_path; + + (void) lttng_directory_handle_init_from_dirfd(&old_handle, + data->u.rename.dirfds[0]); + (void) lttng_directory_handle_init_from_dirfd(&new_handle, + data->u.rename.dirfds[1]); + + /* Ownership of dirfds are transferred to the handles. */ + data->u.rename.dirfds[0] = data->u.rename.dirfds[1] = -1; + + /* Safe to call as we have transitioned to the requested uid/gid. */ + ret_value->u.ret = lttng_directory_handle_rename( + &old_handle, old_path, &new_handle, new_path); + ret_value->_errno = errno; + ret_value->_error = (ret_value->u.ret) ? true : false; + lttng_directory_handle_fini(&old_handle); + lttng_directory_handle_fini(&new_handle); + return ret_value->u.ret; } #ifdef HAVE_ELF_H @@ -269,9 +511,9 @@ int _extract_elf_symbol_offset(struct run_as_data *data, struct run_as_ret *ret_value) { int ret = 0; - ret_value->_error = false; - ret = lttng_elf_get_symbol_offset(data->fd, + ret_value->_error = false; + ret = lttng_elf_get_symbol_offset(data->u.extract_elf_symbol_offset.fd, data->u.extract_elf_symbol_offset.function, &ret_value->u.extract_elf_symbol_offset.offset); if (ret) { @@ -293,7 +535,8 @@ int _extract_sdt_probe_offsets(struct run_as_data *data, ret_value->_error = false; /* On success, this call allocates the offsets paramater. */ - ret = lttng_elf_get_sdt_probe_offsets(data->fd, + ret = lttng_elf_get_sdt_probe_offsets( + data->u.extract_sdt_probe_offsets.fd, data->u.extract_sdt_probe_offsets.provider_name, data->u.extract_sdt_probe_offsets.probe_name, &offsets, &num_offset); @@ -356,8 +599,15 @@ run_as_fct run_as_enum_to_fct(enum run_as_cmd cmd) case RUN_AS_UNLINK: case RUN_AS_UNLINKAT: return _unlink; + case RUN_AS_RMDIR: + case RUN_AS_RMDIRAT: + return _rmdir; case RUN_AS_RMDIR_RECURSIVE: + case RUN_AS_RMDIRAT_RECURSIVE: return _rmdir_recursive; + case RUN_AS_RENAME: + case RUN_AS_RENAMEAT: + return _rename; case RUN_AS_EXTRACT_ELF_SYMBOL_OFFSET: return _extract_elf_symbol_offset; case RUN_AS_EXTRACT_SDT_PROBE_OFFSETS: @@ -369,190 +619,171 @@ run_as_fct run_as_enum_to_fct(enum run_as_cmd cmd) } static -int do_send_fd(int sock, int fd) +int do_send_fds(int sock, const int *fds, unsigned int fd_count) { ssize_t len; + unsigned int i; + + for (i = 0; i < fd_count; i++) { + if (fds[i] < 0) { + ERR("Attempt to send invalid file descriptor to master (fd = %i)", + fds[i]); + /* Return 0 as this is not a fatal error. */ + return 0; + } + } - if (fd < 0) { - ERR("Attempt to send invalid file descriptor to master (fd = %i)", fd); - /* Return 0 as this is not a fatal error. */ - return 0; - } - - len = lttcomm_send_fds_unix_sock(sock, &fd, 1); - if (len < 0) { - PERROR("lttcomm_send_fds_unix_sock"); - return -1; - } - return 0; + len = lttcomm_send_fds_unix_sock(sock, fds, fd_count); + return len < 0 ? -1 : 0; } static -int do_recv_fd(int sock, int *fd) +int do_recv_fds(int sock, int *fds, unsigned int fd_count) { + int ret = 0; + unsigned int i; ssize_t len; - len = lttcomm_recv_fds_unix_sock(sock, fd, 1); - - if (!len) { - return -1; + len = lttcomm_recv_fds_unix_sock(sock, fds, fd_count); + if (len == 0) { + ret = -1; + goto end; } else if (len < 0) { - PERROR("lttcomm_recv_fds_unix_sock"); - return -1; - } - if (*fd < 0) { - ERR("Invalid file descriptor received from worker (fd = %i)", *fd); - /* Return 0 as this is not a fatal error. */ - return 0; + PERROR("Failed to receive file descriptors from socket"); + ret = -1; + goto end; } - return 0; + for (i = 0; i < fd_count; i++) { + if (fds[i] < 0) { + ERR("Invalid file descriptor received from worker (fd = %i)", fds[i]); + /* Return 0 as this is not a fatal error. */ + } + } +end: + return ret; } static -int send_fd_to_worker(struct run_as_worker *worker, enum run_as_cmd cmd, int fd) +int send_fds_to_worker(const struct run_as_worker *worker, + const struct run_as_data *data) { int ret = 0; + unsigned int i; - switch (cmd) { - case RUN_AS_EXTRACT_ELF_SYMBOL_OFFSET: - case RUN_AS_EXTRACT_SDT_PROBE_OFFSETS: - case RUN_AS_MKDIRAT: - case RUN_AS_MKDIRAT_RECURSIVE: - case RUN_AS_OPENAT: - case RUN_AS_UNLINKAT: - break; - default: - return 0; + if (COMMAND_USE_CWD_FD(data) || COMMAND_IN_FD_COUNT(data) == 0) { + goto end; } - if (fd < 0) { - ERR("Refusing to send invalid fd to worker (fd = %i)", fd); - return -1; - } + for (i = 0; i < COMMAND_IN_FD_COUNT(data); i++) { + if (COMMAND_IN_FDS(data)[i] < 0) { + ERR("Refusing to send invalid fd to worker (fd = %i)", + COMMAND_IN_FDS(data)[i]); + ret = -1; + goto end; + } + } - ret = do_send_fd(worker->sockpair[0], fd); + ret = do_send_fds(worker->sockpair[0], COMMAND_IN_FDS(data), + COMMAND_IN_FD_COUNT(data)); if (ret < 0) { - PERROR("do_send_fd"); + PERROR("Failed to send file descriptor to run-as worker"); ret = -1; + goto end; } - +end: return ret; } static -int send_fd_to_master(struct run_as_worker *worker, enum run_as_cmd cmd, int fd) +int send_fds_to_master(struct run_as_worker *worker, enum run_as_cmd cmd, + struct run_as_ret *run_as_ret) { - int ret = 0, ret_close = 0; + int ret = 0; + unsigned int i; - switch (cmd) { - case RUN_AS_OPEN: - case RUN_AS_OPENAT: - break; - default: - return 0; + if (COMMAND_OUT_FD_COUNT(cmd) == 0) { + goto end; } - if (fd < 0) { - DBG("Not sending file descriptor to master as it is invalid (fd = %i)", fd); - return 0; - } - ret = do_send_fd(worker->sockpair[1], fd); + ret = do_send_fds(worker->sockpair[1], COMMAND_OUT_FDS(cmd, run_as_ret), + COMMAND_OUT_FD_COUNT(cmd)); if (ret < 0) { - PERROR("do_send_fd error"); - ret = -1; + PERROR("Failed to send file descriptor to master process"); + goto end; } - ret_close = close(fd); - if (ret_close < 0) { - PERROR("close"); - } + for (i = 0; i < COMMAND_OUT_FD_COUNT(cmd); i++) { + int ret_close = close(COMMAND_OUT_FDS(cmd, run_as_ret)[i]); + if (ret_close < 0) { + PERROR("Failed to close result file descriptor"); + } + } +end: return ret; } static -int recv_fd_from_worker(struct run_as_worker *worker, enum run_as_cmd cmd, int *fd) +int recv_fds_from_worker(const struct run_as_worker *worker, enum run_as_cmd cmd, + struct run_as_ret *run_as_ret) { int ret = 0; - switch (cmd) { - case RUN_AS_OPEN: - case RUN_AS_OPENAT: - break; - default: - return 0; + if (COMMAND_OUT_FD_COUNT(cmd) == 0) { + goto end; } - ret = do_recv_fd(worker->sockpair[0], fd); + ret = do_recv_fds(worker->sockpair[0], COMMAND_OUT_FDS(cmd, run_as_ret), + COMMAND_OUT_FD_COUNT(cmd)); if (ret < 0) { - PERROR("do_recv_fd error"); + PERROR("Failed to receive file descriptor from run-as worker"); ret = -1; } - +end: return ret; } static -int recv_fd_from_master(struct run_as_worker *worker, enum run_as_cmd cmd, int *fd) +int recv_fds_from_master(struct run_as_worker *worker, struct run_as_data *data) { int ret = 0; - switch (cmd) { - case RUN_AS_EXTRACT_ELF_SYMBOL_OFFSET: - case RUN_AS_EXTRACT_SDT_PROBE_OFFSETS: - case RUN_AS_MKDIRAT: - case RUN_AS_MKDIRAT_RECURSIVE: - case RUN_AS_OPENAT: - case RUN_AS_UNLINKAT: - break; - case RUN_AS_MKDIR: - case RUN_AS_MKDIR_RECURSIVE: - case RUN_AS_OPEN: - case RUN_AS_UNLINK: - *fd = AT_FDCWD; - /* fall-through */ - default: - return 0; + if (COMMAND_USE_CWD_FD(data)) { + unsigned int i; + + for (i = 0; i < COMMAND_IN_FD_COUNT(data); i++) { + COMMAND_IN_FDS(data)[i] = AT_FDCWD; + } + goto end; } - ret = do_recv_fd(worker->sockpair[1], fd); + ret = do_recv_fds(worker->sockpair[1], COMMAND_IN_FDS(data), + COMMAND_IN_FD_COUNT(data)); if (ret < 0) { - PERROR("do_recv_fd error"); + PERROR("Failed to receive file descriptors from master process"); ret = -1; } - +end: return ret; } static -int cleanup_received_fd(enum run_as_cmd cmd, int fd) +int cleanup_received_fds(struct run_as_data *data) { - int ret = 0; + int ret = 0, i; - switch (cmd) { - case RUN_AS_EXTRACT_ELF_SYMBOL_OFFSET: - case RUN_AS_EXTRACT_SDT_PROBE_OFFSETS: - case RUN_AS_MKDIRAT: - case RUN_AS_MKDIRAT_RECURSIVE: - case RUN_AS_OPEN: - case RUN_AS_OPENAT: - case RUN_AS_UNLINK: - case RUN_AS_UNLINKAT: - break; - default: - return 0; - } - - if (fd < 0) { - return 0; - } - ret = close(fd); - if (ret < 0) { - PERROR("close error"); - ret = -1; + for (i = 0; i < COMMAND_IN_FD_COUNT(data); i++) { + if (COMMAND_IN_FDS(data)[i] == -1) { + continue; + } + ret = close(COMMAND_IN_FDS(data)[i]); + if (ret) { + PERROR("Failed to close file descriptor received fd in run-as worker"); + goto end; + } } - +end: return ret; } @@ -563,15 +794,12 @@ static int handle_one_cmd(struct run_as_worker *worker) { int ret = 0; - struct run_as_data data; - ssize_t readlen, writelen; - struct run_as_ret sendret; - run_as_fct cmd; + struct run_as_data data = {}; + ssize_t readlen, writelen; + struct run_as_ret sendret = {}; + run_as_fct cmd; uid_t prev_euid; - memset(&sendret, 0, sizeof(sendret)); - sendret.fd = -1; - /* * Stage 1: Receive run_as_data struct from the master. * The structure contains the command type and all the parameters needed for @@ -601,7 +829,7 @@ int handle_one_cmd(struct run_as_worker *worker) * Some commands need a file descriptor as input so if it's needed we * receive the fd using the Unix socket. */ - ret = recv_fd_from_master(worker, data.cmd, &data.fd); + ret = recv_fds_from_master(worker, &data); if (ret < 0) { PERROR("recv_fd_from_master error"); ret = -1; @@ -642,7 +870,7 @@ int handle_one_cmd(struct run_as_worker *worker) } write_return: - ret = cleanup_received_fd(data.cmd, data.fd); + ret = cleanup_received_fds(&data); if (ret < 0) { ERR("Error cleaning up FD"); goto end; @@ -661,11 +889,9 @@ write_return: } /* - * Stage 5: Send file descriptor to the master - * Some commands return a file descriptor so if it's needed we pass it back - * to the master using the Unix socket. + * Stage 5: Send resulting file descriptors to the master. */ - ret = send_fd_to_master(worker, data.cmd, sendret.fd); + ret = send_fds_to_master(worker, data.cmd, &sendret); if (ret < 0) { DBG("Sending FD to master returned an error"); goto end; @@ -771,7 +997,7 @@ int run_as_cmd(struct run_as_worker *worker, /* * Stage 2: Send file descriptor to the worker process if needed */ - ret = send_fd_to_worker(worker, data->cmd, data->fd); + ret = send_fds_to_worker(worker, data); if (ret) { PERROR("do_send_fd error"); ret = -1; @@ -809,7 +1035,7 @@ int run_as_cmd(struct run_as_worker *worker, /* * Stage 5: Receive file descriptor if needed */ - ret = recv_fd_from_worker(worker, data->cmd, &ret_value->fd); + ret = recv_fds_from_worker(worker, cmd, ret_value); if (ret < 0) { ERR("Error receiving fd"); ret = -1; @@ -1158,27 +1384,25 @@ int run_as_mkdirat_recursive(int dirfd, const char *path, mode_t mode, uid_t uid, gid_t gid) { int ret; - struct run_as_data data; - struct run_as_ret run_as_ret; + struct run_as_data data = {}; + struct run_as_ret run_as_ret = {}; - memset(&data, 0, sizeof(data)); - memset(&run_as_ret, 0, sizeof(run_as_ret)); DBG3("mkdirat() recursive fd = %d%s, path = %s, mode = %d, uid = %d, gid = %d", dirfd, dirfd == AT_FDCWD ? " (AT_FDCWD)" : "", path, (int) mode, (int) uid, (int) gid); - ret = lttng_strncpy(data.u.mkdirat.path, path, - sizeof(data.u.mkdirat.path)); + ret = lttng_strncpy(data.u.mkdir.path, path, + sizeof(data.u.mkdir.path)); if (ret) { ERR("Failed to copy path argument of mkdirat recursive command"); goto error; } - data.u.mkdirat.path[PATH_MAX - 1] = '\0'; - data.u.mkdirat.mode = mode; - data.fd = dirfd; + data.u.mkdir.path[sizeof(data.u.mkdir.path) - 1] = '\0'; + data.u.mkdir.mode = mode; + data.u.mkdir.dirfd = dirfd; run_as(dirfd == AT_FDCWD ? RUN_AS_MKDIR_RECURSIVE : RUN_AS_MKDIRAT_RECURSIVE, &data, &run_as_ret, uid, gid); errno = run_as_ret._errno; - ret = run_as_ret.u.mkdirat.ret; + ret = run_as_ret.u.ret; error: return ret; } @@ -1194,28 +1418,25 @@ int run_as_mkdirat(int dirfd, const char *path, mode_t mode, uid_t uid, gid_t gid) { int ret; - struct run_as_data data; - struct run_as_ret run_as_ret; - - memset(&data, 0, sizeof(data)); - memset(&run_as_ret, 0, sizeof(run_as_ret)); + struct run_as_data data = {}; + struct run_as_ret run_as_ret = {}; DBG3("mkdirat() recursive fd = %d%s, path = %s, mode = %d, uid = %d, gid = %d", dirfd, dirfd == AT_FDCWD ? " (AT_FDCWD)" : "", path, (int) mode, (int) uid, (int) gid); - ret = lttng_strncpy(data.u.mkdirat.path, path, - sizeof(data.u.mkdirat.path)); + ret = lttng_strncpy(data.u.mkdir.path, path, + sizeof(data.u.mkdir.path)); if (ret) { ERR("Failed to copy path argument of mkdirat command"); goto error; } - data.u.mkdirat.path[PATH_MAX - 1] = '\0'; - data.u.mkdirat.mode = mode; - data.fd = dirfd; + data.u.mkdir.path[sizeof(data.u.mkdir.path) - 1] = '\0'; + data.u.mkdir.mode = mode; + data.u.mkdir.dirfd = dirfd; run_as(dirfd == AT_FDCWD ? RUN_AS_MKDIR : RUN_AS_MKDIRAT, &data, &run_as_ret, uid, gid); errno = run_as_ret._errno; - ret = run_as_ret.u.mkdirat.ret; + ret = run_as_ret.u.ret; error: return ret; } @@ -1231,25 +1452,28 @@ LTTNG_HIDDEN int run_as_openat(int dirfd, const char *path, int flags, mode_t mode, uid_t uid, gid_t gid) { - struct run_as_data data; - struct run_as_ret ret; - - memset(&data, 0, sizeof(data)); - memset(&ret, 0, sizeof(ret)); + int ret; + struct run_as_data data = {}; + struct run_as_ret run_as_ret = {}; DBG3("openat() fd = %d%s, path = %s, flags = %X, mode = %d, uid %d, gid %d", dirfd, dirfd == AT_FDCWD ? " (AT_FDCWD)" : "", path, flags, (int) mode, (int) uid, (int) gid); - strncpy(data.u.open.path, path, PATH_MAX - 1); - data.u.open.path[PATH_MAX - 1] = '\0'; + ret = lttng_strncpy(data.u.open.path, path, sizeof(data.u.open.path)); + if (ret) { + ERR("Failed to copy path argument of open command"); + goto error; + } data.u.open.flags = flags; data.u.open.mode = mode; - data.fd = dirfd; + data.u.open.dirfd = dirfd; run_as(dirfd == AT_FDCWD ? RUN_AS_OPEN : RUN_AS_OPENAT, - &data, &ret, uid, gid); - errno = ret._errno; - ret.u.open.ret = ret.fd; - return ret.u.open.ret; + &data, &run_as_ret, uid, gid); + errno = run_as_ret._errno; + ret = run_as_ret.u.ret < 0 ? run_as_ret.u.ret : + run_as_ret.u.open.fd; +error: + return ret; } LTTNG_HIDDEN @@ -1261,70 +1485,161 @@ int run_as_unlink(const char *path, uid_t uid, gid_t gid) LTTNG_HIDDEN int run_as_unlinkat(int dirfd, const char *path, uid_t uid, gid_t gid) { - struct run_as_data data; - struct run_as_ret ret; - - memset(&data, 0, sizeof(data)); - memset(&ret, 0, sizeof(ret)); + int ret; + struct run_as_data data = {}; + struct run_as_ret run_as_ret = {}; DBG3("unlinkat() fd = %d%s, path = %s, uid = %d, gid = %d", dirfd, dirfd == AT_FDCWD ? " (AT_FDCWD)" : "", path, (int) uid, (int) gid); - strncpy(data.u.unlink.path, path, PATH_MAX - 1); - data.u.unlink.path[PATH_MAX - 1] = '\0'; - data.fd = dirfd; - run_as(RUN_AS_UNLINK, &data, &ret, uid, gid); - errno = ret._errno; - return ret.u.unlink.ret; + ret = lttng_strncpy(data.u.unlink.path, path, + sizeof(data.u.unlink.path)); + if (ret) { + goto error; + } + data.u.unlink.dirfd = dirfd; + run_as(dirfd == AT_FDCWD ? RUN_AS_UNLINK : RUN_AS_UNLINKAT, &data, + &run_as_ret, uid, gid); + errno = run_as_ret._errno; + ret = run_as_ret.u.ret; +error: + return ret; +} + +LTTNG_HIDDEN +int run_as_rmdir(const char *path, uid_t uid, gid_t gid) +{ + return run_as_rmdirat(AT_FDCWD, path, uid, gid); +} + +LTTNG_HIDDEN +int run_as_rmdirat(int dirfd, const char *path, uid_t uid, gid_t gid) +{ + int ret; + struct run_as_data data = {}; + struct run_as_ret run_as_ret = {}; + + DBG3("rmdirat() fd = %d%s, path = %s, uid = %d, gid = %d", + dirfd, dirfd == AT_FDCWD ? " (AT_FDCWD)" : "", + path, (int) uid, (int) gid); + ret = lttng_strncpy(data.u.rmdir.path, path, + sizeof(data.u.rmdir.path)); + if (ret) { + goto error; + } + data.u.rmdir.dirfd = dirfd; + run_as(dirfd == AT_FDCWD ? RUN_AS_RMDIR : RUN_AS_RMDIRAT, &data, + &run_as_ret, uid, gid); + errno = run_as_ret._errno; + ret = run_as_ret.u.ret; +error: + return ret; } LTTNG_HIDDEN int run_as_rmdir_recursive(const char *path, uid_t uid, gid_t gid) { - struct run_as_data data; - struct run_as_ret ret; + return run_as_rmdirat_recursive(AT_FDCWD, path, uid, gid); +} - memset(&data, 0, sizeof(data)); - memset(&ret, 0, sizeof(ret)); +LTTNG_HIDDEN +int run_as_rmdirat_recursive(int dirfd, const char *path, uid_t uid, gid_t gid) +{ + int ret; + struct run_as_data data = {}; + struct run_as_ret run_as_ret = {}; - DBG3("rmdir_recursive() %s with for uid %d and gid %d", + DBG3("rmdirat() recursive fd = %d%s, path = %s, uid = %d, gid = %d", + dirfd, dirfd == AT_FDCWD ? " (AT_FDCWD)" : "", path, (int) uid, (int) gid); - strncpy(data.u.rmdir_recursive.path, path, PATH_MAX - 1); - data.u.rmdir_recursive.path[PATH_MAX - 1] = '\0'; - run_as(RUN_AS_RMDIR_RECURSIVE, &data, &ret, uid, gid); - errno = ret._errno; - return ret.u.rmdir_recursive.ret; + ret = lttng_strncpy(data.u.rmdir.path, path, + sizeof(data.u.rmdir.path)); + if (ret) { + goto error; + } + data.u.rmdir.dirfd = dirfd; + run_as(dirfd == AT_FDCWD ? RUN_AS_RMDIR_RECURSIVE : RUN_AS_RMDIRAT_RECURSIVE, + &data, &run_as_ret, uid, gid); + errno = run_as_ret._errno; + ret = run_as_ret.u.ret; +error: + return ret; +} + +LTTNG_HIDDEN +int run_as_rename(const char *old, const char *new, uid_t uid, gid_t gid) +{ + return run_as_renameat(AT_FDCWD, old, AT_FDCWD, new, uid, gid); +} + +LTTNG_HIDDEN +int run_as_renameat(int old_dirfd, const char *old_name, + int new_dirfd, const char *new_name, uid_t uid, gid_t gid) +{ + int ret; + struct run_as_data data = {}; + struct run_as_ret run_as_ret = {}; + + DBG3("renameat() old_dirfd = %d%s, old_name = %s, new_dirfd = %d%s, new_name = %s, uid = %d, gid = %d", + old_dirfd, old_dirfd == AT_FDCWD ? " (AT_FDCWD)" : "", + old_name, + new_dirfd, new_dirfd == AT_FDCWD ? " (AT_FDCWD)" : "", + new_name, (int) uid, (int) gid); + ret = lttng_strncpy(data.u.rename.old_path, old_name, + sizeof(data.u.rename.old_path)); + if (ret) { + goto error; + } + ret = lttng_strncpy(data.u.rename.new_path, new_name, + sizeof(data.u.rename.new_path)); + if (ret) { + goto error; + } + + data.u.rename.dirfds[0] = old_dirfd; + data.u.rename.dirfds[1] = new_dirfd; + run_as(old_dirfd == AT_FDCWD && new_dirfd == AT_FDCWD ? + RUN_AS_RENAME : RUN_AS_RENAMEAT, + &data, &run_as_ret, uid, gid); + errno = run_as_ret._errno; + ret = run_as_ret.u.ret; +error: + return ret; } LTTNG_HIDDEN int run_as_extract_elf_symbol_offset(int fd, const char* function, uid_t uid, gid_t gid, uint64_t *offset) { - struct run_as_data data; - struct run_as_ret ret; - - memset(&data, 0, sizeof(data)); - memset(&ret, 0, sizeof(ret)); + int ret; + struct run_as_data data = {}; + struct run_as_ret run_as_ret = {}; DBG3("extract_elf_symbol_offset() on fd=%d and function=%s " - "with for uid %d and gid %d", fd, function, (int) uid, (int) gid); + "with for uid %d and gid %d", fd, function, + (int) uid, (int) gid); - data.fd = fd; + data.u.extract_elf_symbol_offset.fd = fd; strncpy(data.u.extract_elf_symbol_offset.function, function, LTTNG_SYMBOL_NAME_LEN - 1); - data.u.extract_elf_symbol_offset.function[LTTNG_SYMBOL_NAME_LEN - 1] = '\0'; + ret = lttng_strncpy(data.u.extract_elf_symbol_offset.function, + function, + sizeof(data.u.extract_elf_symbol_offset.function)); + if (ret) { + goto error; + } - run_as(RUN_AS_EXTRACT_ELF_SYMBOL_OFFSET, &data, &ret, uid, gid); - - errno = ret._errno; - - if (ret._error) { - return -1; + run_as(RUN_AS_EXTRACT_ELF_SYMBOL_OFFSET, &data, &run_as_ret, uid, gid); + errno = run_as_ret._errno; + if (run_as_ret._error) { + ret = -1; + goto error; } - *offset = ret.u.extract_elf_symbol_offset.offset; - return 0; + *offset = run_as_ret.u.extract_elf_symbol_offset.offset; +error: + return ret; } LTTNG_HIDDEN @@ -1332,41 +1647,46 @@ int run_as_extract_sdt_probe_offsets(int fd, const char* provider_name, const char* probe_name, uid_t uid, gid_t gid, uint64_t **offsets, uint32_t *num_offset) { - struct run_as_data data; - struct run_as_ret ret; - - memset(&data, 0, sizeof(data)); - memset(&ret, 0, sizeof(ret)); + int ret; + struct run_as_data data = {}; + struct run_as_ret run_as_ret = {}; DBG3("extract_sdt_probe_offsets() on fd=%d, probe_name=%s and " - "provider_name=%s with for uid %d and gid %d", fd, probe_name, - provider_name, (int) uid, (int) gid); - - data.fd = fd; - - strncpy(data.u.extract_sdt_probe_offsets.probe_name, probe_name, LTTNG_SYMBOL_NAME_LEN - 1); - strncpy(data.u.extract_sdt_probe_offsets.provider_name, provider_name, LTTNG_SYMBOL_NAME_LEN - 1); - - data.u.extract_sdt_probe_offsets.probe_name[LTTNG_SYMBOL_NAME_LEN - 1] = '\0'; - data.u.extract_sdt_probe_offsets.provider_name[LTTNG_SYMBOL_NAME_LEN - 1] = '\0'; - - run_as(RUN_AS_EXTRACT_SDT_PROBE_OFFSETS, &data, &ret, uid, gid); + "provider_name=%s with for uid %d and gid %d", fd, + probe_name, provider_name, (int) uid, (int) gid); - errno = ret._errno; + data.u.extract_sdt_probe_offsets.fd = fd; - if (ret._error) { - return -1; + ret = lttng_strncpy(data.u.extract_sdt_probe_offsets.probe_name, probe_name, + sizeof(data.u.extract_sdt_probe_offsets.probe_name)); + if (ret) { + goto error; + } + ret = lttng_strncpy(data.u.extract_sdt_probe_offsets.provider_name, + provider_name, + sizeof(data.u.extract_sdt_probe_offsets.provider_name)); + if (ret) { + goto error; } - *num_offset = ret.u.extract_sdt_probe_offsets.num_offset; + run_as(RUN_AS_EXTRACT_SDT_PROBE_OFFSETS, &data, &run_as_ret, uid, gid); + errno = run_as_ret._errno; + if (run_as_ret._error) { + ret = -1; + goto error; + } + *num_offset = run_as_ret.u.extract_sdt_probe_offsets.num_offset; *offsets = zmalloc(*num_offset * sizeof(uint64_t)); if (!*offsets) { - return -ENOMEM; + ret = -ENOMEM; + goto error; } - memcpy(*offsets, ret.u.extract_sdt_probe_offsets.offsets, *num_offset * sizeof(uint64_t)); - return 0; + memcpy(*offsets, run_as_ret.u.extract_sdt_probe_offsets.offsets, + *num_offset * sizeof(uint64_t)); +error: + return ret; } LTTNG_HIDDEN diff --git a/src/common/runas.h b/src/common/runas.h index 55734eb41..d3a9523bf 100644 --- a/src/common/runas.h +++ b/src/common/runas.h @@ -60,8 +60,19 @@ int run_as_unlink(const char *path, uid_t uid, gid_t gid); LTTNG_HIDDEN 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); 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); +LTTNG_HIDDEN +int run_as_rename(const char *old, const char *new, uid_t uid, gid_t gid); +LTTNG_HIDDEN +int run_as_renameat(int old_dirfd, const char *old, + int new_dirfd, const char *new, uid_t uid, gid_t gid); +LTTNG_HIDDEN int run_as_extract_elf_symbol_offset(int fd, const char* function, uid_t uid, gid_t gid, uint64_t *offset); LTTNG_HIDDEN diff --git a/src/common/trace-chunk.c b/src/common/trace-chunk.c index 53cc08a2c..451832fde 100644 --- a/src/common/trace-chunk.c +++ b/src/common/trace-chunk.c @@ -209,7 +209,7 @@ void lttng_trace_chunk_init(struct lttng_trace_chunk *chunk) { urcu_ref_init(&chunk->ref); pthread_mutex_init(&chunk->lock, NULL); - lttng_dynamic_pointer_array_init(&chunk->top_level_directories); + lttng_dynamic_pointer_array_init(&chunk->top_level_directories, free); } static @@ -224,7 +224,7 @@ void lttng_trace_chunk_fini(struct lttng_trace_chunk *chunk) } free(chunk->name); chunk->name = NULL; - lttng_dynamic_pointer_array_reset(&chunk->top_level_directories, free); + lttng_dynamic_pointer_array_reset(&chunk->top_level_directories); pthread_mutex_destroy(&chunk->lock); } diff --git a/src/common/utils.c b/src/common/utils.c index 2f132a060..732a30bf1 100644 --- a/src/common/utils.c +++ b/src/common/utils.c @@ -1485,79 +1485,16 @@ end: LTTNG_HIDDEN int utils_recursive_rmdir(const char *path) { - DIR *dir; - size_t path_len; - int dir_fd, ret = 0, closeret, is_empty = 1; - struct dirent *entry; - - /* Open directory */ - dir = opendir(path); - if (!dir) { - PERROR("Cannot open '%s' path", path); - return -1; - } - dir_fd = lttng_dirfd(dir); - if (dir_fd < 0) { - PERROR("lttng_dirfd"); - return -1; - } - - path_len = strlen(path); - while ((entry = readdir(dir))) { - struct stat st; - size_t name_len; - char filename[PATH_MAX]; - - if (!strcmp(entry->d_name, ".") - || !strcmp(entry->d_name, "..")) { - continue; - } - - name_len = strlen(entry->d_name); - if (path_len + name_len + 2 > sizeof(filename)) { - ERR("Failed to remove file: path name too long (%s/%s)", - path, entry->d_name); - continue; - } - if (snprintf(filename, sizeof(filename), "%s/%s", - path, entry->d_name) < 0) { - ERR("Failed to format path."); - continue; - } - - if (stat(filename, &st)) { - PERROR("stat"); - continue; - } + int ret; + struct lttng_directory_handle handle; - if (S_ISDIR(st.st_mode)) { - char subpath[PATH_MAX]; - - strncpy(subpath, path, PATH_MAX); - subpath[PATH_MAX - 1] = '\0'; - strncat(subpath, "/", - PATH_MAX - strlen(subpath) - 1); - strncat(subpath, entry->d_name, - PATH_MAX - strlen(subpath) - 1); - if (utils_recursive_rmdir(subpath)) { - is_empty = 0; - } - } else if (S_ISREG(st.st_mode)) { - is_empty = 0; - } else { - ret = -EINVAL; - goto end; - } + ret = lttng_directory_handle_init(&handle, NULL); + if (ret) { + goto end; } + ret = lttng_directory_handle_remove_subdirectory(&handle, path); + lttng_directory_handle_fini(&handle); end: - closeret = closedir(dir); - if (closeret) { - PERROR("closedir"); - } - if (is_empty) { - DBG3("Attempting rmdir %s", path); - ret = rmdir(path); - } return ret; } diff --git a/tests/unit/Makefile.am b/tests/unit/Makefile.am index 4ded1dfb1..460e2f752 100644 --- a/tests/unit/Makefile.am +++ b/tests/unit/Makefile.am @@ -15,6 +15,7 @@ TESTS = test_kernel_data \ test_utils_compat_poll \ test_string_utils \ test_notification \ + test_directory_handle \ ini_config/test_ini_config LIBTAP=$(top_builddir)/tests/utils/tap/libtap.la @@ -30,7 +31,7 @@ LIBLTTNG_CTL=$(top_builddir)/src/lib/lttng-ctl/liblttng-ctl.la noinst_PROGRAMS = test_uri test_session test_kernel_data \ test_utils_parse_size_suffix test_utils_parse_time_suffix \ test_utils_expand_path test_utils_compat_poll \ - test_string_utils test_notification + test_string_utils test_notification test_directory_handle if HAVE_LIBLTTNG_UST_CTL noinst_PROGRAMS += test_ust_data @@ -158,6 +159,10 @@ test_utils_compat_poll_LDADD = $(LIBTAP) $(LIBHASHTABLE) $(DL_LIBS) \ test_utils_expand_path_SOURCES = test_utils_expand_path.c test_utils_expand_path_LDADD = $(LIBTAP) $(LIBHASHTABLE) $(LIBCOMMON) $(DL_LIBS) +# directory handle unit test +test_directory_handle_SOURCES = test_directory_handle.c +test_directory_handle_LDADD = $(LIBTAP) $(LIBHASHTABLE) $(LIBCOMMON) $(DL_LIBS) + # string utilities unit test test_string_utils_SOURCES = test_string_utils.c test_string_utils_LDADD = $(LIBTAP) $(LIBCOMMON) $(LIBSTRINGUTILS) $(DL_LIBS) diff --git a/tests/unit/test_directory_handle.c b/tests/unit/test_directory_handle.c new file mode 100644 index 000000000..13db3f4bb --- /dev/null +++ b/tests/unit/test_directory_handle.c @@ -0,0 +1,95 @@ +/* + * Copyright (C) - 2019 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 as published by as + * published by the Free Software Foundation; only version 2 of the License. + * + * 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 + +#define TEST_COUNT 5 + +/* For error.h */ +int lttng_opt_quiet = 1; +int lttng_opt_verbose = 3; +int lttng_opt_mi; + +#define DIR_HIERARCHY "a/b/c/d/e/f/g/h/i/j/k/l/m/n/o/p" +#define DIR_CREATION_MODE (S_IRWXU | S_IRWXG) + +bool dir_exists(const char *path) +{ + int ret; + struct stat st; + + ret = stat(path, &st); + return ret == 0 && S_ISDIR(st.st_mode); +} + +int main(int argc, char **argv) +{ + int ret; + struct lttng_directory_handle test_dir_handle; + char test_dir[] = "/tmp/lttng-XXXXXX"; + char *created_dir = NULL; + + plan_tests(TEST_COUNT); + + diag("directory handle tests"); + + if (!mkdtemp(test_dir)) { + diag("Failed to generate temporary test directory"); + goto end; + } + + ret = lttng_directory_handle_init(&test_dir_handle, test_dir); + ok(ret == 0, "Initialized directory handle from the test directory"); + if (ret) { + goto end; + } + + ret = lttng_directory_handle_create_subdirectory_recursive( + &test_dir_handle, DIR_HIERARCHY, DIR_CREATION_MODE); + ok(ret == 0, "Create folder hierarchy %s from handle to %s", + DIR_HIERARCHY, test_dir); + ret = asprintf(&created_dir, "%s/%s", test_dir, DIR_HIERARCHY); + if (ret < 0) { + diag("Failed to allocate created directory path buffer"); + goto end; + } + ok(dir_exists(created_dir), "Folder %s exists", created_dir); + + ret = lttng_directory_handle_remove_subdirectory_recursive( + &test_dir_handle, "a"); + ok(ret == 0, "Recursively removed directory hierarchy %s by removing %s", + DIR_HIERARCHY, "a"); +end: + ret = rmdir(test_dir); + if (ret) { + diag("Failed to clean-up test directory: %s", strerror(errno)); + } + ok(ret == 0, "Cleaned-up test directory"); + lttng_directory_handle_fini(&test_dir_handle); + free(created_dir); + return exit_status(); +} -- 2.34.1