| | #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(); |
| | } |