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