1 /* Tests for MINIX3 realpath(3) - by Erik van der Kouwe */
2 #include <assert.h>
3 #include <dirent.h>
4 #include <errno.h>
5 #include <libgen.h>
6 #include <limits.h>
7 #include <stdio.h>
8 #include <stdlib.h>
9 #include <string.h>
10 #include <sys/stat.h>
11 #include <unistd.h>
12
13 int max_error = 3;
14 #include "common.h"
15
16
17 int subtest;
18 static const char *executable;
19
20
21 #define ERR (e(__LINE__))
22
remove_last_path_component(char * path)23 static char *remove_last_path_component(char *path)
24 {
25 char *current, *last;
26
27 assert(path);
28
29 /* find last slash */
30 last = NULL;
31 for (current = path; *current; current++)
32 if (*current == '/')
33 last = current;
34
35 /* check path component */
36 if (last)
37 {
38 if (strcmp(last + 1, ".") == 0) ERR;
39 if (strcmp(last + 1, "..") == 0) ERR;
40 }
41
42 /* if only root path slash, we are done */
43 if (last <= path)
44 return NULL;
45
46 /* chop off last path component */
47 *last = 0;
48 return path;
49 }
50
check_path_components(const char * path)51 static int check_path_components(const char *path)
52 {
53 char buffer[PATH_MAX + 1], *bufferpos;
54 struct stat statbuf;
55
56 assert(strlen(path) < sizeof(buffer));
57
58 bufferpos = buffer;
59 while (*path)
60 {
61 /* copy next path segment */
62 do
63 {
64 *(bufferpos++) = *(path++);
65 } while (*path && *path != '/');
66 *bufferpos = 0;
67
68 /*
69 * is this a valid path segment? if not, return errno.
70 * one exception: the last path component need not exist
71 * - unless it is actually a dangling symlink
72 */
73 if (stat(buffer, &statbuf) < 0 &&
74 (*path || errno != ENOENT ||
75 lstat(buffer, &statbuf) == 0))
76 return errno;
77 }
78
79 return 0;
80 }
81
check_realpath(const char * path,int expected_errno)82 static void check_realpath(const char *path, int expected_errno)
83 {
84 char buffer[PATH_MAX + 1], *resolved_path;
85 int expected_errno2;
86 struct stat statbuf[2];
87
88 assert(path);
89
90 /* any errors in the path that realpath should report? */
91 expected_errno2 = check_path_components(path);
92
93 /* run realpath */
94 errno = 0;
95 resolved_path = realpath(path, buffer);
96
97 /* do we get errors when expected? */
98 if (expected_errno || expected_errno2)
99 {
100 if (resolved_path) ERR;
101 if (errno != expected_errno && errno != expected_errno2) ERR;
102 return;
103 }
104
105 /* do we get success when expected? */
106 if (!resolved_path)
107 {
108 ERR;
109 return;
110 }
111 errno = 0;
112
113 /* do the paths point to the same file? (only check if exists) */
114 if (stat(path, &statbuf[0]) < 0)
115 {
116 if (errno != ENOENT) { ERR; return; }
117 }
118 else
119 {
120 if (stat(resolved_path, &statbuf[1]) < 0) { ERR; return; }
121 if (statbuf[0].st_dev != statbuf[1].st_dev) ERR;
122 if (statbuf[0].st_ino != statbuf[1].st_ino) ERR;
123 }
124
125 /* is the path absolute? */
126 if (resolved_path[0] != '/') ERR;
127
128 /* is each path element allowable? */
129 while (remove_last_path_component(resolved_path))
130 {
131 /* not a symlink? */
132 if (lstat(resolved_path, &statbuf[1]) < 0) { ERR; return; }
133 if ((statbuf[1].st_mode & S_IFMT) != S_IFDIR) ERR;
134 }
135 }
136
check_realpath_step_by_step(const char * path,int expected_errno)137 static void check_realpath_step_by_step(const char *path, int expected_errno)
138 {
139 char buffer[PATH_MAX + 1];
140 const char *path_current;
141
142 assert(path);
143 assert(strlen(path) < sizeof(buffer));
144
145 /* check the absolute path */
146 check_realpath(path, expected_errno);
147
148 /* try with different CWDs */
149 for (path_current = path; *path_current; path_current++)
150 if (path_current[0] == '/' && path_current[1])
151 {
152 /* set CWD */
153 memcpy(buffer, path, path_current - path + 1);
154 buffer[path_current - path + 1] = 0;
155 if (chdir(buffer) < 0) { ERR; continue; }
156
157 /* perform test */
158 check_realpath(path_current + 1, expected_errno);
159 }
160 }
161
pathncat(char * buffer,size_t size,const char * path1,const char * path2)162 static char *pathncat(char *buffer, size_t size, const char *path1, const char *path2)
163 {
164 size_t len1, len2, lenslash;
165
166 assert(buffer);
167 assert(path1);
168 assert(path2);
169
170 /* check whether it fits */
171 len1 = strlen(path1);
172 len2 = strlen(path2);
173 lenslash = (len1 > 0 && path1[len1 - 1] == '/') ? 0 : 1;
174 if (len1 >= size || /* check individual components to avoid overflow */
175 len2 >= size ||
176 len1 + len2 + lenslash >= size)
177 return NULL;
178
179 /* perform the copy */
180 memcpy(buffer, path1, len1);
181 if (lenslash)
182 buffer[len1] = '/';
183
184 memcpy(buffer + len1 + lenslash, path2, len2 + 1);
185 return buffer;
186 }
187
check_realpath_recurse(const char * path,int depth)188 static void check_realpath_recurse(const char *path, int depth)
189 {
190 DIR *dir;
191 struct dirent *dirent;
192 char pathsub[PATH_MAX + 1];
193 struct stat st;
194
195 /* check with the path itself */
196 check_realpath_step_by_step(path, 0);
197
198 /* don't go too deep */
199 if (depth < 1)
200 return;
201
202 /* don't bother with non-directories. Due to timeouts in drivers we
203 * might not get expected results and takes way too long */
204 if (stat(path, &st) != 0) {
205 /* dangling symlinks may cause legitimate failures here */
206 if (lstat(path, &st) != 0) ERR;
207 return;
208 }
209 if (!S_ISDIR(st.st_mode))
210 return;
211
212 /* loop through subdirectories (including . and ..) */
213 if (!(dir = opendir(path)))
214 {
215 /* Opening some special files might result in errors when the
216 * corresponding hardware is not present, or simply when access
217 * rights prohibit access (e.g., /dev/log).
218 */
219 if (errno != ENOTDIR
220 && errno != ENXIO && errno != EIO && errno != EACCES) {
221 ERR;
222 }
223 return;
224 }
225 while ((dirent = readdir(dir)) != NULL)
226 {
227 /* build path */
228 if (!pathncat(pathsub, sizeof(pathsub), path, dirent->d_name))
229 {
230 ERR;
231 continue;
232 }
233
234 /* check path */
235 check_realpath_recurse(pathsub, depth - 1);
236 }
237 if (closedir(dir) < 0) ERR;
238 }
239
240 #define PATH_DEPTH 4
241 #define PATH_BASE "/."
242 #define L(x) PATH_BASE "/link_" #x ".tmp"
243
244 static char basepath[PATH_MAX + 1];
245
addbasepath(char * buffer,const char * path)246 static char *addbasepath(char *buffer, const char *path)
247 {
248 size_t basepathlen, pathlen;
249
250 /* assumption: both start with slash and neither end with it */
251 assert(basepath[0] == '/');
252 assert(basepath[strlen(basepath) - 1] != '/');
253 assert(buffer);
254 assert(path);
255 assert(path[0] == '/');
256
257 /* check result length */
258 basepathlen = strlen(basepath);
259 pathlen = strlen(path);
260 if (basepathlen + pathlen > PATH_MAX)
261 {
262 printf("path too long\n");
263 exit(-1);
264 }
265
266 /* concatenate base path and path */
267 memcpy(buffer, basepath, basepathlen);
268 memcpy(buffer + basepathlen, path, pathlen + 1);
269 return buffer;
270 }
271
test_dirname(const char * path,const char * exp)272 static void test_dirname(const char *path, const char *exp)
273 {
274 char buffer[PATH_MAX];
275 int i, j;
276 size_t pathlen = strlen(path);
277 char *pathout;
278
279 assert(pathlen + 3 < PATH_MAX);
280
281 /* try with no, one or two trailing slashes */
282 for (i = 0; i < 3; i++)
283 {
284 /* no trailing slashes for empty string */
285 if (pathlen < 1 && i > 0)
286 continue;
287
288 /* prepare buffer */
289 strcpy(buffer, path);
290 for (j = 0; j < i; j++)
291 buffer[pathlen + j] = '/';
292
293 buffer[pathlen + i] = 0;
294
295 /* perform test */
296 pathout = dirname(buffer);
297 if (strcmp(pathout, exp) != 0)
298 ERR;
299 }
300 }
301
main(int argc,char ** argv)302 int main(int argc, char **argv)
303 {
304 char buffer1[PATH_MAX + 1], buffer2[PATH_MAX + 1];
305 subtest = 1;
306
307 /* initialize */
308 start(43);
309 executable = argv[0];
310 getcwd(basepath, sizeof(basepath));
311
312 /* prepare some symlinks to make it more difficult */
313 if (symlink("/", addbasepath(buffer1, L(1))) < 0) ERR;
314 if (symlink(basepath, addbasepath(buffer1, L(2))) < 0) ERR;
315
316 /* perform some tests */
317 check_realpath_recurse(basepath, PATH_DEPTH);
318
319 /* now try with recursive symlinks */
320 if (symlink(addbasepath(buffer1, L(3)), addbasepath(buffer2, L(3))) < 0) ERR;
321 if (symlink(addbasepath(buffer1, L(5)), addbasepath(buffer2, L(4))) < 0) ERR;
322 if (symlink(addbasepath(buffer1, L(4)), addbasepath(buffer2, L(5))) < 0) ERR;
323 check_realpath_step_by_step(addbasepath(buffer1, L(3)), ELOOP);
324 check_realpath_step_by_step(addbasepath(buffer1, L(4)), ELOOP);
325 check_realpath_step_by_step(addbasepath(buffer1, L(5)), ELOOP);
326
327 /* delete the symlinks */
328 cleanup();
329
330 /* also test dirname */
331 test_dirname("", ".");
332 test_dirname(".", ".");
333 test_dirname("..", ".");
334 test_dirname("x", ".");
335 test_dirname("xy", ".");
336 test_dirname("x/y", "x");
337 test_dirname("xy/z", "xy");
338 test_dirname("x/yz", "x");
339 test_dirname("ab/cd", "ab");
340 test_dirname("x//y", "x");
341 test_dirname("xy//z", "xy");
342 test_dirname("x//yz", "x");
343 test_dirname("ab//cd", "ab");
344 test_dirname("/", "/");
345 test_dirname("/x", "/");
346 test_dirname("/xy", "/");
347 test_dirname("/x/y", "/x");
348 test_dirname("/xy/z", "/xy");
349 test_dirname("/x/yz", "/x");
350 test_dirname("/ab/cd", "/ab");
351 test_dirname("/x//y", "/x");
352 test_dirname("/xy//z", "/xy");
353 test_dirname("/x//yz", "/x");
354 test_dirname("/ab//cd", "/ab");
355 test_dirname("/usr/src", "/usr");
356 test_dirname("/usr/src/test", "/usr/src");
357 test_dirname("usr/src", "usr");
358 test_dirname("usr/src/test", "usr/src");
359
360 /* done */
361 quit();
362 return(-1); /* impossible */
363 }
364