getcwd.c revision 1.32 1 /* $NetBSD: getcwd.c,v 1.32 2003/08/07 16:42:49 agc 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.32 2003/08/07 16:42:49 agc 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 #ifdef OLD_GETCWD
208
209 char *
210 getcwd(pt, size)
211 char *pt;
212 size_t size;
213 {
214 struct dirent *dp;
215 DIR *dir;
216 dev_t dev;
217 ino_t ino;
218 int first;
219 char *bpt, *bup;
220 struct stat s;
221 dev_t root_dev;
222 ino_t root_ino;
223 size_t ptsize, upsize;
224 int save_errno;
225 char *ept, *eup, *up;
226 size_t dlen;
227
228 /*
229 * If no buffer specified by the user, allocate one as necessary.
230 * If a buffer is specified, the size has to be non-zero. The path
231 * is built from the end of the buffer backwards.
232 */
233 if (pt) {
234 ptsize = 0;
235 if (!size) {
236 errno = EINVAL;
237 return (NULL);
238 }
239 ept = pt + size;
240 } else {
241 if ((pt = malloc(ptsize = 1024 - 4)) == NULL)
242 return (NULL);
243 ept = pt + ptsize;
244 }
245 bpt = ept - 1;
246 *bpt = '\0';
247
248 /*
249 * Allocate bytes (1024 - malloc space) for the string of "../"'s.
250 * Should always be enough (it's 340 levels). If it's not, allocate
251 * as necessary. Special case the first stat, it's ".", not "..".
252 */
253 if ((up = malloc(upsize = 1024 - 4)) == NULL)
254 goto err;
255 eup = up + MAXPATHLEN;
256 bup = up;
257 up[0] = '.';
258 up[1] = '\0';
259
260 /* Save root values, so know when to stop. */
261 if (stat("/", &s))
262 goto err;
263 root_dev = s.st_dev;
264 root_ino = s.st_ino;
265
266 errno = 0; /* XXX readdir has no error return. */
267
268 for (first = 1;; first = 0) {
269 /* Stat the current level. */
270 if (lstat(up, &s))
271 goto err;
272
273 /* Save current node values. */
274 ino = s.st_ino;
275 dev = s.st_dev;
276
277 /* Check for reaching root. */
278 if (root_dev == dev && root_ino == ino) {
279 *--bpt = '/';
280 /*
281 * It's unclear that it's a requirement to copy the
282 * path to the beginning of the buffer, but it's always
283 * been that way and stuff would probably break.
284 */
285 memmove(pt, bpt, (size_t)(ept - bpt));
286 free(up);
287 return (pt);
288 }
289
290 /*
291 * Build pointer to the parent directory, allocating memory
292 * as necessary. Max length is 3 for "../", the largest
293 * possible component name, plus a trailing NULL.
294 */
295 if (bup + 3 + MAXNAMLEN + 1 >= eup) {
296 if ((up = realloc(up, upsize *= 2)) == NULL)
297 goto err;
298 bup = up;
299 eup = up + upsize;
300 }
301 *bup++ = '.';
302 *bup++ = '.';
303 *bup = '\0';
304
305 /* Open and stat parent directory. */
306 if (!(dir = opendir(up)) || fstat(dirfd(dir), &s))
307 goto err;
308
309 /* Add trailing slash for next directory. */
310 *bup++ = '/';
311
312 /*
313 * If it's a mount point, have to stat each element because
314 * the inode number in the directory is for the entry in the
315 * parent directory, not the inode number of the mounted file.
316 */
317 save_errno = 0;
318 if (s.st_dev == dev) {
319 for (;;) {
320 if (!(dp = readdir(dir)))
321 goto notfound;
322 if (dp->d_fileno == ino) {
323 #if defined(__SVR4) || defined(__svr4__) || defined(__linux__)
324 dlen = strlen(dp->d_name);
325 #else
326 dlen = dp->d_namlen;
327 #endif
328 break;
329 }
330 }
331 } else
332 for (;;) {
333 if (!(dp = readdir(dir)))
334 goto notfound;
335 if (ISDOT(dp))
336 continue;
337 #if defined(__SVR4) || defined(__svr4__) || defined(__linux__)
338 dlen = strlen(dp->d_name);
339 #else
340 dlen = dp->d_namlen;
341 #endif
342 memmove(bup, dp->d_name, dlen + 1);
343
344 /* Save the first error for later. */
345 if (lstat(up, &s)) {
346 if (!save_errno)
347 save_errno = errno;
348 errno = 0;
349 continue;
350 }
351 if (s.st_dev == dev && s.st_ino == ino)
352 break;
353 }
354
355 /*
356 * Check for length of the current name, preceding slash,
357 * leading slash.
358 */
359 if (bpt - pt <= dlen + (first ? 1 : 2)) {
360 size_t len, off;
361
362 if (!ptsize) {
363 errno = ERANGE;
364 goto err;
365 }
366 off = bpt - pt;
367 len = ept - bpt;
368 if ((pt = realloc(pt, ptsize *= 2)) == NULL)
369 goto err;
370 bpt = pt + off;
371 ept = pt + ptsize;
372 memmove(ept - len, bpt, len);
373 bpt = ept - len;
374 }
375 if (!first)
376 *--bpt = '/';
377 bpt -= dlen;
378 memmove(bpt, dp->d_name, dlen);
379 (void)closedir(dir);
380
381 /* Truncate any file name. */
382 *bup = '\0';
383 }
384
385 notfound:
386 /*
387 * If readdir set errno, use it, not any saved error; otherwise,
388 * didn't find the current directory in its parent directory, set
389 * errno to ENOENT.
390 */
391 if (!errno)
392 errno = save_errno ? save_errno : ENOENT;
393 /* FALLTHROUGH */
394 err:
395 if (ptsize)
396 free(pt);
397 free(up);
398 return (NULL);
399 }
400
401 #else /* New getcwd */
402
403 char *
404 getcwd(pt, size)
405 char *pt;
406 size_t size;
407 {
408 size_t ptsize, bufsize;
409 int len;
410
411 /*
412 * If no buffer specified by the user, allocate one as necessary.
413 * If a buffer is specified, the size has to be non-zero. The path
414 * is built from the end of the buffer backwards.
415 */
416 if (pt) {
417 ptsize = 0;
418 if (!size) {
419 errno = EINVAL;
420 return (NULL);
421 }
422 bufsize = size;
423 } else {
424 if ((pt = malloc(ptsize = 1024 - 4)) == NULL)
425 return (NULL);
426 bufsize = ptsize;
427 }
428 for (;;) {
429 len = __getcwd(pt, bufsize);
430 if ((len < 0) && (size == 0) && (errno == ERANGE)) {
431 if (ptsize > (MAXPATHLEN*4))
432 return NULL;
433 if ((pt = realloc(pt, ptsize *= 2)) == NULL)
434 return NULL;
435 bufsize = ptsize;
436 continue;
437 }
438 break;
439 }
440 if (len < 0)
441 return NULL;
442 else
443 return pt;
444 }
445
446 #endif
447