source: trunk/essentials/app-arch/cpio/lib/getcwd.c

Last change on this file was 3340, checked in by bird, 18 years ago

use the kLIBC getcwd.

File size: 10.2 KB
Line 
1/* Copyright (C) 1991,92,93,94,95,96,97,98,99,2004,2005,2006 Free Software
2 Foundation, Inc.
3 This file is part of the GNU C Library.
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2, or (at your option)
8 any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License along
16 with this program; if not, write to the Free Software Foundation,
17 Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
18
19#ifndef __KLIBC__ /* we really don't need this, esp. since it doesn't return the right value anyway. */
20
21#if !_LIBC
22# include <config.h>
23# include "getcwd.h"
24#endif
25
26#include <errno.h>
27#include <sys/types.h>
28#include <sys/stat.h>
29#include <stdbool.h>
30#include <stddef.h>
31
32#include <fcntl.h> /* For AT_FDCWD on Solaris 9. */
33
34#ifndef __set_errno
35# define __set_errno(val) (errno = (val))
36#endif
37
38#include <dirent.h>
39#ifndef _D_EXACT_NAMLEN
40# define _D_EXACT_NAMLEN(d) strlen ((d)->d_name)
41#endif
42#ifndef _D_ALLOC_NAMLEN
43# define _D_ALLOC_NAMLEN(d) (_D_EXACT_NAMLEN (d) + 1)
44#endif
45
46#include <unistd.h>
47#include <stdlib.h>
48#include <string.h>
49
50#if _LIBC
51# ifndef mempcpy
52# define mempcpy __mempcpy
53# endif
54#else
55# include "mempcpy.h"
56#endif
57
58#include <limits.h>
59
60/* Work around a bug in Solaris 9 and 10: AT_FDCWD is positive. Its
61 value exceeds INT_MAX, so its use as an int doesn't conform to the
62 C standard, and GCC and Sun C complain in some cases. */
63#if 0 < AT_FDCWD && AT_FDCWD == 0xffd19553
64# undef AT_FDCWD
65# define AT_FDCWD (-3041965)
66#endif
67
68#ifdef ENAMETOOLONG
69# define is_ENAMETOOLONG(x) ((x) == ENAMETOOLONG)
70#else
71# define is_ENAMETOOLONG(x) 0
72#endif
73
74#ifndef MAX
75# define MAX(a, b) ((a) < (b) ? (b) : (a))
76#endif
77#ifndef MIN
78# define MIN(a, b) ((a) < (b) ? (a) : (b))
79#endif
80
81#ifndef PATH_MAX
82# ifdef MAXPATHLEN
83# define PATH_MAX MAXPATHLEN
84# else
85# define PATH_MAX 1024
86# endif
87#endif
88
89#if D_INO_IN_DIRENT
90# define MATCHING_INO(dp, ino) ((dp)->d_ino == (ino))
91#else
92# define MATCHING_INO(dp, ino) true
93#endif
94
95#if !_LIBC
96# define __getcwd getcwd
97# define __lstat lstat
98# define __closedir closedir
99# define __opendir opendir
100# define __readdir readdir
101#endif
102
103
104/* Get the name of the current working directory, and put it in SIZE
105 bytes of BUF. Returns NULL if the directory couldn't be determined or
106 SIZE was too small. If successful, returns BUF. In GNU, if BUF is
107 NULL, an array is allocated with `malloc'; the array is SIZE bytes long,
108 unless SIZE == 0, in which case it is as big as necessary. */
109
110char *
111__getcwd (char *buf, size_t size)
112{
113 /* Lengths of big file name components and entire file names, and a
114 deep level of file name nesting. These numbers are not upper
115 bounds; they are merely large values suitable for initial
116 allocations, designed to be large enough for most real-world
117 uses. */
118 enum
119 {
120 BIG_FILE_NAME_COMPONENT_LENGTH = 255,
121 BIG_FILE_NAME_LENGTH = MIN (4095, PATH_MAX - 1),
122 DEEP_NESTING = 100
123 };
124
125#ifdef AT_FDCWD
126 int fd = AT_FDCWD;
127 bool fd_needs_closing = false;
128#else
129 char dots[DEEP_NESTING * sizeof ".." + BIG_FILE_NAME_COMPONENT_LENGTH + 1];
130 char *dotlist = dots;
131 size_t dotsize = sizeof dots;
132 size_t dotlen = 0;
133#endif
134 DIR *dirstream = NULL;
135 dev_t rootdev, thisdev;
136 ino_t rootino, thisino;
137 char *dir;
138 register char *dirp;
139 struct stat st;
140 size_t allocated = size;
141 size_t used;
142
143#if HAVE_PARTLY_WORKING_GETCWD && !defined AT_FDCWD
144 /* The system getcwd works, except it sometimes fails when it
145 shouldn't, setting errno to ERANGE, ENAMETOOLONG, or ENOENT. If
146 AT_FDCWD is not defined, the algorithm below is O(N**2) and this
147 is much slower than the system getcwd (at least on GNU/Linux).
148 So trust the system getcwd's results unless they look
149 suspicious. */
150# undef getcwd
151 dir = getcwd (buf, size);
152 if (dir || (errno != ERANGE && !is_ENAMETOOLONG (errno) && errno != ENOENT))
153 return dir;
154#endif
155
156 if (size == 0)
157 {
158 if (buf != NULL)
159 {
160 __set_errno (EINVAL);
161 return NULL;
162 }
163
164 allocated = BIG_FILE_NAME_LENGTH + 1;
165 }
166
167 if (buf == NULL)
168 {
169 dir = malloc (allocated);
170 if (dir == NULL)
171 return NULL;
172 }
173 else
174 dir = buf;
175
176 dirp = dir + allocated;
177 *--dirp = '\0';
178
179 if (__lstat (".", &st) < 0)
180 goto lose;
181 thisdev = st.st_dev;
182 thisino = st.st_ino;
183
184 if (__lstat ("/", &st) < 0)
185 goto lose;
186 rootdev = st.st_dev;
187 rootino = st.st_ino;
188
189 while (!(thisdev == rootdev && thisino == rootino))
190 {
191 struct dirent *d;
192 dev_t dotdev;
193 ino_t dotino;
194 bool mount_point;
195 int parent_status;
196 size_t dirroom;
197 size_t namlen;
198 bool use_d_ino = true;
199
200 /* Look at the parent directory. */
201#ifdef AT_FDCWD
202 fd = openat (fd, "..", O_RDONLY);
203 if (fd < 0)
204 goto lose;
205 fd_needs_closing = true;
206 parent_status = fstat (fd, &st);
207#else
208 dotlist[dotlen++] = '.';
209 dotlist[dotlen++] = '.';
210 dotlist[dotlen] = '\0';
211 parent_status = __lstat (dotlist, &st);
212#endif
213 if (parent_status != 0)
214 goto lose;
215
216 if (dirstream && __closedir (dirstream) != 0)
217 {
218 dirstream = NULL;
219 goto lose;
220 }
221
222 /* Figure out if this directory is a mount point. */
223 dotdev = st.st_dev;
224 dotino = st.st_ino;
225 mount_point = dotdev != thisdev;
226
227 /* Search for the last directory. */
228#ifdef AT_FDCWD
229 dirstream = fdopendir (fd);
230 if (dirstream == NULL)
231 goto lose;
232 fd_needs_closing = false;
233#else
234 dirstream = __opendir (dotlist);
235 if (dirstream == NULL)
236 goto lose;
237 dotlist[dotlen++] = '/';
238#endif
239 for (;;)
240 {
241 /* Clear errno to distinguish EOF from error if readdir returns
242 NULL. */
243 __set_errno (0);
244 d = __readdir (dirstream);
245
246 /* When we've iterated through all directory entries without finding
247 one with a matching d_ino, rewind the stream and consider each
248 name again, but this time, using lstat. This is necessary in a
249 chroot on at least one system (glibc-2.3.6 + linux 2.6.12), where
250 .., ../.., ../../.., etc. all had the same device number, yet the
251 d_ino values for entries in / did not match those obtained
252 via lstat. */
253 if (d == NULL && errno == 0 && use_d_ino)
254 {
255 use_d_ino = false;
256 rewinddir (dirstream);
257 d = __readdir (dirstream);
258 }
259
260 if (d == NULL)
261 {
262 if (errno == 0)
263 /* EOF on dirstream, which can mean e.g., that the current
264 directory has been removed. */
265 __set_errno (ENOENT);
266 goto lose;
267 }
268 if (d->d_name[0] == '.' &&
269 (d->d_name[1] == '\0' ||
270 (d->d_name[1] == '.' && d->d_name[2] == '\0')))
271 continue;
272
273 if (use_d_ino)
274 {
275 bool match = (MATCHING_INO (d, thisino) || mount_point);
276 if (! match)
277 continue;
278 }
279
280 {
281 int entry_status;
282#ifdef AT_FDCWD
283 entry_status = fstatat (fd, d->d_name, &st, AT_SYMLINK_NOFOLLOW);
284#else
285 /* Compute size needed for this file name, or for the file
286 name ".." in the same directory, whichever is larger.
287 Room for ".." might be needed the next time through
288 the outer loop. */
289 size_t name_alloc = _D_ALLOC_NAMLEN (d);
290 size_t filesize = dotlen + MAX (sizeof "..", name_alloc);
291
292 if (filesize < dotlen)
293 goto memory_exhausted;
294
295 if (dotsize < filesize)
296 {
297 /* My, what a deep directory tree you have, Grandma. */
298 size_t newsize = MAX (filesize, dotsize * 2);
299 size_t i;
300 if (newsize < dotsize)
301 goto memory_exhausted;
302 if (dotlist != dots)
303 free (dotlist);
304 dotlist = malloc (newsize);
305 if (dotlist == NULL)
306 goto lose;
307 dotsize = newsize;
308
309 i = 0;
310 do
311 {
312 dotlist[i++] = '.';
313 dotlist[i++] = '.';
314 dotlist[i++] = '/';
315 }
316 while (i < dotlen);
317 }
318
319 memcpy (dotlist + dotlen, d->d_name, _D_ALLOC_NAMLEN (d));
320 entry_status = __lstat (dotlist, &st);
321#endif
322 /* We don't fail here if we cannot stat() a directory entry.
323 This can happen when (network) file systems fail. If this
324 entry is in fact the one we are looking for we will find
325 out soon as we reach the end of the directory without
326 having found anything. */
327 if (entry_status == 0 && S_ISDIR (st.st_mode)
328 && st.st_dev == thisdev && st.st_ino == thisino)
329 break;
330 }
331 }
332
333 dirroom = dirp - dir;
334 namlen = _D_EXACT_NAMLEN (d);
335
336 if (dirroom <= namlen)
337 {
338 if (size != 0)
339 {
340 __set_errno (ERANGE);
341 goto lose;
342 }
343 else
344 {
345 char *tmp;
346 size_t oldsize = allocated;
347
348 allocated += MAX (allocated, namlen);
349 if (allocated < oldsize
350 || ! (tmp = realloc (dir, allocated)))
351 goto memory_exhausted;
352
353 /* Move current contents up to the end of the buffer.
354 This is guaranteed to be non-overlapping. */
355 dirp = memcpy (tmp + allocated - (oldsize - dirroom),
356 tmp + dirroom,
357 oldsize - dirroom);
358 dir = tmp;
359 }
360 }
361 dirp -= namlen;
362 memcpy (dirp, d->d_name, namlen);
363 *--dirp = '/';
364
365 thisdev = dotdev;
366 thisino = dotino;
367 }
368
369 if (dirstream && __closedir (dirstream) != 0)
370 {
371 dirstream = NULL;
372 goto lose;
373 }
374
375 if (dirp == &dir[allocated - 1])
376 *--dirp = '/';
377
378#ifndef AT_FDCWD
379 if (dotlist != dots)
380 free (dotlist);
381#endif
382
383 used = dir + allocated - dirp;
384 memmove (dir, dirp, used);
385
386 if (buf == NULL && size == 0)
387 /* Ensure that the buffer is only as large as necessary. */
388 buf = realloc (dir, used);
389
390 if (buf == NULL)
391 /* Either buf was NULL all along, or `realloc' failed but
392 we still have the original string. */
393 buf = dir;
394
395 return buf;
396
397 memory_exhausted:
398 __set_errno (ENOMEM);
399 lose:
400 {
401 int save = errno;
402 if (dirstream)
403 __closedir (dirstream);
404#ifdef AT_FDCWD
405 if (fd_needs_closing)
406 close (fd);
407#else
408 if (dotlist != dots)
409 free (dotlist);
410#endif
411 if (buf == NULL)
412 free (dir);
413 __set_errno (save);
414 }
415 return NULL;
416}
417
418#ifdef weak_alias
419weak_alias (__getcwd, getcwd)
420#endif
421
422#endif /* !__KLIBC__ */
Note: See TracBrowser for help on using the repository browser.