realpath.c revision 112820
1/*
2 * Copyright (c) 2003 Constantin S. Svintsoff <kostik@iclub.nsu.ru>
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 *    notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 *    notice, this list of conditions and the following disclaimer in the
11 *    documentation and/or other materials provided with the distribution.
12 * 3. The names of the authors may not be used to endorse or promote
13 *    products derived from this software without specific prior written
14 *    permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 */
28
29#if defined(LIBC_SCCS) && !defined(lint)
30static char sccsid[] = "@(#)realpath.c	8.1 (Berkeley) 2/16/94";
31#endif /* LIBC_SCCS and not lint */
32#include <sys/cdefs.h>
33__FBSDID("$FreeBSD: head/lib/libc/stdlib/realpath.c 112820 2003-03-29 21:34:13Z fjoe $");
34
35#include "namespace.h"
36#include <sys/param.h>
37#include <sys/stat.h>
38
39#include <errno.h>
40#include <string.h>
41#include <unistd.h>
42#include "un-namespace.h"
43
44/*
45 * char *realpath(const char *path, char resolved_path[PATH_MAX]);
46 *
47 * Find the real name of path, by removing all ".", ".." and symlink
48 * components.  Returns (resolved) on success, or (NULL) on failure,
49 * in which case the path which caused trouble is left in (resolved).
50 */
51char *
52realpath(const char *path, char resolved_path[PATH_MAX])
53{
54	unsigned num_symlinks = 0;
55	int saved_errno = errno;
56
57	char left[PATH_MAX];
58	size_t left_len, resolved_len;
59
60	if (path[0] == '/') {
61		resolved_path[0] = '/';
62		resolved_path[1] = '\0';
63		if (path[1] == '\0')
64			return resolved_path;
65		resolved_len = 1;
66		left_len = strlcpy(left, path + 1, PATH_MAX);
67	} else {
68		if (getcwd(resolved_path, PATH_MAX) == NULL) {
69			strlcpy(resolved_path, ".", PATH_MAX);
70			return NULL;
71		}
72		resolved_len = strlen(resolved_path);
73		left_len = strlcpy(left, path, PATH_MAX);
74	}
75	if (left_len >= PATH_MAX || resolved_len >= PATH_MAX) {
76		errno = ENAMETOOLONG;
77		return NULL;
78	}
79
80	while (left_len > 0) {
81		struct stat st;
82		char next_token[PATH_MAX];
83		char *p;
84		char *s = (p = strchr(left, '/')) ? p : left + left_len;
85
86		memmove(next_token, left, s - left);
87		left_len -= s - left;
88		if (p != NULL)
89			memmove(left, s + 1, left_len + 1);
90
91		next_token[s - left] = '\0';
92		if (resolved_path[resolved_len - 1] != '/') {
93			if (resolved_len + 1 >= PATH_MAX) {
94				errno = ENAMETOOLONG;
95				return NULL;
96			}
97
98			resolved_path[resolved_len++] = '/';
99			resolved_path[resolved_len] = '\0';
100		}
101
102		if (next_token[0] == '\0')
103			continue;
104		else if (!strcmp(next_token, "."))
105			continue;
106		else if (!strcmp(next_token, "..")) {
107			if (resolved_len > 1) {
108				char *q;
109
110				/* trailing slash */
111				resolved_path[resolved_len - 1] = '\0';
112
113				q = strrchr(resolved_path, '/');
114				*q = '\0';
115				resolved_len = q - resolved_path;
116			}
117			continue;
118		}
119
120		/* filename */
121		resolved_len = strlcat(resolved_path, next_token, PATH_MAX);
122		if (resolved_len >= PATH_MAX) {
123			errno = ENAMETOOLONG;
124			return NULL;
125		}
126
127		if (lstat(resolved_path, &st) < 0) {
128			if (errno == ENOENT && p == NULL) {
129				errno = saved_errno;
130				return resolved_path;
131			}
132
133			return NULL;
134		}
135
136		if ((st.st_mode & S_IFLNK) == S_IFLNK) {
137			char symlink[PATH_MAX];
138			int slen;
139
140			if (num_symlinks++ > MAXSYMLINKS) {
141				errno = ELOOP;
142				return NULL;
143			}
144			slen = readlink(resolved_path, symlink, PATH_MAX - 1);
145			if (slen < 0)
146				return NULL;
147			symlink[slen] = '\0';
148
149			if (symlink[0] == '/') {
150				/* absolute link */
151				resolved_path[1] = 0;
152				resolved_len = 1;
153			} else if (resolved_len > 1) {
154				char *q;
155
156				/* trailing slash */
157				resolved_path[resolved_len - 1] = '\0';
158
159				q = strrchr(resolved_path, '/');
160				*q = '\0';
161				resolved_len = q - resolved_path;
162			}
163
164			if (p != NULL) {
165				if (symlink[slen - 1] != '/') {
166					if (slen + 1 >= PATH_MAX) {
167						errno = ENAMETOOLONG;
168						return NULL;
169					}
170					symlink[slen] = '/';
171					symlink[slen + 1] = 0;
172				}
173				left_len = strlcat(symlink, left, PATH_MAX);
174				if (left_len >= PATH_MAX) {
175					errno = ENAMETOOLONG;
176					return NULL;
177				}
178			}
179			left_len = strlcpy(left, symlink, PATH_MAX);
180		}
181	}
182
183	if (resolved_len > 1 && resolved_path[resolved_len - 1] == '/')
184		resolved_path[resolved_len - 1] = '\0';
185
186	return resolved_path;
187}
188