diff --git a/src/util-files.h b/src/util-files.h index 0cb51b15..a61358c4 100644 --- a/src/util-files.h +++ b/src/util-files.h @@ -26,6 +26,7 @@ #include "config.h" #include +#include #include #include #include @@ -54,6 +55,38 @@ mkdir_p(const char *dir) return (rc == -1 && errno != EEXIST) ? -errno : 0; } +DEFINE_TRIVIAL_CLEANUP_FUNC(DIR*, closedir); + +static inline int +rmdir_r(const char *dir) +{ + _cleanup_(closedirp) DIR *d = opendir(dir); + if (!d) + return -errno; + + struct dirent *entry; + int rc = 0; + + while (rc >= 0 && (entry = readdir(d))) { + if (streq(entry->d_name, ".") || streq(entry->d_name, "..")) + continue; + + _autofree_ char *path = strdup_printf("%s/%s", dir, entry->d_name); + + struct stat st; + if (stat(path, &st) < 0) + return -errno; + + if (S_ISDIR(st.st_mode)) + rc = rmdir_r(path); + else + rc = unlink(path) < 0 ? -errno : 0; + } + rc = rmdir(dir) < 0 ? -errno : rc; + + return rc; +} + static inline void xclose(int *fd) { diff --git a/test/test-utils.c b/test/test-utils.c index 91775268..cbbff6ed 100644 --- a/test/test-utils.c +++ b/test/test-utils.c @@ -75,6 +75,44 @@ START_TEST(mkdir_p_test) } END_TEST +START_TEST(rmdir_r_test) +{ + const char *testdir = "/tmp/litest_rmdir_test"; + _autofree_ char *path = strdup_printf("%s/foo/bar/baz", testdir); + mkdir_p(path); + + _autofree_ char *f1 = strdup_printf("%s/remain", testdir); + _autofree_ char *f2 = strdup_printf("%s/foo/remove", testdir); + _autofree_ char *f3 = strdup_printf("%s/foo/bar/to-remove", testdir); + _autofree_ char *f4 = strdup_printf("%s/foo/bar/baz/wipeme", testdir); + + litest_assert_errno_success(close(open(f1, O_WRONLY | O_CREAT, 0644))); + litest_assert_errno_success(close(open(f2, O_WRONLY | O_CREAT, 0644))); + litest_assert_errno_success(close(open(f3, O_WRONLY | O_CREAT, 0644))); + litest_assert_errno_success(close(open(f4, O_WRONLY | O_CREAT, 0644))); + + struct stat st; + litest_assert_errno_success(stat(f1, &st)); + litest_assert_errno_success(stat(f2, &st)); + litest_assert_errno_success(stat(f3, &st)); + litest_assert_errno_success(stat(f4, &st)); + + _autofree_ char *rmpath = strdup_printf("%s/foo/", testdir); + int rc = rmdir_r(rmpath); + litest_assert_neg_errno_success(rc); + + litest_assert_errno_success(stat(f1, &st)); + litest_assert_errno_success(stat(testdir, &st)); + + rc = stat(f2, &st) < 0 ? -errno : 0; + litest_assert_int_eq(rc, -ENOENT); + rc = stat(f3, &st) < 0 ? -errno : 0; + litest_assert_int_eq(rc, -ENOENT); + rc = stat(f4, &st) < 0 ? -errno : 0; + litest_assert_int_eq(rc, -ENOENT); +} +END_TEST + START_TEST(find_files_test) { _autofree_ char *dirname = strdup("/tmp/litest_find_files_test.XXXXXX"); @@ -2406,6 +2444,7 @@ int main(void) ADD_TEST(auto_test); ADD_TEST(mkdir_p_test); + ADD_TEST(rmdir_r_test); ADD_TEST(find_files_test); ADD_TEST(array_for_each);