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