(root)/
bison-3.8.2/
lib/
getcwd.c
       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 *
     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
     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