1 /* Copyright (C) 1991-2021 Free Software Foundation, Inc.
2    This file is part of the GNU C Library.
3 
4    This file is free software: you can redistribute it and/or modify
5    it under the terms of the GNU General Public License as published
6    by the Free Software Foundation; either version 3 of the License,
7    or (at your option) any later version.
8 
9    This file 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
12    GNU General Public License for more details.
13 
14    You should have received a copy of the GNU General Public License
15    along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
16 
17 #if !_LIBC
18 # include <config.h>
19 # include <unistd.h>
20 # include "pathmax.h"
21 #else
22 # define HAVE_OPENAT 1
23 # define D_INO_IN_DIRENT 1
24 # define HAVE_MSVC_INVALID_PARAMETER_HANDLER 0
25 # define HAVE_MINIMALLY_WORKING_GETCWD 0
26 #endif
27 
28 #include <errno.h>
29 #include <sys/types.h>
30 #include <sys/stat.h>
31 #include <stdbool.h>
32 #include <stddef.h>
33 
34 #include <fcntl.h> /* For AT_FDCWD on Solaris 9.  */
35 
36 /* If this host provides the openat function or if we're using the
37    gnulib replacement function with a native fdopendir, then enable
38    code below to make getcwd more efficient and robust.  */
39 #if defined HAVE_OPENAT || (defined GNULIB_OPENAT && defined HAVE_FDOPENDIR)
40 # define HAVE_OPENAT_SUPPORT 1
41 #else
42 # define HAVE_OPENAT_SUPPORT 0
43 #endif
44 
45 #ifndef __set_errno
46 # define __set_errno(val) (errno = (val))
47 #endif
48 
49 #include <dirent.h>
50 #ifndef _D_EXACT_NAMLEN
51 # define _D_EXACT_NAMLEN(d) strlen ((d)->d_name)
52 #endif
53 #ifndef _D_ALLOC_NAMLEN
54 # define _D_ALLOC_NAMLEN(d) (_D_EXACT_NAMLEN (d) + 1)
55 #endif
56 
57 #include <unistd.h>
58 #include <stdlib.h>
59 #include <string.h>
60 
61 #if _LIBC
62 # ifndef mempcpy
63 #  define mempcpy __mempcpy
64 # endif
65 #endif
66 
67 #ifndef MAX
68 # define MAX(a, b) ((a) < (b) ? (b) : (a))
69 #endif
70 #ifndef MIN
71 # define MIN(a, b) ((a) < (b) ? (a) : (b))
72 #endif
73 
74 /* In this file, PATH_MAX only serves as a threshold for choosing among two
75    algorithms.  */
76 #ifndef PATH_MAX
77 # define PATH_MAX 8192
78 #endif
79 
80 #if D_INO_IN_DIRENT
81 # define MATCHING_INO(dp, ino) ((dp)->d_ino == (ino))
82 #else
83 # define MATCHING_INO(dp, ino) true
84 #endif
85 
86 #if HAVE_MSVC_INVALID_PARAMETER_HANDLER
87 # include "msvc-inval.h"
88 #endif
89 
90 #if !_LIBC
91 # define GETCWD_RETURN_TYPE char *
92 # define __close_nocancel_nostatus close
93 # define __getcwd_generic rpl_getcwd
94 # undef stat64
95 # define stat64    stat
96 # define __fstat64 fstat
97 # define __fstatat64 fstatat
98 # define __lstat64 lstat
99 # define __closedir closedir
100 # define __opendir opendir
101 # define __readdir64 readdir
102 # define __fdopendir fdopendir
103 # define __openat openat
104 # define __rewinddir rewinddir
105 # define __openat64 openat
106 # define dirent64 dirent
107 #else
108 # include <not-cancel.h>
109 #endif
110 
111 /* The results of opendir() in this file are not used with dirfd and fchdir,
112    and we do not leak fds to any single-threaded code that could use stdio,
113    therefore save some unnecessary recursion in fchdir.c.
114    FIXME - if the kernel ever adds support for multi-thread safety for
115    avoiding standard fds, then we should use opendir_safer and
116    openat_safer.  */
117 #ifdef GNULIB_defined_opendir
118 # undef opendir
119 #endif
120 #ifdef GNULIB_defined_closedir
121 # undef closedir
122 #endif
123 
124 #if defined _WIN32 && !defined __CYGWIN__
125 # if HAVE_MSVC_INVALID_PARAMETER_HANDLER
126 static char *
getcwd_nothrow(char * buf,size_t size)127 getcwd_nothrow (char *buf, size_t size)
128 {
129   char *result;
130 
131   TRY_MSVC_INVAL
132     {
133       result = _getcwd (buf, size);
134     }
135   CATCH_MSVC_INVAL
136     {
137       result = NULL;
138       errno = ERANGE;
139     }
140   DONE_MSVC_INVAL;
141 
142   return result;
143 }
144 # else
145 #  define getcwd_nothrow _getcwd
146 # endif
147 # define getcwd_system getcwd_nothrow
148 #else
149 # define getcwd_system getcwd
150 #endif
151 
152 /* Get the name of the current working directory, and put it in SIZE
153    bytes of BUF.  Returns NULL with errno set if the directory couldn't be
154    determined or SIZE was too small.  If successful, returns BUF.  In GNU,
155    if BUF is NULL, an array is allocated with 'malloc'; the array is SIZE
156    bytes long, unless SIZE == 0, in which case it is as big as necessary.  */
157 
158 GETCWD_RETURN_TYPE
__getcwd_generic(char * buf,size_t size)159 __getcwd_generic (char *buf, size_t size)
160 {
161   /* Lengths of big file name components and entire file names, and a
162      deep level of file name nesting.  These numbers are not upper
163      bounds; they are merely large values suitable for initial
164      allocations, designed to be large enough for most real-world
165      uses.  */
166   enum
167     {
168       BIG_FILE_NAME_COMPONENT_LENGTH = 255,
169       BIG_FILE_NAME_LENGTH = MIN (4095, PATH_MAX - 1),
170       DEEP_NESTING = 100
171     };
172 
173 #if HAVE_OPENAT_SUPPORT
174   int fd = AT_FDCWD;
175   bool fd_needs_closing = false;
176 #else
177   char dots[DEEP_NESTING * sizeof ".." + BIG_FILE_NAME_COMPONENT_LENGTH + 1];
178   char *dotlist = dots;
179   size_t dotsize = sizeof dots;
180   size_t dotlen = 0;
181 #endif
182   DIR *dirstream = NULL;
183   dev_t rootdev, thisdev;
184   ino_t rootino, thisino;
185   char *dir;
186   register char *dirp;
187   struct stat64 st;
188   size_t allocated = size;
189   size_t used;
190 
191 #if HAVE_MINIMALLY_WORKING_GETCWD
192   /* If AT_FDCWD is not defined, the algorithm below is O(N**2) and
193      this is much slower than the system getcwd (at least on
194      GNU/Linux).  So trust the system getcwd's results unless they
195      look suspicious.
196 
197      Use the system getcwd even if we have openat support, since the
198      system getcwd works even when a parent is unreadable, while the
199      openat-based approach does not.
200 
201      But on AIX 5.1..7.1, the system getcwd is not even minimally
202      working: If the current directory name is slightly longer than
203      PATH_MAX, it omits the first directory component and returns
204      this wrong result with errno = 0.  */
205 
206 # undef getcwd
207   dir = getcwd_system (buf, size);
208   if (dir || (size && errno == ERANGE))
209     return dir;
210 
211   /* Solaris getcwd (NULL, 0) fails with errno == EINVAL, but it has
212      internal magic that lets it work even if an ancestor directory is
213      inaccessible, which is better in many cases.  So in this case try
214      again with a buffer that's almost always big enough.  */
215   if (errno == EINVAL && buf == NULL && size == 0)
216     {
217       char big_buffer[BIG_FILE_NAME_LENGTH + 1];
218       dir = getcwd_system (big_buffer, sizeof big_buffer);
219       if (dir)
220         return strdup (dir);
221     }
222 
223 # if HAVE_PARTLY_WORKING_GETCWD
224   /* The system getcwd works, except it sometimes fails when it
225      shouldn't, setting errno to ERANGE, ENAMETOOLONG, or ENOENT.    */
226   if (errno != ERANGE && errno != ENAMETOOLONG && errno != ENOENT)
227     return NULL;
228 # endif
229 #endif
230   if (size == 0)
231     {
232       if (buf != NULL)
233         {
234           __set_errno (EINVAL);
235           return NULL;
236         }
237 
238       allocated = BIG_FILE_NAME_LENGTH + 1;
239     }
240 
241   if (buf == NULL)
242     {
243       dir = malloc (allocated);
244       if (dir == NULL)
245         return NULL;
246     }
247   else
248     dir = buf;
249 
250   dirp = dir + allocated;
251   *--dirp = '\0';
252 
253   if (__lstat64 (".", &st) < 0)
254     goto lose;
255   thisdev = st.st_dev;
256   thisino = st.st_ino;
257 
258   if (__lstat64 ("/", &st) < 0)
259     goto lose;
260   rootdev = st.st_dev;
261   rootino = st.st_ino;
262 
263   while (!(thisdev == rootdev && thisino == rootino))
264     {
265       struct dirent64 *d;
266       dev_t dotdev;
267       ino_t dotino;
268       bool mount_point;
269       int parent_status;
270       size_t dirroom;
271       size_t namlen;
272       bool use_d_ino = true;
273 
274       /* Look at the parent directory.  */
275 #if HAVE_OPENAT_SUPPORT
276       fd = __openat64 (fd, "..", O_RDONLY);
277       if (fd < 0)
278         goto lose;
279       fd_needs_closing = true;
280       parent_status = __fstat64 (fd, &st);
281 #else
282       dotlist[dotlen++] = '.';
283       dotlist[dotlen++] = '.';
284       dotlist[dotlen] = '\0';
285       parent_status = __lstat64 (dotlist, &st);
286 #endif
287       if (parent_status != 0)
288         goto lose;
289 
290       if (dirstream && __closedir (dirstream) != 0)
291         {
292           dirstream = NULL;
293           goto lose;
294         }
295 
296       /* Figure out if this directory is a mount point.  */
297       dotdev = st.st_dev;
298       dotino = st.st_ino;
299       mount_point = dotdev != thisdev;
300 
301       /* Search for the last directory.  */
302 #if HAVE_OPENAT_SUPPORT
303       dirstream = __fdopendir (fd);
304       if (dirstream == NULL)
305         goto lose;
306       fd_needs_closing = false;
307 #else
308       dirstream = __opendir (dotlist);
309       if (dirstream == NULL)
310         goto lose;
311       dotlist[dotlen++] = '/';
312 #endif
313       for (;;)
314         {
315           /* Clear errno to distinguish EOF from error if readdir returns
316              NULL.  */
317           __set_errno (0);
318           d = __readdir64 (dirstream);
319 
320           /* When we've iterated through all directory entries without finding
321              one with a matching d_ino, rewind the stream and consider each
322              name again, but this time, using lstat.  This is necessary in a
323              chroot on at least one system (glibc-2.3.6 + linux 2.6.12), where
324              .., ../.., ../../.., etc. all had the same device number, yet the
325              d_ino values for entries in / did not match those obtained
326              via lstat.  */
327           if (d == NULL && errno == 0 && use_d_ino)
328             {
329               use_d_ino = false;
330               __rewinddir (dirstream);
331               d = __readdir64 (dirstream);
332             }
333 
334           if (d == NULL)
335             {
336               if (errno == 0)
337                 /* EOF on dirstream, which can mean e.g., that the current
338                    directory has been removed.  */
339                 __set_errno (ENOENT);
340               goto lose;
341             }
342           if (d->d_name[0] == '.' &&
343               (d->d_name[1] == '\0' ||
344                (d->d_name[1] == '.' && d->d_name[2] == '\0')))
345             continue;
346 
347           if (use_d_ino)
348             {
349               bool match = (MATCHING_INO (d, thisino) || mount_point);
350               if (! match)
351                 continue;
352             }
353 
354           {
355             int entry_status;
356 #if HAVE_OPENAT_SUPPORT
357             entry_status = __fstatat64 (fd, d->d_name, &st, 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 (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