Bruno Haible wrote:
Test results of a Gnulib POSIX testdir on NetBSD 7.0:

Thanks for doing that. Although I don't know about the math problems, here are thoughts on the file-related issues.

FAIL: test-futimens
===================

../../gltests/test-futimens.h:154: assertion 'ctime_compare (&st3, &st2) < 0' 
failed
FAIL test-futimens (exit status: 134)>
> ...
FAIL: test-utimens
==================

../../gltests/test-utimens.h:130: assertion 'ctime_compare (&st3, &st2) < 0' 
failed
FAIL test-utimens (exit status: 134)

These two appear to because futimens and utimens are not properly marking the ctime for update after they change a file timestamp. If I'm right, it's a NetBSD bug that Gnulib cannot easily work around. It's just a guess, though.

FAIL: test-utimensat
====================

../../gltests/test-utimens.h:71: assertion 'func (BASE "file", ts) == -1' failed
FAIL test-utimensat (exit status: 134)

This appears to be because utimensat is not failing with errno==EINVAL for tv_nsec values less than 0 or greater than 999999999 (and not UTIME_OMIT or UTIME_NOW). If I'm right it's a NetBSD bug that Gnulib could work around though this is low priority. It's just a guess, though.


FAIL: test-renameat
===================

../../gltests/test-rename.h:525: assertion 'stat (BASE "file", &st) == 0' failed
FAIL test-renameat (exit status: 134)

FAIL: test-renameat2
====================

These appear to be because Gnulib tests are too strict about renameat. They should allow the NetBSD behavior, because it's better than the behavior that POSIX requires. I installed the first attached patch to try to fix this.

While looking into the utimensat problem I noticed a portability glitch that is unrelated to these NetBSD issues, and fixed that in the second attached patch. It's probably just theoretical.
From 9a6c7d3f6ea7c328e7760458b18fe6dfc6f8806f Mon Sep 17 00:00:00 2001
From: Paul Eggert <egg...@cs.ucla.edu>
Date: Tue, 15 Aug 2017 15:53:50 -0700
Subject: [PATCH 1/2] rename: document+test NetBSD rename

Test failure reported by Bruno Haible in:
http://lists.gnu.org/archive/html/bug-gnulib/2017-08/msg00104.html
This is an area where NetBSD is better-behaved than POSIX,
so allow the NetBSD behavior in tests.
* doc/posix-functions/rename.texi:
* doc/posix-functions/renameat.texi: Document NetBSD behavior.
* tests/test-rename.h (test_rename): Allow NetBSD behavior.
---
 ChangeLog                         | 11 +++++++++++
 doc/posix-functions/rename.texi   | 10 ++++++++++
 doc/posix-functions/renameat.texi | 10 ++++++++++
 tests/test-rename.h               |  8 +++++++-
 4 files changed, 38 insertions(+), 1 deletion(-)

diff --git a/ChangeLog b/ChangeLog
index b07825f..3d87c90 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,14 @@
+2017-08-15  Paul Eggert  <egg...@cs.ucla.edu>
+
+	rename: document+test NetBSD rename
+	Test failure reported by Bruno Haible in:
+	http://lists.gnu.org/archive/html/bug-gnulib/2017-08/msg00104.html
+	This is an area where NetBSD is better-behaved than POSIX,
+	so allow the NetBSD behavior in tests.
+	* doc/posix-functions/rename.texi:
+	* doc/posix-functions/renameat.texi: Document NetBSD behavior.
+	* tests/test-rename.h (test_rename): Allow NetBSD behavior.
+
 2017-08-15  Bruno Haible  <br...@clisp.org>
 
 	renameat: Ensure declaration in <stdio.h> on NetBSD.
diff --git a/doc/posix-functions/rename.texi b/doc/posix-functions/rename.texi
index 1e80656..b024b96 100644
--- a/doc/posix-functions/rename.texi
+++ b/doc/posix-functions/rename.texi
@@ -60,6 +60,16 @@ is counter-intuitive, so on some systems, @code{rename} fails with
 @code{ENOTDIR} if either argument is a symlink with a trailing slash:
 glibc, OpenBSD, Cygwin 1.7.
 @item
+POSIX requires that @code{renameat} do nothing and return 0 if the
+source and destination are hard links to the same file.  This behavior
+is counterintuitive, and on some systems @code{renameat} is a no-op in
+this way only if the source and destination identify the same
+directory entry.  On these systems, for example, although renaming
+@file{./f} to @file{f} is a no-op, renaming @file{f} to @file{g}
+deletes @file{f} when @file{f} and @file{g} are hard links to the same
+file:
+NetBSD.
+@item
 After renaming a non-empty directory over an existing empty directory,
 the old directory name is still visible through the @code{stat} function
 for 30 seconds after the rename, on NFS file systems, on some platforms:
diff --git a/doc/posix-functions/renameat.texi b/doc/posix-functions/renameat.texi
index ee01b89..dd97132 100644
--- a/doc/posix-functions/renameat.texi
+++ b/doc/posix-functions/renameat.texi
@@ -39,6 +39,16 @@ is counter-intuitive, so on some systems, @code{renameat} fails with
 @code{ENOTDIR} if either argument is a symlink with a trailing slash:
 glibc, OpenBSD, Cygwin 1.7.
 @item
+POSIX requires that @code{renameat} do nothing and return 0 if the
+source and destination are hard links to the same file.  This behavior
+is counterintuitive, and on some systems @code{renameat} is a no-op in
+this way only if the source and destination identify the same
+directory entry.  On these systems, for example, although renaming
+@file{./f} to @file{f} is a no-op, renaming @file{f} to @file{g}
+deletes @file{f} when @file{f} and @file{g} are hard links to the same
+file:
+NetBSD.
+@item
 After renaming a non-empty directory over an existing empty directory,
 the old directory name is still visible through the @code{stat} function
 for 30 seconds after the rename, on NFS file systems, on some platforms:
diff --git a/tests/test-rename.h b/tests/test-rename.h
index 93a1041..010d58d 100644
--- a/tests/test-rename.h
+++ b/tests/test-rename.h
@@ -522,7 +522,13 @@ test_rename (int (*func) (char const *, char const *), bool print)
   { /* File onto hard link.  */
     ASSERT (func (BASE "file", BASE "file2") == 0);
     memset (&st, 0, sizeof st);
-    ASSERT (stat (BASE "file", &st) == 0);
+    if (stat (BASE "file", &st) != 0)
+      {
+        /* This can happen on NetBSD.  */
+        ASSERT (errno == ENOENT);
+        ASSERT (link (BASE "file2", BASE "file") == 0);
+        ASSERT (stat (BASE "file", &st) == 0);
+      }
     ASSERT (st.st_size == 2);
     memset (&st, 0, sizeof st);
     ASSERT (stat (BASE "file2", &st) == 0);
-- 
2.7.4

From 6536b952f68e49259420ee1924a238b5aa3feff3 Mon Sep 17 00:00:00 2001
From: Paul Eggert <egg...@cs.ucla.edu>
Date: Tue, 15 Aug 2017 16:47:22 -0700
Subject: [PATCH 2/2] =?UTF-8?q?futimens:=20don=E2=80=99t=20assume=20struct?=
 =?UTF-8?q?=20timespec=20layout?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* m4/futimens.m4 (gl_FUNC_FUTIMENS):
* m4/utimensat.m4 (gl_FUNC_UTIMENSAT):
* tests/test-fdutimensat.c (main):
* tests/test-futimens.h (test_futimens):
* tests/test-lutimens.h (test_lutimens):
* tests/test-utimens.h (test_utimens):
* tests/test-utimensat.c (main):
Don’t assume that struct timespec is a two-member structure in
tv_sec, tv_nsec order.  Although this is true on all platforms we
know about, POSIX does not guarantee it.
---
 ChangeLog                | 12 ++++++++++++
 m4/futimens.m4           |  6 +++++-
 m4/utimensat.m4          | 12 ++++++++++--
 tests/test-fdutimensat.c |  5 ++++-
 tests/test-futimens.h    | 24 ++++++++++++++++++++----
 tests/test-lutimens.h    | 39 ++++++++++++++++++++++++++++++++-------
 tests/test-utimens.h     | 34 ++++++++++++++++++++++++++++------
 tests/test-utimensat.c   |  6 +++++-
 8 files changed, 116 insertions(+), 22 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index 3d87c90..bc0c169 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,17 @@
 2017-08-15  Paul Eggert  <egg...@cs.ucla.edu>
 
+	futimens: don’t assume struct timespec layout
+	* m4/futimens.m4 (gl_FUNC_FUTIMENS):
+	* m4/utimensat.m4 (gl_FUNC_UTIMENSAT):
+	* tests/test-fdutimensat.c (main):
+	* tests/test-futimens.h (test_futimens):
+	* tests/test-lutimens.h (test_lutimens):
+	* tests/test-utimens.h (test_utimens):
+	* tests/test-utimensat.c (main):
+	Don’t assume that struct timespec is a two-member structure in
+	tv_sec, tv_nsec order.  Although this is true on all platforms we
+	know about, POSIX does not guarantee it.
+
 	rename: document+test NetBSD rename
 	Test failure reported by Bruno Haible in:
 	http://lists.gnu.org/archive/html/bug-gnulib/2017-08/msg00104.html
diff --git a/m4/futimens.m4 b/m4/futimens.m4
index 12bed98..d68bff3 100644
--- a/m4/futimens.m4
+++ b/m4/futimens.m4
@@ -23,10 +23,14 @@ AC_DEFUN([gl_FUNC_FUTIMENS],
 #include <sys/stat.h>
 #include <unistd.h>
 #include <errno.h>
-]], [[struct timespec ts[2] = { { 1, UTIME_OMIT }, { 1, UTIME_NOW } };
+]], [[struct timespec ts[2];
       int fd = creat ("conftest.file", 0600);
       struct stat st;
       if (fd < 0) return 1;
+      ts[0].tv_sec = 1;
+      ts[0].tv_nsec = UTIME_OMIT;
+      ts[1].tv_sec = 1;
+      ts[1].tv_nsec = UTIME_NOW;
       errno = 0;
       if (futimens (AT_FDCWD, NULL) == 0) return 2;
       if (errno != EBADF) return 3;
diff --git a/m4/utimensat.m4 b/m4/utimensat.m4
index b4666c0..78da3cc 100644
--- a/m4/utimensat.m4
+++ b/m4/utimensat.m4
@@ -34,14 +34,22 @@ AC_DEFUN([gl_FUNC_UTIMENSAT],
               }
               /* Test whether UTIME_NOW and UTIME_OMIT work.  */
               {
-                struct timespec ts[2] = { { 1, UTIME_OMIT }, { 1, UTIME_NOW } };
+                struct timespec ts[2];
+                ts[0].tv_sec = 1;
+                ts[0].tv_nsec = UTIME_OMIT;
+                ts[1].tv_sec = 1;
+                ts[1].tv_nsec = UTIME_NOW;
                 if (utimensat (AT_FDCWD, f, ts, 0))
                   result |= 4;
               }
               sleep (1);
               {
-                struct timespec ts[2] = { { 1, UTIME_NOW }, { 1, UTIME_OMIT } };
                 struct stat st;
+                struct timespec ts[2];
+                ts[0].tv_sec = 1;
+                ts[0].tv_nsec = UTIME_NOW;
+                ts[1].tv_sec = 1;
+                ts[1].tv_nsec = UTIME_OMIT;
                 if (utimensat (AT_FDCWD, f, ts, 0))
                   result |= 8;
                 if (stat (f, &st))
diff --git a/tests/test-fdutimensat.c b/tests/test-fdutimensat.c
index fa35bc4..b42cba3 100644
--- a/tests/test-fdutimensat.c
+++ b/tests/test-fdutimensat.c
@@ -125,8 +125,11 @@ main (void)
   ASSERT (fdutimensat (AT_FDCWD, fd, ".", NULL, 0) == -1);
   ASSERT (errno == ENOTDIR);
   {
-    struct timespec ts[2] = { { Y2K, 0 }, { Y2K, 0 } };
+    struct timespec ts[2];
     struct stat st;
+    ts[0].tv_sec = Y2K;
+    ts[0].tv_nsec = 0;
+    ts[1] = ts[0];
     ASSERT (fdutimensat (fd, dfd, BASE "dir/file", ts, 0) == 0);
     ASSERT (stat ("file", &st) == 0);
     ASSERT (st.st_atime == Y2K);
diff --git a/tests/test-futimens.h b/tests/test-futimens.h
index 40bcb9e..727be3b 100644
--- a/tests/test-futimens.h
+++ b/tests/test-futimens.h
@@ -97,13 +97,21 @@ test_futimens (int (*func) (int, struct timespec const *),
     ASSERT (errno == EBADF);
   }
   {
-    struct timespec ts[2] = { { Y2K, UTIME_BOGUS_POS }, { Y2K, 0 } };
+    struct timespec ts[2];
+    ts[0].tv_sec = Y2K;
+    ts[0].tv_nsec = UTIME_BOGUS_POS;
+    ts[1].tv_sec = Y2K;
+    ts[1].tv_nsec = 0;
     errno = 0;
     ASSERT (func (fd, ts) == -1);
     ASSERT (errno == EINVAL);
   }
   {
-    struct timespec ts[2] = { { Y2K, 0 }, { Y2K, UTIME_BOGUS_NEG } };
+    struct timespec ts[2];
+    ts[0].tv_sec = Y2K;
+    ts[0].tv_nsec = 0;
+    ts[1].tv_sec = Y2K;
+    ts[1].tv_nsec = UTIME_BOGUS_NEG;
     errno = 0;
     ASSERT (func (fd, ts) == -1);
     ASSERT (errno == EINVAL);
@@ -115,7 +123,11 @@ test_futimens (int (*func) (int, struct timespec const *),
 
   /* Set both times.  */
   {
-    struct timespec ts[2] = { { Y2K, BILLION / 2 - 1 }, { Y2K, BILLION - 1 } };
+    struct timespec ts[2];
+    ts[0].tv_sec = Y2K;
+    ts[0].tv_nsec = BILLION / 2 - 1;
+    ts[1].tv_sec = Y2K;
+    ts[1].tv_nsec = BILLION - 1;
     ASSERT (func (fd, ts) == 0);
     ASSERT (fstat (fd, &st2) == 0);
     ASSERT (st2.st_atime == Y2K);
@@ -131,7 +143,11 @@ test_futimens (int (*func) (int, struct timespec const *),
   /* Play with UTIME_OMIT, UTIME_NOW.  */
   {
     struct stat st3;
-    struct timespec ts[2] = { { BILLION, UTIME_OMIT }, { 0, UTIME_NOW } };
+    struct timespec ts[2];
+    ts[0].tv_sec = BILLION;
+    ts[0].tv_nsec = UTIME_OMIT;
+    ts[1].tv_sec = 0;
+    ts[1].tv_nsec = UTIME_NOW;
     nap ();
     ASSERT (func (fd, ts) == 0);
     ASSERT (fstat (fd, &st3) == 0);
diff --git a/tests/test-lutimens.h b/tests/test-lutimens.h
index b590855..e18f249 100644
--- a/tests/test-lutimens.h
+++ b/tests/test-lutimens.h
@@ -44,7 +44,10 @@ test_lutimens (int (*func) (char const *, struct timespec const *), bool print)
   ASSERT (st1.st_atime != Y2K);
   ASSERT (st1.st_mtime != Y2K);
   {
-    struct timespec ts[2] = { { Y2K, 0 }, { Y2K, 0 } };
+    struct timespec ts[2];
+    ts[0].tv_sec = Y2K;
+    ts[0].tv_nsec = 0;
+    ts[1] = ts[0];
     errno = 0;
     ASSERT (func (BASE "file/", ts) == -1);
     ASSERT (errno == ENOTDIR);
@@ -53,7 +56,10 @@ test_lutimens (int (*func) (char const *, struct timespec const *), bool print)
     ASSERT (st1.st_mtime == st2.st_mtime);
   }
   {
-    struct timespec ts[2] = { { Y2K, 0 }, { Y2K, 0 } };
+    struct timespec ts[2];
+    ts[0].tv_sec = Y2K;
+    ts[0].tv_nsec = 0;
+    ts[1] = ts[0];
     nap ();
     ASSERT (func (BASE "file", ts) == 0);
   }
@@ -106,13 +112,21 @@ test_lutimens (int (*func) (char const *, struct timespec const *), bool print)
 
   /* Invalid arguments.  */
   {
-    struct timespec ts[2] = { { Y2K, UTIME_BOGUS_POS }, { Y2K, 0 } };
+    struct timespec ts[2];
+    ts[0].tv_sec = Y2K;
+    ts[0].tv_nsec = UTIME_BOGUS_POS;
+    ts[1].tv_sec = Y2K;
+    ts[1].tv_nsec = 0;
     errno = 0;
     ASSERT (func (BASE "link", ts) == -1);
     ASSERT (errno == EINVAL);
   }
   {
-    struct timespec ts[2] = { { Y2K, 0 }, { Y2K, UTIME_BOGUS_NEG } };
+    struct timespec ts[2];
+    ts[0].tv_sec = Y2K;
+    ts[0].tv_nsec = 0;
+    ts[1].tv_sec = Y2K;
+    ts[1].tv_nsec = UTIME_BOGUS_NEG;
     errno = 0;
     ASSERT (func (BASE "link", ts) == -1);
     ASSERT (errno == EINVAL);
@@ -127,7 +141,11 @@ test_lutimens (int (*func) (char const *, struct timespec const *), bool print)
 
   /* Set both times.  */
   {
-    struct timespec ts[2] = { { Y2K, BILLION / 2 - 1 }, { Y2K, BILLION - 1 } };
+    struct timespec ts[2];
+    ts[0].tv_sec = Y2K;
+    ts[0].tv_nsec = BILLION / 2 - 1;
+    ts[1].tv_sec = Y2K;
+    ts[1].tv_nsec = BILLION - 1;
     nap ();
     ASSERT (func (BASE "link", ts) == 0);
     ASSERT (lstat (BASE "link", &st2) == 0);
@@ -147,7 +165,11 @@ test_lutimens (int (*func) (char const *, struct timespec const *), bool print)
   /* Play with UTIME_OMIT, UTIME_NOW.  */
   {
     struct stat st3;
-    struct timespec ts[2] = { { BILLION, UTIME_OMIT }, { 0, UTIME_NOW } };
+    struct timespec ts[2];
+    ts[0].tv_sec = BILLION;
+    ts[0].tv_nsec = UTIME_OMIT;
+    ts[1].tv_sec = 0;
+    ts[1].tv_nsec = UTIME_NOW;
     nap ();
     ASSERT (func (BASE "link", ts) == 0);
     ASSERT (lstat (BASE "link", &st3) == 0);
@@ -181,7 +203,10 @@ test_lutimens (int (*func) (char const *, struct timespec const *), bool print)
   ASSERT (symlink (BASE "dir", BASE "link") == 0);
   ASSERT (mkdir (BASE "dir", 0700) == 0);
   {
-    struct timespec ts[2] = { { Y2K, 0 }, { Y2K, 0 } };
+    struct timespec ts[2];
+    ts[0].tv_sec = Y2K;
+    ts[0].tv_nsec = 0;
+    ts[1] = ts[0];
     ASSERT (func (BASE "link/", ts) == 0);
   }
   /* On cygwin 1.5, stat() changes atime of directories, so only check
diff --git a/tests/test-utimens.h b/tests/test-utimens.h
index 8af2efe..ff53875 100644
--- a/tests/test-utimens.h
+++ b/tests/test-utimens.h
@@ -66,19 +66,30 @@ test_utimens (int (*func) (char const *, struct timespec const *), bool print)
   ASSERT (func ("", NULL) == -1);
   ASSERT (errno == ENOENT);
   {
-    struct timespec ts[2] = { { Y2K, UTIME_BOGUS_POS }, { Y2K, 0 } };
+    struct timespec ts[2];
+    ts[0].tv_sec = Y2K;
+    ts[0].tv_nsec = UTIME_BOGUS_POS;
+    ts[1].tv_sec = Y2K;
+    ts[1].tv_nsec = 0;
     errno = 0;
     ASSERT (func (BASE "file", ts) == -1);
     ASSERT (errno == EINVAL);
   }
   {
-    struct timespec ts[2] = { { Y2K, 0 }, { Y2K, UTIME_BOGUS_NEG } };
+    struct timespec ts[2];
+    ts[0].tv_sec = Y2K;
+    ts[0].tv_nsec = 0;
+    ts[1].tv_sec = Y2K;
+    ts[1].tv_nsec = UTIME_BOGUS_NEG;
     errno = 0;
     ASSERT (func (BASE "file", ts) == -1);
     ASSERT (errno == EINVAL);
   }
   {
-    struct timespec ts[2] = { { Y2K, 0 }, { Y2K, 0 } };
+    struct timespec ts[2];
+    ts[0].tv_sec = Y2K;
+    ts[0].tv_nsec = 0;
+    ts[1] = ts[0];
     errno = 0;
     ASSERT (func (BASE "file/", ts) == -1);
     ASSERT (errno == ENOTDIR || errno == EINVAL);
@@ -90,7 +101,11 @@ test_utimens (int (*func) (char const *, struct timespec const *), bool print)
 
   /* Set both times.  */
   {
-    struct timespec ts[2] = { { Y2K, BILLION / 2 - 1 }, { Y2K, BILLION - 1 } };
+    struct timespec ts[2];
+    ts[0].tv_sec = Y2K;
+    ts[0].tv_nsec = BILLION / 2 - 1;
+    ts[1].tv_sec = Y2K;
+    ts[1].tv_nsec = BILLION - 1;
     ASSERT (func (BASE "file", ts) == 0);
     ASSERT (stat (BASE "file", &st2) == 0);
     ASSERT (st2.st_atime == Y2K);
@@ -106,7 +121,11 @@ test_utimens (int (*func) (char const *, struct timespec const *), bool print)
   /* Play with UTIME_OMIT, UTIME_NOW.  */
   {
     struct stat st3;
-    struct timespec ts[2] = { { BILLION, UTIME_OMIT }, { 0, UTIME_NOW } };
+    struct timespec ts[2];
+    ts[0].tv_sec = BILLION;
+    ts[0].tv_nsec = UTIME_OMIT;
+    ts[1].tv_sec = 0;
+    ts[1].tv_nsec = UTIME_NOW;
     nap ();
     ASSERT (func (BASE "file", ts) == 0);
     ASSERT (stat (BASE "file", &st3) == 0);
@@ -145,7 +164,10 @@ test_utimens (int (*func) (char const *, struct timespec const *), bool print)
   ASSERT (func (BASE "link/", NULL) == -1);
   ASSERT (errno == ENOTDIR);
   {
-    struct timespec ts[2] = { { Y2K, 0 }, { Y2K, 0 } };
+    struct timespec ts[2];
+    ts[0].tv_sec = Y2K;
+    ts[0].tv_nsec = 0;
+    ts[1] = ts[0];
     ASSERT (func (BASE "link", ts) == 0);
     ASSERT (lstat (BASE "link", &st2) == 0);
     /* Can't compare atimes, since lstat() changes symlink atime on cygwin.  */
diff --git a/tests/test-utimensat.c b/tests/test-utimensat.c
index de5ae5a..e5a3685 100644
--- a/tests/test-utimensat.c
+++ b/tests/test-utimensat.c
@@ -101,8 +101,12 @@ main (void)
   ASSERT (utimensat (fd, ".", NULL, 0) == -1);
   ASSERT (errno == ENOTDIR);
   {
-    struct timespec ts[2] = { { Y2K, 0 }, { Y2K, 0 } };
+    struct timespec ts[2];
     struct stat st;
+    ts[0].tv_sec = Y2K;
+    ts[0].tv_nsec = 0;
+    ts[1].tv_sec = Y2K;
+    ts[1].tv_nsec = 0;
     ASSERT (utimensat (dfd, BASE "dir/file", ts, AT_SYMLINK_NOFOLLOW) == 0);
     ASSERT (stat ("file", &st) == 0);
     ASSERT (st.st_atime == Y2K);
-- 
2.7.4

Reply via email to