X-Git-Url: https://git.lttng.org/?p=lttng-tools.git;a=blobdiff_plain;f=src%2Fcommon%2Fcompat%2Fdirectory-handle.c;h=3f35f91655b58b6da7287b476293f8504088776b;hp=80e9e118b8c46994751f8b7267d52f0718552f89;hb=93bed9fe8f48c11b7bb1224db36d82404cea080d;hpb=bbbfd84979130a11aebf172cf7d0d24e28165258 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; +}