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