getcwd regression test.

This commit is contained in:
sommerfe 1999-03-22 18:14:39 +00:00
parent 80729aa7de
commit bada23909e
5 changed files with 836 additions and 2 deletions

View File

@ -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 <bsd.subdir.mk>

View File

@ -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 <bsd.prog.mk>

View File

@ -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 <assert.h>
#include <errno.h>
#include <pwd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include <sys/param.h> /* for MAXPATHLEN */
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#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<nloops; i++) {
(*func)();
}
gettimeofday(&after, 0);
delta_t = after.tv_sec - before.tv_sec;
delta_t += ((double)(after.tv_usec - before.tv_usec))/1000000.0;
printf("%s: %d calls in %10.3f seconds; ", name, nloops, delta_t);
printf("%10.6f ms/call\n", (delta_t*1000.0)/nloops);
}
void
test_speed()
{
int i;
for (i=0; i<5; i++)
time_func("kernel getcwd", time_kern_getcwd);
for (i=0; i<5; i++)
time_func("old user-space getcwd", time_old_getcwd);
}
#define CHECK(dir, call, ret, err) \
check1((dir), kbuf, #call, (call), (ret), (err))
void
test___getcwd_perms()
{
char kbuf[1024];
mkdir ("/tmp/permdir", 0700);
mkdir ("/tmp/permdir/subdir", 0755);
chdir ("/tmp/permdir/subdir");
seteuid(altid);
CHECK("/tmp/permdir/subdir", __getcwd(kbuf, sizeof(kbuf)), -1, EACCES);
seteuid(0);
chdir ("/");
rmdir ("/tmp/permdir/subdir");
rmdir ("/tmp/permdir");
}
void
test___getcwd_chroot()
{
int pid, status;
char kbuf[1024];
/* XXX we need fchroot to do this properly.. */
mkdir ("/tmp/chrootdir", 0755);
mkdir ("/tmp/chrootdir/subdir", 0755);
chdir ("/tmp/chrootdir");
CHECK ("/tmp/chrootdir", __getcwd(kbuf, sizeof(kbuf)), 15, 0);
fflush(NULL);
pid = fork();
if (pid < 0) {
perror("fork");
fail++;
} else if (pid == 0) {
fail = 0;
pass = 0;
/* chroot to root of filesystem (assuming MFS /tmp) */
chroot ("/tmp");
CHECK ("/chrootdir", __getcwd(kbuf, sizeof(kbuf)), 11, 0);
/* chroot to further down */
chroot ("/chrootdir");
CHECK ("/", __getcwd(kbuf, sizeof(kbuf)), 2, 0);
chdir("subdir");
CHECK ("/subdir", __getcwd(kbuf, sizeof(kbuf)), 8, 0);
if (fail)
exit(1);
else
exit(0);
} else {
waitpid(pid, &status, 0);
if (WIFEXITED(status) &&
(WEXITSTATUS(status) == 0))
pass++;
else
fail++;
}
chdir ("/");
rmdir ("/tmp/chrootdir/subdir");
rmdir ("/tmp/chrootdir");
}
void
test___getcwd()
{
int i;
static char kbuf[1024];
chdir("/");
CHECK("/", __getcwd(0, 0), -1, ERANGE);
CHECK("/", __getcwd(0, -1), -1, ERANGE);
CHECK("/", __getcwd(kbuf, 0xdeadbeef), -1, ERANGE); /* large negative */
CHECK("/", __getcwd(kbuf, 0x7000beef), -1, ERANGE); /* large positive */
CHECK("/", __getcwd(kbuf, 0x10000), -1, ERANGE); /* outside address space */
CHECK("/", __getcwd(kbuf+0x100000, sizeof(kbuf)), -1, EFAULT); /* outside address space */
CHECK("/", __getcwd(0, 30), -1, EFAULT);
CHECK("/", __getcwd((void*)0xdeadbeef, 30), -1, EFAULT);
CHECK("/", __getcwd(kbuf, 2), 2, 0);
assert (strcmp(kbuf, "/") == 0);
CHECK("/", __getcwd(kbuf, sizeof(kbuf)), 2, 0);
CHECK("/", __getcwd(kbuf, 0), -1, ERANGE);
CHECK("/", __getcwd(kbuf, 1), -1, ERANGE);
chdir("/sbin");
CHECK("/sbin", __getcwd(kbuf, sizeof(kbuf)), 6, 0);
/* verify that cacheable path gets range check right.. */
CHECK("/sbin", __getcwd(kbuf, 3), -1, ERANGE);
chdir("/etc/mtree");
CHECK("/etc/mtree", __getcwd(kbuf, sizeof(kbuf)), 11, 0);
CHECK("/etc/mtree", __getcwd(kbuf, sizeof(kbuf)), 11, 0);
/* mount point */
chdir("/usr/bin");
CHECK("/usr/bin", __getcwd(kbuf, sizeof(kbuf)), 9, 0);
/* really large (non-cacheable) entry name */
chdir("/tmp");
(void) rmdir(bigname);
mkdir(bigname, 0755);
chdir(bigname);
/* verify that non-cachable path gets range check right.. */
CHECK("/tmp/" bigname, __getcwd(kbuf, 10), -1, ERANGE);
CHECK("/tmp/" bigname, __getcwd(kbuf, sizeof(kbuf)), 40, 0);
if (rmdir("/tmp/" bigname) < 0) {
perror("rmdir");
}
CHECK("deleted directory", __getcwd(kbuf, sizeof(kbuf)), -1, ENOENT);
chdir("/tmp");
(void) rmdir(littlename);
mkdir(littlename, 0755);
chdir(littlename);
CHECK("/tmp/" littlename, __getcwd(kbuf, sizeof(kbuf)), 16, 0);
if (rename("/tmp/" littlename, "/tmp/" othername) < 0) {
perror("rename");
fail++;
}
CHECK("/tmp/" othername, __getcwd(kbuf, sizeof(kbuf)), 16, 0);
if (rmdir("/tmp/" othername) < 0) {
perror("rmdir");
fail++;
}
CHECK("deleted directory", __getcwd(kbuf, sizeof(kbuf)), -1, ENOENT);
mkdir("/tmp/bigdir", 0755);
for (i=0; i<nloops; i++) {
char buf[MAXPATHLEN];
snprintf(buf, MAXPATHLEN, "/tmp/bigdir/bigsubdirwithanamewhichistoolongtocache%04d", i);
(void)rmdir(buf);
if (mkdir (buf, 0755) < 0) {
perror("mkdir");
fail++;
break;
}
}
for (i=0; i<nloops; i++) {
char buf[MAXPATHLEN];
snprintf(buf, MAXPATHLEN, "/tmp/bigdir/bigsubdirwithanamewhichistoolongtocache%04d", i);
if (chdir(buf) < 0) {
perror("chdir");
fail++;
break;
}
CHECK(buf, __getcwd(kbuf, sizeof(kbuf)), strlen(buf)+1, 0);
}
for (i=0; i<nloops; i++) {
char buf[MAXPATHLEN];
snprintf(buf, MAXPATHLEN, "/tmp/bigdir/bigsubdirwithanamewhichistoolongtocache%04d", i);
(void)rmdir(buf);
}
(void)rmdir("/tmp/bigdir");
test___getcwd_perms();
test___getcwd_chroot();
}
void
stress_test_getcwd()
{
char buf[MAXPATHLEN];
char ubuf[MAXPATHLEN];
char kbuf[MAXPATHLEN];
printf("reading directories from stdin..\n");
while (fgets(buf, MAXPATHLEN, stdin)) {
char *cp = strrchr(buf, '\n');
if (cp) *cp = '\0';
chdir (buf);
cp = old_getcwd (ubuf, MAXPATHLEN);
assert (strcmp (buf, ubuf) == 0);
CHECK(buf, __getcwd (kbuf, MAXPATHLEN),
strlen(ubuf)+1, 0);
}
}
/*
* - large directories.
*
* - every single filesystem type
*
* - walk filesystem, compare sys_getcwd with getcwd for each
* directory
*/
void
usage(progname)
char *progname;
{
fprintf(stderr, "usage: %s [-srpvw] [-l nloops]\n", progname);
exit(1);
}
int run_stress = 0;
int run_regression = 0;
int run_performance = 0;
int
main(argc, argv)
int argc;
char **argv;
{
int ch;
char *progname = argv[0];
uid_from_user("nobody", &altid);
while ((ch = getopt(argc, argv, "srpvwl:u:")) != -1)
switch (ch) {
case 's':
run_stress++;
break;
case 'r':
run_regression++;
break;
case 'p':
run_performance++;
break;
case 'v':
verbose++;
break;
case 'w':
sleepflag++;
break;
case 'l':
nloops = atoi(optarg);
if (nloops == 0)
nloops = 100;
break;
case 'u':
if (uid_from_user(optarg, &altid) != 0) {
fprintf(stderr, "unknown user %s\n", optarg);
usage(progname);
exit(1);
}
break;
case '?':
default:
usage(progname);
}
if (argc != optind)
usage(progname);
if (run_regression)
test___getcwd();
if (!fail && run_performance)
test_speed();
if (!fail && run_stress)
stress_test_getcwd();
if (verbose)
printf ("%d passes\n", pass);
if (!fail)
exit (0);
else {
printf("%d failures\n", fail);
exit(1);
}
}

View File

@ -0,0 +1,37 @@
/*-
* 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.
*/
extern char *old_getcwd __P((char *, size_t));

View File

@ -0,0 +1,267 @@
/* $NetBSD: old_getcwd.c,v 1.1 1999/03/22 18:14:39 sommerfe Exp $ */
/*
* Copyright (c) 1989, 1991, 1993, 1995
* The Regents of the University of California. All rights reserved.
*
* This code is derived from software contributed to Berkeley by
* Jan-Simon Pendry.
*
* 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 University of
* California, Berkeley and its contributors.
* 4. Neither the name of the University 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 REGENTS 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 REGENTS 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.
*/
/*
* This file copied from NetBSD's src/lib/libc/gen/getcwd.c 1.15, and edited
* to remove namespace stuff and the unused realpath function.
*/
#include <sys/cdefs.h>
#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 <sys/param.h>
#include <sys/stat.h>
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#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);
}