realpath.c revision 56698
155714Skris/*
255714Skris * Copyright (c) 1994
355714Skris *	The Regents of the University of California.  All rights reserved.
455714Skris *
555714Skris * This code is derived from software contributed to Berkeley by
655714Skris * Jan-Simon Pendry.
755714Skris *
855714Skris * Redistribution and use in source and binary forms, with or without
955714Skris * modification, are permitted provided that the following conditions
1055714Skris * are met:
1155714Skris * 1. Redistributions of source code must retain the above copyright
1255714Skris *    notice, this list of conditions and the following disclaimer.
1355714Skris * 2. Redistributions in binary form must reproduce the above copyright
1455714Skris *    notice, this list of conditions and the following disclaimer in the
1555714Skris *    documentation and/or other materials provided with the distribution.
1655714Skris * 3. All advertising materials mentioning features or use of this software
1755714Skris *    must display the following acknowledgement:
1855714Skris *	This product includes software developed by the University of
1955714Skris *	California, Berkeley and its contributors.
2055714Skris * 4. Neither the name of the University nor the names of its contributors
2155714Skris *    may be used to endorse or promote products derived from this software
2255714Skris *    without specific prior written permission.
2355714Skris *
2455714Skris * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2555714Skris * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2655714Skris * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2755714Skris * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2855714Skris * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2955714Skris * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
3055714Skris * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
3155714Skris * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
3255714Skris * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3355714Skris * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3455714Skris * SUCH DAMAGE.
3555714Skris *
3655714Skris * $FreeBSD: head/lib/libc/stdlib/realpath.c 56698 2000-01-27 23:07:25Z jasone $
3755714Skris */
3855714Skris
3955714Skris#if defined(LIBC_SCCS) && !defined(lint)
4055714Skrisstatic char sccsid[] = "@(#)realpath.c	8.1 (Berkeley) 2/16/94";
4155714Skris#endif /* LIBC_SCCS and not lint */
4255714Skris
4355714Skris#include <sys/param.h>
4455714Skris#include <sys/stat.h>
4555714Skris
4655714Skris#include <errno.h>
4755714Skris#include <fcntl.h>
4855714Skris#include <stdlib.h>
4955714Skris#include <string.h>
5055714Skris#include <unistd.h>
5155714Skris
5255714Skris/*
5355714Skris * char *realpath(const char *path, char resolved_path[MAXPATHLEN]);
5455714Skris *
5555714Skris * Find the real name of path, by removing all ".", ".." and symlink
5655714Skris * components.  Returns (resolved) on success, or (NULL) on failure,
5755714Skris * in which case the path which caused trouble is left in (resolved).
5855714Skris */
5955714Skrischar *
6055714Skrisrealpath(path, resolved)
6155714Skris	const char *path;
6255714Skris	char *resolved;
6355714Skris{
6455714Skris	struct stat sb;
6555714Skris	int fd, n, rootd, serrno;
6655714Skris	char *p, *q, wbuf[MAXPATHLEN];
6755714Skris	int symlinks = 0;
6855714Skris
6955714Skris	/* Save the starting point. */
7055714Skris	if ((fd = _open(".", O_RDONLY)) < 0) {
7155714Skris		(void)strcpy(resolved, ".");
7255714Skris		return (NULL);
7355714Skris	}
7455714Skris
7555714Skris	/*
7655714Skris	 * Find the dirname and basename from the path to be resolved.
7755714Skris	 * Change directory to the dirname component.
7855714Skris	 * lstat the basename part.
7955714Skris	 *     if it is a symlink, read in the value and loop.
8055714Skris	 *     if it is a directory, then change to that directory.
81	 * get the current directory name and append the basename.
82	 */
83	(void)strncpy(resolved, path, MAXPATHLEN - 1);
84	resolved[MAXPATHLEN - 1] = '\0';
85loop:
86	q = strrchr(resolved, '/');
87	if (q != NULL) {
88		p = q + 1;
89		if (q == resolved)
90			q = "/";
91		else {
92			do {
93				--q;
94			} while (q > resolved && *q == '/');
95			q[1] = '\0';
96			q = resolved;
97		}
98		if (chdir(q) < 0)
99			goto err1;
100	} else
101		p = resolved;
102
103	/* Deal with the last component. */
104	if (*p != '\0' && lstat(p, &sb) == 0) {
105		if (S_ISLNK(sb.st_mode)) {
106			if (++symlinks > MAXSYMLINKS) {
107				errno = ELOOP;
108				goto err1;
109			}
110			n = readlink(p, resolved, MAXPATHLEN - 1);
111			if (n < 0)
112				goto err1;
113			resolved[n] = '\0';
114			goto loop;
115		}
116		if (S_ISDIR(sb.st_mode)) {
117			if (chdir(p) < 0)
118				goto err1;
119			p = "";
120		}
121	}
122
123	/*
124	 * Save the last component name and get the full pathname of
125	 * the current directory.
126	 */
127	(void)strcpy(wbuf, p);
128	if (getcwd(resolved, MAXPATHLEN) == 0)
129		goto err1;
130
131	/*
132	 * Join the two strings together, ensuring that the right thing
133	 * happens if the last component is empty, or the dirname is root.
134	 */
135	if (resolved[0] == '/' && resolved[1] == '\0')
136		rootd = 1;
137	else
138		rootd = 0;
139
140	if (*wbuf) {
141		if (strlen(resolved) + strlen(wbuf) + rootd + 1 > MAXPATHLEN) {
142			errno = ENAMETOOLONG;
143			goto err1;
144		}
145		if (rootd == 0)
146			(void)strcat(resolved, "/");
147		(void)strcat(resolved, wbuf);
148	}
149
150	/* Go back to where we came from. */
151	if (fchdir(fd) < 0) {
152		serrno = errno;
153		goto err2;
154	}
155
156	/* It's okay if the close fails, what's an fd more or less? */
157	(void)_close(fd);
158	return (resolved);
159
160err1:	serrno = errno;
161	(void)fchdir(fd);
162err2:	(void)_close(fd);
163	errno = serrno;
164	return (NULL);
165}
166