95d2d09efe01c9b452341d3c0b437a2b49849c41
[tinc] / test / unit / test_fs.c
1 #include "unittest.h"
2 #include "../../src/fs.h"
3 #include "../../src/names.h"
4 #include "../../src/xalloc.h"
5
6 #ifndef HAVE_WINDOWS
7
8 #define FAKE_PATH "nonexistentreallyfakepath"
9
10 typedef struct {
11         const char *arg;
12         const char *want;
13 } testcase_t;
14
15 static void test_absolute_path_on_absolute_returns_it(void **state) {
16         (void)state;
17
18         const char *paths[] = {"/", "/foo", "/foo/./../bar"};
19
20         for(size_t i = 0; i < sizeof(paths) / sizeof(*paths); ++i) {
21                 char *got = absolute_path(paths[i]);
22                 assert_ptr_not_equal(paths[i], got);
23                 assert_string_equal(paths[i], got);
24                 free(got);
25         }
26 }
27
28 static void test_absolute_path_on_empty_returns_null(void **state) {
29         (void)state;
30         assert_null(absolute_path(NULL));
31         assert_null(absolute_path("\0"));
32 }
33
34 static void test_absolute_path_relative(void **state) {
35         (void)state;
36
37         testcase_t cases[] = {
38                 {".", "/"},
39                 {"foo", "/foo"},
40                 {"./"FAKE_PATH, "/./"FAKE_PATH},
41                 {"../foo/./../"FAKE_PATH, "/../foo/./../"FAKE_PATH},
42         };
43
44         for(size_t i = 0; i < sizeof(cases) / sizeof(*cases); ++i) {
45                 char *got = absolute_path(cases[i].arg);
46                 assert_string_equal(cases[i].want, got);
47                 free(got);
48         }
49 }
50
51 static int setup_path_unix(void **state) {
52         (void)state;
53         assert_int_equal(0, chdir("/"));
54         return 0;
55 }
56
57 const char tmp_template[] = "/tmp/tinc.test.fs.XXXXXX";
58 char tmp[sizeof(tmp_template)];
59
60 static int setup_temp_dir(void **state) {
61         (void)state;
62         strcpy(tmp, tmp_template);
63         assert_ptr_equal(tmp, mkdtemp(tmp));
64         confdir = xstrdup(tmp);
65         xasprintf(&confbase, "%s/conf", tmp);
66         return 0;
67 }
68
69 static int teardown_temp_dir(void **state) {
70         (void)state;
71         free(confdir);
72         free(confbase);
73         return 0;
74 }
75
76 static void test_makedir(tinc_dir_t dir, bool exists) {
77         char path[PATH_MAX];
78         char container[PATH_MAX] = {0};
79
80         switch(dir) {
81         case DIR_CONFDIR:
82                 strcpy(path, tmp);
83                 break;
84
85         case DIR_CONFBASE:
86                 sprintf(path, "%s/conf", tmp);
87                 strcpy(container, tmp);
88                 break;
89
90         case DIR_CACHE:
91                 sprintf(path, "%s/conf/cache", tmp);
92                 sprintf(container, "%s/conf", tmp);
93                 break;
94
95         case DIR_HOSTS:
96                 sprintf(path, "%s/conf/hosts", tmp);
97                 sprintf(container, "%s/conf", tmp);
98                 break;
99
100         case DIR_INVITATIONS:
101                 sprintf(path, "%s/conf/invitations", tmp);
102                 sprintf(container, "%s/conf", tmp);
103                 break;
104         }
105
106         struct stat st;
107
108         if(exists) {
109                 assert_int_equal(0, stat(path, &st));
110         } else {
111                 assert_int_equal(-1, stat(path, &st));
112                 assert_int_equal(ENOENT, errno);
113         }
114
115         // Deny write access and make sure makedirs() detects that
116         if(*container) {
117                 assert_int_equal(0, chmod(tmp, 0));
118                 assert_false(makedirs(dir));
119                 assert_int_equal(0, chmod(tmp, 0755));
120         }
121
122         // Now test the happy path
123         assert_true(makedirs(dir));
124         assert_int_equal(0, stat(path, &st));
125         assert_true(S_ISDIR(st.st_mode));
126         assert_int_equal(0, access(path, R_OK | W_OK));
127
128         // Make sure no other directories were created
129         if(*container) {
130                 DIR *d = opendir(container);
131                 assert_non_null(d);
132
133                 struct dirent *ent;
134
135                 while((ent = readdir(d))) {
136                         if(strcmp(".", ent->d_name) && strcmp("..", ent->d_name)) {
137                                 assert_int_equal(st.st_ino, ent->d_ino);
138                                 assert_true(ent->d_type & DT_DIR);
139                         }
140                 }
141
142                 closedir(d);
143         }
144 }
145
146 static void test_makedirs_cache(void **state) {
147         (void)state;
148         test_makedir(DIR_CACHE, false);
149 }
150
151 static void test_makedirs_confbase(void **state) {
152         (void)state;
153         test_makedir(DIR_CONFBASE, false);
154 }
155
156 static void test_makedirs_confdir(void **state) {
157         (void)state;
158         test_makedir(DIR_CONFDIR, true);
159 }
160
161 static void test_makedirs_hosts(void **state) {
162         (void)state;
163         test_makedir(DIR_HOSTS, false);
164 }
165
166 static void test_makedirs_invitations(void **state) {
167         (void)state;
168         test_makedir(DIR_INVITATIONS, false);
169 }
170
171 static int setup_umask(void **state) {
172         (void)state;
173         umask(0);
174         return 0;
175 }
176
177 static void test_fopenmask_existing(void **state) {
178         (void)state;
179
180         struct stat st;
181         strcpy(tmp, tmp_template);
182
183         int fd = mkstemp(tmp);
184         assert_int_not_equal(-1, fd);
185         close(fd);
186
187         assert_int_equal(0, chmod(tmp, 0755));
188         assert_int_equal(0, stat(tmp, &st));
189         assert_int_equal(0755, st.st_mode & 0777);
190
191         FILE *f = fopenmask(tmp, "r", 0700);
192         assert_non_null(f);
193         fclose(f);
194
195         assert_int_equal(0, stat(tmp, &st));
196         assert_int_equal(0700, st.st_mode & 0777);
197 }
198
199 static void test_fopenmask_new(void **state) {
200         (void)state;
201
202         struct stat st;
203         strcpy(tmp, tmp_template);
204
205         // mktemp() nags about safety and using better alternatives
206         int fd = mkstemp(tmp);
207         assert_int_not_equal(-1, fd);
208         close(fd);
209         unlink(tmp);
210
211         FILE *f = fopenmask(tmp, "w", 0750);
212         assert_non_null(f);
213         fclose(f);
214
215         assert_int_equal(0, stat(tmp, &st));
216         assert_int_equal(0750, st.st_mode & 0777);
217 }
218
219 #endif // HAVE_WINDOWS
220
221 static void test_makedirs_bad(void **state) {
222         (void)state;
223
224         assert_false(makedirs(0));
225
226         confbase = NULL; // free not needed, just make it obvious that confbase is NULL
227         assert_false(makedirs(DIR_CACHE));
228         assert_false(makedirs(DIR_CONFBASE));
229         assert_false(makedirs(DIR_HOSTS));
230         assert_false(makedirs(DIR_INVITATIONS));
231
232         confdir = NULL; // same
233         assert_false(makedirs(DIR_CONFDIR));
234 }
235
236 int main(void) {
237         const struct CMUnitTest tests[] = {
238 #ifndef HAVE_WINDOWS
239                 cmocka_unit_test_setup(test_absolute_path_on_absolute_returns_it, setup_path_unix),
240                 cmocka_unit_test_setup(test_absolute_path_on_empty_returns_null, setup_path_unix),
241                 cmocka_unit_test_setup(test_absolute_path_relative, setup_path_unix),
242                 cmocka_unit_test_setup_teardown(test_makedirs_cache, setup_temp_dir, teardown_temp_dir),
243                 cmocka_unit_test_setup_teardown(test_makedirs_confbase, setup_temp_dir, teardown_temp_dir),
244                 cmocka_unit_test_setup_teardown(test_makedirs_confdir, setup_temp_dir, teardown_temp_dir),
245                 cmocka_unit_test_setup_teardown(test_makedirs_hosts, setup_temp_dir, teardown_temp_dir),
246                 cmocka_unit_test_setup_teardown(test_makedirs_invitations, setup_temp_dir, teardown_temp_dir),
247                 cmocka_unit_test_setup(test_fopenmask_existing, setup_umask),
248                 cmocka_unit_test_setup(test_fopenmask_new, setup_umask),
249 #endif
250                 cmocka_unit_test(test_makedirs_bad),
251         };
252         return cmocka_run_group_tests(tests, NULL, NULL);
253 }