#include "../../unity/unity.h" #include #include #include #include #include #include #include /* Unity setup/teardown */ void setUp(void) { /* No global setup needed */ } void tearDown(void) { /* No global teardown needed */ } /* Helpers */ 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; /* caller must free string after rmdir-ing dir */ } 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) { /* Try rmdir; ignore errors if not present */ if (rmdir(path) == 0) return 0; if (errno == ENOENT || errno == ENOTEMPTY) return 0; return -1; } /* Because process_dir may modify its dir argument, provide a fresh modifiable buffer */ 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; } /* Build a default options struct with no SELinux and no announcements. */ 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; /* 0777 */ o->mode_bits = S_IRWXU | S_IRWXG | S_IRWXO;/* affect all rwx bits */ o->set_security_context = NULL; o->created_directory_format = NULL; /* avoid printing */ } /* Tests */ 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 target does not exist */ ensure_dir_absent(target); struct mkdir_options o; init_options_no_selinux(&o, 0); /* no -p */ 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"); /* Cleanup */ 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); /* Pre-create the directory */ 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); /* no -p */ 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"); /* Cleanup */ 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); /* with -p behavior */ 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"); /* Cleanup in reverse order */ 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); /* Do not create m1: ensure missing parent */ struct mkdir_options o; init_options_no_selinux(&o, 0); /* no -p */ 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"); /* Cleanup: just the base temp directory */ TEST_ASSERT_EQUAL_INT(0, rmdir(base)); free(p2); free(p1); free(base); } /* Unity main */ 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(); }