| #include "../../unity/unity.h" |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <errno.h> |
| #include <sys/stat.h> |
| #include <sys/types.h> |
| #include <unistd.h> |
|
|
| |
| void setUp(void) { |
| |
| } |
|
|
| void tearDown(void) { |
| |
| } |
|
|
| |
|
|
| static char *make_temp_dir(void) |
| { |
| char *tmpl = strdup("/tmp/cu_mkdir_process_dir_XXXXXX"); |
| if (!tmpl) |
| return NULL; |
| if (!mkdtemp(tmpl)) { |
| free(tmpl); |
| return NULL; |
| } |
| return tmpl; |
| } |
|
|
| static char *path_join2(const char *a, const char *b) |
| { |
| size_t la = strlen(a); |
| size_t lb = strlen(b); |
| int need_slash = (la == 0 || a[la - 1] != '/'); |
| size_t len = la + (need_slash ? 1 : 0) + lb + 1; |
| char *res = (char *)malloc(len); |
| if (!res) return NULL; |
| strcpy(res, a); |
| if (need_slash) strcat(res, "/"); |
| strcat(res, b); |
| return res; |
| } |
|
|
| static int exists_is_dir(const char *path) |
| { |
| struct stat st; |
| if (stat(path, &st) != 0) |
| return 0; |
| return S_ISDIR(st.st_mode); |
| } |
|
|
| static int ensure_dir_absent(const char *path) |
| { |
| |
| if (rmdir(path) == 0) |
| return 0; |
| if (errno == ENOENT || errno == ENOTEMPTY) |
| return 0; |
| return -1; |
| } |
|
|
| |
| static char *dup_cstr(const char *s) |
| { |
| size_t n = strlen(s); |
| char *p = (char *)malloc(n + 1); |
| if (!p) return NULL; |
| memcpy(p, s, n + 1); |
| return p; |
| } |
|
|
| |
| static void init_options_no_selinux(struct mkdir_options *o, int with_parents) |
| { |
| memset(o, 0, sizeof(*o)); |
| mode_t old = umask(0); |
| umask(old); |
| o->make_ancestor_function = with_parents ? make_ancestor : NULL; |
| o->umask_ancestor = old; |
| o->umask_self = old; |
| o->mode = S_IRWXU | S_IRWXG | S_IRWXO; |
| o->mode_bits = S_IRWXU | S_IRWXG | S_IRWXO; |
| o->set_security_context = NULL; |
| o->created_directory_format = NULL; |
| } |
|
|
| |
|
|
| static void test_process_dir_creates_directory_when_parent_exists(void) |
| { |
| char *base = make_temp_dir(); |
| TEST_ASSERT_NOT_NULL(base); |
|
|
| char *target = path_join2(base, "simple"); |
| TEST_ASSERT_NOT_NULL(target); |
|
|
| |
| ensure_dir_absent(target); |
|
|
| struct mkdir_options o; |
| init_options_no_selinux(&o, 0); |
|
|
| char *mutable_path = dup_cstr(target); |
| TEST_ASSERT_NOT_NULL(mutable_path); |
|
|
| int ret = process_dir(mutable_path, NULL, &o); |
| free(mutable_path); |
|
|
| TEST_ASSERT_EQUAL_INT(EXIT_SUCCESS, ret); |
| TEST_ASSERT_TRUE_MESSAGE(exists_is_dir(target), "Target directory should exist"); |
|
|
| |
| TEST_ASSERT_EQUAL_INT(0, rmdir(target)); |
| TEST_ASSERT_EQUAL_INT(0, rmdir(base)); |
|
|
| free(target); |
| free(base); |
| } |
|
|
| static void test_process_dir_existing_directory_without_parents_fails(void) |
| { |
| char *base = make_temp_dir(); |
| TEST_ASSERT_NOT_NULL(base); |
|
|
| char *target = path_join2(base, "exists"); |
| TEST_ASSERT_NOT_NULL(target); |
|
|
| |
| int mk = mkdir(target, 0700); |
| TEST_ASSERT_TRUE_MESSAGE(mk == 0 || errno == EEXIST, "mkdir pre-create failed"); |
|
|
| struct mkdir_options o; |
| init_options_no_selinux(&o, 0); |
|
|
| char *mutable_path = dup_cstr(target); |
| TEST_ASSERT_NOT_NULL(mutable_path); |
|
|
| int ret = process_dir(mutable_path, NULL, &o); |
| free(mutable_path); |
|
|
| TEST_ASSERT_EQUAL_INT_MESSAGE(EXIT_FAILURE, ret, "Expected failure when directory already exists without -p"); |
|
|
| |
| TEST_ASSERT_EQUAL_INT(0, rmdir(target)); |
| TEST_ASSERT_EQUAL_INT(0, rmdir(base)); |
|
|
| free(target); |
| free(base); |
| } |
|
|
| static void test_process_dir_with_parents_creates_nested_directories(void) |
| { |
| char *base = make_temp_dir(); |
| TEST_ASSERT_NOT_NULL(base); |
|
|
| char *p1 = path_join2(base, "p1"); |
| char *p2 = path_join2(p1, "p2"); |
| char *p3 = path_join2(p2, "p3"); |
| TEST_ASSERT_NOT_NULL(p1); |
| TEST_ASSERT_NOT_NULL(p2); |
| TEST_ASSERT_NOT_NULL(p3); |
|
|
| struct mkdir_options o; |
| init_options_no_selinux(&o, 1); |
|
|
| char *mutable_path = dup_cstr(p3); |
| TEST_ASSERT_NOT_NULL(mutable_path); |
|
|
| int ret = process_dir(mutable_path, NULL, &o); |
| free(mutable_path); |
|
|
| TEST_ASSERT_EQUAL_INT(EXIT_SUCCESS, ret); |
| TEST_ASSERT_TRUE_MESSAGE(exists_is_dir(p1), "p1 should exist"); |
| TEST_ASSERT_TRUE_MESSAGE(exists_is_dir(p2), "p2 should exist"); |
| TEST_ASSERT_TRUE_MESSAGE(exists_is_dir(p3), "p3 should exist"); |
|
|
| |
| TEST_ASSERT_EQUAL_INT(0, rmdir(p3)); |
| TEST_ASSERT_EQUAL_INT(0, rmdir(p2)); |
| TEST_ASSERT_EQUAL_INT(0, rmdir(p1)); |
| TEST_ASSERT_EQUAL_INT(0, rmdir(base)); |
|
|
| free(p3); |
| free(p2); |
| free(p1); |
| free(base); |
| } |
|
|
| static void test_process_dir_no_parents_missing_parent_fails(void) |
| { |
| char *base = make_temp_dir(); |
| TEST_ASSERT_NOT_NULL(base); |
|
|
| char *p1 = path_join2(base, "m1"); |
| char *p2 = path_join2(p1, "m2"); |
| TEST_ASSERT_NOT_NULL(p1); |
| TEST_ASSERT_NOT_NULL(p2); |
|
|
| |
| struct mkdir_options o; |
| init_options_no_selinux(&o, 0); |
|
|
| char *mutable_path = dup_cstr(p2); |
| TEST_ASSERT_NOT_NULL(mutable_path); |
|
|
| int ret = process_dir(mutable_path, NULL, &o); |
| free(mutable_path); |
|
|
| TEST_ASSERT_EQUAL_INT_MESSAGE(EXIT_FAILURE, ret, "Expected failure when parent is missing without -p"); |
| TEST_ASSERT_FALSE_MESSAGE(exists_is_dir(p2), "Leaf directory should not have been created"); |
| TEST_ASSERT_FALSE_MESSAGE(exists_is_dir(p1), "Parent directory should not have been created"); |
|
|
| |
| TEST_ASSERT_EQUAL_INT(0, rmdir(base)); |
|
|
| free(p2); |
| free(p1); |
| free(base); |
| } |
|
|
| |
| int main(void) |
| { |
| UNITY_BEGIN(); |
| RUN_TEST(test_process_dir_creates_directory_when_parent_exists); |
| RUN_TEST(test_process_dir_existing_directory_without_parents_fails); |
| RUN_TEST(test_process_dir_with_parents_creates_nested_directories); |
| RUN_TEST(test_process_dir_no_parents_missing_parent_fails); |
| return UNITY_END(); |
| } |