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