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