GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: lib/libc/stdlib/realpath.c Lines: 0 95 0.0 %
Date: 2017-11-13 Branches: 0 63 0.0 %

Line Branch Exec Source
1
/*	$OpenBSD: realpath.c,v 1.21 2016/08/28 04:08:59 guenther Exp $ */
2
/*
3
 * Copyright (c) 2003 Constantin S. Svintsoff <kostik@iclub.nsu.ru>
4
 *
5
 * Redistribution and use in source and binary forms, with or without
6
 * modification, are permitted provided that the following conditions
7
 * are met:
8
 * 1. Redistributions of source code must retain the above copyright
9
 *    notice, this list of conditions and the following disclaimer.
10
 * 2. Redistributions in binary form must reproduce the above copyright
11
 *    notice, this list of conditions and the following disclaimer in the
12
 *    documentation and/or other materials provided with the distribution.
13
 * 3. The names of the authors may not be used to endorse or promote
14
 *    products derived from this software without specific prior written
15
 *    permission.
16
 *
17
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27
 * SUCH DAMAGE.
28
 */
29
30
#include <errno.h>
31
#include <stdlib.h>
32
#include <string.h>
33
#include <unistd.h>
34
#include <limits.h>
35
36
/* A slightly modified copy of this file exists in libexec/ld.so */
37
38
/*
39
 * char *realpath(const char *path, char resolved[PATH_MAX]);
40
 *
41
 * Find the real name of path, by removing all ".", ".." and symlink
42
 * components.  Returns (resolved) on success, or (NULL) on failure,
43
 * in which case the path which caused trouble is left in (resolved).
44
 */
45
char *
46
realpath(const char *path, char *resolved)
47
{
48
	char *p, *q, *s;
49
	size_t left_len, resolved_len;
50
	unsigned symlinks;
51
	int serrno, slen, mem_allocated;
52
	char left[PATH_MAX], next_token[PATH_MAX], symlink[PATH_MAX];
53
54
	if (path[0] == '\0') {
55
		errno = ENOENT;
56
		return (NULL);
57
	}
58
59
	serrno = errno;
60
61
	if (resolved == NULL) {
62
		resolved = malloc(PATH_MAX);
63
		if (resolved == NULL)
64
			return (NULL);
65
		mem_allocated = 1;
66
	} else
67
		mem_allocated = 0;
68
69
	symlinks = 0;
70
	if (path[0] == '/') {
71
		resolved[0] = '/';
72
		resolved[1] = '\0';
73
		if (path[1] == '\0')
74
			return (resolved);
75
		resolved_len = 1;
76
		left_len = strlcpy(left, path + 1, sizeof(left));
77
	} else {
78
		if (getcwd(resolved, PATH_MAX) == NULL) {
79
			if (mem_allocated)
80
				free(resolved);
81
			else
82
				strlcpy(resolved, ".", PATH_MAX);
83
			return (NULL);
84
		}
85
		resolved_len = strlen(resolved);
86
		left_len = strlcpy(left, path, sizeof(left));
87
	}
88
	if (left_len >= sizeof(left) || resolved_len >= PATH_MAX) {
89
		errno = ENAMETOOLONG;
90
		goto err;
91
	}
92
93
	/*
94
	 * Iterate over path components in `left'.
95
	 */
96
	while (left_len != 0) {
97
		/*
98
		 * Extract the next path component and adjust `left'
99
		 * and its length.
100
		 */
101
		p = strchr(left, '/');
102
		s = p ? p : left + left_len;
103
		if (s - left >= sizeof(next_token)) {
104
			errno = ENAMETOOLONG;
105
			goto err;
106
		}
107
		memcpy(next_token, left, s - left);
108
		next_token[s - left] = '\0';
109
		left_len -= s - left;
110
		if (p != NULL)
111
			memmove(left, s + 1, left_len + 1);
112
		if (resolved[resolved_len - 1] != '/') {
113
			if (resolved_len + 1 >= PATH_MAX) {
114
				errno = ENAMETOOLONG;
115
				goto err;
116
			}
117
			resolved[resolved_len++] = '/';
118
			resolved[resolved_len] = '\0';
119
		}
120
		if (next_token[0] == '\0')
121
			continue;
122
		else if (strcmp(next_token, ".") == 0)
123
			continue;
124
		else if (strcmp(next_token, "..") == 0) {
125
			/*
126
			 * Strip the last path component except when we have
127
			 * single "/"
128
			 */
129
			if (resolved_len > 1) {
130
				resolved[resolved_len - 1] = '\0';
131
				q = strrchr(resolved, '/') + 1;
132
				*q = '\0';
133
				resolved_len = q - resolved;
134
			}
135
			continue;
136
		}
137
138
		/*
139
		 * Append the next path component and lstat() it. If
140
		 * lstat() fails we still can return successfully if
141
		 * there are no more path components left.
142
		 */
143
		resolved_len = strlcat(resolved, next_token, PATH_MAX);
144
		if (resolved_len >= PATH_MAX) {
145
			errno = ENAMETOOLONG;
146
			goto err;
147
		}
148
		slen = readlink(resolved, symlink, sizeof(symlink) - 1);
149
		if (slen < 0) {
150
			switch (errno) {
151
			case EINVAL:
152
				/* not a symlink, continue to next component */
153
				continue;
154
			case ENOENT:
155
				if (p == NULL) {
156
					errno = serrno;
157
					return (resolved);
158
				}
159
				/* FALLTHROUGH */
160
			default:
161
				goto err;
162
			}
163
		} else {
164
			if (symlinks++ > SYMLOOP_MAX) {
165
				errno = ELOOP;
166
				goto err;
167
			}
168
169
			symlink[slen] = '\0';
170
			if (symlink[0] == '/') {
171
				resolved[1] = 0;
172
				resolved_len = 1;
173
			} else if (resolved_len > 1) {
174
				/* Strip the last path component. */
175
				resolved[resolved_len - 1] = '\0';
176
				q = strrchr(resolved, '/') + 1;
177
				*q = '\0';
178
				resolved_len = q - resolved;
179
			}
180
181
			/*
182
			 * If there are any path components left, then
183
			 * append them to symlink. The result is placed
184
			 * in `left'.
185
			 */
186
			if (p != NULL) {
187
				if (symlink[slen - 1] != '/') {
188
					if (slen + 1 >= sizeof(symlink)) {
189
						errno = ENAMETOOLONG;
190
						goto err;
191
					}
192
					symlink[slen] = '/';
193
					symlink[slen + 1] = 0;
194
				}
195
				left_len = strlcat(symlink, left, sizeof(symlink));
196
				if (left_len >= sizeof(symlink)) {
197
					errno = ENAMETOOLONG;
198
					goto err;
199
				}
200
			}
201
			left_len = strlcpy(left, symlink, sizeof(left));
202
		}
203
	}
204
205
	/*
206
	 * Remove trailing slash except when the resolved pathname
207
	 * is a single "/".
208
	 */
209
	if (resolved_len > 1 && resolved[resolved_len - 1] == '/')
210
		resolved[resolved_len - 1] = '\0';
211
	return (resolved);
212
213
err:
214
	if (mem_allocated)
215
		free(resolved);
216
	return (NULL);
217
}