|
|
#include "../../unity/unity.h" |
|
|
#include <stdio.h> |
|
|
#include <stdlib.h> |
|
|
#include <string.h> |
|
|
#include <errno.h> |
|
|
#include <unistd.h> |
|
|
#include <sys/stat.h> |
|
|
#include <fcntl.h> |
|
|
#include <limits.h> |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static char *make_temp_dir(void) |
|
|
{ |
|
|
char *tmpl = malloc(64); |
|
|
TEST_ASSERT_NOT_NULL_MESSAGE(tmpl, "malloc failed for tempdir"); |
|
|
snprintf(tmpl, 64, "/tmp/chmod_pf_%ld_XXXXXX", (long)getpid()); |
|
|
if (!mkdtemp(tmpl)) { |
|
|
free(tmpl); |
|
|
TEST_FAIL_MESSAGE("mkdtemp failed"); |
|
|
} |
|
|
return tmpl; |
|
|
} |
|
|
|
|
|
|
|
|
static char *join_path(const char *dir, const char *name) |
|
|
{ |
|
|
size_t dl = strlen(dir), nl = strlen(name); |
|
|
size_t need = dl + 1 + nl + 1; |
|
|
char *p = malloc(need); |
|
|
TEST_ASSERT_NOT_NULL_MESSAGE(p, "malloc failed in join_path"); |
|
|
memcpy(p, dir, dl); |
|
|
p[dl] = '/'; |
|
|
memcpy(p + dl + 1, name, nl); |
|
|
p[dl + 1 + nl] = '\0'; |
|
|
return p; |
|
|
} |
|
|
|
|
|
|
|
|
static char *create_file_with_mode(const char *dir, const char *name, mode_t mode) |
|
|
{ |
|
|
char *p = join_path(dir, name); |
|
|
int fd = open(p, O_WRONLY | O_CREAT | O_TRUNC, mode); |
|
|
if (fd < 0) { |
|
|
free(p); |
|
|
TEST_FAIL_MESSAGE("open/create file failed"); |
|
|
} |
|
|
close(fd); |
|
|
return p; |
|
|
} |
|
|
|
|
|
|
|
|
static char *create_dir_with_mode(const char *parent, const char *name, mode_t mode) |
|
|
{ |
|
|
char *p = join_path(parent, name); |
|
|
if (mkdir(p, mode) != 0) { |
|
|
free(p); |
|
|
TEST_FAIL_MESSAGE("mkdir failed"); |
|
|
} |
|
|
return p; |
|
|
} |
|
|
|
|
|
|
|
|
static char *create_symlink(const char *dir, const char *linkname, const char *target_relative) |
|
|
{ |
|
|
char *linkpath = join_path(dir, linkname); |
|
|
if (symlink(target_relative, linkpath) != 0) { |
|
|
free(linkpath); |
|
|
TEST_FAIL_MESSAGE("symlink failed"); |
|
|
} |
|
|
return linkpath; |
|
|
} |
|
|
|
|
|
|
|
|
static mode_t saved_process_umask; |
|
|
|
|
|
|
|
|
void setUp(void) { |
|
|
|
|
|
force_silent = true; |
|
|
diagnose_surprises = false; |
|
|
verbosity = V_off; |
|
|
root_dev_ino = NULL; |
|
|
recurse = false; |
|
|
dereference = -1; |
|
|
umask_value = 0; |
|
|
|
|
|
|
|
|
change = mode_compile("u+x", 0); |
|
|
TEST_ASSERT_NOT_NULL_MESSAGE(change, "mode_compile returned NULL"); |
|
|
|
|
|
|
|
|
saved_process_umask = umask(0); |
|
|
} |
|
|
|
|
|
void tearDown(void) { |
|
|
|
|
|
umask(saved_process_umask); |
|
|
} |
|
|
|
|
|
|
|
|
static int test_fts_flags(void) |
|
|
{ |
|
|
return FTS_CWDFD | FTS_PHYSICAL | FTS_NOCHDIR; |
|
|
} |
|
|
|
|
|
|
|
|
void test_process_files_regular_file_success(void) |
|
|
{ |
|
|
char *td = make_temp_dir(); |
|
|
char *file = create_file_with_mode(td, "file.txt", 0600); |
|
|
|
|
|
char *files[] = { file, NULL }; |
|
|
int flags = test_fts_flags(); |
|
|
|
|
|
bool ok = process_files(files, flags); |
|
|
TEST_ASSERT_TRUE_MESSAGE(ok, "process_files should succeed for a regular file"); |
|
|
|
|
|
|
|
|
struct stat st; |
|
|
TEST_ASSERT_EQUAL_INT_MESSAGE(0, stat(file, &st), "stat failed on created file"); |
|
|
TEST_ASSERT_TRUE_MESSAGE((st.st_mode & S_IXUSR) != 0, "u+x not applied to regular file"); |
|
|
|
|
|
|
|
|
unlink(file); |
|
|
rmdir(td); |
|
|
free(file); |
|
|
free(td); |
|
|
} |
|
|
|
|
|
|
|
|
void test_process_files_nonexistent_returns_false(void) |
|
|
{ |
|
|
char *td = make_temp_dir(); |
|
|
char *missing = join_path(td, "no_such_file_or_dir"); |
|
|
|
|
|
char *files[] = { missing, NULL }; |
|
|
int flags = test_fts_flags(); |
|
|
|
|
|
bool ok = process_files(files, flags); |
|
|
TEST_ASSERT_FALSE_MESSAGE(ok, "process_files should fail for a nonexistent path"); |
|
|
|
|
|
|
|
|
free(missing); |
|
|
rmdir(td); |
|
|
free(td); |
|
|
} |
|
|
|
|
|
|
|
|
void test_process_files_mixed_inputs_accumulate_failure(void) |
|
|
{ |
|
|
char *td = make_temp_dir(); |
|
|
char *missing = join_path(td, "nope"); |
|
|
char *file = create_file_with_mode(td, "ok.txt", 0600); |
|
|
|
|
|
char *files[] = { missing, file, NULL }; |
|
|
int flags = test_fts_flags(); |
|
|
|
|
|
bool ok = process_files(files, flags); |
|
|
TEST_ASSERT_FALSE_MESSAGE(ok, "process_files should return false if any input fails"); |
|
|
|
|
|
|
|
|
unlink(file); |
|
|
free(file); |
|
|
free(missing); |
|
|
rmdir(td); |
|
|
free(td); |
|
|
} |
|
|
|
|
|
|
|
|
void test_process_files_recursive_directory_changes_nested(void) |
|
|
{ |
|
|
|
|
|
change = mode_compile("o+x", 0); |
|
|
TEST_ASSERT_NOT_NULL_MESSAGE(change, "mode_compile returned NULL for o+x"); |
|
|
recurse = true; |
|
|
|
|
|
char *td = make_temp_dir(); |
|
|
char *subdir = create_dir_with_mode(td, "d", 0700); |
|
|
char *nested = create_file_with_mode(subdir, "f.txt", 0600); |
|
|
|
|
|
char *files[] = { subdir, NULL }; |
|
|
int flags = test_fts_flags(); |
|
|
|
|
|
bool ok = process_files(files, flags); |
|
|
TEST_ASSERT_TRUE_MESSAGE(ok, "process_files should succeed with recursion"); |
|
|
|
|
|
|
|
|
struct stat st; |
|
|
TEST_ASSERT_EQUAL_INT_MESSAGE(0, stat(subdir, &st), "stat failed on directory"); |
|
|
TEST_ASSERT_TRUE_MESSAGE((st.st_mode & S_IXOTH) != 0, "o+x not applied to directory"); |
|
|
|
|
|
|
|
|
TEST_ASSERT_EQUAL_INT_MESSAGE(0, stat(nested, &st), "stat failed on nested file"); |
|
|
TEST_ASSERT_TRUE_MESSAGE((st.st_mode & S_IXOTH) != 0, "o+x not applied to nested file"); |
|
|
|
|
|
|
|
|
unlink(nested); |
|
|
rmdir(subdir); |
|
|
rmdir(td); |
|
|
free(nested); |
|
|
free(subdir); |
|
|
free(td); |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void test_process_files_symlink_top_level_success(void) |
|
|
{ |
|
|
char *td = make_temp_dir(); |
|
|
char *target = create_file_with_mode(td, "t.txt", 0600); |
|
|
|
|
|
char *linkpath = create_symlink(td, "l", "t.txt"); |
|
|
|
|
|
char *files[] = { linkpath, NULL }; |
|
|
int flags = test_fts_flags(); |
|
|
|
|
|
bool ok = process_files(files, flags); |
|
|
TEST_ASSERT_TRUE_MESSAGE(ok, "process_files should succeed for a top-level symlink"); |
|
|
|
|
|
|
|
|
struct stat st; |
|
|
TEST_ASSERT_EQUAL_INT_MESSAGE(0, stat(target, &st), "stat failed for symlink target"); |
|
|
TEST_ASSERT_TRUE_MESSAGE((st.st_mode & S_IXUSR) != 0, "u+x not applied to symlink target"); |
|
|
|
|
|
|
|
|
unlink(linkpath); |
|
|
unlink(target); |
|
|
rmdir(td); |
|
|
free(linkpath); |
|
|
free(target); |
|
|
free(td); |
|
|
} |
|
|
|
|
|
int main(void) |
|
|
{ |
|
|
UNITY_BEGIN(); |
|
|
RUN_TEST(test_process_files_regular_file_success); |
|
|
RUN_TEST(test_process_files_nonexistent_returns_false); |
|
|
RUN_TEST(test_process_files_mixed_inputs_accumulate_failure); |
|
|
RUN_TEST(test_process_files_recursive_directory_changes_nested); |
|
|
RUN_TEST(test_process_files_symlink_top_level_success); |
|
|
return UNITY_END(); |
|
|
} |