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], link_tgt[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 ('\0' == next_token[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, link_tgt, sizeof(link_tgt)); 195 if (slen <= 0 || slen >= (ssize_t)sizeof(link_tgt)) { 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 } 203 return (NULL); 204 } 205 link_tgt[slen] = '\0'; 206 if (link_tgt[0] == '/') { 207 resolved[1] = '\0'; 208 resolved_len = 1; 209 } else { 210 /* Strip the last path component. */ 211 q = strrchr(resolved, '/') + 1; 212 *q = '\0'; 213 resolved_len = q - resolved; 214 } 215 216 /* 217 * If there are any path components left, then 218 * append them to link_tgt. The result is placed 219 * in `left'. 220 */ 221 if (p != NULL) { 222 if (link_tgt[slen - 1] != '/') { 223 if (slen + 1 >= (ssize_t)sizeof(link_tgt)) { 224 errno = ENAMETOOLONG; 225 return (NULL); 226 } 227 link_tgt[slen] = '/'; 228 link_tgt[slen + 1] = 0; 229 } 230 left_len = strlcat(link_tgt, left, 231 sizeof(link_tgt)); 232 if (left_len >= sizeof(link_tgt)) { 233 errno = ENAMETOOLONG; 234 return (NULL); 235 } 236 } 237 left_len = strlcpy(left, link_tgt, sizeof(left)); 238 } else if (!S_ISDIR(sb.st_mode) && p != NULL) { 239 errno = ENOTDIR; 240 return (NULL); 241 } 242 } 243 244 /* 245 * Remove trailing slash except when the resolved pathname 246 * is a single "/". 247 */ 248 if (resolved_len > 1 && resolved[resolved_len - 1] == '/') 249 resolved[resolved_len - 1] = '\0'; 250 return (resolved); 251 } 252 253 /* ================================================================== */ 254 #endif /* !defined(SYS_WINNT) && !defined(HAVE_POSIX_REALPATH) */ 255 /* ================================================================== */ 256 257 char * 258 ntp_realpath(const char * path) 259 { 260 # if defined(HAVE_FUNC_POSIX_REALPATH) 261 262 return realpath(path, NULL); 263 264 # else 265 266 char *res = NULL, *m = NULL; 267 if (path == NULL) 268 errno = EINVAL; 269 else if (path[0] == '\0') 270 errno = ENOENT; 271 else if ((m = malloc(NTP_PATH_MAX)) == NULL) 272 errno = ENOMEM; /* MSVCRT malloc does not set this... */ 273 else if ((res = realpath1(path, m)) == NULL) 274 free(m); 275 else 276 res = realloc(res, strlen(res) + 1); 277 return (res); 278 279 # endif 280 } 281