realpath.c revision 124208
115885Sjulian/*
215885Sjulian * Copyright (c) 1994
315885Sjulian *	The Regents of the University of California.  All rights reserved.
415885Sjulian *
515885Sjulian * This code is derived from software contributed to Berkeley by
615885Sjulian * Jan-Simon Pendry.
715885Sjulian *
815885Sjulian * Redistribution and use in source and binary forms, with or without
929024Sbde * modification, are permitted provided that the following conditions
1015885Sjulian * are met:
1115885Sjulian * 1. Redistributions of source code must retain the above copyright
1215885Sjulian *    notice, this list of conditions and the following disclaimer.
1315885Sjulian * 2. Redistributions in binary form must reproduce the above copyright
1415885Sjulian *    notice, this list of conditions and the following disclaimer in the
1515885Sjulian *    documentation and/or other materials provided with the distribution.
1615885Sjulian * 3. Neither the name of the University nor the names of its contributors
1718207Sbde *    may be used to endorse or promote products derived from this software
1818207Sbde *    without specific prior written permission.
1918207Sbde *
2015885Sjulian * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2115885Sjulian * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2215885Sjulian * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2328270Swollman * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2428270Swollman * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2528270Swollman * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2628270Swollman * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2728270Swollman * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2828270Swollman * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2928270Swollman * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3015885Sjulian * SUCH DAMAGE.
3115885Sjulian */
3215885Sjulian
3315885Sjulian#include "includes.h"
3415885Sjulian
3515885Sjulian#if !defined(HAVE_REALPATH) || defined(BROKEN_REALPATH)
3625791Sjulian
3725791Sjulian#if defined(LIBC_SCCS) && !defined(lint)
3825791Sjulianstatic char *rcsid = "$OpenBSD: realpath.c,v 1.10 2003/08/01 21:04:59 millert Exp $";
3915885Sjulian#endif /* LIBC_SCCS and not lint */
4025791Sjulian
4125791Sjulian#include <sys/param.h>
4225791Sjulian#include <sys/stat.h>
4325791Sjulian
4415885Sjulian#include <errno.h>
4525791Sjulian#include <fcntl.h>
4615885Sjulian#include <stdlib.h>
4725791Sjulian#include <string.h>
4815885Sjulian#include <unistd.h>
4925791Sjulian
5025791Sjulian/*
5125791Sjulian * MAXSYMLINKS
5225791Sjulian */
5325791Sjulian#ifndef MAXSYMLINKS
5425791Sjulian#define MAXSYMLINKS 5
5515885Sjulian#endif
5625791Sjulian
5725791Sjulian/*
5815885Sjulian * char *realpath(const char *path, char resolved_path[MAXPATHLEN]);
5925791Sjulian *
6025791Sjulian * Find the real name of path, by removing all ".", ".." and symlink
6125791Sjulian * components.  Returns (resolved) on success, or (NULL) on failure,
6225791Sjulian * in which case the path which caused trouble is left in (resolved).
6325791Sjulian */
6425791Sjulianchar *
6525791Sjulianrealpath(const char *path, char *resolved)
6625791Sjulian{
6725791Sjulian	struct stat sb;
6825791Sjulian	int fd, n, needslash, serrno = 0;
6925791Sjulian	char *p, *q, wbuf[MAXPATHLEN], start[MAXPATHLEN];
7025791Sjulian	int symlinks = 0;
7115885Sjulian
7225791Sjulian	/* Save the starting point. */
7325791Sjulian	getcwd(start,MAXPATHLEN);
7425791Sjulian	if ((fd = open(".", O_RDONLY)) < 0) {
7515885Sjulian		(void)strlcpy(resolved, ".", MAXPATHLEN);
7625791Sjulian		return (NULL);
7728270Swollman	}
7825791Sjulian	close(fd);
7925791Sjulian
8025791Sjulian	/* Convert "." -> "" to optimize away a needless lstat() and chdir() */
8125791Sjulian	if (path[0] == '.' && path[1] == '\0')
8225791Sjulian		path = "";
8325791Sjulian
8425791Sjulian	/*
8525791Sjulian	 * Find the dirname and basename from the path to be resolved.
8625791Sjulian	 * Change directory to the dirname component.
8725791Sjulian	 * lstat the basename part.
8828270Swollman	 *     if it is a symlink, read in the value and loop.
8925791Sjulian	 *     if it is a directory, then change to that directory.
9025791Sjulian	 * get the current directory name and append the basename.
9125791Sjulian	 */
9215885Sjulian	strlcpy(resolved, path, MAXPATHLEN);
9325791Sjulianloop:
9428270Swollman	q = strrchr(resolved, '/');
9525791Sjulian	if (q != NULL) {
9625791Sjulian		p = q + 1;
9725791Sjulian		if (q == resolved)
9825791Sjulian			q = "/";
9925791Sjulian		else {
10025791Sjulian			do {
10125791Sjulian				--q;
10225791Sjulian			} while (q > resolved && *q == '/');
10325791Sjulian			q[1] = '\0';
10415885Sjulian			q = resolved;
10515885Sjulian		}
10625791Sjulian		if (chdir(q) < 0)
10715885Sjulian			goto err1;
10815885Sjulian	} else
10925791Sjulian		p = resolved;
11025791Sjulian
11125791Sjulian	/* Deal with the last component. */
11215885Sjulian	if (*p != '\0' && lstat(p, &sb) == 0) {
11315885Sjulian		if (S_ISLNK(sb.st_mode)) {
11425791Sjulian			if (++symlinks > MAXSYMLINKS) {
11525791Sjulian				serrno = ELOOP;
11615885Sjulian				goto err1;
11725791Sjulian			}
11825791Sjulian			n = readlink(p, resolved, MAXPATHLEN-1);
11925791Sjulian			if (n < 0)
12025791Sjulian				goto err1;
12125791Sjulian			resolved[n] = '\0';
12225791Sjulian			goto loop;
12325791Sjulian		}
12425791Sjulian		if (S_ISDIR(sb.st_mode)) {
12525791Sjulian			if (chdir(p) < 0)
12625791Sjulian				goto err1;
12725791Sjulian			p = "";
12825791Sjulian		}
12915885Sjulian	}
13025791Sjulian
13115885Sjulian	/*
13225791Sjulian	 * Save the last component name and get the full pathname of
13325791Sjulian	 * the current directory.
13415885Sjulian	 */
13525791Sjulian	(void)strlcpy(wbuf, p, sizeof wbuf);
13625791Sjulian	if (getcwd(resolved, MAXPATHLEN) == 0)
13715885Sjulian		goto err1;
13825791Sjulian
13925791Sjulian	/*
14015885Sjulian	 * Join the two strings together, ensuring that the right thing
14125791Sjulian	 * happens if the last component is empty, or the dirname is root.
14225791Sjulian	 */
14325791Sjulian	if (resolved[0] == '/' && resolved[1] == '\0')
14425791Sjulian		needslash = 0;
14525791Sjulian	else
14625791Sjulian		needslash = 1;
14725791Sjulian
14825791Sjulian	if (*wbuf) {
14925791Sjulian		if (strlen(resolved) + strlen(wbuf) + needslash >= MAXPATHLEN) {
15025791Sjulian			serrno = ENAMETOOLONG;
15125791Sjulian			goto err1;
15215885Sjulian		}
15325791Sjulian		if (needslash == 0)
15425791Sjulian			strlcat(resolved, "/", MAXPATHLEN);
15515885Sjulian		strlcat(resolved, wbuf, MAXPATHLEN);
15625791Sjulian	}
15728270Swollman
15825791Sjulian	/* Go back to where we came from. */
15925791Sjulian	if (chdir(start) < 0) {
16025791Sjulian		serrno = errno;
16125791Sjulian		goto err2;
16225791Sjulian	}
16325791Sjulian	return (resolved);
16425791Sjulian
16525791Sjulianerr1:	chdir(start);
16625791Sjulianerr2:	errno = serrno;
16725791Sjulian	return (NULL);
16815885Sjulian}
16925791Sjulian#endif /* !defined(HAVE_REALPATH) || defined(BROKEN_REALPATH) */
17025791Sjulian