Patrick Welche <prlw1@cam.ac.uk>
[netbsd-mini2440.git] / regress / sys / kern / getcwd / getcwd.c
blobd14d9206c1f51311504b4fee3c61ef13774bd799
1 /* $NetBSD: getcwd.c,v 1.8 2005/02/06 06:05:20 perry Exp $ */
3 /*-
4 * Copyright (c) 1999 The NetBSD Foundation, Inc.
5 * All rights reserved.
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Bill Sommerfeld.
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
33 * test SYS___getcwd.
36 #include <assert.h>
37 #include <err.h>
38 #include <errno.h>
39 #include <pwd.h>
40 #include <stdio.h>
41 #include <stdlib.h>
42 #include <string.h>
43 #include <time.h>
44 #include <unistd.h>
46 #include <sys/param.h> /* for MAXPATHLEN */
47 #include <sys/types.h>
48 #include <sys/stat.h>
49 #include <sys/wait.h>
51 #include "getcwd.h"
53 int main(int, char *[]);
55 static void check1(char *dir, char *buf, char *calltext,
56 int actual, int expected, int experr);
58 static void time_old_getcwd(void);
59 static void time_kern_getcwd(void);
60 static void time_func(char *name,
61 void (*func)(void));
63 static void test_speed(void);
64 static void test___getcwd (void);
65 static void test___getcwd_perms (void);
66 static void test___getcwd_chroot(void);
68 static void stress_test_getcwd(void);
69 static void usage(char *progname);
71 /* libc-private interface */
72 int __getcwd(char *, size_t);
75 * test cases:
76 * NULL pointer
77 * broken pointer
78 * zero-length buffer
79 * negative length
80 * one-character buffer
81 * two-character buffer
82 * full-length buffer
83 * large (uncacheable) name in path.
84 * deleted directory
85 * after rename of parent.
86 * permission failure.
87 * good pointer near end of address space
88 * really huge length
89 * really large (multi-block) directories
90 * chroot interactions:
91 * chroot, at / inside the directory.
92 * chroot, at some other inside directory.
96 * test cases not yet done:
97 * -o union mount
98 * chroot interactions:
99 * chroot to mounted directory.
100 * (i.e., proc a: chroot /foo; sleep;
101 * proc b: mount blort /foo)
102 * concurrent with force-unmounting of filesystem.
105 #define bigname "Funkelhausersteinweitz.SIPBADMIN.a" /* don't ask */
106 #define littlename "getcwdtest"
107 #define othername "testgetcwd"
109 static int verbose = 0;
110 static int test = 1;
111 static int fail = 0;
112 static int pass = 0;
113 static int sleepflag = 0;
115 static uid_t altid = -1;
117 static void
118 check1 (dir, buf, calltext, actual, expected, experr)
119 char *dir;
120 char *buf;
121 char *calltext;
122 int actual, expected, experr;
124 int ntest = test++;
125 if (actual != expected) {
126 fprintf(stderr,
127 "test %d: in %s, %s failed; expected %d, got %d\n",
128 ntest, dir, calltext, expected, actual);
129 if (actual < 0) perror("getcwd");
130 fail++;
131 } else if ((expected == -1) && (errno != (experr))) {
132 fprintf(stderr,
133 "test %d: in %s, %s failed; expected error %d, got %d\n",
134 ntest, dir, calltext, experr, errno);
135 if (actual < 0) perror("getcwd");
136 fail++;
137 } else if ((expected > 0) &&
138 (buf != NULL) &&
139 (strcmp (dir, buf) != 0)) {
140 fprintf(stderr,
141 "test %d: in %s, %s got wrong dir %s\n",
142 ntest, dir, calltext, buf);
143 fail++;
144 } else {
145 if (expected > 0) {
146 char newbuf[1024];
147 char *cp = old_getcwd(newbuf, sizeof(newbuf));
148 if (cp == NULL) {
149 fail++;
150 fprintf(stderr,
151 "test %d: in %s, old getcwd failed!\n",
152 ntest, dir);
153 } else if (strcmp(cp, buf)) {
154 fail++;
155 fprintf(stderr,
156 "test %d: in %s, old_getcwd returned different dir %s\n",
157 ntest, dir, cp);
160 pass++;
161 if (verbose)
162 printf("test %d: in %s, %s passed\n", ntest, dir, calltext);
164 if (sleepflag)
165 sleep(1);
168 int nloops = 100;
170 void
171 time_old_getcwd()
173 char result_buf[1024];
174 if (old_getcwd(result_buf, 1024) == NULL) {
175 fprintf(stderr, "old_getcwd failed during timing test!\n");
176 perror("old_getcwd");
177 exit(1);
182 void
183 time_kern_getcwd()
185 char result_buf[1024];
186 if (__getcwd(result_buf, sizeof(result_buf)) < 0) {
187 fprintf(stderr, "getcwd failed during timing test!");
188 perror("getcwd");
189 exit(1);
193 static void
194 time_func(name, func)
195 char *name;
196 void (*func)(void);
198 struct timeval before, after;
199 double delta_t;
201 int i;
202 chdir ("/usr/share/examples/emul/ultrix/etc");
204 gettimeofday(&before, 0);
205 for (i=0; i<nloops; i++) {
206 (*func)();
208 gettimeofday(&after, 0);
210 delta_t = after.tv_sec - before.tv_sec;
212 delta_t += ((double)(after.tv_usec - before.tv_usec))/1000000.0;
214 printf("%s: %d calls in %10.3f seconds; ", name, nloops, delta_t);
215 printf("%10.6f ms/call\n", (delta_t*1000.0)/nloops);
218 void
219 test_speed()
221 int i;
222 for (i=0; i<5; i++)
223 time_func("kernel getcwd", time_kern_getcwd);
225 for (i=0; i<5; i++)
226 time_func("old user-space getcwd", time_old_getcwd);
229 #define CHECK(dir, call, ret, err) \
230 check1((dir), kbuf, #call, (call), (ret), (err))
233 void
234 test___getcwd_perms()
236 char kbuf[1024];
238 if (geteuid() != 0)
240 fprintf(stderr, "Not root; skipping permission tests\n");
241 return;
244 mkdir ("/tmp/permdir", 0700);
245 mkdir ("/tmp/permdir/subdir", 0755);
246 chdir ("/tmp/permdir/subdir");
248 seteuid(altid);
250 CHECK("/tmp/permdir/subdir", __getcwd(kbuf, sizeof(kbuf)), -1, EACCES);
252 seteuid(0);
253 chdir ("/");
254 rmdir ("/tmp/permdir/subdir");
255 rmdir ("/tmp/permdir");
257 mkdir ("/tmp/permdir", 0755);
258 mkdir ("/tmp/permdir/subdir", 0711);
259 chdir ("/tmp/permdir/subdir");
261 seteuid(altid);
263 CHECK("/tmp/permdir/subdir", __getcwd(kbuf, sizeof(kbuf)), 20, 0);
265 seteuid(0);
266 chdir ("/");
267 rmdir ("/tmp/permdir/subdir");
268 rmdir ("/tmp/permdir");
271 void
272 test___getcwd_chroot()
274 int pid, status;
275 char kbuf[1024];
277 if (geteuid() != 0)
279 fprintf(stderr, "Not root; skipping chroot tests\n");
280 return;
283 /* XXX we need fchroot to do this properly.. */
284 mkdir ("/tmp/chrootdir", 0755);
285 mkdir ("/tmp/chrootdir/subdir", 0755);
287 chdir ("/tmp/chrootdir");
289 CHECK ("/tmp/chrootdir", __getcwd(kbuf, sizeof(kbuf)), 15, 0);
291 fflush(NULL);
293 pid = fork();
295 if (pid < 0) {
296 perror("fork");
297 fail++;
298 } else if (pid == 0) {
299 fail = 0;
300 pass = 0;
301 /* chroot to root of filesystem (assuming MFS /tmp) */
302 chroot ("/tmp");
303 CHECK ("/chrootdir", __getcwd(kbuf, sizeof(kbuf)), 11, 0);
304 /* chroot to further down */
305 chroot ("/chrootdir");
306 CHECK ("/", __getcwd(kbuf, sizeof(kbuf)), 2, 0);
307 chdir("subdir");
308 CHECK ("/subdir", __getcwd(kbuf, sizeof(kbuf)), 8, 0);
310 if (fail)
311 exit(1);
312 else
313 exit(0);
314 } else {
315 waitpid(pid, &status, 0);
317 if (WIFEXITED(status) &&
318 (WEXITSTATUS(status) == 0))
319 pass++;
320 else
321 fail++;
325 chdir ("/");
326 rmdir ("/tmp/chrootdir/subdir");
327 rmdir ("/tmp/chrootdir");
333 void
334 test___getcwd()
336 int i;
337 static char kbuf[1024];
339 chdir("/");
341 CHECK("/", __getcwd(0, 0), -1, ERANGE);
342 CHECK("/", __getcwd(0, -1), -1, ERANGE);
343 CHECK("/", __getcwd(kbuf, 0xdeadbeef), -1, ERANGE); /* large negative */
344 CHECK("/", __getcwd(kbuf, 0x7000beef), 2, 0); /* large positive, rounds down */
345 CHECK("/", __getcwd(kbuf, 0x10000), 2, 0); /* slightly less large positive, rounds down */
346 CHECK("/", __getcwd(kbuf+0x100000, sizeof(kbuf)), -1, EFAULT); /* outside address space */
347 CHECK("/", __getcwd(0, 30), -1, EFAULT);
348 CHECK("/", __getcwd((void*)0xdeadbeef, 30), -1, EFAULT);
349 CHECK("/", __getcwd(kbuf, 2), 2, 0);
350 assert (strcmp(kbuf, "/") == 0);
351 CHECK("/", __getcwd(kbuf, sizeof(kbuf)), 2, 0);
353 CHECK("/", __getcwd(kbuf, 0), -1, ERANGE);
354 CHECK("/", __getcwd(kbuf, 1), -1, ERANGE);
356 chdir("/sbin");
357 CHECK("/sbin", __getcwd(kbuf, sizeof(kbuf)), 6, 0);
358 /* verify that cacheable path gets range check right.. */
359 CHECK("/sbin", __getcwd(kbuf, 3), -1, ERANGE);
360 chdir("/etc/mtree");
361 CHECK("/etc/mtree", __getcwd(kbuf, sizeof(kbuf)), 11, 0);
362 CHECK("/etc/mtree", __getcwd(kbuf, sizeof(kbuf)), 11, 0);
363 /* mount point */
364 chdir("/usr/bin");
365 CHECK("/usr/bin", __getcwd(kbuf, sizeof(kbuf)), 9, 0);
367 /* really large (non-cacheable) entry name */
368 chdir("/tmp");
369 (void) rmdir(bigname);
370 mkdir(bigname, 0755);
371 chdir(bigname);
373 /* verify that non-cachable path gets range check right.. */
374 CHECK("/tmp/" bigname, __getcwd(kbuf, 10), -1, ERANGE);
375 CHECK("/tmp/" bigname, __getcwd(kbuf, sizeof(kbuf)), 40, 0);
377 if (rmdir("/tmp/" bigname) < 0) {
378 perror("rmdir");
380 CHECK("deleted directory", __getcwd(kbuf, sizeof(kbuf)), -1, ENOENT);
382 chdir("/tmp");
383 (void) rmdir(littlename);
384 mkdir(littlename, 0755);
385 chdir(littlename);
386 CHECK("/tmp/" littlename, __getcwd(kbuf, sizeof(kbuf)), 16, 0);
387 if (rename("/tmp/" littlename, "/tmp/" othername) < 0) {
388 perror("rename");
389 fail++;
391 CHECK("/tmp/" othername, __getcwd(kbuf, sizeof(kbuf)), 16, 0);
392 if (rmdir("/tmp/" othername) < 0) {
393 perror("rmdir");
394 fail++;
396 CHECK("deleted directory", __getcwd(kbuf, sizeof(kbuf)), -1, ENOENT);
398 mkdir("/tmp/bigdir", 0755);
399 for (i=0; i<nloops; i++) {
400 char buf[MAXPATHLEN];
401 snprintf(buf, MAXPATHLEN, "/tmp/bigdir/bigsubdirwithanamewhichistoolongtocache%04d", i);
402 (void)rmdir(buf);
403 if (mkdir (buf, 0755) < 0) {
404 perror("mkdir");
405 fail++;
406 break;
409 for (i=0; i<nloops; i++) {
410 char buf[MAXPATHLEN];
411 snprintf(buf, MAXPATHLEN, "/tmp/bigdir/bigsubdirwithanamewhichistoolongtocache%04d", i);
412 if (chdir(buf) < 0) {
413 perror("chdir");
414 fail++;
415 break;
417 CHECK(buf, __getcwd(kbuf, sizeof(kbuf)), strlen(buf)+1, 0);
419 for (i=0; i<nloops; i++) {
420 char buf[MAXPATHLEN];
421 snprintf(buf, MAXPATHLEN, "/tmp/bigdir/bigsubdirwithanamewhichistoolongtocache%04d", i);
422 (void)rmdir(buf);
424 (void)rmdir("/tmp/bigdir");
426 test___getcwd_perms();
427 test___getcwd_chroot();
431 void
432 stress_test_getcwd()
434 char buf[MAXPATHLEN];
435 char ubuf[MAXPATHLEN];
436 char kbuf[MAXPATHLEN];
437 printf("reading directories from stdin..\n");
438 while (fgets(buf, MAXPATHLEN, stdin)) {
439 char *cp = strrchr(buf, '\n');
440 if (cp) *cp = '\0';
442 if (chdir (buf) < 0) {
443 warn("Can't change directory to %s", buf);
444 continue;
448 cp = old_getcwd (ubuf, MAXPATHLEN);
449 if (strcmp(buf, ubuf) != 0) {
450 warnx("In %s, old_getcwd says %s",
451 buf, ubuf);
455 CHECK(buf, __getcwd (kbuf, MAXPATHLEN),
456 strlen(ubuf)+1, 0);
462 * - large directories.
464 * - every single filesystem type
466 * - walk filesystem, compare sys_getcwd with getcwd for each
467 * directory
470 void
471 usage(progname)
472 char *progname;
474 fprintf(stderr, "usage: %s [-srpvw] [-l nloops]\n", progname);
475 exit(1);
478 int run_stress = 0;
479 int run_regression = 0;
480 int run_performance = 0;
483 main(argc, argv)
484 int argc;
485 char **argv;
487 int ch;
488 char *progname = argv[0];
490 uid_from_user("nobody", &altid);
492 while ((ch = getopt(argc, argv, "srpvwl:u:")) != -1)
493 switch (ch) {
494 case 's':
495 run_stress++;
496 break;
497 case 'r':
498 run_regression++;
499 break;
500 case 'p':
501 run_performance++;
502 break;
503 case 'v':
504 verbose++;
505 break;
506 case 'w':
507 sleepflag++;
508 break;
509 case 'l':
510 nloops = atoi(optarg);
511 if (nloops == 0)
512 nloops = 100;
513 break;
514 case 'u':
515 if (uid_from_user(optarg, &altid) != 0) {
516 fprintf(stderr, "unknown user %s\n", optarg);
517 usage(progname);
518 exit(1);
520 break;
521 case '?':
522 default:
523 usage(progname);
525 if (argc != optind)
526 usage(progname);
528 if (run_regression)
529 test___getcwd();
531 if (!fail && run_performance)
532 test_speed();
534 if (!fail && run_stress)
535 stress_test_getcwd();
538 if (verbose)
539 printf ("%d passes\n", pass);
540 if (!fail)
541 exit (0);
542 else {
543 printf("%d failures\n", fail);
544 exit(1);