summary refs log tree commit diff
path: root/sysdeps/posix
diff options
context:
space:
mode:
authorRoland McGrath <roland@hack.frob.com>2012-08-07 14:47:34 -0700
committerRoland McGrath <roland@hack.frob.com>2012-08-07 14:47:34 -0700
commita281decc878cf26cae12a5bdf5f4c6e0297303d6 (patch)
treeb28e5b00fdbeb4289346b0df64d6f75aaacece75 /sysdeps/posix
parent22895b476794b69a9a42e6bb4ceb929dc6a43917 (diff)
downloadglibc-a281decc878cf26cae12a5bdf5f4c6e0297303d6.tar.gz
glibc-a281decc878cf26cae12a5bdf5f4c6e0297303d6.tar.xz
glibc-a281decc878cf26cae12a5bdf5f4c6e0297303d6.zip
Move common dirent implementation from sysdeps/unix to sysdeps/posix.
Diffstat (limited to 'sysdeps/posix')
-rw-r--r--sysdeps/posix/closedir.c55
-rw-r--r--sysdeps/posix/dirfd.c29
-rw-r--r--sysdeps/posix/dirstream.h48
-rw-r--r--sysdeps/posix/fdopendir.c51
-rw-r--r--sysdeps/posix/opendir.c228
-rw-r--r--sysdeps/posix/readdir.c122
-rw-r--r--sysdeps/posix/readdir_r.c137
-rw-r--r--sysdeps/posix/rewinddir.c40
-rw-r--r--sysdeps/posix/seekdir.c37
-rw-r--r--sysdeps/posix/telldir.c27
10 files changed, 774 insertions, 0 deletions
diff --git a/sysdeps/posix/closedir.c b/sysdeps/posix/closedir.c
new file mode 100644
index 0000000000..41abf28c6f
--- /dev/null
+++ b/sysdeps/posix/closedir.c
@@ -0,0 +1,55 @@
+/* Copyright (C) 1991,1993,1995,1996,1998,2002,2003, 2007
+   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, see
+   <http://www.gnu.org/licenses/>.  */
+
+#include <errno.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <dirent.h>
+#include <unistd.h>
+#include <dirstream.h>
+#include <not-cancel.h>
+
+
+/* Close the directory stream DIRP.
+   Return 0 if successful, -1 if not.  */
+int
+__closedir (DIR *dirp)
+{
+  int fd;
+
+  if (dirp == NULL)
+    {
+      __set_errno (EINVAL);
+      return -1;
+    }
+
+  /* We do not try to synchronize access here.  If some other thread
+     still uses this handle it is a big mistake and that thread
+     deserves all the bad data it gets.  */
+
+  fd = dirp->fd;
+
+#ifndef NOT_IN_libc
+  __libc_lock_fini (dirp->lock);
+#endif
+
+  free ((void *) dirp);
+
+  return close_not_cancel (fd);
+}
+weak_alias (__closedir, closedir)
diff --git a/sysdeps/posix/dirfd.c b/sysdeps/posix/dirfd.c
new file mode 100644
index 0000000000..536c44e30e
--- /dev/null
+++ b/sysdeps/posix/dirfd.c
@@ -0,0 +1,29 @@
+/* Return the file descriptor used by a DIR stream.  Unix version.
+   Copyright (C) 1995, 1996 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, see
+   <http://www.gnu.org/licenses/>.  */
+
+#include <dirent.h>
+#include <dirstream.h>
+
+#undef dirfd
+
+int
+dirfd (dirp)
+     DIR *dirp;
+{
+  return dirp->fd;
+}
diff --git a/sysdeps/posix/dirstream.h b/sysdeps/posix/dirstream.h
new file mode 100644
index 0000000000..6ca290471d
--- /dev/null
+++ b/sysdeps/posix/dirstream.h
@@ -0,0 +1,48 @@
+/* Copyright (C) 1993, 1995, 1996, 2007 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, see
+   <http://www.gnu.org/licenses/>.  */
+
+#ifndef	_DIRSTREAM_H
+#define	_DIRSTREAM_H	1
+
+#include <sys/types.h>
+
+#include <bits/libc-lock.h>
+
+/* Directory stream type.
+
+   The miscellaneous Unix `readdir' implementations read directory data
+   into a buffer and return `struct dirent *' pointers into it.  */
+
+struct __dirstream
+  {
+    int fd;			/* File descriptor.  */
+
+    __libc_lock_define (, lock) /* Mutex lock for this structure.  */
+
+    size_t allocation;		/* Space allocated for the block.  */
+    size_t size;		/* Total valid data in the block.  */
+    size_t offset;		/* Current offset into the block.  */
+
+    off_t filepos;		/* Position of next entry to read.  */
+
+    /* Directory block.  */
+    char data[0] __attribute__ ((aligned (__alignof__ (void*))));
+  };
+
+#define _DIR_dirfd(dirp)	((dirp)->fd)
+
+#endif	/* dirstream.h */
diff --git a/sysdeps/posix/fdopendir.c b/sysdeps/posix/fdopendir.c
new file mode 100644
index 0000000000..beddcbe029
--- /dev/null
+++ b/sysdeps/posix/fdopendir.c
@@ -0,0 +1,51 @@
+/* Copyright (C) 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
+   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, see
+   <http://www.gnu.org/licenses/>.  */
+
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+
+#include <not-cancel.h>
+
+
+DIR *
+__fdopendir (int fd)
+{
+  struct stat64 statbuf;
+
+  if (__builtin_expect (__fxstat64 (_STAT_VER, fd, &statbuf), 0) < 0)
+    return NULL;
+  if (__builtin_expect (! S_ISDIR (statbuf.st_mode), 0))
+    {
+      __set_errno (ENOTDIR);
+      return NULL;
+    }
+
+  /* Make sure the descriptor allows for reading.  */
+  int flags = __fcntl (fd, F_GETFL);
+  if (__builtin_expect (flags == -1, 0))
+    return NULL;
+  if (__builtin_expect ((flags & O_ACCMODE) == O_WRONLY, 0))
+    {
+      __set_errno (EINVAL);
+      return NULL;
+    }
+
+  return __alloc_dir (fd, false, flags, &statbuf);
+}
+weak_alias (__fdopendir, fdopendir)
diff --git a/sysdeps/posix/opendir.c b/sysdeps/posix/opendir.c
new file mode 100644
index 0000000000..e093142f62
--- /dev/null
+++ b/sysdeps/posix/opendir.c
@@ -0,0 +1,228 @@
+/* Copyright (C) 1991-1996,98,2000-2003,2005,2007,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, see
+   <http://www.gnu.org/licenses/>.  */
+
+#include <assert.h>
+#include <errno.h>
+#include <limits.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <dirent.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <dirstream.h>
+#include <not-cancel.h>
+#include <kernel-features.h>
+
+
+/* opendir() must not accidentally open something other than a directory.
+   Some OS's have kernel support for that, some don't.  In the worst
+   case we have to stat() before the open() AND fstat() after.
+
+   We have to test at runtime for kernel support since libc may have
+   been compiled with different headers to the kernel it's running on.
+   This test can't be done reliably in the general case.  We'll use
+   /dev/null, which if it's not a device lots of stuff will break, as
+   a guinea pig.  It may be missing in chroot environments, so we
+   make sure to fail safe. */
+#ifdef O_DIRECTORY
+# ifdef O_DIRECTORY_WORKS
+#  define o_directory_works 1
+#  define tryopen_o_directory() while (1) /* This must not be called.  */
+# else
+static int o_directory_works;
+
+static void
+tryopen_o_directory (void)
+{
+  int serrno = errno;
+  int x = open_not_cancel_2 ("/dev/null", O_RDONLY|O_NDELAY|O_DIRECTORY);
+
+  if (x >= 0)
+    {
+      close_not_cancel_no_status (x);
+      o_directory_works = -1;
+    }
+  else if (errno != ENOTDIR)
+    o_directory_works = -1;
+  else
+    o_directory_works = 1;
+
+  __set_errno (serrno);
+}
+# endif
+# define EXTRA_FLAGS O_DIRECTORY
+#else
+# define EXTRA_FLAGS 0
+#endif
+
+
+DIR *
+internal_function
+__opendirat (int dfd, const char *name)
+{
+  struct stat64 statbuf;
+  struct stat64 *statp = NULL;
+
+  if (__builtin_expect (name[0], '\1') == '\0')
+    {
+      /* POSIX.1-1990 says an empty name gets ENOENT;
+	 but `open' might like it fine.  */
+      __set_errno (ENOENT);
+      return NULL;
+    }
+
+#ifdef O_DIRECTORY
+  /* Test whether O_DIRECTORY works.  */
+  if (o_directory_works == 0)
+    tryopen_o_directory ();
+
+  /* We can skip the expensive `stat' call if O_DIRECTORY works.  */
+  if (o_directory_works < 0)
+#endif
+    {
+      /* We first have to check whether the name is for a directory.  We
+	 cannot do this after the open() call since the open/close operation
+	 performed on, say, a tape device might have undesirable effects.  */
+      if (__builtin_expect (__xstat64 (_STAT_VER, name, &statbuf), 0) < 0)
+	return NULL;
+      if (__builtin_expect (! S_ISDIR (statbuf.st_mode), 0))
+	{
+	  __set_errno (ENOTDIR);
+	  return NULL;
+	 }
+    }
+
+  int flags = O_RDONLY|O_NDELAY|EXTRA_FLAGS|O_LARGEFILE;
+#ifdef O_CLOEXEC
+  flags |= O_CLOEXEC;
+#endif
+  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;
+
+#ifdef O_DIRECTORY
+  if (o_directory_works <= 0)
+#endif
+    {
+      /* Now make sure this really is a directory and nothing changed since
+	 the `stat' call.  */
+      if (__builtin_expect (__fxstat64 (_STAT_VER, fd, &statbuf), 0) < 0)
+	goto lose;
+      if (__builtin_expect (! S_ISDIR (statbuf.st_mode), 0))
+	{
+	  __set_errno (ENOTDIR);
+	lose:
+	  close_not_cancel_no_status (fd);
+	  return NULL;
+	}
+      statp = &statbuf;
+    }
+
+  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)
+
+
+#ifdef __ASSUME_O_CLOEXEC
+# define check_have_o_cloexec(fd) 1
+#else
+static int
+check_have_o_cloexec (int fd)
+{
+  if (__have_o_cloexec == 0)
+    __have_o_cloexec = (__fcntl (fd, F_GETFD, 0) & FD_CLOEXEC) == 0 ? -1 : 1;
+  return __have_o_cloexec > 0;
+}
+#endif
+
+
+DIR *
+internal_function
+__alloc_dir (int fd, bool close_fd, int flags, const struct stat64 *statp)
+{
+  /* We always have to set the close-on-exit flag if the user provided
+     the file descriptor.  Otherwise only if we have no working
+     O_CLOEXEC support.  */
+#ifdef O_CLOEXEC
+  if ((! close_fd && (flags & O_CLOEXEC) == 0)
+      || ! check_have_o_cloexec (fd))
+#endif
+    {
+      if (__builtin_expect (__fcntl (fd, F_SETFD, FD_CLOEXEC), 0) < 0)
+	goto lose;
+    }
+
+  const size_t default_allocation = (4 * BUFSIZ < sizeof (struct dirent64)
+				     ? sizeof (struct dirent64) : 4 * BUFSIZ);
+  const size_t small_allocation = (BUFSIZ < sizeof (struct dirent64)
+				   ? sizeof (struct dirent64) : BUFSIZ);
+  size_t allocation = default_allocation;
+#ifdef _STATBUF_ST_BLKSIZE
+  if (statp != NULL && default_allocation < statp->st_blksize)
+    allocation = statp->st_blksize;
+#endif
+
+  DIR *dirp = (DIR *) malloc (sizeof (DIR) + allocation);
+  if (dirp == NULL)
+    {
+      allocation = small_allocation;
+      dirp = (DIR *) malloc (sizeof (DIR) + allocation);
+
+      if (dirp == NULL)
+      lose:
+	{
+	  if (close_fd)
+	    {
+	      int save_errno = errno;
+	      close_not_cancel_no_status (fd);
+	      __set_errno (save_errno);
+	    }
+	  return NULL;
+	}
+    }
+
+  dirp->fd = fd;
+#ifndef NOT_IN_libc
+  __libc_lock_init (dirp->lock);
+#endif
+  dirp->allocation = allocation;
+  dirp->size = 0;
+  dirp->offset = 0;
+  dirp->filepos = 0;
+
+  return dirp;
+}
diff --git a/sysdeps/posix/readdir.c b/sysdeps/posix/readdir.c
new file mode 100644
index 0000000000..be0fcaee9f
--- /dev/null
+++ b/sysdeps/posix/readdir.c
@@ -0,0 +1,122 @@
+/* Copyright (C) 1991-1997,1999,2000,2002,2007 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, see
+   <http://www.gnu.org/licenses/>.  */
+
+#include <errno.h>
+#include <limits.h>
+#include <stddef.h>
+#include <string.h>
+#include <dirent.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <assert.h>
+
+#include <dirstream.h>
+
+#ifndef __READDIR
+# define __READDIR __readdir
+# define __GETDENTS __getdents
+# define DIRENT_TYPE struct dirent
+# define __READDIR_ALIAS
+#endif
+
+/* Read a directory entry from DIRP.  */
+DIRENT_TYPE *
+__READDIR (DIR *dirp)
+{
+  DIRENT_TYPE *dp;
+  int saved_errno = errno;
+
+#ifndef NOT_IN_libc
+  __libc_lock_lock (dirp->lock);
+#endif
+
+  do
+    {
+      size_t reclen;
+
+      if (dirp->offset >= dirp->size)
+	{
+	  /* We've emptied out our buffer.  Refill it.  */
+
+	  size_t maxread;
+	  ssize_t bytes;
+
+#ifndef _DIRENT_HAVE_D_RECLEN
+	  /* Fixed-size struct; must read one at a time (see below).  */
+	  maxread = sizeof *dp;
+#else
+	  maxread = dirp->allocation;
+#endif
+
+	  bytes = __GETDENTS (dirp->fd, dirp->data, maxread);
+	  if (bytes <= 0)
+	    {
+	      /* On some systems getdents fails with ENOENT when the
+		 open directory has been rmdir'd already.  POSIX.1
+		 requires that we treat this condition like normal EOF.  */
+	      if (bytes < 0 && errno == ENOENT)
+		bytes = 0;
+
+	      /* Don't modifiy errno when reaching EOF.  */
+	      if (bytes == 0)
+		__set_errno (saved_errno);
+	      dp = NULL;
+	      break;
+	    }
+	  dirp->size = (size_t) bytes;
+
+	  /* Reset the offset into the buffer.  */
+	  dirp->offset = 0;
+	}
+
+      dp = (DIRENT_TYPE *) &dirp->data[dirp->offset];
+
+#ifdef _DIRENT_HAVE_D_RECLEN
+      reclen = dp->d_reclen;
+#else
+      /* The only version of `struct dirent*' that lacks `d_reclen'
+	 is fixed-size.  */
+      assert (sizeof dp->d_name > 1);
+      reclen = sizeof *dp;
+      /* The name is not terminated if it is the largest possible size.
+	 Clobber the following byte to ensure proper null termination.  We
+	 read jst one entry at a time above so we know that byte will not
+	 be used later.  */
+      dp->d_name[sizeof dp->d_name] = '\0';
+#endif
+
+      dirp->offset += reclen;
+
+#ifdef _DIRENT_HAVE_D_OFF
+      dirp->filepos = dp->d_off;
+#else
+      dirp->filepos += reclen;
+#endif
+
+      /* Skip deleted files.  */
+    } while (dp->d_ino == 0);
+
+#ifndef NOT_IN_libc
+  __libc_lock_unlock (dirp->lock);
+#endif
+
+  return dp;
+}
+
+#ifdef __READDIR_ALIAS
+weak_alias (__readdir, readdir)
+#endif
diff --git a/sysdeps/posix/readdir_r.c b/sysdeps/posix/readdir_r.c
new file mode 100644
index 0000000000..bfa2c0b8f5
--- /dev/null
+++ b/sysdeps/posix/readdir_r.c
@@ -0,0 +1,137 @@
+/* Copyright (C) 1991,92,93,94,95,96,97,98,99,2000,02,10
+	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, see
+   <http://www.gnu.org/licenses/>.  */
+
+#include <errno.h>
+#include <limits.h>
+#include <stddef.h>
+#include <string.h>
+#include <dirent.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <assert.h>
+
+#include <dirstream.h>
+
+#ifndef __READDIR_R
+# define __READDIR_R __readdir_r
+# define __GETDENTS __getdents
+# define DIRENT_TYPE struct dirent
+# define __READDIR_R_ALIAS
+#endif
+
+/* Read a directory entry from DIRP.  */
+int
+__READDIR_R (DIR *dirp, DIRENT_TYPE *entry, DIRENT_TYPE **result)
+{
+  DIRENT_TYPE *dp;
+  size_t reclen;
+  const int saved_errno = errno;
+
+  __libc_lock_lock (dirp->lock);
+
+  do
+    {
+      if (dirp->offset >= dirp->size)
+	{
+	  /* We've emptied out our buffer.  Refill it.  */
+
+	  size_t maxread;
+	  ssize_t bytes;
+
+#ifndef _DIRENT_HAVE_D_RECLEN
+	  /* Fixed-size struct; must read one at a time (see below).  */
+	  maxread = sizeof *dp;
+#else
+	  maxread = dirp->allocation;
+#endif
+
+	  bytes = __GETDENTS (dirp->fd, dirp->data, maxread);
+	  if (bytes <= 0)
+	    {
+	      /* On some systems getdents fails with ENOENT when the
+		 open directory has been rmdir'd already.  POSIX.1
+		 requires that we treat this condition like normal EOF.  */
+	      if (bytes < 0 && errno == ENOENT)
+		{
+		  bytes = 0;
+		  __set_errno (saved_errno);
+		}
+
+	      dp = NULL;
+	      /* Reclen != 0 signals that an error occurred.  */
+	      reclen = bytes != 0;
+	      break;
+	    }
+	  dirp->size = (size_t) bytes;
+
+	  /* Reset the offset into the buffer.  */
+	  dirp->offset = 0;
+	}
+
+      dp = (DIRENT_TYPE *) &dirp->data[dirp->offset];
+
+#ifdef _DIRENT_HAVE_D_RECLEN
+      reclen = dp->d_reclen;
+#else
+      /* The only version of `struct dirent*' that lacks `d_reclen'
+	 is fixed-size.  */
+      assert (sizeof dp->d_name > 1);
+      reclen = sizeof *dp;
+      /* The name is not terminated if it is the largest possible size.
+	 Clobber the following byte to ensure proper null termination.  We
+	 read just one entry at a time above so we know that byte will not
+	 be used later.  */
+      dp->d_name[sizeof dp->d_name] = '\0';
+#endif
+
+      dirp->offset += reclen;
+
+#ifdef _DIRENT_HAVE_D_OFF
+      dirp->filepos = dp->d_off;
+#else
+      dirp->filepos += reclen;
+#endif
+
+      /* Skip deleted files.  */
+    }
+  while (dp->d_ino == 0);
+
+  if (dp != NULL)
+    {
+#ifdef GETDENTS_64BIT_ALIGNED
+      /* The d_reclen value might include padding which is not part of
+	 the DIRENT_TYPE data structure.  */
+      reclen = MIN (reclen,
+		    offsetof (DIRENT_TYPE, d_name) + sizeof (dp->d_name));
+#endif
+      *result = memcpy (entry, dp, reclen);
+#ifdef GETDENTS_64BIT_ALIGNED
+      entry->d_reclen = reclen;
+#endif
+    }
+  else
+    *result = NULL;
+
+  __libc_lock_unlock (dirp->lock);
+
+  return dp != NULL ? 0 : reclen ? errno : 0;
+}
+
+#ifdef __READDIR_R_ALIAS
+weak_alias (__readdir_r, readdir_r)
+#endif
diff --git a/sysdeps/posix/rewinddir.c b/sysdeps/posix/rewinddir.c
new file mode 100644
index 0000000000..f8eefea542
--- /dev/null
+++ b/sysdeps/posix/rewinddir.c
@@ -0,0 +1,40 @@
+/* Copyright (C) 1991, 1995-1998, 2005, 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, see
+   <http://www.gnu.org/licenses/>.  */
+
+#include <stddef.h>
+#include <dirent.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <dirstream.h>
+
+/* Rewind DIRP to the beginning of the directory.  */
+void
+rewinddir (dirp)
+     DIR *dirp;
+{
+#ifndef NOT_IN_libc
+  __libc_lock_lock (dirp->lock);
+#endif
+  (void) __lseek (dirp->fd, (off_t) 0, SEEK_SET);
+  dirp->filepos = 0;
+  dirp->offset = 0;
+  dirp->size = 0;
+#ifndef NOT_IN_libc
+  __libc_lock_unlock (dirp->lock);
+#endif
+}
+libc_hidden_def (rewinddir)
diff --git a/sysdeps/posix/seekdir.c b/sysdeps/posix/seekdir.c
new file mode 100644
index 0000000000..88e34ee28f
--- /dev/null
+++ b/sysdeps/posix/seekdir.c
@@ -0,0 +1,37 @@
+/* Copyright (C) 1991,1995,1996,1997,1999,2001 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, see
+   <http://www.gnu.org/licenses/>.  */
+
+#include <errno.h>
+#include <stddef.h>
+#include <dirent.h>
+#include <unistd.h>
+#include <dirstream.h>
+
+/* Seek to position POS in DIRP.  */
+/* XXX should be __seekdir ? */
+void
+seekdir (dirp, pos)
+     DIR *dirp;
+     long int pos;
+{
+  __libc_lock_lock (dirp->lock);
+  (void) __lseek (dirp->fd, pos, SEEK_SET);
+  dirp->size = 0;
+  dirp->offset = 0;
+  dirp->filepos = pos;
+  __libc_lock_unlock (dirp->lock);
+}
diff --git a/sysdeps/posix/telldir.c b/sysdeps/posix/telldir.c
new file mode 100644
index 0000000000..4a3ad25b1d
--- /dev/null
+++ b/sysdeps/posix/telldir.c
@@ -0,0 +1,27 @@
+/* Copyright (C) 1991, 1995, 1996, 1997, 1999 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, see
+   <http://www.gnu.org/licenses/>.  */
+
+#include <dirent.h>
+
+#include <dirstream.h>
+
+/* Return the current position of DIRP.  */
+long int
+telldir (DIR *dirp)
+{
+  return dirp->filepos;
+}