backupfile, chdir-long, fts, savedir: make safer
[gnulib.git] / lib / getcwd.c
1 /* Copyright (C) 1991-1999, 2004-2009 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, then enable
31    code below to make getcwd more efficient and robust.  */
32 #ifdef HAVE_OPENAT
33 # define HAVE_OPENAT_SUPPORT 1
34 #else
35 # define HAVE_OPENAT_SUPPORT 0
36 #endif
37
38 #ifndef __set_errno
39 # define __set_errno(val) (errno = (val))
40 #endif
41
42 #include <dirent.h>
43 #ifndef _D_EXACT_NAMLEN
44 # define _D_EXACT_NAMLEN(d) strlen ((d)->d_name)
45 #endif
46 #ifndef _D_ALLOC_NAMLEN
47 # define _D_ALLOC_NAMLEN(d) (_D_EXACT_NAMLEN (d) + 1)
48 #endif
49
50 #include <unistd.h>
51 #include <stdlib.h>
52 #include <string.h>
53
54 #if _LIBC
55 # ifndef mempcpy
56 #  define mempcpy __mempcpy
57 # endif
58 #endif
59
60 #include <limits.h>
61
62 /* Work around a bug in Solaris 9 and 10: AT_FDCWD is positive.  Its
63    value exceeds INT_MAX, so its use as an int doesn't conform to the
64    C standard, and GCC and Sun C complain in some cases.  */
65 #if 0 < AT_FDCWD && AT_FDCWD == 0xffd19553
66 # undef AT_FDCWD
67 # define AT_FDCWD (-3041965)
68 #endif
69
70 #ifdef ENAMETOOLONG
71 # define is_ENAMETOOLONG(x) ((x) == ENAMETOOLONG)
72 #else
73 # define is_ENAMETOOLONG(x) 0
74 #endif
75
76 #ifndef MAX
77 # define MAX(a, b) ((a) < (b) ? (b) : (a))
78 #endif
79 #ifndef MIN
80 # define MIN(a, b) ((a) < (b) ? (a) : (b))
81 #endif
82
83 #ifndef PATH_MAX
84 # ifdef MAXPATHLEN
85 #  define PATH_MAX MAXPATHLEN
86 # else
87 #  define PATH_MAX 1024
88 # endif
89 #endif
90
91 #if D_INO_IN_DIRENT
92 # define MATCHING_INO(dp, ino) ((dp)->d_ino == (ino))
93 #else
94 # define MATCHING_INO(dp, ino) true
95 #endif
96
97 #if !_LIBC
98 # define __getcwd rpl_getcwd
99 # define __lstat lstat
100 # define __closedir closedir
101 # define __opendir opendir
102 # define __readdir readdir
103 #endif
104
105 /* The results of opendir() in this file are not used with dirfd and fchdir,
106    and we do not leak fds to any single-threaded code that could use stdio,
107    therefore save some unnecessary recursion in fchdir.c.
108    FIXME - if the kernel ever adds support for multi-thread safety for
109    avoiding standard fds, then we should use opendir_safer and
110    openat_safer.  */
111 #undef opendir
112 #undef closedir
113 \f
114 /* Get the name of the current working directory, and put it in SIZE
115    bytes of BUF.  Returns NULL if the directory couldn't be determined or
116    SIZE was too small.  If successful, returns BUF.  In GNU, if BUF is
117    NULL, an array is allocated with `malloc'; the array is SIZE bytes long,
118    unless SIZE == 0, in which case it is as big as necessary.  */
119
120 char *
121 __getcwd (char *buf, size_t size)
122 {
123   /* Lengths of big file name components and entire file names, and a
124      deep level of file name nesting.  These numbers are not upper
125      bounds; they are merely large values suitable for initial
126      allocations, designed to be large enough for most real-world
127      uses.  */
128   enum
129     {
130       BIG_FILE_NAME_COMPONENT_LENGTH = 255,
131       BIG_FILE_NAME_LENGTH = MIN (4095, PATH_MAX - 1),
132       DEEP_NESTING = 100
133     };
134
135 #if HAVE_OPENAT_SUPPORT
136   int fd = AT_FDCWD;
137   bool fd_needs_closing = false;
138 #else
139   char dots[DEEP_NESTING * sizeof ".." + BIG_FILE_NAME_COMPONENT_LENGTH + 1];
140   char *dotlist = dots;
141   size_t dotsize = sizeof dots;
142   size_t dotlen = 0;
143 #endif
144   DIR *dirstream = NULL;
145   dev_t rootdev, thisdev;
146   ino_t rootino, thisino;
147   char *dir;
148   register char *dirp;
149   struct stat st;
150   size_t allocated = size;
151   size_t used;
152
153 #if HAVE_PARTLY_WORKING_GETCWD
154   /* The system getcwd works, except it sometimes fails when it
155      shouldn't, setting errno to ERANGE, ENAMETOOLONG, or ENOENT.  If
156      AT_FDCWD is not defined, the algorithm below is O(N**2) and this
157      is much slower than the system getcwd (at least on GNU/Linux).
158      So trust the system getcwd's results unless they look
159      suspicious.
160
161      Use the system getcwd even if we have openat support, since the
162      system getcwd works even when a parent is unreadable, while the
163      openat-based approach does not.  */
164
165 # undef getcwd
166   dir = getcwd (buf, size);
167   if (dir || (errno != ERANGE && !is_ENAMETOOLONG (errno) && errno != ENOENT))
168     return dir;
169 #endif
170
171   if (size == 0)
172     {
173       if (buf != NULL)
174         {
175           __set_errno (EINVAL);
176           return NULL;
177         }
178
179       allocated = BIG_FILE_NAME_LENGTH + 1;
180     }
181
182   if (buf == NULL)
183     {
184       dir = malloc (allocated);
185       if (dir == NULL)
186         return NULL;
187     }
188   else
189     dir = buf;
190
191   dirp = dir + allocated;
192   *--dirp = '\0';
193
194   if (__lstat (".", &st) < 0)
195     goto lose;
196   thisdev = st.st_dev;
197   thisino = st.st_ino;
198
199   if (__lstat ("/", &st) < 0)
200     goto lose;
201   rootdev = st.st_dev;
202   rootino = st.st_ino;
203
204   while (!(thisdev == rootdev && thisino == rootino))
205     {
206       struct dirent *d;
207       dev_t dotdev;
208       ino_t dotino;
209       bool mount_point;
210       int parent_status;
211       size_t dirroom;
212       size_t namlen;
213       bool use_d_ino = true;
214
215       /* Look at the parent directory.  */
216 #if HAVE_OPENAT_SUPPORT
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 #if HAVE_OPENAT_SUPPORT
244       dirstream = fdopendir (fd);
245       if (dirstream == NULL)
246         goto lose;
247       /* Reset fd.  It may have been closed by fdopendir.  */
248       fd = dirfd (dirstream);
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