1 /* 2 * ntp_realpath.c - get real path for a file 3 * Juergen Perlinger (perlinger@ntp.org) for the NTP project. 4 * Feb 11, 2014 for the NTP project. 5 * 6 * This is a butchered version of FreeBSD's implementation of 'realpath()', 7 * and the following copyright applies: 8 *---------------------------------------------------------------------- 9 */ 10 11 /*- 12 * SPDX-License-Identifier: BSD-3-Clause 13 * 14 * Copyright (c) 2003 Constantin S. Svintsoff <kostik@iclub.nsu.ru> 15 * 16 * Redistribution and use in source and binary forms, with or without 17 * modification, are permitted provided that the following conditions 18 * are met: 19 * 1. Redistributions of source code must retain the above copyright 20 * notice, this list of conditions and the following disclaimer. 21 * 2. Redistributions in binary form must reproduce the above copyright 22 * notice, this list of conditions and the following disclaimer in the 23 * documentation and/or other materials provided with the distribution. 24 * 3. The names of the authors may not be used to endorse or promote 25 * products derived from this software without specific prior written 26 * permission. 27 * 28 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 29 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 30 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 31 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 32 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 33 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 34 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 35 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 36 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 37 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 38 * SUCH DAMAGE. 39 */ 40 41 #ifdef HAVE_CONFIG_H 42 #include <config.h> 43 #endif 44 #include "ntp_stdlib.h" 45 46 /* ================================================================== */ 47 #if defined(SYS_WINNT) 48 /* ================================================================== */ 49 50 #include <stdlib.h> 51 52 /* On Windows, we assume 2k for a file path is enough. */ 53 #define NTP_PATH_MAX 2048 54 55 static char * 56 realpath1(const char *path, char *resolved) 57 { 58 /* Items in the device name space get passed back AS IS. Everything 59 * else is fed through '_fullpath()', which is probably the closest 60 * counterpart to what 'realpath()' is expected to do on Windows... 61 */ 62 char * retval = NULL; 63 64 if (!strncmp(path, "\\\\.\\", 4)) { 65 if (strlcpy(resolved, path, NTP_PATH_MAX) >= NTP_PATH_MAX) 66 errno = ENAMETOOLONG; 67 else 68 retval = resolved; 69 } else if ((retval = _fullpath(resolved, path, NTP_PATH_MAX)) == NULL) { 70 errno = ENAMETOOLONG; 71 } 72 return retval; 73 } 74 75 /* ================================================================== */ 76 #elif !defined(HAVE_FUNC_POSIX_REALPATH) 77 /* ================================================================== */ 78 79 #include <sys/stat.h> 80 #include <errno.h> 81 #include <stdlib.h> 82 #include <string.h> 83 #include <unistd.h> 84 #include <fcntl.h> 85 86 /* The following definitions are to avoid system settings with excessive 87 * values for maxmimum path length and symlink chains/loops. Adjust with 88 * care, if that's ever needed: some buffers are on the stack! 89 */ 90 #define NTP_PATH_MAX 1024 91 #define NTP_MAXSYMLINKS 16 92 93 /* 94 * Find the real name of path, by removing all ".", ".." and symlink 95 * components. Returns (resolved) on success, or (NULL) on failure, 96 * in which case the path which caused trouble is left in (resolved). 97 */ 98 static char * 99 realpath1(const char *path, char *resolved) 100 { 101 struct stat sb; 102 char *p, *q; 103 size_t left_len, resolved_len, next_token_len; 104 unsigned symlinks; 105 ssize_t slen; 106 char left[NTP_PATH_MAX], next_token[NTP_PATH_MAX], symlink[NTP_PATH_MAX]; 107 108 symlinks = 0; 109 if (path[0] == '/') { 110 resolved[0] = '/'; 111 resolved[1] = '\0'; 112 if (path[1] == '\0') 113 return (resolved); 114 resolved_len = 1; 115 left_len = strlcpy(left, path + 1, sizeof(left)); 116 } else { 117 if (getcwd(resolved, NTP_PATH_MAX) == NULL) { 118 resolved[0] = '.'; 119 resolved[1] = '\0'; 120 return (NULL); 121 } 122 resolved_len = strlen(resolved); 123 left_len = strlcpy(left, path, sizeof(left)); 124 } 125 if (left_len >= sizeof(left) || resolved_len >= NTP_PATH_MAX) { 126 errno = ENAMETOOLONG; 127 return (NULL); 128 } 129 130 /* 131 * Iterate over path components in `left'. 132 */ 133 while (left_len != 0) { 134 /* 135 * Extract the next path component and adjust `left' 136 * and its length. 137 */ 138 p = strchr(left, '/'); 139 140 next_token_len = p != NULL ? (size_t)(p - left) : left_len; 141 memcpy(next_token, left, next_token_len); 142 next_token[next_token_len] = '\0'; 143 144 if (p != NULL) { 145 left_len -= next_token_len + 1; 146 memmove(left, p + 1, left_len + 1); 147 } else { 148 left[0] = '\0'; 149 left_len = 0; 150 } 151 152 if (resolved[resolved_len - 1] != '/') { 153 if (resolved_len + 1 >= NTP_PATH_MAX) { 154 errno = ENAMETOOLONG; 155 return (NULL); 156 } 157 resolved[resolved_len++] = '/'; 158 resolved[resolved_len] = '\0'; 159 } 160 if (next_token[0] == '\0') { 161 /* Handle consequential slashes. */ 162 continue; 163 } else if (strcmp(next_token, ".") == 0) { 164 continue; 165 } else if (strcmp(next_token, "..") == 0) { 166 /* 167 * Strip the last path component except when we have 168 * single "/" 169 */ 170 if (resolved_len > 1) { 171 resolved[resolved_len - 1] = '\0'; 172 q = strrchr(resolved, '/') + 1; 173 *q = '\0'; 174 resolved_len = q - resolved; 175 } 176 continue; 177 } 178 179 /* 180 * Append the next path component and lstat() it. 181 */ 182 resolved_len = strlcat(resolved, next_token, NTP_PATH_MAX); 183 if (resolved_len >= NTP_PATH_MAX) { 184 errno = ENAMETOOLONG; 185 return (NULL); 186 } 187 if (lstat(resolved, &sb) != 0) 188 return (NULL); 189 if (S_ISLNK(sb.st_mode)) { 190 if (symlinks++ > NTP_MAXSYMLINKS) { 191 errno = ELOOP; 192 return (NULL); 193 } 194 slen = readlink(resolved, symlink, sizeof(symlink)); 195 if (slen <= 0 || slen >= (ssize_t)sizeof(symlink)) { 196 if (slen < 0) 197 ; /* keep errno from readlink(2) call */ 198 else if (slen == 0) 199 errno = ENOENT; 200 else 201 errno = ENAMETOOLONG; 202 return (NULL); 203 } 204 symlink[slen] = '\0'; 205 if (symlink[0] == '/') { 206 resolved[1] = 0; 207 resolved_len = 1; 208 } else { 209 /* Strip the last path component. */ 210 q = strrchr(resolved, '/') + 1; 211 *q = '\0'; 212 resolved_len = q - resolved; 213 } 214 215 /* 216 * If there are any path components left, then 217 * append them to symlink. The result is placed 218 * in `left'. 219 */ 220 if (p != NULL) { 221 if (symlink[slen - 1] != '/') { 222 if (slen + 1 >= (ssize_t)sizeof(symlink)) { 223 errno = ENAMETOOLONG; 224 return (NULL); 225 } 226 symlink[slen] = '/'; 227 symlink[slen + 1] = 0; 228 } 229 left_len = strlcat(symlink, left, 230 sizeof(symlink)); 231 if (left_len >= sizeof(symlink)) { 232 errno = ENAMETOOLONG; 233 return (NULL); 234 } 235 } 236 left_len = strlcpy(left, symlink, sizeof(left)); 237 } else if (!S_ISDIR(sb.st_mode) && p != NULL) { 238 errno = ENOTDIR; 239 return (NULL); 240 } 241 } 242 243 /* 244 * Remove trailing slash except when the resolved pathname 245 * is a single "/". 246 */ 247 if (resolved_len > 1 && resolved[resolved_len - 1] == '/') 248 resolved[resolved_len - 1] = '\0'; 249 return (resolved); 250 } 251 252 /* ================================================================== */ 253 #endif /* !defined(SYS_WINNT) && !defined(HAVE_POSIX_REALPATH) */ 254 /* ================================================================== */ 255 256 char * 257 ntp_realpath(const char * path) 258 { 259 # if defined(HAVE_FUNC_POSIX_REALPATH) 260 261 return realpath(path, NULL); 262 263 # else 264 265 char *res = NULL, *m = NULL; 266 if (path == NULL) 267 errno = EINVAL; 268 else if (path[0] == '\0') 269 errno = ENOENT; 270 else if ((m = malloc(NTP_PATH_MAX)) == NULL) 271 errno = ENOMEM; /* MSVCRT malloc does not set this... */ 272 else if ((res = realpath1(path, m)) == NULL) 273 free(m); 274 else 275 res = realloc(res, strlen(res) + 1); 276 return (res); 277 278 # endif 279 } 280