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