getcwd.c revision 1.1 1 /* $NetBSD: getcwd.c,v 1.1 1999/03/22 18:14:39 sommerfe Exp $ */
2
3 /*-
4 * Copyright (c) 1999 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Bill Sommerfeld.
9 *
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.
18 * 3. All advertising materials mentioning features or use of this software
19 * must display the following acknowledgement:
20 * This product includes software developed by the NetBSD
21 * Foundation, Inc. and its contributors.
22 * 4. Neither the name of The NetBSD Foundation nor the names of its
23 * contributors may be used to endorse or promote products derived
24 * from this software without specific prior written permission.
25 *
26 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
27 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
28 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
29 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
30 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
31 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
32 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
33 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
34 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
35 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36 * POSSIBILITY OF SUCH DAMAGE.
37 */
38
39 /*
40 * test SYS___getcwd.
41 */
42
43 #include <assert.h>
44 #include <errno.h>
45 #include <pwd.h>
46 #include <stdio.h>
47 #include <stdlib.h>
48 #include <string.h>
49 #include <time.h>
50 #include <unistd.h>
51
52 #include <sys/param.h> /* for MAXPATHLEN */
53 #include <sys/types.h>
54 #include <sys/stat.h>
55 #include <sys/wait.h>
56
57 #include "getcwd.h"
58
59 int main __P((int, char *[]));
60
61 static void check1 __P((char *dir, char *buf, char *calltext,
62 int actual, int expected, int experr));
63
64 static void time_old_getcwd __P((void));
65 static void time_kern_getcwd __P((void));
66 static void time_func __P((char *name,
67 void (*func)(void)));
68
69 static void test_speed __P((void));
70 static void test___getcwd __P((void));
71 static void test___getcwd_perms __P((void));
72 static void test___getcwd_chroot __P((void));
73
74 static void stress_test_getcwd __P((void));
75 static void usage __P((char *progname));
76
77 /*
78 * test cases:
79 * NULL pointer
80 * broken pointer
81 * zero-length buffer
82 * negative length
83 * one-character buffer
84 * two-character buffer
85 * full-length buffer
86 * large (uncacheable) name in path.
87 * deleted directory
88 * after rename of parent.
89 * permission failure.
90 * good pointer near end of address space
91 * really huge length
92 * really large (multi-block) directories
93 * chroot interactions:
94 * chroot, at / inside the directory.
95 * chroot, at some other inside directory.
96 */
97
98 /*
99 * test cases not yet done:
100 * -o union mount
101 * chroot interactions:
102 * chroot to mounted directory.
103 * (i.e., proc a: chroot /foo; sleep;
104 * proc b: mount blort /foo)
105 * concurrent with force-unmounting of filesystem.
106 */
107
108 #define bigname "Funkelhausersteinweitz.SIPBADMIN.a" /* don't ask */
109 #define littlename "getcwdtest"
110 #define othername "testgetcwd"
111
112 static int verbose = 0;
113 static int test = 1;
114 static int fail = 0;
115 static int pass = 0;
116 static int sleepflag = 0;
117
118 static uid_t altid = -1;
119
120 static void
121 check1 (dir, buf, calltext, actual, expected, experr)
122 char *dir;
123 char *buf;
124 char *calltext;
125 int actual, expected, experr;
126 {
127 int ntest = test++;
128 if (actual != expected) {
129 fprintf(stderr,
130 "test %d: in %s, %s failed; expected %d, got %d\n",
131 ntest, dir, calltext, expected, actual);
132 if (actual < 0) perror("getcwd");
133 fail++;
134 } else if ((expected == -1) && (errno != (experr))) {
135 fprintf(stderr,
136 "test %d: in %s, %s failed; expected error %d, got %d\n",
137 ntest, dir, calltext, experr, errno);
138 if (actual < 0) perror("getcwd");
139 fail++;
140 } else if ((expected > 0) &&
141 (buf != NULL) &&
142 (strcmp (dir, buf) != 0)) {
143 fprintf(stderr,
144 "test %d: in %s, %s got wrong dir %s\n",
145 ntest, dir, calltext, buf);
146 fail++;
147 } else {
148 if (expected > 0) {
149 char newbuf[1024];
150 char *cp = old_getcwd(newbuf, sizeof(newbuf));
151 if (cp == NULL) {
152 fail++;
153 fprintf(stderr,
154 "test %d: in %s, old getcwd failed!\n",
155 ntest, dir);
156 } else if (strcmp(cp, buf)) {
157 fail++;
158 fprintf(stderr,
159 "test %d: in %s, old_getcwd returned different dir %s\n",
160 ntest, dir, cp);
161 }
162 }
163 pass++;
164 if (verbose)
165 printf("test %d: in %s, %s passed\n", ntest, dir, calltext);
166 }
167 if (sleepflag)
168 sleep(1);
169 }
170
171 int nloops = 100;
172
173 void
174 time_old_getcwd()
175 {
176 char result_buf[1024];
177 if (old_getcwd(result_buf, 1024) == NULL) {
178 fprintf(stderr, "old_getcwd failed during timing test!\n");
179 perror("old_getcwd");
180 exit(1);
181 }
182
183 }
184
185 void
186 time_kern_getcwd()
187 {
188 char result_buf[1024];
189 if (__getcwd(result_buf, sizeof(result_buf)) < 0) {
190 fprintf(stderr, "getcwd failed during timing test!");
191 perror("getcwd");
192 exit(1);
193 }
194 }
195
196 static void
197 time_func(name, func)
198 char *name;
199 void (*func) __P((void));
200 {
201 struct timeval before, after;
202 double delta_t;
203
204 int i;
205 chdir ("/usr/share/examples/emul/ultrix/etc");
206
207 gettimeofday(&before, 0);
208 for (i=0; i<nloops; i++) {
209 (*func)();
210 }
211 gettimeofday(&after, 0);
212
213 delta_t = after.tv_sec - before.tv_sec;
214
215 delta_t += ((double)(after.tv_usec - before.tv_usec))/1000000.0;
216
217 printf("%s: %d calls in %10.3f seconds; ", name, nloops, delta_t);
218 printf("%10.6f ms/call\n", (delta_t*1000.0)/nloops);
219 }
220
221 void
222 test_speed()
223 {
224 int i;
225 for (i=0; i<5; i++)
226 time_func("kernel getcwd", time_kern_getcwd);
227
228 for (i=0; i<5; i++)
229 time_func("old user-space getcwd", time_old_getcwd);
230 }
231
232 #define CHECK(dir, call, ret, err) \
233 check1((dir), kbuf, #call, (call), (ret), (err))
234
235
236 void
237 test___getcwd_perms()
238 {
239 char kbuf[1024];
240
241 mkdir ("/tmp/permdir", 0700);
242 mkdir ("/tmp/permdir/subdir", 0755);
243 chdir ("/tmp/permdir/subdir");
244
245 seteuid(altid);
246
247 CHECK("/tmp/permdir/subdir", __getcwd(kbuf, sizeof(kbuf)), -1, EACCES);
248
249 seteuid(0);
250 chdir ("/");
251 rmdir ("/tmp/permdir/subdir");
252 rmdir ("/tmp/permdir");
253 }
254
255 void
256 test___getcwd_chroot()
257 {
258 int pid, status;
259 char kbuf[1024];
260
261 /* XXX we need fchroot to do this properly.. */
262 mkdir ("/tmp/chrootdir", 0755);
263 mkdir ("/tmp/chrootdir/subdir", 0755);
264
265 chdir ("/tmp/chrootdir");
266
267 CHECK ("/tmp/chrootdir", __getcwd(kbuf, sizeof(kbuf)), 15, 0);
268
269 fflush(NULL);
270
271 pid = fork();
272
273 if (pid < 0) {
274 perror("fork");
275 fail++;
276 } else if (pid == 0) {
277 fail = 0;
278 pass = 0;
279 /* chroot to root of filesystem (assuming MFS /tmp) */
280 chroot ("/tmp");
281 CHECK ("/chrootdir", __getcwd(kbuf, sizeof(kbuf)), 11, 0);
282 /* chroot to further down */
283 chroot ("/chrootdir");
284 CHECK ("/", __getcwd(kbuf, sizeof(kbuf)), 2, 0);
285 chdir("subdir");
286 CHECK ("/subdir", __getcwd(kbuf, sizeof(kbuf)), 8, 0);
287
288 if (fail)
289 exit(1);
290 else
291 exit(0);
292 } else {
293 waitpid(pid, &status, 0);
294
295 if (WIFEXITED(status) &&
296 (WEXITSTATUS(status) == 0))
297 pass++;
298 else
299 fail++;
300
301 }
302
303 chdir ("/");
304 rmdir ("/tmp/chrootdir/subdir");
305 rmdir ("/tmp/chrootdir");
306 }
307
308
309
310
311 void
312 test___getcwd()
313 {
314 int i;
315 static char kbuf[1024];
316
317 chdir("/");
318
319 CHECK("/", __getcwd(0, 0), -1, ERANGE);
320 CHECK("/", __getcwd(0, -1), -1, ERANGE);
321 CHECK("/", __getcwd(kbuf, 0xdeadbeef), -1, ERANGE); /* large negative */
322 CHECK("/", __getcwd(kbuf, 0x7000beef), -1, ERANGE); /* large positive */
323 CHECK("/", __getcwd(kbuf, 0x10000), -1, ERANGE); /* outside address space */
324 CHECK("/", __getcwd(kbuf+0x100000, sizeof(kbuf)), -1, EFAULT); /* outside address space */
325 CHECK("/", __getcwd(0, 30), -1, EFAULT);
326 CHECK("/", __getcwd((void*)0xdeadbeef, 30), -1, EFAULT);
327 CHECK("/", __getcwd(kbuf, 2), 2, 0);
328 assert (strcmp(kbuf, "/") == 0);
329 CHECK("/", __getcwd(kbuf, sizeof(kbuf)), 2, 0);
330
331 CHECK("/", __getcwd(kbuf, 0), -1, ERANGE);
332 CHECK("/", __getcwd(kbuf, 1), -1, ERANGE);
333
334 chdir("/sbin");
335 CHECK("/sbin", __getcwd(kbuf, sizeof(kbuf)), 6, 0);
336 /* verify that cacheable path gets range check right.. */
337 CHECK("/sbin", __getcwd(kbuf, 3), -1, ERANGE);
338 chdir("/etc/mtree");
339 CHECK("/etc/mtree", __getcwd(kbuf, sizeof(kbuf)), 11, 0);
340 CHECK("/etc/mtree", __getcwd(kbuf, sizeof(kbuf)), 11, 0);
341 /* mount point */
342 chdir("/usr/bin");
343 CHECK("/usr/bin", __getcwd(kbuf, sizeof(kbuf)), 9, 0);
344
345 /* really large (non-cacheable) entry name */
346 chdir("/tmp");
347 (void) rmdir(bigname);
348 mkdir(bigname, 0755);
349 chdir(bigname);
350
351 /* verify that non-cachable path gets range check right.. */
352 CHECK("/tmp/" bigname, __getcwd(kbuf, 10), -1, ERANGE);
353 CHECK("/tmp/" bigname, __getcwd(kbuf, sizeof(kbuf)), 40, 0);
354
355 if (rmdir("/tmp/" bigname) < 0) {
356 perror("rmdir");
357 }
358 CHECK("deleted directory", __getcwd(kbuf, sizeof(kbuf)), -1, ENOENT);
359
360 chdir("/tmp");
361 (void) rmdir(littlename);
362 mkdir(littlename, 0755);
363 chdir(littlename);
364 CHECK("/tmp/" littlename, __getcwd(kbuf, sizeof(kbuf)), 16, 0);
365 if (rename("/tmp/" littlename, "/tmp/" othername) < 0) {
366 perror("rename");
367 fail++;
368 }
369 CHECK("/tmp/" othername, __getcwd(kbuf, sizeof(kbuf)), 16, 0);
370 if (rmdir("/tmp/" othername) < 0) {
371 perror("rmdir");
372 fail++;
373 }
374 CHECK("deleted directory", __getcwd(kbuf, sizeof(kbuf)), -1, ENOENT);
375
376 mkdir("/tmp/bigdir", 0755);
377 for (i=0; i<nloops; i++) {
378 char buf[MAXPATHLEN];
379 snprintf(buf, MAXPATHLEN, "/tmp/bigdir/bigsubdirwithanamewhichistoolongtocache%04d", i);
380 (void)rmdir(buf);
381 if (mkdir (buf, 0755) < 0) {
382 perror("mkdir");
383 fail++;
384 break;
385 }
386 }
387 for (i=0; i<nloops; i++) {
388 char buf[MAXPATHLEN];
389 snprintf(buf, MAXPATHLEN, "/tmp/bigdir/bigsubdirwithanamewhichistoolongtocache%04d", i);
390 if (chdir(buf) < 0) {
391 perror("chdir");
392 fail++;
393 break;
394 }
395 CHECK(buf, __getcwd(kbuf, sizeof(kbuf)), strlen(buf)+1, 0);
396 }
397 for (i=0; i<nloops; i++) {
398 char buf[MAXPATHLEN];
399 snprintf(buf, MAXPATHLEN, "/tmp/bigdir/bigsubdirwithanamewhichistoolongtocache%04d", i);
400 (void)rmdir(buf);
401 }
402 (void)rmdir("/tmp/bigdir");
403
404 test___getcwd_perms();
405 test___getcwd_chroot();
406 }
407
408
409 void
410 stress_test_getcwd()
411 {
412 char buf[MAXPATHLEN];
413 char ubuf[MAXPATHLEN];
414 char kbuf[MAXPATHLEN];
415 printf("reading directories from stdin..\n");
416 while (fgets(buf, MAXPATHLEN, stdin)) {
417 char *cp = strrchr(buf, '\n');
418 if (cp) *cp = '\0';
419
420 chdir (buf);
421
422 cp = old_getcwd (ubuf, MAXPATHLEN);
423 assert (strcmp (buf, ubuf) == 0);
424
425 CHECK(buf, __getcwd (kbuf, MAXPATHLEN),
426 strlen(ubuf)+1, 0);
427 }
428 }
429
430
431 /*
432 * - large directories.
433 *
434 * - every single filesystem type
435 *
436 * - walk filesystem, compare sys_getcwd with getcwd for each
437 * directory
438 */
439
440 void
441 usage(progname)
442 char *progname;
443 {
444 fprintf(stderr, "usage: %s [-srpvw] [-l nloops]\n", progname);
445 exit(1);
446 }
447
448 int run_stress = 0;
449 int run_regression = 0;
450 int run_performance = 0;
451
452 int
453 main(argc, argv)
454 int argc;
455 char **argv;
456 {
457 int ch;
458 char *progname = argv[0];
459
460 uid_from_user("nobody", &altid);
461
462 while ((ch = getopt(argc, argv, "srpvwl:u:")) != -1)
463 switch (ch) {
464 case 's':
465 run_stress++;
466 break;
467 case 'r':
468 run_regression++;
469 break;
470 case 'p':
471 run_performance++;
472 break;
473 case 'v':
474 verbose++;
475 break;
476 case 'w':
477 sleepflag++;
478 break;
479 case 'l':
480 nloops = atoi(optarg);
481 if (nloops == 0)
482 nloops = 100;
483 break;
484 case 'u':
485 if (uid_from_user(optarg, &altid) != 0) {
486 fprintf(stderr, "unknown user %s\n", optarg);
487 usage(progname);
488 exit(1);
489 }
490 break;
491 case '?':
492 default:
493 usage(progname);
494 }
495 if (argc != optind)
496 usage(progname);
497
498 if (run_regression)
499 test___getcwd();
500
501 if (!fail && run_performance)
502 test_speed();
503
504 if (!fail && run_stress)
505 stress_test_getcwd();
506
507
508 if (verbose)
509 printf ("%d passes\n", pass);
510 if (!fail)
511 exit (0);
512 else {
513 printf("%d failures\n", fail);
514 exit(1);
515 }
516 }
517
518
519