1 /* $NetBSD: getcwd.c,v 1.10 2011/08/18 02:32:32 christos 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 *
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.
30 */
31
32 /*
33 * test SYS___getcwd.
34 */
35
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>
45
46 #include <sys/param.h> /* for MAXPATHLEN */
47 #include <sys/types.h>
48 #include <sys/stat.h>
49 #include <sys/wait.h>
50
51 #include "getcwd.h"
52
53 int main(int, char *[]);
54
55 static void check1(char *dir, char *buf, char *calltext,
56 int actual, int expected, int experr);
57
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));
62
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);
67
68 static void stress_test_getcwd(void);
69 static void usage(char *progname);
70
71 /* libc-private interface */
72 int __getcwd(char *, size_t);
73
74 /*
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.
93 */
94
95 /*
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.
103 */
104
105 #define bigname "Funkelhausersteinweitz.SIPBADMIN.a" /* don't ask */
106 #define littlename "getcwdtest"
107 #define othername "testgetcwd"
108
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;
114
115 static uid_t altid = -1;
116
117 static void
check1(dir,buf,calltext,actual,expected,experr)118 check1 (dir, buf, calltext, actual, expected, experr)
119 char *dir;
120 char *buf;
121 char *calltext;
122 int actual, expected, experr;
123 {
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);
158 }
159 }
160 pass++;
161 if (verbose)
162 printf("test %d: in %s, %s passed\n", ntest, dir, calltext);
163 }
164 if (sleepflag)
165 sleep(1);
166 }
167
168 int nloops = 100;
169
170 void
time_old_getcwd()171 time_old_getcwd()
172 {
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);
178 }
179
180 }
181
182 void
time_kern_getcwd()183 time_kern_getcwd()
184 {
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);
190 }
191 }
192
193 static void
time_func(name,func)194 time_func(name, func)
195 char *name;
196 void (*func)(void);
197 {
198 struct timeval before, after;
199 double delta_t;
200
201 int i;
202 chdir ("/usr/share/examples/emul/ultrix/etc");
203
204 gettimeofday(&before, 0);
205 for (i=0; i<nloops; i++) {
206 (*func)();
207 }
208 gettimeofday(&after, 0);
209
210 delta_t = after.tv_sec - before.tv_sec;
211
212 delta_t += ((double)(after.tv_usec - before.tv_usec))/1000000.0;
213
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);
216 }
217
218 void
test_speed()219 test_speed()
220 {
221 int i;
222 for (i=0; i<5; i++)
223 time_func("kernel getcwd", time_kern_getcwd);
224
225 for (i=0; i<5; i++)
226 time_func("old user-space getcwd", time_old_getcwd);
227 }
228
229 #define CHECK(dir, call, ret, err) \
230 check1((dir), kbuf, #call, (call), (ret), (err))
231
232
233 void
test___getcwd_perms()234 test___getcwd_perms()
235 {
236 char kbuf[1024];
237
238 if (geteuid() != 0)
239 {
240 fprintf(stderr, "Not root; skipping permission tests\n");
241 return;
242 }
243
244 mkdir ("/tmp/permdir", 0700);
245 mkdir ("/tmp/permdir/subdir", 0755);
246 chdir ("/tmp/permdir/subdir");
247
248 seteuid(altid);
249
250 CHECK("/tmp/permdir/subdir", __getcwd(kbuf, sizeof(kbuf)), -1, EACCES);
251
252 seteuid(0);
253 chdir ("/");
254 rmdir ("/tmp/permdir/subdir");
255 rmdir ("/tmp/permdir");
256
257 mkdir ("/tmp/permdir", 0755);
258 mkdir ("/tmp/permdir/subdir", 0711);
259 chdir ("/tmp/permdir/subdir");
260
261 seteuid(altid);
262
263 CHECK("/tmp/permdir/subdir", __getcwd(kbuf, sizeof(kbuf)), 20, 0);
264
265 seteuid(0);
266 chdir ("/");
267 rmdir ("/tmp/permdir/subdir");
268 rmdir ("/tmp/permdir");
269 }
270
271 void
test___getcwd_chroot()272 test___getcwd_chroot()
273 {
274 int pid, status;
275 char kbuf[1024];
276
277 if (geteuid() != 0)
278 {
279 fprintf(stderr, "Not root; skipping chroot tests\n");
280 return;
281 }
282
283 /* XXX we need fchroot to do this properly.. */
284 mkdir ("/tmp/chrootdir", 0755);
285 mkdir ("/tmp/chrootdir/subdir", 0755);
286
287 chdir ("/tmp/chrootdir");
288
289 CHECK ("/tmp/chrootdir", __getcwd(kbuf, sizeof(kbuf)), 15, 0);
290
291 fflush(NULL);
292
293 pid = fork();
294
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);
309
310 if (fail)
311 exit(1);
312 else
313 exit(0);
314 } else {
315 waitpid(pid, &status, 0);
316
317 if (WIFEXITED(status) &&
318 (WEXITSTATUS(status) == 0))
319 pass++;
320 else
321 fail++;
322
323 }
324
325 chdir ("/");
326 rmdir ("/tmp/chrootdir/subdir");
327 rmdir ("/tmp/chrootdir");
328 }
329
330
331
332
333 void
test___getcwd()334 test___getcwd()
335 {
336 int i;
337 static char kbuf[1024];
338
339 chdir("/");
340
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((void *)0x10000, 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);
352
353 CHECK("/", __getcwd(kbuf, 0), -1, ERANGE);
354 CHECK("/", __getcwd(kbuf, 1), -1, ERANGE);
355
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);
366
367 /* really large (non-cacheable) entry name */
368 chdir("/tmp");
369 (void) rmdir(bigname);
370 mkdir(bigname, 0755);
371 chdir(bigname);
372
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);
376
377 if (rmdir("/tmp/" bigname) < 0) {
378 perror("rmdir");
379 }
380 CHECK("deleted directory", __getcwd(kbuf, sizeof(kbuf)), -1, ENOENT);
381
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++;
390 }
391 CHECK("/tmp/" othername, __getcwd(kbuf, sizeof(kbuf)), 16, 0);
392 if (rmdir("/tmp/" othername) < 0) {
393 perror("rmdir");
394 fail++;
395 }
396 CHECK("deleted directory", __getcwd(kbuf, sizeof(kbuf)), -1, ENOENT);
397
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;
407 }
408 }
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;
416 }
417 CHECK(buf, __getcwd(kbuf, sizeof(kbuf)), strlen(buf)+1, 0);
418 }
419 for (i=0; i<nloops; i++) {
420 char buf[MAXPATHLEN];
421 snprintf(buf, MAXPATHLEN, "/tmp/bigdir/bigsubdirwithanamewhichistoolongtocache%04d", i);
422 (void)rmdir(buf);
423 }
424 (void)rmdir("/tmp/bigdir");
425
426 test___getcwd_perms();
427 test___getcwd_chroot();
428 }
429
430
431 void
stress_test_getcwd()432 stress_test_getcwd()
433 {
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';
441
442 if (chdir (buf) < 0) {
443 warn("Can't change directory to %s", buf);
444 continue;
445 }
446
447
448 cp = old_getcwd (ubuf, MAXPATHLEN);
449 if (strcmp(buf, ubuf) != 0) {
450 warnx("In %s, old_getcwd says %s",
451 buf, ubuf);
452 }
453
454
455 CHECK(buf, __getcwd (kbuf, MAXPATHLEN),
456 strlen(ubuf)+1, 0);
457 }
458 }
459
460
461 /*
462 * - large directories.
463 *
464 * - every single filesystem type
465 *
466 * - walk filesystem, compare sys_getcwd with getcwd for each
467 * directory
468 */
469
470 void
usage(progname)471 usage(progname)
472 char *progname;
473 {
474 fprintf(stderr, "usage: %s [-srpvw] [-l nloops]\n", progname);
475 exit(1);
476 }
477
478 int run_stress = 0;
479 int run_regression = 0;
480 int run_performance = 0;
481
482 int
main(argc,argv)483 main(argc, argv)
484 int argc;
485 char **argv;
486 {
487 int ch;
488 char *progname = argv[0];
489
490 uid_from_user("nobody", &altid);
491
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);
519 }
520 break;
521 case '?':
522 default:
523 usage(progname);
524 }
525 if (argc != optind)
526 usage(progname);
527
528 if (run_regression)
529 test___getcwd();
530
531 if (!fail && run_performance)
532 test_speed();
533
534 if (!fail && run_stress)
535 stress_test_getcwd();
536
537
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);
545 }
546 }
547
548
549