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
53int	main(int, char *[]);
54
55static void check1(char *dir, char *buf, char *calltext,
56    int actual, int expected, int experr);
57
58static void time_old_getcwd(void);
59static void time_kern_getcwd(void);
60static void time_func(char *name,
61    void (*func)(void));
62
63static void test_speed(void);
64static void test___getcwd (void);
65static void test___getcwd_perms (void);
66static void test___getcwd_chroot(void);
67
68static void stress_test_getcwd(void);
69static void usage(char *progname);
70
71/* libc-private interface */
72int __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
109static int verbose = 0;
110static int test = 1;
111static int fail = 0;
112static int pass = 0;
113static int sleepflag = 0;
114
115static uid_t altid = -1;
116
117static void
118check1 (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
168int nloops = 100;
169
170void
171time_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
182void
183time_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
193static void
194time_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
218void
219test_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
233void
234test___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
271void
272test___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
333void
334test___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
431void
432stress_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
470void
471usage(progname)
472	char *progname;
473{
474	fprintf(stderr, "usage: %s [-srpvw] [-l nloops]\n", progname);
475	exit(1);
476}
477
478int run_stress = 0;
479int run_regression = 0;
480int run_performance = 0;
481
482int
483main(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