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