File size: 5,187 Bytes
78d2150 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 |
#include "../../unity/unity.h"
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <stdio.h>
// 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();
} |