getcwd.c revision 1.35 1 /* $NetBSD: getcwd.c,v 1.35 2005/01/23 01:00:51 enami 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.35 2005/01/23 01:00:51 enami 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 /*
65 * char *realpath(const char *path, char resolved_path[MAXPATHLEN]);
66 *
67 * Find the real name of path, by removing all ".", ".." and symlink
68 * components. Returns (resolved) on success, or (NULL) on failure,
69 * in which case the path which caused trouble is left in (resolved).
70 */
71 char *
72 realpath(path, resolved)
73 const char *path;
74 char *resolved;
75 {
76 struct stat sb;
77 int fd, n, rootd, serrno, nlnk = 0;
78 char *p, *q, wbuf[MAXPATHLEN];
79
80 _DIAGASSERT(path != NULL);
81 _DIAGASSERT(resolved != NULL);
82
83 /* Save the starting point. */
84 if ((fd = open(".", O_RDONLY)) < 0) {
85 (void)strlcpy(resolved, ".", MAXPATHLEN);
86 return (NULL);
87 }
88
89 /*
90 * Find the dirname and basename from the path to be resolved.
91 * Change directory to the dirname component.
92 * lstat the basename part.
93 * if it is a symlink, read in the value and loop.
94 * if it is a directory, then change to that directory.
95 * get the current directory name and append the basename.
96 */
97 if (strlcpy(resolved, path, MAXPATHLEN) >= MAXPATHLEN) {
98 errno = ENAMETOOLONG;
99 goto err1;
100 }
101 loop:
102 q = strrchr(resolved, '/');
103 if (q != NULL) {
104 p = q + 1;
105 if (q == resolved)
106 q = "/";
107 else {
108 do {
109 --q;
110 } while (q > resolved && *q == '/');
111 q[1] = '\0';
112 q = resolved;
113 }
114 if (chdir(q) < 0)
115 goto err1;
116 } else
117 p = resolved;
118
119 /* Deal with the last component. */
120 if (lstat(p, &sb) == 0) {
121 if (S_ISLNK(sb.st_mode)) {
122 if (nlnk++ >= MAXSYMLINKS) {
123 errno = ELOOP;
124 goto err1;
125 }
126 n = readlink(p, resolved, MAXPATHLEN-1);
127 if (n < 0)
128 goto err1;
129 resolved[n] = '\0';
130 goto loop;
131 }
132 if (S_ISDIR(sb.st_mode)) {
133 if (chdir(p) < 0)
134 goto err1;
135 p = "";
136 }
137 }
138
139 /*
140 * Save the last component name and get the full pathname of
141 * the current directory.
142 */
143 if (strlcpy(wbuf, p, sizeof(wbuf)) >= sizeof(wbuf)) {
144 errno = ENAMETOOLONG;
145 goto err1;
146 }
147
148 /*
149 * Call the inernal internal version of getcwd which
150 * does a physical search rather than using the $PWD short-cut
151 */
152 if (getcwd(resolved, MAXPATHLEN) == 0)
153 goto err1;
154
155 /*
156 * Join the two strings together, ensuring that the right thing
157 * happens if the last component is empty, or the dirname is root.
158 */
159 if (resolved[0] == '/' && resolved[1] == '\0')
160 rootd = 1;
161 else
162 rootd = 0;
163
164 if (*wbuf) {
165 if (strlen(resolved) + strlen(wbuf) + (rootd ? 0 : 1) + 1 >
166 MAXPATHLEN) {
167 errno = ENAMETOOLONG;
168 goto err1;
169 }
170 if (rootd == 0)
171 if (strlcat(resolved, "/", MAXPATHLEN) >= MAXPATHLEN) {
172 errno = ENAMETOOLONG;
173 goto err1;
174 }
175 if (strlcat(resolved, wbuf, MAXPATHLEN) >= MAXPATHLEN) {
176 errno = ENAMETOOLONG;
177 goto err1;
178 }
179 }
180
181 /* Go back to where we came from. */
182 if (fchdir(fd) < 0) {
183 serrno = errno;
184 goto err2;
185 }
186
187 /* It's okay if the close fails, what's an fd more or less? */
188 (void)close(fd);
189 return (resolved);
190
191 err1: serrno = errno;
192 (void)fchdir(fd);
193 err2: (void)close(fd);
194 errno = serrno;
195 return (NULL);
196 }
197
198 char *
199 getcwd(char *pt, size_t size)
200 {
201 char *npt;
202
203 /*
204 * If a buffer is specified, the size has to be non-zero.
205 */
206 if (pt != NULL) {
207 if (size == 0) {
208 /* __getcwd(pt, 0) results ERANGE. */
209 errno = EINVAL;
210 return (NULL);
211 }
212 if (__getcwd(pt, size) >= 0)
213 return (pt);
214 return (NULL);
215 }
216
217 /*
218 * If no buffer specified by the user, allocate one as necessary.
219 */
220 size = 1024 >> 1;
221 do {
222 if ((npt = realloc(pt, size <<= 1)) == NULL)
223 break;
224 pt = npt;
225 if (__getcwd(pt, size) >= 0)
226 return (pt);
227 } while (size <= MAXPATHLEN * 4 && errno == ERANGE);
228
229 free(pt);
230 return (NULL);
231 }
232