* lib/getcwd.c (__getcwd): Don't assume getcwd (NULL, 0) works
[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 (size == 0)
145     {
146       if (buf != NULL)
147         {
148           __set_errno (EINVAL);
149           return NULL;
150         }
151
152       allocated = BIG_FILE_NAME_LENGTH + 1;
153     }
154
155   if (buf == NULL)
156     {
157       dir = malloc (allocated);
158       if (dir == NULL)
159         return NULL;
160     }
161   else
162     dir = buf;
163
164 #if HAVE_PARTLY_WORKING_GETCWD
165   /* The system getcwd works, except it sometimes fails when it
166      shouldn't, setting errno to ERANGE, ENAMETOOLONG, or ENOENT.  If
167      AT_FDCWD is not defined, the algorithm below is O(N**2) and this
168      is much slower than the system getcwd (at least on GNU/Linux).
169      So trust the system getcwd's results unless they look
170      suspicious.
171
172      Use the system getcwd even if we have openat support, since the
173      system getcwd works even when a parent is unreadable, while the
174      openat-based approach does not.  */
175
176 # undef getcwd
177   if (getcwd (dir, allocated))
178     {
179       if (buf == NULL && size == 0)
180         buf = realloc (dir, strlen (dir) + 1);
181       return (buf ? buf : dir);
182     }
183
184   if (! (errno == ERANGE || is_ENAMETOOLONG (errno) || errno == ENOENT))
185     return NULL;
186 #endif
187
188   dirp = dir + allocated;
189   *--dirp = '\0';
190
191   if (__lstat (".", &st) < 0)
192     goto lose;
193   thisdev = st.st_dev;
194   thisino = st.st_ino;
195
196   if (__lstat ("/", &st) < 0)
197     goto lose;
198   rootdev = st.st_dev;
199   rootino = st.st_ino;
200
201   while (!(thisdev == rootdev && thisino == rootino))
202     {
203       struct dirent *d;
204       dev_t dotdev;
205       ino_t dotino;
206       bool mount_point;
207       int parent_status;
208       size_t dirroom;
209       size_t namlen;
210       bool use_d_ino = true;
211
212       /* Look at the parent directory.  */
213 #ifdef AT_FDCWD
214       fd = openat (fd, "..", O_RDONLY);
215       if (fd < 0)
216         goto lose;
217       fd_needs_closing = true;
218       parent_status = fstat (fd, &st);
219 #else
220       dotlist[dotlen++] = '.';
221       dotlist[dotlen++] = '.';
222       dotlist[dotlen] = '\0';
223       parent_status = __lstat (dotlist, &st);
224 #endif
225       if (parent_status != 0)
226         goto lose;
227
228       if (dirstream && __closedir (dirstream) != 0)
229         {
230           dirstream = NULL;
231           goto lose;
232         }
233
234       /* Figure out if this directory is a mount point.  */
235       dotdev = st.st_dev;
236       dotino = st.st_ino;
237       mount_point = dotdev != thisdev;
238
239       /* Search for the last directory.  */
240 #ifdef AT_FDCWD
241       dirstream = fdopendir (fd);
242       if (dirstream == NULL)
243         goto lose;
244       /* Reset fd.  It may have been closed by fdopendir.  */
245       fd = dirfd (dirstream);
246       fd_needs_closing = false;
247 #else
248       dirstream = __opendir (dotlist);
249       if (dirstream == NULL)
250         goto lose;
251       dotlist[dotlen++] = '/';
252 #endif
253       for (;;)
254         {
255           /* Clear errno to distinguish EOF from error if readdir returns
256              NULL.  */
257           __set_errno (0);
258           d = __readdir (dirstream);
259
260           /* When we've iterated through all directory entries without finding
261              one with a matching d_ino, rewind the stream and consider each
262              name again, but this time, using lstat.  This is necessary in a
263              chroot on at least one system (glibc-2.3.6 + linux 2.6.12), where
264              .., ../.., ../../.., etc. all had the same device number, yet the
265              d_ino values for entries in / did not match those obtained
266              via lstat.  */
267           if (d == NULL && errno == 0 && use_d_ino)
268             {
269               use_d_ino = false;
270               rewinddir (dirstream);
271               d = __readdir (dirstream);
272             }
273
274           if (d == NULL)
275             {
276               if (errno == 0)
277                 /* EOF on dirstream, which can mean e.g., that the current
278                    directory has been removed.  */
279                 __set_errno (ENOENT);
280               goto lose;
281             }
282           if (d->d_name[0] == '.' &&
283               (d->d_name[1] == '\0' ||
284                (d->d_name[1] == '.' && d->d_name[2] == '\0')))
285             continue;
286
287           if (use_d_ino)
288             {
289               bool match = (MATCHING_INO (d, thisino) || mount_point);
290               if (! match)
291                 continue;
292             }
293
294           {
295             int entry_status;
296 #ifdef AT_FDCWD
297             entry_status = fstatat (fd, d->d_name, &st, AT_SYMLINK_NOFOLLOW);
298 #else
299             /* Compute size needed for this file name, or for the file
300                name ".." in the same directory, whichever is larger.
301                Room for ".." might be needed the next time through
302                the outer loop.  */
303             size_t name_alloc = _D_ALLOC_NAMLEN (d);
304             size_t filesize = dotlen + MAX (sizeof "..", name_alloc);
305
306             if (filesize < dotlen)
307               goto memory_exhausted;
308
309             if (dotsize < filesize)
310               {
311                 /* My, what a deep directory tree you have, Grandma.  */
312                 size_t newsize = MAX (filesize, dotsize * 2);
313                 size_t i;
314                 if (newsize < dotsize)
315                   goto memory_exhausted;
316                 if (dotlist != dots)
317                   free (dotlist);
318                 dotlist = malloc (newsize);
319                 if (dotlist == NULL)
320                   goto lose;
321                 dotsize = newsize;
322
323                 i = 0;
324                 do
325                   {
326                     dotlist[i++] = '.';
327                     dotlist[i++] = '.';
328                     dotlist[i++] = '/';
329                   }
330                 while (i < dotlen);
331               }
332
333             memcpy (dotlist + dotlen, d->d_name, _D_ALLOC_NAMLEN (d));
334             entry_status = __lstat (dotlist, &st);
335 #endif
336             /* We don't fail here if we cannot stat() a directory entry.
337                This can happen when (network) file systems fail.  If this
338                entry is in fact the one we are looking for we will find
339                out soon as we reach the end of the directory without
340                having found anything.  */
341             if (entry_status == 0 && S_ISDIR (st.st_mode)
342                 && st.st_dev == thisdev && st.st_ino == thisino)
343               break;
344           }
345         }
346
347       dirroom = dirp - dir;
348       namlen = _D_EXACT_NAMLEN (d);
349
350       if (dirroom <= namlen)
351         {
352           if (size != 0)
353             {
354               __set_errno (ERANGE);
355               goto lose;
356             }
357           else
358             {
359               char *tmp;
360               size_t oldsize = allocated;
361
362               allocated += MAX (allocated, namlen);
363               if (allocated < oldsize
364                   || ! (tmp = realloc (dir, allocated)))
365                 goto memory_exhausted;
366
367               /* Move current contents up to the end of the buffer.
368                  This is guaranteed to be non-overlapping.  */
369               dirp = memcpy (tmp + allocated - (oldsize - dirroom),
370                              tmp + dirroom,
371                              oldsize - dirroom);
372               dir = tmp;
373             }
374         }
375       dirp -= namlen;
376       memcpy (dirp, d->d_name, namlen);
377       *--dirp = '/';
378
379       thisdev = dotdev;
380       thisino = dotino;
381     }
382
383   if (dirstream && __closedir (dirstream) != 0)
384     {
385       dirstream = NULL;
386       goto lose;
387     }
388
389   if (dirp == &dir[allocated - 1])
390     *--dirp = '/';
391
392 #ifndef AT_FDCWD
393   if (dotlist != dots)
394     free (dotlist);
395 #endif
396
397   used = dir + allocated - dirp;
398   memmove (dir, dirp, used);
399
400   if (buf == NULL && size == 0)
401     /* Ensure that the buffer is only as large as necessary.  */
402     buf = realloc (dir, used);
403
404   if (buf == NULL)
405     /* Either buf was NULL all along, or `realloc' failed but
406        we still have the original string.  */
407     buf = dir;
408
409   return buf;
410
411  memory_exhausted:
412   __set_errno (ENOMEM);
413  lose:
414   {
415     int save = errno;
416     if (dirstream)
417       __closedir (dirstream);
418 #ifdef AT_FDCWD
419     if (fd_needs_closing)
420       close (fd);
421 #else
422     if (dotlist != dots)
423       free (dotlist);
424 #endif
425     if (buf == NULL)
426       free (dir);
427     __set_errno (save);
428   }
429   return NULL;
430 }
431
432 #ifdef weak_alias
433 weak_alias (__getcwd, getcwd)
434 #endif