getcwd.c revision 1.33 1 /* $NetBSD: getcwd.c,v 1.33 2005/01/06 00:07:41 christos 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.33 2005/01/06 00:07:41 christos 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, *nup;
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 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 = MAXPATHLEN)) == NULL)
254 goto err;
255 eup = up + upsize;
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 ((nup = realloc(up, upsize *= 2)) == NULL)
297 goto err;
298 bup = nup + (buf - up);
299 up = nup;
300 eup = up + upsize;
301 }
302 *bup++ = '.';
303 *bup++ = '.';
304 *bup = '\0';
305
306 /* Open and stat parent directory. */
307 if (!(dir = opendir(up)) || fstat(dirfd(dir), &s))
308 goto err;
309
310 /* Add trailing slash for next directory. */
311 *bup++ = '/';
312
313 /*
314 * If it's a mount point, have to stat each element because
315 * the inode number in the directory is for the entry in the
316 * parent directory, not the inode number of the mounted file.
317 */
318 save_errno = 0;
319 if (s.st_dev == dev) {
320 for (;;) {
321 if (!(dp = readdir(dir)))
322 goto notfound;
323 if (dp->d_fileno == ino) {
324 #if defined(__SVR4) || defined(__svr4__) || defined(__linux__)
325 dlen = strlen(dp->d_name);
326 #else
327 dlen = dp->d_namlen;
328 #endif
329 break;
330 }
331 }
332 } else
333 for (;;) {
334 if (!(dp = readdir(dir)))
335 goto notfound;
336 if (ISDOT(dp))
337 continue;
338 #if defined(__SVR4) || defined(__svr4__) || defined(__linux__)
339 dlen = strlen(dp->d_name);
340 #else
341 dlen = dp->d_namlen;
342 #endif
343 memmove(bup, dp->d_name, dlen + 1);
344
345 /* Save the first error for later. */
346 if (lstat(up, &s)) {
347 if (!save_errno)
348 save_errno = errno;
349 errno = 0;
350 continue;
351 }
352 if (s.st_dev == dev && s.st_ino == ino)
353 break;
354 }
355
356 /*
357 * Check for length of the current name, preceding slash,
358 * leading slash.
359 */
360 if (bpt - pt <= dlen + (first ? 1 : 2)) {
361 size_t len, off;
362
363 if (!ptsize) {
364 errno = ERANGE;
365 goto err;
366 }
367 off = bpt - pt;
368 len = ept - bpt;
369 if ((pt = realloc(pt, ptsize *= 2)) == NULL)
370 goto err;
371 bpt = pt + off;
372 ept = pt + ptsize;
373 memmove(ept - len, bpt, len);
374 bpt = ept - len;
375 }
376 if (!first)
377 *--bpt = '/';
378 bpt -= dlen;
379 memmove(bpt, dp->d_name, dlen);
380 (void)closedir(dir);
381
382 /* Truncate any file name. */
383 *bup = '\0';
384 }
385
386 notfound:
387 /*
388 * If readdir set errno, use it, not any saved error; otherwise,
389 * didn't find the current directory in its parent directory, set
390 * errno to ENOENT.
391 */
392 if (!errno)
393 errno = save_errno ? save_errno : ENOENT;
394 /* FALLTHROUGH */
395 err:
396 if (ptsize)
397 free(pt);
398 free(up);
399 return (NULL);
400 }
401
402 #else /* New getcwd */
403
404 char *
405 getcwd(pt, size)
406 char *pt;
407 size_t size;
408 {
409 size_t ptsize, bufsize;
410 int len;
411
412 /*
413 * If no buffer specified by the user, allocate one as necessary.
414 * If a buffer is specified, the size has to be non-zero. The path
415 * is built from the end of the buffer backwards.
416 */
417 if (pt) {
418 ptsize = 0;
419 if (!size) {
420 errno = EINVAL;
421 return (NULL);
422 }
423 bufsize = size;
424 } else {
425 if ((pt = malloc(ptsize = 1024 - 4)) == NULL)
426 return (NULL);
427 bufsize = ptsize;
428 }
429 for (;;) {
430 len = __getcwd(pt, bufsize);
431 if ((len < 0) && (size == 0) && (errno == ERANGE)) {
432 if (ptsize > (MAXPATHLEN*4))
433 return NULL;
434 if ((pt = realloc(pt, ptsize *= 2)) == NULL)
435 return NULL;
436 bufsize = ptsize;
437 continue;
438 }
439 break;
440 }
441 if (len < 0)
442 return NULL;
443 else
444 return pt;
445 }
446
447 #endif
448