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