On 2025-10-28 14:46, Bruno Haible wrote:

Thanks for taking the trouble of writing this unit test.

You're welcome.

Though I am starting to have my doubts about openat2 except as a base to implement a nicer API. The openat2 API works only as an openat extension, whereas what tar needs is an extension that also works for fstatat, fchmodat, mkdirat, etc.


1) On Cygwin 3.x:

Although I don't have easy access to that platform I looked at its source code and see what may be the issue: its O_TMPFILE is a single bit whereas GNU/Linux's is a hacky combination that includes O_DIRECTORY. I installed the 2nd attached patch to try to fix that issue. This may not suffice for Cygwin but the patch needed to be done anyway.


2) On FreeBSD 14.0 and NetBSD 10.0:

I installed the first attached patch to fix that. Tested on cfarm240, which is FreeBSD 15.0-CURRENT.


3) On MSVC (but not on mingw):
...
I think I can deal with 3), but I'll let you deal with 1) and 2) first.

Thanks for doing this testing.
From c706216fec5a509bf9b1214892de01aa9303ade0 Mon Sep 17 00:00:00 2001
From: Paul Eggert <[email protected]>
Date: Tue, 28 Oct 2025 17:03:28 -0700
Subject: [PATCH 1/2] openat2-tests: port to FreeBSD, NetBSD

Problem reported by Bruno Haible in:
https://lists.gnu.org/r/bug-gnulib/2025-10/msg00117.html
* tests/test-openat2.c (is_nofollow_error): New function.
(do_test_resolve): Use it.
---
 ChangeLog            |  8 ++++++++
 tests/test-openat2.c | 26 ++++++++++++++++++++------
 2 files changed, 28 insertions(+), 6 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index 415568a156..3ccd074a4c 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,11 @@
+2025-10-28  Paul Eggert  <[email protected]>
+
+	openat2-tests: port to FreeBSD, NetBSD
+	Problem reported by Bruno Haible in:
+	https://lists.gnu.org/r/bug-gnulib/2025-10/msg00117.html
+	* tests/test-openat2.c (is_nofollow_error): New function.
+	(do_test_resolve): Use it.
+
 2025-10-28  Bruno Haible  <[email protected]>
 
 	posix_spawn_file_actions_addclose: Fix test failure on NetBSD 10.0.
diff --git a/tests/test-openat2.c b/tests/test-openat2.c
index 0ac7aca6f3..fc93a7a9a9 100644
--- a/tests/test-openat2.c
+++ b/tests/test-openat2.c
@@ -317,6 +317,20 @@ do_test_flags (void)
     }
 }
 
+static bool
+is_nofollow_error (int err)
+{
+#ifdef EFTYPE /* NetBSD openat+O_NOFOLLOW on symlink */
+  if (err == EFTYPE)
+    return true;
+#endif
+#ifdef EMLINK /* FreeBSD openat+O_NOFOLLOW on symlink */
+  if (err == EMLINK)
+    return true;
+#endif
+  return err == ELOOP;
+}
+
 static void
 do_test_resolve (void)
 {
@@ -331,7 +345,7 @@ do_test_resolve (void)
                    .resolve = RESOLVE_BENEATH | RESOLVE_NO_SYMLINKS,
                  }),
 		sizeof (struct open_how));
-  ASSERT (errno == ELOOP || errno == EXDEV);
+  ASSERT ((errno == EXDEV) | is_nofollow_error (errno));
   ASSERT (fd == -1);
 
   /* Same as before, ESCAPING_LINK_2 links to ESCAPING_LINK.  */
@@ -342,7 +356,7 @@ do_test_resolve (void)
                    .resolve = RESOLVE_BENEATH | RESOLVE_NO_SYMLINKS,
                  }),
 		sizeof (struct open_how));
-  ASSERT (errno == ELOOP || errno == EXDEV);
+  ASSERT ((errno == EXDEV) | is_nofollow_error (errno));
   ASSERT (fd == -1);
 
   /* ESCAPING_LINK links to the temporary directory itself (dfd).  */
@@ -353,7 +367,7 @@ do_test_resolve (void)
                    .resolve = RESOLVE_BENEATH | RESOLVE_NO_SYMLINKS,
                  }),
 		sizeof (struct open_how));
-  ASSERT (errno == ELOOP || errno == EXDEV);
+  ASSERT ((errno == EXDEV) | is_nofollow_error (errno));
   ASSERT (fd == -1);
 
   /* Although it points to a valid file in same path, the link refers to
@@ -365,7 +379,7 @@ do_test_resolve (void)
                    .resolve = RESOLVE_BENEATH | RESOLVE_NO_SYMLINKS,
                  }),
 		sizeof (struct open_how));
-  ASSERT (errno == ELOOP || errno == EXDEV);
+  ASSERT ((errno == EXDEV) | is_nofollow_error (errno));
   ASSERT (fd == -1);
 
   fd = openat2 (dfd,
@@ -375,7 +389,7 @@ do_test_resolve (void)
                    .resolve = RESOLVE_BENEATH | RESOLVE_NO_SYMLINKS,
                  }),
 		sizeof (struct open_how));
-  ASSERT (errno == ELOOP);
+  ASSERT (is_nofollow_error (errno));
   ASSERT (fd == -1);
 
   fd = openat2 (dfd,
@@ -385,7 +399,7 @@ do_test_resolve (void)
                    .resolve = RESOLVE_IN_ROOT | RESOLVE_NO_SYMLINKS,
                  }),
 	       sizeof (struct open_how));
-  ASSERT (errno == ELOOP | errno == ENOENT);
+  ASSERT ((errno == ENOENT) | is_nofollow_error (errno));
   ASSERT (fd == -1);
 
   {
-- 
2.48.1

From c6502cda83752ff2235d2064c213e7a9e2214201 Mon Sep 17 00:00:00 2001
From: Paul Eggert <[email protected]>
Date: Tue, 28 Oct 2025 21:15:11 -0700
Subject: [PATCH 2/2] openat2: port O_TMPFILE check to non-GNU/Linux
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* lib/openat2.c (openat2): Don’t assume !!(O_TMPFILE & O_DIRECTORY).
Although true on GNU/Linux, it is a hack and might not be true on
other systems such as Cygwin.
---
 ChangeLog     |  5 +++++
 lib/openat2.c | 10 ++++------
 2 files changed, 9 insertions(+), 6 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index 3ccd074a4c..b475160780 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,10 @@
 2025-10-28  Paul Eggert  <[email protected]>
 
+	openat2: port O_TMPFILE check to non-GNU/Linux
+	* lib/openat2.c (openat2): Don’t assume !!(O_TMPFILE & O_DIRECTORY).
+	Although true on GNU/Linux, it is a hack and might not be true on
+	other systems such as Cygwin.
+
 	openat2-tests: port to FreeBSD, NetBSD
 	Problem reported by Bruno Haible in:
 	https://lists.gnu.org/r/bug-gnulib/2025-10/msg00117.html
diff --git a/lib/openat2.c b/lib/openat2.c
index ba5f579716..ee64d05836 100644
--- a/lib/openat2.c
+++ b/lib/openat2.c
@@ -512,13 +512,11 @@ openat2 (int dfd, char const *filename,
                        | O_NOLINK | O_NOLINKS | O_NONBLOCK | O_NOTRANS
                        | O_RSYNC | O_SYNC
                        | O_TEXT | O_TMPFILE | O_TRUNC | O_TTY_INIT))))
-           | ((how->flags & (O_DIRECTORY | O_CREAT))
-              == (O_DIRECTORY | O_CREAT))
+           | (!!(how->flags & O_CREAT)
+              & !!(how->flags & (O_DIRECTORY | O_TMPFILE)))
            | (!!(how->flags & O_TMPFILE & ~O_DIRECTORY)
-              & ((how->flags & (O_ACCMODE | O_DIRECTORY))
-                 != (O_WRONLY | O_DIRECTORY))
-              & ((how->flags & (O_ACCMODE | O_DIRECTORY))
-                 != (O_RDWR | O_DIRECTORY)))
+              & ((how->flags & (O_ACCMODE | O_PATH)) != O_WRONLY)
+              & ((how->flags & (O_ACCMODE | O_PATH)) != O_RDWR))
            | (how->mode
               & ~ (how->flags & (O_CREAT | (O_TMPFILE & ~O_DIRECTORY))
                    ? (S_ISUID | S_ISGID | S_ISVTX
-- 
2.48.1

Reply via email to