On 2025-12-01 09:41, Paul Eggert wrote:
On 2025-11-30 23:42, Bruno Haible wrote:
The second added test fails on macOS 15 (but succeeds on macOS 13, 14,
26):
../../gltests/test-openat2.c:514: assertion 'errno == EXDEV' failed
On reading xnu source code I think I see the problem: macOS 15
O_RESOLVE_BENEATH failures set errno to EACCES not ENOTCAPABLE, as
ENOTCAPABLE was not introduced to macOS until macos 26.
This led me to discover that the fixes I installed November 26 in
response to Pavel's suggestion incorrectly assumed that Gnulib openat2
should always translate ENOTCAPABLE to EXDEV. However, FreeBSD and macOS
26 openat can fail with ENOTCAPABLE for reasons other than
O_RESOLVE_BENEATH violations, and these instances of ENOTCAPABLE should
not be translated to EXDEV.
I installed the attached patch to try to address both these issues.
From 45b6e6898d1f931bfca41d961289bd6ac33238e5 Mon Sep 17 00:00:00 2001
From: Paul Eggert <[email protected]>
Date: Mon, 1 Dec 2025 14:30:03 -0800
Subject: [PATCH] openat2: port to O_RESOLVE_BENEATH && !ENOTCAPABLE
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Problem reported by Bruno Haible in:
https://lists.gnu.org/r/bug-gnulib/2025-12/msg00000.html
Also, fix related bugs involving ENOTCAPABLE.
* lib/openat2.c (do_openat2): Do not map ENOTCAPABLE to EXDEV.
On platforms with ENOTCAPABLE, an ENOTCAPABLE failure here
has nothing to do with O_RESOLVE_BENEATH.
(openat2): Port to macOS 15, which lacks ENOTCAPABLE,
and where O_RESOLVE_BENEATH violations set errno = EACCES.
Don’t assume that an openat2 ENOTCAPABLE (or on macOS 15, EACCES)
failure means an O_RESOLVE_BENEATH failure;
it could merely mean that the process is in capability mode.
---
ChangeLog | 10 ++++++++++
lib/openat2.c | 21 +++++++++++----------
2 files changed, 21 insertions(+), 10 deletions(-)
diff --git a/ChangeLog b/ChangeLog
index d7fc875684..f13140ef81 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,13 @@
+2025-12-01 Paul Eggert <[email protected]>
+
+ openat2: port to O_RESOLVE_BENEATH && !ENOTCAPABLE
+ Problem reported by Bruno Haible in:
+ https://lists.gnu.org/r/bug-gnulib/2025-12/msg00000.html
+ * lib/openat2.c (openat2): Port to macOS 15, which lacks ENOTCAPABLE,
+ and where O_RESOLVE_BENEATH violations set errno = EACCES.
+ While we’re at it, fix a bug in FreeBSD which can fail with
+ ENOTCAPABLE even when O_RESOLVE_BENEATH is not specified.
+
2025-11-30 Bruno Haible <[email protected]>
HACKING: Add remark about ChangeLog entry dates.
diff --git a/lib/openat2.c b/lib/openat2.c
index d8087a0d2a..bfa2a8ee7f 100644
--- a/lib/openat2.c
+++ b/lib/openat2.c
@@ -337,8 +337,6 @@ do_openat2 (int *fd, char const *filename,
if (subfd < 0)
{
int openerr = negative_errno ();
- if (O_RESOLVE_BENEATH && openerr == -ENOTCAPABLE)
- return -EXDEV;
if (! ((openerr == -_GL_OPENAT_ESYMLINK)
| (!!(subflags & O_DIRECTORY) & (openerr == -ENOTDIR))))
return openerr;
@@ -561,16 +559,19 @@ openat2 (int dfd, char const *filename,
int fd;
- /* For speed use openat if it suffices, though it is unlikely a
- caller would use openat2 when openat's simpler API would do. */
+ /* For speed use a single openat if it suffices. */
if (O_RESOLVE_BENEATH ? !(resolve & ~RESOLVE_BENEATH) : !resolve)
{
- if (resolve & RESOLVE_BENEATH)
- flags |= O_RESOLVE_BENEATH;
- fd = openat (dfd, filename, flags, mode);
- if (O_RESOLVE_BENEATH && fd < 0 && errno == ENOTCAPABLE)
- errno = EXDEV;
- return fd;
+ fd = openat (dfd, filename,
+ flags | (resolve ? O_RESOLVE_BENEATH : 0), mode);
+
+ /* Return FD now unless openat failed with an errno that might
+ be an O_RESOLVE_BENEATH failure. On platforms with
+ ENOTCAPABLE that is the errno; otherwise, this is macOS 15
+ where EACCES is the errno. */
+ if (! (fd < 0 && resolve
+ && errno == (ENOTCAPABLE ? ENOTCAPABLE : EACCES)))
+ return fd;
}
fd = dfd;
--
2.51.0