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

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

tar 1.16.1

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