1 /* $NetBSD: getcwd.c,v 1.8 2005/02/06 06:05:20 perry Exp $ */
4 * Copyright (c) 1999 The NetBSD Foundation, Inc.
7 * This code is derived from software contributed to The NetBSD Foundation
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
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.
46 #include <sys/param.h> /* for MAXPATHLEN */
47 #include <sys/types.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
,
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);
80 * one-character buffer
81 * two-character buffer
83 * large (uncacheable) name in path.
85 * after rename of parent.
87 * good pointer near end of address space
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:
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;
113 static int sleepflag
= 0;
115 static uid_t altid
= -1;
118 check1 (dir
, buf
, calltext
, actual
, expected
, experr
)
122 int actual
, expected
, experr
;
125 if (actual
!= expected
) {
127 "test %d: in %s, %s failed; expected %d, got %d\n",
128 ntest
, dir
, calltext
, expected
, actual
);
129 if (actual
< 0) perror("getcwd");
131 } else if ((expected
== -1) && (errno
!= (experr
))) {
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");
137 } else if ((expected
> 0) &&
139 (strcmp (dir
, buf
) != 0)) {
141 "test %d: in %s, %s got wrong dir %s\n",
142 ntest
, dir
, calltext
, buf
);
147 char *cp
= old_getcwd(newbuf
, sizeof(newbuf
));
151 "test %d: in %s, old getcwd failed!\n",
153 } else if (strcmp(cp
, buf
)) {
156 "test %d: in %s, old_getcwd returned different dir %s\n",
162 printf("test %d: in %s, %s passed\n", ntest
, dir
, calltext
);
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");
185 char result_buf
[1024];
186 if (__getcwd(result_buf
, sizeof(result_buf
)) < 0) {
187 fprintf(stderr
, "getcwd failed during timing test!");
194 time_func(name
, func
)
198 struct timeval before
, after
;
202 chdir ("/usr/share/examples/emul/ultrix/etc");
204 gettimeofday(&before
, 0);
205 for (i
=0; i
<nloops
; i
++) {
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
);
223 time_func("kernel getcwd", time_kern_getcwd
);
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))
234 test___getcwd_perms()
240 fprintf(stderr
, "Not root; skipping permission tests\n");
244 mkdir ("/tmp/permdir", 0700);
245 mkdir ("/tmp/permdir/subdir", 0755);
246 chdir ("/tmp/permdir/subdir");
250 CHECK("/tmp/permdir/subdir", __getcwd(kbuf
, sizeof(kbuf
)), -1, EACCES
);
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");
263 CHECK("/tmp/permdir/subdir", __getcwd(kbuf
, sizeof(kbuf
)), 20, 0);
267 rmdir ("/tmp/permdir/subdir");
268 rmdir ("/tmp/permdir");
272 test___getcwd_chroot()
279 fprintf(stderr
, "Not root; skipping chroot tests\n");
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);
298 } else if (pid
== 0) {
301 /* chroot to root of filesystem (assuming MFS /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);
308 CHECK ("/subdir", __getcwd(kbuf
, sizeof(kbuf
)), 8, 0);
315 waitpid(pid
, &status
, 0);
317 if (WIFEXITED(status
) &&
318 (WEXITSTATUS(status
) == 0))
326 rmdir ("/tmp/chrootdir/subdir");
327 rmdir ("/tmp/chrootdir");
337 static char kbuf
[1024];
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
);
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
);
361 CHECK("/etc/mtree", __getcwd(kbuf
, sizeof(kbuf
)), 11, 0);
362 CHECK("/etc/mtree", __getcwd(kbuf
, sizeof(kbuf
)), 11, 0);
365 CHECK("/usr/bin", __getcwd(kbuf
, sizeof(kbuf
)), 9, 0);
367 /* really large (non-cacheable) entry name */
369 (void) rmdir(bigname
);
370 mkdir(bigname
, 0755);
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) {
380 CHECK("deleted directory", __getcwd(kbuf
, sizeof(kbuf
)), -1, ENOENT
);
383 (void) rmdir(littlename
);
384 mkdir(littlename
, 0755);
386 CHECK("/tmp/" littlename
, __getcwd(kbuf
, sizeof(kbuf
)), 16, 0);
387 if (rename("/tmp/" littlename
, "/tmp/" othername
) < 0) {
391 CHECK("/tmp/" othername
, __getcwd(kbuf
, sizeof(kbuf
)), 16, 0);
392 if (rmdir("/tmp/" othername
) < 0) {
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
);
403 if (mkdir (buf
, 0755) < 0) {
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) {
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
);
424 (void)rmdir("/tmp/bigdir");
426 test___getcwd_perms();
427 test___getcwd_chroot();
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');
442 if (chdir (buf
) < 0) {
443 warn("Can't change directory to %s", buf
);
448 cp
= old_getcwd (ubuf
, MAXPATHLEN
);
449 if (strcmp(buf
, ubuf
) != 0) {
450 warnx("In %s, old_getcwd says %s",
455 CHECK(buf
, __getcwd (kbuf
, MAXPATHLEN
),
462 * - large directories.
464 * - every single filesystem type
466 * - walk filesystem, compare sys_getcwd with getcwd for each
474 fprintf(stderr
, "usage: %s [-srpvw] [-l nloops]\n", progname
);
479 int run_regression
= 0;
480 int run_performance
= 0;
488 char *progname
= argv
[0];
490 uid_from_user("nobody", &altid
);
492 while ((ch
= getopt(argc
, argv
, "srpvwl:u:")) != -1)
510 nloops
= atoi(optarg
);
515 if (uid_from_user(optarg
, &altid
) != 0) {
516 fprintf(stderr
, "unknown user %s\n", optarg
);
531 if (!fail
&& run_performance
)
534 if (!fail
&& run_stress
)
535 stress_test_getcwd();
539 printf ("%d passes\n", pass
);
543 printf("%d failures\n", fail
);