+
+LTTNG_HIDDEN
+int lttng_directory_handle_open_file_as_user(
+ const struct lttng_directory_handle *handle,
+ const char *filename,
+ int flags, mode_t mode,
+ const struct lttng_credentials *creds)
+{
+ int ret;
+
+ if (!creds) {
+ /* Run as current user. */
+ ret = lttng_directory_handle_open(handle, filename, flags,
+ mode);
+ } else {
+ ret = _run_as_open(handle, filename, flags, mode,
+ creds->uid, creds->gid);
+ }
+ return ret;
+}
+
+LTTNG_HIDDEN
+int lttng_directory_handle_open_file(
+ const struct lttng_directory_handle *handle,
+ const char *filename,
+ int flags, mode_t mode)
+{
+ return lttng_directory_handle_open_file_as_user(handle, filename, flags,
+ mode, NULL);
+}
+
+LTTNG_HIDDEN
+int lttng_directory_handle_unlink_file_as_user(
+ const struct lttng_directory_handle *handle,
+ const char *filename,
+ const struct lttng_credentials *creds)
+{
+ int ret;
+
+ if (!creds) {
+ /* Run as current user. */
+ ret = lttng_directory_handle_unlink(handle, filename);
+ } else {
+ ret = _run_as_unlink(handle, filename, creds->uid, creds->gid);
+ }
+ return ret;
+}
+
+LTTNG_HIDDEN
+int lttng_directory_handle_unlink_file(
+ const struct lttng_directory_handle *handle,
+ const char *filename)
+{
+ 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 {
+ ssize_t parent_frame_idx;
+ DIR *dir;
+ bool empty;
+ /* 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 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;
+ const char separator = '/';
+
+ lttng_dynamic_buffer_init(¤t_path);
+ lttng_dynamic_array_init(&frames, sizeof(struct rmdir_frame),
+ 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) {
+ 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);
+
+ 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;
+
+ 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)) {
+ if ((flags & LTTNG_DIRECTORY_HANDLE_SKIP_NON_EMPTY_FLAG) &&
+ errno == ENOENT) {
+ break;
+ }
+ PERROR("Failed to stat \"%s\"",
+ current_path.data);
+ ret = -1;
+ goto end;
+ }
+
+ if (!S_ISDIR(st.st_mode)) {
+ 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;
+ }
+
+ /* Pop rmdir frame. */
+ if (current_frame->empty) {
+ ret = lttng_directory_handle_rmdir(
+ handle, current_path.data);
+ if (ret) {
+ 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");
+ }
+ } 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);
+ 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,
+ int flags)
+{
+ return lttng_directory_handle_remove_subdirectory_recursive_as_user(
+ 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,
+ int flags)
+{
+ int ret;
+
+ if (!creds) {
+ /* Run as current user. */
+ ret = remove_directory_recursive(handle, name, flags);
+ } else {
+ ret = _run_as_rmdir_recursive(handle, name, creds->uid,
+ creds->gid, flags);
+ }
+ return ret;
+}