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