xref: /freebsd/contrib/bmake/realpath.c (revision 8ddb146abcdf061be9f2c0db7e391697dafad85c)
1 /* $Id: realpath.c,v 1.3 2013/01/25 17:06:09 sjg Exp $ */
2 /* from: $NetBSD: getcwd.c,v 1.53 2012/06/21 23:29:23 enami Exp $	*/
3 
4 /*
5  * Copyright (c) 1989, 1991, 1993, 1995
6  *	The Regents of the University of California.  All rights reserved.
7  *
8  * This code is derived from software contributed to Berkeley by
9  * Jan-Simon Pendry.
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted provided that the following conditions
13  * are met:
14  * 1. Redistributions of source code must retain the above copyright
15  *    notice, this list of conditions and the following disclaimer.
16  * 2. Redistributions in binary form must reproduce the above copyright
17  *    notice, this list of conditions and the following disclaimer in the
18  *    documentation and/or other materials provided with the distribution.
19  * 3. Neither the name of the University nor the names of its contributors
20  *    may be used to endorse or promote products derived from this software
21  *    without specific prior written permission.
22  *
23  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33  * SUCH DAMAGE.
34  */
35 #ifdef HAVE_CONFIG_H
36 # include <config.h>
37 #endif
38 #ifndef HAVE_REALPATH
39 
40 #include <sys/cdefs.h>
41 #include <sys/param.h>
42 #include <sys/stat.h>
43 
44 #include <errno.h>
45 #ifdef HAVE_STDLIB_H
46 # include <stdlib.h>
47 #endif
48 #ifdef HAVE_STRING_H
49 # include <string.h>
50 #endif
51 #ifdef HAVE_UNISTD_H
52 # include <unistd.h>
53 #endif
54 
55 #ifndef __restrict
56 # define __restrict /* restrict */
57 #endif
58 
59 /*
60  * char *realpath(const char *path, char *resolved);
61  *
62  * Find the real name of path, by removing all ".", ".." and symlink
63  * components.  Returns (resolved) on success, or (NULL) on failure,
64  * in which case the path which caused trouble is left in (resolved).
65  */
66 char *
67 realpath(const char * __restrict path, char * __restrict resolved)
68 {
69 	struct stat sb;
70 	int idx = 0, nlnk = 0;
71 	const char *q;
72 	char *p, wbuf[2][MAXPATHLEN], *fres;
73 	size_t len;
74 	ssize_t n;
75 
76 	/* POSIX sez we must test for this */
77 	if (path == NULL) {
78 		errno = EINVAL;
79 		return NULL;
80 	}
81 
82 	if (resolved == NULL) {
83 		fres = resolved = malloc(MAXPATHLEN);
84 		if (resolved == NULL)
85 			return NULL;
86 	} else
87 		fres = NULL;
88 
89 
90 	/*
91 	 * Build real path one by one with paying an attention to .,
92 	 * .. and symbolic link.
93 	 */
94 
95 	/*
96 	 * `p' is where we'll put a new component with prepending
97 	 * a delimiter.
98 	 */
99 	p = resolved;
100 
101 	if (*path == '\0') {
102 		*p = '\0';
103 		errno = ENOENT;
104 		goto out;
105 	}
106 
107 	/* If relative path, start from current working directory. */
108 	if (*path != '/') {
109 		/* check for resolved pointer to appease coverity */
110 		if (resolved && getcwd(resolved, MAXPATHLEN) == NULL) {
111 			p[0] = '.';
112 			p[1] = '\0';
113 			goto out;
114 		}
115 		len = strlen(resolved);
116 		if (len > 1)
117 			p += len;
118 	}
119 
120 loop:
121 	/* Skip any slash. */
122 	while (*path == '/')
123 		path++;
124 
125 	if (*path == '\0') {
126 		if (p == resolved)
127 			*p++ = '/';
128 		*p = '\0';
129 		return resolved;
130 	}
131 
132 	/* Find the end of this component. */
133 	q = path;
134 	do
135 		q++;
136 	while (*q != '/' && *q != '\0');
137 
138 	/* Test . or .. */
139 	if (path[0] == '.') {
140 		if (q - path == 1) {
141 			path = q;
142 			goto loop;
143 		}
144 		if (path[1] == '.' && q - path == 2) {
145 			/* Trim the last component. */
146 			if (p != resolved)
147 				while (*--p != '/')
148 					continue;
149 			path = q;
150 			goto loop;
151 		}
152 	}
153 
154 	/* Append this component. */
155 	if (p - resolved + 1 + q - path + 1 > MAXPATHLEN) {
156 		errno = ENAMETOOLONG;
157 		if (p == resolved)
158 			*p++ = '/';
159 		*p = '\0';
160 		goto out;
161 	}
162 	p[0] = '/';
163 	memcpy(&p[1], path,
164 	    /* LINTED We know q > path. */
165 	    q - path);
166 	p[1 + q - path] = '\0';
167 
168 	/*
169 	 * If this component is a symlink, toss it and prepend link
170 	 * target to unresolved path.
171 	 */
172 	if (lstat(resolved, &sb) == -1)
173 		goto out;
174 
175 	if (S_ISLNK(sb.st_mode)) {
176 		if (nlnk++ >= MAXSYMLINKS) {
177 			errno = ELOOP;
178 			goto out;
179 		}
180 		n = readlink(resolved, wbuf[idx], sizeof(wbuf[0]) - 1);
181 		if (n < 0)
182 			goto out;
183 		if (n == 0) {
184 			errno = ENOENT;
185 			goto out;
186 		}
187 
188 		/* Append unresolved path to link target and switch to it. */
189 		if (n + (len = strlen(q)) + 1 > sizeof(wbuf[0])) {
190 			errno = ENAMETOOLONG;
191 			goto out;
192 		}
193 		memcpy(&wbuf[idx][n], q, len + 1);
194 		path = wbuf[idx];
195 		idx ^= 1;
196 
197 		/* If absolute symlink, start from root. */
198 		if (*path == '/')
199 			p = resolved;
200 		goto loop;
201 	}
202 	if (*q == '/' && !S_ISDIR(sb.st_mode)) {
203 		errno = ENOTDIR;
204 		goto out;
205 	}
206 
207 	/* Advance both resolved and unresolved path. */
208 	p += 1 + q - path;
209 	path = q;
210 	goto loop;
211 out:
212 	free(fres);
213 	return NULL;
214 }
215 #endif
216