summary refs log tree commit diff
diff options
context:
space:
mode:
authorUlrich Drepper <drepper@gmail.com>2011-08-09 09:57:55 -0400
committerUlrich Drepper <drepper@gmail.com>2011-08-09 09:57:55 -0400
commitc55fbd1ea768f9fdef34a01377702c0d72cbc213 (patch)
tree0e67d339ad240756843292384535c40cad03df95
parent879165f25a1a6b13995e43c11e88b1a21b6f101e (diff)
downloadglibc-c55fbd1ea768f9fdef34a01377702c0d72cbc213.tar.gz
glibc-c55fbd1ea768f9fdef34a01377702c0d72cbc213.tar.xz
glibc-c55fbd1ea768f9fdef34a01377702c0d72cbc213.zip
Implement scandirat function
-rw-r--r--ChangeLog23
-rw-r--r--NEWS5
-rw-r--r--Versions.def1
-rw-r--r--dirent/Makefile5
-rw-r--r--dirent/Versions3
-rw-r--r--dirent/dirent.h45
-rw-r--r--dirent/opendir.c13
-rw-r--r--dirent/scandir.c122
-rw-r--r--dirent/scandir64.c9
-rw-r--r--dirent/scandirat.c143
-rw-r--r--dirent/scandirat64.c28
-rw-r--r--include/dirent.h14
-rw-r--r--sysdeps/unix/opendir.c21
-rw-r--r--sysdeps/unix/sysv/linux/i386/scandir64.c102
-rw-r--r--sysdeps/wordsize-64/scandirat.c6
-rw-r--r--sysdeps/wordsize-64/scandirat64.c1
16 files changed, 398 insertions, 143 deletions
diff --git a/ChangeLog b/ChangeLog
index 4b6a62f07a..3ca8232372 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,26 @@
+2011-08-09  Ulrich Drepper  <drepper@gmail.com>
+
+	* Versions.def [libc]: Add GLIBC_2.15.
+	* dirent/Makefile (routines): Add scandirat and scandirat64.
+	* dirent/Versions [libc]: Export scandirat and scandirat64 for
+	GLIBC_2.15.
+	* dirent/dirent.h: Declare scandirat and scandirat64.
+	* dirent/scandirat.c: New file.
+	* dirent/scandirat64.c: New file.
+	* sysdeps/wordsize-64/scandirat.c: New file.
+	* sysdeps/wordsize-64/scandirat64.c: New file.
+	* dirent/opendir.c: Define opendirat.
+	* dirent/scandir.c: Move code to scandirat.c.  Implement scandir
+	using scandirat.
+	* dirent/scandir64.c: Adjust for scandir.c change.
+	* include/dirent.h: Define scandir_cancel_struct.  Declare __opendirat,
+	__scandirat64, and __scandir_cancel_handler.
+	* sysdeps/unix/opendir.c: Rename __opendir to __opendirat.  Take
+	additional parameter and use openat instead of open (outside of ld.so).
+	Add new __opendir as wrapper around __opendirat.
+	* sysdeps/unix/sysv/linux/i386/scandir64.c: Reimplement __old_scandir64
+	here without requiring old scandirat implementation.
+
 2011-08-08  Ulrich Drepper  <drepper@gmail.com>
 
 	* dirent/scandir.c (cancel_handler): Renamed to
diff --git a/NEWS b/NEWS
index 1abcae1f9d..78810a9d5e 100644
--- a/NEWS
+++ b/NEWS
@@ -1,4 +1,4 @@
-GNU C Library NEWS -- history of user-visible changes.  2011-7-23
+GNU C Library NEWS -- history of user-visible changes.  2011-8-8
 Copyright (C) 1992-2009, 2010, 2011 Free Software Foundation, Inc.
 See the end for copying conditions.
 
@@ -26,6 +26,9 @@ Version 2.15
 
 * Optimized strcat and strncat on x86-64.
   Contributed by Liubov Dmitrieva.
+
+* New interfaces: scandirat, scandirat64
+  Implemented by Ulrich Drepper.
 
 Version 2.14
 
diff --git a/Versions.def b/Versions.def
index 666ea73108..98c28001ac 100644
--- a/Versions.def
+++ b/Versions.def
@@ -31,6 +31,7 @@ libc {
   GLIBC_2.12
   GLIBC_2.13
   GLIBC_2.14
+  GLIBC_2.15
 %ifdef USE_IN_LIBIO
   HURD_CTHREADS_0.3
 %endif
diff --git a/dirent/Makefile b/dirent/Makefile
index ef639f2095..07f706e678 100644
--- a/dirent/Makefile
+++ b/dirent/Makefile
@@ -1,4 +1,4 @@
-# Copyright (C) 1991-2000,2002,2003,2005,2006 Free Software Foundation, Inc.
+# Copyright (C) 1991-2000,2002,2003,2005,2006,2011 Free Software Foundation, Inc.
 # This file is part of the GNU C Library.
 
 # The GNU C Library is free software; you can redistribute it and/or
@@ -25,7 +25,8 @@ headers		:= dirent.h bits/dirent.h
 routines	:= opendir closedir readdir readdir_r rewinddir \
 		   seekdir telldir scandir alphasort versionsort \
 		   getdents getdents64 dirfd readdir64 readdir64_r scandir64 \
-		   alphasort64 versionsort64 fdopendir
+		   alphasort64 versionsort64 fdopendir \
+		   scandirat scandirat64
 distribute := dirstream.h
 
 tests	   := list tst-seekdir opendir-tst1 bug-readdir1 tst-fdopendir \
diff --git a/dirent/Versions b/dirent/Versions
index 41c1584426..d976d373f6 100644
--- a/dirent/Versions
+++ b/dirent/Versions
@@ -44,4 +44,7 @@ libc {
   GLIBC_2.4 {
     fdopendir;
   }
+  GLIBC_2.15 {
+    scandirat; scandirat64;
+  }
 }
diff --git a/dirent/dirent.h b/dirent/dirent.h
index bb4ede1580..6a5a0ef81c 100644
--- a/dirent/dirent.h
+++ b/dirent/dirent.h
@@ -1,4 +1,5 @@
-/* Copyright (C) 1991-2000,2003-2005,2009,2010 Free Software Foundation, Inc.
+/* Copyright (C) 1991-2000,2003-2005,2009,2010,2011
+   Free Software Foundation, Inc.
    This file is part of the GNU C Library.
 
    The GNU C Library is free software; you can redistribute it and/or
@@ -247,7 +248,10 @@ extern int dirfd (DIR *__dirp) __THROW __nonnull ((1));
 /* Scan the directory DIR, calling SELECTOR on each directory entry.
    Entries for which SELECT returns nonzero are individually malloc'd,
    sorted using qsort with CMP, and collected in a malloc'd array in
-   *NAMELIST.  Returns the number of entries selected, or -1 on error.  */
+   *NAMELIST.  Returns the number of entries selected, or -1 on error.
+
+   This function is a cancellation point and therefore not marked with
+   __THROW.  */
 # ifndef __USE_FILE_OFFSET64
 extern int scandir (__const char *__restrict __dir,
 		    struct dirent ***__restrict __namelist,
@@ -280,6 +284,43 @@ extern int scandir64 (__const char *__restrict __dir,
      __nonnull ((1, 2));
 # endif
 
+# ifdef __USE_GNU
+/* Similar to `scandir' but a relative DIR name is interpreted relative
+   to the directory for which DFD is a descriptor.
+
+   This function is a cancellation point and therefore not marked with
+   __THROW.  */
+#  ifndef __USE_FILE_OFFSET64
+extern int scandirat (int __dfd, __const char *__restrict __dir,
+		      struct dirent ***__restrict __namelist,
+		      int (*__selector) (__const struct dirent *),
+		      int (*__cmp) (__const struct dirent **,
+				    __const struct dirent **))
+     __nonnull ((2, 3));
+#  else
+#   ifdef __REDIRECT
+extern int __REDIRECT (scandirat,
+		       (int __dfd, __const char *__restrict __dir,
+			struct dirent ***__restrict __namelist,
+			int (*__selector) (__const struct dirent *),
+			int (*__cmp) (__const struct dirent **,
+				      __const struct dirent **)),
+		       scandirat64) __nonnull ((2, 3));
+#   else
+#    define scandirat scandirat64
+#   endif
+#  endif
+
+/* This function is like `scandir' but it uses the 64bit dirent structure.
+   Please note that the CMP function must now work with struct dirent64 **.  */
+extern int scandirat64 (int __dfd, __const char *__restrict __dir,
+			struct dirent64 ***__restrict __namelist,
+			int (*__selector) (__const struct dirent64 *),
+			int (*__cmp) (__const struct dirent64 **,
+				      __const struct dirent64 **))
+     __nonnull ((2, 3));
+# endif
+
 /* Function to compare two `struct dirent's alphabetically.  */
 # ifndef __USE_FILE_OFFSET64
 extern int alphasort (__const struct dirent **__e1,
diff --git a/dirent/opendir.c b/dirent/opendir.c
index 771013f6eb..6375b6786a 100644
--- a/dirent/opendir.c
+++ b/dirent/opendir.c
@@ -1,4 +1,4 @@
-/* Copyright (C) 1991, 1995, 1996, 1997 Free Software Foundation, Inc.
+/* Copyright (C) 1991, 1995, 1996, 1997, 2011 Free Software Foundation, Inc.
    This file is part of the GNU C Library.
 
    The GNU C Library is free software; you can redistribute it and/or
@@ -21,13 +21,20 @@
 #include <dirent.h>
 
 
-/* Open a directory stream on NAME.  */
 DIR *
-__opendir (const char *name)
+__opendirat (int dfd, const char *name)
 {
   __set_errno (ENOSYS);
   return NULL;
 }
+
+
+/* Open a directory stream on NAME.  */
+DIR *
+__opendir (const char *name)
+{
+  return __opendirat (AT_FDCWD, name);
+}
 weak_alias (__opendir, opendir)
 
 stub_warning (opendir)
diff --git a/dirent/scandir.c b/dirent/scandir.c
index e90b942b1d..3f69a8db77 100644
--- a/dirent/scandir.c
+++ b/dirent/scandir.c
@@ -18,44 +18,14 @@
    02111-1307 USA.  */
 
 #include <dirent.h>
-#include <stdlib.h>
-#include <string.h>
-#include <errno.h>
-#include <bits/libc-lock.h>
+#include <fcntl.h>
 
 #ifndef SCANDIR
 # define SCANDIR scandir
-# define READDIR __readdir
+# define SCANDIRAT scandirat
 # define DIRENT_TYPE struct dirent
 #endif
 
-#ifndef SCANDIR_CANCEL
-# define SCANDIR_CANCEL
-struct scandir_cancel_struct
-{
-  DIR *dp;
-  void *v;
-  size_t cnt;
-};
-
-# ifndef SKIP_SCANDIR_CANCEL
-void
-__scandir_cancel_handler (void *arg)
-{
-  struct scandir_cancel_struct *cp = arg;
-  size_t i;
-  void **v = cp->v;
-
-  for (i = 0; i < cp->cnt; ++i)
-    free (v[i]);
-  free (v);
-  (void) __closedir (cp->dp);
-}
-# else
-extern void __scandir_cancel_handler (void *arg);
-# endif
-#endif
-
 
 int
 SCANDIR (dir, namelist, select, cmp)
@@ -64,91 +34,5 @@ SCANDIR (dir, namelist, select, cmp)
      int (*select) (const DIRENT_TYPE *);
      int (*cmp) (const DIRENT_TYPE **, const DIRENT_TYPE **);
 {
-  DIR *dp = __opendir (dir);
-  DIRENT_TYPE **v = NULL;
-  size_t vsize = 0;
-  struct scandir_cancel_struct c;
-  DIRENT_TYPE *d;
-  int save;
-
-  if (dp == NULL)
-    return -1;
-
-  save = errno;
-  __set_errno (0);
-
-  c.dp = dp;
-  c.v = NULL;
-  c.cnt = 0;
-  __libc_cleanup_push (__scandir_cancel_handler, &c);
-
-  while ((d = READDIR (dp)) != NULL)
-    {
-      int use_it = select == NULL;
-
-      if (! use_it)
-	{
-	  use_it = select (d);
-	  /* The select function might have changed errno.  It was
-	     zero before and it need to be again to make the latter
-	     tests work.  */
-	  __set_errno (0);
-	}
-
-      if (use_it)
-	{
-	  DIRENT_TYPE *vnew;
-	  size_t dsize;
-
-	  /* Ignore errors from select or readdir */
-	  __set_errno (0);
-
-	  if (__builtin_expect (c.cnt == vsize, 0))
-	    {
-	      DIRENT_TYPE **new;
-	      if (vsize == 0)
-		vsize = 10;
-	      else
-		vsize *= 2;
-	      new = (DIRENT_TYPE **) realloc (v, vsize * sizeof (*v));
-	      if (new == NULL)
-		break;
-	      v = new;
-	      c.v = (void *) v;
-	    }
-
-	  dsize = &d->d_name[_D_ALLOC_NAMLEN (d)] - (char *) d;
-	  vnew = (DIRENT_TYPE *) malloc (dsize);
-	  if (vnew == NULL)
-	    break;
-
-	  v[c.cnt++] = (DIRENT_TYPE *) memcpy (vnew, d, dsize);
-	}
-    }
-
-  if (__builtin_expect (errno, 0) != 0)
-    {
-      save = errno;
-
-      while (c.cnt > 0)
-	free (v[--c.cnt]);
-      free (v);
-      c.cnt = -1;
-    }
-  else
-    {
-      /* Sort the list if we have a comparison function to sort with.  */
-      if (cmp != NULL)
-	qsort (v, c.cnt, sizeof (*v),
-	       (int (*) (const void *, const void *)) cmp);
-
-      *namelist = v;
-    }
-
-  __libc_cleanup_pop (0);
-
-  (void) __closedir (dp);
-  __set_errno (save);
-
-  return c.cnt;
+  return SCANDIRAT (AT_FDCWD, dir, namelist, select, cmp);
 }
diff --git a/dirent/scandir64.c b/dirent/scandir64.c
index 274822e125..21a936e4bb 100644
--- a/dirent/scandir64.c
+++ b/dirent/scandir64.c
@@ -19,14 +19,7 @@
 #include <dirent.h>
 
 #define SCANDIR scandir64
-#define READDIR __readdir64
+#define SCANDIRAT scandirat64
 #define DIRENT_TYPE struct dirent64
-#define SKIP_SCANDIR_CANCEL 1
-
-int scandir64 (__const char *__restrict __dir,
-	       struct dirent64 ***__restrict __namelist,
-	       int (*__selector) (__const struct dirent64 *),
-	       int (*__cmp) (__const struct dirent64 **,
-			     __const struct dirent64 **));
 
 #include <dirent/scandir.c>
diff --git a/dirent/scandirat.c b/dirent/scandirat.c
new file mode 100644
index 0000000000..e6f5ece76f
--- /dev/null
+++ b/dirent/scandirat.c
@@ -0,0 +1,143 @@
+/* Copyright (C) 1992-1998,2000,2002,2003,2009,2011
+   Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, write to the Free
+   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+   02111-1307 USA.  */
+
+#include <dirent.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <bits/libc-lock.h>
+
+#ifndef SCANDIRAT
+# define SCANDIRAT scandirat
+# define READDIR __readdir
+# define DIRENT_TYPE struct dirent
+#endif
+
+#ifndef SKIP_SCANDIR_CANCEL
+void
+__scandir_cancel_handler (void *arg)
+{
+  struct scandir_cancel_struct *cp = arg;
+  size_t i;
+  void **v = cp->v;
+
+  for (i = 0; i < cp->cnt; ++i)
+    free (v[i]);
+  free (v);
+  (void) __closedir (cp->dp);
+}
+#endif
+
+
+int
+SCANDIRAT (dfd, dir, namelist, select, cmp)
+     int dfd;
+     const char *dir;
+     DIRENT_TYPE ***namelist;
+     int (*select) (const DIRENT_TYPE *);
+     int (*cmp) (const DIRENT_TYPE **, const DIRENT_TYPE **);
+{
+  DIR *dp = __opendirat (dfd, dir);
+  DIRENT_TYPE **v = NULL;
+  size_t vsize = 0;
+  struct scandir_cancel_struct c;
+  DIRENT_TYPE *d;
+  int save;
+
+  if (dp == NULL)
+    return -1;
+
+  save = errno;
+  __set_errno (0);
+
+  c.dp = dp;
+  c.v = NULL;
+  c.cnt = 0;
+  __libc_cleanup_push (__scandir_cancel_handler, &c);
+
+  while ((d = READDIR (dp)) != NULL)
+    {
+      int use_it = select == NULL;
+
+      if (! use_it)
+	{
+	  use_it = select (d);
+	  /* The select function might have changed errno.  It was
+	     zero before and it need to be again to make the latter
+	     tests work.  */
+	  __set_errno (0);
+	}
+
+      if (use_it)
+	{
+	  DIRENT_TYPE *vnew;
+	  size_t dsize;
+
+	  /* Ignore errors from select or readdir */
+	  __set_errno (0);
+
+	  if (__builtin_expect (c.cnt == vsize, 0))
+	    {
+	      DIRENT_TYPE **new;
+	      if (vsize == 0)
+		vsize = 10;
+	      else
+		vsize *= 2;
+	      new = (DIRENT_TYPE **) realloc (v, vsize * sizeof (*v));
+	      if (new == NULL)
+		break;
+	      v = new;
+	      c.v = (void *) v;
+	    }
+
+	  dsize = &d->d_name[_D_ALLOC_NAMLEN (d)] - (char *) d;
+	  vnew = (DIRENT_TYPE *) malloc (dsize);
+	  if (vnew == NULL)
+	    break;
+
+	  v[c.cnt++] = (DIRENT_TYPE *) memcpy (vnew, d, dsize);
+	}
+    }
+
+  if (__builtin_expect (errno, 0) != 0)
+    {
+      save = errno;
+
+      while (c.cnt > 0)
+	free (v[--c.cnt]);
+      free (v);
+      c.cnt = -1;
+    }
+  else
+    {
+      /* Sort the list if we have a comparison function to sort with.  */
+      if (cmp != NULL)
+	qsort (v, c.cnt, sizeof (*v),
+	       (int (*) (const void *, const void *)) cmp);
+
+      *namelist = v;
+    }
+
+  __libc_cleanup_pop (0);
+
+  (void) __closedir (dp);
+  __set_errno (save);
+
+  return c.cnt;
+}
diff --git a/dirent/scandirat64.c b/dirent/scandirat64.c
new file mode 100644
index 0000000000..c4afb90d3d
--- /dev/null
+++ b/dirent/scandirat64.c
@@ -0,0 +1,28 @@
+/* Copyright (C) 2000, 2009, 2011 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, write to the Free
+   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+   02111-1307 USA.  */
+
+#include <dirent.h>
+
+#define SCANDIRAT __scandirat64
+#define READDIR __readdir64
+#define DIRENT_TYPE struct dirent64
+#define SKIP_SCANDIR_CANCEL 1
+
+#include "scandirat.c"
+
+weak_alias (__scandirat64, scandirat64)
diff --git a/include/dirent.h b/include/dirent.h
index 0f8f1cbe88..9ed5876892 100644
--- a/include/dirent.h
+++ b/include/dirent.h
@@ -4,8 +4,16 @@
 # include <sys/stat.h>
 # include <stdbool.h>
 
+struct scandir_cancel_struct
+{
+  DIR *dp;
+  void *v;
+  size_t cnt;
+};
+
 /* Now define the internal interfaces.  */
 extern DIR *__opendir (__const char *__name);
+extern DIR *__opendirat (int dfd, __const char *__name) internal_function;
 extern DIR *__fdopendir (int __fd);
 extern int __closedir (DIR *__dirp);
 extern struct dirent *__readdir (DIR *__dirp);
@@ -19,6 +27,11 @@ extern int __scandir64 (__const char * __dir,
 			int (*__selector) (__const struct dirent64 *),
 			int (*__cmp) (__const struct dirent64 **,
 				      __const struct dirent64 **));
+extern int __scandirat64 (int __dfd, __const char * __dir,
+			  struct dirent64 *** __namelist,
+			  int (*__selector) (__const struct dirent64 *),
+			  int (*__cmp) (__const struct dirent64 **,
+					__const struct dirent64 **));
 extern __ssize_t __getdents (int __fd, char *__buf, size_t __nbytes)
      internal_function;
 extern __ssize_t __getdents64 (int __fd, char *__buf, size_t __nbytes)
@@ -31,6 +44,7 @@ extern int __versionsort64 (const struct dirent64 **a,
 extern DIR *__alloc_dir (int fd, bool close_fd, int flags,
 			 const struct stat64 *statp)
      internal_function;
+extern void __scandir_cancel_handler (void *arg);
 
 libc_hidden_proto (rewinddir)
 
diff --git a/sysdeps/unix/opendir.c b/sysdeps/unix/opendir.c
index c2d1ddaf88..58d31764da 100644
--- a/sysdeps/unix/opendir.c
+++ b/sysdeps/unix/opendir.c
@@ -17,6 +17,7 @@
    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
    02111-1307 USA.  */
 
+#include <assert.h>
 #include <errno.h>
 #include <limits.h>
 #include <stddef.h>
@@ -76,9 +77,9 @@ tryopen_o_directory (void)
 #endif
 
 
-/* Open a directory stream on NAME.  */
 DIR *
-__opendir (const char *name)
+internal_function
+__opendirat (int dfd, const char *name)
 {
   struct stat64 statbuf;
   struct stat64 *statp = NULL;
@@ -116,7 +117,13 @@ __opendir (const char *name)
 #ifdef O_CLOEXEC
   flags |= O_CLOEXEC;
 #endif
-  int fd = open_not_cancel_2 (name, flags);
+  int fd;
+#ifdef IS_IN_rtld
+  assert (dfd == AT_FDCWD);
+  fd = open_not_cancel_2 (name, flags);
+#else
+  fd = openat_not_cancel_3 (dfd, name, flags);
+#endif
   if (__builtin_expect (fd, 0) < 0)
     return NULL;
 
@@ -140,6 +147,14 @@ __opendir (const char *name)
 
   return __alloc_dir (fd, true, 0, statp);
 }
+
+
+/* Open a directory stream on NAME.  */
+DIR *
+__opendir (const char *name)
+{
+  return __opendirat (AT_FDCWD, name);
+}
 weak_alias (__opendir, opendir)
 
 
diff --git a/sysdeps/unix/sysv/linux/i386/scandir64.c b/sysdeps/unix/sysv/linux/i386/scandir64.c
index 837e1b9438..dacac0a44c 100644
--- a/sysdeps/unix/sysv/linux/i386/scandir64.c
+++ b/sysdeps/unix/sysv/linux/i386/scandir64.c
@@ -19,6 +19,7 @@
 #include <dirent.h>
 
 #define SCANDIR __scandir64
+#define SCANDIRAT __scandirat64
 #define READDIR __readdir64
 #define DIRENT_TYPE struct dirent64
 #define SKIP_SCANDIR_CANCEL 1
@@ -34,15 +35,106 @@
 versioned_symbol (libc, __scandir64, scandir64, GLIBC_2_2);
 
 #if SHLIB_COMPAT(libc, GLIBC_2_1, GLIBC_2_2)
+# include <errno.h>
+# include "olddirent.h"
 
-#include <sysdeps/unix/sysv/linux/i386/olddirent.h>
+int
+__old_scandir64 (dir, namelist, select, cmp)
+     const char *dir;
+     struct __old_dirent64 ***namelist;
+     int (*select) (const struct __old_dirent64 *);
+     int (*cmp) (const struct __old_dirent64 **,
+		 const struct __old_dirent64 **);
+{
+  DIR *dp = __opendir (dir);
+  struct __old_dirent64 **v = NULL;
+  size_t vsize = 0;
+  struct scandir_cancel_struct c;
+  struct __old_dirent64 *d;
+  int save;
 
-#define SCANDIR attribute_compat_text_section __old_scandir64
-#define READDIR __old_readdir64
-#define DIRENT_TYPE struct __old_dirent64
+  if (dp == NULL)
+    return -1;
 
-#include <dirent/scandir.c>
+  save = errno;
+  __set_errno (0);
+
+  c.dp = dp;
+  c.v = NULL;
+  c.cnt = 0;
+  __libc_cleanup_push (__scandir_cancel_handler, &c);
+
+  while ((d = __old_readdir64 (dp)) != NULL)
+    {
+      int use_it = select == NULL;
+
+      if (! use_it)
+	{
+	  use_it = select (d);
+	  /* The select function might have changed errno.  It was
+	     zero before and it need to be again to make the latter
+	     tests work.  */
+	  __set_errno (0);
+	}
+
+      if (use_it)
+	{
+	  struct __old_dirent64 *vnew;
+	  size_t dsize;
+
+	  /* Ignore errors from select or readdir */
+	  __set_errno (0);
+
+	  if (__builtin_expect (c.cnt == vsize, 0))
+	    {
+	      struct __old_dirent64 **new;
+	      if (vsize == 0)
+		vsize = 10;
+	      else
+		vsize *= 2;
+	      new = (struct __old_dirent64 **) realloc (v,
+							vsize * sizeof (*v));
+	      if (new == NULL)
+		break;
+	      v = new;
+	      c.v = (void *) v;
+	    }
+
+	  dsize = &d->d_name[_D_ALLOC_NAMLEN (d)] - (char *) d;
+	  vnew = (struct __old_dirent64 *) malloc (dsize);
+	  if (vnew == NULL)
+	    break;
+
+	  v[c.cnt++] = (struct __old_dirent64 *) memcpy (vnew, d, dsize);
+	}
+    }
+
+  if (__builtin_expect (errno, 0) != 0)
+    {
+      save = errno;
+
+      while (c.cnt > 0)
+	free (v[--c.cnt]);
+      free (v);
+      c.cnt = -1;
+    }
+  else
+    {
+      /* Sort the list if we have a comparison function to sort with.  */
+      if (cmp != NULL)
+	qsort (v, c.cnt, sizeof (*v),
+	       (int (*) (const void *, const void *)) cmp);
+
+      *namelist = v;
+    }
+
+  __libc_cleanup_pop (0);
+
+  (void) __closedir (dp);
+  __set_errno (save);
 
+  return c.cnt;
+}
 compat_symbol (libc, __old_scandir64, scandir64, GLIBC_2_1);
 
 #endif
diff --git a/sysdeps/wordsize-64/scandirat.c b/sysdeps/wordsize-64/scandirat.c
new file mode 100644
index 0000000000..02b8fdee0f
--- /dev/null
+++ b/sysdeps/wordsize-64/scandirat.c
@@ -0,0 +1,6 @@
+#define scandirat64 scandirat64_renamed
+
+#include "../../dirent/scandirat.c"
+
+#undef scandirat64
+weak_alias (scandirat, scandirat64)
diff --git a/sysdeps/wordsize-64/scandirat64.c b/sysdeps/wordsize-64/scandirat64.c
new file mode 100644
index 0000000000..fb938654a4
--- /dev/null
+++ b/sysdeps/wordsize-64/scandirat64.c
@@ -0,0 +1 @@
+/* Defined in scandirat.c.  */