1  /* Copyright (C) 1991-2023 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 *
     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
     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    /* A size of 1 byte is never useful.  */
     191    if (allocated == 1)
     192      {
     193        __set_errno (ERANGE);
     194        return NULL;
     195      }
     196  
     197  #if HAVE_MINIMALLY_WORKING_GETCWD
     198    /* If AT_FDCWD is not defined, the algorithm below is O(N**2) and
     199       this is much slower than the system getcwd (at least on
     200       GNU/Linux).  So trust the system getcwd's results unless they
     201       look suspicious.
     202  
     203       Use the system getcwd even if we have openat support, since the
     204       system getcwd works even when a parent is unreadable, while the
     205       openat-based approach does not.
     206  
     207       But on AIX 5.1..7.1, the system getcwd is not even minimally
     208       working: If the current directory name is slightly longer than
     209       PATH_MAX, it omits the first directory component and returns
     210       this wrong result with errno = 0.  */
     211  
     212  # undef getcwd
     213    dir = getcwd_system (buf, size);
     214    if (dir || (size && errno == ERANGE))
     215      return dir;
     216  
     217    /* Solaris getcwd (NULL, 0) fails with errno == EINVAL, but it has
     218       internal magic that lets it work even if an ancestor directory is
     219       inaccessible, which is better in many cases.  So in this case try
     220       again with a buffer that's almost always big enough.  */
     221    if (errno == EINVAL && buf == NULL && size == 0)
     222      {
     223        char big_buffer[BIG_FILE_NAME_LENGTH + 1];
     224        dir = getcwd_system (big_buffer, sizeof big_buffer);
     225        if (dir)
     226          return strdup (dir);
     227      }
     228  
     229  # if HAVE_PARTLY_WORKING_GETCWD
     230    /* The system getcwd works, except it sometimes fails when it
     231       shouldn't, setting errno to ERANGE, ENAMETOOLONG, or ENOENT.    */
     232    if (errno != ERANGE && errno != ENAMETOOLONG && errno != ENOENT)
     233      return NULL;
     234  # endif
     235  #endif
     236    if (size == 0)
     237      {
     238        if (buf != NULL)
     239          {
     240            __set_errno (EINVAL);
     241            return NULL;
     242          }
     243  
     244        allocated = BIG_FILE_NAME_LENGTH + 1;
     245      }
     246  
     247    if (buf == NULL)
     248      {
     249        dir = malloc (allocated);
     250        if (dir == NULL)
     251          return NULL;
     252      }
     253    else
     254      dir = buf;
     255  
     256    dirp = dir + allocated;
     257    *--dirp = '\0';
     258  
     259    if (__lstat64_time64 (".", &st) < 0)
     260      goto lose;
     261    thisdev = st.st_dev;
     262    thisino = st.st_ino;
     263  
     264    if (__lstat64_time64 ("/", &st) < 0)
     265      goto lose;
     266    rootdev = st.st_dev;
     267    rootino = st.st_ino;
     268  
     269    while (!(thisdev == rootdev && thisino == rootino))
     270      {
     271        struct dirent64 *d;
     272        dev_t dotdev;
     273        ino_t dotino;
     274        bool mount_point;
     275        int parent_status;
     276        size_t dirroom;
     277        size_t namlen;
     278        bool use_d_ino = true;
     279  
     280        /* Look at the parent directory.  */
     281  #if HAVE_OPENAT_SUPPORT
     282        fd = __openat64 (fd, "..", O_RDONLY);
     283        if (fd < 0)
     284          goto lose;
     285        fd_needs_closing = true;
     286        parent_status = __fstat64_time64 (fd, &st);
     287  #else
     288        dotlist[dotlen++] = '.';
     289        dotlist[dotlen++] = '.';
     290        dotlist[dotlen] = '\0';
     291        parent_status = __lstat64_time64 (dotlist, &st);
     292  #endif
     293        if (parent_status != 0)
     294          goto lose;
     295  
     296        if (dirstream && __closedir (dirstream) != 0)
     297          {
     298            dirstream = NULL;
     299            goto lose;
     300          }
     301  
     302        /* Figure out if this directory is a mount point.  */
     303        dotdev = st.st_dev;
     304        dotino = st.st_ino;
     305        mount_point = dotdev != thisdev;
     306  
     307        /* Search for the last directory.  */
     308  #if HAVE_OPENAT_SUPPORT
     309        dirstream = __fdopendir (fd);
     310        if (dirstream == NULL)
     311          goto lose;
     312        fd_needs_closing = false;
     313  #else
     314        dirstream = __opendir (dotlist);
     315        if (dirstream == NULL)
     316          goto lose;
     317        dotlist[dotlen++] = '/';
     318  #endif
     319        for (;;)
     320          {
     321            /* Clear errno to distinguish EOF from error if readdir returns
     322               NULL.  */
     323            __set_errno (0);
     324            d = __readdir64 (dirstream);
     325  
     326            /* When we've iterated through all directory entries without finding
     327               one with a matching d_ino, rewind the stream and consider each
     328               name again, but this time, using lstat.  This is necessary in a
     329               chroot on at least one system (glibc-2.3.6 + linux 2.6.12), where
     330               .., ../.., ../../.., etc. all had the same device number, yet the
     331               d_ino values for entries in / did not match those obtained
     332               via lstat.  */
     333            if (d == NULL && errno == 0 && use_d_ino)
     334              {
     335                use_d_ino = false;
     336                __rewinddir (dirstream);
     337                d = __readdir64 (dirstream);
     338              }
     339  
     340            if (d == NULL)
     341              {
     342                if (errno == 0)
     343                  /* EOF on dirstream, which can mean e.g., that the current
     344                     directory has been removed.  */
     345                  __set_errno (ENOENT);
     346                goto lose;
     347              }
     348            if (d->d_name[0] == '.' &&
     349                (d->d_name[1] == '\0' ||
     350                 (d->d_name[1] == '.' && d->d_name[2] == '\0')))
     351              continue;
     352  
     353            if (use_d_ino)
     354              {
     355                bool match = (MATCHING_INO (d, thisino) || mount_point);
     356                if (! match)
     357                  continue;
     358              }
     359  
     360            {
     361              int entry_status;
     362  #if HAVE_OPENAT_SUPPORT
     363              entry_status = __fstatat64_time64 (fd, d->d_name, &st,
     364  					       AT_SYMLINK_NOFOLLOW);
     365  #else
     366              /* Compute size needed for this file name, or for the file
     367                 name ".." in the same directory, whichever is larger.
     368                 Room for ".." might be needed the next time through
     369                 the outer loop.  */
     370              size_t name_alloc = _D_ALLOC_NAMLEN (d);
     371              size_t filesize = dotlen + MAX (sizeof "..", name_alloc);
     372  
     373              if (filesize < dotlen)
     374                goto memory_exhausted;
     375  
     376              if (dotsize < filesize)
     377                {
     378                  /* My, what a deep directory tree you have, Grandma.  */
     379                  size_t newsize = MAX (filesize, dotsize * 2);
     380                  size_t i;
     381                  if (newsize < dotsize)
     382                    goto memory_exhausted;
     383                  if (dotlist != dots)
     384                    free (dotlist);
     385                  dotlist = malloc (newsize);
     386                  if (dotlist == NULL)
     387                    goto lose;
     388                  dotsize = newsize;
     389  
     390                  i = 0;
     391                  do
     392                    {
     393                      dotlist[i++] = '.';
     394                      dotlist[i++] = '.';
     395                      dotlist[i++] = '/';
     396                    }
     397                  while (i < dotlen);
     398                }
     399  
     400              memcpy (dotlist + dotlen, d->d_name, _D_ALLOC_NAMLEN (d));
     401              entry_status = __lstat64_time64 (dotlist, &st);
     402  #endif
     403              /* We don't fail here if we cannot stat() a directory entry.
     404                 This can happen when (network) file systems fail.  If this
     405                 entry is in fact the one we are looking for we will find
     406                 out soon as we reach the end of the directory without
     407                 having found anything.  */
     408              if (entry_status == 0 && S_ISDIR (st.st_mode)
     409                  && st.st_dev == thisdev && st.st_ino == thisino)
     410                break;
     411            }
     412          }
     413  
     414        dirroom = dirp - dir;
     415        namlen = _D_EXACT_NAMLEN (d);
     416  
     417        if (dirroom <= namlen)
     418          {
     419            if (size != 0)
     420              {
     421                __set_errno (ERANGE);
     422                goto lose;
     423              }
     424            else
     425              {
     426                char *tmp;
     427                size_t oldsize = allocated;
     428  
     429                allocated += MAX (allocated, namlen);
     430                if (allocated < oldsize
     431                    || ! (tmp = realloc (dir, allocated)))
     432                  goto memory_exhausted;
     433  
     434                /* Move current contents up to the end of the buffer.
     435                   This is guaranteed to be non-overlapping.  */
     436                dirp = memcpy (tmp + allocated - (oldsize - dirroom),
     437                               tmp + dirroom,
     438                               oldsize - dirroom);
     439                dir = tmp;
     440              }
     441          }
     442        dirp -= namlen;
     443        memcpy (dirp, d->d_name, namlen);
     444        *--dirp = '/';
     445  
     446        thisdev = dotdev;
     447        thisino = dotino;
     448      }
     449  
     450    if (dirstream && __closedir (dirstream) != 0)
     451      {
     452        dirstream = NULL;
     453        goto lose;
     454      }
     455  
     456    if (dirp == &dir[allocated - 1])
     457      *--dirp = '/';
     458  
     459  #if ! HAVE_OPENAT_SUPPORT
     460    if (dotlist != dots)
     461      free (dotlist);
     462  #endif
     463  
     464    used = dir + allocated - dirp;
     465    memmove (dir, dirp, used);
     466  
     467    if (size == 0)
     468      /* Ensure that the buffer is only as large as necessary.  */
     469      buf = (used < allocated ? realloc (dir, used) : dir);
     470  
     471    if (buf == NULL)
     472      /* Either buf was NULL all along, or 'realloc' failed but
     473         we still have the original string.  */
     474      buf = dir;
     475  
     476    return buf;
     477  
     478   memory_exhausted:
     479    __set_errno (ENOMEM);
     480   lose:
     481    {
     482      int save = errno;
     483      if (dirstream)
     484        __closedir (dirstream);
     485  #if HAVE_OPENAT_SUPPORT
     486      if (fd_needs_closing)
     487         __close_nocancel_nostatus (fd);
     488  #else
     489      if (dotlist != dots)
     490        free (dotlist);
     491  #endif
     492      if (buf == NULL)
     493        free (dir);
     494      __set_errno (save);
     495    }
     496    return NULL;
     497  }
     498  
     499  #if defined _LIBC && !defined GETCWD_RETURN_TYPE
     500  libc_hidden_def (__getcwd)
     501  weak_alias (__getcwd, getcwd)
     502  #endif