xref: /dragonfly/contrib/bmake/realpath.c (revision 5f1e34d9df77f5c410591d2bc3da08302e0e3fa4)
1 /* $Id: realpath.c,v 1.3 2013/01/25 17:06:09 sjg Exp $ */
2 /* from: $NetBSD: getcwd.c,v 1.53 2012/06/21 23:29:23 enami Exp $     */
3 
4 /*
5  * Copyright (c) 1989, 1991, 1993, 1995
6  *        The Regents of the University of California.  All rights reserved.
7  *
8  * This code is derived from software contributed to Berkeley by
9  * Jan-Simon Pendry.
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted provided that the following conditions
13  * are met:
14  * 1. Redistributions of source code must retain the above copyright
15  *    notice, this list of conditions and the following disclaimer.
16  * 2. Redistributions in binary form must reproduce the above copyright
17  *    notice, this list of conditions and the following disclaimer in the
18  *    documentation and/or other materials provided with the distribution.
19  * 3. Neither the name of the University nor the names of its contributors
20  *    may be used to endorse or promote products derived from this software
21  *    without specific prior written permission.
22  *
23  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33  * SUCH DAMAGE.
34  */
35 #ifdef HAVE_CONFIG_H
36 # include <config.h>
37 #endif
38 #ifndef HAVE_REALPATH
39 
40 #include <sys/cdefs.h>
41 #include <sys/param.h>
42 #include <sys/stat.h>
43 
44 #include <errno.h>
45 #ifdef HAVE_STDLIB_H
46 # include <stdlib.h>
47 #endif
48 #ifdef HAVE_STRING_H
49 # include <string.h>
50 #endif
51 #ifdef HAVE_UNISTD_H
52 # include <unistd.h>
53 #endif
54 
55 #ifndef __restrict
56 # define __restrict /* restrict */
57 #endif
58 
59 /*
60  * char *realpath(const char *path, char *resolved);
61  *
62  * Find the real name of path, by removing all ".", ".." and symlink
63  * components.  Returns (resolved) on success, or (NULL) on failure,
64  * in which case the path which caused trouble is left in (resolved).
65  */
66 char *
realpath(const char * __restrict path,char * __restrict resolved)67 realpath(const char * __restrict path, char * __restrict resolved)
68 {
69           struct stat sb;
70           int idx = 0, nlnk = 0;
71           const char *q;
72           char *p, wbuf[2][MAXPATHLEN], *fres;
73           size_t len;
74           ssize_t n;
75 
76           /* POSIX sez we must test for this */
77           if (path == NULL) {
78                     errno = EINVAL;
79                     return NULL;
80           }
81 
82           if (resolved == NULL) {
83                     fres = resolved = malloc(MAXPATHLEN);
84                     if (resolved == NULL)
85                               return NULL;
86           } else
87                     fres = NULL;
88 
89 
90           /*
91            * Build real path one by one with paying an attention to .,
92            * .. and symbolic link.
93            */
94 
95           /*
96            * `p' is where we'll put a new component with prepending
97            * a delimiter.
98            */
99           p = resolved;
100 
101           if (*path == '\0') {
102                     *p = '\0';
103                     errno = ENOENT;
104                     goto out;
105           }
106 
107           /* If relative path, start from current working directory. */
108           if (*path != '/') {
109                     /* check for resolved pointer to appease coverity */
110                     if (resolved && getcwd(resolved, MAXPATHLEN) == NULL) {
111                               p[0] = '.';
112                               p[1] = '\0';
113                               goto out;
114                     }
115                     len = strlen(resolved);
116                     if (len > 1)
117                               p += len;
118           }
119 
120 loop:
121           /* Skip any slash. */
122           while (*path == '/')
123                     path++;
124 
125           if (*path == '\0') {
126                     if (p == resolved)
127                               *p++ = '/';
128                     *p = '\0';
129                     return resolved;
130           }
131 
132           /* Find the end of this component. */
133           q = path;
134           do
135                     q++;
136           while (*q != '/' && *q != '\0');
137 
138           /* Test . or .. */
139           if (path[0] == '.') {
140                     if (q - path == 1) {
141                               path = q;
142                               goto loop;
143                     }
144                     if (path[1] == '.' && q - path == 2) {
145                               /* Trim the last component. */
146                               if (p != resolved)
147                                         while (*--p != '/')
148                                                   continue;
149                               path = q;
150                               goto loop;
151                     }
152           }
153 
154           /* Append this component. */
155           if (p - resolved + 1 + q - path + 1 > MAXPATHLEN) {
156                     errno = ENAMETOOLONG;
157                     if (p == resolved)
158                               *p++ = '/';
159                     *p = '\0';
160                     goto out;
161           }
162           p[0] = '/';
163           memcpy(&p[1], path,
164               /* LINTED We know q > path. */
165               q - path);
166           p[1 + q - path] = '\0';
167 
168           /*
169            * If this component is a symlink, toss it and prepend link
170            * target to unresolved path.
171            */
172           if (lstat(resolved, &sb) == -1)
173                     goto out;
174 
175           if (S_ISLNK(sb.st_mode)) {
176                     if (nlnk++ >= MAXSYMLINKS) {
177                               errno = ELOOP;
178                               goto out;
179                     }
180                     n = readlink(resolved, wbuf[idx], sizeof(wbuf[0]) - 1);
181                     if (n < 0)
182                               goto out;
183                     if (n == 0) {
184                               errno = ENOENT;
185                               goto out;
186                     }
187 
188                     /* Append unresolved path to link target and switch to it. */
189                     if (n + (len = strlen(q)) + 1 > sizeof(wbuf[0])) {
190                               errno = ENAMETOOLONG;
191                               goto out;
192                     }
193                     memcpy(&wbuf[idx][n], q, len + 1);
194                     path = wbuf[idx];
195                     idx ^= 1;
196 
197                     /* If absolute symlink, start from root. */
198                     if (*path == '/')
199                               p = resolved;
200                     goto loop;
201           }
202           if (*q == '/' && !S_ISDIR(sb.st_mode)) {
203                     errno = ENOTDIR;
204                     goto out;
205           }
206 
207           /* Advance both resolved and unresolved path. */
208           p += 1 + q - path;
209           path = q;
210           goto loop;
211 out:
212           free(fres);
213           return NULL;
214 }
215 #endif
216