[PATCH v8] linux: Add openat2 (BZ 31664)
Adhemerval Zanella
adhemerval.zanella@linaro.org
Wed Nov 19 16:44:34 GMT 2025
The openat2 syscall was added on Linux 5.6, as an extension of openat.
Unlike other open-like functions, the kernel only provides the LFS
variant (so files larger than 4GB always succeed, unlike other
functions with an offset larger than off_t). Also, similar to other
open functions, the new symbol is a cancellable entrypoint.
The test case added only stress tests for some of the syscalls' provided
functionality, and it is based on an existing kernel self-test.
A fortify wrapper is added to verify the argument size if not larger
than the current support open_how struct.
Gnulib added an openat2 module, which uses read-only for the open_how
argument [1]. There is no clear indication whether the kernel will
indeed use the argument as in-out, how it would do so, or for which
kind of functionality [2]. Also, adding a potentially different prototype
than gnulib only would add extra unnecessary friction and extra
wrappers to handle it.
Checked on x86_64-linux-gnu and aarch64-linux-gnu.
[1] https://gitweb.git.savannah.gnu.org/gitweb/?p=gnulib.git;a=commit;h=0b97ffdf32bdab909d02449043447237273df75e
[2] https://sourceware.org/pipermail/libc-alpha/2025-September/169740.html
Reviewed-by: Carlos O'Donell <carlos@redhat.com>
--
Changes from v7:
* Fixed manual remarks from Carlos.
* Fixed typos.
---
NEWS | 4 +
manual/llio.texi | 98 ++++
sysdeps/unix/sysv/linux/Makefile | 20 +
sysdeps/unix/sysv/linux/Versions | 1 +
sysdeps/unix/sysv/linux/aarch64/libc.abilist | 1 +
sysdeps/unix/sysv/linux/alpha/libc.abilist | 1 +
sysdeps/unix/sysv/linux/arc/libc.abilist | 1 +
sysdeps/unix/sysv/linux/arm/be/libc.abilist | 1 +
sysdeps/unix/sysv/linux/arm/le/libc.abilist | 1 +
.../sysv/linux/bits/fcntl-linux-fortify.h | 49 ++
sysdeps/unix/sysv/linux/bits/fcntl-linux.h | 27 +
sysdeps/unix/sysv/linux/bits/openat2.h | 60 +++
sysdeps/unix/sysv/linux/csky/libc.abilist | 1 +
sysdeps/unix/sysv/linux/hppa/libc.abilist | 1 +
sysdeps/unix/sysv/linux/i386/libc.abilist | 1 +
.../sysv/linux/loongarch/lp64/libc.abilist | 1 +
.../sysv/linux/m68k/coldfire/libc.abilist | 1 +
.../unix/sysv/linux/m68k/m680x0/libc.abilist | 1 +
.../sysv/linux/microblaze/be/libc.abilist | 1 +
.../sysv/linux/microblaze/le/libc.abilist | 1 +
.../sysv/linux/mips/mips32/fpu/libc.abilist | 1 +
.../sysv/linux/mips/mips32/nofpu/libc.abilist | 1 +
.../sysv/linux/mips/mips64/n32/libc.abilist | 1 +
.../sysv/linux/mips/mips64/n64/libc.abilist | 1 +
sysdeps/unix/sysv/linux/openat2.c | 29 ++
sysdeps/unix/sysv/linux/or1k/libc.abilist | 1 +
.../linux/powerpc/powerpc32/fpu/libc.abilist | 1 +
.../powerpc/powerpc32/nofpu/libc.abilist | 1 +
.../linux/powerpc/powerpc64/be/libc.abilist | 1 +
.../linux/powerpc/powerpc64/le/libc.abilist | 1 +
.../unix/sysv/linux/riscv/rv32/libc.abilist | 1 +
.../unix/sysv/linux/riscv/rv64/libc.abilist | 1 +
.../unix/sysv/linux/s390/s390-32/libc.abilist | 1 +
.../unix/sysv/linux/s390/s390-64/libc.abilist | 1 +
sysdeps/unix/sysv/linux/sh/be/libc.abilist | 1 +
sysdeps/unix/sysv/linux/sh/le/libc.abilist | 1 +
.../sysv/linux/sparc/sparc32/libc.abilist | 1 +
.../sysv/linux/sparc/sparc64/libc.abilist | 1 +
sysdeps/unix/sysv/linux/tst-openat2-consts.py | 63 +++
sysdeps/unix/sysv/linux/tst-openat2-lfs.c | 1 +
sysdeps/unix/sysv/linux/tst-openat2.c | 482 ++++++++++++++++++
.../unix/sysv/linux/x86_64/64/libc.abilist | 1 +
.../unix/sysv/linux/x86_64/x32/libc.abilist | 1 +
43 files changed, 866 insertions(+)
create mode 100644 sysdeps/unix/sysv/linux/bits/fcntl-linux-fortify.h
create mode 100644 sysdeps/unix/sysv/linux/bits/openat2.h
create mode 100644 sysdeps/unix/sysv/linux/openat2.c
create mode 100755 sysdeps/unix/sysv/linux/tst-openat2-consts.py
create mode 100644 sysdeps/unix/sysv/linux/tst-openat2-lfs.c
create mode 100644 sysdeps/unix/sysv/linux/tst-openat2.c
diff --git a/NEWS b/NEWS
index a2618545d5..90cd650cf8 100644
--- a/NEWS
+++ b/NEWS
@@ -26,6 +26,10 @@ Major new features:
such as changes to protection permissions, unmapping, relocation to
another location, or shrinking the size.
+* On Linux, the openat2 function has been added. It is an extension of
+ openat and provides a superset of its functionality. It is supported only
+ in LFS mode and it is a cancellable entrypoint.
+
Deprecated and removed features, and other changes affecting compatibility:
* Support for dumped heaps has been removed - malloc_set_state() now always
diff --git a/manual/llio.texi b/manual/llio.texi
index 806ff0a27d..7094ff4145 100644
--- a/manual/llio.texi
+++ b/manual/llio.texi
@@ -218,6 +218,104 @@ new, extended API using 64 bit file sizes and offsets transparently
replaces the old API.
@end deftypefun
+@deftp {Data Type} {struct open_how}
+@standards{Linux, fcntl.h}
+The @code{open_how} structure describes how to open a file using @code{openat2}.
+
+@strong{Portability note:} In the future, additional fields can be added
+to @code{struct open_how}, so that the size of this data type increases.
+Do not use it in places where this matters, such as structure fields in
+installed header files, where such a change could affect the application
+binary interface (ABI).
+
+The following generic fields are available.
+
+@table @code
+@item flags
+This field specifies the file creation and file status flags to use when
+opening the file.
+All of the @code{O_*} flags defined for @code{openat} are valid.
+The @code{openat2} is stricter than @code{openat} in rejecting unknown or
+conflicting values. For example, @code{openat2} rejects a mode that
+exceeds 07777, whereas @code{openat} silently ignores
+excess high-order bits.
+
+@item mode
+This field specifies the mode for the new file, similar to @code{mode}
+argument of @code{openat}. It should be in the range 0..07777.
+
+@item resolve
+This is a bitmask of flags that affect the resolution of file name components.
+Unlike @code{O_NOFOLLOW}, it affects all file name components, not just the
+last one. The following flags are available.
+
+@table @code
+@item RESOLVE_NO_XDEV
+Disallow traversal of mount points during path resolution (including all
+bind mounts).
+
+@item RESOLVE_NO_MAGICLINKS
+Disallow all @strong{magic-link} resolution during path resolution. Magic
+links are symbolic link-like objects that are found in @strong{procfs};
+for example the @code{/proc/pid/exe}.
+
+@item RESOLVE_NO_SYMLINKS
+Disallow resolution of symbolic links during path resolution.
+This option implies @code{RESOLVE_NO_MAGICLINKS}.
+
+@item RESOLVE_BENEATH
+This rejects absolute pathnames, pathnames containing @file{..}@:
+components that would be resolved relative to @var{dfd},
+and symbolic links that resolve to pathnames that would be rejected.
+The rejection occurs even if @var{dfd} is the root directory.
+
+@item RESOLVE_IN_ROOT
+Treat absolute pathnames as being relative to @var{dfd},
+treat a @file{..}@: component as being equivalent to @file{.}@:
+if it is resolved relative to @var{dfd},
+and treat symbolic link contents consistently with this.
+@end table
+
+@end table
+
+@end deftp
+
+
+@deftypefun int openat2 (int @var{dirfd}, const char *@var{pathname}, const struct open_how *@var{how}, size_t @var{size})
+@standards{Linux, fcntl.h}
+@safety{@mtsafe{}@assafe{}@acsafe{}}
+This function is an extension of the @code{openat} and provides a superset of its
+functionality. @xref{Descriptor-Relative Access}.
+
+The @var{size} argument must equal @code{sizeof *@var{how}}. For portability
+to future API versions that may extend @code{struct open_how}, @code{*@var{how}}
+should be fully initialized e.g., by a struct initializer, by @code{memset} to zero,
+or by having static storage duration.
+
+On failure, @code{openat2} returns @math{-1} and sets @code{errno}. It can
+fail for any of the reasons @code{openat} fails, plus the following reasons:
+
+@table @code
+@item E2BIG
+@var{size} is too large for any future extension, @code{*@var{how}} contains
+non-zero members that are future extensions not supported by this kernel
+
+@item EINVAL
+An unknown flag or invalid value was used on @code{*@var{how}}; or
+@code{@var{how}->mode} is non-zero, but @code{@var{how}->flags} does not contain
+@code{O_CREAT} or @code{O_TMPFILE}, or @var{size} is smaller than the ones supported
+by the kernel.
+@end table
+
+It can also return all the errors @code{openat} returns.
+
+Similar to @code{openat}, @code{openat2} is a cancellation point.
+
+Unlike other @code{open}-like functions, this function ignores
+@code{_FILE_OFFSET_BITS} and always operates in large file mode.
+@end deftypefun
+
+
@deftypefn {Obsolete function} int creat (const char *@var{filename}, mode_t @var{mode})
@standards{POSIX.1, fcntl.h}
@safety{@prelim{}@mtsafe{}@assafe{}@acsafe{@acsfd{}}}
diff --git a/sysdeps/unix/sysv/linux/Makefile b/sysdeps/unix/sysv/linux/Makefile
index 199031da64..7f622277c7 100644
--- a/sysdeps/unix/sysv/linux/Makefile
+++ b/sysdeps/unix/sysv/linux/Makefile
@@ -135,6 +135,7 @@ sysdep_headers += \
bits/mman-linux.h \
bits/mman-map-flags-generic.h \
bits/mman-shared.h \
+ bits/openat2.h \
bits/procfs-extra.h \
bits/procfs-id.h \
bits/procfs-prregset.h \
@@ -623,6 +624,7 @@ sysdep_routines += \
internal_statvfs \
open64_nocancel \
open_nocancel \
+ openat2 \
openat64_nocancel \
openat_nocancel \
pread64_nocancel \
@@ -635,6 +637,7 @@ sysdep_routines += \
# sysdep_routines
sysdep_headers += \
+ bits/fcntl-linux-fortify.h \
bits/fcntl-linux.h \
# sysdep_headers
@@ -643,7 +646,24 @@ tests += \
tst-fallocate64 \
tst-getcwd-smallbuff \
tst-o_path-locks \
+ tst-openat2 \
+ tst-openat2-lfs \
# tests
+
+tests-special += \
+ $(objpfx)tst-openat2-consts.out \
+ # tests-special
+$(objpfx)tst-openat2-consts.out: ../sysdeps/unix/sysv/linux/tst-openat2-consts.py
+ $(sysdeps-linux-python) \
+ ../sysdeps/unix/sysv/linux/tst-openat2-consts.py \
+ $(sysdeps-linux-python-cc) \
+ < /dev/null > $@ 2>&1; $(evaluate-test)
+$(objpfx)tst-openat2-consts.out: $(sysdeps-linux-python-deps)
+
+# openat2 only provides LFS support, the tests check if the interface is correctly
+# provided regardless of the flags.
+CFLAGS-tst-openat2-lfs.c += -D_FILE_OFFSET_BITS=64 -D_LARGEFILE64_SOURCE
+
endif
ifeq ($(subdir),elf)
diff --git a/sysdeps/unix/sysv/linux/Versions b/sysdeps/unix/sysv/linux/Versions
index 8f4d71ad7f..c7f199f13f 100644
--- a/sysdeps/unix/sysv/linux/Versions
+++ b/sysdeps/unix/sysv/linux/Versions
@@ -341,6 +341,7 @@ libc {
}
GLIBC_2.43 {
mseal;
+ openat2;
}
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 2f049a4b41..0ded7d7a0a 100644
--- a/sysdeps/unix/sysv/linux/aarch64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/aarch64/libc.abilist
@@ -2771,4 +2771,5 @@ GLIBC_2.43 __memset_explicit_chk F
GLIBC_2.43 memalignment F
GLIBC_2.43 memset_explicit F
GLIBC_2.43 mseal F
+GLIBC_2.43 openat2 F
GLIBC_2.43 umaxabs F
diff --git a/sysdeps/unix/sysv/linux/alpha/libc.abilist b/sysdeps/unix/sysv/linux/alpha/libc.abilist
index bebfad9de2..408b3f655d 100644
--- a/sysdeps/unix/sysv/linux/alpha/libc.abilist
+++ b/sysdeps/unix/sysv/linux/alpha/libc.abilist
@@ -3118,6 +3118,7 @@ GLIBC_2.43 __memset_explicit_chk F
GLIBC_2.43 memalignment F
GLIBC_2.43 memset_explicit F
GLIBC_2.43 mseal F
+GLIBC_2.43 openat2 F
GLIBC_2.43 umaxabs F
GLIBC_2.5 __readlinkat_chk F
GLIBC_2.5 inet6_opt_append F
diff --git a/sysdeps/unix/sysv/linux/arc/libc.abilist b/sysdeps/unix/sysv/linux/arc/libc.abilist
index d6341e98e7..58dadbf196 100644
--- a/sysdeps/unix/sysv/linux/arc/libc.abilist
+++ b/sysdeps/unix/sysv/linux/arc/libc.abilist
@@ -2532,4 +2532,5 @@ GLIBC_2.43 __memset_explicit_chk F
GLIBC_2.43 memalignment F
GLIBC_2.43 memset_explicit F
GLIBC_2.43 mseal F
+GLIBC_2.43 openat2 F
GLIBC_2.43 umaxabs F
diff --git a/sysdeps/unix/sysv/linux/arm/be/libc.abilist b/sysdeps/unix/sysv/linux/arm/be/libc.abilist
index a9076eb320..ad6f686928 100644
--- a/sysdeps/unix/sysv/linux/arm/be/libc.abilist
+++ b/sysdeps/unix/sysv/linux/arm/be/libc.abilist
@@ -2824,6 +2824,7 @@ GLIBC_2.43 __memset_explicit_chk F
GLIBC_2.43 memalignment F
GLIBC_2.43 memset_explicit F
GLIBC_2.43 mseal F
+GLIBC_2.43 openat2 F
GLIBC_2.43 umaxabs F
GLIBC_2.5 __readlinkat_chk F
GLIBC_2.5 inet6_opt_append F
diff --git a/sysdeps/unix/sysv/linux/arm/le/libc.abilist b/sysdeps/unix/sysv/linux/arm/le/libc.abilist
index d3be53ea25..a6ceb7e694 100644
--- a/sysdeps/unix/sysv/linux/arm/le/libc.abilist
+++ b/sysdeps/unix/sysv/linux/arm/le/libc.abilist
@@ -2821,6 +2821,7 @@ GLIBC_2.43 __memset_explicit_chk F
GLIBC_2.43 memalignment F
GLIBC_2.43 memset_explicit F
GLIBC_2.43 mseal F
+GLIBC_2.43 openat2 F
GLIBC_2.43 umaxabs F
GLIBC_2.5 __readlinkat_chk F
GLIBC_2.5 inet6_opt_append F
diff --git a/sysdeps/unix/sysv/linux/bits/fcntl-linux-fortify.h b/sysdeps/unix/sysv/linux/bits/fcntl-linux-fortify.h
new file mode 100644
index 0000000000..e6b414d7ef
--- /dev/null
+++ b/sysdeps/unix/sysv/linux/bits/fcntl-linux-fortify.h
@@ -0,0 +1,49 @@
+/* Checking macros for fcntl functions. Linux version.
+ Copyright (C) 2025 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
+ <https://www.gnu.org/licenses/>. */
+
+#ifndef _FCNTL_H
+# error "Never include <bits/fcntl-linux-fortify.h> directly; use <fcntl.h> instead."
+#endif
+
+#ifdef __USE_GNU
+
+extern int __REDIRECT (__openat2_alias, (int __dfd, const char *__filename,
+ const struct open_how *__how,
+ size_t __usize), openat2)
+ __nonnull ((2, 3));
+
+#if !__fortify_use_clang
+__errordecl (__openat2_invalid_size,
+ "the specified size is larger than sizeof (struct open_how)");
+#endif
+
+__fortify_function int
+openat2 (int __dfd, const char *__filename, const struct open_how *__how,
+ size_t __usize)
+ __fortify_clang_warning (__builtin_constant_p (__usize)
+ && __usize > sizeof (struct open_how),
+ "the specified size is larger than sizeof (struct open_how)")
+{
+#if !__fortify_use_clang
+ if (__builtin_constant_p (__usize) && __usize > sizeof (struct open_how))
+ __openat2_invalid_size ();
+#endif
+ return __openat2_alias (__dfd, __filename, __how, __usize);
+}
+
+#endif /* use GNU */
diff --git a/sysdeps/unix/sysv/linux/bits/fcntl-linux.h b/sysdeps/unix/sysv/linux/bits/fcntl-linux.h
index f9b3de11f4..bdab40f559 100644
--- a/sysdeps/unix/sysv/linux/bits/fcntl-linux.h
+++ b/sysdeps/unix/sysv/linux/bits/fcntl-linux.h
@@ -463,6 +463,33 @@ extern int name_to_handle_at (int __dfd, const char *__name,
extern int open_by_handle_at (int __mountdirfd, struct file_handle *__handle,
int __flags);
+#ifdef __has_include
+# if __has_include ("linux/openat2.h")
+# include "linux/openat2.h"
+# define __glibc_has_open_how 1
+# endif
+#endif
+
+#include <bits/openat2.h>
+
+/* Similar to `openat' but the arguments are packed on HOW with the size
+ USIZE. If flags and mode from HOW are non-zero, then openat2 operates
+ like openat.
+
+ Unlike openat, unknown or invalid flags result in an error (EINVAL),
+ rather than being ignored. The mode must be zero unless one of O_CREAT
+ or O_TMPFILE are set.
+
+ The kernel does not support legacy non-LFS interface. */
+extern int openat2 (int __dfd, const char * __filename,
+ const struct open_how * __how,
+ __SIZE_TYPE__ __usize)
+ __nonnull ((2, 3));
+
#endif /* use GNU */
+#if __USE_FORTIFY_LEVEL > 0 && defined __fortify_function
+# include <bits/fcntl-linux-fortify.h>
+#endif
+
__END_DECLS
diff --git a/sysdeps/unix/sysv/linux/bits/openat2.h b/sysdeps/unix/sysv/linux/bits/openat2.h
new file mode 100644
index 0000000000..a3fe858bc2
--- /dev/null
+++ b/sysdeps/unix/sysv/linux/bits/openat2.h
@@ -0,0 +1,60 @@
+/* openat2 definition. Linux specific.
+ Copyright (C) 2025 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
+ <https://www.gnu.org/licenses/>. */
+
+#ifndef _FCNTL_H
+# error "Never use <bits/openat2.h> directly; include <fcntl.h> instead."
+#endif
+
+#ifndef __glibc_has_open_how
+/* Arguments for how openat2 should open the target path. */
+struct open_how
+{
+ __uint64_t flags;
+ __uint64_t mode;
+ __uint64_t resolve;
+};
+#endif
+
+/* how->resolve flags for openat2. */
+#ifndef RESOLVE_NO_XDEV
+# define RESOLVE_NO_XDEV 0x01 /* Block mount-point crossings
+ (includes bind-mounts). */
+#endif
+#ifndef RESOLVE_NO_MAGICLINKS
+# define RESOLVE_NO_MAGICLINKS 0x02 /* Block traversal through procfs-style
+ "magic-links". */
+#endif
+#ifndef RESOLVE_NO_SYMLINKS
+# define RESOLVE_NO_SYMLINKS 0x04 /* Block traversal through all symlinks. */
+#endif
+#ifndef RESOLVE_BENEATH
+# define RESOLVE_BENEATH 0x08 /* Block "lexical" trickery like
+ "..", symlinks, and absolute
+ paths which escape the dirfd. */
+#endif
+#ifndef RESOLVE_IN_ROOT
+# define RESOLVE_IN_ROOT 0x10 /* Make all jumps to "/" and ".."
+ be scoped inside the dirfd
+ (similar to chroot). */
+#endif
+#ifndef RESOLVE_CACHED
+# define RESOLVE_CACHED 0x20 /* Only complete if resolution can be
+ completed through cached lookup. May
+ return -EAGAIN if that's not
+ possible. */
+#endif
diff --git a/sysdeps/unix/sysv/linux/csky/libc.abilist b/sysdeps/unix/sysv/linux/csky/libc.abilist
index e110766976..643d322822 100644
--- a/sysdeps/unix/sysv/linux/csky/libc.abilist
+++ b/sysdeps/unix/sysv/linux/csky/libc.abilist
@@ -2808,4 +2808,5 @@ GLIBC_2.43 __memset_explicit_chk F
GLIBC_2.43 memalignment F
GLIBC_2.43 memset_explicit F
GLIBC_2.43 mseal F
+GLIBC_2.43 openat2 F
GLIBC_2.43 umaxabs F
diff --git a/sysdeps/unix/sysv/linux/hppa/libc.abilist b/sysdeps/unix/sysv/linux/hppa/libc.abilist
index a079e7d1a0..0ee1a4fd3f 100644
--- a/sysdeps/unix/sysv/linux/hppa/libc.abilist
+++ b/sysdeps/unix/sysv/linux/hppa/libc.abilist
@@ -2845,6 +2845,7 @@ GLIBC_2.43 __memset_explicit_chk F
GLIBC_2.43 memalignment F
GLIBC_2.43 memset_explicit F
GLIBC_2.43 mseal F
+GLIBC_2.43 openat2 F
GLIBC_2.43 umaxabs F
GLIBC_2.5 __readlinkat_chk F
GLIBC_2.5 inet6_opt_append F
diff --git a/sysdeps/unix/sysv/linux/i386/libc.abilist b/sysdeps/unix/sysv/linux/i386/libc.abilist
index 421b3c742b..223f5971ef 100644
--- a/sysdeps/unix/sysv/linux/i386/libc.abilist
+++ b/sysdeps/unix/sysv/linux/i386/libc.abilist
@@ -3028,6 +3028,7 @@ GLIBC_2.43 __memset_explicit_chk F
GLIBC_2.43 memalignment F
GLIBC_2.43 memset_explicit F
GLIBC_2.43 mseal F
+GLIBC_2.43 openat2 F
GLIBC_2.43 umaxabs F
GLIBC_2.5 __readlinkat_chk F
GLIBC_2.5 inet6_opt_append F
diff --git a/sysdeps/unix/sysv/linux/loongarch/lp64/libc.abilist b/sysdeps/unix/sysv/linux/loongarch/lp64/libc.abilist
index 1d7a1de570..4d905a46cb 100644
--- a/sysdeps/unix/sysv/linux/loongarch/lp64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/loongarch/lp64/libc.abilist
@@ -2292,4 +2292,5 @@ GLIBC_2.43 __memset_explicit_chk F
GLIBC_2.43 memalignment F
GLIBC_2.43 memset_explicit F
GLIBC_2.43 mseal F
+GLIBC_2.43 openat2 F
GLIBC_2.43 umaxabs F
diff --git a/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist b/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist
index b512384ed0..024c465bb8 100644
--- a/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist
+++ b/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist
@@ -2804,6 +2804,7 @@ GLIBC_2.43 __memset_explicit_chk F
GLIBC_2.43 memalignment F
GLIBC_2.43 memset_explicit F
GLIBC_2.43 mseal F
+GLIBC_2.43 openat2 F
GLIBC_2.43 umaxabs F
GLIBC_2.5 __readlinkat_chk F
GLIBC_2.5 inet6_opt_append F
diff --git a/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist b/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist
index 223e4825ab..b7b87a0b6d 100644
--- a/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist
+++ b/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist
@@ -2971,6 +2971,7 @@ GLIBC_2.43 __memset_explicit_chk F
GLIBC_2.43 memalignment F
GLIBC_2.43 memset_explicit F
GLIBC_2.43 mseal F
+GLIBC_2.43 openat2 F
GLIBC_2.43 umaxabs F
GLIBC_2.5 __readlinkat_chk F
GLIBC_2.5 inet6_opt_append F
diff --git a/sysdeps/unix/sysv/linux/microblaze/be/libc.abilist b/sysdeps/unix/sysv/linux/microblaze/be/libc.abilist
index f314f55e1f..bb8e4d25d6 100644
--- a/sysdeps/unix/sysv/linux/microblaze/be/libc.abilist
+++ b/sysdeps/unix/sysv/linux/microblaze/be/libc.abilist
@@ -2857,4 +2857,5 @@ GLIBC_2.43 __memset_explicit_chk F
GLIBC_2.43 memalignment F
GLIBC_2.43 memset_explicit F
GLIBC_2.43 mseal F
+GLIBC_2.43 openat2 F
GLIBC_2.43 umaxabs F
diff --git a/sysdeps/unix/sysv/linux/microblaze/le/libc.abilist b/sysdeps/unix/sysv/linux/microblaze/le/libc.abilist
index 8a33db2cb2..466b439270 100644
--- a/sysdeps/unix/sysv/linux/microblaze/le/libc.abilist
+++ b/sysdeps/unix/sysv/linux/microblaze/le/libc.abilist
@@ -2854,4 +2854,5 @@ GLIBC_2.43 __memset_explicit_chk F
GLIBC_2.43 memalignment F
GLIBC_2.43 memset_explicit F
GLIBC_2.43 mseal F
+GLIBC_2.43 openat2 F
GLIBC_2.43 umaxabs F
diff --git a/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist
index 3f704bd87d..ac5062be2a 100644
--- a/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist
+++ b/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist
@@ -2934,6 +2934,7 @@ GLIBC_2.43 __memset_explicit_chk F
GLIBC_2.43 memalignment F
GLIBC_2.43 memset_explicit F
GLIBC_2.43 mseal F
+GLIBC_2.43 openat2 F
GLIBC_2.43 umaxabs F
GLIBC_2.5 __readlinkat_chk F
GLIBC_2.5 inet6_opt_append F
diff --git a/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist
index 633d031c72..8ac8426ae6 100644
--- a/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist
+++ b/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist
@@ -2932,6 +2932,7 @@ GLIBC_2.43 __memset_explicit_chk F
GLIBC_2.43 memalignment F
GLIBC_2.43 memset_explicit F
GLIBC_2.43 mseal F
+GLIBC_2.43 openat2 F
GLIBC_2.43 umaxabs F
GLIBC_2.5 __readlinkat_chk F
GLIBC_2.5 inet6_opt_append F
diff --git a/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist
index 8b8e5af806..5f7ab3f95d 100644
--- a/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist
+++ b/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist
@@ -2940,6 +2940,7 @@ GLIBC_2.43 __memset_explicit_chk F
GLIBC_2.43 memalignment F
GLIBC_2.43 memset_explicit F
GLIBC_2.43 mseal F
+GLIBC_2.43 openat2 F
GLIBC_2.43 umaxabs F
GLIBC_2.5 __readlinkat_chk F
GLIBC_2.5 inet6_opt_append F
diff --git a/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist
index f45d5075fd..b6543a8773 100644
--- a/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist
@@ -2842,6 +2842,7 @@ GLIBC_2.43 __memset_explicit_chk F
GLIBC_2.43 memalignment F
GLIBC_2.43 memset_explicit F
GLIBC_2.43 mseal F
+GLIBC_2.43 openat2 F
GLIBC_2.43 umaxabs F
GLIBC_2.5 __readlinkat_chk F
GLIBC_2.5 inet6_opt_append F
diff --git a/sysdeps/unix/sysv/linux/openat2.c b/sysdeps/unix/sysv/linux/openat2.c
new file mode 100644
index 0000000000..d45eb05c32
--- /dev/null
+++ b/sysdeps/unix/sysv/linux/openat2.c
@@ -0,0 +1,29 @@
+/* Linux openat2 syscall implementation.
+ Copyright (C) 2025 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
+ <https://www.gnu.org/licenses/>. */
+
+#include <fcntl.h>
+#include <bits/openat2.h>
+#include <sysdep-cancel.h>
+
+int
+__openat2 (int dfd, const char *filename,
+ const struct open_how *how, size_t usize)
+{
+ return SYSCALL_CANCEL (openat2, dfd, filename, how, usize);
+}
+weak_alias (__openat2, openat2)
diff --git a/sysdeps/unix/sysv/linux/or1k/libc.abilist b/sysdeps/unix/sysv/linux/or1k/libc.abilist
index 96b056d14a..160dff8c79 100644
--- a/sysdeps/unix/sysv/linux/or1k/libc.abilist
+++ b/sysdeps/unix/sysv/linux/or1k/libc.abilist
@@ -2282,4 +2282,5 @@ GLIBC_2.43 __memset_explicit_chk F
GLIBC_2.43 memalignment F
GLIBC_2.43 memset_explicit F
GLIBC_2.43 mseal F
+GLIBC_2.43 openat2 F
GLIBC_2.43 umaxabs F
diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist
index 6420f23d06..16653ba77f 100644
--- a/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist
+++ b/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist
@@ -3161,6 +3161,7 @@ GLIBC_2.43 __memset_explicit_chk F
GLIBC_2.43 memalignment F
GLIBC_2.43 memset_explicit F
GLIBC_2.43 mseal F
+GLIBC_2.43 openat2 F
GLIBC_2.43 umaxabs F
GLIBC_2.5 __readlinkat_chk F
GLIBC_2.5 inet6_opt_append F
diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist
index 9a2a258e02..a453e23744 100644
--- a/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist
+++ b/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist
@@ -3206,6 +3206,7 @@ GLIBC_2.43 __memset_explicit_chk F
GLIBC_2.43 memalignment F
GLIBC_2.43 memset_explicit F
GLIBC_2.43 mseal F
+GLIBC_2.43 openat2 F
GLIBC_2.43 umaxabs F
GLIBC_2.5 __readlinkat_chk F
GLIBC_2.5 inet6_opt_append F
diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc64/be/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc64/be/libc.abilist
index 6a20d8d031..7d01a4f1cb 100644
--- a/sysdeps/unix/sysv/linux/powerpc/powerpc64/be/libc.abilist
+++ b/sysdeps/unix/sysv/linux/powerpc/powerpc64/be/libc.abilist
@@ -2915,6 +2915,7 @@ GLIBC_2.43 __memset_explicit_chk F
GLIBC_2.43 memalignment F
GLIBC_2.43 memset_explicit F
GLIBC_2.43 mseal F
+GLIBC_2.43 openat2 F
GLIBC_2.43 umaxabs F
GLIBC_2.5 __readlinkat_chk F
GLIBC_2.5 inet6_opt_append F
diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc64/le/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc64/le/libc.abilist
index c0fa82e6a2..f5c1927d34 100644
--- a/sysdeps/unix/sysv/linux/powerpc/powerpc64/le/libc.abilist
+++ b/sysdeps/unix/sysv/linux/powerpc/powerpc64/le/libc.abilist
@@ -2991,4 +2991,5 @@ GLIBC_2.43 __memset_explicit_chk F
GLIBC_2.43 memalignment F
GLIBC_2.43 memset_explicit F
GLIBC_2.43 mseal F
+GLIBC_2.43 openat2 F
GLIBC_2.43 umaxabs F
diff --git a/sysdeps/unix/sysv/linux/riscv/rv32/libc.abilist b/sysdeps/unix/sysv/linux/riscv/rv32/libc.abilist
index 5c46dcacdc..366c740532 100644
--- a/sysdeps/unix/sysv/linux/riscv/rv32/libc.abilist
+++ b/sysdeps/unix/sysv/linux/riscv/rv32/libc.abilist
@@ -2535,4 +2535,5 @@ GLIBC_2.43 __memset_explicit_chk F
GLIBC_2.43 memalignment F
GLIBC_2.43 memset_explicit F
GLIBC_2.43 mseal F
+GLIBC_2.43 openat2 F
GLIBC_2.43 umaxabs F
diff --git a/sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist b/sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist
index 4ce2007c56..7c80ed4bc4 100644
--- a/sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist
@@ -2735,4 +2735,5 @@ GLIBC_2.43 __memset_explicit_chk F
GLIBC_2.43 memalignment F
GLIBC_2.43 memset_explicit F
GLIBC_2.43 mseal F
+GLIBC_2.43 openat2 F
GLIBC_2.43 umaxabs F
diff --git a/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist b/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist
index 9a672c6a4c..0aa665c18e 100644
--- a/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist
+++ b/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist
@@ -3159,6 +3159,7 @@ GLIBC_2.43 __memset_explicit_chk F
GLIBC_2.43 memalignment F
GLIBC_2.43 memset_explicit F
GLIBC_2.43 mseal F
+GLIBC_2.43 openat2 F
GLIBC_2.43 umaxabs F
GLIBC_2.5 __readlinkat_chk F
GLIBC_2.5 inet6_opt_append F
diff --git a/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist b/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist
index 1926e675ca..f2bf32ed95 100644
--- a/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist
@@ -2952,6 +2952,7 @@ GLIBC_2.43 __memset_explicit_chk F
GLIBC_2.43 memalignment F
GLIBC_2.43 memset_explicit F
GLIBC_2.43 mseal F
+GLIBC_2.43 openat2 F
GLIBC_2.43 umaxabs F
GLIBC_2.5 __readlinkat_chk F
GLIBC_2.5 inet6_opt_append F
diff --git a/sysdeps/unix/sysv/linux/sh/be/libc.abilist b/sysdeps/unix/sysv/linux/sh/be/libc.abilist
index b3510af9ef..2b6d77d8ff 100644
--- a/sysdeps/unix/sysv/linux/sh/be/libc.abilist
+++ b/sysdeps/unix/sysv/linux/sh/be/libc.abilist
@@ -2851,6 +2851,7 @@ GLIBC_2.43 __memset_explicit_chk F
GLIBC_2.43 memalignment F
GLIBC_2.43 memset_explicit F
GLIBC_2.43 mseal F
+GLIBC_2.43 openat2 F
GLIBC_2.43 umaxabs F
GLIBC_2.5 __readlinkat_chk F
GLIBC_2.5 inet6_opt_append F
diff --git a/sysdeps/unix/sysv/linux/sh/le/libc.abilist b/sysdeps/unix/sysv/linux/sh/le/libc.abilist
index fc1fea412a..65b990f5ea 100644
--- a/sysdeps/unix/sysv/linux/sh/le/libc.abilist
+++ b/sysdeps/unix/sysv/linux/sh/le/libc.abilist
@@ -2848,6 +2848,7 @@ GLIBC_2.43 __memset_explicit_chk F
GLIBC_2.43 memalignment F
GLIBC_2.43 memset_explicit F
GLIBC_2.43 mseal F
+GLIBC_2.43 openat2 F
GLIBC_2.43 umaxabs F
GLIBC_2.5 __readlinkat_chk F
GLIBC_2.5 inet6_opt_append F
diff --git a/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist b/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist
index fce9f948b7..2fc6479c2c 100644
--- a/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist
+++ b/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist
@@ -3182,6 +3182,7 @@ GLIBC_2.43 __memset_explicit_chk F
GLIBC_2.43 memalignment F
GLIBC_2.43 memset_explicit F
GLIBC_2.43 mseal F
+GLIBC_2.43 openat2 F
GLIBC_2.43 umaxabs F
GLIBC_2.5 __readlinkat_chk F
GLIBC_2.5 inet6_opt_append F
diff --git a/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist b/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist
index 9d4721c16e..2446991f28 100644
--- a/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist
@@ -2818,6 +2818,7 @@ GLIBC_2.43 __memset_explicit_chk F
GLIBC_2.43 memalignment F
GLIBC_2.43 memset_explicit F
GLIBC_2.43 mseal F
+GLIBC_2.43 openat2 F
GLIBC_2.43 umaxabs F
GLIBC_2.5 __readlinkat_chk F
GLIBC_2.5 inet6_opt_append F
diff --git a/sysdeps/unix/sysv/linux/tst-openat2-consts.py b/sysdeps/unix/sysv/linux/tst-openat2-consts.py
new file mode 100755
index 0000000000..9b65d184d7
--- /dev/null
+++ b/sysdeps/unix/sysv/linux/tst-openat2-consts.py
@@ -0,0 +1,63 @@
+#!/usr/bin/python3
+# Test that glibc's sys/openat2.h constants match the kernel's.
+# Copyright (C) 2025 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
+# <https://www.gnu.org/licenses/>.
+
+import argparse
+import sys
+
+import glibcextract
+import glibcsyscalls
+
+
+def main():
+ """The main entry point."""
+ parser = argparse.ArgumentParser(
+ description="Test that glibc's sys/openat2.h constants "
+ "match the kernel's.")
+ parser.add_argument('--cc', metavar='CC',
+ help='C compiler (including options) to use')
+ args = parser.parse_args()
+
+ if glibcextract.compile_c_snippet(
+ '#include <linux/openat2.h>',
+ args.cc).returncode != 0:
+ sys.exit (77)
+
+ linux_version_headers = glibcsyscalls.linux_kernel_version(args.cc)
+ # Constants in glibc were updated to match Linux v6.8. When glibc
+ # constants are updated this value should be updated to match the
+ # released kernel version from which the constants were taken.
+ linux_version_glibc = (6, 8)
+ def check(cte, exclude=None):
+ return glibcextract.compare_macro_consts(
+ '#define _FCNTL_H\n'
+ '#include <stdint.h>\n'
+ '#include <bits/openat2.h>\n',
+ '#include <asm/fcntl.h>\n'
+ '#include <linux/openat2.h>\n',
+ args.cc,
+ cte,
+ exclude,
+ linux_version_glibc > linux_version_headers,
+ linux_version_headers > linux_version_glibc)
+
+ status = check('RESOLVE.*')
+ sys.exit(status)
+
+if __name__ == '__main__':
+ main()
diff --git a/sysdeps/unix/sysv/linux/tst-openat2-lfs.c b/sysdeps/unix/sysv/linux/tst-openat2-lfs.c
new file mode 100644
index 0000000000..85962d30c1
--- /dev/null
+++ b/sysdeps/unix/sysv/linux/tst-openat2-lfs.c
@@ -0,0 +1 @@
+#include "tst-openat2.c"
diff --git a/sysdeps/unix/sysv/linux/tst-openat2.c b/sysdeps/unix/sysv/linux/tst-openat2.c
new file mode 100644
index 0000000000..0778b98b6c
--- /dev/null
+++ b/sysdeps/unix/sysv/linux/tst-openat2.c
@@ -0,0 +1,482 @@
+/* Linux openat2 tests.
+ Copyright (C) 2025 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
+ <https://www.gnu.org/licenses/>. */
+
+#include <array_length.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <libgen.h>
+#include <stdint.h>
+
+#include <support/check.h>
+#include <support/support.h>
+#include <support/temp_file.h>
+#include <support/test-driver.h>
+#include <support/xunistd.h>
+
+static int dir_fd;
+static char *temp_dir;
+static char *temp_subdir;
+static char *temp_some_file;
+
+#define TEST_DIR_LINK "test_dir_link"
+#define TEST_DIR_LINK_2 "test_dir_link_2"
+#define TEMP_DIR_LINK "temp_dir_link"
+#define INVALID_LINK "invalid-link"
+#define VALID_LINK "valid-link"
+
+static void
+create_symlink (const char *target, const char *linkpath)
+{
+ TEST_VERIFY_EXIT (symlinkat (target, dir_fd, linkpath) == 0);
+ add_temp_file (xasprintf ("%s/%s", temp_dir, linkpath));
+}
+
+static void
+do_prepare (int argc, char *argv[])
+{
+ /*
+ Construct a test directory with the following structure:
+
+ temp_dir/
+ |- tst-openat2.xxxxxxx
+ |- test_dir_link -> test_dir (/tmp)
+ |- test_dir_link_2 -> test_dir_link
+ |- temp_dir_link -> temp_dir/tst-openat2.xxxxxxx
+ |- some-file.xxxxxxx
+ |- invalid_link -> temp_dir/tst-openat2.xxxxxxx/some-file.xxxxxxx
+ |- valid_link -> some-file.xxxxxxx
+ |- subdir.xxxxxxx
+ |- some-file.xxxxxxx
+ */
+
+ temp_dir = support_create_temp_directory ("tst-openat2.");
+ dir_fd = xopen (temp_dir, O_RDONLY | O_DIRECTORY, 0);
+
+ create_symlink (test_dir, TEST_DIR_LINK);
+ create_symlink (TEST_DIR_LINK, TEST_DIR_LINK_2);
+ create_symlink (temp_dir, TEMP_DIR_LINK);
+
+ {
+ char *filename;
+ int fd = create_temp_file_in_dir ("some-file.", temp_dir, &filename);
+ TEST_VERIFY_EXIT (fd != -1);
+
+ create_symlink (filename, INVALID_LINK);
+
+ create_symlink (basename (filename), VALID_LINK);
+ }
+
+ temp_subdir = support_create_temp_directory (xasprintf ("%s/subdir.",
+ basename (temp_dir)));
+ {
+ int fd = create_temp_file_in_dir ("some-file.", temp_subdir,
+ &temp_some_file);
+ TEST_VERIFY_EXIT (fd != -1);
+ }
+}
+#define PREPARE do_prepare
+
+static int
+do_test_struct (void)
+{
+ static struct struct_test
+ {
+ struct open_how_ext
+ {
+ struct open_how inner;
+ int extra1;
+ int extra2;
+ int extra3;
+ } arg;
+ size_t size;
+ int err;
+ } tests[] =
+ {
+ {
+ /* Zero size. */
+ .arg.inner.flags = O_RDONLY,
+ .size = 0,
+ .err = EINVAL,
+ },
+ {
+ /* Normal struct. */
+ .arg.inner.flags = O_RDONLY,
+ .size = sizeof (struct open_how),
+ },
+ {
+ /* Larger struct, zeroed out the unused values. */
+ .arg.inner.flags = O_RDONLY,
+ .size = sizeof (struct open_how_ext),
+ },
+ {
+ /* Larger struct, non-zeroed out the unused values. */
+ .arg.inner.flags = O_RDONLY,
+ .arg.extra1 = 0xdeadbeef,
+ .size = sizeof (struct open_how_ext),
+ .err = E2BIG,
+ },
+ {
+ /* Larger struct, non-zeroed out the unused values. */
+ .arg.inner.flags = O_RDONLY,
+ .arg.extra2 = 0xdeadbeef,
+ .size = sizeof (struct open_how_ext),
+ .err = E2BIG,
+ },
+ };
+
+ for (struct struct_test *t = tests; t != array_end (tests); t++)
+ {
+ int fd = openat2 (AT_FDCWD, ".", (struct open_how *) &t->arg, t->size);
+ if (t->err != 0)
+ {
+ TEST_COMPARE (fd, -1);
+ TEST_COMPARE (errno, t->err);
+ }
+ else
+ TEST_VERIFY (fd >= 0);
+ }
+
+ return 0;
+}
+
+static int
+do_test_flags (void)
+{
+ static struct flag_test
+ {
+ const char *path;
+ struct open_how how;
+ int err;
+ } tests[] =
+ {
+ /* O_TMPFILE is incompatible with O_PATH and O_CREAT. */
+ {
+ .how.flags = O_TMPFILE | O_PATH | O_RDWR,
+ .err = EINVAL },
+ {
+ .how.flags = O_TMPFILE | O_CREAT | O_RDWR,
+ .err = EINVAL },
+
+ /* O_PATH only permits certain other flags to be set ... */
+ {
+ .how.flags = O_PATH | O_CLOEXEC
+ },
+ {
+ .how.flags = O_PATH | O_DIRECTORY
+ },
+ {
+ .how.flags = O_PATH | O_NOFOLLOW
+ },
+ /* ... and others are absolutely not permitted. */
+ {
+ .how.flags = O_PATH | O_RDWR,
+ .err = EINVAL },
+ {
+ .how.flags = O_PATH | O_CREAT,
+ .err = EINVAL },
+ {
+ .how.flags = O_PATH | O_EXCL,
+ .err = EINVAL },
+ {
+ .how.flags = O_PATH | O_NOCTTY,
+ .err = EINVAL },
+ {
+ .how.flags = O_PATH | O_DIRECT,
+ .err = EINVAL },
+
+ /* ->mode must only be set with O_{CREAT,TMPFILE}. */
+ {
+ .how.flags = O_RDONLY,
+ .how.mode = 0600,
+ .err = EINVAL },
+ {
+ .how.flags = O_PATH,
+ .how.mode = 0600,
+ .err = EINVAL },
+ {
+ .how.flags = O_CREAT,
+ .how.mode = 0600 },
+ {
+ .how.flags = O_TMPFILE | O_RDWR,
+ .how.mode = 0600 },
+ /* ->mode must only contain 0777 bits. */
+ {
+ .how.flags = O_CREAT, .how.mode = 0xFFFF, .err = EINVAL },
+ {
+ .how.flags = O_CREAT, .how.mode = 0xC000000000000000ULL,
+ .err = EINVAL },
+ {
+ .how.flags = O_TMPFILE | O_RDWR, .how.mode = 0x1337,
+ .err = EINVAL },
+ {
+ .how.flags = O_TMPFILE | O_RDWR,
+ .how.mode = 0x0000A00000000000ULL,
+ .err = EINVAL
+ },
+
+ /* ->resolve flags must not conflict. */
+ {
+ .how.flags = O_RDONLY,
+ .how.resolve = RESOLVE_BENEATH | RESOLVE_IN_ROOT,
+ .err = EINVAL
+ },
+
+ /* ->resolve must only contain RESOLVE_* flags. */
+ {
+ .how.flags = O_RDONLY,
+ .how.resolve = 0x1337,
+ .err = EINVAL
+ },
+ {
+ .how.flags = O_CREAT,
+ .how.resolve = 0x1337,
+ .err = EINVAL
+ },
+ {
+ .how.flags = O_TMPFILE | O_RDWR,
+ .how.resolve = 0x1337,
+ .err = EINVAL
+ },
+ {
+ .how.flags = O_PATH,
+ .how.resolve = 0x1337,
+ .err = EINVAL
+ },
+
+ /* currently unknown upper 32 bit rejected. */
+ {
+ .how.flags = O_RDONLY | (1ULL << 63),
+ .how.resolve = 0,
+ .err = EINVAL
+ },
+ };
+
+ for (struct flag_test *t = tests; t != array_end (tests); t++)
+ {
+ const char *path;
+ if (t->how.flags & O_CREAT)
+ {
+ char *newfile;
+ int temp_fd = create_temp_file ("tst-openat2.", &newfile);
+ TEST_VERIFY_EXIT (temp_fd != -1);
+ xunlink (newfile);
+ path = newfile;
+ }
+ else
+ path = ".";
+
+ int fd = openat2 (AT_FDCWD, path, &t->how, sizeof (struct open_how));
+ if (fd != 0 && errno == EOPNOTSUPP)
+ {
+ /* Skip the testcase if FS does not support the operation (e.g.
+ valid O_TMPFILE on NFS). */
+ continue;
+ }
+
+ if (t->err != 0)
+ {
+ TEST_COMPARE (fd, -1);
+ TEST_COMPARE (errno, t->err);
+ }
+ else
+ TEST_VERIFY (fd >= 0);
+ }
+
+ return 0;
+}
+
+static void
+do_test_resolve (void)
+{
+ int fd;
+
+ /* TEMP_DIR_LINK links to the absolute temp_dir, which escapes the temporary
+ test directory. */
+ fd = openat2 (dir_fd,
+ TEST_DIR_LINK,
+ &(struct open_how)
+ {
+ .resolve = RESOLVE_BENEATH,
+ },
+ sizeof (struct open_how));
+ TEST_COMPARE (fd, -1);
+ TEST_COMPARE (errno, EXDEV);
+
+ /* Same as before, TEMP_DIR_LINK_2 links to TEMP_DIR_LINK. */
+ fd = openat2 (dir_fd,
+ TEST_DIR_LINK_2,
+ &(struct open_how)
+ {
+ .resolve = RESOLVE_BENEATH,
+ },
+ sizeof (struct open_how));
+ TEST_COMPARE (fd, -1);
+ TEST_COMPARE (errno, EXDEV);
+
+ /* TEMP_DIR_LINK links to the temporary directory itself (dir_fd). */
+ fd = openat2 (dir_fd,
+ TEMP_DIR_LINK,
+ &(struct open_how)
+ {
+ .resolve = RESOLVE_BENEATH,
+ },
+ sizeof (struct open_how));
+ TEST_COMPARE (fd, -1);
+ TEST_COMPARE (errno, EXDEV);
+
+ /* Although it points to a valid file in same path, the link refers to
+ an absolute path. */
+ fd = openat2 (dir_fd,
+ INVALID_LINK,
+ &(struct open_how)
+ {
+ .resolve = RESOLVE_BENEATH,
+ },
+ sizeof (struct open_how));
+ TEST_COMPARE (fd, -1);
+ TEST_COMPARE (errno, EXDEV);
+
+ fd = openat2 (dir_fd,
+ VALID_LINK,
+ &(struct open_how)
+ {
+ .resolve = RESOLVE_BENEATH,
+ },
+ sizeof (struct open_how));
+ TEST_VERIFY (fd != -1);
+ xclose (fd);
+
+ /* There is no such file in temp_dir/tst-openat2.xxxxxxx. */
+ fd = openat2 (dir_fd,
+ "should-not-work",
+ &(struct open_how)
+ {
+ .resolve = RESOLVE_IN_ROOT,
+ },
+ sizeof (struct open_how));
+ TEST_COMPARE (fd, -1);
+ TEST_COMPARE (errno, ENOENT);
+
+ {
+ int subdir_fd = openat2 (dir_fd,
+ basename (temp_subdir),
+ &(struct open_how)
+ {
+ .flags = O_RDONLY | O_DIRECTORY,
+ .resolve = RESOLVE_IN_ROOT,
+ },
+ sizeof (struct open_how));
+ TEST_VERIFY (subdir_fd != -1);
+
+ /* Open the file within the subdir.xxxxxx with both tst-openat2.xxxxxxx
+ and tst-openat2.xxxxxxx/subdir.xxxxxxx file descriptors. */
+ fd = openat2 (subdir_fd,
+ basename (temp_some_file),
+ &(struct open_how)
+ {
+ .resolve = RESOLVE_IN_ROOT,
+ },
+ sizeof (struct open_how));
+ TEST_VERIFY (fd != -1);
+ xclose (fd);
+
+ fd = openat2 (dir_fd,
+ xasprintf ("%s/%s",
+ basename (temp_subdir),
+ basename (temp_some_file)),
+ &(struct open_how)
+ {
+ .resolve = RESOLVE_IN_ROOT,
+ },
+ sizeof (struct open_how));
+ TEST_VERIFY (fd != -1);
+ xclose (fd);
+ }
+}
+
+static int
+do_test_basic (void)
+{
+ int fd;
+
+ fd = openat2 (dir_fd,
+ "some-file",
+ &(struct open_how)
+ {
+ .flags = O_CREAT|O_RDWR|O_EXCL,
+ .mode = 0666,
+ },
+ sizeof (struct open_how));
+ TEST_VERIFY (fd != -1);
+
+ xwrite (fd, "hello", 5);
+
+ /* Before closing the file, try using this file descriptor to open
+ another file. This must fail. */
+ {
+ int fd2 = openat2 (fd,
+ "should-not-work",
+ &(struct open_how)
+ {
+ .flags = O_CREAT|O_RDWR|O_EXCL,
+ .mode = 0666,
+ },
+ sizeof (struct open_how));
+ TEST_COMPARE (fd2, -1);
+ TEST_COMPARE (errno, ENOTDIR);
+ }
+
+ /* Remove the created file. */
+ int cwdfd = xopen (".", O_RDONLY | O_DIRECTORY, 0);
+ TEST_COMPARE (fchdir (dir_fd), 0);
+ xunlink ("some-file");
+ TEST_COMPARE (fchdir (cwdfd), 0);
+
+ xclose (dir_fd);
+ xclose (cwdfd);
+
+ fd = openat2 (dir_fd,
+ "some-file",
+ &(struct open_how)
+ {
+ .flags = O_CREAT|O_RDWR|O_EXCL,
+ .mode = 0666,
+ },
+ sizeof (struct open_how));
+ TEST_COMPARE (fd, -1);
+ TEST_COMPARE (errno, EBADF);
+
+ return 0;
+}
+
+static int
+do_test (void)
+{
+ if (openat2 (AT_FDCWD, ".", &(struct open_how) {}, sizeof (struct open_how))
+ == -1 && errno == ENOSYS)
+ FAIL_UNSUPPORTED ("openat2 is not supported by the kernel");
+
+ do_test_struct ();
+ do_test_flags ();
+ do_test_resolve ();
+ do_test_basic ();
+
+ 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 a836a57426..941a483510 100644
--- a/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist
@@ -2767,6 +2767,7 @@ GLIBC_2.43 __memset_explicit_chk F
GLIBC_2.43 memalignment F
GLIBC_2.43 memset_explicit F
GLIBC_2.43 mseal F
+GLIBC_2.43 openat2 F
GLIBC_2.43 umaxabs F
GLIBC_2.5 __readlinkat_chk F
GLIBC_2.5 inet6_opt_append F
diff --git a/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist b/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist
index aecce5bbc5..a5714cf59f 100644
--- a/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist
+++ b/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist
@@ -2786,4 +2786,5 @@ GLIBC_2.43 __memset_explicit_chk F
GLIBC_2.43 memalignment F
GLIBC_2.43 memset_explicit F
GLIBC_2.43 mseal F
+GLIBC_2.43 openat2 F
GLIBC_2.43 umaxabs F
--
2.43.0
More information about the Libc-alpha
mailing list