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