LCOV - code coverage report
Current view: top level - lib - getcwd.c (source / functions) Hit Total Coverage
Test: coreutils.info Lines: 66 109 60.6 %
Date: 2018-01-30 Functions: 1 1 100.0 %

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

Generated by: LCOV version 1.10