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