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