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