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