root/maint/gnulib/lib/getcwd.c

/* [previous][next][first][last][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. getcwd_nothrow
  2. __getcwd_generic

   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)
     /* [previous][next][first][last][top][bottom][index][help] */
 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)
     /* [previous][next][first][last][top][bottom][index][help] */
 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

/* [previous][next][first][last][top][bottom][index][help] */