xref: /freebsd-src/contrib/bmake/realpath.c (revision cfe30d02adda7c3b5c76156ac52d50d8cab325d9)
1*3cbdda60SSimon J. Gerraty /* $Id: realpath.c,v 1.3 2013/01/25 17:06:09 sjg Exp $ */
2*3cbdda60SSimon J. Gerraty /* from: $NetBSD: getcwd.c,v 1.53 2012/06/21 23:29:23 enami Exp $	*/
33955d011SMarcel Moolenaar 
43955d011SMarcel Moolenaar /*
53955d011SMarcel Moolenaar  * Copyright (c) 1989, 1991, 1993, 1995
63955d011SMarcel Moolenaar  *	The Regents of the University of California.  All rights reserved.
73955d011SMarcel Moolenaar  *
83955d011SMarcel Moolenaar  * This code is derived from software contributed to Berkeley by
93955d011SMarcel Moolenaar  * Jan-Simon Pendry.
103955d011SMarcel Moolenaar  *
113955d011SMarcel Moolenaar  * Redistribution and use in source and binary forms, with or without
123955d011SMarcel Moolenaar  * modification, are permitted provided that the following conditions
133955d011SMarcel Moolenaar  * are met:
143955d011SMarcel Moolenaar  * 1. Redistributions of source code must retain the above copyright
153955d011SMarcel Moolenaar  *    notice, this list of conditions and the following disclaimer.
163955d011SMarcel Moolenaar  * 2. Redistributions in binary form must reproduce the above copyright
173955d011SMarcel Moolenaar  *    notice, this list of conditions and the following disclaimer in the
183955d011SMarcel Moolenaar  *    documentation and/or other materials provided with the distribution.
193955d011SMarcel Moolenaar  * 3. Neither the name of the University nor the names of its contributors
203955d011SMarcel Moolenaar  *    may be used to endorse or promote products derived from this software
213955d011SMarcel Moolenaar  *    without specific prior written permission.
223955d011SMarcel Moolenaar  *
233955d011SMarcel Moolenaar  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
243955d011SMarcel Moolenaar  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
253955d011SMarcel Moolenaar  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
263955d011SMarcel Moolenaar  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
273955d011SMarcel Moolenaar  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
283955d011SMarcel Moolenaar  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
293955d011SMarcel Moolenaar  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
303955d011SMarcel Moolenaar  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
313955d011SMarcel Moolenaar  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
323955d011SMarcel Moolenaar  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
333955d011SMarcel Moolenaar  * SUCH DAMAGE.
343955d011SMarcel Moolenaar  */
353955d011SMarcel Moolenaar #ifdef HAVE_CONFIG_H
363955d011SMarcel Moolenaar # include <config.h>
373955d011SMarcel Moolenaar #endif
383955d011SMarcel Moolenaar #ifndef HAVE_REALPATH
393955d011SMarcel Moolenaar 
403955d011SMarcel Moolenaar #include <sys/cdefs.h>
413955d011SMarcel Moolenaar #include <sys/param.h>
423955d011SMarcel Moolenaar #include <sys/stat.h>
433955d011SMarcel Moolenaar 
443955d011SMarcel Moolenaar #include <errno.h>
453955d011SMarcel Moolenaar #ifdef HAVE_STDLIB_H
463955d011SMarcel Moolenaar # include <stdlib.h>
473955d011SMarcel Moolenaar #endif
483955d011SMarcel Moolenaar #ifdef HAVE_STRING_H
493955d011SMarcel Moolenaar # include <string.h>
503955d011SMarcel Moolenaar #endif
513955d011SMarcel Moolenaar #ifdef HAVE_UNISTD_H
523955d011SMarcel Moolenaar # include <unistd.h>
533955d011SMarcel Moolenaar #endif
543955d011SMarcel Moolenaar 
55*3cbdda60SSimon J. Gerraty #ifndef __restrict
56*3cbdda60SSimon J. Gerraty # define __restrict /* restrict */
57*3cbdda60SSimon J. Gerraty #endif
58*3cbdda60SSimon J. Gerraty 
593955d011SMarcel Moolenaar /*
60*3cbdda60SSimon J. Gerraty  * char *realpath(const char *path, char *resolved);
613955d011SMarcel Moolenaar  *
623955d011SMarcel Moolenaar  * Find the real name of path, by removing all ".", ".." and symlink
633955d011SMarcel Moolenaar  * components.  Returns (resolved) on success, or (NULL) on failure,
643955d011SMarcel Moolenaar  * in which case the path which caused trouble is left in (resolved).
653955d011SMarcel Moolenaar  */
663955d011SMarcel Moolenaar char *
realpath(const char * __restrict path,char * __restrict resolved)67*3cbdda60SSimon J. Gerraty realpath(const char * __restrict path, char * __restrict resolved)
683955d011SMarcel Moolenaar {
693955d011SMarcel Moolenaar 	struct stat sb;
70*3cbdda60SSimon J. Gerraty 	int idx = 0, nlnk = 0;
713955d011SMarcel Moolenaar 	const char *q;
72*3cbdda60SSimon J. Gerraty 	char *p, wbuf[2][MAXPATHLEN], *fres;
733955d011SMarcel Moolenaar 	size_t len;
74*3cbdda60SSimon J. Gerraty 	ssize_t n;
753955d011SMarcel Moolenaar 
76*3cbdda60SSimon J. Gerraty 	/* POSIX sez we must test for this */
77*3cbdda60SSimon J. Gerraty 	if (path == NULL) {
78*3cbdda60SSimon J. Gerraty 		errno = EINVAL;
79*3cbdda60SSimon J. Gerraty 		return NULL;
80*3cbdda60SSimon J. Gerraty 	}
81*3cbdda60SSimon J. Gerraty 
82*3cbdda60SSimon J. Gerraty 	if (resolved == NULL) {
83*3cbdda60SSimon J. Gerraty 		fres = resolved = malloc(MAXPATHLEN);
84*3cbdda60SSimon J. Gerraty 		if (resolved == NULL)
85*3cbdda60SSimon J. Gerraty 			return NULL;
86*3cbdda60SSimon J. Gerraty 	} else
87*3cbdda60SSimon J. Gerraty 		fres = NULL;
88*3cbdda60SSimon J. Gerraty 
893955d011SMarcel Moolenaar 
903955d011SMarcel Moolenaar 	/*
913955d011SMarcel Moolenaar 	 * Build real path one by one with paying an attention to .,
923955d011SMarcel Moolenaar 	 * .. and symbolic link.
933955d011SMarcel Moolenaar 	 */
943955d011SMarcel Moolenaar 
953955d011SMarcel Moolenaar 	/*
963955d011SMarcel Moolenaar 	 * `p' is where we'll put a new component with prepending
973955d011SMarcel Moolenaar 	 * a delimiter.
983955d011SMarcel Moolenaar 	 */
993955d011SMarcel Moolenaar 	p = resolved;
1003955d011SMarcel Moolenaar 
101*3cbdda60SSimon J. Gerraty 	if (*path == '\0') {
102*3cbdda60SSimon J. Gerraty 		*p = '\0';
1033955d011SMarcel Moolenaar 		errno = ENOENT;
104*3cbdda60SSimon J. Gerraty 		goto out;
1053955d011SMarcel Moolenaar 	}
1063955d011SMarcel Moolenaar 
1073955d011SMarcel Moolenaar 	/* If relative path, start from current working directory. */
1083955d011SMarcel Moolenaar 	if (*path != '/') {
1093955d011SMarcel Moolenaar 		/* check for resolved pointer to appease coverity */
1103955d011SMarcel Moolenaar 		if (resolved && getcwd(resolved, MAXPATHLEN) == NULL) {
1113955d011SMarcel Moolenaar 			p[0] = '.';
112*3cbdda60SSimon J. Gerraty 			p[1] = '\0';
113*3cbdda60SSimon J. Gerraty 			goto out;
1143955d011SMarcel Moolenaar 		}
1153955d011SMarcel Moolenaar 		len = strlen(resolved);
1163955d011SMarcel Moolenaar 		if (len > 1)
1173955d011SMarcel Moolenaar 			p += len;
1183955d011SMarcel Moolenaar 	}
1193955d011SMarcel Moolenaar 
1203955d011SMarcel Moolenaar loop:
1213955d011SMarcel Moolenaar 	/* Skip any slash. */
1223955d011SMarcel Moolenaar 	while (*path == '/')
1233955d011SMarcel Moolenaar 		path++;
1243955d011SMarcel Moolenaar 
125*3cbdda60SSimon J. Gerraty 	if (*path == '\0') {
1263955d011SMarcel Moolenaar 		if (p == resolved)
1273955d011SMarcel Moolenaar 			*p++ = '/';
128*3cbdda60SSimon J. Gerraty 		*p = '\0';
129*3cbdda60SSimon J. Gerraty 		return resolved;
1303955d011SMarcel Moolenaar 	}
1313955d011SMarcel Moolenaar 
1323955d011SMarcel Moolenaar 	/* Find the end of this component. */
1333955d011SMarcel Moolenaar 	q = path;
1343955d011SMarcel Moolenaar 	do
1353955d011SMarcel Moolenaar 		q++;
136*3cbdda60SSimon J. Gerraty 	while (*q != '/' && *q != '\0');
1373955d011SMarcel Moolenaar 
1383955d011SMarcel Moolenaar 	/* Test . or .. */
1393955d011SMarcel Moolenaar 	if (path[0] == '.') {
1403955d011SMarcel Moolenaar 		if (q - path == 1) {
1413955d011SMarcel Moolenaar 			path = q;
1423955d011SMarcel Moolenaar 			goto loop;
1433955d011SMarcel Moolenaar 		}
1443955d011SMarcel Moolenaar 		if (path[1] == '.' && q - path == 2) {
1453955d011SMarcel Moolenaar 			/* Trim the last component. */
1463955d011SMarcel Moolenaar 			if (p != resolved)
1473955d011SMarcel Moolenaar 				while (*--p != '/')
148*3cbdda60SSimon J. Gerraty 					continue;
1493955d011SMarcel Moolenaar 			path = q;
1503955d011SMarcel Moolenaar 			goto loop;
1513955d011SMarcel Moolenaar 		}
1523955d011SMarcel Moolenaar 	}
1533955d011SMarcel Moolenaar 
1543955d011SMarcel Moolenaar 	/* Append this component. */
1553955d011SMarcel Moolenaar 	if (p - resolved + 1 + q - path + 1 > MAXPATHLEN) {
1563955d011SMarcel Moolenaar 		errno = ENAMETOOLONG;
1573955d011SMarcel Moolenaar 		if (p == resolved)
1583955d011SMarcel Moolenaar 			*p++ = '/';
159*3cbdda60SSimon J. Gerraty 		*p = '\0';
160*3cbdda60SSimon J. Gerraty 		goto out;
1613955d011SMarcel Moolenaar 	}
1623955d011SMarcel Moolenaar 	p[0] = '/';
1633955d011SMarcel Moolenaar 	memcpy(&p[1], path,
1643955d011SMarcel Moolenaar 	    /* LINTED We know q > path. */
1653955d011SMarcel Moolenaar 	    q - path);
166*3cbdda60SSimon J. Gerraty 	p[1 + q - path] = '\0';
1673955d011SMarcel Moolenaar 
1683955d011SMarcel Moolenaar 	/*
1693955d011SMarcel Moolenaar 	 * If this component is a symlink, toss it and prepend link
1703955d011SMarcel Moolenaar 	 * target to unresolved path.
1713955d011SMarcel Moolenaar 	 */
172*3cbdda60SSimon J. Gerraty 	if (lstat(resolved, &sb) == -1)
173*3cbdda60SSimon J. Gerraty 		goto out;
174*3cbdda60SSimon J. Gerraty 
1753955d011SMarcel Moolenaar 	if (S_ISLNK(sb.st_mode)) {
1763955d011SMarcel Moolenaar 		if (nlnk++ >= MAXSYMLINKS) {
1773955d011SMarcel Moolenaar 			errno = ELOOP;
178*3cbdda60SSimon J. Gerraty 			goto out;
1793955d011SMarcel Moolenaar 		}
1803955d011SMarcel Moolenaar 		n = readlink(resolved, wbuf[idx], sizeof(wbuf[0]) - 1);
1813955d011SMarcel Moolenaar 		if (n < 0)
182*3cbdda60SSimon J. Gerraty 			goto out;
1833955d011SMarcel Moolenaar 		if (n == 0) {
1843955d011SMarcel Moolenaar 			errno = ENOENT;
185*3cbdda60SSimon J. Gerraty 			goto out;
1863955d011SMarcel Moolenaar 		}
1873955d011SMarcel Moolenaar 
1883955d011SMarcel Moolenaar 		/* Append unresolved path to link target and switch to it. */
1893955d011SMarcel Moolenaar 		if (n + (len = strlen(q)) + 1 > sizeof(wbuf[0])) {
1903955d011SMarcel Moolenaar 			errno = ENAMETOOLONG;
191*3cbdda60SSimon J. Gerraty 			goto out;
1923955d011SMarcel Moolenaar 		}
1933955d011SMarcel Moolenaar 		memcpy(&wbuf[idx][n], q, len + 1);
1943955d011SMarcel Moolenaar 		path = wbuf[idx];
1953955d011SMarcel Moolenaar 		idx ^= 1;
1963955d011SMarcel Moolenaar 
1973955d011SMarcel Moolenaar 		/* If absolute symlink, start from root. */
1983955d011SMarcel Moolenaar 		if (*path == '/')
1993955d011SMarcel Moolenaar 			p = resolved;
2003955d011SMarcel Moolenaar 		goto loop;
2013955d011SMarcel Moolenaar 	}
2023955d011SMarcel Moolenaar 	if (*q == '/' && !S_ISDIR(sb.st_mode)) {
2033955d011SMarcel Moolenaar 		errno = ENOTDIR;
204*3cbdda60SSimon J. Gerraty 		goto out;
2053955d011SMarcel Moolenaar 	}
2063955d011SMarcel Moolenaar 
2073955d011SMarcel Moolenaar 	/* Advance both resolved and unresolved path. */
2083955d011SMarcel Moolenaar 	p += 1 + q - path;
2093955d011SMarcel Moolenaar 	path = q;
2103955d011SMarcel Moolenaar 	goto loop;
211*3cbdda60SSimon J. Gerraty out:
212*3cbdda60SSimon J. Gerraty 	free(fres);
213*3cbdda60SSimon J. Gerraty 	return NULL;
2143955d011SMarcel Moolenaar }
2153955d011SMarcel Moolenaar #endif
216