Patchwork Linux: Add getdents64 system call

login
register
mail settings
Submitter Florian Weimer
Date May 15, 2019, 3:10 p.m.
Message ID <87bm03wy6u.fsf@oldenburg2.str.redhat.com>
Download mbox | patch
Permalink /patch/32698/
State Superseded
Headers show

Comments

Florian Weimer - May 15, 2019, 3:10 p.m.
No 32-bit system call wrapper is added because the interface
is problematic because it cannot deal with 32-bit inode numbers
and 64-bit directory hashes.

A future commit will deprecate the undocumented getdirentries
and getdirentries64 functions.

2019-05-15  Florian Weimer  <fweimer@redhat.com>

	Linux: Add getdents64 system call.
	* include/dirnent.h (getdents): Add comment and change buffer
	argument type to void *.
	(getdents64): Likewise.  Add hidden prototype.
	* sysdeps/unix/sysv/linux/bits/Versions (GLIBC_2.30): Export
	getdents64.
	* sysdeps/unix/sysv/linux/Makefile [$(subdir) == dirent] (tests):
	Add tst-getdents64.
	* sysdeps/unix/sysv/linux/bits/unistd_ext.h (getdents64): Declare.
	* sysdeps/unix/sysv/linux/getdents.c (__getdents): Change buffer
	argument type to void *.
	* sysdeps/unix/sysv/linux/getdents64.c (__getdents64): Likewise.
	Add hidden definition and getdents64 alias.
	* sysdeps/unix/sysv/linux/mips/mips64/getdents64.c (__getdents64):
	Likewise.
	* sysdeps/unix/sysv/linux/tst-getdents64.c: New file.
	* manual/filesys.texi (Accessing Directories): Add Low-level
	Directory Access node reference.
	(Opening a Directory): Cross-reference it.
	(Low-level Directory Access): New node.
	* sysdeps/unix/sysv/linux/aarch64/libc.abilist (GLIBC_2.30): Add
	getdents64.
	* sysdeps/unix/sysv/linux/alpha/libc.abilist (GLIBC_2.30):
	Likewise.
	* sysdeps/unix/sysv/linux/arm/libc.abilist (GLIBC_2.30): Likewise.
	* sysdeps/unix/sysv/linux/csky/libc.abilist (GLIBC_2.30):
	Likewise.
	* sysdeps/unix/sysv/linux/hppa/libc.abilist (GLIBC_2.30):
	Likewise.
	* sysdeps/unix/sysv/linux/i386/libc.abilist (GLIBC_2.30):
	Likewise.
	* sysdeps/unix/sysv/linux/ia64/libc.abilist (GLIBC_2.30):
	Likewise.
	* sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist (GLIBC_2.30):
	Likewise.
	* sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist (GLIBC_2.30):
	Likewise.
	* sysdeps/unix/sysv/linux/microblaze/libc.abilist (GLIBC_2.30):
	Likewise.
	* sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist
	(GLIBC_2.30): Likewise.
	* sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist
	(GLIBC_2.30): Likewise.
	* sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist
	(GLIBC_2.30): Likewise.
	* sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist
	(GLIBC_2.30): Likewise.
	* sysdeps/unix/sysv/linux/nios2/libc.abilist (GLIBC_2.30):
	Likewise.
	* sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist
	(GLIBC_2.30): Likewise.
	* sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist
	(GLIBC_2.30): Likewise.
	* sysdeps/unix/sysv/linux/powerpc/powerpc64/be/libc.abilist
	(GLIBC_2.30): Likewise.
	* sysdeps/unix/sysv/linux/powerpc/powerpc64/le/libc.abilist
	(GLIBC_2.30): Likewise.
	* sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist (GLIBC_2.30):
	Likewise.
	* sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist (GLIBC_2.30):
	Likewise.
	* sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist (GLIBC_2.30):
	Likewise.
	* sysdeps/unix/sysv/linux/sh/libc.abilist (GLIBC_2.30): Likewise.
	* sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist (GLIBC_2.30):
	Likewise.
	* sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist (GLIBC_2.30):
	Likewise.
	* sysdeps/unix/sysv/linux/x86_64/64/libc.abilist (GLIBC_2.30):
	Likewise.
	* sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist (GLIBC_2.30):
	Likewise.
Carlos O'Donell - May 28, 2019, 2:49 a.m.
On 5/15/19 11:10 AM, Florian Weimer wrote:
> No 32-bit system call wrapper is added because the interface
> is problematic because it cannot deal with 32-bit inode numbers
> and 64-bit directory hashes.

Are you saying we do not want to ever add a getdents syscall wrapper
because it would have problems that users couldn't fix, or problems
the user could fix if they were careful?

> A future commit will deprecate the undocumented getdirentries
> and getdirentries64 functions.

Why are getdirentries and getdirentries64 considered deprecated?
Is it because getdents and getdents64 superseded them?
  
> 2019-05-15  Florian Weimer  <fweimer@redhat.com>
> 
> 	Linux: Add getdents64 system call.
> 	* include/dirnent.h (getdents): Add comment and change buffer
> 	argument type to void *.
> 	(getdents64): Likewise.  Add hidden prototype.
> 	* sysdeps/unix/sysv/linux/bits/Versions (GLIBC_2.30): Export
> 	getdents64.
> 	* sysdeps/unix/sysv/linux/Makefile [$(subdir) == dirent] (tests):
> 	Add tst-getdents64.
> 	* sysdeps/unix/sysv/linux/bits/unistd_ext.h (getdents64): Declare.
> 	* sysdeps/unix/sysv/linux/getdents.c (__getdents): Change buffer
> 	argument type to void *.
> 	* sysdeps/unix/sysv/linux/getdents64.c (__getdents64): Likewise.
> 	Add hidden definition and getdents64 alias.
> 	* sysdeps/unix/sysv/linux/mips/mips64/getdents64.c (__getdents64):
> 	Likewise.
> 	* sysdeps/unix/sysv/linux/tst-getdents64.c: New file.
> 	* manual/filesys.texi (Accessing Directories): Add Low-level
> 	Directory Access node reference.
> 	(Opening a Directory): Cross-reference it.
> 	(Low-level Directory Access): New node.
> 	* sysdeps/unix/sysv/linux/aarch64/libc.abilist (GLIBC_2.30): Add
> 	getdents64.
> 	* sysdeps/unix/sysv/linux/alpha/libc.abilist (GLIBC_2.30):
> 	Likewise.
> 	* sysdeps/unix/sysv/linux/arm/libc.abilist (GLIBC_2.30): Likewise.
> 	* sysdeps/unix/sysv/linux/csky/libc.abilist (GLIBC_2.30):
> 	Likewise.
> 	* sysdeps/unix/sysv/linux/hppa/libc.abilist (GLIBC_2.30):
> 	Likewise.
> 	* sysdeps/unix/sysv/linux/i386/libc.abilist (GLIBC_2.30):
> 	Likewise.
> 	* sysdeps/unix/sysv/linux/ia64/libc.abilist (GLIBC_2.30):
> 	Likewise.
> 	* sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist (GLIBC_2.30):
> 	Likewise.
> 	* sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist (GLIBC_2.30):
> 	Likewise.
> 	* sysdeps/unix/sysv/linux/microblaze/libc.abilist (GLIBC_2.30):
> 	Likewise.
> 	* sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist
> 	(GLIBC_2.30): Likewise.
> 	* sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist
> 	(GLIBC_2.30): Likewise.
> 	* sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist
> 	(GLIBC_2.30): Likewise.
> 	* sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist
> 	(GLIBC_2.30): Likewise.
> 	* sysdeps/unix/sysv/linux/nios2/libc.abilist (GLIBC_2.30):
> 	Likewise.
> 	* sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist
> 	(GLIBC_2.30): Likewise.
> 	* sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist
> 	(GLIBC_2.30): Likewise.
> 	* sysdeps/unix/sysv/linux/powerpc/powerpc64/be/libc.abilist
> 	(GLIBC_2.30): Likewise.
> 	* sysdeps/unix/sysv/linux/powerpc/powerpc64/le/libc.abilist
> 	(GLIBC_2.30): Likewise.
> 	* sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist (GLIBC_2.30):
> 	Likewise.
> 	* sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist (GLIBC_2.30):
> 	Likewise.
> 	* sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist (GLIBC_2.30):
> 	Likewise.
> 	* sysdeps/unix/sysv/linux/sh/libc.abilist (GLIBC_2.30): Likewise.
> 	* sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist (GLIBC_2.30):
> 	Likewise.
> 	* sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist (GLIBC_2.30):
> 	Likewise.
> 	* sysdeps/unix/sysv/linux/x86_64/64/libc.abilist (GLIBC_2.30):
> 	Likewise.
> 	* sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist (GLIBC_2.30):
> 	Likewise.
> 
> diff --git a/NEWS b/NEWS
> index 0e4c57f273..5eb9d05c22 100644
> --- a/NEWS
> +++ b/NEWS
> @@ -20,7 +20,7 @@ Major new features:
>     twalk function, but it passes an additional caller-supplied argument
>     to the callback function.
>   
> -* On Linux, the gettid and tgkill functions have been added.
> +* On Linux, the getdents64, gettid, and tgkill functions have been added.

OK.

>   
>   * 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/dirent.h b/include/dirent.h
> index 400835eefe..3736803bce 100644
> --- a/include/dirent.h
> +++ b/include/dirent.h
> @@ -35,10 +35,14 @@ extern __ssize_t __getdirentries (int __fd, char *__restrict __buf,
>   				size_t __nbytes,
>   				__off_t *__restrict __basep)
>        __THROW __nonnull ((2, 4));
> -extern __ssize_t __getdents (int __fd, char *__buf, size_t __nbytes)
> -     attribute_hidden;
> -extern __ssize_t __getdents64 (int __fd, char *__buf, size_t __nbytes)
> +
> +/* These functions are only implemented on Linux.  The public
> +   interface for getdents64 is declared in <unistd.h>.  */
> +extern __ssize_t __getdents (int __fd, void *__buf, size_t __nbytes)
>        attribute_hidden;
> +extern __ssize_t __getdents64 (int __fd, void *__buf, size_t __nbytes);
> +libc_hidden_proto (__getdents64)
> +

We use void*, but the linux man pages use 'struct  linux_dirent64', what should we use?

>   extern int __alphasort64 (const struct dirent64 **a, const struct dirent64 **b)
>        __attribute_pure__;
>   extern int __versionsort64 (const struct dirent64 **a,
> diff --git a/manual/filesys.texi b/manual/filesys.texi
> index 28480e7608..aa4a44dc96 100644
> --- a/manual/filesys.texi
> +++ b/manual/filesys.texi
> @@ -242,6 +242,7 @@ here to the stream facilities for ordinary files, described in
>   * Scanning Directory Content::  Get entries for user selected subset of
>                                    contents in given directory.
>   * Simple Directory Lister Mark II::  Revised version of the program.
> +* Low-level Directory Access::  Async-signal-functions for directory access.

Please use:

"AS-Safe functions for directory access." ("AS-safe" is defined in the safety notes)

or

"Asynchronous-signal-safe functions for directory access."


>   @end menu
>   
>   @node Directory Entries
> @@ -360,6 +361,10 @@ You shouldn't ever allocate objects of the @code{struct dirent} or
>   you.  Instead, you refer to these objects using the pointers returned by
>   the following functions.
>   
> +Directory streams are a high-level interface.  On Linux, alternative
> +interfaces for accessing directories using file descriptors are
> +available.  @xref{Low-level Directory Access}.

Should this be:

"available in @xref{Low-level Directory Access}."

Rather than having the @xref as a distinct sentence?

> +
>   @deftypefun {DIR *} opendir (const char *@var{dirname})
>   @standards{POSIX.1, dirent.h}
>   @safety{@prelim{}@mtsafe{}@asunsafe{@ascuheap{}}@acunsafe{@acsmem{} @acsfd{}}}
> @@ -826,6 +831,39 @@ After the call the returned entries are available for direct use.
>   Note the simple selector function in this example.  Since we want to see
>   all directory entries we always return @code{1}.
>   
> +@node Low-level Directory Access
> +@subsection Low-level Directory Access
> +
> +The stream-based directory functions are not async-signal-safe and
> +cannot be used after @code{vfork}.  The functions below provide an
> +alternative that can be used in these contexts.
> +
> +Directory data is obtained from a file descriptor, as created by the
> +@code{open} function, with or without the @code{O_DIRECTORY} flag.
> +@xref{Opening and Closing Files}.

Should this reference be parenthetical?

e.g.  ... flag (@xref{Opening and Closing Files}).

> +
> +@deftypefun ssize_t getdents64 (int @var{fd}, void *@var{buffer}, size_t @var{length})
> +@standards{Linux, unistd.h}
> +@safety{@prelim{}@mtsafe{}@assafe{}@acsafe{}}
> +The @code{getdents64} function reads at most @var{length} bytes of
> +directory entry data from the file descriptor @var{fd} and stores it
> +into the byte array starting at @var{buffer}.
> +
> +On success, the function returns the number of bytes written to the
> +buffer.  This number is zero if @var{fd} is already at the end of the
> +directory stream.  On error, the function returns @code{-1} and sets
> +@code{errno} to the appropriate error code.
> +
> +The data is stored as a sequence of @code{struct dirent64} records,

OK. Confirmed layout and checked it against linux man page docs.

> +which can be traversed using the @code{d_reclen} member.  The buffer
> +should be large enough to hold the largest possible directory entry.
> +Note that some file systems support file names longer than
> +@code{NAME_MAX} bytes (e.g., because they support up to 255 Unicode
> +characters), so a buffer size of at least 1024 is recommended.
> +

OK.

> +This function is specific to Linux.

OK.

> +@end deftypefun
> +
>   
>   @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 d2f0b60ea9..afcdc658b5 100644
> --- a/sysdeps/unix/sysv/linux/Makefile
> +++ b/sysdeps/unix/sysv/linux/Makefile
> @@ -188,6 +188,7 @@ inhibit-glue = yes
>   
>   ifeq ($(subdir),dirent)
>   sysdep_routines += getdirentries getdirentries64
> +tests += tst-getdents64

OK.

>   tests-internal += tst-readdir64-compat
>   endif
>   
> diff --git a/sysdeps/unix/sysv/linux/Versions b/sysdeps/unix/sysv/linux/Versions
> index c7137e2c2c..1ca102a9e2 100644
> --- a/sysdeps/unix/sysv/linux/Versions
> +++ b/sysdeps/unix/sysv/linux/Versions
> @@ -175,7 +175,7 @@ libc {
>       getcpu;
>     }
>     GLIBC_2.30 {
> -    gettid; tgkill;
> +    getdents64; gettid; tgkill;

OK.

>     }
>     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 324909d553..a4c31932cb 100644
> --- a/sysdeps/unix/sysv/linux/aarch64/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/aarch64/libc.abilist
> @@ -2141,6 +2141,7 @@ 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 getdents64 F
>   GLIBC_2.30 gettid F
>   GLIBC_2.30 tgkill F
>   GLIBC_2.30 twalk_r F
> diff --git a/sysdeps/unix/sysv/linux/alpha/libc.abilist b/sysdeps/unix/sysv/linux/alpha/libc.abilist
> index 084e475569..fe85a35620 100644
> --- a/sysdeps/unix/sysv/linux/alpha/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/alpha/libc.abilist
> @@ -2216,6 +2216,7 @@ 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 getdents64 F
>   GLIBC_2.30 gettid F
>   GLIBC_2.30 tgkill F
>   GLIBC_2.30 twalk_r F
> diff --git a/sysdeps/unix/sysv/linux/arm/libc.abilist b/sysdeps/unix/sysv/linux/arm/libc.abilist
> index f58d16e210..bc3df8dcea 100644
> --- a/sysdeps/unix/sysv/linux/arm/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/arm/libc.abilist
> @@ -126,6 +126,7 @@ 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 getdents64 F
>   GLIBC_2.30 gettid F
>   GLIBC_2.30 tgkill F
>   GLIBC_2.30 twalk_r F
> diff --git a/sysdeps/unix/sysv/linux/bits/unistd_ext.h b/sysdeps/unix/sysv/linux/bits/unistd_ext.h
> index 0061172f23..6e7b2bb83d 100644
> --- a/sysdeps/unix/sysv/linux/bits/unistd_ext.h
> +++ b/sysdeps/unix/sysv/linux/bits/unistd_ext.h
> @@ -22,6 +22,12 @@
>   
>   #ifdef __USE_GNU
>   
> +/* Read from the directory descriptor FD into LENGTH bytes at BUFFER.
> +   Return the number of bytes read on success (0 for end of
> +   directory), and -1 for failure.  */
> +extern ssize_t getdents64 (int __fd, void *__buffer, size_t __length)
> +  __THROW __nonnull ((2));

OK.

> +
>   /* Return the kernel thread ID (TID) of the current thread.  The
>      returned value is not subject to caching.  Most Linux system calls
>      accept a TID in place of a PID.  Using the TID to change properties
> diff --git a/sysdeps/unix/sysv/linux/csky/libc.abilist b/sysdeps/unix/sysv/linux/csky/libc.abilist
> index 38f6abaa30..9b3cee65bb 100644
> --- a/sysdeps/unix/sysv/linux/csky/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/csky/libc.abilist
> @@ -2085,6 +2085,7 @@ 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 getdents64 F
>   GLIBC_2.30 gettid F
>   GLIBC_2.30 tgkill F
>   GLIBC_2.30 twalk_r F
> diff --git a/sysdeps/unix/sysv/linux/getdents.c b/sysdeps/unix/sysv/linux/getdents.c
> index e796d9354b..3eec4c4221 100644
> --- a/sysdeps/unix/sysv/linux/getdents.c
> +++ b/sysdeps/unix/sysv/linux/getdents.c
> @@ -31,8 +31,10 @@
>   /* Pack the dirent64 struct down into 32-bit offset/inode fields, and
>      ensure that no overflow occurs.  */
>   ssize_t
> -__getdents (int fd, char *buf, size_t nbytes)
> +__getdents (int fd, void *buf0, size_t nbytes)
>   {
> +  char *buf = buf0;
> +

OK. Allowed.

>     union
>     {
>       /* For !_DIRENT_MATCHES_DIRENT64 kernel 'linux_dirent64' has the same
> diff --git a/sysdeps/unix/sysv/linux/getdents64.c b/sysdeps/unix/sysv/linux/getdents64.c
> index 0786a15195..a6dd22106d 100644
> --- a/sysdeps/unix/sysv/linux/getdents64.c
> +++ b/sysdeps/unix/sysv/linux/getdents64.c
> @@ -20,12 +20,14 @@
>   #include <dirent.h>
>   #include <errno.h>
>   
> -/* The kernel struct linux_dirent64 matches the 'struct getdents64' type.  */
> +/* The kernel struct linux_dirent64 matches the 'struct dirent64' type.  */

OK.

>   ssize_t
> -__getdents64 (int fd, char *buf, size_t nbytes)
> +__getdents64 (int fd, void *buf, size_t nbytes)

OK.

>   {
>     return INLINE_SYSCALL_CALL (getdents64, fd, buf, nbytes);
>   }
> +libc_hidden_def (__getdents64)
> +weak_alias (__getdents64, getdents64)


OK.

>   
>   #if _DIRENT_MATCHES_DIRENT64
>   strong_alias (__getdents64, __getdents)
> diff --git a/sysdeps/unix/sysv/linux/hppa/libc.abilist b/sysdeps/unix/sysv/linux/hppa/libc.abilist
> index eb628f907b..75edece94a 100644
> --- a/sysdeps/unix/sysv/linux/hppa/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/hppa/libc.abilist
> @@ -2037,6 +2037,7 @@ 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 getdents64 F
>   GLIBC_2.30 gettid F
>   GLIBC_2.30 tgkill F
>   GLIBC_2.30 twalk_r F
> diff --git a/sysdeps/unix/sysv/linux/i386/libc.abilist b/sysdeps/unix/sysv/linux/i386/libc.abilist
> index 6f240d9a59..edeaf8e722 100644
> --- a/sysdeps/unix/sysv/linux/i386/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/i386/libc.abilist
> @@ -2203,6 +2203,7 @@ 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 getdents64 F
>   GLIBC_2.30 gettid F
>   GLIBC_2.30 tgkill F
>   GLIBC_2.30 twalk_r F
> diff --git a/sysdeps/unix/sysv/linux/ia64/libc.abilist b/sysdeps/unix/sysv/linux/ia64/libc.abilist
> index e589e3d7e2..b5d460eeb2 100644
> --- a/sysdeps/unix/sysv/linux/ia64/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/ia64/libc.abilist
> @@ -2069,6 +2069,7 @@ 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 getdents64 F
>   GLIBC_2.30 gettid F
>   GLIBC_2.30 tgkill F
>   GLIBC_2.30 twalk_r F
> diff --git a/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist b/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist
> index 02c3ceee8d..05633b3cb8 100644
> --- a/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist
> @@ -127,6 +127,7 @@ 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 getdents64 F
>   GLIBC_2.30 gettid F
>   GLIBC_2.30 tgkill F
>   GLIBC_2.30 twalk_r F
> diff --git a/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist b/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist
> index f9a86bd951..47eb7b4608 100644
> --- a/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist
> @@ -2146,6 +2146,7 @@ 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 getdents64 F
>   GLIBC_2.30 gettid F
>   GLIBC_2.30 tgkill F
>   GLIBC_2.30 twalk_r F
> diff --git a/sysdeps/unix/sysv/linux/microblaze/libc.abilist b/sysdeps/unix/sysv/linux/microblaze/libc.abilist
> index d153fe0df5..f7ced487f7 100644
> --- a/sysdeps/unix/sysv/linux/microblaze/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/microblaze/libc.abilist
> @@ -2133,6 +2133,7 @@ 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 getdents64 F
>   GLIBC_2.30 gettid F
>   GLIBC_2.30 tgkill F
>   GLIBC_2.30 twalk_r F
> diff --git a/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist
> index fb3a9b18fc..e49dc4272e 100644
> --- a/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist
> @@ -2120,6 +2120,7 @@ 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 getdents64 F
>   GLIBC_2.30 gettid F
>   GLIBC_2.30 tgkill F
>   GLIBC_2.30 twalk_r F
> diff --git a/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist
> index 22c80418f6..daa3b60c5b 100644
> --- a/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist
> @@ -2118,6 +2118,7 @@ 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 getdents64 F
>   GLIBC_2.30 gettid F
>   GLIBC_2.30 tgkill F
>   GLIBC_2.30 twalk_r F
> diff --git a/sysdeps/unix/sysv/linux/mips/mips64/getdents64.c b/sysdeps/unix/sysv/linux/mips/mips64/getdents64.c
> index 8771c3c904..1e22fa4325 100644
> --- a/sysdeps/unix/sysv/linux/mips/mips64/getdents64.c
> +++ b/sysdeps/unix/sysv/linux/mips/mips64/getdents64.c
> @@ -25,8 +25,10 @@
>   #include <scratch_buffer.h>
>   
>   ssize_t
> -__getdents64 (int fd, char *buf, size_t nbytes)
> +__getdents64 (int fd, void *buf0, size_t nbytes)
>   {
> +  char *buf = buf0;
> +
>   #ifdef __NR_getdents64
>     ssize_t ret = INLINE_SYSCALL_CALL (getdents64, fd, buf, nbytes);
>     if (ret != -1)
> @@ -107,6 +109,9 @@ __getdents64 (int fd, char *buf, size_t nbytes)
>     scratch_buffer_free (&tmpbuf);
>     return (char *) dp - buf;
>   }
> +libc_hidden_def (__getdents64)
> +weak_alias (__getdents64, getdents64)
> +

OK.

>   #if _DIRENT_MATCHES_DIRENT64
>   strong_alias (__getdents64, __getdents)
>   #endif
> diff --git a/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist
> index a32aafb552..457ce0b6f2 100644
> --- a/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist
> @@ -2126,6 +2126,7 @@ 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 getdents64 F
>   GLIBC_2.30 gettid F
>   GLIBC_2.30 tgkill F
>   GLIBC_2.30 twalk_r F
> diff --git a/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist
> index 45342a587e..63d5c03bfb 100644
> --- a/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist
> @@ -2120,6 +2120,7 @@ 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 getdents64 F
>   GLIBC_2.30 gettid F
>   GLIBC_2.30 tgkill F
>   GLIBC_2.30 twalk_r F
> diff --git a/sysdeps/unix/sysv/linux/nios2/libc.abilist b/sysdeps/unix/sysv/linux/nios2/libc.abilist
> index fe1e96c170..7fec0c9670 100644
> --- a/sysdeps/unix/sysv/linux/nios2/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/nios2/libc.abilist
> @@ -2174,6 +2174,7 @@ 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 getdents64 F
>   GLIBC_2.30 gettid F
>   GLIBC_2.30 tgkill F
>   GLIBC_2.30 twalk_r F
> diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist
> index cbcad5db98..9200a54309 100644
> --- a/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist
> @@ -2176,6 +2176,7 @@ 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 getdents64 F
>   GLIBC_2.30 gettid F
>   GLIBC_2.30 tgkill F
>   GLIBC_2.30 twalk_r F
> diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist
> index 060994eccf..ef7779905f 100644
> --- a/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist
> @@ -2209,6 +2209,7 @@ 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 getdents64 F
>   GLIBC_2.30 gettid F
>   GLIBC_2.30 tgkill F
>   GLIBC_2.30 twalk_r F
> diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc64/be/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc64/be/libc.abilist
> index af68ed8d01..2860df8ebc 100644
> --- a/sysdeps/unix/sysv/linux/powerpc/powerpc64/be/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/powerpc/powerpc64/be/libc.abilist
> @@ -2039,6 +2039,7 @@ 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 getdents64 F
>   GLIBC_2.30 gettid F
>   GLIBC_2.30 tgkill F
>   GLIBC_2.30 twalk_r F
> diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc64/le/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc64/le/libc.abilist
> index d317dc0586..2229a1dcc0 100644
> --- a/sysdeps/unix/sysv/linux/powerpc/powerpc64/le/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/powerpc/powerpc64/le/libc.abilist
> @@ -2243,6 +2243,7 @@ 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 getdents64 F
>   GLIBC_2.30 gettid F
>   GLIBC_2.30 tgkill F
>   GLIBC_2.30 twalk_r F
> diff --git a/sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist b/sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist
> index 07d75c0481..31010e6cf7 100644
> --- a/sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist
> @@ -2103,6 +2103,7 @@ 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 getdents64 F
>   GLIBC_2.30 gettid F
>   GLIBC_2.30 tgkill F
>   GLIBC_2.30 twalk_r F
> diff --git a/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist b/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist
> index 0fdcd6109c..576295deff 100644
> --- a/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist
> @@ -2171,6 +2171,7 @@ 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 getdents64 F
>   GLIBC_2.30 gettid F
>   GLIBC_2.30 tgkill F
>   GLIBC_2.30 twalk_r F
> diff --git a/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist b/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist
> index ca871ad4be..abf0473683 100644
> --- a/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist
> @@ -2075,6 +2075,7 @@ 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 getdents64 F
>   GLIBC_2.30 gettid F
>   GLIBC_2.30 tgkill F
>   GLIBC_2.30 twalk_r F
> diff --git a/sysdeps/unix/sysv/linux/sh/libc.abilist b/sysdeps/unix/sysv/linux/sh/libc.abilist
> index b559e5b316..41977f6e9c 100644
> --- a/sysdeps/unix/sysv/linux/sh/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/sh/libc.abilist
> @@ -2041,6 +2041,7 @@ 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 getdents64 F
>   GLIBC_2.30 gettid F
>   GLIBC_2.30 tgkill F
>   GLIBC_2.30 twalk_r F
> diff --git a/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist b/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist
> index 37daaa28e4..3d2f00ca52 100644
> --- a/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist
> @@ -2165,6 +2165,7 @@ 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 getdents64 F
>   GLIBC_2.30 gettid F
>   GLIBC_2.30 tgkill F
>   GLIBC_2.30 twalk_r F
> diff --git a/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist b/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist
> index 5f0a3adc3c..2f20643e8e 100644
> --- a/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist
> @@ -2092,6 +2092,7 @@ 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 getdents64 F
>   GLIBC_2.30 gettid F
>   GLIBC_2.30 tgkill F
>   GLIBC_2.30 twalk_r F
> diff --git a/sysdeps/unix/sysv/linux/tst-getdents64.c b/sysdeps/unix/sysv/linux/tst-getdents64.c
> new file mode 100644
> index 0000000000..c1f7721221
> --- /dev/null
> +++ b/sysdeps/unix/sysv/linux/tst-getdents64.c
> @@ -0,0 +1,113 @@
> +/* Test for reading directories with getdents64.

OK.

> +   Copyright (C) 2019 Free Software Foundation, Inc.

OK.

> +   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 <string.h>
> +#include <support/check.h>
> +#include <support/support.h>
> +#include <support/xunistd.h>
> +#include <unistd.h>

OK.

> +
> +static int
> +do_test (void)
> +{
> +  /* The test compares the iteration order with readdir64.  */
> +  DIR *reference = opendir (".");
> +  TEST_VERIFY_EXIT (reference != NULL);
> +
> +  int fd = xopen (".", O_RDONLY | O_DIRECTORY, 0);

OK.

> +  TEST_VERIFY (fd >= 0);
> +
> +  /* Perform two passes, with a rewind operating between passes.  */
> +  for (int pass = 0; pass < 2; ++pass)
> +    {
> +      /* Check that we need to fill the buffer multiple times.  */
> +      int read_count = 0;
> +
> +      while (true)
> +        {
> +          /* Simple way to make sure that the memcpy below does not read
> +             non-existing data.  */
> +          struct
> +          {
> +            char buffer[1024];
> +            struct dirent64 pad;
> +          } data;
> +
> +          ssize_t ret = getdents64 (fd, &data.buffer, sizeof (data.buffer));
> +          if (ret < 0)
> +            FAIL_EXIT1 ("getdents64: %m");
> +          if (ret == 0)
> +            break;
> +          ++read_count;
> +
> +          char *current = data.buffer;
> +          char *end = data.buffer + ret;
> +          while (current != end)
> +            {
> +              struct dirent64 entry;
> +              memcpy (&entry, current, sizeof (entry));
> +              /* Truncate overlong strings.  */
> +              entry.d_name[sizeof (entry.d_name) - 1] = '\0';
> +              TEST_VERIFY (strlen (entry.d_name) < sizeof (entry.d_name) - 1);
> +
> +              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_off, refentry->d_off);
> +              TEST_COMPARE (entry.d_type, refentry->d_type);

OK.

> +
> +              /* Offset zero is reserved for the first entry.  */
> +              TEST_VERIFY (entry.d_off != 0);
> +
> +              TEST_VERIFY_EXIT (entry.d_reclen <= end - current);
> +              current += entry.d_reclen;
> +            }
> +        }
> +
> +      /* We expect to have reached the end of the stream.  */
> +      errno = 0;
> +      TEST_VERIFY (readdir64 (reference) == NULL);
> +      TEST_COMPARE (errno, 0);
> +
> +      /* direntries_read has been called more than once.  */
> +      TEST_VERIFY (read_count > 0);
> +
> +      /* Rewind both directory streams.  */
> +      xlseek (fd, 0, SEEK_SET);
> +      rewinddir (reference);

OK.

> +    }
> +
> +  xclose (fd);
> +  closedir (reference);
> +  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 c82b3ba7b8..59f85d9373 100644
> --- a/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist
> @@ -2050,6 +2050,7 @@ 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 getdents64 F
>   GLIBC_2.30 gettid F
>   GLIBC_2.30 tgkill F
>   GLIBC_2.30 twalk_r F
> diff --git a/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist b/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist
> index ed89c34c55..67a4e238d6 100644
> --- a/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist
> @@ -2149,6 +2149,7 @@ 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 getdents64 F
>   GLIBC_2.30 gettid F
>   GLIBC_2.30 tgkill F
>   GLIBC_2.30 twalk_r F
>

Patch

diff --git a/NEWS b/NEWS
index 0e4c57f273..5eb9d05c22 100644
--- a/NEWS
+++ b/NEWS
@@ -20,7 +20,7 @@  Major new features:
   twalk function, but it passes an additional caller-supplied argument
   to the callback function.
 
-* On Linux, the gettid and tgkill functions have been added.
+* On Linux, the getdents64, 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/dirent.h b/include/dirent.h
index 400835eefe..3736803bce 100644
--- a/include/dirent.h
+++ b/include/dirent.h
@@ -35,10 +35,14 @@  extern __ssize_t __getdirentries (int __fd, char *__restrict __buf,
 				size_t __nbytes,
 				__off_t *__restrict __basep)
      __THROW __nonnull ((2, 4));
-extern __ssize_t __getdents (int __fd, char *__buf, size_t __nbytes)
-     attribute_hidden;
-extern __ssize_t __getdents64 (int __fd, char *__buf, size_t __nbytes)
+
+/* These functions are only implemented on Linux.  The public
+   interface for getdents64 is declared in <unistd.h>.  */
+extern __ssize_t __getdents (int __fd, void *__buf, size_t __nbytes)
      attribute_hidden;
+extern __ssize_t __getdents64 (int __fd, void *__buf, size_t __nbytes);
+libc_hidden_proto (__getdents64)
+
 extern int __alphasort64 (const struct dirent64 **a, const struct dirent64 **b)
      __attribute_pure__;
 extern int __versionsort64 (const struct dirent64 **a,
diff --git a/manual/filesys.texi b/manual/filesys.texi
index 28480e7608..aa4a44dc96 100644
--- a/manual/filesys.texi
+++ b/manual/filesys.texi
@@ -242,6 +242,7 @@  here to the stream facilities for ordinary files, described in
 * Scanning Directory Content::  Get entries for user selected subset of
                                  contents in given directory.
 * Simple Directory Lister Mark II::  Revised version of the program.
+* Low-level Directory Access::  Async-signal-functions for directory access.
 @end menu
 
 @node Directory Entries
@@ -360,6 +361,10 @@  You shouldn't ever allocate objects of the @code{struct dirent} or
 you.  Instead, you refer to these objects using the pointers returned by
 the following functions.
 
+Directory streams are a high-level interface.  On Linux, alternative
+interfaces for accessing directories using file descriptors are
+available.  @xref{Low-level Directory Access}.
+
 @deftypefun {DIR *} opendir (const char *@var{dirname})
 @standards{POSIX.1, dirent.h}
 @safety{@prelim{}@mtsafe{}@asunsafe{@ascuheap{}}@acunsafe{@acsmem{} @acsfd{}}}
@@ -826,6 +831,39 @@  After the call the returned entries are available for direct use.
 Note the simple selector function in this example.  Since we want to see
 all directory entries we always return @code{1}.
 
+@node Low-level Directory Access
+@subsection Low-level Directory Access
+
+The stream-based directory functions are not async-signal-safe and
+cannot be used after @code{vfork}.  The functions below provide an
+alternative that can be used in these contexts.
+
+Directory data is obtained from a file descriptor, as created by the
+@code{open} function, with or without the @code{O_DIRECTORY} flag.
+@xref{Opening and Closing Files}.
+
+@deftypefun ssize_t getdents64 (int @var{fd}, void *@var{buffer}, size_t @var{length})
+@standards{Linux, unistd.h}
+@safety{@prelim{}@mtsafe{}@assafe{}@acsafe{}}
+The @code{getdents64} function reads at most @var{length} bytes of
+directory entry data from the file descriptor @var{fd} and stores it
+into the byte array starting at @var{buffer}.
+
+On success, the function returns the number of bytes written to the
+buffer.  This number is zero if @var{fd} is already at the end of the
+directory stream.  On error, the function returns @code{-1} and sets
+@code{errno} to the appropriate error code.
+
+The data is stored as a sequence of @code{struct dirent64} records,
+which can be traversed using the @code{d_reclen} member.  The buffer
+should be large enough to hold the largest possible directory entry.
+Note that some file systems support file names longer than
+@code{NAME_MAX} bytes (e.g., because they support up to 255 Unicode
+characters), so a buffer size of at least 1024 is recommended.
+
+This function is specific to Linux.
+@end deftypefun
+
 
 @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 d2f0b60ea9..afcdc658b5 100644
--- a/sysdeps/unix/sysv/linux/Makefile
+++ b/sysdeps/unix/sysv/linux/Makefile
@@ -188,6 +188,7 @@  inhibit-glue = yes
 
 ifeq ($(subdir),dirent)
 sysdep_routines += getdirentries getdirentries64
+tests += tst-getdents64
 tests-internal += tst-readdir64-compat
 endif
 
diff --git a/sysdeps/unix/sysv/linux/Versions b/sysdeps/unix/sysv/linux/Versions
index c7137e2c2c..1ca102a9e2 100644
--- a/sysdeps/unix/sysv/linux/Versions
+++ b/sysdeps/unix/sysv/linux/Versions
@@ -175,7 +175,7 @@  libc {
     getcpu;
   }
   GLIBC_2.30 {
-    gettid; tgkill;
+    getdents64; gettid; tgkill;
   }
   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 324909d553..a4c31932cb 100644
--- a/sysdeps/unix/sysv/linux/aarch64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/aarch64/libc.abilist
@@ -2141,6 +2141,7 @@  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 getdents64 F
 GLIBC_2.30 gettid F
 GLIBC_2.30 tgkill F
 GLIBC_2.30 twalk_r F
diff --git a/sysdeps/unix/sysv/linux/alpha/libc.abilist b/sysdeps/unix/sysv/linux/alpha/libc.abilist
index 084e475569..fe85a35620 100644
--- a/sysdeps/unix/sysv/linux/alpha/libc.abilist
+++ b/sysdeps/unix/sysv/linux/alpha/libc.abilist
@@ -2216,6 +2216,7 @@  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 getdents64 F
 GLIBC_2.30 gettid F
 GLIBC_2.30 tgkill F
 GLIBC_2.30 twalk_r F
diff --git a/sysdeps/unix/sysv/linux/arm/libc.abilist b/sysdeps/unix/sysv/linux/arm/libc.abilist
index f58d16e210..bc3df8dcea 100644
--- a/sysdeps/unix/sysv/linux/arm/libc.abilist
+++ b/sysdeps/unix/sysv/linux/arm/libc.abilist
@@ -126,6 +126,7 @@  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 getdents64 F
 GLIBC_2.30 gettid F
 GLIBC_2.30 tgkill F
 GLIBC_2.30 twalk_r F
diff --git a/sysdeps/unix/sysv/linux/bits/unistd_ext.h b/sysdeps/unix/sysv/linux/bits/unistd_ext.h
index 0061172f23..6e7b2bb83d 100644
--- a/sysdeps/unix/sysv/linux/bits/unistd_ext.h
+++ b/sysdeps/unix/sysv/linux/bits/unistd_ext.h
@@ -22,6 +22,12 @@ 
 
 #ifdef __USE_GNU
 
+/* Read from the directory descriptor FD into LENGTH bytes at BUFFER.
+   Return the number of bytes read on success (0 for end of
+   directory), and -1 for failure.  */
+extern ssize_t getdents64 (int __fd, void *__buffer, size_t __length)
+  __THROW __nonnull ((2));
+
 /* Return the kernel thread ID (TID) of the current thread.  The
    returned value is not subject to caching.  Most Linux system calls
    accept a TID in place of a PID.  Using the TID to change properties
diff --git a/sysdeps/unix/sysv/linux/csky/libc.abilist b/sysdeps/unix/sysv/linux/csky/libc.abilist
index 38f6abaa30..9b3cee65bb 100644
--- a/sysdeps/unix/sysv/linux/csky/libc.abilist
+++ b/sysdeps/unix/sysv/linux/csky/libc.abilist
@@ -2085,6 +2085,7 @@  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 getdents64 F
 GLIBC_2.30 gettid F
 GLIBC_2.30 tgkill F
 GLIBC_2.30 twalk_r F
diff --git a/sysdeps/unix/sysv/linux/getdents.c b/sysdeps/unix/sysv/linux/getdents.c
index e796d9354b..3eec4c4221 100644
--- a/sysdeps/unix/sysv/linux/getdents.c
+++ b/sysdeps/unix/sysv/linux/getdents.c
@@ -31,8 +31,10 @@ 
 /* Pack the dirent64 struct down into 32-bit offset/inode fields, and
    ensure that no overflow occurs.  */
 ssize_t
-__getdents (int fd, char *buf, size_t nbytes)
+__getdents (int fd, void *buf0, size_t nbytes)
 {
+  char *buf = buf0;
+
   union
   {
     /* For !_DIRENT_MATCHES_DIRENT64 kernel 'linux_dirent64' has the same
diff --git a/sysdeps/unix/sysv/linux/getdents64.c b/sysdeps/unix/sysv/linux/getdents64.c
index 0786a15195..a6dd22106d 100644
--- a/sysdeps/unix/sysv/linux/getdents64.c
+++ b/sysdeps/unix/sysv/linux/getdents64.c
@@ -20,12 +20,14 @@ 
 #include <dirent.h>
 #include <errno.h>
 
-/* The kernel struct linux_dirent64 matches the 'struct getdents64' type.  */
+/* The kernel struct linux_dirent64 matches the 'struct dirent64' type.  */
 ssize_t
-__getdents64 (int fd, char *buf, size_t nbytes)
+__getdents64 (int fd, void *buf, size_t nbytes)
 {
   return INLINE_SYSCALL_CALL (getdents64, fd, buf, nbytes);
 }
+libc_hidden_def (__getdents64)
+weak_alias (__getdents64, getdents64)
 
 #if _DIRENT_MATCHES_DIRENT64
 strong_alias (__getdents64, __getdents)
diff --git a/sysdeps/unix/sysv/linux/hppa/libc.abilist b/sysdeps/unix/sysv/linux/hppa/libc.abilist
index eb628f907b..75edece94a 100644
--- a/sysdeps/unix/sysv/linux/hppa/libc.abilist
+++ b/sysdeps/unix/sysv/linux/hppa/libc.abilist
@@ -2037,6 +2037,7 @@  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 getdents64 F
 GLIBC_2.30 gettid F
 GLIBC_2.30 tgkill F
 GLIBC_2.30 twalk_r F
diff --git a/sysdeps/unix/sysv/linux/i386/libc.abilist b/sysdeps/unix/sysv/linux/i386/libc.abilist
index 6f240d9a59..edeaf8e722 100644
--- a/sysdeps/unix/sysv/linux/i386/libc.abilist
+++ b/sysdeps/unix/sysv/linux/i386/libc.abilist
@@ -2203,6 +2203,7 @@  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 getdents64 F
 GLIBC_2.30 gettid F
 GLIBC_2.30 tgkill F
 GLIBC_2.30 twalk_r F
diff --git a/sysdeps/unix/sysv/linux/ia64/libc.abilist b/sysdeps/unix/sysv/linux/ia64/libc.abilist
index e589e3d7e2..b5d460eeb2 100644
--- a/sysdeps/unix/sysv/linux/ia64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/ia64/libc.abilist
@@ -2069,6 +2069,7 @@  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 getdents64 F
 GLIBC_2.30 gettid F
 GLIBC_2.30 tgkill F
 GLIBC_2.30 twalk_r F
diff --git a/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist b/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist
index 02c3ceee8d..05633b3cb8 100644
--- a/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist
+++ b/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist
@@ -127,6 +127,7 @@  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 getdents64 F
 GLIBC_2.30 gettid F
 GLIBC_2.30 tgkill F
 GLIBC_2.30 twalk_r F
diff --git a/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist b/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist
index f9a86bd951..47eb7b4608 100644
--- a/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist
+++ b/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist
@@ -2146,6 +2146,7 @@  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 getdents64 F
 GLIBC_2.30 gettid F
 GLIBC_2.30 tgkill F
 GLIBC_2.30 twalk_r F
diff --git a/sysdeps/unix/sysv/linux/microblaze/libc.abilist b/sysdeps/unix/sysv/linux/microblaze/libc.abilist
index d153fe0df5..f7ced487f7 100644
--- a/sysdeps/unix/sysv/linux/microblaze/libc.abilist
+++ b/sysdeps/unix/sysv/linux/microblaze/libc.abilist
@@ -2133,6 +2133,7 @@  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 getdents64 F
 GLIBC_2.30 gettid F
 GLIBC_2.30 tgkill F
 GLIBC_2.30 twalk_r F
diff --git a/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist
index fb3a9b18fc..e49dc4272e 100644
--- a/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist
+++ b/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist
@@ -2120,6 +2120,7 @@  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 getdents64 F
 GLIBC_2.30 gettid F
 GLIBC_2.30 tgkill F
 GLIBC_2.30 twalk_r F
diff --git a/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist
index 22c80418f6..daa3b60c5b 100644
--- a/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist
+++ b/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist
@@ -2118,6 +2118,7 @@  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 getdents64 F
 GLIBC_2.30 gettid F
 GLIBC_2.30 tgkill F
 GLIBC_2.30 twalk_r F
diff --git a/sysdeps/unix/sysv/linux/mips/mips64/getdents64.c b/sysdeps/unix/sysv/linux/mips/mips64/getdents64.c
index 8771c3c904..1e22fa4325 100644
--- a/sysdeps/unix/sysv/linux/mips/mips64/getdents64.c
+++ b/sysdeps/unix/sysv/linux/mips/mips64/getdents64.c
@@ -25,8 +25,10 @@ 
 #include <scratch_buffer.h>
 
 ssize_t
-__getdents64 (int fd, char *buf, size_t nbytes)
+__getdents64 (int fd, void *buf0, size_t nbytes)
 {
+  char *buf = buf0;
+
 #ifdef __NR_getdents64
   ssize_t ret = INLINE_SYSCALL_CALL (getdents64, fd, buf, nbytes);
   if (ret != -1)
@@ -107,6 +109,9 @@  __getdents64 (int fd, char *buf, size_t nbytes)
   scratch_buffer_free (&tmpbuf);
   return (char *) dp - buf;
 }
+libc_hidden_def (__getdents64)
+weak_alias (__getdents64, getdents64)
+
 #if _DIRENT_MATCHES_DIRENT64
 strong_alias (__getdents64, __getdents)
 #endif
diff --git a/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist
index a32aafb552..457ce0b6f2 100644
--- a/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist
+++ b/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist
@@ -2126,6 +2126,7 @@  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 getdents64 F
 GLIBC_2.30 gettid F
 GLIBC_2.30 tgkill F
 GLIBC_2.30 twalk_r F
diff --git a/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist
index 45342a587e..63d5c03bfb 100644
--- a/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist
@@ -2120,6 +2120,7 @@  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 getdents64 F
 GLIBC_2.30 gettid F
 GLIBC_2.30 tgkill F
 GLIBC_2.30 twalk_r F
diff --git a/sysdeps/unix/sysv/linux/nios2/libc.abilist b/sysdeps/unix/sysv/linux/nios2/libc.abilist
index fe1e96c170..7fec0c9670 100644
--- a/sysdeps/unix/sysv/linux/nios2/libc.abilist
+++ b/sysdeps/unix/sysv/linux/nios2/libc.abilist
@@ -2174,6 +2174,7 @@  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 getdents64 F
 GLIBC_2.30 gettid F
 GLIBC_2.30 tgkill F
 GLIBC_2.30 twalk_r F
diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist
index cbcad5db98..9200a54309 100644
--- a/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist
+++ b/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist
@@ -2176,6 +2176,7 @@  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 getdents64 F
 GLIBC_2.30 gettid F
 GLIBC_2.30 tgkill F
 GLIBC_2.30 twalk_r F
diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist
index 060994eccf..ef7779905f 100644
--- a/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist
+++ b/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist
@@ -2209,6 +2209,7 @@  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 getdents64 F
 GLIBC_2.30 gettid F
 GLIBC_2.30 tgkill F
 GLIBC_2.30 twalk_r F
diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc64/be/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc64/be/libc.abilist
index af68ed8d01..2860df8ebc 100644
--- a/sysdeps/unix/sysv/linux/powerpc/powerpc64/be/libc.abilist
+++ b/sysdeps/unix/sysv/linux/powerpc/powerpc64/be/libc.abilist
@@ -2039,6 +2039,7 @@  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 getdents64 F
 GLIBC_2.30 gettid F
 GLIBC_2.30 tgkill F
 GLIBC_2.30 twalk_r F
diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc64/le/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc64/le/libc.abilist
index d317dc0586..2229a1dcc0 100644
--- a/sysdeps/unix/sysv/linux/powerpc/powerpc64/le/libc.abilist
+++ b/sysdeps/unix/sysv/linux/powerpc/powerpc64/le/libc.abilist
@@ -2243,6 +2243,7 @@  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 getdents64 F
 GLIBC_2.30 gettid F
 GLIBC_2.30 tgkill F
 GLIBC_2.30 twalk_r F
diff --git a/sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist b/sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist
index 07d75c0481..31010e6cf7 100644
--- a/sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist
@@ -2103,6 +2103,7 @@  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 getdents64 F
 GLIBC_2.30 gettid F
 GLIBC_2.30 tgkill F
 GLIBC_2.30 twalk_r F
diff --git a/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist b/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist
index 0fdcd6109c..576295deff 100644
--- a/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist
+++ b/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist
@@ -2171,6 +2171,7 @@  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 getdents64 F
 GLIBC_2.30 gettid F
 GLIBC_2.30 tgkill F
 GLIBC_2.30 twalk_r F
diff --git a/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist b/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist
index ca871ad4be..abf0473683 100644
--- a/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist
@@ -2075,6 +2075,7 @@  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 getdents64 F
 GLIBC_2.30 gettid F
 GLIBC_2.30 tgkill F
 GLIBC_2.30 twalk_r F
diff --git a/sysdeps/unix/sysv/linux/sh/libc.abilist b/sysdeps/unix/sysv/linux/sh/libc.abilist
index b559e5b316..41977f6e9c 100644
--- a/sysdeps/unix/sysv/linux/sh/libc.abilist
+++ b/sysdeps/unix/sysv/linux/sh/libc.abilist
@@ -2041,6 +2041,7 @@  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 getdents64 F
 GLIBC_2.30 gettid F
 GLIBC_2.30 tgkill F
 GLIBC_2.30 twalk_r F
diff --git a/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist b/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist
index 37daaa28e4..3d2f00ca52 100644
--- a/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist
+++ b/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist
@@ -2165,6 +2165,7 @@  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 getdents64 F
 GLIBC_2.30 gettid F
 GLIBC_2.30 tgkill F
 GLIBC_2.30 twalk_r F
diff --git a/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist b/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist
index 5f0a3adc3c..2f20643e8e 100644
--- a/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist
@@ -2092,6 +2092,7 @@  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 getdents64 F
 GLIBC_2.30 gettid F
 GLIBC_2.30 tgkill F
 GLIBC_2.30 twalk_r F
diff --git a/sysdeps/unix/sysv/linux/tst-getdents64.c b/sysdeps/unix/sysv/linux/tst-getdents64.c
new file mode 100644
index 0000000000..c1f7721221
--- /dev/null
+++ b/sysdeps/unix/sysv/linux/tst-getdents64.c
@@ -0,0 +1,113 @@ 
+/* Test for reading directories with getdents64.
+   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 <string.h>
+#include <support/check.h>
+#include <support/support.h>
+#include <support/xunistd.h>
+#include <unistd.h>
+
+static int
+do_test (void)
+{
+  /* 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);
+
+  /* Perform two passes, with a rewind operating between passes.  */
+  for (int pass = 0; pass < 2; ++pass)
+    {
+      /* Check that we need to fill the buffer multiple times.  */
+      int read_count = 0;
+
+      while (true)
+        {
+          /* Simple way to make sure that the memcpy below does not read
+             non-existing data.  */
+          struct
+          {
+            char buffer[1024];
+            struct dirent64 pad;
+          } data;
+
+          ssize_t ret = getdents64 (fd, &data.buffer, sizeof (data.buffer));
+          if (ret < 0)
+            FAIL_EXIT1 ("getdents64: %m");
+          if (ret == 0)
+            break;
+          ++read_count;
+
+          char *current = data.buffer;
+          char *end = data.buffer + ret;
+          while (current != end)
+            {
+              struct dirent64 entry;
+              memcpy (&entry, current, sizeof (entry));
+              /* Truncate overlong strings.  */
+              entry.d_name[sizeof (entry.d_name) - 1] = '\0';
+              TEST_VERIFY (strlen (entry.d_name) < sizeof (entry.d_name) - 1);
+
+              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_off, refentry->d_off);
+              TEST_COMPARE (entry.d_type, refentry->d_type);
+
+              /* Offset zero is reserved for the first entry.  */
+              TEST_VERIFY (entry.d_off != 0);
+
+              TEST_VERIFY_EXIT (entry.d_reclen <= end - current);
+              current += entry.d_reclen;
+            }
+        }
+
+      /* We expect to have reached the end of the stream.  */
+      errno = 0;
+      TEST_VERIFY (readdir64 (reference) == NULL);
+      TEST_COMPARE (errno, 0);
+
+      /* direntries_read has been called more than once.  */
+      TEST_VERIFY (read_count > 0);
+
+      /* Rewind both directory streams.  */
+      xlseek (fd, 0, SEEK_SET);
+      rewinddir (reference);
+    }
+
+  xclose (fd);
+  closedir (reference);
+  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 c82b3ba7b8..59f85d9373 100644
--- a/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist
@@ -2050,6 +2050,7 @@  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 getdents64 F
 GLIBC_2.30 gettid F
 GLIBC_2.30 tgkill F
 GLIBC_2.30 twalk_r F
diff --git a/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist b/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist
index ed89c34c55..67a4e238d6 100644
--- a/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist
+++ b/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist
@@ -2149,6 +2149,7 @@  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 getdents64 F
 GLIBC_2.30 gettid F
 GLIBC_2.30 tgkill F
 GLIBC_2.30 twalk_r F