getcwd.c revision 1.34 1 /* $NetBSD: getcwd.c,v 1.34 2005/01/06 23:43:32 simonb Exp $ */
2
3 /*
4 * Copyright (c) 1989, 1991, 1993, 1995
5 * The Regents of the University of California. All rights reserved.
6 *
7 * This code is derived from software contributed to Berkeley by
8 * Jan-Simon Pendry.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 * 3. Neither the name of the University nor the names of its contributors
19 * may be used to endorse or promote products derived from this software
20 * without specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 * SUCH DAMAGE.
33 */
34
35 #include <sys/cdefs.h>
36 #if defined(LIBC_SCCS) && !defined(lint)
37 #if 0
38 static char sccsid[] = "@(#)getcwd.c 8.5 (Berkeley) 2/7/95";
39 #else
40 __RCSID("$NetBSD: getcwd.c,v 1.34 2005/01/06 23:43:32 simonb Exp $");
41 #endif
42 #endif /* LIBC_SCCS and not lint */
43
44 #include "namespace.h"
45 #include <sys/param.h>
46 #include <sys/stat.h>
47
48 #include <assert.h>
49 #include <dirent.h>
50 #include <errno.h>
51 #include <fcntl.h>
52 #include <stdio.h>
53 #include <stdlib.h>
54 #include <string.h>
55 #include <unistd.h>
56
57 #include "extern.h"
58
59 #ifdef __weak_alias
60 __weak_alias(getcwd,_getcwd)
61 __weak_alias(realpath,_realpath)
62 #endif
63
64 #define ISDOT(dp) \
65 (dp->d_name[0] == '.' && (dp->d_name[1] == '\0' || \
66 (dp->d_name[1] == '.' && dp->d_name[2] == '\0')))
67
68
69 #if defined(__SVR4) || defined(__svr4__)
70 #define d_fileno d_ino
71 #endif
72
73 /*
74 * char *realpath(const char *path, char resolved_path[MAXPATHLEN]);
75 *
76 * Find the real name of path, by removing all ".", ".." and symlink
77 * components. Returns (resolved) on success, or (NULL) on failure,
78 * in which case the path which caused trouble is left in (resolved).
79 */
80 char *
81 realpath(path, resolved)
82 const char *path;
83 char *resolved;
84 {
85 struct stat sb;
86 int fd, n, rootd, serrno, nlnk = 0;
87 char *p, *q, wbuf[MAXPATHLEN];
88
89 _DIAGASSERT(path != NULL);
90 _DIAGASSERT(resolved != NULL);
91
92 /* Save the starting point. */
93 if ((fd = open(".", O_RDONLY)) < 0) {
94 (void)strlcpy(resolved, ".", MAXPATHLEN);
95 return (NULL);
96 }
97
98 /*
99 * Find the dirname and basename from the path to be resolved.
100 * Change directory to the dirname component.
101 * lstat the basename part.
102 * if it is a symlink, read in the value and loop.
103 * if it is a directory, then change to that directory.
104 * get the current directory name and append the basename.
105 */
106 if (strlcpy(resolved, path, MAXPATHLEN) >= MAXPATHLEN) {
107 errno = ENAMETOOLONG;
108 goto err1;
109 }
110 loop:
111 q = strrchr(resolved, '/');
112 if (q != NULL) {
113 p = q + 1;
114 if (q == resolved)
115 q = "/";
116 else {
117 do {
118 --q;
119 } while (q > resolved && *q == '/');
120 q[1] = '\0';
121 q = resolved;
122 }
123 if (chdir(q) < 0)
124 goto err1;
125 } else
126 p = resolved;
127
128 /* Deal with the last component. */
129 if (lstat(p, &sb) == 0) {
130 if (S_ISLNK(sb.st_mode)) {
131 if (nlnk++ >= MAXSYMLINKS) {
132 errno = ELOOP;
133 goto err1;
134 }
135 n = readlink(p, resolved, MAXPATHLEN-1);
136 if (n < 0)
137 goto err1;
138 resolved[n] = '\0';
139 goto loop;
140 }
141 if (S_ISDIR(sb.st_mode)) {
142 if (chdir(p) < 0)
143 goto err1;
144 p = "";
145 }
146 }
147
148 /*
149 * Save the last component name and get the full pathname of
150 * the current directory.
151 */
152 if (strlcpy(wbuf, p, sizeof(wbuf)) >= sizeof(wbuf)) {
153 errno = ENAMETOOLONG;
154 goto err1;
155 }
156
157 /*
158 * Call the inernal internal version of getcwd which
159 * does a physical search rather than using the $PWD short-cut
160 */
161 if (getcwd(resolved, MAXPATHLEN) == 0)
162 goto err1;
163
164 /*
165 * Join the two strings together, ensuring that the right thing
166 * happens if the last component is empty, or the dirname is root.
167 */
168 if (resolved[0] == '/' && resolved[1] == '\0')
169 rootd = 1;
170 else
171 rootd = 0;
172
173 if (*wbuf) {
174 if (strlen(resolved) + strlen(wbuf) + (rootd ? 0 : 1) + 1 >
175 MAXPATHLEN) {
176 errno = ENAMETOOLONG;
177 goto err1;
178 }
179 if (rootd == 0)
180 if (strlcat(resolved, "/", MAXPATHLEN) >= MAXPATHLEN) {
181 errno = ENAMETOOLONG;
182 goto err1;
183 }
184 if (strlcat(resolved, wbuf, MAXPATHLEN) >= MAXPATHLEN) {
185 errno = ENAMETOOLONG;
186 goto err1;
187 }
188 }
189
190 /* Go back to where we came from. */
191 if (fchdir(fd) < 0) {
192 serrno = errno;
193 goto err2;
194 }
195
196 /* It's okay if the close fails, what's an fd more or less? */
197 (void)close(fd);
198 return (resolved);
199
200 err1: serrno = errno;
201 (void)fchdir(fd);
202 err2: (void)close(fd);
203 errno = serrno;
204 return (NULL);
205 }
206
207 char *
208 getcwd(pt, size)
209 char *pt;
210 size_t size;
211 {
212 size_t ptsize, bufsize;
213 int len;
214
215 /*
216 * If no buffer specified by the user, allocate one as necessary.
217 * If a buffer is specified, the size has to be non-zero. The path
218 * is built from the end of the buffer backwards.
219 */
220 if (pt) {
221 ptsize = 0;
222 if (!size) {
223 errno = EINVAL;
224 return (NULL);
225 }
226 bufsize = size;
227 } else {
228 if ((pt = malloc(ptsize = 1024 - 4)) == NULL)
229 return (NULL);
230 bufsize = ptsize;
231 }
232 for (;;) {
233 len = __getcwd(pt, bufsize);
234 if ((len < 0) && (size == 0) && (errno == ERANGE)) {
235 if (ptsize > (MAXPATHLEN*4))
236 return NULL;
237 if ((pt = realloc(pt, ptsize *= 2)) == NULL)
238 return NULL;
239 bufsize = ptsize;
240 continue;
241 }
242 break;
243 }
244 if (len < 0)
245 return NULL;
246 else
247 return pt;
248 }
249