1 /* Tests for MINIX3 realpath(3) - by Erik van der Kouwe */
2 #define _POSIX_SOURCE 1
17 static const char *executable
;
21 #define ERR (e(__LINE__))
23 static char *remove_last_path_component(char *path
)
31 for (current
= path
; *current
; current
++)
35 /* check path component */
38 if (strcmp(last
+ 1, ".") == 0) ERR
;
39 if (strcmp(last
+ 1, "..") == 0) ERR
;
42 /* if only root path slash, we are done */
46 /* chop off last path component */
51 static int check_path_components(const char *path
)
53 char buffer
[PATH_MAX
+ 1], *bufferpos
;
56 assert(strlen(path
) < sizeof(buffer
));
61 /* copy next path segment */
64 *(bufferpos
++) = *(path
++);
65 } while (*path
&& *path
!= '/');
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
73 if (stat(buffer
, &statbuf
) < 0 &&
74 (*path
|| errno
!= ENOENT
||
75 lstat(buffer
, &statbuf
) == 0))
82 static void check_realpath(const char *path
, int expected_errno
)
84 char buffer
[PATH_MAX
+ 1], *resolved_path
;
86 struct stat statbuf
[2];
90 /* any errors in the path that realpath should report? */
91 expected_errno2
= check_path_components(path
);
95 resolved_path
= realpath(path
, buffer
);
97 /* do we get errors when expected? */
98 if (expected_errno
|| expected_errno2
)
100 if (resolved_path
) ERR
;
101 if (errno
!= expected_errno
&& errno
!= expected_errno2
) ERR
;
105 /* do we get success when expected? */
113 /* do the paths point to the same file? (only check if exists) */
114 if (stat(path
, &statbuf
[0]) < 0)
116 if (errno
!= ENOENT
) { ERR
; return; }
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
;
125 /* is the path absolute? */
126 if (resolved_path
[0] != '/') ERR
;
128 /* is each path element allowable? */
129 while (remove_last_path_component(resolved_path
))
132 if (lstat(resolved_path
, &statbuf
[1]) < 0) { ERR
; return; }
133 if ((statbuf
[1].st_mode
& S_IFMT
) != S_IFDIR
) ERR
;
137 static void check_realpath_step_by_step(const char *path
, int expected_errno
)
139 char buffer
[PATH_MAX
+ 1];
140 const char *path_current
;
143 assert(strlen(path
) < sizeof(buffer
));
145 /* check the absolute path */
146 check_realpath(path
, expected_errno
);
148 /* try with different CWDs */
149 for (path_current
= path
; *path_current
; path_current
++)
150 if (path_current
[0] == '/' && path_current
[1])
153 memcpy(buffer
, path
, path_current
- path
+ 1);
154 buffer
[path_current
- path
+ 1] = 0;
155 if (chdir(buffer
) < 0) { ERR
; continue; }
158 check_realpath(path_current
+ 1, expected_errno
);
162 static char *pathncat(char *buffer
, size_t size
, const char *path1
, const char *path2
)
164 size_t len1
, len2
, lenslash
;
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 */
176 len1
+ len2
+ lenslash
>= size
)
179 /* perform the copy */
180 memcpy(buffer
, path1
, len1
);
184 memcpy(buffer
+ len1
+ lenslash
, path2
, len2
+ 1);
188 static void check_realpath_recurse(const char *path
, int depth
)
191 struct dirent
*dirent
;
192 char pathsub
[PATH_MAX
+ 1];
195 /* check with the path itself */
196 check_realpath_step_by_step(path
, 0);
198 /* don't go too deep */
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
;
209 if (!S_ISDIR(st
.st_mode
))
212 /* loop through subdirectories (including . and ..) */
213 if (!(dir
= opendir(path
)))
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).
220 && errno
!= ENXIO
&& errno
!= EIO
&& errno
!= EACCES
) {
225 while ((dirent
= readdir(dir
)) != NULL
)
228 if (!pathncat(pathsub
, sizeof(pathsub
), path
, dirent
->d_name
))
235 check_realpath_recurse(pathsub
, depth
- 1);
237 if (closedir(dir
) < 0) ERR
;
241 #define PATH_BASE "/."
242 #define L(x) PATH_BASE "/link_" #x ".tmp"
244 static char basepath
[PATH_MAX
+ 1];
246 static char *addbasepath(char *buffer
, const char *path
)
248 size_t basepathlen
, pathlen
;
250 /* assumption: both start with slash and neither end with it */
251 assert(basepath
[0] == '/');
252 assert(basepath
[strlen(basepath
) - 1] != '/');
255 assert(path
[0] == '/');
257 /* check result length */
258 basepathlen
= strlen(basepath
);
259 pathlen
= strlen(path
);
260 if (basepathlen
+ pathlen
> PATH_MAX
)
262 printf("path too long\n");
266 /* concatenate base path and path */
267 memcpy(buffer
, basepath
, basepathlen
);
268 memcpy(buffer
+ basepathlen
, path
, pathlen
+ 1);
272 static void test_dirname(const char *path
, const char *exp
)
274 char buffer
[PATH_MAX
];
276 size_t pathlen
= strlen(path
);
279 assert(pathlen
+ 3 < PATH_MAX
);
281 /* try with no, one or two trailing slashes */
282 for (i
= 0; i
< 3; i
++)
284 /* no trailing slashes for empty string */
285 if (pathlen
< 1 && i
> 0)
289 strcpy(buffer
, path
);
290 for (j
= 0; j
< i
; j
++)
291 buffer
[pathlen
+ j
] = '/';
293 buffer
[pathlen
+ i
] = 0;
296 pathout
= dirname(buffer
);
297 if (strcmp(pathout
, exp
) != 0)
302 int main(int argc
, char **argv
)
304 char buffer1
[PATH_MAX
+ 1], buffer2
[PATH_MAX
+ 1];
309 executable
= argv
[0];
310 getcwd(basepath
, sizeof(basepath
));
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
;
316 /* perform some tests */
317 check_realpath_recurse(basepath
, PATH_DEPTH
);
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
);
327 /* delete the symlinks */
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");
362 return(-1); /* impossible */