old_getcwd.c revision 1.1 1
2 /* $NetBSD: old_getcwd.c,v 1.1 1999/03/22 18:14:39 sommerfe Exp $ */
3
4 /*
5 * Copyright (c) 1989, 1991, 1993, 1995
6 * The Regents of the University of California. All rights reserved.
7 *
8 * This code is derived from software contributed to Berkeley by
9 * Jan-Simon Pendry.
10 *
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
19 * 3. All advertising materials mentioning features or use of this software
20 * must display the following acknowledgement:
21 * This product includes software developed by the University of
22 * California, Berkeley and its contributors.
23 * 4. Neither the name of the University nor the names of its contributors
24 * may be used to endorse or promote products derived from this software
25 * without specific prior written permission.
26 *
27 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
28 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
29 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
30 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
31 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
32 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
33 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
34 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
35 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
36 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
37 * SUCH DAMAGE.
38 */
39
40 /*
41 * This file copied from NetBSD's src/lib/libc/gen/getcwd.c 1.15, and edited
42 * to remove namespace stuff and the unused realpath function.
43 */
44
45
46 #include <sys/cdefs.h>
47 #if defined(LIBC_SCCS) && !defined(lint)
48 #if 0
49 static char sccsid[] = "@(#)getcwd.c 8.5 (Berkeley) 2/7/95";
50 #else
51 __RCSID("$NetBSD: old_getcwd.c,v 1.1 1999/03/22 18:14:39 sommerfe Exp $");
52 #endif
53 #endif /* LIBC_SCCS and not lint */
54
55 #include <sys/param.h>
56 #include <sys/stat.h>
57
58 #include <dirent.h>
59 #include <errno.h>
60 #include <fcntl.h>
61 #include <stdio.h>
62 #include <stdlib.h>
63 #include <string.h>
64 #include <unistd.h>
65
66 #include "getcwd.h"
67
68 #define ISDOT(dp) \
69 (dp->d_name[0] == '.' && (dp->d_name[1] == '\0' || \
70 (dp->d_name[1] == '.' && dp->d_name[2] == '\0')))
71
72
73 #if defined(__SVR4) || defined(__svr4__)
74 #define d_fileno d_ino
75 #endif
76
77 char *
78 old_getcwd(pt, size)
79 char *pt;
80 size_t size;
81 {
82 struct dirent *dp;
83 DIR *dir;
84 dev_t dev;
85 ino_t ino;
86 int first;
87 char *bpt, *bup;
88 struct stat s;
89 dev_t root_dev;
90 ino_t root_ino;
91 size_t ptsize, upsize;
92 int save_errno;
93 char *ept, *eup, *up;
94 size_t dlen;
95
96 /*
97 * If no buffer specified by the user, allocate one as necessary.
98 * If a buffer is specified, the size has to be non-zero. The path
99 * is built from the end of the buffer backwards.
100 */
101 if (pt) {
102 ptsize = 0;
103 if (!size) {
104 errno = EINVAL;
105 return (NULL);
106 }
107 ept = pt + size;
108 } else {
109 if ((pt = malloc(ptsize = 1024 - 4)) == NULL)
110 return (NULL);
111 ept = pt + ptsize;
112 }
113 bpt = ept - 1;
114 *bpt = '\0';
115
116 /*
117 * Allocate bytes (1024 - malloc space) for the string of "../"'s.
118 * Should always be enough (it's 340 levels). If it's not, allocate
119 * as necessary. Special case the first stat, it's ".", not "..".
120 */
121 if ((up = malloc(upsize = 1024 - 4)) == NULL)
122 goto err;
123 eup = up + MAXPATHLEN;
124 bup = up;
125 up[0] = '.';
126 up[1] = '\0';
127
128 /* Save root values, so know when to stop. */
129 if (stat("/", &s))
130 goto err;
131 root_dev = s.st_dev;
132 root_ino = s.st_ino;
133
134 errno = 0; /* XXX readdir has no error return. */
135
136 for (first = 1;; first = 0) {
137 /* Stat the current level. */
138 if (lstat(up, &s))
139 goto err;
140
141 /* Save current node values. */
142 ino = s.st_ino;
143 dev = s.st_dev;
144
145 /* Check for reaching root. */
146 if (root_dev == dev && root_ino == ino) {
147 *--bpt = '/';
148 /*
149 * It's unclear that it's a requirement to copy the
150 * path to the beginning of the buffer, but it's always
151 * been that way and stuff would probably break.
152 */
153 memmove(pt, bpt, (size_t)(ept - bpt));
154 free(up);
155 return (pt);
156 }
157
158 /*
159 * Build pointer to the parent directory, allocating memory
160 * as necessary. Max length is 3 for "../", the largest
161 * possible component name, plus a trailing NULL.
162 */
163 if (bup + 3 + MAXNAMLEN + 1 >= eup) {
164 if ((up = realloc(up, upsize *= 2)) == NULL)
165 goto err;
166 bup = up;
167 eup = up + upsize;
168 }
169 *bup++ = '.';
170 *bup++ = '.';
171 *bup = '\0';
172
173 /* Open and stat parent directory. */
174 if (!(dir = opendir(up)) || fstat(dirfd(dir), &s))
175 goto err;
176
177 /* Add trailing slash for next directory. */
178 *bup++ = '/';
179
180 /*
181 * If it's a mount point, have to stat each element because
182 * the inode number in the directory is for the entry in the
183 * parent directory, not the inode number of the mounted file.
184 */
185 save_errno = 0;
186 if (s.st_dev == dev) {
187 for (;;) {
188 if (!(dp = readdir(dir)))
189 goto notfound;
190 if (dp->d_fileno == ino) {
191 #if defined(__SVR4) || defined(__svr4__)
192 dlen = strlen(dp->d_name);
193 #else
194 dlen = dp->d_namlen;
195 #endif
196 break;
197 }
198 }
199 } else
200 for (;;) {
201 if (!(dp = readdir(dir)))
202 goto notfound;
203 if (ISDOT(dp))
204 continue;
205 #if defined(__SVR4) || defined(__svr4__)
206 dlen = strlen(dp->d_name);
207 #else
208 dlen = dp->d_namlen;
209 #endif
210 memmove(bup, dp->d_name, dlen + 1);
211
212 /* Save the first error for later. */
213 if (lstat(up, &s)) {
214 if (!save_errno)
215 save_errno = errno;
216 errno = 0;
217 continue;
218 }
219 if (s.st_dev == dev && s.st_ino == ino)
220 break;
221 }
222
223 /*
224 * Check for length of the current name, preceding slash,
225 * leading slash.
226 */
227 if (bpt - pt <= dlen + (first ? 1 : 2)) {
228 size_t len, off;
229
230 if (!ptsize) {
231 errno = ERANGE;
232 goto err;
233 }
234 off = bpt - pt;
235 len = ept - bpt;
236 if ((pt = realloc(pt, ptsize *= 2)) == NULL)
237 goto err;
238 bpt = pt + off;
239 ept = pt + ptsize;
240 memmove(ept - len, bpt, len);
241 bpt = ept - len;
242 }
243 if (!first)
244 *--bpt = '/';
245 bpt -= dlen;
246 memmove(bpt, dp->d_name, dlen);
247 (void)closedir(dir);
248
249 /* Truncate any file name. */
250 *bup = '\0';
251 }
252
253 notfound:
254 /*
255 * If readdir set errno, use it, not any saved error; otherwise,
256 * didn't find the current directory in its parent directory, set
257 * errno to ENOENT.
258 */
259 if (!errno)
260 errno = save_errno ? save_errno : ENOENT;
261 /* FALLTHROUGH */
262 err:
263 if (ptsize)
264 free(pt);
265 free(up);
266 return (NULL);
267 }
268