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