1 /* Copyright (c) 2016-2018 Dovecot authors, see the included COPYING file */
2
3 #include "test-lib.h"
4 #include "path-util.h"
5 #include "unlink-directory.h"
6 #include "str.h"
7
8 #include <unistd.h>
9 #include <fcntl.h>
10 #include <stdlib.h>
11 #include <sys/stat.h>
12
13 #define TEMP_DIRNAME ".test-path-util"
14
15 static const char *tmpdir;
16 static const char *cwd;
17 static const char *link1;
18 static const char *link2;
19 static const char *link3;
20 static const char *link4;
21
test_local_path(void)22 static void test_local_path(void)
23 {
24 const char *expected = t_strconcat(cwd, "/README.md", NULL);
25 const char *npath = NULL, *error = NULL;
26 test_assert(t_normpath_to("README.md", cwd, &npath, &error) == 0);
27 test_assert_strcmp(npath, expected);
28 }
29
test_absolute_path_no_change(void)30 static void test_absolute_path_no_change(void)
31 {
32 const char *npath = NULL, *error = NULL;
33 test_assert(t_normpath_to("/", "/", &npath, &error) == 0);
34 test_assert_strcmp(npath, "/");
35
36 test_assert(t_normpath_to(cwd, cwd, &npath, &error) == 0);
37 test_assert_strcmp(npath, cwd);
38 }
39
path_height(const char * p)40 static int path_height(const char *p)
41 {
42 int n;
43 for (n = 0; *p != '\0'; ++p)
44 n += *p == '/';
45 return n;
46 }
47
test_travel_to_root(void)48 static void test_travel_to_root(void)
49 {
50 int l = path_height(cwd);
51 const char *npath = cwd;
52 for (npath = cwd; l != 0; l--) {
53 const char *error;
54 test_assert_idx(t_normpath_to("../", npath, &npath, &error) == 0, l);
55 }
56 test_assert_strcmp(npath, "/");
57 }
58
test_extra_slashes(void)59 static void test_extra_slashes(void)
60 {
61 const char *npath = NULL, *error = NULL;
62 test_assert(t_normpath_to(".", cwd, &npath, &error) == 0);
63 test_assert_strcmp(npath, cwd);
64
65 test_assert(t_normpath_to("./", cwd, &npath, &error) == 0);
66 test_assert_strcmp(npath, cwd);
67
68 test_assert(t_normpath_to(".////", cwd, &npath, &error) == 0);
69 test_assert_strcmp(npath, cwd);
70 }
71
test_nonexistent_path(void)72 static void test_nonexistent_path(void)
73 {
74 const char *npath = NULL, *error = NULL;
75 const char *expected = t_strconcat(cwd, "/nonexistent", NULL);
76 test_assert(t_normpath_to("nonexistent", cwd, &npath, &error) == 0);
77 test_assert_strcmp(npath, expected);
78 test_assert(t_realpath_to("nonexistent", cwd, &npath, &error) == -1);
79 test_assert(error != NULL);
80 }
81
test_relative_dotdot(void)82 static void test_relative_dotdot(void)
83 {
84 const char *rel_path = "../"TEMP_DIRNAME;
85 const char *npath = NULL, *error = NULL;
86 test_assert(t_normpath_to(rel_path, tmpdir, &npath, &error) == 0);
87 test_assert_strcmp(npath, tmpdir);
88
89 test_assert(t_normpath_to("..", tmpdir, &npath, &error) == 0);
90 test_assert_strcmp(npath, cwd);
91
92 test_assert(t_normpath_to("../", tmpdir, &npath, &error) == 0);
93 test_assert_strcmp(npath, cwd);
94
95 test_assert(t_normpath_to("../.", tmpdir, &npath, &error) == 0);
96 test_assert_strcmp(npath, cwd);
97 }
98
test_link1(void)99 static void test_link1(void)
100 {
101 const char *old_dir, *npath = NULL, *error = NULL;
102 test_assert(t_realpath_to(link1, "/", &npath, &error) == 0);
103 test_assert_strcmp(npath, tmpdir);
104
105 /* .../link1/link1/child */
106 test_assert(t_realpath_to(t_strconcat(link1, "/link1/child", NULL),
107 "/", &npath, &error) == 0);
108 test_assert_strcmp(npath, t_strconcat(tmpdir, "/child", NULL));
109
110 /* relative link1/link1/child */
111 if (t_get_working_dir(&old_dir, &error) < 0)
112 i_fatal("t_get_working_dir() failed: %s", error);
113 if (chdir(tmpdir) < 0)
114 i_fatal("chdir(%s) failed: %m", tmpdir);
115 test_assert(t_realpath(t_strconcat("link1", "/link1/child", NULL),
116 &npath, &error) == 0);
117 if (chdir(old_dir) < 0)
118 i_fatal("chdir(%s) failed: %m", old_dir);
119 }
120
test_link4(void)121 static void test_link4(void)
122 {
123 const char *npath = NULL, *error = NULL;
124
125 test_assert(t_realpath_to(t_strconcat(link1, "/link4/child", NULL),
126 "/", &npath, &error) == 0);
127 test_assert_strcmp(npath, t_strconcat(tmpdir, "/child", NULL));
128 }
129
test_link_loop(void)130 static void test_link_loop(void)
131 {
132 const char *npath = NULL, *error = NULL;
133 errno = 0;
134 test_assert(t_realpath_to(link2, "/", &npath, &error) == -1);
135 test_assert(errno == ELOOP);
136 test_assert(error != NULL);
137 }
138
test_abspath_vs_normpath(void)139 static void test_abspath_vs_normpath(void)
140 {
141 const char *abs = t_abspath_to("../../bin", "/usr/lib/");
142 test_assert_strcmp(abs, "/usr/lib//../../bin");
143
144 const char *norm = NULL, *error = NULL;
145 test_assert(t_normpath_to("../../bin", "/usr///lib/", &norm, &error) == 0);
146 test_assert_strcmp(norm, "/bin");
147 }
148
create_links(const char * tmpdir)149 static void create_links(const char *tmpdir)
150 {
151 link1 = t_strconcat(tmpdir, "/link1", NULL);
152 if (symlink(tmpdir, link1) < 0)
153 i_fatal("symlink(%s, %s) failed: %m", tmpdir, link1);
154
155 const char *link1_child = t_strconcat(link1, "/child", NULL);
156 int fd = creat(link1_child, 0600);
157 if (fd == -1)
158 i_fatal("creat(%s) failed: %m", link1_child);
159 i_close_fd(&fd);
160
161 /* link2 and link3 point to each other to create a loop */
162 link2 = t_strconcat(tmpdir, "/link2", NULL);
163 link3 = t_strconcat(tmpdir, "/link3", NULL);
164 if (symlink(link3, link2) < 0)
165 i_fatal("symlink(%s, %s) failed: %m", link3, link2);
166 if (symlink(link2, link3) < 0)
167 i_fatal("symlink(%s, %s) failed: %m", link2, link3);
168
169 /* link4 points to link1 */
170 link4 = t_strconcat(tmpdir, "/link4", NULL);
171 if (symlink("link1", link4) < 0)
172 i_fatal("symlink(link1, %s) failed: %m", link4);
173 }
174
test_link_alloc(void)175 static void test_link_alloc(void)
176 {
177 #define COMPONENT_COMPONENT "/component-component"
178 const char *o_tmpdir;
179
180 /* idea here is to make sure component-component
181 would optimally hit to the nearest_power value.
182
183 it has to be big enough to cause requirement for
184 allocation in t_realpath. */
185 string_t *basedir = t_str_new(256);
186 str_append(basedir, cwd);
187 str_append(basedir, "/"TEMP_DIRNAME);
188 size_t len = nearest_power(I_MAX(127, str_len(basedir) + strlen(COMPONENT_COMPONENT) + 1)) -
189 strlen(COMPONENT_COMPONENT);
190
191 while(str_len(basedir) < len) {
192 str_append(basedir, COMPONENT_COMPONENT);
193 (void)mkdir(str_c(basedir), 0700);
194 }
195 o_tmpdir = tmpdir;
196 tmpdir = str_c(basedir);
197
198 create_links(tmpdir);
199
200 test_link1();
201 test_link_loop();
202
203 tmpdir = o_tmpdir;
204 }
205
test_link_alloc2(void)206 static void test_link_alloc2(void)
207 {
208 const char *o_tmpdir;
209
210 /* try enough different sized base directory lengths so the code
211 hits the different reallocations and tests for off-by-one errors */
212 string_t *basedir = t_str_new(256);
213 str_append(basedir, cwd);
214 str_append(basedir, "/"TEMP_DIRNAME);
215 str_append_c(basedir, '/');
216 size_t base_len = str_len(basedir);
217
218 o_tmpdir = tmpdir;
219 /* path_normalize() initially allocates 128 bytes, so we'll test paths
220 up to that length+1. */
221 unsigned char buf[128+1];
222 memset(buf, 'x', sizeof(buf));
223 for (size_t i = 1; i <= sizeof(buf); i++) {
224 str_truncate(basedir, base_len);
225 str_append_data(basedir, buf, i);
226 tmpdir = str_c(basedir);
227 (void)mkdir(str_c(basedir), 0700);
228
229 create_links(tmpdir);
230 test_link1();
231 test_link_loop();
232 }
233 tmpdir = o_tmpdir;
234 }
235
test_cleanup(void)236 static void test_cleanup(void)
237 {
238 const char *error;
239
240 if (unlink_directory(tmpdir, UNLINK_DIRECTORY_FLAG_RMDIR, &error) < 0)
241 i_error("unlink_directory() failed: %s", error);
242 }
243
test_init(void)244 static void test_init(void)
245 {
246 const char *error;
247 test_assert(t_get_working_dir(&cwd, &error) == 0);
248 tmpdir = t_strconcat(cwd, "/"TEMP_DIRNAME, NULL);
249
250 test_cleanup();
251 if (mkdir(tmpdir, 0700) < 0) {
252 i_fatal("mkdir: %m");
253 }
254
255 create_links(tmpdir);
256 }
257
test_path_util(void)258 void test_path_util(void)
259 {
260 test_begin("test_path_util");
261 alarm(20);
262 test_init();
263 test_local_path();
264 test_absolute_path_no_change();
265 test_travel_to_root();
266 test_extra_slashes();
267 test_nonexistent_path();
268 test_relative_dotdot();
269 test_link1();
270 test_link4();
271 test_link_loop();
272 test_abspath_vs_normpath();
273 test_link_alloc();
274 test_link_alloc2();
275 test_cleanup();
276 alarm(0);
277 test_end();
278 }
279