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