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