From f94c68ef867327c42d873c508301bfcdc4c66801 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pavel=20B=C5=99ezina?= <pbrezina@redhat.com>
Date: Wed, 13 Jul 2016 12:17:58 +0200
Subject: [PATCH 1/2] utils: add remove_subtree

Remove all entries in a directory but will
not remove the directory itself.
---
 src/tests/files-tests.c | 53 +++++++++++++++++++++++++++++++++++++++++++++++++
 src/tools/files.c       | 35 +++++++++++++++++++++++++-------
 src/tools/tools_util.h  |  1 +
 3 files changed, 82 insertions(+), 7 deletions(-)

diff --git a/src/tests/files-tests.c b/src/tests/files-tests.c
index 596069e2858e07953b3d48f6b8015ce66e2dd423..e96a60af1817b5f7a2e99d8b09ebc91c1a52667b 100644
--- a/src/tests/files-tests.c
+++ b/src/tests/files-tests.c
@@ -153,6 +153,58 @@ START_TEST(test_remove_tree)
 }
 END_TEST
 
+START_TEST(test_remove_subtree)
+{
+    int ret;
+    char origpath[PATH_MAX+1];
+
+    errno = 0;
+    fail_unless(getcwd(origpath, PATH_MAX) == origpath, "Cannot getcwd\n");
+    fail_unless(errno == 0, "Cannot getcwd\n");
+
+    DEBUG(SSSDBG_FUNC_DATA, "About to delete %s\n", dir_path);
+
+    /* create a file */
+    ret = chdir(dir_path);
+    fail_if(ret == -1, "Cannot chdir1\n");
+
+    ret = create_simple_file("bar", "bar");
+    fail_if(ret == -1, "Cannot create file1\n");
+
+    /* create a subdir and file inside it */
+    ret = mkdir("subdir", 0700);
+    fail_if(ret == -1, "Cannot create subdir\n");
+
+    ret = chdir("subdir");
+    fail_if(ret == -1, "Cannot chdir\n");
+
+    ret = create_simple_file("foo", "foo");
+    fail_if(ret == -1, "Cannot create file\n");
+
+    /* create another subdir, empty this time */
+    ret = mkdir("subdir2", 0700);
+    fail_if(ret == -1, "Cannot create subdir\n");
+
+    ret = chdir(origpath);
+    fail_if(ret == -1, "Cannot chdir2\n");
+
+    /* go back */
+    ret = chdir(origpath);
+    fail_if(ret == -1, "Cannot chdir\n");
+
+    /* and finally wipe it out.. */
+    ret = remove_subtree(dir_path);
+    fail_unless(ret == EOK, "remove_subtree failed\n");
+
+    /* check if really gone */
+    ret = access(dir_path, F_OK);
+    fail_unless(ret == 0, "directory was deleted\n");
+
+    ret = rmdir(dir_path);
+    fail_unless(ret == 0, "unable to delete root directory\n");
+}
+END_TEST
+
 START_TEST(test_simple_copy)
 {
     int ret;
@@ -337,6 +389,7 @@ static Suite *files_suite(void)
                               teardown_files_test);
 
     tcase_add_test(tc_files, test_remove_tree);
+    tcase_add_test(tc_files, test_remove_subtree);
     tcase_add_test(tc_files, test_simple_copy);
     tcase_add_test(tc_files, test_copy_file);
     tcase_add_test(tc_files, test_copy_symlink);
diff --git a/src/tools/files.c b/src/tools/files.c
index 8f1aa68beeb2676b56733f49550de170b404c789..9f4e7caa7257144702c417c39bc1643f0be8661a 100644
--- a/src/tools/files.c
+++ b/src/tools/files.c
@@ -137,7 +137,8 @@ static int sss_futime_set(int fd, const struct stat *statp)
 static int remove_tree_with_ctx(TALLOC_CTX *mem_ctx,
                                 int parent_fd,
                                 const char *dir_name,
-                                dev_t parent_dev);
+                                dev_t parent_dev,
+                                bool keep_root_dir);
 
 int remove_tree(const char *root)
 {
@@ -149,7 +150,22 @@ int remove_tree(const char *root)
         return ENOMEM;
     }
 
-    ret = remove_tree_with_ctx(tmp_ctx, AT_FDCWD, root, 0);
+    ret = remove_tree_with_ctx(tmp_ctx, AT_FDCWD, root, 0, false);
+    talloc_free(tmp_ctx);
+    return ret;
+}
+
+int remove_subtree(const char *root)
+{
+    TALLOC_CTX *tmp_ctx = NULL;
+    int ret;
+
+    tmp_ctx = talloc_new(NULL);
+    if (!tmp_ctx) {
+        return ENOMEM;
+    }
+
+    ret = remove_tree_with_ctx(tmp_ctx, AT_FDCWD, root, 0, true);
     talloc_free(tmp_ctx);
     return ret;
 }
@@ -162,7 +178,8 @@ int remove_tree(const char *root)
 static int remove_tree_with_ctx(TALLOC_CTX *mem_ctx,
                                 int parent_fd,
                                 const char *dir_name,
-                                dev_t parent_dev)
+                                dev_t parent_dev,
+                                bool keep_root_dir)
 {
     struct dirent *result;
     struct stat statres;
@@ -213,7 +230,8 @@ static int remove_tree_with_ctx(TALLOC_CTX *mem_ctx,
                 goto fail;
             }
 
-            ret = remove_tree_with_ctx(mem_ctx, dir_fd, result->d_name, statres.st_dev);
+            ret = remove_tree_with_ctx(mem_ctx, dir_fd, result->d_name,
+                                       statres.st_dev, false);
             if (ret != EOK) {
                 DEBUG(SSSDBG_CRIT_FAILURE,
                       "Removing subdirectory failed: [%d][%s]\n",
@@ -239,9 +257,12 @@ static int remove_tree_with_ctx(TALLOC_CTX *mem_ctx,
         goto fail;
     }
 
-    ret = unlinkat(parent_fd, dir_name, AT_REMOVEDIR);
-    if (ret == -1) {
-        ret = errno;
+    if (!keep_root_dir) {
+        /* Remove also root directory. */
+        ret = unlinkat(parent_fd, dir_name, AT_REMOVEDIR);
+        if (ret == -1) {
+            ret = errno;
+        }
     }
 
     ret = EOK;
diff --git a/src/tools/tools_util.h b/src/tools/tools_util.h
index f31e843deb7122ee52664229b75eeb92878abe54..389c7b5c4563118ccb7fdc9448a0879638a35a0b 100644
--- a/src/tools/tools_util.h
+++ b/src/tools/tools_util.h
@@ -113,6 +113,7 @@ errno_t sss_mc_refresh_grouplist(struct tools_ctx *tctx,
 
 /* from files.c */
 int remove_tree(const char *root);
+int remove_subtree(const char *root);
 
 int copy_tree(const char *src_root, const char *dst_root,
               mode_t mode_root, uid_t uid, gid_t gid);
-- 
2.1.0

