1 /* $Id: realpath.c,v 1.3 2013/01/25 17:06:09 sjg Exp $ */ 2 /* from: $NetBSD: getcwd.c,v 1.53 2012/06/21 23:29:23 enami Exp $ */ 3 4 /* 5 * Copyright (c) 1989, 1991, 1993, 1995 6 * The Regents of the University of California. All rights reserved. 7 * 8 * This code is derived from software contributed to Berkeley by 9 * Jan-Simon Pendry. 10 * 11 * Redistribution and use in source and binary forms, with or without 12 * modification, are permitted provided that the following conditions 13 * are met: 14 * 1. Redistributions of source code must retain the above copyright 15 * notice, this list of conditions and the following disclaimer. 16 * 2. Redistributions in binary form must reproduce the above copyright 17 * notice, this list of conditions and the following disclaimer in the 18 * documentation and/or other materials provided with the distribution. 19 * 3. Neither the name of the University nor the names of its contributors 20 * may be used to endorse or promote products derived from this software 21 * without specific prior written permission. 22 * 23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 26 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 33 * SUCH DAMAGE. 34 */ 35 #ifdef HAVE_CONFIG_H 36 # include <config.h> 37 #endif 38 #ifndef HAVE_REALPATH 39 40 #include <sys/cdefs.h> 41 #include <sys/param.h> 42 #include <sys/stat.h> 43 44 #include <errno.h> 45 #ifdef HAVE_STDLIB_H 46 # include <stdlib.h> 47 #endif 48 #ifdef HAVE_STRING_H 49 # include <string.h> 50 #endif 51 #ifdef HAVE_UNISTD_H 52 # include <unistd.h> 53 #endif 54 55 #ifndef __restrict 56 # define __restrict /* restrict */ 57 #endif 58 59 /* 60 * char *realpath(const char *path, char *resolved); 61 * 62 * Find the real name of path, by removing all ".", ".." and symlink 63 * components. Returns (resolved) on success, or (NULL) on failure, 64 * in which case the path which caused trouble is left in (resolved). 65 */ 66 char * 67 realpath(const char * __restrict path, char * __restrict resolved) 68 { 69 struct stat sb; 70 int idx = 0, nlnk = 0; 71 const char *q; 72 char *p, wbuf[2][MAXPATHLEN], *fres; 73 size_t len; 74 ssize_t n; 75 76 /* POSIX sez we must test for this */ 77 if (path == NULL) { 78 errno = EINVAL; 79 return NULL; 80 } 81 82 if (resolved == NULL) { 83 fres = resolved = malloc(MAXPATHLEN); 84 if (resolved == NULL) 85 return NULL; 86 } else 87 fres = NULL; 88 89 90 /* 91 * Build real path one by one with paying an attention to ., 92 * .. and symbolic link. 93 */ 94 95 /* 96 * `p' is where we'll put a new component with prepending 97 * a delimiter. 98 */ 99 p = resolved; 100 101 if (*path == '\0') { 102 *p = '\0'; 103 errno = ENOENT; 104 goto out; 105 } 106 107 /* If relative path, start from current working directory. */ 108 if (*path != '/') { 109 /* check for resolved pointer to appease coverity */ 110 if (resolved && getcwd(resolved, MAXPATHLEN) == NULL) { 111 p[0] = '.'; 112 p[1] = '\0'; 113 goto out; 114 } 115 len = strlen(resolved); 116 if (len > 1) 117 p += len; 118 } 119 120 loop: 121 /* Skip any slash. */ 122 while (*path == '/') 123 path++; 124 125 if (*path == '\0') { 126 if (p == resolved) 127 *p++ = '/'; 128 *p = '\0'; 129 return resolved; 130 } 131 132 /* Find the end of this component. */ 133 q = path; 134 do 135 q++; 136 while (*q != '/' && *q != '\0'); 137 138 /* Test . or .. */ 139 if (path[0] == '.') { 140 if (q - path == 1) { 141 path = q; 142 goto loop; 143 } 144 if (path[1] == '.' && q - path == 2) { 145 /* Trim the last component. */ 146 if (p != resolved) 147 while (*--p != '/') 148 continue; 149 path = q; 150 goto loop; 151 } 152 } 153 154 /* Append this component. */ 155 if (p - resolved + 1 + q - path + 1 > MAXPATHLEN) { 156 errno = ENAMETOOLONG; 157 if (p == resolved) 158 *p++ = '/'; 159 *p = '\0'; 160 goto out; 161 } 162 p[0] = '/'; 163 memcpy(&p[1], path, 164 /* LINTED We know q > path. */ 165 q - path); 166 p[1 + q - path] = '\0'; 167 168 /* 169 * If this component is a symlink, toss it and prepend link 170 * target to unresolved path. 171 */ 172 if (lstat(resolved, &sb) == -1) 173 goto out; 174 175 if (S_ISLNK(sb.st_mode)) { 176 if (nlnk++ >= MAXSYMLINKS) { 177 errno = ELOOP; 178 goto out; 179 } 180 n = readlink(resolved, wbuf[idx], sizeof(wbuf[0]) - 1); 181 if (n < 0) 182 goto out; 183 if (n == 0) { 184 errno = ENOENT; 185 goto out; 186 } 187 188 /* Append unresolved path to link target and switch to it. */ 189 if (n + (len = strlen(q)) + 1 > sizeof(wbuf[0])) { 190 errno = ENAMETOOLONG; 191 goto out; 192 } 193 memcpy(&wbuf[idx][n], q, len + 1); 194 path = wbuf[idx]; 195 idx ^= 1; 196 197 /* If absolute symlink, start from root. */ 198 if (*path == '/') 199 p = resolved; 200 goto loop; 201 } 202 if (*q == '/' && !S_ISDIR(sb.st_mode)) { 203 errno = ENOTDIR; 204 goto out; 205 } 206 207 /* Advance both resolved and unresolved path. */ 208 p += 1 + q - path; 209 path = q; 210 goto loop; 211 out: 212 free(fres); 213 return NULL; 214 } 215 #endif 216