xref: /freebsd-src/contrib/ntp/libntp/ntp_realpath.c (revision a466cc55373fc3cf86837f09da729535b57e69a1)
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