diff --git a/NEWS b/NEWS
index 8a2fecef47..6b3958cd34 100644
--- a/NEWS
+++ b/NEWS
@@ -20,7 +20,8 @@ Major new features:
   twalk function, but it passes an additional caller-supplied argument
   to the callback function.
 
-* On Linux, the getdents64, gettid, and tgkill functions have been added.
+* On Linux, the getdents64, direntries_init, direntries_read,
+  direntries_next, gettid, and tgkill functions have been added.
 
 * Minguo (Republic of China) calendar support has been added as an
   alternative calendar for the following locales: zh_TW, cmn_TW, hak_TW,
diff --git a/include/sys/direntries.h b/include/sys/direntries.h
new file mode 100644
index 0000000000..33675fcb79
--- /dev/null
+++ b/include/sys/direntries.h
@@ -0,0 +1,38 @@
+/* Wrapper for <sys/direntries.h>.
+   Copyright (C) 2019 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/>.  */
+
+/* The direntries parser is available on Linux only for now.  */
+
+#ifndef _LIBC_DIRENTRIES_H
+#define _LIBC_DIRENTRIES_H
+
+#include_next <sys/direntries.h>
+
+#ifndef _ISOMAC
+extern __typeof__ (direntries_init) __direntries_init;
+libc_hidden_proto (__direntries_init)
+extern __typeof__ (direntries_read) __direntries_read;
+libc_hidden_proto (__direntries_read)
+extern __typeof__ (direntries_next) __direntries_next;
+libc_hidden_proto (__direntries_next)
+
+/* Members of struct direntries.   */
+# define direntries_buffer_begin __glibc_internal_1
+# define direntries_buffer_end __glibc_internal_2
+#endif /* !_ISOMAC */
+#endif /* _LIBC_DIRENTRIES_H */
diff --git a/manual/examples/direntries.c b/manual/examples/direntries.c
new file mode 100644
index 0000000000..8aaabfa6d0
--- /dev/null
+++ b/manual/examples/direntries.c
@@ -0,0 +1,42 @@
+/* Implement directory iteration using <sys/direntries.h>.
+   Copyright (C) 2019 Free Software Foundation, Inc.
+
+   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 General Public License
+   along with this program; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <stdbool.h>
+#include <sys/direntries.h>
+
+int
+foreach_directory_entry (int fd,
+                         int (*callback) (const struct direntry *, void *),
+                         void *closure)
+{
+  while (true)
+    {
+      char buffer[4096];
+      struct direntries entries;
+      ssize_t ret = direntries_read (fd, &entries, buffer, sizeof (buffer));
+      if (ret <= 0)
+        return ret;
+
+      struct direntry entry;
+      while (direntries_next (&entries, &entry) == 0)
+        {
+          int callback_result = callback (&entry, closure);
+          if (callback_result != 0)
+            return callback_result;
+        }
+    }
+}
diff --git a/manual/filesys.texi b/manual/filesys.texi
index 513319418a..908bc44cc6 100644
--- a/manual/filesys.texi
+++ b/manual/filesys.texi
@@ -864,6 +864,88 @@ characters), so a buffer size of at least 1024 is recommended.
 This function is specific to Linux.
 @end deftypefun
 
+@deftp {Data Type} {struct direntries}
+@standards {GNU, sys/direntries.h}
+
+This type is used to store an iterator which is used to traverse a
+buffer filled by the @code{getdents64} function.
+@end deftp
+
+@deftp {Data Type} {struct direntry}
+@standards {GNU, sys/direntries.h}
+
+This type provides access to one directory entry.  It is similar to
+@code{struct dirent} defined in @file{dirent.h}.  The
+@code{direntries_next} function described below uses this type to
+provide information to its caller.
+
+The following members are available to applications.
+
+@table @code
+@item d_name
+The name of the directory entry.  Note that unlike
+@code{struct dirent}, this is a pointer to a null-terminated string.
+
+@item d_ino
+The inode number of the directory entry.
+
+@item d_type
+The file type.  This is one of the @code{DT_} constants described for
+@code{struct direntry}.  It is @code{DT_UNKNOWN} if the information is
+not available.  @xref{Directory Entries}.
+
+@item d_flags
+Flags indicating extensions.  Currently always zero.
+@end table
+@end deftp
+
+@deftypefun void direntries_init (struct direntries *@var{iterator}, void *@var{buffer}, size_t @var{length})
+@standards{Linux, sys/direntries.h}
+@safety{@prelim{}@mtsafe{}@assafe{}@acsafe{}}
+This function initializes @code{*@var{iterator}} for traversing
+@var{length} bytes of directory data at @var{buffer}.  The buffer must
+have been filled with the @code{getdents64} function, and @var{length}
+should correspond to the return value of a successful call to
+@code{getdents64} for that buffer.
+
+This function is specific to Linux.
+@end deftypefun
+
+@deftypefun int direntries_read (int @var{fd}, struct direntries *@var{iterator}, void *@var{buffer}, size_t @var{length})
+@standards{Linux, sys/direntries.h}
+@safety{@prelim{}@mtsafe{}@assafe{}@acsafe{}}
+This function combines reading directory data from the file descriptor
+@var{fd} using the @code{getdents64} function and initialization of the
+iterator using the @code{direntries_init} function.  On success, the
+@code{direntries_read} function returns either zero (if there are no
+more directory entries available in @var{fd}), or a positive value (if
+more entries are availabe).  On failure, it returns @code{-1} and sets
+@code{errno} accordingly, and @code{*@var{iterator}} is not
+initialized.
+
+The buffer size considerations for @code{getdents64} also apply to
+this function.
+
+This function is specific to Linux.
+@end deftypefun
+
+@deftypefun int direntries_next (struct direntries *@var{iterator}, struct direntry *@var{entry})
+@standards{Linux, sys/direntries.h}
+@safety{@prelim{}@mtsafe{}@assafe{}@acsafe{}}
+This function copies the next directory entry (if available) from the
+iterator to @code{*@var{entry}}, and updates @code{*@var{iterator}}
+accordingly.  On success, it returns zero, and on failure, it returns
+@code{-1} and sets @code{errno} to @code{ENOENT}.
+
+This function is specific to Linux.
+@end deftypefun
+
+The example below shows how implement callback-based traversal of one
+directory using @code{direntries_read} and @code{direntries_next}.
+
+@smallexample
+@include direntries.c.texi
+@end smallexample
 
 @node Working with Directory Trees
 @section Working with Directory Trees
diff --git a/sysdeps/unix/sysv/linux/Makefile b/sysdeps/unix/sysv/linux/Makefile
index afcdc658b5..498e60c223 100644
--- a/sysdeps/unix/sysv/linux/Makefile
+++ b/sysdeps/unix/sysv/linux/Makefile
@@ -187,8 +187,10 @@ endif
 inhibit-glue = yes
 
 ifeq ($(subdir),dirent)
-sysdep_routines += getdirentries getdirentries64
-tests += tst-getdents64
+sysdep_headers += sys/direntries.h
+sysdep_routines += getdirentries getdirentries64 \
+  direntries_init direntries_read direntries_next
+tests += tst-getdents64 tst-direntries
 tests-internal += tst-readdir64-compat
 endif
 
diff --git a/sysdeps/unix/sysv/linux/Versions b/sysdeps/unix/sysv/linux/Versions
index 1ca102a9e2..e478c6bf39 100644
--- a/sysdeps/unix/sysv/linux/Versions
+++ b/sysdeps/unix/sysv/linux/Versions
@@ -176,6 +176,7 @@ libc {
   }
   GLIBC_2.30 {
     getdents64; gettid; tgkill;
+    direntries_init; direntries_read; direntries_next;
   }
   GLIBC_PRIVATE {
     # functions used in other libraries
diff --git a/sysdeps/unix/sysv/linux/aarch64/libc.abilist b/sysdeps/unix/sysv/linux/aarch64/libc.abilist
index a4c31932cb..cf2c128a2d 100644
--- a/sysdeps/unix/sysv/linux/aarch64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/aarch64/libc.abilist
@@ -2141,6 +2141,9 @@ GLIBC_2.28 thrd_yield F
 GLIBC_2.29 getcpu F
 GLIBC_2.29 posix_spawn_file_actions_addchdir_np F
 GLIBC_2.29 posix_spawn_file_actions_addfchdir_np F
+GLIBC_2.30 direntries_init F
+GLIBC_2.30 direntries_next F
+GLIBC_2.30 direntries_read F
 GLIBC_2.30 getdents64 F
 GLIBC_2.30 gettid F
 GLIBC_2.30 tgkill F
diff --git a/sysdeps/unix/sysv/linux/alpha/libc.abilist b/sysdeps/unix/sysv/linux/alpha/libc.abilist
index fe85a35620..80daa9383f 100644
--- a/sysdeps/unix/sysv/linux/alpha/libc.abilist
+++ b/sysdeps/unix/sysv/linux/alpha/libc.abilist
@@ -2216,6 +2216,9 @@ GLIBC_2.30 __nldbl_vwarn F
 GLIBC_2.30 __nldbl_vwarnx F
 GLIBC_2.30 __nldbl_warn F
 GLIBC_2.30 __nldbl_warnx F
+GLIBC_2.30 direntries_init F
+GLIBC_2.30 direntries_next F
+GLIBC_2.30 direntries_read F
 GLIBC_2.30 getdents64 F
 GLIBC_2.30 gettid F
 GLIBC_2.30 tgkill F
diff --git a/sysdeps/unix/sysv/linux/arm/libc.abilist b/sysdeps/unix/sysv/linux/arm/libc.abilist
index bc3df8dcea..3ba50a2618 100644
--- a/sysdeps/unix/sysv/linux/arm/libc.abilist
+++ b/sysdeps/unix/sysv/linux/arm/libc.abilist
@@ -126,6 +126,9 @@ GLIBC_2.28 thrd_yield F
 GLIBC_2.29 getcpu F
 GLIBC_2.29 posix_spawn_file_actions_addchdir_np F
 GLIBC_2.29 posix_spawn_file_actions_addfchdir_np F
+GLIBC_2.30 direntries_init F
+GLIBC_2.30 direntries_next F
+GLIBC_2.30 direntries_read F
 GLIBC_2.30 getdents64 F
 GLIBC_2.30 gettid F
 GLIBC_2.30 tgkill F
diff --git a/sysdeps/unix/sysv/linux/csky/libc.abilist b/sysdeps/unix/sysv/linux/csky/libc.abilist
index 9b3cee65bb..3cf2aaa6ed 100644
--- a/sysdeps/unix/sysv/linux/csky/libc.abilist
+++ b/sysdeps/unix/sysv/linux/csky/libc.abilist
@@ -2085,6 +2085,9 @@ GLIBC_2.29 xdrstdio_create F
 GLIBC_2.29 xencrypt F
 GLIBC_2.29 xprt_register F
 GLIBC_2.29 xprt_unregister F
+GLIBC_2.30 direntries_init F
+GLIBC_2.30 direntries_next F
+GLIBC_2.30 direntries_read F
 GLIBC_2.30 getdents64 F
 GLIBC_2.30 gettid F
 GLIBC_2.30 tgkill F
diff --git a/sysdeps/unix/sysv/linux/direntries_init.c b/sysdeps/unix/sysv/linux/direntries_init.c
new file mode 100644
index 0000000000..812bcc4cdd
--- /dev/null
+++ b/sysdeps/unix/sysv/linux/direntries_init.c
@@ -0,0 +1,34 @@
+/* Initialization of directory iterators.
+   Copyright (C) 2019 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 <sys/direntries.h>
+#include <stdio.h>
+
+void
+__direntries_init (struct direntries *iterator, void *buffer, size_t length)
+{
+  /* Cheap security check if the caller accidentally passed an error
+     result from getdirentries to this function.  */
+  if ((ssize_t) length < 0)
+    __fortify_fail ("invalid direntries_init call");
+
+  iterator->direntries_buffer_begin = buffer;
+  iterator->direntries_buffer_end = (char *) buffer + length;
+}
+libc_hidden_def (__direntries_init)
+strong_alias (__direntries_init, direntries_init)
diff --git a/sysdeps/unix/sysv/linux/direntries_next.c b/sysdeps/unix/sysv/linux/direntries_next.c
new file mode 100644
index 0000000000..8624c679f4
--- /dev/null
+++ b/sysdeps/unix/sysv/linux/direntries_next.c
@@ -0,0 +1,51 @@
+/* Advancing directory iterators.
+   Copyright (C) 2019 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 <sys/direntries.h>
+
+#include <dirent.h>
+#include <errno.h>
+#include <string.h>
+
+int
+__direntries_next (struct direntries *iterator, struct direntry *result)
+{
+  if (iterator->direntries_buffer_begin == iterator->direntries_buffer_end)
+    {
+      __set_errno (ENOENT);
+      return -1;
+    }
+
+  char *begin = iterator->direntries_buffer_begin;
+
+  /* The caller may have supplied an unaligned buffer.  Make an
+     aligned copy of the entry, excluding its name.  */
+  struct dirent64 entry;
+  memcpy (&entry, begin, offsetof (struct dirent64, d_name));
+
+  /* The name is not copied.  It points into the existing buffer.  */
+  result->d_name = begin + offsetof (struct dirent64, d_name);
+  result->d_ino = entry.d_ino;
+  result->d_type = entry.d_type;
+  result->d_flags = 0;
+
+  iterator->direntries_buffer_begin = begin + entry.d_reclen;
+  return 0;
+}
+libc_hidden_def (__direntries_next)
+strong_alias (__direntries_next, direntries_next)
diff --git a/sysdeps/unix/sysv/linux/direntries_read.c b/sysdeps/unix/sysv/linux/direntries_read.c
new file mode 100644
index 0000000000..493a0aaaaf
--- /dev/null
+++ b/sysdeps/unix/sysv/linux/direntries_read.c
@@ -0,0 +1,34 @@
+/* Buffer reading for directory iterators.
+   Copyright (C) 2019 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 <sys/direntries.h>
+
+#include <dirent.h>
+
+ssize_t
+__direntries_read (int fd, struct direntries *iterator,
+                   void *buffer, size_t length)
+{
+  ssize_t ret = __getdents64 (fd, buffer, length);
+  if (ret < 0)
+    return ret;
+  __direntries_init (iterator, buffer, ret);
+  return ret;
+}
+libc_hidden_def (__direntries_read)
+strong_alias (__direntries_read, direntries_read)
diff --git a/sysdeps/unix/sysv/linux/hppa/libc.abilist b/sysdeps/unix/sysv/linux/hppa/libc.abilist
index 75edece94a..333986e6ed 100644
--- a/sysdeps/unix/sysv/linux/hppa/libc.abilist
+++ b/sysdeps/unix/sysv/linux/hppa/libc.abilist
@@ -2037,6 +2037,9 @@ GLIBC_2.3.4 setipv4sourcefilter F
 GLIBC_2.3.4 setsourcefilter F
 GLIBC_2.3.4 xdr_quad_t F
 GLIBC_2.3.4 xdr_u_quad_t F
+GLIBC_2.30 direntries_init F
+GLIBC_2.30 direntries_next F
+GLIBC_2.30 direntries_read F
 GLIBC_2.30 getdents64 F
 GLIBC_2.30 gettid F
 GLIBC_2.30 tgkill F
diff --git a/sysdeps/unix/sysv/linux/i386/libc.abilist b/sysdeps/unix/sysv/linux/i386/libc.abilist
index edeaf8e722..99cef29872 100644
--- a/sysdeps/unix/sysv/linux/i386/libc.abilist
+++ b/sysdeps/unix/sysv/linux/i386/libc.abilist
@@ -2203,6 +2203,9 @@ GLIBC_2.3.4 setsourcefilter F
 GLIBC_2.3.4 vm86 F
 GLIBC_2.3.4 xdr_quad_t F
 GLIBC_2.3.4 xdr_u_quad_t F
+GLIBC_2.30 direntries_init F
+GLIBC_2.30 direntries_next F
+GLIBC_2.30 direntries_read F
 GLIBC_2.30 getdents64 F
 GLIBC_2.30 gettid F
 GLIBC_2.30 tgkill F
diff --git a/sysdeps/unix/sysv/linux/ia64/libc.abilist b/sysdeps/unix/sysv/linux/ia64/libc.abilist
index b5d460eeb2..4bcd0d284b 100644
--- a/sysdeps/unix/sysv/linux/ia64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/ia64/libc.abilist
@@ -2069,6 +2069,9 @@ GLIBC_2.3.4 setipv4sourcefilter F
 GLIBC_2.3.4 setsourcefilter F
 GLIBC_2.3.4 xdr_quad_t F
 GLIBC_2.3.4 xdr_u_quad_t F
+GLIBC_2.30 direntries_init F
+GLIBC_2.30 direntries_next F
+GLIBC_2.30 direntries_read F
 GLIBC_2.30 getdents64 F
 GLIBC_2.30 gettid F
 GLIBC_2.30 tgkill F
diff --git a/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist b/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist
index 05633b3cb8..e070c1f604 100644
--- a/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist
+++ b/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist
@@ -127,6 +127,9 @@ GLIBC_2.28 thrd_yield F
 GLIBC_2.29 getcpu F
 GLIBC_2.29 posix_spawn_file_actions_addchdir_np F
 GLIBC_2.29 posix_spawn_file_actions_addfchdir_np F
+GLIBC_2.30 direntries_init F
+GLIBC_2.30 direntries_next F
+GLIBC_2.30 direntries_read F
 GLIBC_2.30 getdents64 F
 GLIBC_2.30 gettid F
 GLIBC_2.30 tgkill F
diff --git a/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist b/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist
index 47eb7b4608..2135ca7f75 100644
--- a/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist
+++ b/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist
@@ -2146,6 +2146,9 @@ GLIBC_2.3.4 setipv4sourcefilter F
 GLIBC_2.3.4 setsourcefilter F
 GLIBC_2.3.4 xdr_quad_t F
 GLIBC_2.3.4 xdr_u_quad_t F
+GLIBC_2.30 direntries_init F
+GLIBC_2.30 direntries_next F
+GLIBC_2.30 direntries_read F
 GLIBC_2.30 getdents64 F
 GLIBC_2.30 gettid F
 GLIBC_2.30 tgkill F
diff --git a/sysdeps/unix/sysv/linux/microblaze/libc.abilist b/sysdeps/unix/sysv/linux/microblaze/libc.abilist
index f7ced487f7..9b7ade8fe9 100644
--- a/sysdeps/unix/sysv/linux/microblaze/libc.abilist
+++ b/sysdeps/unix/sysv/linux/microblaze/libc.abilist
@@ -2133,6 +2133,9 @@ GLIBC_2.28 thrd_yield F
 GLIBC_2.29 getcpu F
 GLIBC_2.29 posix_spawn_file_actions_addchdir_np F
 GLIBC_2.29 posix_spawn_file_actions_addfchdir_np F
+GLIBC_2.30 direntries_init F
+GLIBC_2.30 direntries_next F
+GLIBC_2.30 direntries_read F
 GLIBC_2.30 getdents64 F
 GLIBC_2.30 gettid F
 GLIBC_2.30 tgkill F
diff --git a/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist
index e49dc4272e..2beeb2521d 100644
--- a/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist
+++ b/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist
@@ -2120,6 +2120,9 @@ GLIBC_2.3.4 setipv4sourcefilter F
 GLIBC_2.3.4 setsourcefilter F
 GLIBC_2.3.4 xdr_quad_t F
 GLIBC_2.3.4 xdr_u_quad_t F
+GLIBC_2.30 direntries_init F
+GLIBC_2.30 direntries_next F
+GLIBC_2.30 direntries_read F
 GLIBC_2.30 getdents64 F
 GLIBC_2.30 gettid F
 GLIBC_2.30 tgkill F
diff --git a/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist
index daa3b60c5b..2ccf091945 100644
--- a/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist
+++ b/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist
@@ -2118,6 +2118,9 @@ GLIBC_2.3.4 setipv4sourcefilter F
 GLIBC_2.3.4 setsourcefilter F
 GLIBC_2.3.4 xdr_quad_t F
 GLIBC_2.3.4 xdr_u_quad_t F
+GLIBC_2.30 direntries_init F
+GLIBC_2.30 direntries_next F
+GLIBC_2.30 direntries_read F
 GLIBC_2.30 getdents64 F
 GLIBC_2.30 gettid F
 GLIBC_2.30 tgkill F
diff --git a/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist
index 457ce0b6f2..ff555e9fa1 100644
--- a/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist
+++ b/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist
@@ -2126,6 +2126,9 @@ GLIBC_2.3.4 setipv4sourcefilter F
 GLIBC_2.3.4 setsourcefilter F
 GLIBC_2.3.4 xdr_quad_t F
 GLIBC_2.3.4 xdr_u_quad_t F
+GLIBC_2.30 direntries_init F
+GLIBC_2.30 direntries_next F
+GLIBC_2.30 direntries_read F
 GLIBC_2.30 getdents64 F
 GLIBC_2.30 gettid F
 GLIBC_2.30 tgkill F
diff --git a/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist
index 63d5c03bfb..964d4f6070 100644
--- a/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist
@@ -2120,6 +2120,9 @@ GLIBC_2.3.4 setipv4sourcefilter F
 GLIBC_2.3.4 setsourcefilter F
 GLIBC_2.3.4 xdr_quad_t F
 GLIBC_2.3.4 xdr_u_quad_t F
+GLIBC_2.30 direntries_init F
+GLIBC_2.30 direntries_next F
+GLIBC_2.30 direntries_read F
 GLIBC_2.30 getdents64 F
 GLIBC_2.30 gettid F
 GLIBC_2.30 tgkill F
diff --git a/sysdeps/unix/sysv/linux/nios2/libc.abilist b/sysdeps/unix/sysv/linux/nios2/libc.abilist
index 7fec0c9670..9600c65a74 100644
--- a/sysdeps/unix/sysv/linux/nios2/libc.abilist
+++ b/sysdeps/unix/sysv/linux/nios2/libc.abilist
@@ -2174,6 +2174,9 @@ GLIBC_2.28 thrd_yield F
 GLIBC_2.29 getcpu F
 GLIBC_2.29 posix_spawn_file_actions_addchdir_np F
 GLIBC_2.29 posix_spawn_file_actions_addfchdir_np F
+GLIBC_2.30 direntries_init F
+GLIBC_2.30 direntries_next F
+GLIBC_2.30 direntries_read F
 GLIBC_2.30 getdents64 F
 GLIBC_2.30 gettid F
 GLIBC_2.30 tgkill F
diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist
index 9200a54309..610b9ba711 100644
--- a/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist
+++ b/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist
@@ -2176,6 +2176,9 @@ GLIBC_2.30 __nldbl_vwarn F
 GLIBC_2.30 __nldbl_vwarnx F
 GLIBC_2.30 __nldbl_warn F
 GLIBC_2.30 __nldbl_warnx F
+GLIBC_2.30 direntries_init F
+GLIBC_2.30 direntries_next F
+GLIBC_2.30 direntries_read F
 GLIBC_2.30 getdents64 F
 GLIBC_2.30 gettid F
 GLIBC_2.30 tgkill F
diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist
index ef7779905f..11313a15cb 100644
--- a/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist
+++ b/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist
@@ -2209,6 +2209,9 @@ GLIBC_2.30 __nldbl_vwarn F
 GLIBC_2.30 __nldbl_vwarnx F
 GLIBC_2.30 __nldbl_warn F
 GLIBC_2.30 __nldbl_warnx F
+GLIBC_2.30 direntries_init F
+GLIBC_2.30 direntries_next F
+GLIBC_2.30 direntries_read F
 GLIBC_2.30 getdents64 F
 GLIBC_2.30 gettid F
 GLIBC_2.30 tgkill F
diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc64/be/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc64/be/libc.abilist
index 2860df8ebc..aad19fe59a 100644
--- a/sysdeps/unix/sysv/linux/powerpc/powerpc64/be/libc.abilist
+++ b/sysdeps/unix/sysv/linux/powerpc/powerpc64/be/libc.abilist
@@ -2039,6 +2039,9 @@ GLIBC_2.30 __nldbl_vwarn F
 GLIBC_2.30 __nldbl_vwarnx F
 GLIBC_2.30 __nldbl_warn F
 GLIBC_2.30 __nldbl_warnx F
+GLIBC_2.30 direntries_init F
+GLIBC_2.30 direntries_next F
+GLIBC_2.30 direntries_read F
 GLIBC_2.30 getdents64 F
 GLIBC_2.30 gettid F
 GLIBC_2.30 tgkill F
diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc64/le/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc64/le/libc.abilist
index 2229a1dcc0..756caf69e5 100644
--- a/sysdeps/unix/sysv/linux/powerpc/powerpc64/le/libc.abilist
+++ b/sysdeps/unix/sysv/linux/powerpc/powerpc64/le/libc.abilist
@@ -2243,6 +2243,9 @@ GLIBC_2.30 __nldbl_vwarn F
 GLIBC_2.30 __nldbl_vwarnx F
 GLIBC_2.30 __nldbl_warn F
 GLIBC_2.30 __nldbl_warnx F
+GLIBC_2.30 direntries_init F
+GLIBC_2.30 direntries_next F
+GLIBC_2.30 direntries_read F
 GLIBC_2.30 getdents64 F
 GLIBC_2.30 gettid F
 GLIBC_2.30 tgkill F
diff --git a/sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist b/sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist
index 31010e6cf7..d0b175117e 100644
--- a/sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist
@@ -2103,6 +2103,9 @@ GLIBC_2.28 thrd_yield F
 GLIBC_2.29 getcpu F
 GLIBC_2.29 posix_spawn_file_actions_addchdir_np F
 GLIBC_2.29 posix_spawn_file_actions_addfchdir_np F
+GLIBC_2.30 direntries_init F
+GLIBC_2.30 direntries_next F
+GLIBC_2.30 direntries_read F
 GLIBC_2.30 getdents64 F
 GLIBC_2.30 gettid F
 GLIBC_2.30 tgkill F
diff --git a/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist b/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist
index 576295deff..61d15d4b0c 100644
--- a/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist
+++ b/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist
@@ -2171,6 +2171,9 @@ GLIBC_2.30 __nldbl_vwarn F
 GLIBC_2.30 __nldbl_vwarnx F
 GLIBC_2.30 __nldbl_warn F
 GLIBC_2.30 __nldbl_warnx F
+GLIBC_2.30 direntries_init F
+GLIBC_2.30 direntries_next F
+GLIBC_2.30 direntries_read F
 GLIBC_2.30 getdents64 F
 GLIBC_2.30 gettid F
 GLIBC_2.30 tgkill F
diff --git a/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist b/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist
index abf0473683..a43049c313 100644
--- a/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist
@@ -2075,6 +2075,9 @@ GLIBC_2.30 __nldbl_vwarn F
 GLIBC_2.30 __nldbl_vwarnx F
 GLIBC_2.30 __nldbl_warn F
 GLIBC_2.30 __nldbl_warnx F
+GLIBC_2.30 direntries_init F
+GLIBC_2.30 direntries_next F
+GLIBC_2.30 direntries_read F
 GLIBC_2.30 getdents64 F
 GLIBC_2.30 gettid F
 GLIBC_2.30 tgkill F
diff --git a/sysdeps/unix/sysv/linux/sh/libc.abilist b/sysdeps/unix/sysv/linux/sh/libc.abilist
index 41977f6e9c..69000da3d7 100644
--- a/sysdeps/unix/sysv/linux/sh/libc.abilist
+++ b/sysdeps/unix/sysv/linux/sh/libc.abilist
@@ -2041,6 +2041,9 @@ GLIBC_2.3.4 setipv4sourcefilter F
 GLIBC_2.3.4 setsourcefilter F
 GLIBC_2.3.4 xdr_quad_t F
 GLIBC_2.3.4 xdr_u_quad_t F
+GLIBC_2.30 direntries_init F
+GLIBC_2.30 direntries_next F
+GLIBC_2.30 direntries_read F
 GLIBC_2.30 getdents64 F
 GLIBC_2.30 gettid F
 GLIBC_2.30 tgkill F
diff --git a/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist b/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist
index 3d2f00ca52..367b1af9b1 100644
--- a/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist
+++ b/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist
@@ -2165,6 +2165,9 @@ GLIBC_2.30 __nldbl_vwarn F
 GLIBC_2.30 __nldbl_vwarnx F
 GLIBC_2.30 __nldbl_warn F
 GLIBC_2.30 __nldbl_warnx F
+GLIBC_2.30 direntries_init F
+GLIBC_2.30 direntries_next F
+GLIBC_2.30 direntries_read F
 GLIBC_2.30 getdents64 F
 GLIBC_2.30 gettid F
 GLIBC_2.30 tgkill F
diff --git a/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist b/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist
index 2f20643e8e..e961fcc386 100644
--- a/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist
@@ -2092,6 +2092,9 @@ GLIBC_2.3.4 setipv4sourcefilter F
 GLIBC_2.3.4 setsourcefilter F
 GLIBC_2.3.4 xdr_quad_t F
 GLIBC_2.3.4 xdr_u_quad_t F
+GLIBC_2.30 direntries_init F
+GLIBC_2.30 direntries_next F
+GLIBC_2.30 direntries_read F
 GLIBC_2.30 getdents64 F
 GLIBC_2.30 gettid F
 GLIBC_2.30 tgkill F
diff --git a/sysdeps/unix/sysv/linux/sys/direntries.h b/sysdeps/unix/sysv/linux/sys/direntries.h
new file mode 100644
index 0000000000..f2bd2667f5
--- /dev/null
+++ b/sysdeps/unix/sysv/linux/sys/direntries.h
@@ -0,0 +1,93 @@
+/* Parsing directory streams.  Linux version.
+   Copyright (C) 2019 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 _SYS_DIRENTRIES_H
+#define _SYS_DIRENTRIES_H
+
+#include <features.h>
+#include <sys/types.h>
+
+/* One directory entry.  See struct dirent in <dirent.h>.  The main
+   difference is that d_name is a pointer here, and not an array, as
+   in struct dirent.  */
+struct direntry
+{
+  /* Name of the directory entry.  The string is part of the buffer
+     passed to direntries_init or direntries_read below.  */
+  const char *d_name;
+
+  /* Inode number of this directory entry.  */
+  __ino64_t d_ino;
+
+  /* Type of the entry.  Can be DT_UNKNOWN if unknown.  */
+  unsigned char d_type;
+
+  /* Currently always zero.  */
+  unsigned char d_flags;
+
+  /* Room for further extension.  Do not use.  */
+  union
+  {
+    __int64_t __glibc__reserved_1[3];
+    void *__glibc__reserved_2[3];
+  } __glibc__reserved_3;
+};
+
+/* Initialized by direntries_init or direntires_read below and used by
+   direntry_next.  */
+struct direntries
+{
+  /* Internal information.  Do not use.  */
+  void *__glibc_internal_1;
+  void *__glibc_internal_2;
+
+  /* Room for further extension.  Do not use.  */
+  union
+  {
+    __int64_t __glibc__reserved_1[4];
+    void *__glibc__reserved_2[4];
+  } __glibc__reserved_3;
+};
+
+__BEGIN_DECLS
+
+/* Initialize *ITERATOR based on BUFFER and LENGTH.  BUFFER must have
+   been filled by getdents, and length must be a non-negative return
+   value from this function.  Afterwards, individual entries can be
+   retrieved using direntries_next.  */
+void direntries_init (struct direntries *__iterator,
+                      void *__buffer, size_t __length)
+  __THROW __nonnull ((1, 2));
+
+/* Read directory entries from FD and write them to LENGTH bytes at
+   BUFFER and initialize *ITERATOR so that individual entries can be
+   retrieved using direntries_next.  Returns -1 on error, 0 on end of
+   stream, and a positive value in case of more data.  */
+__ssize_t direntries_read (int __fd, struct direntries *__iterator,
+                           void *__buffer, size_t __length)
+  __THROW __nonnull ((2, 3)) __wur;
+
+/* If there is a next entry, copy the next directory entry in ITERATOR
+   to *ENTRY, update *ITERATOR, and returns 0.  If there is no next
+   entry, return -1 and set errno to ENOENT.  */
+int direntries_next (struct direntries *__iterator, struct direntry *__entry)
+  __THROW __nonnull ((1, 2)) __wur;
+
+__END_DECLS
+
+#endif /* _SYS_DIRENTRIES_H */
diff --git a/sysdeps/unix/sysv/linux/tst-direntries.c b/sysdeps/unix/sysv/linux/tst-direntries.c
new file mode 100644
index 0000000000..1636dbd9e7
--- /dev/null
+++ b/sysdeps/unix/sysv/linux/tst-direntries.c
@@ -0,0 +1,120 @@
+/* Test for directory iterators.
+   Copyright (C) 2019 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 <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <support/check.h>
+#include <support/support.h>
+#include <support/test-driver.h>
+#include <support/xunistd.h>
+#include <sys/direntries.h>
+#include <unistd.h>
+
+static void
+test_one (bool do_read)
+{
+  /* The test compares the iteration order with readdir64.  */
+  DIR *reference = opendir (".");
+  TEST_VERIFY_EXIT (reference != NULL);
+
+  int fd = xopen (".", O_RDONLY | O_DIRECTORY, 0);
+  TEST_VERIFY (fd >= 0);
+
+  /* Check that we need to fill the buffer multiple times.  */
+  int read_count = 0;
+
+  while (true)
+    {
+      char buffer[1024];
+      struct direntries entries;
+      ssize_t ret;
+      if (do_read)
+        {
+          ret = direntries_read (fd, &entries, buffer, sizeof (buffer));
+          if (ret < 0)
+            FAIL_EXIT1 ("direntries_read: %m");
+        }
+      else
+        {
+          ret = getdents64 (fd, buffer, sizeof (buffer));
+          if (ret < 0)
+            FAIL_EXIT1 ("getdents64: %m");
+          if (ret > 0)
+            direntries_init (&entries, buffer, ret);
+        }
+      if (ret == 0)
+        break;
+      ++read_count;
+      if (test_verbose > 0)
+        {
+          char *s = support_quote_blob (buffer, ret);
+          printf ("info: data: \"%s\"\n", s);
+          free (s);
+        }
+
+      struct direntry entry;
+      errno = 0;
+      while (direntries_next (&entries, &entry) == 0)
+        {
+          errno = 0;
+          struct dirent64 *refentry = readdir64 (reference);
+          if (refentry == NULL && errno == 0)
+              FAIL_EXIT1 ("readdir64 failed too early, at: %s",
+                          entry.d_name);
+          else if (refentry == NULL)
+            FAIL_EXIT1 ("readdir64: %m");
+
+          TEST_COMPARE_STRING (entry.d_name, refentry->d_name);
+          TEST_COMPARE (entry.d_ino, refentry->d_ino);
+          TEST_COMPARE (entry.d_type, refentry->d_type);
+          TEST_COMPARE (entry.d_flags, 0);
+
+          errno = 0;
+        }
+      TEST_COMPARE (errno, ENOENT);
+    }
+
+  /* We expect to have reached the end of the stream.  */
+  errno = 0;
+  TEST_VERIFY (readdir64 (reference) == NULL);
+  TEST_COMPARE (errno, 0);
+
+  /* We should have read more than one block.  */
+  TEST_VERIFY (read_count > 0);
+
+  xclose (fd);
+  closedir (reference);
+}
+
+static int
+do_test (void)
+{
+  puts ("info: test using getdents64");
+  test_one (false);
+
+  puts ("info: test using direntries_read");
+  test_one (true);
+
+  return 0;
+}
+
+#include <support/test-driver.c>
diff --git a/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist b/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist
index 59f85d9373..b4456c0928 100644
--- a/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist
@@ -2050,6 +2050,9 @@ GLIBC_2.3.4 setipv4sourcefilter F
 GLIBC_2.3.4 setsourcefilter F
 GLIBC_2.3.4 xdr_quad_t F
 GLIBC_2.3.4 xdr_u_quad_t F
+GLIBC_2.30 direntries_init F
+GLIBC_2.30 direntries_next F
+GLIBC_2.30 direntries_read F
 GLIBC_2.30 getdents64 F
 GLIBC_2.30 gettid F
 GLIBC_2.30 tgkill F
diff --git a/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist b/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist
index 67a4e238d6..4c5937e609 100644
--- a/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist
+++ b/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist
@@ -2149,6 +2149,9 @@ GLIBC_2.28 thrd_yield F
 GLIBC_2.29 getcpu F
 GLIBC_2.29 posix_spawn_file_actions_addchdir_np F
 GLIBC_2.29 posix_spawn_file_actions_addfchdir_np F
+GLIBC_2.30 direntries_init F
+GLIBC_2.30 direntries_next F
+GLIBC_2.30 direntries_read F
 GLIBC_2.30 getdents64 F
 GLIBC_2.30 gettid F
 GLIBC_2.30 tgkill F
