-/* Copyright (C) 1991,92,93,94,95,96,97,98,99,2004,2005 Free Software
+/* Copyright (C) 1991,92,93,94,95,96,97,98,99,2004,2005,2006 Free Software
Foundation, Inc.
This file is part of the GNU C Library.
with this program; if not, write to the Free Software Foundation,
Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
-#ifdef HAVE_CONFIG_H
-# include "config.h"
+#ifdef HAVE_CONFIG_H
+# include <config.h>
#endif
#if !_LIBC
# define __set_errno(val) (errno = (val))
#endif
-#if HAVE_DIRENT_H || _LIBC
-# include <dirent.h>
-# ifndef _D_EXACT_NAMLEN
-# define _D_EXACT_NAMLEN(d) strlen ((d)->d_name)
-# endif
-#else
-# define dirent direct
-# if HAVE_SYS_NDIR_H
-# include <sys/ndir.h>
-# endif
-# if HAVE_SYS_DIR_H
-# include <sys/dir.h>
-# endif
-# if HAVE_NDIR_H
-# include <ndir.h>
-# endif
-#endif
+#include <dirent.h>
#ifndef _D_EXACT_NAMLEN
-# define _D_EXACT_NAMLEN(d) ((d)->d_namlen)
+# define _D_EXACT_NAMLEN(d) strlen ((d)->d_name)
#endif
#ifndef _D_ALLOC_NAMLEN
# define _D_ALLOC_NAMLEN(d) (_D_EXACT_NAMLEN (d) + 1)
#endif
-#if HAVE_UNISTD_H || _LIBC
-# include <unistd.h>
-#endif
-
+#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>
+/* Work around a bug in Solaris 9 and 10: AT_FDCWD is positive. Its
+ value exceeds INT_MAX, so its use as an int doesn't conform to the
+ C standard, and GCC and Sun C complain in some cases. */
+#if 0 < AT_FDCWD && AT_FDCWD == 0xffd19553
+# undef AT_FDCWD
+# define AT_FDCWD (-3041965)
+#endif
+
#ifdef ENAMETOOLONG
# define is_ENAMETOOLONG(x) ((x) == ENAMETOOLONG)
#else
ino_t dotino;
bool mount_point;
int parent_status;
+ size_t dirroom;
+ size_t namlen;
+ bool use_d_ino = true;
/* Look at the parent directory. */
#ifdef AT_FDCWD
goto lose;
dotlist[dotlen++] = '/';
#endif
- /* Clear errno to distinguish EOF from error if readdir returns
- NULL. */
- __set_errno (0);
- while ((d = __readdir (dirstream)) != NULL)
+ 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 (MATCHING_INO (d, thisino) || mount_point)
+
+ if (use_d_ino)
{
- int entry_status;
+ bool match = (MATCHING_INO (d, thisino) || mount_point);
+ if (! match)
+ continue;
+ }
+
+ {
+ int entry_status;
#ifdef AT_FDCWD
- entry_status = fstatat (fd, d->d_name, &st, AT_SYMLINK_NOFOLLOW);
+ entry_status = fstatat (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);
- }
-
- strcpy (dotlist + dotlen, d->d_name);
- entry_status = __lstat (dotlist, &st);
+ /* 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 = __lstat (dotlist, &st);
#endif
- /* 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;
- }
+ /* 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;
+ }
}
- if (d == NULL)
- {
- if (errno == 0)
- /* EOF on dirstream, which means that the current directory
- has been removed. */
- __set_errno (ENOENT);
- goto lose;
- }
- else
- {
- size_t dirroom = dirp - dir;
- size_t namlen = _D_EXACT_NAMLEN (d);
- if (dirroom <= namlen)
+ dirroom = dirp - dir;
+ namlen = _D_EXACT_NAMLEN (d);
+
+ if (dirroom <= namlen)
+ {
+ if (size != 0)
{
- 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;
- }
+ __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 = '/';
}
+ dirp -= namlen;
+ memcpy (dirp, d->d_name, namlen);
+ *--dirp = '/';
thisdev = dotdev;
thisino = dotino;