18791Sjkh/* 28791Sjkh * ntp_realpath.c - get real path for a file 38791Sjkh * Juergen Perlinger (perlinger@ntp.org) for the NTP project. 48791Sjkh * Feb 11, 2014 for the NTP project. 58791Sjkh * 68791Sjkh * This is a butchered version of FreeBSD's implementation of 'realpath()', 720233Sjkh * and the following copyright applies: 88791Sjkh *---------------------------------------------------------------------- 98791Sjkh */ 108791Sjkh 118791Sjkh/*- 128791Sjkh * SPDX-License-Identifier: BSD-3-Clause 138791Sjkh * 148791Sjkh * Copyright (c) 2003 Constantin S. Svintsoff <kostik@iclub.nsu.ru> 158791Sjkh * 168881Srgrimes * Redistribution and use in source and binary forms, with or without 178881Srgrimes * modification, are permitted provided that the following conditions 188791Sjkh * are met: 1920231Sjkh * 1. Redistributions of source code must retain the above copyright 208791Sjkh * notice, this list of conditions and the following disclaimer. 218791Sjkh * 2. Redistributions in binary form must reproduce the above copyright 228791Sjkh * notice, this list of conditions and the following disclaimer in the 238791Sjkh * documentation and/or other materials provided with the distribution. 248791Sjkh * 3. The names of the authors may not be used to endorse or promote 258791Sjkh * products derived from this software without specific prior written 268791Sjkh * permission. 278791Sjkh * 288791Sjkh * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 298791Sjkh * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 308791Sjkh * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 318791Sjkh * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 328791Sjkh * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 338791Sjkh * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 348791Sjkh * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 358791Sjkh * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 368791Sjkh * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 378791Sjkh * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 388803Sjkh * SUCH DAMAGE. 398791Sjkh */ 408791Sjkh 4112661Speter#ifdef HAVE_CONFIG_H 428810Sjkh#include <config.h> 4312661Speter#endif 448837Sjkh#include "ntp_stdlib.h" 458791Sjkh 469202Srgrimes/* ================================================================== */ 479202Srgrimes#if defined(SYS_WINNT) 488791Sjkh/* ================================================================== */ 4920233Sjkh 5020233Sjkh#include <stdlib.h> 518791Sjkh 528791Sjkh/* On Windows, we assume 2k for a file path is enough. */ 538791Sjkh#define NTP_PATH_MAX 2048 548791Sjkh 558791Sjkhstatic char * 5620233Sjkhrealpath1(const char *path, char *resolved) 578791Sjkh{ 5812661Speter /* Items in the device name space get passed back AS IS. Everything 598791Sjkh * else is fed through '_fullpath()', which is probably the closest 608791Sjkh * counterpart to what 'realpath()' is expected to do on Windows... 6112661Speter */ 6212661Speter char * retval = NULL; 6312661Speter 6412661Speter if (!strncmp(path, "\\\\.\\", 4)) { 6520233Sjkh if (strlcpy(resolved, path, NTP_PATH_MAX) >= NTP_PATH_MAX) 6620233Sjkh errno = ENAMETOOLONG; 6720233Sjkh else 6820233Sjkh retval = resolved; 6912661Speter } else if ((retval = _fullpath(resolved, path, NTP_PATH_MAX)) == NULL) { 7016330Sjkh errno = ENAMETOOLONG; 7120233Sjkh } 7216330Sjkh return retval; 7316330Sjkh} 748791Sjkh 7516330Sjkh/* ================================================================== */ 7616330Sjkh#elif !defined(HAVE_FUNC_POSIX_REALPATH) 7716330Sjkh/* ================================================================== */ 7816330Sjkh 7916330Sjkh#include <sys/stat.h> 8016330Sjkh#include <errno.h> 818837Sjkh#include <stdlib.h> 8217404Sjkh#include <string.h> 8316330Sjkh#include <unistd.h> 8416330Sjkh#include <fcntl.h> 8516330Sjkh 8616330Sjkh/* The following definitions are to avoid system settings with excessive 8716330Sjkh * values for maxmimum path length and symlink chains/loops. Adjust with 8816330Sjkh * care, if that's ever needed: some buffers are on the stack! 8916330Sjkh */ 9016330Sjkh#define NTP_PATH_MAX 1024 9116330Sjkh#define NTP_MAXSYMLINKS 16 9216330Sjkh 9316330Sjkh/* 9416330Sjkh * Find the real name of path, by removing all ".", ".." and symlink 9516330Sjkh * components. Returns (resolved) on success, or (NULL) on failure, 9616330Sjkh * in which case the path which caused trouble is left in (resolved). 9716330Sjkh */ 9820231Sjkhstatic char * 9920231Sjkhrealpath1(const char *path, char *resolved) 10020233Sjkh{ 10120233Sjkh struct stat sb; 10220231Sjkh char *p, *q; 10316330Sjkh size_t left_len, resolved_len, next_token_len; 10416330Sjkh unsigned symlinks; 10516330Sjkh ssize_t slen; 10616330Sjkh char left[NTP_PATH_MAX], next_token[NTP_PATH_MAX], link_tgt[NTP_PATH_MAX]; 1078837Sjkh 1088791Sjkh symlinks = 0; 1098791Sjkh if (path[0] == '/') { 11012661Speter resolved[0] = '/'; 11112661Speter resolved[1] = '\0'; 11210882Speter if (path[1] == '\0') 11312661Speter return (resolved); 11416330Sjkh resolved_len = 1; 11510882Speter left_len = strlcpy(left, path + 1, sizeof(left)); 1168791Sjkh } else { 11720208Sjkh if (getcwd(resolved, NTP_PATH_MAX) == NULL) { 11816330Sjkh resolved[0] = '.'; 11910882Speter resolved[1] = '\0'; 12012661Speter return (NULL); 12116330Sjkh } 12210882Speter resolved_len = strlen(resolved); 12310882Speter left_len = strlcpy(left, path, sizeof(left)); 1248791Sjkh } 12512661Speter if (left_len >= sizeof(left) || resolved_len >= NTP_PATH_MAX) { 12612661Speter errno = ENAMETOOLONG; 12712661Speter return (NULL); 12812661Speter } 12912661Speter 13012661Speter /* 13112661Speter * Iterate over path components in `left'. 1328791Sjkh */ 13312661Speter while (left_len != 0) { 13420208Sjkh /* 13520208Sjkh * Extract the next path component and adjust `left' 1368791Sjkh * and its length. 1378791Sjkh */ 1388791Sjkh p = strchr(left, '/'); 1398791Sjkh 1408791Sjkh next_token_len = p != NULL ? (size_t)(p - left) : left_len; 1418791Sjkh memcpy(next_token, left, next_token_len); 1428791Sjkh next_token[next_token_len] = '\0'; 1438791Sjkh 1448791Sjkh if (p != NULL) { 14512661Speter left_len -= next_token_len + 1; 1468791Sjkh memmove(left, p + 1, left_len + 1); 1478791Sjkh } else { 14820208Sjkh left[0] = '\0'; 14920208Sjkh left_len = 0; 15016330Sjkh } 15116330Sjkh 1528791Sjkh if (resolved[resolved_len - 1] != '/') { 15312661Speter if (resolved_len + 1 >= NTP_PATH_MAX) { 1548791Sjkh errno = ENAMETOOLONG; 15512661Speter return (NULL); 15612661Speter } 1578791Sjkh resolved[resolved_len++] = '/'; 1588791Sjkh resolved[resolved_len] = '\0'; 15920208Sjkh } 1608791Sjkh if ('\0' == next_token[0]) { 16115355Sjkh /* Handle consequential slashes. */ 1628791Sjkh continue; 16312661Speter } else if (strcmp(next_token, ".") == 0) { 16412661Speter continue; 16512661Speter } else if (strcmp(next_token, "..") == 0) { 1668810Sjkh /* 16712661Speter * Strip the last path component except when we have 1688791Sjkh * single "/" 16920233Sjkh */ 17020233Sjkh if (resolved_len > 1) { 17120233Sjkh resolved[resolved_len - 1] = '\0'; 17220233Sjkh q = strrchr(resolved, '/') + 1; 1739202Srgrimes *q = '\0'; 17420208Sjkh resolved_len = q - resolved; 1758810Sjkh } 1768791Sjkh continue; 1778810Sjkh } 1789202Srgrimes 1798810Sjkh /* 1808810Sjkh * Append the next path component and lstat() it. 18112661Speter */ 1828810Sjkh resolved_len = strlcat(resolved, next_token, NTP_PATH_MAX); 1838810Sjkh if (resolved_len >= NTP_PATH_MAX) { 18415439Sjkh errno = ENAMETOOLONG; 1859202Srgrimes return (NULL); 1868810Sjkh } 1879202Srgrimes if (lstat(resolved, &sb) != 0) 18817005Sjkh return (NULL); 18917005Sjkh if (S_ISLNK(sb.st_mode)) { 19017005Sjkh if (++symlinks > NTP_MAXSYMLINKS) { 1919202Srgrimes errno = ELOOP; 19217404Sjkh return (NULL); 19312661Speter } 19412661Speter slen = readlink(resolved, link_tgt, sizeof(link_tgt)); 1959202Srgrimes if (slen <= 0 || slen >= (ssize_t)sizeof(link_tgt)) { 19612661Speter if (slen < 0) { 19712661Speter /* keep errno from readlink(2) call */ 19812661Speter } else if (slen == 0) { 19912661Speter errno = ENOENT; 20012661Speter } else { 20120233Sjkh errno = ENAMETOOLONG; 2029202Srgrimes } 20320233Sjkh return (NULL); 20412661Speter } 20512661Speter link_tgt[slen] = '\0'; 20620233Sjkh if (link_tgt[0] == '/') { 2079202Srgrimes resolved[1] = '\0'; 2089202Srgrimes resolved_len = 1; 2099202Srgrimes } else { 2109202Srgrimes /* Strip the last path component. */ 2119202Srgrimes q = strrchr(resolved, '/') + 1; 2129202Srgrimes *q = '\0'; 21317007Sjkh resolved_len = q - resolved; 21417007Sjkh } 21517007Sjkh 21617007Sjkh /* 2179202Srgrimes * If there are any path components left, then 2189202Srgrimes * append them to link_tgt. The result is placed 2199202Srgrimes * in `left'. 2209202Srgrimes */ 2219202Srgrimes if (p != NULL) { 2229202Srgrimes if (link_tgt[slen - 1] != '/') { 2238828Sjkh if (slen + 1 >= (ssize_t)sizeof(link_tgt)) { 22417007Sjkh errno = ENAMETOOLONG; 22517007Sjkh return (NULL); 22617007Sjkh } 22717007Sjkh link_tgt[slen] = '/'; 2288828Sjkh link_tgt[slen + 1] = 0; 22912661Speter } 2308828Sjkh left_len = strlcat(link_tgt, left, 2318828Sjkh sizeof(link_tgt)); 23217007Sjkh if (left_len >= sizeof(link_tgt)) { 23317007Sjkh errno = ENAMETOOLONG; 23417007Sjkh return (NULL); 23517007Sjkh } 2368810Sjkh } 2378810Sjkh left_len = strlcpy(left, link_tgt, sizeof(left)); 2389202Srgrimes } else if (!S_ISDIR(sb.st_mode) && p != NULL) { 2398810Sjkh errno = ENOTDIR; 2408810Sjkh return (NULL); 2419202Srgrimes } 2428810Sjkh } 2439202Srgrimes 2448810Sjkh /* 2459202Srgrimes * Remove trailing slash except when the resolved pathname 24617007Sjkh * is a single "/". 2479202Srgrimes */ 2489202Srgrimes if (resolved_len > 1 && resolved[resolved_len - 1] == '/') 2499202Srgrimes resolved[resolved_len - 1] = '\0'; 25015439Sjkh return (resolved); 25115439Sjkh} 25212661Speter 25312661Speter/* ================================================================== */ 25412661Speter#endif /* !defined(SYS_WINNT) && !defined(HAVE_POSIX_REALPATH) */ 25512661Speter/* ================================================================== */ 25612661Speter 25712661Speterchar * 25812661Speterntp_realpath(const char * path) 25912661Speter{ 26012661Speter# if defined(HAVE_FUNC_POSIX_REALPATH) 26112661Speter 26212661Speter return realpath(path, NULL); 26312661Speter 26412661Speter# else 26512661Speter 26612661Speter char *res = NULL, *m = NULL; 26712661Speter if (path == NULL) 26812661Speter errno = EINVAL; 26912661Speter else if (path[0] == '\0') 27012661Speter errno = ENOENT; 27112661Speter else if ((m = malloc(NTP_PATH_MAX)) == NULL) 27212661Speter errno = ENOMEM; /* MSVCRT malloc does not set this... */ 27312661Speter else if ((res = realpath1(path, m)) == NULL) 27412661Speter free(m); 27512661Speter else 2768810Sjkh res = realloc(res, strlen(res) + 1); 2778810Sjkh return (res); 27812661Speter 27917007Sjkh# endif 28012661Speter} 28112661Speter