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