Fix: rmdir recursive: skip non-empty directories with flag
authorMathieu Desnoyers <mathieu.desnoyers@efficios.com>
Mon, 12 Aug 2019 21:25:19 +0000 (17:25 -0400)
committerJérémie Galarneau <jeremie.galarneau@efficios.com>
Thu, 5 Sep 2019 20:39:15 +0000 (16:39 -0400)
Restore the behavior of LTTng 2.10 with a flag.

Signed-off-by: Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
Signed-off-by: Jérémie Galarneau <jeremie.galarneau@efficios.com>
src/bin/lttng-sessiond/ust-registry.c
src/common/compat/directory-handle.c
src/common/compat/directory-handle.h
src/common/runas.c
src/common/runas.h
src/common/ust-consumer/ust-consumer.c
tests/unit/test_directory_handle.c

index a8db79ea637d8dea53aced4240cd0aac5496ff44..7345bcbddcca8d646a1751179b7bc4de3c5865a0 100644 (file)
@@ -1031,7 +1031,8 @@ void ust_registry_session_destroy(struct ust_registry_session *reg)
                 * Try deleting the directory hierarchy.
                 */
                (void) run_as_rmdir_recursive(reg->root_shm_path,
-                               reg->uid, reg->gid);
+                               reg->uid, reg->gid,
+                               LTTNG_DIRECTORY_HANDLE_SKIP_NON_EMPTY_FLAG);
        }
        /* Destroy the enum hash table */
        if (reg->enums) {
index d6fb7037b663396a190615772e7860f319eabaff..4dad7b75744a7d81305910a3d86556cca3179bf1 100644 (file)
@@ -85,7 +85,7 @@ int _run_as_rmdir(const struct lttng_directory_handle *handle,
 static
 int _run_as_rmdir_recursive(
                const struct lttng_directory_handle *handle, const char *name,
-               uid_t uid, gid_t gid);
+               uid_t uid, gid_t gid, int flags);
 static
 void lttng_directory_handle_invalidate(struct lttng_directory_handle *handle);
 
@@ -305,9 +305,9 @@ int _run_as_rmdir(const struct lttng_directory_handle *handle,
 static
 int _run_as_rmdir_recursive(
                const struct lttng_directory_handle *handle, const char *name,
-               uid_t uid, gid_t gid)
+               uid_t uid, gid_t gid, int flags)
 {
-       return run_as_rmdirat_recursive(handle->dirfd, name, uid, gid);
+       return run_as_rmdirat_recursive(handle->dirfd, name, uid, gid, flags);
 }
 
 #else /* COMPAT_DIRFD */
@@ -759,7 +759,7 @@ end:
 static
 int _run_as_rmdir_recursive(
                const struct lttng_directory_handle *handle, const char *name,
-               uid_t uid, gid_t gid)
+               uid_t uid, gid_t gid, int flags)
 {
        int ret;
        char fullpath[LTTNG_PATH_MAX];
@@ -770,7 +770,7 @@ int _run_as_rmdir_recursive(
                goto end;
        }
 
-       ret = run_as_rmdir_recursive(fullpath, uid, gid);
+       ret = run_as_rmdir_recursive(fullpath, uid, gid, flags);
 end:
        return ret;
 }
@@ -1059,7 +1059,9 @@ int lttng_directory_handle_remove_subdirectory_as_user(
 }
 
 struct rmdir_frame {
+       ssize_t parent_frame_idx;
        DIR *dir;
+       bool empty;
        /* Size including '\0'. */
        size_t path_size;
 };
@@ -1074,13 +1076,15 @@ void rmdir_frame_fini(void *data)
 
 static
 int remove_directory_recursive(const struct lttng_directory_handle *handle,
-               const char *path)
+               const char *path, int flags)
 {
        int ret;
        struct lttng_dynamic_array frames;
        size_t current_frame_idx = 0;
        struct rmdir_frame initial_frame = {
+               .parent_frame_idx = -1,
                .dir = lttng_directory_handle_opendir(handle, path),
+               .empty = true,
                .path_size = strlen(path) + 1,
        };
        struct lttng_dynamic_buffer current_path;
@@ -1088,7 +1092,27 @@ int remove_directory_recursive(const struct lttng_directory_handle *handle,
 
        lttng_dynamic_buffer_init(&current_path);
        lttng_dynamic_array_init(&frames, sizeof(struct rmdir_frame),
-                       rmdir_frame_fini);
+                       rmdir_frame_fini);
+
+       if (flags & ~(LTTNG_DIRECTORY_HANDLE_SKIP_NON_EMPTY_FLAG |
+                                   LTTNG_DIRECTORY_HANDLE_FAIL_NON_EMPTY_FLAG)) {
+               ERR("Unknown flags %d", flags);
+               ret = -1;
+               goto end;
+       }
+
+       if (!initial_frame.dir) {
+               if (flags & LTTNG_DIRECTORY_HANDLE_SKIP_NON_EMPTY_FLAG &&
+                               errno == ENOENT) {
+                       DBG("Cannot rmdir \"%s\": root does not exist", path);
+                       ret = 0;
+                       goto end;
+               } else {
+                       PERROR("Failed to rmdir \"%s\"", path);
+                       ret = -1;
+                       goto end;
+               }
+       }
 
        ret = lttng_dynamic_array_add_element(&frames, &initial_frame);
        if (ret) {
@@ -1097,42 +1121,37 @@ int remove_directory_recursive(const struct lttng_directory_handle *handle,
                goto end;
        }
 
-       ret = lttng_dynamic_buffer_append(&current_path, path,
-                       initial_frame.path_size);
-        if (ret) {
+       ret = lttng_dynamic_buffer_append(
+                       &current_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(&current_path,
-                               current_frame->path_size);
+               assert(current_frame->dir);
+               ret = lttng_dynamic_buffer_set_size(
+                               &current_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(&current_path,
-                                       current_path.size - 1);
+                       ret = lttng_dynamic_buffer_set_size(
+                                       &current_path, current_path.size - 1);
                        assert(!ret);
                        ret = lttng_dynamic_buffer_append(&current_path,
                                        &separator, sizeof(separator));
@@ -1146,8 +1165,12 @@ int remove_directory_recursive(const struct lttng_directory_handle *handle,
                                goto end;
                        }
 
-                       if (lttng_directory_handle_stat(handle,
-                                       current_path.data, &st)) {
+                       if (lttng_directory_handle_stat(
+                                           handle, current_path.data, &st)) {
+                               if ((flags & LTTNG_DIRECTORY_HANDLE_SKIP_NON_EMPTY_FLAG) &&
+                                               errno == ENOENT) {
+                                       break;
+                               }
                                PERROR("Failed to stat \"%s\"",
                                                current_path.data);
                                ret = -1;
@@ -1155,42 +1178,81 @@ int remove_directory_recursive(const struct lttng_directory_handle *handle,
                        }
 
                        if (!S_ISDIR(st.st_mode)) {
-                               /* Not empty, abort. */
-                               DBG("Directory \"%s\" is not empty; refusing to remove directory",
-                                               current_path.data);
-                               ret = -1;
-                               goto end;
+                               if (flags & LTTNG_DIRECTORY_HANDLE_SKIP_NON_EMPTY_FLAG) {
+                                       current_frame->empty = false;
+                                       break;
+                               } else {
+                                       /* Not empty, abort. */
+                                       DBG("Directory \"%s\" is not empty; refusing to remove directory",
+                                                       current_path.data);
+                                       ret = -1;
+                                       goto end;
+                               }
+                       } else {
+                               struct rmdir_frame new_frame = {
+                                       .path_size = current_path.size,
+                                       .dir = lttng_directory_handle_opendir(
+                                                       handle,
+                                                       current_path.data),
+                                       .empty = true,
+                                       .parent_frame_idx = current_frame_idx,
+                               };
+
+                               if (!new_frame.dir) {
+                                       if (flags & LTTNG_DIRECTORY_HANDLE_SKIP_NON_EMPTY_FLAG &&
+                                                       errno == ENOENT) {
+                                               DBG("Non-existing directory stream during recursive directory removal");
+                                               break;
+                                       } else {
+                                               PERROR("Failed to open directory stream during recursive directory removal");
+                                               ret = -1;
+                                               goto end;
+                                       }
+                               }
+                               ret = lttng_dynamic_array_add_element(
+                                               &frames, &new_frame);
+                               if (ret) {
+                                       ERR("Failed to push context frame during recursive directory removal");
+                                       rmdir_frame_fini(&new_frame);
+                                       goto end;
+                               }
+                               current_frame_idx++;
+                               /* We break iteration on readdir. */
+                               break;
                        }
+               }
+               if (entry) {
+                       continue;
+               }
 
-                       new_frame.path_size = current_path.size;
-                       new_frame.dir = lttng_directory_handle_opendir(handle,
-                                       current_path.data);
-                       ret = lttng_dynamic_array_add_element(&frames,
-                                       &new_frame);
-                        if (ret) {
-                               ERR("Failed to push context frame during recursive directory removal");
-                               rmdir_frame_fini(&new_frame);
-                               goto end;
-                        }
-                       current_frame_idx++;
-                       break;
-                }
-               if (!entry) {
-                       ret = lttng_directory_handle_rmdir(handle,
-                                       current_path.data);
-                        if (ret) {
-                               PERROR("Failed to remove \"%s\" during recursive directory removal",
-                                               current_path.data);
-                               goto end;
-                        }
-                       ret = lttng_dynamic_array_remove_element(&frames,
-                                       current_frame_idx);
+               /* Pop rmdir frame. */
+               if (current_frame->empty) {
+                       ret = lttng_directory_handle_rmdir(
+                                       handle, current_path.data);
                        if (ret) {
-                               ERR("Failed to pop context frame during recursive directory removal");
-                               goto end;
+                               if ((flags & LTTNG_DIRECTORY_HANDLE_FAIL_NON_EMPTY_FLAG) ||
+                                               errno != ENOENT) {
+                                       PERROR("Failed to remove \"%s\" during recursive directory removal",
+                                                       current_path.data);
+                                       goto end;
+                               }
+                               DBG("Non-existing directory stream during recursive directory removal");
                        }
-                       current_frame_idx--;
-                }
+               } else if (current_frame->parent_frame_idx >= 0) {
+                       struct rmdir_frame *parent_frame;
+
+                       parent_frame = lttng_dynamic_array_get_element(&frames,
+                                       current_frame->parent_frame_idx);
+                       assert(parent_frame);
+                       parent_frame->empty = false;
+               }
+               ret = lttng_dynamic_array_remove_element(
+                               &frames, current_frame_idx);
+               if (ret) {
+                       ERR("Failed to pop context frame during recursive directory removal");
+                       goto end;
+               }
+               current_frame_idx--;
        }
 end:
        lttng_dynamic_array_reset(&frames);
@@ -1201,26 +1263,28 @@ end:
 LTTNG_HIDDEN
 int lttng_directory_handle_remove_subdirectory_recursive(
                const struct lttng_directory_handle *handle,
-               const char *name)
+               const char *name,
+               int flags)
 {
        return lttng_directory_handle_remove_subdirectory_recursive_as_user(
-                       handle, name, NULL);
+                       handle, name, NULL, flags);
 }
 
 LTTNG_HIDDEN
 int lttng_directory_handle_remove_subdirectory_recursive_as_user(
                const struct lttng_directory_handle *handle,
                const char *name,
-               const struct lttng_credentials *creds)
+               const struct lttng_credentials *creds,
+               int flags)
 {
        int ret;
 
        if (!creds) {
                /* Run as current user. */
-               ret = remove_directory_recursive(handle, name);
+               ret = remove_directory_recursive(handle, name, flags);
        } else {
                ret = _run_as_rmdir_recursive(handle, name, creds->uid,
-                               creds->gid);
+                               creds->gid, flags);
        }
        return ret;
 }
index 6f2df1ba18c2eeb4048f9a636979175456e6ed94..26da76bcc7d7a751971adee30a9cd252f2235a84 100644 (file)
 #include <common/macros.h>
 #include <common/credentials.h>
 
+enum lttng_directory_handle_rmdir_recursive_flags {
+       LTTNG_DIRECTORY_HANDLE_FAIL_NON_EMPTY_FLAG = (1U << 0),
+       LTTNG_DIRECTORY_HANDLE_SKIP_NON_EMPTY_FLAG = (1U << 1),
+};
+
 /*
  * Some platforms, such as Solaris 10, do not support directory file descriptors
  * and their associated functions (*at(...)), which are defined in POSIX.2008.
@@ -233,20 +238,23 @@ int lttng_directory_handle_remove_subdirectory_as_user(
 /*
  * Remove a subdirectory and remove its contents if it only
  * consists in empty directories.
+ * @flags: enum lttng_directory_handle_rmdir_recursive_flags
  */
 LTTNG_HIDDEN
 int lttng_directory_handle_remove_subdirectory_recursive(
                const struct lttng_directory_handle *handle,
-               const char *name);
+               const char *name, int flags);
 
 /*
  * Remove a subdirectory and remove its contents if it only
  * consists in empty directories as a given user.
+ * @flags: enum lttng_directory_handle_rmdir_recursive_flags
  */
 LTTNG_HIDDEN
 int lttng_directory_handle_remove_subdirectory_recursive_as_user(
                const struct lttng_directory_handle *handle,
                const char *name,
-               const struct lttng_credentials *creds);
+               const struct lttng_credentials *creds,
+               int flags);
 
 #endif /* _COMPAT_PATH_HANDLE_H */
index f8263793dad72c94dec9180d5feb2f4094b0d112..cddddb9ecb99f35e978e34ee30e1797936195c2a 100644 (file)
@@ -90,6 +90,7 @@ struct run_as_unlink_data {
 struct run_as_rmdir_data {
        int dirfd;
        char path[LTTNG_PATH_MAX];
+       int flags; /* enum lttng_directory_handle_rmdir_recursive_flags */
 } LTTNG_PACKED;
 
 struct run_as_extract_elf_symbol_offset_data {
@@ -471,7 +472,7 @@ int _rmdir_recursive(struct run_as_data *data, struct run_as_ret *ret_value)
        data->u.rmdir.dirfd = -1;
 
        ret_value->u.ret = lttng_directory_handle_remove_subdirectory_recursive(
-                       &handle, data->u.rmdir.path);
+                       &handle, data->u.rmdir.path, data->u.rmdir.flags);
        ret_value->_errno = errno;
        ret_value->_error = (ret_value->u.ret) ? true : false;
        lttng_directory_handle_fini(&handle);
@@ -1538,13 +1539,13 @@ error:
 }
 
 LTTNG_HIDDEN
-int run_as_rmdir_recursive(const char *path, uid_t uid, gid_t gid)
+int run_as_rmdir_recursive(const char *path, uid_t uid, gid_t gid, int flags)
 {
-       return run_as_rmdirat_recursive(AT_FDCWD, path, uid, gid);
+       return run_as_rmdirat_recursive(AT_FDCWD, path, uid, gid, flags);
 }
 
 LTTNG_HIDDEN
-int run_as_rmdirat_recursive(int dirfd, const char *path, uid_t uid, gid_t gid)
+int run_as_rmdirat_recursive(int dirfd, const char *path, uid_t uid, gid_t gid, int flags)
 {
        int ret;
        struct run_as_data data = {};
@@ -1559,6 +1560,7 @@ int run_as_rmdirat_recursive(int dirfd, const char *path, uid_t uid, gid_t gid)
                goto error;
        }
        data.u.rmdir.dirfd = dirfd;
+       data.u.rmdir.flags = flags;
        run_as(dirfd == AT_FDCWD ? RUN_AS_RMDIR_RECURSIVE : RUN_AS_RMDIRAT_RECURSIVE,
                        &data, &run_as_ret, uid, gid);
        errno = run_as_ret._errno;
index d3a9523bf17b37a91ec4538b71e7a23e58d9d64d..a6eb46f7eb9e89bd790bf3c74da54ae10c3fdddb 100644 (file)
@@ -62,11 +62,11 @@ int run_as_unlinkat(int dirfd, const char *filename, uid_t uid, gid_t gid);
 LTTNG_HIDDEN
 int run_as_rmdir(const char *path, uid_t uid, gid_t gid);
 LTTNG_HIDDEN
-int run_as_rmdir_recursive(const char *path, uid_t uid, gid_t gid);
+int run_as_rmdir_recursive(const char *path, uid_t uid, gid_t gid, int flags);
 LTTNG_HIDDEN
 int run_as_rmdirat(int dirfd, const char *path, uid_t uid, gid_t gid);
 LTTNG_HIDDEN
-int run_as_rmdirat_recursive(int dirfd, const char *path, uid_t uid, gid_t gid);
+int run_as_rmdirat_recursive(int dirfd, const char *path, uid_t uid, gid_t gid, int flags);
 LTTNG_HIDDEN
 int run_as_rename(const char *old, const char *new, uid_t uid, gid_t gid);
 LTTNG_HIDDEN
index 0d61b866f46d426c120694d9138f57a83be26a5d..4a7efad3ec28dcbea09a6a070b9f9aef5a16dbef 100644 (file)
@@ -519,7 +519,8 @@ error_open:
        if (channel->root_shm_path[0]) {
                (void) run_as_rmdir_recursive(channel->root_shm_path,
                                channel->buffer_credentials.value.uid,
-                               channel->buffer_credentials.value.gid);
+                               channel->buffer_credentials.value.gid,
+                               LTTNG_DIRECTORY_HANDLE_SKIP_NON_EMPTY_FLAG);
        }
        free(stream_fds);
 error_alloc:
@@ -2349,7 +2350,8 @@ void lttng_ustconsumer_free_channel(struct lttng_consumer_channel *chan)
        if (chan->root_shm_path[0]) {
                (void) run_as_rmdir_recursive(chan->root_shm_path,
                                chan->buffer_credentials.value.uid,
-                               chan->buffer_credentials.value.gid);
+                               chan->buffer_credentials.value.gid,
+                               LTTNG_DIRECTORY_HANDLE_SKIP_NON_EMPTY_FLAG);
        }
        free(chan->stream_fds);
 }
index 13db3f4bb891e3433e2a8a17eaeeba81408e32e2..6d3028a35c566e7b56687fcaa956d2b2270b7010 100644 (file)
@@ -80,7 +80,7 @@ int main(int argc, char **argv)
        ok(dir_exists(created_dir), "Folder %s exists", created_dir);
 
        ret = lttng_directory_handle_remove_subdirectory_recursive(
-                       &test_dir_handle, "a");
+                       &test_dir_handle, "a", LTTNG_DIRECTORY_HANDLE_FAIL_NON_EMPTY_FLAG);
        ok(ret == 0, "Recursively removed directory hierarchy %s by removing %s",
                        DIR_HIERARCHY, "a");
 end:
This page took 0.0450739999999999 seconds and 4 git commands to generate.