Bump to m4 1.4.19
[platform/upstream/m4.git] / lib / getcwd.c
1 /* Copyright (C) 1991-2021 Free Software Foundation, Inc.
2    This file is part of the GNU C Library.
3
4    The GNU C Library is free software; you can redistribute it and/or
5    modify it under the terms of the GNU General Public
6    License as published by the Free Software Foundation; either
7    version 3 of the License, or (at your option) any later version.
8
9    The GNU C Library 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 GNU
12    General Public License for more details.
13
14    You should have received a copy of the GNU General Public
15    License along with the GNU C Library; if not, see
16    <https://www.gnu.org/licenses/>.  */
17
18 #if !_LIBC
19 # include <config.h>
20 # include <unistd.h>
21 # include "pathmax.h"
22 #else
23 # define HAVE_OPENAT 1
24 # define D_INO_IN_DIRENT 1
25 # define HAVE_MSVC_INVALID_PARAMETER_HANDLER 0
26 # define HAVE_MINIMALLY_WORKING_GETCWD 0
27 #endif
28
29 #include <errno.h>
30 #include <sys/types.h>
31 #include <sys/stat.h>
32 #include <stdbool.h>
33 #include <stddef.h>
34
35 #include <fcntl.h> /* For AT_FDCWD on Solaris 9.  */
36
37 /* If this host provides the openat function or if we're using the
38    gnulib replacement function with a native fdopendir, then enable
39    code below to make getcwd more efficient and robust.  */
40 #if defined HAVE_OPENAT || (defined GNULIB_OPENAT && defined HAVE_FDOPENDIR)
41 # define HAVE_OPENAT_SUPPORT 1
42 #else
43 # define HAVE_OPENAT_SUPPORT 0
44 #endif
45
46 #ifndef __set_errno
47 # define __set_errno(val) (errno = (val))
48 #endif
49
50 #include <dirent.h>
51 #ifndef _D_EXACT_NAMLEN
52 # define _D_EXACT_NAMLEN(d) strlen ((d)->d_name)
53 #endif
54 #ifndef _D_ALLOC_NAMLEN
55 # define _D_ALLOC_NAMLEN(d) (_D_EXACT_NAMLEN (d) + 1)
56 #endif
57
58 #include <unistd.h>
59 #include <stdlib.h>
60 #include <string.h>
61
62 #if _LIBC
63 # ifndef mempcpy
64 #  define mempcpy __mempcpy
65 # endif
66 #endif
67
68 #ifndef MAX
69 # define MAX(a, b) ((a) < (b) ? (b) : (a))
70 #endif
71 #ifndef MIN
72 # define MIN(a, b) ((a) < (b) ? (a) : (b))
73 #endif
74
75 /* In this file, PATH_MAX only serves as a threshold for choosing among two
76    algorithms.  */
77 #ifndef PATH_MAX
78 # define PATH_MAX 8192
79 #endif
80
81 #if D_INO_IN_DIRENT
82 # define MATCHING_INO(dp, ino) ((dp)->d_ino == (ino))
83 #else
84 # define MATCHING_INO(dp, ino) true
85 #endif
86
87 #if HAVE_MSVC_INVALID_PARAMETER_HANDLER
88 # include "msvc-inval.h"
89 #endif
90
91 #if !_LIBC
92 # define GETCWD_RETURN_TYPE char *
93 # define __close_nocancel_nostatus close
94 # define __getcwd_generic rpl_getcwd
95 # undef stat64
96 # define stat64    stat
97 # define __fstat64 fstat
98 # define __fstatat64 fstatat
99 # define __lstat64 lstat
100 # define __closedir closedir
101 # define __opendir opendir
102 # define __readdir64 readdir
103 # define __fdopendir fdopendir
104 # define __openat openat
105 # define __rewinddir rewinddir
106 # define __openat64 openat
107 # define dirent64 dirent
108 #else
109 # include <not-cancel.h>
110 #endif
111
112 /* The results of opendir() in this file are not used with dirfd and fchdir,
113    and we do not leak fds to any single-threaded code that could use stdio,
114    therefore save some unnecessary recursion in fchdir.c.
115    FIXME - if the kernel ever adds support for multi-thread safety for
116    avoiding standard fds, then we should use opendir_safer and
117    openat_safer.  */
118 #ifdef GNULIB_defined_opendir
119 # undef opendir
120 #endif
121 #ifdef GNULIB_defined_closedir
122 # undef closedir
123 #endif
124 \f
125 #if defined _WIN32 && !defined __CYGWIN__
126 # if HAVE_MSVC_INVALID_PARAMETER_HANDLER
127 static char *
128 getcwd_nothrow (char *buf, size_t size)
129 {
130   char *result;
131
132   TRY_MSVC_INVAL
133     {
134       result = _getcwd (buf, size);
135     }
136   CATCH_MSVC_INVAL
137     {
138       result = NULL;
139       errno = ERANGE;
140     }
141   DONE_MSVC_INVAL;
142
143   return result;
144 }
145 # else
146 #  define getcwd_nothrow _getcwd
147 # endif
148 # define getcwd_system getcwd_nothrow
149 #else
150 # define getcwd_system getcwd
151 #endif
152
153 /* Get the name of the current working directory, and put it in SIZE
154    bytes of BUF.  Returns NULL with errno set if the directory couldn't be
155    determined or SIZE was too small.  If successful, returns BUF.  In GNU,
156    if BUF is NULL, an array is allocated with 'malloc'; the array is SIZE
157    bytes long, unless SIZE == 0, in which case it is as big as necessary.  */
158
159 GETCWD_RETURN_TYPE
160 __getcwd_generic (char *buf, size_t size)
161 {
162   /* Lengths of big file name components and entire file names, and a
163      deep level of file name nesting.  These numbers are not upper
164      bounds; they are merely large values suitable for initial
165      allocations, designed to be large enough for most real-world
166      uses.  */
167   enum
168     {
169       BIG_FILE_NAME_COMPONENT_LENGTH = 255,
170       BIG_FILE_NAME_LENGTH = MIN (4095, PATH_MAX - 1),
171       DEEP_NESTING = 100
172     };
173
174 #if HAVE_OPENAT_SUPPORT
175   int fd = AT_FDCWD;
176   bool fd_needs_closing = false;
177 #else
178   char dots[DEEP_NESTING * sizeof ".." + BIG_FILE_NAME_COMPONENT_LENGTH + 1];
179   char *dotlist = dots;
180   size_t dotsize = sizeof dots;
181   size_t dotlen = 0;
182 #endif
183   DIR *dirstream = NULL;
184   dev_t rootdev, thisdev;
185   ino_t rootino, thisino;
186   char *dir;
187   register char *dirp;
188   struct stat64 st;
189   size_t allocated = size;
190   size_t used;
191
192 #if HAVE_MINIMALLY_WORKING_GETCWD
193   /* If AT_FDCWD is not defined, the algorithm below is O(N**2) and
194      this is much slower than the system getcwd (at least on
195      GNU/Linux).  So trust the system getcwd's results unless they
196      look suspicious.
197
198      Use the system getcwd even if we have openat support, since the
199      system getcwd works even when a parent is unreadable, while the
200      openat-based approach does not.
201
202      But on AIX 5.1..7.1, the system getcwd is not even minimally
203      working: If the current directory name is slightly longer than
204      PATH_MAX, it omits the first directory component and returns
205      this wrong result with errno = 0.  */
206
207 # undef getcwd
208   dir = getcwd_system (buf, size);
209   if (dir || (size && errno == ERANGE))
210     return dir;
211
212   /* Solaris getcwd (NULL, 0) fails with errno == EINVAL, but it has
213      internal magic that lets it work even if an ancestor directory is
214      inaccessible, which is better in many cases.  So in this case try
215      again with a buffer that's almost always big enough.  */
216   if (errno == EINVAL && buf == NULL && size == 0)
217     {
218       char big_buffer[BIG_FILE_NAME_LENGTH + 1];
219       dir = getcwd_system (big_buffer, sizeof big_buffer);
220       if (dir)
221         return strdup (dir);
222     }
223
224 # if HAVE_PARTLY_WORKING_GETCWD
225   /* The system getcwd works, except it sometimes fails when it
226      shouldn't, setting errno to ERANGE, ENAMETOOLONG, or ENOENT.    */
227   if (errno != ERANGE && errno != ENAMETOOLONG && errno != ENOENT)
228     return NULL;
229 # endif
230 #endif
231   if (size == 0)
232     {
233       if (buf != NULL)
234         {
235           __set_errno (EINVAL);
236           return NULL;
237         }
238
239       allocated = BIG_FILE_NAME_LENGTH + 1;
240     }
241
242   if (buf == NULL)
243     {
244       dir = malloc (allocated);
245       if (dir == NULL)
246         return NULL;
247     }
248   else
249     dir = buf;
250
251   dirp = dir + allocated;
252   *--dirp = '\0';
253
254   if (__lstat64 (".", &st) < 0)
255     goto lose;
256   thisdev = st.st_dev;
257   thisino = st.st_ino;
258
259   if (__lstat64 ("/", &st) < 0)
260     goto lose;
261   rootdev = st.st_dev;
262   rootino = st.st_ino;
263
264   while (!(thisdev == rootdev && thisino == rootino))
265     {
266       struct dirent64 *d;
267       dev_t dotdev;
268       ino_t dotino;
269       bool mount_point;
270       int parent_status;
271       size_t dirroom;
272       size_t namlen;
273       bool use_d_ino = true;
274
275       /* Look at the parent directory.  */
276 #if HAVE_OPENAT_SUPPORT
277       fd = __openat64 (fd, "..", O_RDONLY);
278       if (fd < 0)
279         goto lose;
280       fd_needs_closing = true;
281       parent_status = __fstat64 (fd, &st);
282 #else
283       dotlist[dotlen++] = '.';
284       dotlist[dotlen++] = '.';
285       dotlist[dotlen] = '\0';
286       parent_status = __lstat64 (dotlist, &st);
287 #endif
288       if (parent_status != 0)
289         goto lose;
290
291       if (dirstream && __closedir (dirstream) != 0)
292         {
293           dirstream = NULL;
294           goto lose;
295         }
296
297       /* Figure out if this directory is a mount point.  */
298       dotdev = st.st_dev;
299       dotino = st.st_ino;
300       mount_point = dotdev != thisdev;
301
302       /* Search for the last directory.  */
303 #if HAVE_OPENAT_SUPPORT
304       dirstream = __fdopendir (fd);
305       if (dirstream == NULL)
306         goto lose;
307       fd_needs_closing = false;
308 #else
309       dirstream = __opendir (dotlist);
310       if (dirstream == NULL)
311         goto lose;
312       dotlist[dotlen++] = '/';
313 #endif
314       for (;;)
315         {
316           /* Clear errno to distinguish EOF from error if readdir returns
317              NULL.  */
318           __set_errno (0);
319           d = __readdir64 (dirstream);
320
321           /* When we've iterated through all directory entries without finding
322              one with a matching d_ino, rewind the stream and consider each
323              name again, but this time, using lstat.  This is necessary in a
324              chroot on at least one system (glibc-2.3.6 + linux 2.6.12), where
325              .., ../.., ../../.., etc. all had the same device number, yet the
326              d_ino values for entries in / did not match those obtained
327              via lstat.  */
328           if (d == NULL && errno == 0 && use_d_ino)
329             {
330               use_d_ino = false;
331               __rewinddir (dirstream);
332               d = __readdir64 (dirstream);
333             }
334
335           if (d == NULL)
336             {
337               if (errno == 0)
338                 /* EOF on dirstream, which can mean e.g., that the current
339                    directory has been removed.  */
340                 __set_errno (ENOENT);
341               goto lose;
342             }
343           if (d->d_name[0] == '.' &&
344               (d->d_name[1] == '\0' ||
345                (d->d_name[1] == '.' && d->d_name[2] == '\0')))
346             continue;
347
348           if (use_d_ino)
349             {
350               bool match = (MATCHING_INO (d, thisino) || mount_point);
351               if (! match)
352                 continue;
353             }
354
355           {
356             int entry_status;
357 #if HAVE_OPENAT_SUPPORT
358             entry_status = __fstatat64 (fd, d->d_name, &st, AT_SYMLINK_NOFOLLOW);
359 #else
360             /* Compute size needed for this file name, or for the file
361                name ".." in the same directory, whichever is larger.
362                Room for ".." might be needed the next time through
363                the outer loop.  */
364             size_t name_alloc = _D_ALLOC_NAMLEN (d);
365             size_t filesize = dotlen + MAX (sizeof "..", name_alloc);
366
367             if (filesize < dotlen)
368               goto memory_exhausted;
369
370             if (dotsize < filesize)
371               {
372                 /* My, what a deep directory tree you have, Grandma.  */
373                 size_t newsize = MAX (filesize, dotsize * 2);
374                 size_t i;
375                 if (newsize < dotsize)
376                   goto memory_exhausted;
377                 if (dotlist != dots)
378                   free (dotlist);
379                 dotlist = malloc (newsize);
380                 if (dotlist == NULL)
381                   goto lose;
382                 dotsize = newsize;
383
384                 i = 0;
385                 do
386                   {
387                     dotlist[i++] = '.';
388                     dotlist[i++] = '.';
389                     dotlist[i++] = '/';
390                   }
391                 while (i < dotlen);
392               }
393
394             memcpy (dotlist + dotlen, d->d_name, _D_ALLOC_NAMLEN (d));
395             entry_status = __lstat64 (dotlist, &st);
396 #endif
397             /* We don't fail here if we cannot stat() a directory entry.
398                This can happen when (network) file systems fail.  If this
399                entry is in fact the one we are looking for we will find
400                out soon as we reach the end of the directory without
401                having found anything.  */
402             if (entry_status == 0 && S_ISDIR (st.st_mode)
403                 && st.st_dev == thisdev && st.st_ino == thisino)
404               break;
405           }
406         }
407
408       dirroom = dirp - dir;
409       namlen = _D_EXACT_NAMLEN (d);
410
411       if (dirroom <= namlen)
412         {
413           if (size != 0)
414             {
415               __set_errno (ERANGE);
416               goto lose;
417             }
418           else
419             {
420               char *tmp;
421               size_t oldsize = allocated;
422
423               allocated += MAX (allocated, namlen);
424               if (allocated < oldsize
425                   || ! (tmp = realloc (dir, allocated)))
426                 goto memory_exhausted;
427
428               /* Move current contents up to the end of the buffer.
429                  This is guaranteed to be non-overlapping.  */
430               dirp = memcpy (tmp + allocated - (oldsize - dirroom),
431                              tmp + dirroom,
432                              oldsize - dirroom);
433               dir = tmp;
434             }
435         }
436       dirp -= namlen;
437       memcpy (dirp, d->d_name, namlen);
438       *--dirp = '/';
439
440       thisdev = dotdev;
441       thisino = dotino;
442     }
443
444   if (dirstream && __closedir (dirstream) != 0)
445     {
446       dirstream = NULL;
447       goto lose;
448     }
449
450   if (dirp == &dir[allocated - 1])
451     *--dirp = '/';
452
453 #if ! HAVE_OPENAT_SUPPORT
454   if (dotlist != dots)
455     free (dotlist);
456 #endif
457
458   used = dir + allocated - dirp;
459   memmove (dir, dirp, used);
460
461   if (size == 0)
462     /* Ensure that the buffer is only as large as necessary.  */
463     buf = (used < allocated ? realloc (dir, used) : dir);
464
465   if (buf == NULL)
466     /* Either buf was NULL all along, or 'realloc' failed but
467        we still have the original string.  */
468     buf = dir;
469
470   return buf;
471
472  memory_exhausted:
473   __set_errno (ENOMEM);
474  lose:
475   {
476     int save = errno;
477     if (dirstream)
478       __closedir (dirstream);
479 #if HAVE_OPENAT_SUPPORT
480     if (fd_needs_closing)
481        __close_nocancel_nostatus (fd);
482 #else
483     if (dotlist != dots)
484       free (dotlist);
485 #endif
486     if (buf == NULL)
487       free (dir);
488     __set_errno (save);
489   }
490   return NULL;
491 }
492
493 #if defined _LIBC && !defined GETCWD_RETURN_TYPE
494 libc_hidden_def (__getcwd)
495 weak_alias (__getcwd, getcwd)
496 #endif