Annotation of src/lib/libc/gen/getcwd.c, Revision 1.32
1.32 ! agc 1: /* $NetBSD: getcwd.c,v 1.31 2003/08/03 04:35:50 itojun Exp $ */
1.4 cgd 2:
1.1 cgd 3: /*
1.8 perry 4: * Copyright (c) 1989, 1991, 1993, 1995
1.4 cgd 5: * The Regents of the University of California. All rights reserved.
1.1 cgd 6: *
1.8 perry 7: * This code is derived from software contributed to Berkeley by
8: * Jan-Simon Pendry.
9: *
1.1 cgd 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.
1.32 ! agc 18: * 3. Neither the name of the University nor the names of its contributors
1.1 cgd 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:
1.6 christos 35: #include <sys/cdefs.h>
1.1 cgd 36: #if defined(LIBC_SCCS) && !defined(lint)
1.4 cgd 37: #if 0
1.8 perry 38: static char sccsid[] = "@(#)getcwd.c 8.5 (Berkeley) 2/7/95";
1.4 cgd 39: #else
1.32 ! agc 40: __RCSID("$NetBSD: getcwd.c,v 1.31 2003/08/03 04:35:50 itojun Exp $");
1.4 cgd 41: #endif
1.1 cgd 42: #endif /* LIBC_SCCS and not lint */
43:
1.7 jtc 44: #include "namespace.h"
1.1 cgd 45: #include <sys/param.h>
46: #include <sys/stat.h>
1.8 perry 47:
1.22 lukem 48: #include <assert.h>
1.8 perry 49: #include <dirent.h>
1.1 cgd 50: #include <errno.h>
1.8 perry 51: #include <fcntl.h>
1.1 cgd 52: #include <stdio.h>
53: #include <stdlib.h>
54: #include <string.h>
55: #include <unistd.h>
1.17 sommerfe 56:
57: #include "extern.h"
1.7 jtc 58:
59: #ifdef __weak_alias
1.24 mycroft 60: __weak_alias(getcwd,_getcwd)
61: __weak_alias(realpath,_realpath)
1.7 jtc 62: #endif
1.1 cgd 63:
64: #define ISDOT(dp) \
65: (dp->d_name[0] == '.' && (dp->d_name[1] == '\0' || \
1.6 christos 66: (dp->d_name[1] == '.' && dp->d_name[2] == '\0')))
1.1 cgd 67:
1.8 perry 68:
1.15 christos 69: #if defined(__SVR4) || defined(__svr4__)
70: #define d_fileno d_ino
71: #endif
72:
1.8 perry 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;
1.21 fvdl 86: int fd, n, rootd, serrno, nlnk = 0;
1.8 perry 87: char *p, *q, wbuf[MAXPATHLEN];
1.22 lukem 88:
89: _DIAGASSERT(path != NULL);
90: _DIAGASSERT(resolved != NULL);
1.8 perry 91:
92: /* Save the starting point. */
93: if ((fd = open(".", O_RDONLY)) < 0) {
1.28 itojun 94: (void)strlcpy(resolved, ".", MAXPATHLEN);
1.8 perry 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: */
1.31 itojun 106: if (strlcpy(resolved, path, MAXPATHLEN) >= MAXPATHLEN) {
107: errno = ENAMETOOLONG;
108: goto err1;
109: }
1.8 perry 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)) {
1.21 fvdl 131: if (nlnk++ >= MAXSYMLINKS) {
132: errno = ELOOP;
133: goto err1;
134: }
1.26 provos 135: n = readlink(p, resolved, MAXPATHLEN-1);
1.8 perry 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: */
1.31 itojun 152: if (strlcpy(wbuf, p, sizeof(wbuf)) >= sizeof(wbuf)) {
153: errno = ENAMETOOLONG;
154: goto err1;
155: }
1.8 perry 156:
157: /*
158: * Call the inernal internal version of getcwd which
159: * does a physical search rather than using the $PWD short-cut
160: */
1.12 lukem 161: if (getcwd(resolved, MAXPATHLEN) == 0)
1.8 perry 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) {
1.29 itojun 174: if (strlen(resolved) + strlen(wbuf) + (rootd ? 0 : 1) + 1 >
175: MAXPATHLEN) {
1.8 perry 176: errno = ENAMETOOLONG;
177: goto err1;
178: }
179: if (rootd == 0)
1.31 itojun 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: }
1.8 perry 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:
1.16 sommerfe 207: #ifdef OLD_GETCWD
208:
1.12 lukem 209: char *
210: getcwd(pt, size)
1.8 perry 211: char *pt;
212: size_t size;
213: {
1.10 perry 214: struct dirent *dp;
215: DIR *dir;
216: dev_t dev;
217: ino_t ino;
218: int first;
219: char *bpt, *bup;
1.1 cgd 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;
1.15 christos 226: size_t dlen;
1.1 cgd 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;
1.4 cgd 237: return (NULL);
1.1 cgd 238: }
239: ept = pt + size;
240: } else {
1.4 cgd 241: if ((pt = malloc(ptsize = 1024 - 4)) == NULL)
242: return (NULL);
1.1 cgd 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
1.8 perry 251: * as necessary. Special case the first stat, it's ".", not "..".
1.1 cgd 252: */
1.4 cgd 253: if ((up = malloc(upsize = 1024 - 4)) == NULL)
1.1 cgd 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: */
1.14 perry 285: memmove(pt, bpt, (size_t)(ept - bpt));
1.1 cgd 286: free(up);
1.4 cgd 287: return (pt);
1.1 cgd 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) {
1.4 cgd 296: if ((up = realloc(up, upsize *= 2)) == NULL)
1.1 cgd 297: goto err;
1.4 cgd 298: bup = up;
1.1 cgd 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;
1.15 christos 322: if (dp->d_fileno == ino) {
1.19 christos 323: #if defined(__SVR4) || defined(__svr4__) || defined(__linux__)
1.15 christos 324: dlen = strlen(dp->d_name);
325: #else
326: dlen = dp->d_namlen;
327: #endif
1.1 cgd 328: break;
1.15 christos 329: }
1.1 cgd 330: }
331: } else
332: for (;;) {
333: if (!(dp = readdir(dir)))
334: goto notfound;
335: if (ISDOT(dp))
336: continue;
1.19 christos 337: #if defined(__SVR4) || defined(__svr4__) || defined(__linux__)
1.15 christos 338: dlen = strlen(dp->d_name);
339: #else
340: dlen = dp->d_namlen;
341: #endif
342: memmove(bup, dp->d_name, dlen + 1);
1.1 cgd 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: */
1.15 christos 359: if (bpt - pt <= dlen + (first ? 1 : 2)) {
1.1 cgd 360: size_t len, off;
361:
362: if (!ptsize) {
363: errno = ERANGE;
364: goto err;
365: }
366: off = bpt - pt;
367: len = ept - bpt;
1.4 cgd 368: if ((pt = realloc(pt, ptsize *= 2)) == NULL)
1.1 cgd 369: goto err;
370: bpt = pt + off;
371: ept = pt + ptsize;
1.14 perry 372: memmove(ept - len, bpt, len);
1.1 cgd 373: bpt = ept - len;
374: }
375: if (!first)
376: *--bpt = '/';
1.15 christos 377: bpt -= dlen;
378: memmove(bpt, dp->d_name, dlen);
1.1 cgd 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);
1.4 cgd 398: return (NULL);
1.1 cgd 399: }
1.16 sommerfe 400:
401: #else /* New getcwd */
402:
403: char *
404: getcwd(pt, size)
405: char *pt;
406: size_t size;
407: {
1.18 christos 408: size_t ptsize, bufsize;
409: int len;
1.16 sommerfe 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: }
1.18 christos 428: for (;;) {
1.16 sommerfe 429: len = __getcwd(pt, bufsize);
430: if ((len < 0) && (size == 0) && (errno == ERANGE)) {
1.20 sommerfe 431: if (ptsize > (MAXPATHLEN*4))
432: return NULL;
1.16 sommerfe 433: if ((pt = realloc(pt, ptsize *= 2)) == NULL)
434: return NULL;
435: bufsize = ptsize;
436: continue;
437: }
1.18 christos 438: break;
439: }
1.16 sommerfe 440: if (len < 0)
441: return NULL;
442: else
443: return pt;
444: }
445:
446: #endif
CVSweb <webmaster@jp.NetBSD.org>