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