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