From 39a719159eedda9aff5fd5d82ebf8f5b202ca936 Mon Sep 17 00:00:00 2001 From: "Todd C. Miller" Date: Mon, 7 Sep 1998 02:55:14 +0000 Subject: [PATCH] getcwd(3) from OpenBSD for those without it. --- getcwd.c | 371 +++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 238 insertions(+), 133 deletions(-) diff --git a/getcwd.c b/getcwd.c index b9a7f5f62..d1f87f847 100644 --- a/getcwd.c +++ b/getcwd.c @@ -1,45 +1,50 @@ /* - * CU sudo version 1.5.6 + * Copyright (c) 1989, 1991, 1993 + * The Regents of the University of California. All rights reserved. * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 1, or (at your option) - * any later version. + * 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 program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - * - * Please send bugs, changes, problems to sudo-bugs@courtesan.com - * - ******************************************************************* - * - * This module contains getcwd(3) for those systems that lack it. - * getcwd(3) returns a pointer to the current working dir. It uses - * path as a copy-out parameter and malloc(3)s space if path is NULL. - * This implementation of getcwd(3) restricts len(path) to be < MAXPATHLEN. - * - * Todd C. Miller (millert@colorado.edu) Fri Jun 3 18:32:19 MDT 1994 + * 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. */ -#ifndef lint -static char rcsid[] = "$Id$"; -#endif /* lint */ +#if defined(LIBC_SCCS) && !defined(lint) +static char rcsid[] = "$OpenBSD: getcwd.c,v 1.5 1998/08/14 21:39:26 deraadt Exp $"; +#endif /* LIBC_SCCS and not lint */ #include "config.h" +#include +#include + +#include #include #ifdef STDC_HEADERS #include #endif /* STDC_HEADERS */ -#ifdef HAVE_UNISTD_H -#include -#endif /* HAVE_UNISTD_H */ #ifdef HAVE_STRING_H #include #endif /* HAVE_STRING_H */ @@ -47,118 +52,218 @@ static char rcsid[] = "$Id$"; #include #endif /* HAVE_STRINGS_H */ #if defined(HAVE_MALLOC_H) && !defined(STDC_HEADERS) -# include +#include #endif /* HAVE_MALLOC_H && !STDC_HEADERS */ -#include -#include -#include -#include -#include +#ifdef HAVE_UNISTD_H +#include +#endif /* HAVE_UNISTD_H */ +#if HAVE_DIRENT_H +# include +# define NAMLEN(dirent) strlen((dirent)->d_name) +#else +# define dirent direct +# define NAMLEN(dirent) (dirent)->d_namlen +# if HAVE_SYS_NDIR_H +# include +# endif +# if HAVE_SYS_DIR_H +# include +# endif +# if HAVE_NDIR_H +# include +# endif +#endif -#include #include "compat.h" -#ifndef STDC_HEADERS -#ifndef __GNUC__ /* gcc has its own malloc */ -extern char *malloc __P((size_t)); -#endif /* __GNUC__ */ -extern char *strcpy __P((char *, const char *)); -extern int strlen __P((const char *)); -extern char *getwd __P((char *)); -extern char *fgets __P((char *, int, FILE *)); -#endif /* !STDC_HEADERS */ +#define ISDOT(dp) \ + (dp->d_name[0] == '.' && (dp->d_name[1] == '\0' || \ + (dp->d_name[1] == '.' && dp->d_name[2] == '\0'))) +char * +getcwd(pt, size) + char *pt; + size_t size; +{ + struct dirent *dp; + DIR *dir = NULL; + 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; -/* - * Since we can't count on this being defined... - */ -extern int errno; + /* + * 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'; -/****************************************************************** - * - * getcwd() - * - * getcwd() returns a pointer to the current working dir. It uses - * path as a copy-out parameter and malloc(3)s space if path is NULL. - */ + /* Save root values, so know when to stop. */ + if (stat("/", &s)) + goto err; + root_dev = s.st_dev; + root_ino = s.st_ino; -char * getcwd(path, psize) - char * path; /* path to copy into */ - size_t psize; /* size of path */ -{ - char buf[MAXPATHLEN+1]; /* +1 for the newline */ - size_t blen; /* length of buf */ - FILE * pwd; /* stream for "pwd" process */ - int fd[2]; /* for pipe(2) */ - pid_t pid; /* pid of "pwd" process */ - int status; /* status from wait(2) */ - - /* sanity check */ - if (path && psize == 0) { - errno = EINVAL; - return(NULL); - } - - /* - * open a pipe to pwd and read a line - */ - if (pipe(fd) < 0) - return(NULL); - switch ((pid = fork())) { - case -1: - /* fork failed */ - (void) close(fd[0]); - (void) close(fd[1]); - return(NULL); - case 0: - /* in child */ - (void) dup2(fd[0], 0); - (void) dup2(fd[1], 1); - (void) close(fd[0]); - (void) close(fd[1]); - execl(_PATH_PWD, "pwd", NULL); - _exit(-1); /* should not happen */ - } - - /* in parent */ - if ((pwd = fdopen(fd[0], "r")) == NULL) { - (void) close(fd[0]); - (void) close(fd[1]); - return(NULL); - } - - if (!fgets(buf, sizeof(buf), pwd)) { - errno = EACCES; /* what an assumption... */ - pclose(pwd); - return(NULL); - } - - /* wait for the pipe to close */ - while (wait(&status) != pid) - ; - - blen = strlen(buf); - if (buf[blen - 1] == '\n') - buf[--blen] = '\0'; /* remove newline */ - else if (blen >= MAXPATHLEN) { - errno = ENAMETOOLONG; /* only possible w/o newline */ - return(NULL); - } - - /* sanity check */ - if (path && psize < blen + 1) { - errno = ERANGE; - return(NULL); - } - - if (path == NULL) { - if (!(path = (char *) malloc(MAXPATHLEN))) { - errno = ENOMEM; - return(NULL); + 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. + */ + bcopy(bpt, pt, 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) { + char *nup; + + if ((nup = realloc(up, upsize *= 2)) == NULL) + goto err; + up = nup; + 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) + break; + } + } else + for (;;) { + if (!(dp = readdir(dir))) + goto notfound; + if (ISDOT(dp)) + continue; + bcopy(dp->d_name, bup, NAMLEN(dp) + 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 <= NAMLEN(dp) + (first ? 1 : 2)) { + size_t len, off; + char *npt; + + if (!ptsize) { + errno = ERANGE; + goto err; + } + off = bpt - pt; + len = ept - bpt; + if ((npt = realloc(pt, ptsize *= 2)) == NULL) + goto err; + pt = npt; + bpt = pt + off; + ept = pt + ptsize; + bcopy(bpt, ept - len, len); + bpt = ept - len; + } + if (!first) + *--bpt = '/'; + bpt -= NAMLEN(dp); + bcopy(dp->d_name, bpt, NAMLEN(dp)); + (void)closedir(dir); + + /* Truncate any file name. */ + *bup = '\0'; } - } - (void) strcpy(path, buf); - return(path); +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); + if (up) + free(up); + if (dir) + (void)closedir(dir); + return (NULL); } -- 2.40.0