1 /* Tests for MINIX3 realpath(3) - by Erik van der Kouwe */
2 #define _POSIX_SOURCE 1
17 static const char *executable
, *subtest
;
19 #define ERR (e(__LINE__))
23 printf("File %s, error line %d, errno %d: %s\n",
24 subtest
, n
, errno
, strerror(errno
));
26 if (errct
++ > MAX_ERROR
)
28 printf("Too many errors; test aborted\n");
33 static void quit(void)
42 printf("%d errors\n", errct
);
47 static char *remove_last_path_component(char *path
)
55 for (current
= path
; *current
; current
++)
59 /* check path component */
62 if (strcmp(last
+ 1, ".") == 0) ERR
;
63 if (strcmp(last
+ 1, "..") == 0) ERR
;
66 /* if only root path slash, we are done */
70 /* chop off last path component */
75 static int check_path_components(const char *path
)
77 char buffer
[PATH_MAX
+ 1], *bufferpos
;
80 assert(strlen(path
) < sizeof(buffer
));
85 /* copy next path segment */
88 *(bufferpos
++) = *(path
++);
89 } while (*path
&& *path
!= '/');
93 * is this a valid path segment? if not, return errno.
94 * one exception: the last path component need not exist
96 if (stat(buffer
, &statbuf
) < 0 &&
97 (*path
|| errno
!= ENOENT
))
104 static void check_realpath(const char *path
, int expected_errno
)
106 char buffer
[PATH_MAX
+ 1], *resolved_path
;
108 struct stat statbuf
[2];
112 /* any errors in the path that realpath should report? */
113 expected_errno2
= check_path_components(path
);
118 resolved_path
= realpath(path
, buffer
);
120 /* do we get errors when expected? */
121 if (expected_errno
|| expected_errno2
)
123 if (resolved_path
) ERR
;
124 if (errno
!= expected_errno
&& errno
!= expected_errno2
) ERR
;
129 /* do we get success when expected? */
138 /* do the paths point to the same file? (only check if exists) */
139 if (stat(path
, &statbuf
[0]) < 0)
141 if (errno
!= ENOENT
) { ERR
; return; }
145 if (stat(resolved_path
, &statbuf
[1]) < 0) { ERR
; return; }
146 if (statbuf
[0].st_dev
!= statbuf
[1].st_dev
) ERR
;
147 if (statbuf
[0].st_ino
!= statbuf
[1].st_ino
) ERR
;
150 /* is the path absolute? */
151 if (resolved_path
[0] != '/') ERR
;
153 /* is each path element allowable? */
154 while (remove_last_path_component(resolved_path
))
157 if (lstat(resolved_path
, &statbuf
[1]) < 0) { ERR
; return; }
158 if ((statbuf
[1].st_mode
& S_IFMT
) != S_IFDIR
) ERR
;
163 static void check_realpath_step_by_step(const char *path
, int expected_errno
)
165 char buffer
[PATH_MAX
+ 1];
166 const char *path_current
;
169 assert(strlen(path
) < sizeof(buffer
));
171 /* check the absolute path */
172 check_realpath(path
, expected_errno
);
174 /* try with different CWDs */
175 for (path_current
= path
; *path_current
; path_current
++)
176 if (path_current
[0] == '/' && path_current
[1])
179 memcpy(buffer
, path
, path_current
- path
+ 1);
180 buffer
[path_current
- path
+ 1] = 0;
181 if (chdir(buffer
) < 0) { ERR
; continue; }
184 check_realpath(path_current
+ 1, expected_errno
);
188 static char *pathncat(char *buffer
, size_t size
, const char *path1
, const char *path2
)
190 size_t len1
, len2
, lenslash
;
196 /* check whether it fits */
197 len1
= strlen(path1
);
198 len2
= strlen(path2
);
199 lenslash
= (len1
> 0 && path1
[len1
- 1] == '/') ? 0 : 1;
200 if (len1
>= size
|| /* check individual components to avoid overflow */
202 len1
+ len2
+ lenslash
>= size
)
205 /* perform the copy */
206 memcpy(buffer
, path1
, len1
);
210 memcpy(buffer
+ len1
+ lenslash
, path2
, len2
+ 1);
214 static void check_realpath_recurse(const char *path
, int depth
)
217 struct dirent
*dirent
;
218 char pathsub
[PATH_MAX
+ 1];
220 /* check with the path itself */
221 check_realpath_step_by_step(path
, 0);
223 /* don't go too deep */
227 /* loop through subdirectories (including . and ..) */
228 if (!(dir
= opendir(path
)))
230 if (errno
!= ENOENT
&& errno
!= ENOTDIR
)
234 while (dirent
= readdir(dir
))
237 if (!pathncat(pathsub
, sizeof(pathsub
), path
, dirent
->d_name
))
244 check_realpath_recurse(pathsub
, depth
- 1);
246 if (closedir(dir
) < 0) ERR
;
250 #define PATH_BASE "/t43"
251 #define L(x) PATH_BASE "/link_" #x ".tmp"
253 static char basepath
[PATH_MAX
+ 1];
255 static char *addbasepath(char *buffer
, const char *path
)
257 size_t basepathlen
, pathlen
;
260 /* assumption: both start with slash and neither end with it */
261 assert(basepath
[0] == '/');
262 assert(basepath
[strlen(basepath
) - 1] != '/');
265 assert(path
[0] == '/');
267 /* check result length */
268 basepathlen
= strlen(basepath
);
269 pathlen
= strlen(path
);
270 if (basepathlen
+ pathlen
> PATH_MAX
)
272 printf("path too long\n");
276 /* concatenate base path and path */
277 memcpy(buffer
, basepath
, basepathlen
);
278 memcpy(buffer
+ basepathlen
, path
, pathlen
+ 1);
282 static void cleanup(const char *path
)
285 struct dirent
*dirent
;
286 char pathsub
[PATH_MAX
+ 1];
289 /* determine file type, avoid following links */
290 if (lstat(path
, &statbuf
) < 0)
292 if (errno
!= ENOENT
) ERR
;
296 /* only recursively process directories (NOT symlinks!) */
297 if ((statbuf
.st_mode
& S_IFMT
) != S_IFDIR
)
299 if (unlink(path
) < 0) ERR
;
303 /* loop through subdirectories (excluding . and ..) */
304 if (!(dir
= opendir(path
)))
309 while (dirent
= readdir(dir
))
311 /* ignore current and parent directories */
312 if (strcmp(dirent
->d_name
, ".") == 0 ||
313 strcmp(dirent
->d_name
, "..") == 0)
317 if (!pathncat(pathsub
, sizeof(pathsub
), path
, dirent
->d_name
))
326 if (closedir(dir
) < 0) ERR
;
328 /* remove the (now empty) directory itself */
329 if (rmdir(path
) < 0) ERR
;
332 static void test_dirname(const char *path
, const char *exp
)
334 char buffer
[PATH_MAX
];
336 size_t pathlen
= strlen(path
);
339 assert(pathlen
+ 3 < PATH_MAX
);
341 /* try with no, one or two trailing slashes */
342 for (i
= 0; i
< 3; i
++)
344 /* no trailing slashes for empty string */
345 if (pathlen
< 1 && i
> 0)
349 strcpy(buffer
, path
);
350 for (j
= 0; j
< i
; j
++)
351 buffer
[pathlen
+ j
] = '/';
353 buffer
[pathlen
+ i
] = 0;
356 pathout
= dirname(buffer
);
357 if (strcmp(pathout
, exp
) != 0)
362 int main(int argc
, char **argv
)
364 char buffer1
[PATH_MAX
+ 1], buffer2
[PATH_MAX
+ 1];
369 executable
= argv
[0];
370 getcwd(basepath
, sizeof(basepath
));
371 cleanup(addbasepath(buffer1
, PATH_BASE
));
373 /* prepare some symlinks to make it more difficult */
374 if (mkdir(addbasepath(buffer1
, PATH_BASE
), S_IRWXU
) < 0) ERR
;
375 if (symlink("/", addbasepath(buffer1
, L(1))) < 0) ERR
;
376 if (symlink(basepath
, addbasepath(buffer1
, L(2))) < 0) ERR
;
378 /* perform some tests */
379 check_realpath_recurse(basepath
, PATH_DEPTH
);
381 /* now try with recursive symlinks */
382 if (symlink(addbasepath(buffer1
, L(3)), addbasepath(buffer2
, L(3))) < 0) ERR
;
383 if (symlink(addbasepath(buffer1
, L(5)), addbasepath(buffer2
, L(4))) < 0) ERR
;
384 if (symlink(addbasepath(buffer1
, L(4)), addbasepath(buffer2
, L(5))) < 0) ERR
;
385 check_realpath_step_by_step(addbasepath(buffer1
, L(3)), ELOOP
);
386 check_realpath_step_by_step(addbasepath(buffer1
, L(4)), ELOOP
);
387 check_realpath_step_by_step(addbasepath(buffer1
, L(5)), ELOOP
);
389 /* delete the symlinks */
390 cleanup(addbasepath(buffer1
, PATH_BASE
));
392 /* also test dirname */
393 test_dirname("", ".");
394 test_dirname(".", ".");
395 test_dirname("..", ".");
396 test_dirname("x", ".");
397 test_dirname("xy", ".");
398 test_dirname("x/y", "x");
399 test_dirname("xy/z", "xy");
400 test_dirname("x/yz", "x");
401 test_dirname("ab/cd", "ab");
402 test_dirname("x//y", "x");
403 test_dirname("xy//z", "xy");
404 test_dirname("x//yz", "x");
405 test_dirname("ab//cd", "ab");
406 test_dirname("/", "/");
407 test_dirname("/x", "/");
408 test_dirname("/xy", "/");
409 test_dirname("/x/y", "/x");
410 test_dirname("/xy/z", "/xy");
411 test_dirname("/x/yz", "/x");
412 test_dirname("/ab/cd", "/ab");
413 test_dirname("/x//y", "/x");
414 test_dirname("/xy//z", "/xy");
415 test_dirname("/x//yz", "/x");
416 test_dirname("/ab//cd", "/ab");
417 test_dirname("/usr/src", "/usr");
418 test_dirname("/usr/src/test", "/usr/src");
419 test_dirname("usr/src", "usr");
420 test_dirname("usr/src/test", "usr/src");
424 return(-1); /* impossible */