1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21 /*
22 * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
24 */
25
26 /*
27 * Copyright (c) 2003 Constantin S. Svintsoff <kostik@iclub.nsu.ru>
28 *
29 * Redistribution and use in source and binary forms, with or without
30 * modification, are permitted provided that the following conditions
31 * are met:
32 * 1. Redistributions of source code must retain the above copyright
33 * notice, this list of conditions and the following disclaimer.
34 * 2. Redistributions in binary form must reproduce the above copyright
35 * notice, this list of conditions and the following disclaimer in the
36 * documentation and/or other materials provided with the distribution.
37 * 3. The names of the authors may not be used to endorse or promote
38 * products derived from this software without specific prior written
39 * permission.
40 *
41 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
42 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
43 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
44 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
45 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
46 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
47 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
48 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
49 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
50 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
51 * SUCH DAMAGE.
52 */
53 /*
54 * http://www.openbsd.org/cgi-bin/cvsweb/src/lib/libc/stdlib/realpath.c
55 * $OpenBSD: realpath.c,v 1.13 2005/08/08 08:05:37 espie Exp $
56 */
57
58 #include <stdio.h>
59 #include <unistd.h>
60 #include <string.h>
61 #include <limits.h>
62 #include <errno.h>
63 #include <sys/types.h>
64 #include <sys/stat.h>
65 #include <sys/param.h>
66
67 /*
68 * char *s_realpath(const char *path, char resolved_path[MAXPATHLEN]);
69 *
70 * Find the real name of path, by removing all ".", ".." and symlink
71 * components. Returns (resolved) on success, or (NULL) on failure,
72 * in which case the path which caused trouble is left in (resolved).
73 *
74 * DEVINFO: For libdevinfo we have added code to special case symlinks into
75 * /devices - the path below that point is known to not point to any
76 * additional symlinks. This knowledge allows us to avoid causing attach.
77 */
78 char *
s_realpath(const char * path,char * resolved)79 s_realpath(const char *path, char *resolved)
80 {
81 struct stat sb;
82 char *p, *q, *s;
83 size_t left_len, resolved_len;
84 unsigned symlinks;
85 int serrno, slen;
86 char left[PATH_MAX], next_token[PATH_MAX], symlink[PATH_MAX];
87
88 serrno = errno;
89 symlinks = 0;
90 if (path[0] == '/') {
91 resolved[0] = '/';
92 resolved[1] = '\0';
93 if (path[1] == '\0')
94 return (resolved);
95 resolved_len = 1;
96 left_len = strlcpy(left, path + 1, sizeof (left));
97 } else {
98 if (getcwd(resolved, PATH_MAX) == NULL) {
99 (void) strlcpy(resolved, ".", PATH_MAX);
100 return (NULL);
101 }
102 resolved_len = strlen(resolved);
103 left_len = strlcpy(left, path, sizeof (left));
104 }
105 if (left_len >= sizeof (left) || resolved_len >= PATH_MAX) {
106 errno = ENAMETOOLONG;
107 return (NULL);
108 }
109
110 /*
111 * Iterate over path components in `left'.
112 */
113 while (left_len != 0) {
114 /*
115 * Extract the next path component and adjust `left'
116 * and its length.
117 */
118 p = strchr(left, '/');
119 s = p ? p : left + left_len;
120 if (s - left >= sizeof (next_token)) {
121 errno = ENAMETOOLONG;
122 return (NULL);
123 }
124 (void) memcpy(next_token, left, s - left);
125 next_token[s - left] = '\0';
126 left_len -= s - left;
127 if (p != NULL)
128 (void) memmove(left, s + 1, left_len + 1);
129 if (resolved[resolved_len - 1] != '/') {
130 if (resolved_len + 1 >= PATH_MAX) {
131 errno = ENAMETOOLONG;
132 return (NULL);
133 }
134 resolved[resolved_len++] = '/';
135 resolved[resolved_len] = '\0';
136 }
137 if (next_token[0] == '\0')
138 continue;
139 else if (strcmp(next_token, ".") == 0)
140 continue;
141 else if (strcmp(next_token, "..") == 0) {
142 /*
143 * Strip the last path component except when we have
144 * single "/"
145 */
146 if (resolved_len > 1) {
147 resolved[resolved_len - 1] = '\0';
148 q = strrchr(resolved, '/') + 1;
149 *q = '\0';
150 resolved_len = q - resolved;
151 }
152 continue;
153 }
154
155 /*
156 * Append the next path component and lstat() it. If
157 * lstat() fails we still can return successfully if
158 * there are no more path components left.
159 */
160 resolved_len = strlcat(resolved, next_token, PATH_MAX);
161 if (resolved_len >= PATH_MAX) {
162 errno = ENAMETOOLONG;
163 return (NULL);
164 }
165
166 /*
167 * DEVINFO: Check if link points into /devices and resolve
168 * without causing attach if that is the case - there are no
169 * further symlinks in /devices.
170 */
171 if (strcmp(resolved, "/devices") == 0) {
172 resolved[resolved_len] = '/';
173 resolved_len = strlcat(resolved, left, sizeof (left));
174 left_len = 0;
175 continue;
176 }
177
178 if (lstat(resolved, &sb) != 0) {
179 if (errno == ENOENT && p == NULL) {
180 errno = serrno;
181 return (resolved);
182 }
183 return (NULL);
184 }
185
186 if (S_ISLNK(sb.st_mode)) {
187 if (symlinks++ > MAXSYMLINKS) {
188 errno = ELOOP;
189 return (NULL);
190 }
191 slen = readlink(resolved, symlink,
192 sizeof (symlink) - 1);
193 if (slen < 0)
194 return (NULL);
195 symlink[slen] = '\0';
196
197 if (symlink[0] == '/') {
198 resolved[1] = 0;
199 resolved_len = 1;
200 } else if (resolved_len > 1) {
201 /* Strip the last path component. */
202 resolved[resolved_len - 1] = '\0';
203 q = strrchr(resolved, '/') + 1;
204 *q = '\0';
205 resolved_len = q - resolved;
206 }
207
208 /*
209 * If there are any path components left, then
210 * append them to symlink. The result is placed
211 * in `left'.
212 */
213 if (p != NULL) {
214 if (symlink[slen - 1] != '/') {
215 if (slen + 1 >= sizeof (symlink)) {
216 errno = ENAMETOOLONG;
217 return (NULL);
218 }
219 symlink[slen] = '/';
220 symlink[slen + 1] = 0;
221 }
222 left_len = strlcat(symlink, left,
223 sizeof (left));
224 if (left_len >= sizeof (left)) {
225 errno = ENAMETOOLONG;
226 return (NULL);
227 }
228 }
229 left_len = strlcpy(left, symlink, sizeof (left));
230 }
231 }
232
233 /*
234 * Remove trailing slash except when the resolved pathname
235 * is a single "/".
236 */
237 if (resolved_len > 1 && resolved[resolved_len - 1] == '/')
238 resolved[resolved_len - 1] = '\0';
239 return (resolved);
240 }
241