xref: /dflybsd-src/contrib/bmake/realpath.c (revision 5f1e34d9df77f5c410591d2bc3da08302e0e3fa4)
1*5f1e34d9SAlexandre Perrin /* $Id: realpath.c,v 1.3 2013/01/25 17:06:09 sjg Exp $ */
2*5f1e34d9SAlexandre Perrin /* from: $NetBSD: getcwd.c,v 1.53 2012/06/21 23:29:23 enami Exp $	*/
301e196c8SJohn Marino 
401e196c8SJohn Marino /*
501e196c8SJohn Marino  * Copyright (c) 1989, 1991, 1993, 1995
601e196c8SJohn Marino  *	The Regents of the University of California.  All rights reserved.
701e196c8SJohn Marino  *
801e196c8SJohn Marino  * This code is derived from software contributed to Berkeley by
901e196c8SJohn Marino  * Jan-Simon Pendry.
1001e196c8SJohn Marino  *
1101e196c8SJohn Marino  * Redistribution and use in source and binary forms, with or without
1201e196c8SJohn Marino  * modification, are permitted provided that the following conditions
1301e196c8SJohn Marino  * are met:
1401e196c8SJohn Marino  * 1. Redistributions of source code must retain the above copyright
1501e196c8SJohn Marino  *    notice, this list of conditions and the following disclaimer.
1601e196c8SJohn Marino  * 2. Redistributions in binary form must reproduce the above copyright
1701e196c8SJohn Marino  *    notice, this list of conditions and the following disclaimer in the
1801e196c8SJohn Marino  *    documentation and/or other materials provided with the distribution.
1901e196c8SJohn Marino  * 3. Neither the name of the University nor the names of its contributors
2001e196c8SJohn Marino  *    may be used to endorse or promote products derived from this software
2101e196c8SJohn Marino  *    without specific prior written permission.
2201e196c8SJohn Marino  *
2301e196c8SJohn Marino  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2401e196c8SJohn Marino  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2501e196c8SJohn Marino  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2601e196c8SJohn Marino  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2701e196c8SJohn Marino  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2801e196c8SJohn Marino  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2901e196c8SJohn Marino  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
3001e196c8SJohn Marino  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
3101e196c8SJohn Marino  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3201e196c8SJohn Marino  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3301e196c8SJohn Marino  * SUCH DAMAGE.
3401e196c8SJohn Marino  */
3501e196c8SJohn Marino #ifdef HAVE_CONFIG_H
3601e196c8SJohn Marino # include <config.h>
3701e196c8SJohn Marino #endif
3801e196c8SJohn Marino #ifndef HAVE_REALPATH
3901e196c8SJohn Marino 
4001e196c8SJohn Marino #include <sys/cdefs.h>
4101e196c8SJohn Marino #include <sys/param.h>
4201e196c8SJohn Marino #include <sys/stat.h>
4301e196c8SJohn Marino 
4401e196c8SJohn Marino #include <errno.h>
4501e196c8SJohn Marino #ifdef HAVE_STDLIB_H
4601e196c8SJohn Marino # include <stdlib.h>
4701e196c8SJohn Marino #endif
4801e196c8SJohn Marino #ifdef HAVE_STRING_H
4901e196c8SJohn Marino # include <string.h>
5001e196c8SJohn Marino #endif
5101e196c8SJohn Marino #ifdef HAVE_UNISTD_H
5201e196c8SJohn Marino # include <unistd.h>
5301e196c8SJohn Marino #endif
5401e196c8SJohn Marino 
55*5f1e34d9SAlexandre Perrin #ifndef __restrict
56*5f1e34d9SAlexandre Perrin # define __restrict /* restrict */
57*5f1e34d9SAlexandre Perrin #endif
58*5f1e34d9SAlexandre Perrin 
5901e196c8SJohn Marino /*
60*5f1e34d9SAlexandre Perrin  * char *realpath(const char *path, char *resolved);
6101e196c8SJohn Marino  *
6201e196c8SJohn Marino  * Find the real name of path, by removing all ".", ".." and symlink
6301e196c8SJohn Marino  * components.  Returns (resolved) on success, or (NULL) on failure,
6401e196c8SJohn Marino  * in which case the path which caused trouble is left in (resolved).
6501e196c8SJohn Marino  */
6601e196c8SJohn Marino char *
realpath(const char * __restrict path,char * __restrict resolved)67*5f1e34d9SAlexandre Perrin realpath(const char * __restrict path, char * __restrict resolved)
6801e196c8SJohn Marino {
6901e196c8SJohn Marino 	struct stat sb;
70*5f1e34d9SAlexandre Perrin 	int idx = 0, nlnk = 0;
7101e196c8SJohn Marino 	const char *q;
72*5f1e34d9SAlexandre Perrin 	char *p, wbuf[2][MAXPATHLEN], *fres;
7301e196c8SJohn Marino 	size_t len;
74*5f1e34d9SAlexandre Perrin 	ssize_t n;
7501e196c8SJohn Marino 
76*5f1e34d9SAlexandre Perrin 	/* POSIX sez we must test for this */
77*5f1e34d9SAlexandre Perrin 	if (path == NULL) {
78*5f1e34d9SAlexandre Perrin 		errno = EINVAL;
79*5f1e34d9SAlexandre Perrin 		return NULL;
80*5f1e34d9SAlexandre Perrin 	}
81*5f1e34d9SAlexandre Perrin 
82*5f1e34d9SAlexandre Perrin 	if (resolved == NULL) {
83*5f1e34d9SAlexandre Perrin 		fres = resolved = malloc(MAXPATHLEN);
84*5f1e34d9SAlexandre Perrin 		if (resolved == NULL)
85*5f1e34d9SAlexandre Perrin 			return NULL;
86*5f1e34d9SAlexandre Perrin 	} else
87*5f1e34d9SAlexandre Perrin 		fres = NULL;
88*5f1e34d9SAlexandre Perrin 
8901e196c8SJohn Marino 
9001e196c8SJohn Marino 	/*
9101e196c8SJohn Marino 	 * Build real path one by one with paying an attention to .,
9201e196c8SJohn Marino 	 * .. and symbolic link.
9301e196c8SJohn Marino 	 */
9401e196c8SJohn Marino 
9501e196c8SJohn Marino 	/*
9601e196c8SJohn Marino 	 * `p' is where we'll put a new component with prepending
9701e196c8SJohn Marino 	 * a delimiter.
9801e196c8SJohn Marino 	 */
9901e196c8SJohn Marino 	p = resolved;
10001e196c8SJohn Marino 
101*5f1e34d9SAlexandre Perrin 	if (*path == '\0') {
102*5f1e34d9SAlexandre Perrin 		*p = '\0';
10301e196c8SJohn Marino 		errno = ENOENT;
104*5f1e34d9SAlexandre Perrin 		goto out;
10501e196c8SJohn Marino 	}
10601e196c8SJohn Marino 
10701e196c8SJohn Marino 	/* If relative path, start from current working directory. */
10801e196c8SJohn Marino 	if (*path != '/') {
10901e196c8SJohn Marino 		/* check for resolved pointer to appease coverity */
11001e196c8SJohn Marino 		if (resolved && getcwd(resolved, MAXPATHLEN) == NULL) {
11101e196c8SJohn Marino 			p[0] = '.';
112*5f1e34d9SAlexandre Perrin 			p[1] = '\0';
113*5f1e34d9SAlexandre Perrin 			goto out;
11401e196c8SJohn Marino 		}
11501e196c8SJohn Marino 		len = strlen(resolved);
11601e196c8SJohn Marino 		if (len > 1)
11701e196c8SJohn Marino 			p += len;
11801e196c8SJohn Marino 	}
11901e196c8SJohn Marino 
12001e196c8SJohn Marino loop:
12101e196c8SJohn Marino 	/* Skip any slash. */
12201e196c8SJohn Marino 	while (*path == '/')
12301e196c8SJohn Marino 		path++;
12401e196c8SJohn Marino 
125*5f1e34d9SAlexandre Perrin 	if (*path == '\0') {
12601e196c8SJohn Marino 		if (p == resolved)
12701e196c8SJohn Marino 			*p++ = '/';
128*5f1e34d9SAlexandre Perrin 		*p = '\0';
129*5f1e34d9SAlexandre Perrin 		return resolved;
13001e196c8SJohn Marino 	}
13101e196c8SJohn Marino 
13201e196c8SJohn Marino 	/* Find the end of this component. */
13301e196c8SJohn Marino 	q = path;
13401e196c8SJohn Marino 	do
13501e196c8SJohn Marino 		q++;
136*5f1e34d9SAlexandre Perrin 	while (*q != '/' && *q != '\0');
13701e196c8SJohn Marino 
13801e196c8SJohn Marino 	/* Test . or .. */
13901e196c8SJohn Marino 	if (path[0] == '.') {
14001e196c8SJohn Marino 		if (q - path == 1) {
14101e196c8SJohn Marino 			path = q;
14201e196c8SJohn Marino 			goto loop;
14301e196c8SJohn Marino 		}
14401e196c8SJohn Marino 		if (path[1] == '.' && q - path == 2) {
14501e196c8SJohn Marino 			/* Trim the last component. */
14601e196c8SJohn Marino 			if (p != resolved)
14701e196c8SJohn Marino 				while (*--p != '/')
148*5f1e34d9SAlexandre Perrin 					continue;
14901e196c8SJohn Marino 			path = q;
15001e196c8SJohn Marino 			goto loop;
15101e196c8SJohn Marino 		}
15201e196c8SJohn Marino 	}
15301e196c8SJohn Marino 
15401e196c8SJohn Marino 	/* Append this component. */
15501e196c8SJohn Marino 	if (p - resolved + 1 + q - path + 1 > MAXPATHLEN) {
15601e196c8SJohn Marino 		errno = ENAMETOOLONG;
15701e196c8SJohn Marino 		if (p == resolved)
15801e196c8SJohn Marino 			*p++ = '/';
159*5f1e34d9SAlexandre Perrin 		*p = '\0';
160*5f1e34d9SAlexandre Perrin 		goto out;
16101e196c8SJohn Marino 	}
16201e196c8SJohn Marino 	p[0] = '/';
16301e196c8SJohn Marino 	memcpy(&p[1], path,
16401e196c8SJohn Marino 	    /* LINTED We know q > path. */
16501e196c8SJohn Marino 	    q - path);
166*5f1e34d9SAlexandre Perrin 	p[1 + q - path] = '\0';
16701e196c8SJohn Marino 
16801e196c8SJohn Marino 	/*
16901e196c8SJohn Marino 	 * If this component is a symlink, toss it and prepend link
17001e196c8SJohn Marino 	 * target to unresolved path.
17101e196c8SJohn Marino 	 */
172*5f1e34d9SAlexandre Perrin 	if (lstat(resolved, &sb) == -1)
173*5f1e34d9SAlexandre Perrin 		goto out;
174*5f1e34d9SAlexandre Perrin 
17501e196c8SJohn Marino 	if (S_ISLNK(sb.st_mode)) {
17601e196c8SJohn Marino 		if (nlnk++ >= MAXSYMLINKS) {
17701e196c8SJohn Marino 			errno = ELOOP;
178*5f1e34d9SAlexandre Perrin 			goto out;
17901e196c8SJohn Marino 		}
18001e196c8SJohn Marino 		n = readlink(resolved, wbuf[idx], sizeof(wbuf[0]) - 1);
18101e196c8SJohn Marino 		if (n < 0)
182*5f1e34d9SAlexandre Perrin 			goto out;
18301e196c8SJohn Marino 		if (n == 0) {
18401e196c8SJohn Marino 			errno = ENOENT;
185*5f1e34d9SAlexandre Perrin 			goto out;
18601e196c8SJohn Marino 		}
18701e196c8SJohn Marino 
18801e196c8SJohn Marino 		/* Append unresolved path to link target and switch to it. */
18901e196c8SJohn Marino 		if (n + (len = strlen(q)) + 1 > sizeof(wbuf[0])) {
19001e196c8SJohn Marino 			errno = ENAMETOOLONG;
191*5f1e34d9SAlexandre Perrin 			goto out;
19201e196c8SJohn Marino 		}
19301e196c8SJohn Marino 		memcpy(&wbuf[idx][n], q, len + 1);
19401e196c8SJohn Marino 		path = wbuf[idx];
19501e196c8SJohn Marino 		idx ^= 1;
19601e196c8SJohn Marino 
19701e196c8SJohn Marino 		/* If absolute symlink, start from root. */
19801e196c8SJohn Marino 		if (*path == '/')
19901e196c8SJohn Marino 			p = resolved;
20001e196c8SJohn Marino 		goto loop;
20101e196c8SJohn Marino 	}
20201e196c8SJohn Marino 	if (*q == '/' && !S_ISDIR(sb.st_mode)) {
20301e196c8SJohn Marino 		errno = ENOTDIR;
204*5f1e34d9SAlexandre Perrin 		goto out;
20501e196c8SJohn Marino 	}
20601e196c8SJohn Marino 
20701e196c8SJohn Marino 	/* Advance both resolved and unresolved path. */
20801e196c8SJohn Marino 	p += 1 + q - path;
20901e196c8SJohn Marino 	path = q;
21001e196c8SJohn Marino 	goto loop;
211*5f1e34d9SAlexandre Perrin out:
212*5f1e34d9SAlexandre Perrin 	free(fres);
213*5f1e34d9SAlexandre Perrin 	return NULL;
21401e196c8SJohn Marino }
21501e196c8SJohn Marino #endif
216