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