1/* $OpenBSD: realpath.c,v 1.20 2015/10/13 20:55:37 millert Exp $ */ 2/* 3 * Copyright (c) 2003 Constantin S. Svintsoff <kostik@iclub.nsu.ru> 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 3. The names of the authors may not be used to endorse or promote 14 * products derived from this software without specific prior written 15 * permission. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 * SUCH DAMAGE. 28 */ 29 30/* OPENBSD ORIGINAL: lib/libc/stdlib/realpath.c */ 31 32#include "includes.h" 33 34#if !defined(HAVE_REALPATH) || defined(BROKEN_REALPATH) 35 36#include <sys/types.h> 37#include <sys/param.h> 38#include <sys/stat.h> 39 40#include <errno.h> 41#include <stdlib.h> 42#include <stddef.h> 43#include <string.h> 44#include <unistd.h> 45#include <limits.h> 46 47#ifndef SYMLOOP_MAX 48# define SYMLOOP_MAX 32 49#endif 50 51/* A slightly modified copy of this file exists in libexec/ld.so */ 52 53/* 54 * char *realpath(const char *path, char resolved[PATH_MAX]); 55 * 56 * Find the real name of path, by removing all ".", ".." and symlink 57 * components. Returns (resolved) on success, or (NULL) on failure, 58 * in which case the path which caused trouble is left in (resolved). 59 */ 60char * 61realpath(const char *path, char *resolved) 62{ 63 struct stat sb; 64 char *p, *q, *s; 65 size_t left_len, resolved_len; 66 unsigned symlinks; 67 int serrno, slen, mem_allocated; 68 char left[PATH_MAX], next_token[PATH_MAX], symlink[PATH_MAX]; 69 70 if (path[0] == '\0') { 71 errno = ENOENT; 72 return (NULL); 73 } 74 75 serrno = errno; 76 77 if (resolved == NULL) { 78 resolved = malloc(PATH_MAX); 79 if (resolved == NULL) 80 return (NULL); 81 mem_allocated = 1; 82 } else 83 mem_allocated = 0; 84 85 symlinks = 0; 86 if (path[0] == '/') { 87 resolved[0] = '/'; 88 resolved[1] = '\0'; 89 if (path[1] == '\0') 90 return (resolved); 91 resolved_len = 1; 92 left_len = strlcpy(left, path + 1, sizeof(left)); 93 } else { 94 if (getcwd(resolved, PATH_MAX) == NULL) { 95 if (mem_allocated) 96 free(resolved); 97 else 98 strlcpy(resolved, ".", PATH_MAX); 99 return (NULL); 100 } 101 resolved_len = strlen(resolved); 102 left_len = strlcpy(left, path, sizeof(left)); 103 } 104 if (left_len >= sizeof(left) || resolved_len >= PATH_MAX) { 105 errno = ENAMETOOLONG; 106 goto err; 107 } 108 109 /* 110 * Iterate over path components in `left'. 111 */ 112 while (left_len != 0) { 113 /* 114 * Extract the next path component and adjust `left' 115 * and its length. 116 */ 117 p = strchr(left, '/'); 118 s = p ? p : left + left_len; 119 if (s - left >= (ptrdiff_t)sizeof(next_token)) { 120 errno = ENAMETOOLONG; 121 goto err; 122 } 123 memcpy(next_token, left, s - left); 124 next_token[s - left] = '\0'; 125 left_len -= s - left; 126 if (p != NULL) 127 memmove(left, s + 1, left_len + 1); 128 if (resolved[resolved_len - 1] != '/') { 129 if (resolved_len + 1 >= PATH_MAX) { 130 errno = ENAMETOOLONG; 131 goto err; 132 } 133 resolved[resolved_len++] = '/'; 134 resolved[resolved_len] = '\0'; 135 } 136 if (next_token[0] == '\0') 137 continue; 138 else if (strcmp(next_token, ".") == 0) 139 continue; 140 else if (strcmp(next_token, "..") == 0) { 141 /* 142 * Strip the last path component except when we have 143 * single "/" 144 */ 145 if (resolved_len > 1) { 146 resolved[resolved_len - 1] = '\0'; 147 q = strrchr(resolved, '/') + 1; 148 *q = '\0'; 149 resolved_len = q - resolved; 150 } 151 continue; 152 } 153 154 /* 155 * Append the next path component and lstat() it. If 156 * lstat() fails we still can return successfully if 157 * there are no more path components left. 158 */ 159 resolved_len = strlcat(resolved, next_token, PATH_MAX); 160 if (resolved_len >= PATH_MAX) { 161 errno = ENAMETOOLONG; 162 goto err; 163 } 164 if (lstat(resolved, &sb) != 0) { 165 if (errno == ENOENT && p == NULL) { 166 errno = serrno; 167 return (resolved); 168 } 169 goto err; 170 } 171 if (S_ISLNK(sb.st_mode)) { 172 if (symlinks++ > SYMLOOP_MAX) { 173 errno = ELOOP; 174 goto err; 175 } 176 slen = readlink(resolved, symlink, sizeof(symlink) - 1); 177 if (slen < 0) 178 goto err; 179 symlink[slen] = '\0'; 180 if (symlink[0] == '/') { 181 resolved[1] = 0; 182 resolved_len = 1; 183 } else if (resolved_len > 1) { 184 /* Strip the last path component. */ 185 resolved[resolved_len - 1] = '\0'; 186 q = strrchr(resolved, '/') + 1; 187 *q = '\0'; 188 resolved_len = q - resolved; 189 } 190 191 /* 192 * If there are any path components left, then 193 * append them to symlink. The result is placed 194 * in `left'. 195 */ 196 if (p != NULL) { 197 if (symlink[slen - 1] != '/') { 198 if (slen + 1 >= 199 (ptrdiff_t)sizeof(symlink)) { 200 errno = ENAMETOOLONG; 201 goto err; 202 } 203 symlink[slen] = '/'; 204 symlink[slen + 1] = 0; 205 } 206 left_len = strlcat(symlink, left, sizeof(symlink)); 207 if (left_len >= sizeof(symlink)) { 208 errno = ENAMETOOLONG; 209 goto err; 210 } 211 } 212 left_len = strlcpy(left, symlink, sizeof(left)); 213 } 214 } 215 216 /* 217 * Remove trailing slash except when the resolved pathname 218 * is a single "/". 219 */ 220 if (resolved_len > 1 && resolved[resolved_len - 1] == '/') 221 resolved[resolved_len - 1] = '\0'; 222 return (resolved); 223 224err: 225 if (mem_allocated) 226 free(resolved); 227 return (NULL); 228} 229#endif /* !defined(HAVE_REALPATH) || defined(BROKEN_REALPATH) */ 230