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