#include "../../unity/unity.h" #include #include #include #include #include #include #include // The function under test is static in this translation unit and thus directly callable: // static bool mode_changed (int dir_fd, char const *file, char const *file_full_name, // mode_t old_mode, mode_t new_mode); // Access the existing file-scope static 'force_silent' defined in the program source. extern bool force_silent; // The variable is defined above in the same translation unit. // Helpers static mode_t get_mode(const char *path) { struct stat st; TEST_ASSERT_EQUAL_INT_MESSAGE(0, stat(path, &st), "stat on temp file failed"); return st.st_mode; } static void make_temp_path(char *buf, size_t buflen) { // Create a unique template in /tmp snprintf(buf, buflen, "/tmp/chmod_mode_changed_%ld_XXXXXX", (long)getpid()); } static void create_temp_file(char *path_buf, size_t buflen) { make_temp_path(path_buf, buflen); int fd = mkstemp(path_buf); TEST_ASSERT_MESSAGE(fd >= 0, "mkstemp failed"); // Close immediately; we only need the file to exist for stat/chmod close(fd); } static void remove_temp_file(const char *path) { // Best-effort cleanup unlink(path); } void setUp(void) { /* Setup code here, or leave empty */ } void tearDown(void) { /* Cleanup code here, or leave empty */ } // 1) No special bits, no change => expect false void test_mode_changed_no_special_no_change(void) { char path[256]; create_temp_file(path, sizeof(path)); mode_t old_mode = get_mode(path); mode_t new_mode = old_mode; // No change, and no special bits added bool changed = mode_changed(AT_FDCWD, path, path, old_mode, new_mode); TEST_ASSERT_FALSE(changed); remove_temp_file(path); } // 2) No special bits, with a permission bit change => expect true void test_mode_changed_no_special_with_change(void) { char path[256]; create_temp_file(path, sizeof(path)); mode_t old_mode = get_mode(path); // Flip owner execute bit (does not include suid/sgid/sticky) mode_t new_mode = old_mode ^ S_IXUSR; bool changed = mode_changed(AT_FDCWD, path, path, old_mode, new_mode); TEST_ASSERT_TRUE(changed); remove_temp_file(path); } // 3) Special bits present; fstatat fails (nonexistent file) => expect false void test_mode_changed_special_fstatat_failure(void) { // Suppress error diagnostics from the function for this test bool prev_silent = force_silent; force_silent = true; const char *missing = "/this/path/does/not/exist/___definitely___"; mode_t old_mode = 0; // Set a special bit to trigger the fstatat path mode_t new_mode = old_mode | S_ISUID; bool changed = mode_changed(AT_FDCWD, missing, missing, old_mode, new_mode); TEST_ASSERT_FALSE(changed); force_silent = prev_silent; } // 4) Special bits present; fstatat succeeds; no actual change to file => expect false void test_mode_changed_special_no_actual_change(void) { char path[256]; create_temp_file(path, sizeof(path)); mode_t old_mode = get_mode(path); // Trigger special-bit path; function will use fstatat to get actual (unchanged) mode mode_t new_mode = old_mode | S_ISUID; bool changed = mode_changed(AT_FDCWD, path, path, old_mode, new_mode); TEST_ASSERT_FALSE(changed); remove_temp_file(path); } // 5) Special bits present; fstatat succeeds; actual mode changed via chmod => expect true void test_mode_changed_special_actual_change(void) { char path[256]; create_temp_file(path, sizeof(path)); mode_t old_mode = get_mode(path); // Change a permission bit on the actual file (mask to permissions only for chmod) mode_t changed_perm = (old_mode ^ S_IXUSR) & 07777; int rc = chmod(path, changed_perm); TEST_ASSERT_EQUAL_INT_MESSAGE(0, rc, "chmod to alter file mode failed"); // Trigger special-bit path; function will pick up the actual new mode from stat mode_t new_mode_param = old_mode | S_ISUID; bool changed = mode_changed(AT_FDCWD, path, path, old_mode, new_mode_param); TEST_ASSERT_TRUE(changed); // Restore original perms (best effort) chmod(path, old_mode & 07777); remove_temp_file(path); } // 6) No special bits, difference only in file type bits should be ignored => expect false void test_mode_changed_ignores_file_type_bits(void) { // Craft synthetic modes: permission bits equal, file type bits different. mode_t old_mode = S_IFREG | 0644; mode_t new_mode = S_IFDIR | 0644; // Different type, same perms; no special bits bool changed = mode_changed(AT_FDCWD, "unused", "unused", old_mode, new_mode); TEST_ASSERT_FALSE(changed); } int main(void) { UNITY_BEGIN(); RUN_TEST(test_mode_changed_no_special_no_change); RUN_TEST(test_mode_changed_no_special_with_change); RUN_TEST(test_mode_changed_special_fstatat_failure); RUN_TEST(test_mode_changed_special_no_actual_change); RUN_TEST(test_mode_changed_special_actual_change); RUN_TEST(test_mode_changed_ignores_file_type_bits); return UNITY_END(); }