1*3cbdda60SSimon J. Gerraty /* $Id: realpath.c,v 1.3 2013/01/25 17:06:09 sjg Exp $ */
2*3cbdda60SSimon J. Gerraty /* from: $NetBSD: getcwd.c,v 1.53 2012/06/21 23:29:23 enami Exp $ */
33955d011SMarcel Moolenaar
43955d011SMarcel Moolenaar /*
53955d011SMarcel Moolenaar * Copyright (c) 1989, 1991, 1993, 1995
63955d011SMarcel Moolenaar * The Regents of the University of California. All rights reserved.
73955d011SMarcel Moolenaar *
83955d011SMarcel Moolenaar * This code is derived from software contributed to Berkeley by
93955d011SMarcel Moolenaar * Jan-Simon Pendry.
103955d011SMarcel Moolenaar *
113955d011SMarcel Moolenaar * Redistribution and use in source and binary forms, with or without
123955d011SMarcel Moolenaar * modification, are permitted provided that the following conditions
133955d011SMarcel Moolenaar * are met:
143955d011SMarcel Moolenaar * 1. Redistributions of source code must retain the above copyright
153955d011SMarcel Moolenaar * notice, this list of conditions and the following disclaimer.
163955d011SMarcel Moolenaar * 2. Redistributions in binary form must reproduce the above copyright
173955d011SMarcel Moolenaar * notice, this list of conditions and the following disclaimer in the
183955d011SMarcel Moolenaar * documentation and/or other materials provided with the distribution.
193955d011SMarcel Moolenaar * 3. Neither the name of the University nor the names of its contributors
203955d011SMarcel Moolenaar * may be used to endorse or promote products derived from this software
213955d011SMarcel Moolenaar * without specific prior written permission.
223955d011SMarcel Moolenaar *
233955d011SMarcel Moolenaar * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
243955d011SMarcel Moolenaar * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
253955d011SMarcel Moolenaar * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
263955d011SMarcel Moolenaar * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
273955d011SMarcel Moolenaar * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
283955d011SMarcel Moolenaar * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
293955d011SMarcel Moolenaar * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
303955d011SMarcel Moolenaar * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
313955d011SMarcel Moolenaar * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
323955d011SMarcel Moolenaar * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
333955d011SMarcel Moolenaar * SUCH DAMAGE.
343955d011SMarcel Moolenaar */
353955d011SMarcel Moolenaar #ifdef HAVE_CONFIG_H
363955d011SMarcel Moolenaar # include <config.h>
373955d011SMarcel Moolenaar #endif
383955d011SMarcel Moolenaar #ifndef HAVE_REALPATH
393955d011SMarcel Moolenaar
403955d011SMarcel Moolenaar #include <sys/cdefs.h>
413955d011SMarcel Moolenaar #include <sys/param.h>
423955d011SMarcel Moolenaar #include <sys/stat.h>
433955d011SMarcel Moolenaar
443955d011SMarcel Moolenaar #include <errno.h>
453955d011SMarcel Moolenaar #ifdef HAVE_STDLIB_H
463955d011SMarcel Moolenaar # include <stdlib.h>
473955d011SMarcel Moolenaar #endif
483955d011SMarcel Moolenaar #ifdef HAVE_STRING_H
493955d011SMarcel Moolenaar # include <string.h>
503955d011SMarcel Moolenaar #endif
513955d011SMarcel Moolenaar #ifdef HAVE_UNISTD_H
523955d011SMarcel Moolenaar # include <unistd.h>
533955d011SMarcel Moolenaar #endif
543955d011SMarcel Moolenaar
55*3cbdda60SSimon J. Gerraty #ifndef __restrict
56*3cbdda60SSimon J. Gerraty # define __restrict /* restrict */
57*3cbdda60SSimon J. Gerraty #endif
58*3cbdda60SSimon J. Gerraty
593955d011SMarcel Moolenaar /*
60*3cbdda60SSimon J. Gerraty * char *realpath(const char *path, char *resolved);
613955d011SMarcel Moolenaar *
623955d011SMarcel Moolenaar * Find the real name of path, by removing all ".", ".." and symlink
633955d011SMarcel Moolenaar * components. Returns (resolved) on success, or (NULL) on failure,
643955d011SMarcel Moolenaar * in which case the path which caused trouble is left in (resolved).
653955d011SMarcel Moolenaar */
663955d011SMarcel Moolenaar char *
realpath(const char * __restrict path,char * __restrict resolved)67*3cbdda60SSimon J. Gerraty realpath(const char * __restrict path, char * __restrict resolved)
683955d011SMarcel Moolenaar {
693955d011SMarcel Moolenaar struct stat sb;
70*3cbdda60SSimon J. Gerraty int idx = 0, nlnk = 0;
713955d011SMarcel Moolenaar const char *q;
72*3cbdda60SSimon J. Gerraty char *p, wbuf[2][MAXPATHLEN], *fres;
733955d011SMarcel Moolenaar size_t len;
74*3cbdda60SSimon J. Gerraty ssize_t n;
753955d011SMarcel Moolenaar
76*3cbdda60SSimon J. Gerraty /* POSIX sez we must test for this */
77*3cbdda60SSimon J. Gerraty if (path == NULL) {
78*3cbdda60SSimon J. Gerraty errno = EINVAL;
79*3cbdda60SSimon J. Gerraty return NULL;
80*3cbdda60SSimon J. Gerraty }
81*3cbdda60SSimon J. Gerraty
82*3cbdda60SSimon J. Gerraty if (resolved == NULL) {
83*3cbdda60SSimon J. Gerraty fres = resolved = malloc(MAXPATHLEN);
84*3cbdda60SSimon J. Gerraty if (resolved == NULL)
85*3cbdda60SSimon J. Gerraty return NULL;
86*3cbdda60SSimon J. Gerraty } else
87*3cbdda60SSimon J. Gerraty fres = NULL;
88*3cbdda60SSimon J. Gerraty
893955d011SMarcel Moolenaar
903955d011SMarcel Moolenaar /*
913955d011SMarcel Moolenaar * Build real path one by one with paying an attention to .,
923955d011SMarcel Moolenaar * .. and symbolic link.
933955d011SMarcel Moolenaar */
943955d011SMarcel Moolenaar
953955d011SMarcel Moolenaar /*
963955d011SMarcel Moolenaar * `p' is where we'll put a new component with prepending
973955d011SMarcel Moolenaar * a delimiter.
983955d011SMarcel Moolenaar */
993955d011SMarcel Moolenaar p = resolved;
1003955d011SMarcel Moolenaar
101*3cbdda60SSimon J. Gerraty if (*path == '\0') {
102*3cbdda60SSimon J. Gerraty *p = '\0';
1033955d011SMarcel Moolenaar errno = ENOENT;
104*3cbdda60SSimon J. Gerraty goto out;
1053955d011SMarcel Moolenaar }
1063955d011SMarcel Moolenaar
1073955d011SMarcel Moolenaar /* If relative path, start from current working directory. */
1083955d011SMarcel Moolenaar if (*path != '/') {
1093955d011SMarcel Moolenaar /* check for resolved pointer to appease coverity */
1103955d011SMarcel Moolenaar if (resolved && getcwd(resolved, MAXPATHLEN) == NULL) {
1113955d011SMarcel Moolenaar p[0] = '.';
112*3cbdda60SSimon J. Gerraty p[1] = '\0';
113*3cbdda60SSimon J. Gerraty goto out;
1143955d011SMarcel Moolenaar }
1153955d011SMarcel Moolenaar len = strlen(resolved);
1163955d011SMarcel Moolenaar if (len > 1)
1173955d011SMarcel Moolenaar p += len;
1183955d011SMarcel Moolenaar }
1193955d011SMarcel Moolenaar
1203955d011SMarcel Moolenaar loop:
1213955d011SMarcel Moolenaar /* Skip any slash. */
1223955d011SMarcel Moolenaar while (*path == '/')
1233955d011SMarcel Moolenaar path++;
1243955d011SMarcel Moolenaar
125*3cbdda60SSimon J. Gerraty if (*path == '\0') {
1263955d011SMarcel Moolenaar if (p == resolved)
1273955d011SMarcel Moolenaar *p++ = '/';
128*3cbdda60SSimon J. Gerraty *p = '\0';
129*3cbdda60SSimon J. Gerraty return resolved;
1303955d011SMarcel Moolenaar }
1313955d011SMarcel Moolenaar
1323955d011SMarcel Moolenaar /* Find the end of this component. */
1333955d011SMarcel Moolenaar q = path;
1343955d011SMarcel Moolenaar do
1353955d011SMarcel Moolenaar q++;
136*3cbdda60SSimon J. Gerraty while (*q != '/' && *q != '\0');
1373955d011SMarcel Moolenaar
1383955d011SMarcel Moolenaar /* Test . or .. */
1393955d011SMarcel Moolenaar if (path[0] == '.') {
1403955d011SMarcel Moolenaar if (q - path == 1) {
1413955d011SMarcel Moolenaar path = q;
1423955d011SMarcel Moolenaar goto loop;
1433955d011SMarcel Moolenaar }
1443955d011SMarcel Moolenaar if (path[1] == '.' && q - path == 2) {
1453955d011SMarcel Moolenaar /* Trim the last component. */
1463955d011SMarcel Moolenaar if (p != resolved)
1473955d011SMarcel Moolenaar while (*--p != '/')
148*3cbdda60SSimon J. Gerraty continue;
1493955d011SMarcel Moolenaar path = q;
1503955d011SMarcel Moolenaar goto loop;
1513955d011SMarcel Moolenaar }
1523955d011SMarcel Moolenaar }
1533955d011SMarcel Moolenaar
1543955d011SMarcel Moolenaar /* Append this component. */
1553955d011SMarcel Moolenaar if (p - resolved + 1 + q - path + 1 > MAXPATHLEN) {
1563955d011SMarcel Moolenaar errno = ENAMETOOLONG;
1573955d011SMarcel Moolenaar if (p == resolved)
1583955d011SMarcel Moolenaar *p++ = '/';
159*3cbdda60SSimon J. Gerraty *p = '\0';
160*3cbdda60SSimon J. Gerraty goto out;
1613955d011SMarcel Moolenaar }
1623955d011SMarcel Moolenaar p[0] = '/';
1633955d011SMarcel Moolenaar memcpy(&p[1], path,
1643955d011SMarcel Moolenaar /* LINTED We know q > path. */
1653955d011SMarcel Moolenaar q - path);
166*3cbdda60SSimon J. Gerraty p[1 + q - path] = '\0';
1673955d011SMarcel Moolenaar
1683955d011SMarcel Moolenaar /*
1693955d011SMarcel Moolenaar * If this component is a symlink, toss it and prepend link
1703955d011SMarcel Moolenaar * target to unresolved path.
1713955d011SMarcel Moolenaar */
172*3cbdda60SSimon J. Gerraty if (lstat(resolved, &sb) == -1)
173*3cbdda60SSimon J. Gerraty goto out;
174*3cbdda60SSimon J. Gerraty
1753955d011SMarcel Moolenaar if (S_ISLNK(sb.st_mode)) {
1763955d011SMarcel Moolenaar if (nlnk++ >= MAXSYMLINKS) {
1773955d011SMarcel Moolenaar errno = ELOOP;
178*3cbdda60SSimon J. Gerraty goto out;
1793955d011SMarcel Moolenaar }
1803955d011SMarcel Moolenaar n = readlink(resolved, wbuf[idx], sizeof(wbuf[0]) - 1);
1813955d011SMarcel Moolenaar if (n < 0)
182*3cbdda60SSimon J. Gerraty goto out;
1833955d011SMarcel Moolenaar if (n == 0) {
1843955d011SMarcel Moolenaar errno = ENOENT;
185*3cbdda60SSimon J. Gerraty goto out;
1863955d011SMarcel Moolenaar }
1873955d011SMarcel Moolenaar
1883955d011SMarcel Moolenaar /* Append unresolved path to link target and switch to it. */
1893955d011SMarcel Moolenaar if (n + (len = strlen(q)) + 1 > sizeof(wbuf[0])) {
1903955d011SMarcel Moolenaar errno = ENAMETOOLONG;
191*3cbdda60SSimon J. Gerraty goto out;
1923955d011SMarcel Moolenaar }
1933955d011SMarcel Moolenaar memcpy(&wbuf[idx][n], q, len + 1);
1943955d011SMarcel Moolenaar path = wbuf[idx];
1953955d011SMarcel Moolenaar idx ^= 1;
1963955d011SMarcel Moolenaar
1973955d011SMarcel Moolenaar /* If absolute symlink, start from root. */
1983955d011SMarcel Moolenaar if (*path == '/')
1993955d011SMarcel Moolenaar p = resolved;
2003955d011SMarcel Moolenaar goto loop;
2013955d011SMarcel Moolenaar }
2023955d011SMarcel Moolenaar if (*q == '/' && !S_ISDIR(sb.st_mode)) {
2033955d011SMarcel Moolenaar errno = ENOTDIR;
204*3cbdda60SSimon J. Gerraty goto out;
2053955d011SMarcel Moolenaar }
2063955d011SMarcel Moolenaar
2073955d011SMarcel Moolenaar /* Advance both resolved and unresolved path. */
2083955d011SMarcel Moolenaar p += 1 + q - path;
2093955d011SMarcel Moolenaar path = q;
2103955d011SMarcel Moolenaar goto loop;
211*3cbdda60SSimon J. Gerraty out:
212*3cbdda60SSimon J. Gerraty free(fres);
213*3cbdda60SSimon J. Gerraty return NULL;
2143955d011SMarcel Moolenaar }
2153955d011SMarcel Moolenaar #endif
216