realpath.c revision 55837
1/*
2 * Copyright (c) 1994
3 *	The Regents of the University of California.  All rights reserved.
4 *
5 * This code is derived from software contributed to Berkeley by
6 * Jan-Simon Pendry.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 *    notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 * 3. All advertising materials mentioning features or use of this software
17 *    must display the following acknowledgement:
18 *	This product includes software developed by the University of
19 *	California, Berkeley and its contributors.
20 * 4. Neither the name of the University nor the names of its contributors
21 *    may be used to endorse or promote products derived from this software
22 *    without specific prior written permission.
23 *
24 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34 * SUCH DAMAGE.
35 *
36 * $FreeBSD: head/lib/libc/stdlib/realpath.c 55837 2000-01-12 09:23:48Z jasone $
37 */
38
39#if defined(LIBC_SCCS) && !defined(lint)
40static char sccsid[] = "@(#)realpath.c	8.1 (Berkeley) 2/16/94";
41#endif /* LIBC_SCCS and not lint */
42
43#include <sys/param.h>
44#include <sys/stat.h>
45
46#include <errno.h>
47#include <fcntl.h>
48#include <stdlib.h>
49#include <string.h>
50#include <unistd.h>
51
52/*
53 * char *realpath(const char *path, char resolved_path[MAXPATHLEN]);
54 *
55 * Find the real name of path, by removing all ".", ".." and symlink
56 * components.  Returns (resolved) on success, or (NULL) on failure,
57 * in which case the path which caused trouble is left in (resolved).
58 */
59char *
60realpath(path, resolved)
61	const char *path;
62	char *resolved;
63{
64	struct stat sb;
65	int fd, n, rootd, serrno;
66	char *p, *q, wbuf[MAXPATHLEN];
67	int symlinks = 0;
68
69	/* Save the starting point. */
70	if ((fd = _libc_open(".", O_RDONLY)) < 0) {
71		(void)strcpy(resolved, ".");
72		return (NULL);
73	}
74
75	/*
76	 * Find the dirname and basename from the path to be resolved.
77	 * Change directory to the dirname component.
78	 * lstat the basename part.
79	 *     if it is a symlink, read in the value and loop.
80	 *     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)_libc_close(fd);
158	return (resolved);
159
160err1:	serrno = errno;
161	(void)fchdir(fd);
162err2:	(void)_libc_close(fd);
163	errno = serrno;
164	return (NULL);
165}
166