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