summaryrefslogtreecommitdiff
path: root/sysdeps/posix
diff options
context:
space:
mode:
authorAdhemerval Zanella <adhemerval.zanella@linaro.org>2020-08-20 09:04:16 -0300
committerAdhemerval Zanella <adhemerval.zanella@linaro.org>2020-09-02 09:16:05 -0300
commitfcdbd910679997f257de70e9e49801dc4db93222 (patch)
treea025ab7a3ac968496152fc2842c19c56d33cca64 /sysdeps/posix
parent23af890b3f04e80da783ba64e6b6d94822e01d54 (diff)
downloadglibc-fcdbd910679997f257de70e9e49801dc4db93222.tar.gz
Sync getcwd with gnulib
This is the first of a series of patches to sync with Gnulib commit 615b43e1f9. This patch adopts most of the changes of Gnulib, except it retains GETCWD_RETURN_TYPE and does not always use a 64-bit internal API. These remaining discrepancies will be addressed in later patches in this series. Checked on x86_64-linux-gnu and i686-linux-gnu.
Diffstat (limited to 'sysdeps/posix')
-rw-r--r--sysdeps/posix/getcwd.c785
1 files changed, 371 insertions, 414 deletions
diff --git a/sysdeps/posix/getcwd.c b/sysdeps/posix/getcwd.c
index f00b337a13..e466a4a8bf 100644
--- a/sysdeps/posix/getcwd.c
+++ b/sysdeps/posix/getcwd.c
@@ -15,522 +15,479 @@
License along with the GNU C Library; if not, see
<https://www.gnu.org/licenses/>. */
-/* Wants:
- AC_STDC_HEADERS
- AC_DIR_HEADER
- AC_UNISTD_H
- AC_MEMORY_H
- AC_CONST
- AC_ALLOCA
- */
-
-/* AIX requires this to be the first thing in the file. */
-#if defined _AIX && !defined __GNUC__
- #pragma alloca
-#endif
-
-#ifdef HAVE_CONFIG_H
-# include "config.h"
+#if !_LIBC
+# include <config.h>
+# include <unistd.h>
+# include "pathmax.h"
+#else
+# define HAVE_OPENAT 1
+# define D_INO_IN_DIRENT 1
+# define HAVE_MSVC_INVALID_PARAMETER_HANDLER 0
+# define HAVE_MINIMALLY_WORKING_GETCWD 0
#endif
#include <errno.h>
-#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
+#include <stdbool.h>
+#include <stddef.h>
-#ifdef STDC_HEADERS
-# include <stddef.h>
-#endif
+#include <fcntl.h> /* For AT_FDCWD on Solaris 9. */
-#if !defined __GNU_LIBRARY__ && !defined STDC_HEADERS
-extern int errno;
+/* If this host provides the openat function or if we're using the
+ gnulib replacement function with a native fdopendir, then enable
+ code below to make getcwd more efficient and robust. */
+#if defined HAVE_OPENAT || (defined GNULIB_OPENAT && defined HAVE_FDOPENDIR)
+# define HAVE_OPENAT_SUPPORT 1
+#else
+# define HAVE_OPENAT_SUPPORT 0
#endif
+
#ifndef __set_errno
-# define __set_errno(val) errno = (val)
+# define __set_errno(val) (errno = (val))
#endif
-#ifndef NULL
-# define NULL 0
+#include <dirent.h>
+#ifndef _D_EXACT_NAMLEN
+# define _D_EXACT_NAMLEN(d) strlen ((d)->d_name)
#endif
-
-#if defined USGr3 && !defined DIRENT
-# define DIRENT
-#endif /* USGr3 */
-#if defined Xenix && !defined SYSNDIR
-# define SYSNDIR
-#endif /* Xenix */
-
-#if defined POSIX || defined DIRENT || defined __GNU_LIBRARY__
-# include <dirent.h>
-# ifndef __GNU_LIBRARY__
-# define D_NAMLEN(d) strlen((d)->d_name)
-# else
-# define HAVE_D_NAMLEN
-# define D_NAMLEN(d) ((d)->d_namlen)
-# endif
-#else /* not POSIX or DIRENT */
-# define dirent direct
-# define D_NAMLEN(d) ((d)->d_namlen)
-# define HAVE_D_NAMLEN
-# if defined USG && !defined sgi
-# if defined SYSNDIR
-# include <sys/ndir.h>
-# else /* Not SYSNDIR */
-# include "ndir.h"
-# endif /* SYSNDIR */
-# else /* not USG */
-# include <sys/dir.h>
-# endif /* USG */
-#endif /* POSIX or DIRENT or __GNU_LIBRARY__ */
-
-#if defined HAVE_UNISTD_H || defined __GNU_LIBRARY__
-# include <unistd.h>
+#ifndef _D_ALLOC_NAMLEN
+# define _D_ALLOC_NAMLEN(d) (_D_EXACT_NAMLEN (d) + 1)
#endif
-#if defined STDC_HEADERS || defined __GNU_LIBRARY__ || defined POSIX
-# include <stdlib.h>
-# include <string.h>
-# define ANSI_STRING
-#else /* No standard headers. */
-
-# ifdef USG
-
-# include <string.h>
-# ifdef NEED_MEMORY_H
-# include <memory.h>
-# endif
-# define ANSI_STRING
-
-# else /* Not USG. */
-
-# ifdef NeXT
-
-# include <string.h>
-
-# else /* Not NeXT. */
-
-# include <strings.h>
-
-# ifndef bcmp
-extern int bcmp ();
-# endif
-# ifndef bzero
-extern void bzero ();
-# endif
-# ifndef bcopy
-extern void bcopy ();
-# endif
-
-# endif /* NeXT. */
-
-# endif /* USG. */
-
-extern char *malloc (), *realloc ();
-extern void free ();
-
-#endif /* Standard headers. */
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
-#ifndef ANSI_STRING
-# define memcpy(d, s, n) bcopy((s), (d), (n))
-# define memmove memcpy
-#endif /* Not ANSI_STRING. */
-
-#ifndef MAX
-# define MAX(a, b) ((a) < (b) ? (b) : (a))
-#endif
-
-#ifdef _LIBC
+#if _LIBC
# ifndef mempcpy
# define mempcpy __mempcpy
# endif
-# define HAVE_MEMPCPY 1
#endif
-#if !defined __alloca && !defined __GNU_LIBRARY__
-
-# ifdef __GNUC__
-# undef alloca
-# define alloca(n) __builtin_alloca (n)
-# else /* Not GCC. */
-# if defined sparc || defined HAVE_ALLOCA_H
-# include <alloca.h>
-# else /* Not sparc or HAVE_ALLOCA_H. */
-# ifndef _AIX
-extern char *alloca ();
-# endif /* Not _AIX. */
-# endif /* sparc or HAVE_ALLOCA_H. */
-# endif /* GCC. */
-
-# define __alloca alloca
-
+#ifndef MAX
+# define MAX(a, b) ((a) < (b) ? (b) : (a))
+#endif
+#ifndef MIN
+# define MIN(a, b) ((a) < (b) ? (a) : (b))
#endif
-#if defined HAVE_LIMITS_H || defined STDC_HEADERS || defined __GNU_LIBRARY__
-# include <limits.h>
-#else
-# include <sys/param.h>
+/* In this file, PATH_MAX only serves as a threshold for choosing among two
+ algorithms. */
+#ifndef PATH_MAX
+# define PATH_MAX 8192
#endif
-#if defined _LIBC
-# include <not-cancel.h>
-# include <kernel-features.h>
+#if D_INO_IN_DIRENT
+# define MATCHING_INO(dp, ino) ((dp)->d_ino == (ino))
#else
-# define __openat64_nocancel(dfd, name, mode) openat64 (dfd, name, mode)
-# define __close_nocancel_nostatus(fd) close (fd)
+# define MATCHING_INO(dp, ino) true
#endif
-#ifndef PATH_MAX
-# ifdef MAXPATHLEN
-# define PATH_MAX MAXPATHLEN
-# else
-# define PATH_MAX 1024
-# endif
+#if HAVE_MSVC_INVALID_PARAMETER_HANDLER
+# include "msvc-inval.h"
#endif
-#if !defined STDC_HEADERS && !defined __GNU_LIBRARY__
-# undef size_t
-# define size_t unsigned int
+#if !_LIBC
+# define __close_nocancel_nostatus close
+# define __getcwd_generic rpl_getcwd
+# define stat64 stat
+# define __fstat64 fstat
+# define __fstatat64 fstatat
+# define __lstat64 lstat
+# define __closedir closedir
+# define __opendir opendir
+# define __readdir readdir
+# define __fdopendir fdopendir
+# define __openat openat
+# define __rewinddir rewinddir
+# define __openat64 openat
+#else
+# include <not-cancel.h>
#endif
-#ifndef __GNU_LIBRARY__
-# define __lstat64 stat64
+/* The results of opendir() in this file are not used with dirfd and fchdir,
+ and we do not leak fds to any single-threaded code that could use stdio,
+ therefore save some unnecessary recursion in fchdir.c.
+ FIXME - if the kernel ever adds support for multi-thread safety for
+ avoiding standard fds, then we should use opendir_safer and
+ openat_safer. */
+#ifdef GNULIB_defined_opendir
+# undef opendir
#endif
-
-#ifndef _LIBC
-# define __rewinddir rewinddir
+#ifdef GNULIB_defined_closedir
+# undef closedir
#endif
-#ifndef _LIBC
-# define __getcwd getcwd
-#endif
+#if defined _WIN32 && !defined __CYGWIN__
+# if HAVE_MSVC_INVALID_PARAMETER_HANDLER
+static char *
+getcwd_nothrow (char *buf, size_t size)
+{
+ char *result;
-#ifndef GETCWD_RETURN_TYPE
-# define GETCWD_RETURN_TYPE char *
-#endif
+ TRY_MSVC_INVAL
+ {
+ result = _getcwd (buf, size);
+ }
+ CATCH_MSVC_INVAL
+ {
+ result = NULL;
+ errno = ERANGE;
+ }
+ DONE_MSVC_INVAL;
-#ifdef __ASSUME_ATFCTS
-# define __have_atfcts 1
-#elif IS_IN (rtld)
-static int __rtld_have_atfcts;
-# define __have_atfcts __rtld_have_atfcts
+ return result;
+}
+# else
+# define getcwd_nothrow _getcwd
+# endif
+# define getcwd_system getcwd_nothrow
+#else
+# define getcwd_system getcwd
#endif
-/* Get the pathname of the current working directory, and put it in SIZE
- bytes of BUF. Returns NULL if the directory couldn't be determined or
- SIZE was too small. If successful, returns BUF. In GNU, if BUF is
- NULL, an array is allocated with `malloc'; the array is SIZE bytes long,
- unless SIZE == 0, in which case it is as big as necessary. */
+/* Get the name of the current working directory, and put it in SIZE
+ bytes of BUF. Returns NULL with errno set if the directory couldn't be
+ determined or SIZE was too small. If successful, returns BUF. In GNU,
+ if BUF is NULL, an array is allocated with 'malloc'; the array is SIZE
+ bytes long, unless SIZE == 0, in which case it is as big as necessary. */
GETCWD_RETURN_TYPE
-__getcwd (char *buf, size_t size)
+__getcwd_generic (char *buf, size_t size)
{
-#ifndef __ASSUME_ATFCTS
- static const char dots[]
- = "../../../../../../../../../../../../../../../../../../../../../../../\
-../../../../../../../../../../../../../../../../../../../../../../../../../../\
-../../../../../../../../../../../../../../../../../../../../../../../../../..";
- const char *dotp = &dots[sizeof (dots)];
- const char *dotlist = dots;
- size_t dotsize = sizeof (dots) - 1;
+ /* Lengths of big file name components and entire file names, and a
+ deep level of file name nesting. These numbers are not upper
+ bounds; they are merely large values suitable for initial
+ allocations, designed to be large enough for most real-world
+ uses. */
+ enum
+ {
+ BIG_FILE_NAME_COMPONENT_LENGTH = 255,
+ BIG_FILE_NAME_LENGTH = MIN (4095, PATH_MAX - 1),
+ DEEP_NESTING = 100
+ };
+
+#if HAVE_OPENAT_SUPPORT
+ int fd = AT_FDCWD;
+ bool fd_needs_closing = false;
+#else
+ char dots[DEEP_NESTING * sizeof ".." + BIG_FILE_NAME_COMPONENT_LENGTH + 1];
+ char *dotlist = dots;
+ size_t dotsize = sizeof dots;
+ size_t dotlen = 0;
#endif
- int prev_errno = errno;
DIR *dirstream = NULL;
- bool fd_needs_closing = false;
- int fd = AT_FDCWD;
-
- char *path;
-#ifndef NO_ALLOCATION
+ dev_t rootdev, thisdev;
+ ino_t rootino, thisino;
+ char *dir;
+ register char *dirp;
+ struct stat64 st;
size_t allocated = size;
+ size_t used;
+
+#if HAVE_MINIMALLY_WORKING_GETCWD
+ /* If AT_FDCWD is not defined, the algorithm below is O(N**2) and
+ this is much slower than the system getcwd (at least on
+ GNU/Linux). So trust the system getcwd's results unless they
+ look suspicious.
+
+ Use the system getcwd even if we have openat support, since the
+ system getcwd works even when a parent is unreadable, while the
+ openat-based approach does not.
+
+ But on AIX 5.1..7.1, the system getcwd is not even minimally
+ working: If the current directory name is slightly longer than
+ PATH_MAX, it omits the first directory component and returns
+ this wrong result with errno = 0. */
+
+# undef getcwd
+ dir = getcwd_system (buf, size);
+ if (dir || (size && errno == ERANGE))
+ return dir;
+
+ /* Solaris getcwd (NULL, 0) fails with errno == EINVAL, but it has
+ internal magic that lets it work even if an ancestor directory is
+ inaccessible, which is better in many cases. So in this case try
+ again with a buffer that's almost always big enough. */
+ if (errno == EINVAL && buf == NULL && size == 0)
+ {
+ char big_buffer[BIG_FILE_NAME_LENGTH + 1];
+ dir = getcwd_system (big_buffer, sizeof big_buffer);
+ if (dir)
+ return strdup (dir);
+ }
+
+# if HAVE_PARTLY_WORKING_GETCWD
+ /* The system getcwd works, except it sometimes fails when it
+ shouldn't, setting errno to ERANGE, ENAMETOOLONG, or ENOENT. */
+ if (errno != ERANGE && errno != ENAMETOOLONG && errno != ENOENT)
+ return NULL;
+# endif
+#endif
if (size == 0)
{
if (buf != NULL)
- {
- __set_errno (EINVAL);
- return NULL;
- }
+ {
+ __set_errno (EINVAL);
+ return NULL;
+ }
- allocated = PATH_MAX + 1;
+ allocated = BIG_FILE_NAME_LENGTH + 1;
}
if (buf == NULL)
{
- path = malloc (allocated);
- if (path == NULL)
- return NULL;
+ dir = malloc (allocated);
+ if (dir == NULL)
+ return NULL;
}
else
-#else
-# define allocated size
-#endif
- path = buf;
+ dir = buf;
- char *pathp = path + allocated;
- *--pathp = '\0';
+ dirp = dir + allocated;
+ *--dirp = '\0';
- struct stat64 st;
if (__lstat64 (".", &st) < 0)
goto lose;
- dev_t thisdev = st.st_dev;
- ino_t thisino = st.st_ino;
+ thisdev = st.st_dev;
+ thisino = st.st_ino;
if (__lstat64 ("/", &st) < 0)
goto lose;
- dev_t rootdev = st.st_dev;
- ino_t rootino = st.st_ino;
+ rootdev = st.st_dev;
+ rootino = st.st_ino;
while (!(thisdev == rootdev && thisino == rootino))
{
- if (__have_atfcts >= 0)
- fd = __openat64_nocancel (fd, "..", O_RDONLY | O_CLOEXEC);
- else
- fd = -1;
- if (fd >= 0)
- {
- fd_needs_closing = true;
- if (__fstat64 (fd, &st) < 0)
- goto lose;
- }
-#ifndef __ASSUME_ATFCTS
- else if (errno == ENOSYS)
- {
- __have_atfcts = -1;
-
- /* Look at the parent directory. */
- if (dotp == dotlist)
- {
-# ifdef NO_ALLOCATION
- __set_errno (ENOMEM);
- goto lose;
-# else
- /* My, what a deep directory tree you have, Grandma. */
- char *new;
- if (dotlist == dots)
- {
- new = malloc (dotsize * 2 + 1);
- if (new == NULL)
- goto lose;
-# ifdef HAVE_MEMPCPY
- dotp = mempcpy (new, dots, dotsize);
-# else
- memcpy (new, dots, dotsize);
- dotp = &new[dotsize];
-# endif
- }
- else
- {
- new = realloc ((void *) dotlist, dotsize * 2 + 1);
- if (new == NULL)
- goto lose;
- dotp = &new[dotsize];
- }
-# ifdef HAVE_MEMPCPY
- *((char *) mempcpy ((char *) dotp, new, dotsize)) = '\0';
- dotsize *= 2;
-# else
- memcpy ((char *) dotp, new, dotsize);
- dotsize *= 2;
- new[dotsize] = '\0';
-# endif
- dotlist = new;
-# endif
- }
-
- dotp -= 3;
+ struct dirent *d;
+ dev_t dotdev;
+ ino_t dotino;
+ bool mount_point;
+ int parent_status;
+ size_t dirroom;
+ size_t namlen;
+ bool use_d_ino = true;
- /* Figure out if this directory is a mount point. */
- if (__lstat64 (dotp, &st) < 0)
- goto lose;
- }
+ /* Look at the parent directory. */
+#if HAVE_OPENAT_SUPPORT
+ fd = __openat64 (fd, "..", O_RDONLY);
+ if (fd < 0)
+ goto lose;
+ fd_needs_closing = true;
+ parent_status = __fstat64 (fd, &st);
+#else
+ dotlist[dotlen++] = '.';
+ dotlist[dotlen++] = '.';
+ dotlist[dotlen] = '\0';
+ parent_status = __lstat64 (dotlist, &st);
#endif
- else
- goto lose;
+ if (parent_status != 0)
+ goto lose;
if (dirstream && __closedir (dirstream) != 0)
- {
- dirstream = NULL;
- goto lose;
- }
+ {
+ dirstream = NULL;
+ goto lose;
+ }
- dev_t dotdev = st.st_dev;
- ino_t dotino = st.st_ino;
- bool mount_point = dotdev != thisdev;
+ /* Figure out if this directory is a mount point. */
+ dotdev = st.st_dev;
+ dotino = st.st_ino;
+ mount_point = dotdev != thisdev;
/* Search for the last directory. */
- if (__have_atfcts >= 0)
- dirstream = __fdopendir (fd);
-#ifndef __ASSUME_ATFCTS
- else
- dirstream = __opendir (dotp);
-#endif
+#if HAVE_OPENAT_SUPPORT
+ dirstream = __fdopendir (fd);
if (dirstream == NULL)
- goto lose;
+ goto lose;
fd_needs_closing = false;
-
- struct dirent *d;
- bool use_d_ino = true;
- while (1)
- {
- /* Clear errno to distinguish EOF from error if readdir returns
- NULL. */
- __set_errno (0);
- d = __readdir (dirstream);
- if (d == NULL)
- {
- if (errno == 0)
- {
- /* When we've iterated through all directory entries
- without finding one with a matching d_ino, rewind the
- stream and consider each name again, but this time, using
- lstat64. This is necessary in a chroot on at least one
- system. */
- if (use_d_ino)
- {
- use_d_ino = false;
- __rewinddir (dirstream);
- continue;
- }
-
- /* EOF on dirstream, which means that the current directory
- has been removed. */
- __set_errno (ENOENT);
- }
- goto lose;
- }
-
-#ifdef _DIRENT_HAVE_D_TYPE
- if (d->d_type != DT_DIR && d->d_type != DT_UNKNOWN)
- continue;
-#endif
- if (d->d_name[0] == '.'
- && (d->d_name[1] == '\0'
- || (d->d_name[1] == '.' && d->d_name[2] == '\0')))
- continue;
- if (use_d_ino && !mount_point && (ino_t) d->d_ino != thisino)
- continue;
-
- if (__have_atfcts >= 0)
- {
- /* We don't fail here if we cannot stat64() a directory entry.
- This can happen when (network) filesystems fail. If this
- entry is in fact the one we are looking for we will find
- out soon as we reach the end of the directory without
- having found anything. */
- if (__fstatat64 (fd, d->d_name, &st, AT_SYMLINK_NOFOLLOW) < 0)
- continue;
- }
-#ifndef __ASSUME_ATFCTS
- else
- {
- char name[dotlist + dotsize - dotp + 1 + _D_ALLOC_NAMLEN (d)];
-# ifdef HAVE_MEMPCPY
- char *tmp = mempcpy (name, dotp, dotlist + dotsize - dotp);
- *tmp++ = '/';
- strcpy (tmp, d->d_name);
-# else
- memcpy (name, dotp, dotlist + dotsize - dotp);
- name[dotlist + dotsize - dotp] = '/';
- strcpy (&name[dotlist + dotsize - dotp + 1], d->d_name);
-# endif
- /* We don't fail here if we cannot stat64() a directory entry.
- This can happen when (network) filesystems fail. If this
- entry is in fact the one we are looking for we will find
- out soon as we reach the end of the directory without
- having found anything. */
- if (__lstat64 (name, &st) < 0)
- continue;
- }
+#else
+ dirstream = __opendir (dotlist);
+ if (dirstream == NULL)
+ goto lose;
+ dotlist[dotlen++] = '/';
#endif
- if (S_ISDIR (st.st_mode)
- && st.st_dev == thisdev && st.st_ino == thisino)
- break;
- }
-
- size_t namlen = _D_EXACT_NAMLEN (d);
-
- if ((size_t) (pathp - path) <= namlen)
- {
-#ifndef NO_ALLOCATION
- if (size == 0)
- {
- size_t oldsize = allocated;
-
- allocated = 2 * MAX (allocated, namlen);
- char *tmp = realloc (path, allocated);
- if (tmp == NULL)
- goto lose;
-
- /* Move current contents up to the end of the buffer.
- This is guaranteed to be non-overlapping. */
- pathp = memcpy (tmp + allocated - (path + oldsize - pathp),
- tmp + (pathp - path),
- path + oldsize - pathp);
- path = tmp;
- }
- else
+ for (;;)
+ {
+ /* Clear errno to distinguish EOF from error if readdir returns
+ NULL. */
+ __set_errno (0);
+ d = __readdir (dirstream);
+
+ /* When we've iterated through all directory entries without finding
+ one with a matching d_ino, rewind the stream and consider each
+ name again, but this time, using lstat. This is necessary in a
+ chroot on at least one system (glibc-2.3.6 + linux 2.6.12), where
+ .., ../.., ../../.., etc. all had the same device number, yet the
+ d_ino values for entries in / did not match those obtained
+ via lstat. */
+ if (d == NULL && errno == 0 && use_d_ino)
+ {
+ use_d_ino = false;
+ __rewinddir (dirstream);
+ d = __readdir (dirstream);
+ }
+
+ if (d == NULL)
+ {
+ if (errno == 0)
+ /* EOF on dirstream, which can mean e.g., that the current
+ directory has been removed. */
+ __set_errno (ENOENT);
+ goto lose;
+ }
+ if (d->d_name[0] == '.' &&
+ (d->d_name[1] == '\0' ||
+ (d->d_name[1] == '.' && d->d_name[2] == '\0')))
+ continue;
+
+ if (use_d_ino)
+ {
+ bool match = (MATCHING_INO (d, thisino) || mount_point);
+ if (! match)
+ continue;
+ }
+
+ {
+ int entry_status;
+#if HAVE_OPENAT_SUPPORT
+ entry_status = __fstatat64 (fd, d->d_name, &st, AT_SYMLINK_NOFOLLOW);
+#else
+ /* Compute size needed for this file name, or for the file
+ name ".." in the same directory, whichever is larger.
+ Room for ".." might be needed the next time through
+ the outer loop. */
+ size_t name_alloc = _D_ALLOC_NAMLEN (d);
+ size_t filesize = dotlen + MAX (sizeof "..", name_alloc);
+
+ if (filesize < dotlen)
+ goto memory_exhausted;
+
+ if (dotsize < filesize)
+ {
+ /* My, what a deep directory tree you have, Grandma. */
+ size_t newsize = MAX (filesize, dotsize * 2);
+ size_t i;
+ if (newsize < dotsize)
+ goto memory_exhausted;
+ if (dotlist != dots)
+ free (dotlist);
+ dotlist = malloc (newsize);
+ if (dotlist == NULL)
+ goto lose;
+ dotsize = newsize;
+
+ i = 0;
+ do
+ {
+ dotlist[i++] = '.';
+ dotlist[i++] = '.';
+ dotlist[i++] = '/';
+ }
+ while (i < dotlen);
+ }
+
+ memcpy (dotlist + dotlen, d->d_name, _D_ALLOC_NAMLEN (d));
+ entry_status = __lstat64 (dotlist, &st);
#endif
- {
- __set_errno (ERANGE);
- goto lose;
- }
- }
- pathp -= namlen;
- (void) memcpy (pathp, d->d_name, namlen);
- *--pathp = '/';
+ /* We don't fail here if we cannot stat() a directory entry.
+ This can happen when (network) file systems fail. If this
+ entry is in fact the one we are looking for we will find
+ out soon as we reach the end of the directory without
+ having found anything. */
+ if (entry_status == 0 && S_ISDIR (st.st_mode)
+ && st.st_dev == thisdev && st.st_ino == thisino)
+ break;
+ }
+ }
+
+ dirroom = dirp - dir;
+ namlen = _D_EXACT_NAMLEN (d);
+
+ if (dirroom <= namlen)
+ {
+ if (size != 0)
+ {
+ __set_errno (ERANGE);
+ goto lose;
+ }
+ else
+ {
+ char *tmp;
+ size_t oldsize = allocated;
+
+ allocated += MAX (allocated, namlen);
+ if (allocated < oldsize
+ || ! (tmp = realloc (dir, allocated)))
+ goto memory_exhausted;
+
+ /* Move current contents up to the end of the buffer.
+ This is guaranteed to be non-overlapping. */
+ dirp = memcpy (tmp + allocated - (oldsize - dirroom),
+ tmp + dirroom,
+ oldsize - dirroom);
+ dir = tmp;
+ }
+ }
+ dirp -= namlen;
+ memcpy (dirp, d->d_name, namlen);
+ *--dirp = '/';
thisdev = dotdev;
thisino = dotino;
}
- if (dirstream != NULL && __closedir (dirstream) != 0)
+ if (dirstream && __closedir (dirstream) != 0)
{
dirstream = NULL;
goto lose;
}
- if (pathp == &path[allocated - 1])
- *--pathp = '/';
+ if (dirp == &dir[allocated - 1])
+ *--dirp = '/';
-#ifndef __ASSUME_ATFCTS
+#if ! HAVE_OPENAT_SUPPORT
if (dotlist != dots)
- free ((void *) dotlist);
+ free (dotlist);
#endif
- size_t used = path + allocated - pathp;
- memmove (path, pathp, used);
+ used = dir + allocated - dirp;
+ memmove (dir, dirp, used);
if (size == 0)
/* Ensure that the buffer is only as large as necessary. */
- buf = realloc (path, used);
+ buf = (used < allocated ? realloc (dir, used) : dir);
if (buf == NULL)
- /* Either buf was NULL all along, or `realloc' failed but
+ /* Either buf was NULL all along, or 'realloc' failed but
we still have the original string. */
- buf = path;
-
- /* Restore errno on successful return. */
- __set_errno (prev_errno);
+ buf = dir;
return buf;
- lose:;
- int save_errno = errno;
-#ifndef __ASSUME_ATFCTS
- if (dotlist != dots)
- free ((void *) dotlist);
-#endif
- if (dirstream != NULL)
- __closedir (dirstream);
- if (fd_needs_closing)
- __close_nocancel_nostatus (fd);
-#ifndef NO_ALLOCATION
- if (buf == NULL)
- free (path);
+ memory_exhausted:
+ __set_errno (ENOMEM);
+ lose:
+ {
+ int save = errno;
+ if (dirstream)
+ __closedir (dirstream);
+#if HAVE_OPENAT_SUPPORT
+ if (fd_needs_closing)
+ __close_nocancel_nostatus (fd);
+#else
+ if (dotlist != dots)
+ free (dotlist);
#endif
- __set_errno (save_errno);
+ if (buf == NULL)
+ free (dir);
+ __set_errno (save);
+ }
return NULL;
}
-#if defined _LIBC && !defined __getcwd
+#if defined _LIBC && !defined GETCWD_RETURN_TYPE
libc_hidden_def (__getcwd)
weak_alias (__getcwd, getcwd)
#endif