| /* $OpenBSD: realpath.c,v 1.17 2014/10/18 20:43:52 doug Exp $ */ |
| /* |
| * Copyright (c) 2003 Constantin S. Svintsoff <kostik@iclub.nsu.ru> |
| * |
| * 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. 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 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 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) |
| * 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. |
| */ |
| |
| /* OPENBSD ORIGINAL: lib/libc/stdlib/realpath.c */ |
| |
| #include "includes.h" |
| |
| #if !defined(HAVE_REALPATH) || defined(BROKEN_REALPATH) |
| |
| #include <sys/types.h> |
| #include <sys/param.h> |
| #include <sys/stat.h> |
| |
| #include <errno.h> |
| #include <stdlib.h> |
| #include <stddef.h> |
| #include <string.h> |
| #include <unistd.h> |
| |
| /* A slightly modified copy of this file exists in libexec/ld.so */ |
| |
| /* |
| * 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) |
| { |
| struct stat sb; |
| char *p, *q, *s; |
| size_t left_len, resolved_len; |
| unsigned symlinks; |
| int serrno, slen, mem_allocated; |
| char left[PATH_MAX], next_token[PATH_MAX], symlink[PATH_MAX]; |
| |
| if (path == NULL) { |
| errno = EINVAL; |
| return (NULL); |
| } |
| if (path[0] == '\0') { |
| errno = ENOENT; |
| return (NULL); |
| } |
| |
| serrno = errno; |
| |
| if (resolved == NULL) { |
| resolved = malloc(PATH_MAX); |
| if (resolved == NULL) |
| return (NULL); |
| mem_allocated = 1; |
| } else |
| mem_allocated = 0; |
| |
| symlinks = 0; |
| if (path[0] == '/') { |
| resolved[0] = '/'; |
| resolved[1] = '\0'; |
| if (path[1] == '\0') |
| return (resolved); |
| resolved_len = 1; |
| left_len = strlcpy(left, path + 1, sizeof(left)); |
| } else { |
| if (getcwd(resolved, PATH_MAX) == NULL) { |
| if (mem_allocated) |
| free(resolved); |
| else |
| strlcpy(resolved, ".", PATH_MAX); |
| return (NULL); |
| } |
| resolved_len = strlen(resolved); |
| left_len = strlcpy(left, path, sizeof(left)); |
| } |
| if (left_len >= sizeof(left) || resolved_len >= PATH_MAX) { |
| errno = ENAMETOOLONG; |
| goto err; |
| } |
| |
| /* |
| * Iterate over path components in `left'. |
| */ |
| 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 >= (ptrdiff_t)sizeof(next_token)) { |
| errno = ENAMETOOLONG; |
| goto err; |
| } |
| 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; |
| goto err; |
| } |
| resolved[resolved_len++] = '/'; |
| resolved[resolved_len] = '\0'; |
| } |
| 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; |
| } |
| |
| /* |
| * Append the next path component and lstat() it. |
| */ |
| resolved_len = strlcat(resolved, next_token, PATH_MAX); |
| if (resolved_len >= PATH_MAX) { |
| errno = ENAMETOOLONG; |
| goto err; |
| } |
| if (lstat(resolved, &sb) != 0) |
| goto err; |
| if (S_ISLNK(sb.st_mode)) { |
| if (symlinks++ > MAXSYMLINKS) { |
| errno = ELOOP; |
| goto err; |
| } |
| slen = readlink(resolved, symlink, sizeof(symlink) - 1); |
| if (slen < 0) |
| goto err; |
| 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; |
| } |
| |
| /* |
| * 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 >= |
| (ptrdiff_t)sizeof(symlink)) { |
| errno = ENAMETOOLONG; |
| goto err; |
| } |
| symlink[slen] = '/'; |
| symlink[slen + 1] = 0; |
| } |
| left_len = strlcat(symlink, left, sizeof(symlink)); |
| if (left_len >= sizeof(left)) { |
| errno = ENAMETOOLONG; |
| goto err; |
| } |
| } |
| left_len = strlcpy(left, symlink, sizeof(left)); |
| } else if (!S_ISDIR(sb.st_mode) && p != NULL) { |
| errno = ENOTDIR; |
| goto err; |
| } |
| } |
| |
| /* |
| * 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); |
| |
| err: |
| if (mem_allocated) |
| free(resolved); |
| return (NULL); |
| } |
| #endif /* !defined(HAVE_REALPATH) || defined(BROKEN_REALPATH) */ |