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);
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 */
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];
goto end;
}
- ret = run_as_rmdir_recursive(fullpath, uid, gid);
+ ret = run_as_rmdir_recursive(fullpath, uid, gid, flags);
end:
return ret;
}
}
struct rmdir_frame {
+ ssize_t parent_frame_idx;
DIR *dir;
+ bool empty;
/* Size including '\0'. */
size_t path_size;
};
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;
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) {
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));
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;
}
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);
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;
}
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 {
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);
}
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 = {};
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;