1*5f1e34d9SAlexandre Perrin /* $Id: realpath.c,v 1.3 2013/01/25 17:06:09 sjg Exp $ */
2*5f1e34d9SAlexandre Perrin /* from: $NetBSD: getcwd.c,v 1.53 2012/06/21 23:29:23 enami Exp $ */
301e196c8SJohn Marino
401e196c8SJohn Marino /*
501e196c8SJohn Marino * Copyright (c) 1989, 1991, 1993, 1995
601e196c8SJohn Marino * The Regents of the University of California. All rights reserved.
701e196c8SJohn Marino *
801e196c8SJohn Marino * This code is derived from software contributed to Berkeley by
901e196c8SJohn Marino * Jan-Simon Pendry.
1001e196c8SJohn Marino *
1101e196c8SJohn Marino * Redistribution and use in source and binary forms, with or without
1201e196c8SJohn Marino * modification, are permitted provided that the following conditions
1301e196c8SJohn Marino * are met:
1401e196c8SJohn Marino * 1. Redistributions of source code must retain the above copyright
1501e196c8SJohn Marino * notice, this list of conditions and the following disclaimer.
1601e196c8SJohn Marino * 2. Redistributions in binary form must reproduce the above copyright
1701e196c8SJohn Marino * notice, this list of conditions and the following disclaimer in the
1801e196c8SJohn Marino * documentation and/or other materials provided with the distribution.
1901e196c8SJohn Marino * 3. Neither the name of the University nor the names of its contributors
2001e196c8SJohn Marino * may be used to endorse or promote products derived from this software
2101e196c8SJohn Marino * without specific prior written permission.
2201e196c8SJohn Marino *
2301e196c8SJohn Marino * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2401e196c8SJohn Marino * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2501e196c8SJohn Marino * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2601e196c8SJohn Marino * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2701e196c8SJohn Marino * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2801e196c8SJohn Marino * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2901e196c8SJohn Marino * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
3001e196c8SJohn Marino * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
3101e196c8SJohn Marino * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3201e196c8SJohn Marino * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3301e196c8SJohn Marino * SUCH DAMAGE.
3401e196c8SJohn Marino */
3501e196c8SJohn Marino #ifdef HAVE_CONFIG_H
3601e196c8SJohn Marino # include <config.h>
3701e196c8SJohn Marino #endif
3801e196c8SJohn Marino #ifndef HAVE_REALPATH
3901e196c8SJohn Marino
4001e196c8SJohn Marino #include <sys/cdefs.h>
4101e196c8SJohn Marino #include <sys/param.h>
4201e196c8SJohn Marino #include <sys/stat.h>
4301e196c8SJohn Marino
4401e196c8SJohn Marino #include <errno.h>
4501e196c8SJohn Marino #ifdef HAVE_STDLIB_H
4601e196c8SJohn Marino # include <stdlib.h>
4701e196c8SJohn Marino #endif
4801e196c8SJohn Marino #ifdef HAVE_STRING_H
4901e196c8SJohn Marino # include <string.h>
5001e196c8SJohn Marino #endif
5101e196c8SJohn Marino #ifdef HAVE_UNISTD_H
5201e196c8SJohn Marino # include <unistd.h>
5301e196c8SJohn Marino #endif
5401e196c8SJohn Marino
55*5f1e34d9SAlexandre Perrin #ifndef __restrict
56*5f1e34d9SAlexandre Perrin # define __restrict /* restrict */
57*5f1e34d9SAlexandre Perrin #endif
58*5f1e34d9SAlexandre Perrin
5901e196c8SJohn Marino /*
60*5f1e34d9SAlexandre Perrin * char *realpath(const char *path, char *resolved);
6101e196c8SJohn Marino *
6201e196c8SJohn Marino * Find the real name of path, by removing all ".", ".." and symlink
6301e196c8SJohn Marino * components. Returns (resolved) on success, or (NULL) on failure,
6401e196c8SJohn Marino * in which case the path which caused trouble is left in (resolved).
6501e196c8SJohn Marino */
6601e196c8SJohn Marino char *
realpath(const char * __restrict path,char * __restrict resolved)67*5f1e34d9SAlexandre Perrin realpath(const char * __restrict path, char * __restrict resolved)
6801e196c8SJohn Marino {
6901e196c8SJohn Marino struct stat sb;
70*5f1e34d9SAlexandre Perrin int idx = 0, nlnk = 0;
7101e196c8SJohn Marino const char *q;
72*5f1e34d9SAlexandre Perrin char *p, wbuf[2][MAXPATHLEN], *fres;
7301e196c8SJohn Marino size_t len;
74*5f1e34d9SAlexandre Perrin ssize_t n;
7501e196c8SJohn Marino
76*5f1e34d9SAlexandre Perrin /* POSIX sez we must test for this */
77*5f1e34d9SAlexandre Perrin if (path == NULL) {
78*5f1e34d9SAlexandre Perrin errno = EINVAL;
79*5f1e34d9SAlexandre Perrin return NULL;
80*5f1e34d9SAlexandre Perrin }
81*5f1e34d9SAlexandre Perrin
82*5f1e34d9SAlexandre Perrin if (resolved == NULL) {
83*5f1e34d9SAlexandre Perrin fres = resolved = malloc(MAXPATHLEN);
84*5f1e34d9SAlexandre Perrin if (resolved == NULL)
85*5f1e34d9SAlexandre Perrin return NULL;
86*5f1e34d9SAlexandre Perrin } else
87*5f1e34d9SAlexandre Perrin fres = NULL;
88*5f1e34d9SAlexandre Perrin
8901e196c8SJohn Marino
9001e196c8SJohn Marino /*
9101e196c8SJohn Marino * Build real path one by one with paying an attention to .,
9201e196c8SJohn Marino * .. and symbolic link.
9301e196c8SJohn Marino */
9401e196c8SJohn Marino
9501e196c8SJohn Marino /*
9601e196c8SJohn Marino * `p' is where we'll put a new component with prepending
9701e196c8SJohn Marino * a delimiter.
9801e196c8SJohn Marino */
9901e196c8SJohn Marino p = resolved;
10001e196c8SJohn Marino
101*5f1e34d9SAlexandre Perrin if (*path == '\0') {
102*5f1e34d9SAlexandre Perrin *p = '\0';
10301e196c8SJohn Marino errno = ENOENT;
104*5f1e34d9SAlexandre Perrin goto out;
10501e196c8SJohn Marino }
10601e196c8SJohn Marino
10701e196c8SJohn Marino /* If relative path, start from current working directory. */
10801e196c8SJohn Marino if (*path != '/') {
10901e196c8SJohn Marino /* check for resolved pointer to appease coverity */
11001e196c8SJohn Marino if (resolved && getcwd(resolved, MAXPATHLEN) == NULL) {
11101e196c8SJohn Marino p[0] = '.';
112*5f1e34d9SAlexandre Perrin p[1] = '\0';
113*5f1e34d9SAlexandre Perrin goto out;
11401e196c8SJohn Marino }
11501e196c8SJohn Marino len = strlen(resolved);
11601e196c8SJohn Marino if (len > 1)
11701e196c8SJohn Marino p += len;
11801e196c8SJohn Marino }
11901e196c8SJohn Marino
12001e196c8SJohn Marino loop:
12101e196c8SJohn Marino /* Skip any slash. */
12201e196c8SJohn Marino while (*path == '/')
12301e196c8SJohn Marino path++;
12401e196c8SJohn Marino
125*5f1e34d9SAlexandre Perrin if (*path == '\0') {
12601e196c8SJohn Marino if (p == resolved)
12701e196c8SJohn Marino *p++ = '/';
128*5f1e34d9SAlexandre Perrin *p = '\0';
129*5f1e34d9SAlexandre Perrin return resolved;
13001e196c8SJohn Marino }
13101e196c8SJohn Marino
13201e196c8SJohn Marino /* Find the end of this component. */
13301e196c8SJohn Marino q = path;
13401e196c8SJohn Marino do
13501e196c8SJohn Marino q++;
136*5f1e34d9SAlexandre Perrin while (*q != '/' && *q != '\0');
13701e196c8SJohn Marino
13801e196c8SJohn Marino /* Test . or .. */
13901e196c8SJohn Marino if (path[0] == '.') {
14001e196c8SJohn Marino if (q - path == 1) {
14101e196c8SJohn Marino path = q;
14201e196c8SJohn Marino goto loop;
14301e196c8SJohn Marino }
14401e196c8SJohn Marino if (path[1] == '.' && q - path == 2) {
14501e196c8SJohn Marino /* Trim the last component. */
14601e196c8SJohn Marino if (p != resolved)
14701e196c8SJohn Marino while (*--p != '/')
148*5f1e34d9SAlexandre Perrin continue;
14901e196c8SJohn Marino path = q;
15001e196c8SJohn Marino goto loop;
15101e196c8SJohn Marino }
15201e196c8SJohn Marino }
15301e196c8SJohn Marino
15401e196c8SJohn Marino /* Append this component. */
15501e196c8SJohn Marino if (p - resolved + 1 + q - path + 1 > MAXPATHLEN) {
15601e196c8SJohn Marino errno = ENAMETOOLONG;
15701e196c8SJohn Marino if (p == resolved)
15801e196c8SJohn Marino *p++ = '/';
159*5f1e34d9SAlexandre Perrin *p = '\0';
160*5f1e34d9SAlexandre Perrin goto out;
16101e196c8SJohn Marino }
16201e196c8SJohn Marino p[0] = '/';
16301e196c8SJohn Marino memcpy(&p[1], path,
16401e196c8SJohn Marino /* LINTED We know q > path. */
16501e196c8SJohn Marino q - path);
166*5f1e34d9SAlexandre Perrin p[1 + q - path] = '\0';
16701e196c8SJohn Marino
16801e196c8SJohn Marino /*
16901e196c8SJohn Marino * If this component is a symlink, toss it and prepend link
17001e196c8SJohn Marino * target to unresolved path.
17101e196c8SJohn Marino */
172*5f1e34d9SAlexandre Perrin if (lstat(resolved, &sb) == -1)
173*5f1e34d9SAlexandre Perrin goto out;
174*5f1e34d9SAlexandre Perrin
17501e196c8SJohn Marino if (S_ISLNK(sb.st_mode)) {
17601e196c8SJohn Marino if (nlnk++ >= MAXSYMLINKS) {
17701e196c8SJohn Marino errno = ELOOP;
178*5f1e34d9SAlexandre Perrin goto out;
17901e196c8SJohn Marino }
18001e196c8SJohn Marino n = readlink(resolved, wbuf[idx], sizeof(wbuf[0]) - 1);
18101e196c8SJohn Marino if (n < 0)
182*5f1e34d9SAlexandre Perrin goto out;
18301e196c8SJohn Marino if (n == 0) {
18401e196c8SJohn Marino errno = ENOENT;
185*5f1e34d9SAlexandre Perrin goto out;
18601e196c8SJohn Marino }
18701e196c8SJohn Marino
18801e196c8SJohn Marino /* Append unresolved path to link target and switch to it. */
18901e196c8SJohn Marino if (n + (len = strlen(q)) + 1 > sizeof(wbuf[0])) {
19001e196c8SJohn Marino errno = ENAMETOOLONG;
191*5f1e34d9SAlexandre Perrin goto out;
19201e196c8SJohn Marino }
19301e196c8SJohn Marino memcpy(&wbuf[idx][n], q, len + 1);
19401e196c8SJohn Marino path = wbuf[idx];
19501e196c8SJohn Marino idx ^= 1;
19601e196c8SJohn Marino
19701e196c8SJohn Marino /* If absolute symlink, start from root. */
19801e196c8SJohn Marino if (*path == '/')
19901e196c8SJohn Marino p = resolved;
20001e196c8SJohn Marino goto loop;
20101e196c8SJohn Marino }
20201e196c8SJohn Marino if (*q == '/' && !S_ISDIR(sb.st_mode)) {
20301e196c8SJohn Marino errno = ENOTDIR;
204*5f1e34d9SAlexandre Perrin goto out;
20501e196c8SJohn Marino }
20601e196c8SJohn Marino
20701e196c8SJohn Marino /* Advance both resolved and unresolved path. */
20801e196c8SJohn Marino p += 1 + q - path;
20901e196c8SJohn Marino path = q;
21001e196c8SJohn Marino goto loop;
211*5f1e34d9SAlexandre Perrin out:
212*5f1e34d9SAlexandre Perrin free(fres);
213*5f1e34d9SAlexandre Perrin return NULL;
21401e196c8SJohn Marino }
21501e196c8SJohn Marino #endif
216