xref: /netbsd/regress/sys/kern/getcwd/getcwd.c (revision 6550d01e)
1 /*	$NetBSD: getcwd.c,v 1.9 2008/04/28 20:23:06 martin 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
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
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
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
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
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
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
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
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(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);
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
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
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
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