From 1a9ecc62f33ee22b6e258ec68b0df427b23ed374 Mon Sep 17 00:00:00 2001 From: dtucker Date: Wed, 10 Aug 2005 11:52:36 +0000 Subject: [PATCH] - (dtucker) [LICENCE configure.ac defines.h openbsd-compat/realpath.c] Sync current (thread-safe) version of realpath.c from OpenBSD (which is in turn based on FreeBSD's). ok djm@ --- ChangeLog | 3 + LICENCE | 1 + configure.ac | 1 - defines.h | 4 + openbsd-compat/realpath.c | 266 ++++++++++++++++++-------------------- 5 files changed, 137 insertions(+), 138 deletions(-) diff --git a/ChangeLog b/ChangeLog index 58fae16c..f3326e0e 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,6 +1,9 @@ 20050810 - (dtucker) [configure.ac] Test libedit library and headers for compatibility. Report from skeleten AT shillest.net, ok djm@ + - (dtucker) [LICENCE configure.ac defines.h openbsd-compat/realpath.c] + Sync current (thread-safe) version of realpath.c from OpenBSD (which is + in turn based on FreeBSD's). ok djm@ 20050809 - (tim) [configure.ac] Allow --with-audit=no. OK dtucker@ diff --git a/LICENCE b/LICENCE index ae03eb3a..5def839e 100644 --- a/LICENCE +++ b/LICENCE @@ -255,6 +255,7 @@ OpenSSH contains no GPL code. Damien Miller Eric P. Allman The Regents of the University of California + Constantin S. Svintsoff * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions diff --git a/configure.ac b/configure.ac index 6b035fe7..5eec34dc 100644 --- a/configure.ac +++ b/configure.ac @@ -1085,7 +1085,6 @@ AC_CHECK_FUNCS( \ clock \ closefrom \ dirfd \ - fchdir \ fchmod \ fchown \ freeaddrinfo \ diff --git a/defines.h b/defines.h index ac7c17d2..8ab85cac 100644 --- a/defines.h +++ b/defines.h @@ -62,6 +62,10 @@ enum # endif /* PATH_MAX */ #endif /* MAXPATHLEN */ +#ifndef MAXSYMLINKS +# define MAXSYMLINKS 5 +#endif + #ifndef STDIN_FILENO # define STDIN_FILENO 0 #endif diff --git a/openbsd-compat/realpath.c b/openbsd-compat/realpath.c index 7f73bd99..8430bec2 100644 --- a/openbsd-compat/realpath.c +++ b/openbsd-compat/realpath.c @@ -1,11 +1,7 @@ /* OPENBSD ORIGINAL: lib/libc/stdlib/realpath.c */ /* - * Copyright (c) 1994 - * The Regents of the University of California. All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Jan-Simon Pendry. + * Copyright (c) 2003 Constantin S. Svintsoff * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -15,14 +11,14 @@ * 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. 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. + * 3. The names of the authors may not 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 + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR 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) @@ -36,169 +32,165 @@ #if !defined(HAVE_REALPATH) || defined(BROKEN_REALPATH) -#if defined(LIBC_SCCS) && !defined(lint) -static char *rcsid = "$OpenBSD: realpath.c,v 1.11 2004/11/30 15:12:59 millert Exp $"; -#endif /* LIBC_SCCS and not lint */ - #include #include #include -#include #include #include #include /* - * MAXSYMLINKS - */ -#ifndef MAXSYMLINKS -#define MAXSYMLINKS 5 -#endif - -/* - * char *realpath(const char *path, char resolved_path[MAXPATHLEN]); + * char *realpath(const char *path, char resolved[PATH_MAX]); * * Find the real name of path, by removing all ".", ".." and symlink * components. Returns (resolved) on success, or (NULL) on failure, * in which case the path which caused trouble is left in (resolved). */ char * -realpath(const char *path, char *resolved) +realpath(const char *path, char resolved[PATH_MAX]) { struct stat sb; - int fd, n, needslash, serrno; - char *p, *q, wbuf[MAXPATHLEN]; - int symlinks = 0; - - /* Save the starting point. */ -#ifndef HAVE_FCHDIR - char start[MAXPATHLEN]; - /* this is potentially racy but without fchdir we have no option */ - if (getcwd(start, sizeof(start)) == NULL) { - resolved[0] = '.'; + char *p, *q, *s; + size_t left_len, resolved_len; + unsigned symlinks; + int serrno, slen; + char left[PATH_MAX], next_token[PATH_MAX], symlink[PATH_MAX]; + + serrno = errno; + symlinks = 0; + if (path[0] == '/') { + resolved[0] = '/'; resolved[1] = '\0'; - return (NULL); + if (path[1] == '\0') + return (resolved); + resolved_len = 1; + left_len = strlcpy(left, path + 1, sizeof(left)); + } else { + if (getcwd(resolved, PATH_MAX) == NULL) { + strlcpy(resolved, ".", PATH_MAX); + return (NULL); + } + resolved_len = strlen(resolved); + left_len = strlcpy(left, path, sizeof(left)); } -#endif - if ((fd = open(".", O_RDONLY)) < 0) { - resolved[0] = '.'; - resolved[1] = '\0'; + if (left_len >= sizeof(left) || resolved_len >= PATH_MAX) { + errno = ENAMETOOLONG; return (NULL); } - /* Convert "." -> "" to optimize away a needless lstat() and chdir() */ - if (path[0] == '.' && path[1] == '\0') - path = ""; - /* - * Find the dirname and basename from the path to be resolved. - * Change directory to the dirname component. - * lstat the basename part. - * if it is a symlink, read in the value and loop. - * if it is a directory, then change to that directory. - * get the current directory name and append the basename. + * Iterate over path components in `left'. */ - if (strlcpy(resolved, path, MAXPATHLEN) >= MAXPATHLEN) { - serrno = ENAMETOOLONG; - goto err2; - } -loop: - q = strrchr(resolved, '/'); - if (q != NULL) { - p = q + 1; - if (q == resolved) - q = "/"; - else { - do { - --q; - } while (q > resolved && *q == '/'); - q[1] = '\0'; - q = resolved; + while (left_len != 0) { + /* + * Extract the next path component and adjust `left' + * and its length. + */ + p = strchr(left, '/'); + s = p ? p : left + left_len; + if (s - left >= sizeof(next_token)) { + errno = ENAMETOOLONG; + return (NULL); } - if (chdir(q) < 0) - goto err1; - } else - p = resolved; - - /* Deal with the last component. */ - if (*p != '\0' && lstat(p, &sb) == 0) { - if (S_ISLNK(sb.st_mode)) { - if (++symlinks > MAXSYMLINKS) { - errno = ELOOP; - goto err1; + memcpy(next_token, left, s - left); + next_token[s - left] = '\0'; + left_len -= s - left; + if (p != NULL) + memmove(left, s + 1, left_len + 1); + if (resolved[resolved_len - 1] != '/') { + if (resolved_len + 1 >= PATH_MAX) { + errno = ENAMETOOLONG; + return (NULL); } - if ((n = readlink(p, resolved, MAXPATHLEN-1)) < 0) - goto err1; - resolved[n] = '\0'; - goto loop; + resolved[resolved_len++] = '/'; + resolved[resolved_len] = '\0'; } - if (S_ISDIR(sb.st_mode)) { - if (chdir(p) < 0) - goto err1; - p = ""; + if (next_token[0] == '\0') + continue; + else if (strcmp(next_token, ".") == 0) + continue; + else if (strcmp(next_token, "..") == 0) { + /* + * Strip the last path component except when we have + * single "/" + */ + if (resolved_len > 1) { + resolved[resolved_len - 1] = '\0'; + q = strrchr(resolved, '/') + 1; + *q = '\0'; + resolved_len = q - resolved; + } + continue; } - } - - /* - * Save the last component name and get the full pathname of - * the current directory. - */ - if (strlcpy(wbuf, p, sizeof(wbuf)) >= sizeof(wbuf)) { - errno = ENAMETOOLONG; - goto err1; - } - if (getcwd(resolved, MAXPATHLEN) == NULL) - goto err1; - - /* - * Join the two strings together, ensuring that the right thing - * happens if the last component is empty, or the dirname is root. - */ - if (resolved[0] == '/' && resolved[1] == '\0') - needslash = 0; - else - needslash = 1; - if (*wbuf) { - if (strlen(resolved) + strlen(wbuf) + needslash >= MAXPATHLEN) { + /* + * Append the next path component and lstat() it. If + * lstat() fails we still can return successfully if + * there are no more path components left. + */ + resolved_len = strlcat(resolved, next_token, PATH_MAX); + if (resolved_len >= PATH_MAX) { errno = ENAMETOOLONG; - goto err1; + return (NULL); } - if (needslash) { - if (strlcat(resolved, "/", MAXPATHLEN) >= MAXPATHLEN) { - errno = ENAMETOOLONG; - goto err1; + if (lstat(resolved, &sb) != 0) { + if (errno == ENOENT && p == NULL) { + errno = serrno; + return (resolved); } + return (NULL); } - if (strlcat(resolved, wbuf, MAXPATHLEN) >= MAXPATHLEN) { - errno = ENAMETOOLONG; - goto err1; - } - } + if (S_ISLNK(sb.st_mode)) { + if (symlinks++ > MAXSYMLINKS) { + errno = ELOOP; + return (NULL); + } + slen = readlink(resolved, symlink, sizeof(symlink) - 1); + if (slen < 0) + return (NULL); + symlink[slen] = '\0'; + if (symlink[0] == '/') { + resolved[1] = 0; + resolved_len = 1; + } else if (resolved_len > 1) { + /* Strip the last path component. */ + resolved[resolved_len - 1] = '\0'; + q = strrchr(resolved, '/') + 1; + *q = '\0'; + resolved_len = q - resolved; + } - /* Go back to where we came from. */ -#ifdef HAVE_FCHDIR - if (fchdir(fd) < 0) { -#else - if (chdir(start) < 0) { -#endif - serrno = errno; - goto err2; + /* + * If there are any path components left, then + * append them to symlink. The result is placed + * in `left'. + */ + if (p != NULL) { + if (symlink[slen - 1] != '/') { + if (slen + 1 >= sizeof(symlink)) { + errno = ENAMETOOLONG; + return (NULL); + } + symlink[slen] = '/'; + symlink[slen + 1] = 0; + } + left_len = strlcat(symlink, left, sizeof(left)); + if (left_len >= sizeof(left)) { + errno = ENAMETOOLONG; + return (NULL); + } + } + left_len = strlcpy(left, symlink, sizeof(left)); + } } - /* It's okay if the close fails, what's an fd more or less? */ - (void)close(fd); + /* + * Remove trailing slash except when the resolved pathname + * is a single "/". + */ + if (resolved_len > 1 && resolved[resolved_len - 1] == '/') + resolved[resolved_len - 1] = '\0'; return (resolved); - -err1: serrno = errno; -#ifdef HAVE_FCHDIR - (void)fchdir(fd); -#else - chdir(start); -#endif -err2: (void)close(fd); - errno = serrno; - return (NULL); } #endif /* !defined(HAVE_REALPATH) || defined(BROKEN_REALPATH) */ -- 2.45.2