diff --git a/regress/sys/kern/Makefile b/regress/sys/kern/Makefile index a46623bfbe7c..9fbd1f91bf87 100644 --- a/regress/sys/kern/Makefile +++ b/regress/sys/kern/Makefile @@ -1,5 +1,5 @@ -# $NetBSD: Makefile,v 1.5 1998/01/09 13:39:57 lukem Exp $ +# $NetBSD: Makefile,v 1.6 1999/03/22 18:15:08 sommerfe Exp $ -SUBDIR+= execve unfdpass +SUBDIR+= execve unfdpass getcwd .include diff --git a/regress/sys/kern/getcwd/Makefile b/regress/sys/kern/getcwd/Makefile new file mode 100644 index 000000000000..bffb79791df1 --- /dev/null +++ b/regress/sys/kern/getcwd/Makefile @@ -0,0 +1,12 @@ +# $NetBSD: Makefile,v 1.1 1999/03/22 18:14:39 sommerfe Exp $ + +PROG= getcwd +SRCS= getcwd.c old_getcwd.c +MKMAN= no +LDFLAGS=-static +WARNS=1 + +regress: + @./getcwd -r + +.include diff --git a/regress/sys/kern/getcwd/getcwd.c b/regress/sys/kern/getcwd/getcwd.c new file mode 100644 index 000000000000..fd116e48c3f8 --- /dev/null +++ b/regress/sys/kern/getcwd/getcwd.c @@ -0,0 +1,518 @@ +/* $NetBSD: getcwd.c,v 1.1 1999/03/22 18:14:39 sommerfe Exp $ */ + +/*- + * Copyright (c) 1999 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Bill Sommerfeld. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * test SYS___getcwd. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include /* for MAXPATHLEN */ +#include +#include +#include + +#include "getcwd.h" + +int main __P((int, char *[])); + +static void check1 __P((char *dir, char *buf, char *calltext, + int actual, int expected, int experr)); + +static void time_old_getcwd __P((void)); +static void time_kern_getcwd __P((void)); +static void time_func __P((char *name, + void (*func)(void))); + +static void test_speed __P((void)); +static void test___getcwd __P((void)); +static void test___getcwd_perms __P((void)); +static void test___getcwd_chroot __P((void)); + +static void stress_test_getcwd __P((void)); +static void usage __P((char *progname)); + +/* + * test cases: + * NULL pointer + * broken pointer + * zero-length buffer + * negative length + * one-character buffer + * two-character buffer + * full-length buffer + * large (uncacheable) name in path. + * deleted directory + * after rename of parent. + * permission failure. + * good pointer near end of address space + * really huge length + * really large (multi-block) directories + * chroot interactions: + * chroot, at / inside the directory. + * chroot, at some other inside directory. + */ + +/* + * test cases not yet done: + * -o union mount + * chroot interactions: + * chroot to mounted directory. + * (i.e., proc a: chroot /foo; sleep; + * proc b: mount blort /foo) + * concurrent with force-unmounting of filesystem. + */ + +#define bigname "Funkelhausersteinweitz.SIPBADMIN.a" /* don't ask */ +#define littlename "getcwdtest" +#define othername "testgetcwd" + +static int verbose = 0; +static int test = 1; +static int fail = 0; +static int pass = 0; +static int sleepflag = 0; + +static uid_t altid = -1; + +static void +check1 (dir, buf, calltext, actual, expected, experr) + char *dir; + char *buf; + char *calltext; + int actual, expected, experr; +{ + int ntest = test++; + if (actual != expected) { + fprintf(stderr, + "test %d: in %s, %s failed; expected %d, got %d\n", + ntest, dir, calltext, expected, actual); + if (actual < 0) perror("getcwd"); + fail++; + } else if ((expected == -1) && (errno != (experr))) { + fprintf(stderr, + "test %d: in %s, %s failed; expected error %d, got %d\n", + ntest, dir, calltext, experr, errno); + if (actual < 0) perror("getcwd"); + fail++; + } else if ((expected > 0) && + (buf != NULL) && + (strcmp (dir, buf) != 0)) { + fprintf(stderr, + "test %d: in %s, %s got wrong dir %s\n", + ntest, dir, calltext, buf); + fail++; + } else { + if (expected > 0) { + char newbuf[1024]; + char *cp = old_getcwd(newbuf, sizeof(newbuf)); + if (cp == NULL) { + fail++; + fprintf(stderr, + "test %d: in %s, old getcwd failed!\n", + ntest, dir); + } else if (strcmp(cp, buf)) { + fail++; + fprintf(stderr, + "test %d: in %s, old_getcwd returned different dir %s\n", + ntest, dir, cp); + } + } + pass++; + if (verbose) + printf("test %d: in %s, %s passed\n", ntest, dir, calltext); + } + if (sleepflag) + sleep(1); +} + +int nloops = 100; + +void +time_old_getcwd() +{ + char result_buf[1024]; + if (old_getcwd(result_buf, 1024) == NULL) { + fprintf(stderr, "old_getcwd failed during timing test!\n"); + perror("old_getcwd"); + exit(1); + } + +} + +void +time_kern_getcwd() +{ + char result_buf[1024]; + if (__getcwd(result_buf, sizeof(result_buf)) < 0) { + fprintf(stderr, "getcwd failed during timing test!"); + perror("getcwd"); + exit(1); + } +} + +static void +time_func(name, func) + char *name; + void (*func) __P((void)); +{ + struct timeval before, after; + double delta_t; + + int i; + chdir ("/usr/share/examples/emul/ultrix/etc"); + + gettimeofday(&before, 0); + for (i=0; i +#if defined(LIBC_SCCS) && !defined(lint) +#if 0 +static char sccsid[] = "@(#)getcwd.c 8.5 (Berkeley) 2/7/95"; +#else +__RCSID("$NetBSD: old_getcwd.c,v 1.1 1999/03/22 18:14:39 sommerfe Exp $"); +#endif +#endif /* LIBC_SCCS and not lint */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "getcwd.h" + +#define ISDOT(dp) \ + (dp->d_name[0] == '.' && (dp->d_name[1] == '\0' || \ + (dp->d_name[1] == '.' && dp->d_name[2] == '\0'))) + + +#if defined(__SVR4) || defined(__svr4__) +#define d_fileno d_ino +#endif + +char * +old_getcwd(pt, size) + char *pt; + size_t size; +{ + struct dirent *dp; + DIR *dir; + dev_t dev; + ino_t ino; + int first; + char *bpt, *bup; + struct stat s; + dev_t root_dev; + ino_t root_ino; + size_t ptsize, upsize; + int save_errno; + char *ept, *eup, *up; + size_t dlen; + + /* + * If no buffer specified by the user, allocate one as necessary. + * If a buffer is specified, the size has to be non-zero. The path + * is built from the end of the buffer backwards. + */ + if (pt) { + ptsize = 0; + if (!size) { + errno = EINVAL; + return (NULL); + } + ept = pt + size; + } else { + if ((pt = malloc(ptsize = 1024 - 4)) == NULL) + return (NULL); + ept = pt + ptsize; + } + bpt = ept - 1; + *bpt = '\0'; + + /* + * Allocate bytes (1024 - malloc space) for the string of "../"'s. + * Should always be enough (it's 340 levels). If it's not, allocate + * as necessary. Special case the first stat, it's ".", not "..". + */ + if ((up = malloc(upsize = 1024 - 4)) == NULL) + goto err; + eup = up + MAXPATHLEN; + bup = up; + up[0] = '.'; + up[1] = '\0'; + + /* Save root values, so know when to stop. */ + if (stat("/", &s)) + goto err; + root_dev = s.st_dev; + root_ino = s.st_ino; + + errno = 0; /* XXX readdir has no error return. */ + + for (first = 1;; first = 0) { + /* Stat the current level. */ + if (lstat(up, &s)) + goto err; + + /* Save current node values. */ + ino = s.st_ino; + dev = s.st_dev; + + /* Check for reaching root. */ + if (root_dev == dev && root_ino == ino) { + *--bpt = '/'; + /* + * It's unclear that it's a requirement to copy the + * path to the beginning of the buffer, but it's always + * been that way and stuff would probably break. + */ + memmove(pt, bpt, (size_t)(ept - bpt)); + free(up); + return (pt); + } + + /* + * Build pointer to the parent directory, allocating memory + * as necessary. Max length is 3 for "../", the largest + * possible component name, plus a trailing NULL. + */ + if (bup + 3 + MAXNAMLEN + 1 >= eup) { + if ((up = realloc(up, upsize *= 2)) == NULL) + goto err; + bup = up; + eup = up + upsize; + } + *bup++ = '.'; + *bup++ = '.'; + *bup = '\0'; + + /* Open and stat parent directory. */ + if (!(dir = opendir(up)) || fstat(dirfd(dir), &s)) + goto err; + + /* Add trailing slash for next directory. */ + *bup++ = '/'; + + /* + * If it's a mount point, have to stat each element because + * the inode number in the directory is for the entry in the + * parent directory, not the inode number of the mounted file. + */ + save_errno = 0; + if (s.st_dev == dev) { + for (;;) { + if (!(dp = readdir(dir))) + goto notfound; + if (dp->d_fileno == ino) { +#if defined(__SVR4) || defined(__svr4__) + dlen = strlen(dp->d_name); +#else + dlen = dp->d_namlen; +#endif + break; + } + } + } else + for (;;) { + if (!(dp = readdir(dir))) + goto notfound; + if (ISDOT(dp)) + continue; +#if defined(__SVR4) || defined(__svr4__) + dlen = strlen(dp->d_name); +#else + dlen = dp->d_namlen; +#endif + memmove(bup, dp->d_name, dlen + 1); + + /* Save the first error for later. */ + if (lstat(up, &s)) { + if (!save_errno) + save_errno = errno; + errno = 0; + continue; + } + if (s.st_dev == dev && s.st_ino == ino) + break; + } + + /* + * Check for length of the current name, preceding slash, + * leading slash. + */ + if (bpt - pt <= dlen + (first ? 1 : 2)) { + size_t len, off; + + if (!ptsize) { + errno = ERANGE; + goto err; + } + off = bpt - pt; + len = ept - bpt; + if ((pt = realloc(pt, ptsize *= 2)) == NULL) + goto err; + bpt = pt + off; + ept = pt + ptsize; + memmove(ept - len, bpt, len); + bpt = ept - len; + } + if (!first) + *--bpt = '/'; + bpt -= dlen; + memmove(bpt, dp->d_name, dlen); + (void)closedir(dir); + + /* Truncate any file name. */ + *bup = '\0'; + } + +notfound: + /* + * If readdir set errno, use it, not any saved error; otherwise, + * didn't find the current directory in its parent directory, set + * errno to ENOENT. + */ + if (!errno) + errno = save_errno ? save_errno : ENOENT; + /* FALLTHROUGH */ +err: + if (ptsize) + free(pt); + free(up); + return (NULL); +}