123833Speter/*
2112726Sfjoe * Copyright (c) 2003 Constantin S. Svintsoff <kostik@iclub.nsu.ru>
323833Speter *
423833Speter * Redistribution and use in source and binary forms, with or without
523833Speter * modification, are permitted provided that the following conditions
623833Speter * are met:
723833Speter * 1. Redistributions of source code must retain the above copyright
823833Speter *    notice, this list of conditions and the following disclaimer.
923833Speter * 2. Redistributions in binary form must reproduce the above copyright
1023833Speter *    notice, this list of conditions and the following disclaimer in the
1123833Speter *    documentation and/or other materials provided with the distribution.
12112726Sfjoe * 3. The names of the authors may not be used to endorse or promote
13112726Sfjoe *    products derived from this software without specific prior written
14112726Sfjoe *    permission.
1523833Speter *
16112726Sfjoe * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1723833Speter * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1823833Speter * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19112726Sfjoe * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
2023833Speter * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2123833Speter * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2223833Speter * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2323833Speter * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2423833Speter * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2523833Speter * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2623833Speter * SUCH DAMAGE.
2723833Speter */
2823833Speter
2923833Speter#if defined(LIBC_SCCS) && !defined(lint)
3023833Speterstatic char sccsid[] = "@(#)realpath.c	8.1 (Berkeley) 2/16/94";
3123833Speter#endif /* LIBC_SCCS and not lint */
3292986Sobrien#include <sys/cdefs.h>
3392986Sobrien__FBSDID("$FreeBSD$");
3423833Speter
3571579Sdeischen#include "namespace.h"
3623833Speter#include <sys/param.h>
3723833Speter#include <sys/stat.h>
3823833Speter
3923833Speter#include <errno.h>
40112823Sfjoe#include <stdlib.h>
4123833Speter#include <string.h>
4223833Speter#include <unistd.h>
4371579Sdeischen#include "un-namespace.h"
4423833Speter
4523833Speter/*
4623833Speter * Find the real name of path, by removing all ".", ".." and symlink
4723833Speter * components.  Returns (resolved) on success, or (NULL) on failure,
4823833Speter * in which case the path which caused trouble is left in (resolved).
4923833Speter */
5023833Speterchar *
51206893Skibrealpath(const char * __restrict path, char * __restrict resolved)
5223833Speter{
53112823Sfjoe	struct stat sb;
54112823Sfjoe	char *p, *q, *s;
55112726Sfjoe	size_t left_len, resolved_len;
56112823Sfjoe	unsigned symlinks;
57236618Sache	int m, slen;
58112823Sfjoe	char left[PATH_MAX], next_token[PATH_MAX], symlink[PATH_MAX];
59112726Sfjoe
60206893Skib	if (path == NULL) {
61206893Skib		errno = EINVAL;
62206893Skib		return (NULL);
63206893Skib	}
64206893Skib	if (path[0] == '\0') {
65206893Skib		errno = ENOENT;
66206893Skib		return (NULL);
67206893Skib	}
68206893Skib	if (resolved == NULL) {
69206893Skib		resolved = malloc(PATH_MAX);
70206893Skib		if (resolved == NULL)
71206893Skib			return (NULL);
72206893Skib		m = 1;
73206893Skib	} else
74206893Skib		m = 0;
75112823Sfjoe	symlinks = 0;
76112726Sfjoe	if (path[0] == '/') {
77112823Sfjoe		resolved[0] = '/';
78112823Sfjoe		resolved[1] = '\0';
79112726Sfjoe		if (path[1] == '\0')
80112823Sfjoe			return (resolved);
81112726Sfjoe		resolved_len = 1;
82114443Snectar		left_len = strlcpy(left, path + 1, sizeof(left));
83112726Sfjoe	} else {
84112823Sfjoe		if (getcwd(resolved, PATH_MAX) == NULL) {
85236618Sache			if (m)
86206893Skib				free(resolved);
87236618Sache			else {
88217144Skib				resolved[0] = '.';
89217144Skib				resolved[1] = '\0';
90217144Skib			}
91112823Sfjoe			return (NULL);
92112726Sfjoe		}
93112823Sfjoe		resolved_len = strlen(resolved);
94114443Snectar		left_len = strlcpy(left, path, sizeof(left));
9523833Speter	}
96112823Sfjoe	if (left_len >= sizeof(left) || resolved_len >= PATH_MAX) {
97206893Skib		if (m)
98206893Skib			free(resolved);
99112726Sfjoe		errno = ENAMETOOLONG;
100112823Sfjoe		return (NULL);
101112726Sfjoe	}
10223833Speter
103112823Sfjoe	/*
104112823Sfjoe	 * Iterate over path components in `left'.
105112823Sfjoe	 */
106112823Sfjoe	while (left_len != 0) {
107112823Sfjoe		/*
108112823Sfjoe		 * Extract the next path component and adjust `left'
109112823Sfjoe		 * and its length.
110112823Sfjoe		 */
111112823Sfjoe		p = strchr(left, '/');
112112823Sfjoe		s = p ? p : left + left_len;
113112823Sfjoe		if (s - left >= sizeof(next_token)) {
114206893Skib			if (m)
115206893Skib				free(resolved);
116112823Sfjoe			errno = ENAMETOOLONG;
117112823Sfjoe			return (NULL);
118112823Sfjoe		}
119112823Sfjoe		memcpy(next_token, left, s - left);
120112823Sfjoe		next_token[s - left] = '\0';
121112726Sfjoe		left_len -= s - left;
122112726Sfjoe		if (p != NULL)
123112726Sfjoe			memmove(left, s + 1, left_len + 1);
124112823Sfjoe		if (resolved[resolved_len - 1] != '/') {
125112743Sfjoe			if (resolved_len + 1 >= PATH_MAX) {
126206893Skib				if (m)
127206893Skib					free(resolved);
128112726Sfjoe				errno = ENAMETOOLONG;
129112823Sfjoe				return (NULL);
130112726Sfjoe			}
131112823Sfjoe			resolved[resolved_len++] = '/';
132112823Sfjoe			resolved[resolved_len] = '\0';
13323833Speter		}
134235266Skib		if (next_token[0] == '\0') {
135267161Sjilles			/* Handle consequential slashes. */
136112726Sfjoe			continue;
137235266Skib		}
138112823Sfjoe		else if (strcmp(next_token, ".") == 0)
139112726Sfjoe			continue;
140112823Sfjoe		else if (strcmp(next_token, "..") == 0) {
141112823Sfjoe			/*
142112823Sfjoe			 * Strip the last path component except when we have
143112823Sfjoe			 * single "/"
144112823Sfjoe			 */
145112726Sfjoe			if (resolved_len > 1) {
146112823Sfjoe				resolved[resolved_len - 1] = '\0';
147115362Sfjoe				q = strrchr(resolved, '/') + 1;
148112726Sfjoe				*q = '\0';
149112823Sfjoe				resolved_len = q - resolved;
15043937Sache			}
151112726Sfjoe			continue;
15223833Speter		}
153112726Sfjoe
154112823Sfjoe		/*
155235266Skib		 * Append the next path component and lstat() it.
156112823Sfjoe		 */
157114443Snectar		resolved_len = strlcat(resolved, next_token, PATH_MAX);
158112743Sfjoe		if (resolved_len >= PATH_MAX) {
159206893Skib			if (m)
160206893Skib				free(resolved);
161112726Sfjoe			errno = ENAMETOOLONG;
162112823Sfjoe			return (NULL);
16323833Speter		}
164112823Sfjoe		if (lstat(resolved, &sb) != 0) {
165236618Sache			if (m)
166206893Skib				free(resolved);
167112823Sfjoe			return (NULL);
168112726Sfjoe		}
169112823Sfjoe		if (S_ISLNK(sb.st_mode)) {
170112823Sfjoe			if (symlinks++ > MAXSYMLINKS) {
171206893Skib				if (m)
172206893Skib					free(resolved);
173112726Sfjoe				errno = ELOOP;
174112823Sfjoe				return (NULL);
175112726Sfjoe			}
176112823Sfjoe			slen = readlink(resolved, symlink, sizeof(symlink) - 1);
177206893Skib			if (slen < 0) {
178236618Sache				if (m)
179206893Skib					free(resolved);
180112823Sfjoe				return (NULL);
181206893Skib			}
182112726Sfjoe			symlink[slen] = '\0';
183112726Sfjoe			if (symlink[0] == '/') {
184112823Sfjoe				resolved[1] = 0;
185112726Sfjoe				resolved_len = 1;
186112726Sfjoe			} else if (resolved_len > 1) {
187112823Sfjoe				/* Strip the last path component. */
188112823Sfjoe				resolved[resolved_len - 1] = '\0';
189115362Sfjoe				q = strrchr(resolved, '/') + 1;
190112726Sfjoe				*q = '\0';
191112823Sfjoe				resolved_len = q - resolved;
192112726Sfjoe			}
193112726Sfjoe
194112823Sfjoe			/*
195112823Sfjoe			 * If there are any path components left, then
196112823Sfjoe			 * append them to symlink. The result is placed
197112823Sfjoe			 * in `left'.
198112823Sfjoe			 */
199112820Sfjoe			if (p != NULL) {
200112820Sfjoe				if (symlink[slen - 1] != '/') {
201112823Sfjoe					if (slen + 1 >= sizeof(symlink)) {
202206893Skib						if (m)
203206893Skib							free(resolved);
204112820Sfjoe						errno = ENAMETOOLONG;
205112823Sfjoe						return (NULL);
206112820Sfjoe					}
207112820Sfjoe					symlink[slen] = '/';
208112820Sfjoe					symlink[slen + 1] = 0;
209112820Sfjoe				}
210227090Sed				left_len = strlcat(symlink, left,
211227090Sed				    sizeof(symlink));
212112823Sfjoe				if (left_len >= sizeof(left)) {
213206893Skib					if (m)
214206893Skib						free(resolved);
215112726Sfjoe					errno = ENAMETOOLONG;
216112823Sfjoe					return (NULL);
217112726Sfjoe				}
218112726Sfjoe			}
219114443Snectar			left_len = strlcpy(left, symlink, sizeof(left));
220267161Sjilles		} else if (!S_ISDIR(sb.st_mode) && p != NULL) {
221267161Sjilles			if (m)
222267161Sjilles				free(resolved);
223267161Sjilles			errno = ENOTDIR;
224267161Sjilles			return (NULL);
22523833Speter		}
22623833Speter	}
22723833Speter
228112823Sfjoe	/*
229112823Sfjoe	 * Remove trailing slash except when the resolved pathname
230112823Sfjoe	 * is a single "/".
231112823Sfjoe	 */
232112823Sfjoe	if (resolved_len > 1 && resolved[resolved_len - 1] == '/')
233112823Sfjoe		resolved[resolved_len - 1] = '\0';
234112823Sfjoe	return (resolved);
23523833Speter}
236