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