On macOS 10.13, I'm seeing test failures: FAIL: test-fdutimensat ======================
./test-utimens.h:75: assertion 'func (BASE "file", ts) == -1' failed FAIL test-fdutimensat (exit status: 134) FAIL: test-utimensat ==================== ./test-utimens.h:75: assertion 'func (BASE "file", ts) == -1' failed FAIL test-utimensat (exit status: 134) Once I add the validation of the tv_nsec values to the Gnulib code, another failure remains: FAIL: test-fdutimensat ====================== ./test-utimens.h:164: assertion 'func (BASE "link/", NULL) == -1' failed FAIL test-fdutimensat (exit status: 134) FAIL: test-utimensat ==================== ./test-utimens.h:164: assertion 'func (BASE "link/", NULL) == -1' failed FAIL test-utimensat (exit status: 134) A missing recognition of a trailing slash, like in the previous patch. This patch fixes both issues: 2021-01-02 Bruno Haible <br...@clisp.org> utimensat: Fix test failures on macOS 10.13. * lib/utimensat.c: Include <string.h>, <sys/stat.h>. (rpl_utimensat): Check against invalid tv_nsec values. Before calling utimensat, recognize a filename ending in a slash that does not point to a directory. diff --git a/lib/utimensat.c b/lib/utimensat.c index 2cea64f..1daff88 100644 --- a/lib/utimensat.c +++ b/lib/utimensat.c @@ -24,6 +24,8 @@ #include <errno.h> #include <fcntl.h> #include <stdlib.h> +#include <string.h> +#include <sys/stat.h> #include "stat-time.h" #include "timespec.h" @@ -106,6 +108,34 @@ rpl_utimensat (int fd, char const *file, struct timespec const times[2], } # endif # endif +# if defined __APPLE__ && defined __MACH__ + /* macOS 10.13 does not reject invalid tv_nsec values either. */ + if (times + && ((times[0].tv_nsec != UTIME_OMIT + && times[0].tv_nsec != UTIME_NOW + && ! (0 <= times[0].tv_nsec + && times[0].tv_nsec < TIMESPEC_HZ)) + || (times[1].tv_nsec != UTIME_OMIT + && times[1].tv_nsec != UTIME_NOW + && ! (0 <= times[1].tv_nsec + && times[1].tv_nsec < TIMESPEC_HZ)))) + { + errno = EINVAL; + return -1; + } + size_t len = strlen (file); + if (len > 0 && file[len - 1] == '/') + { + struct stat statbuf; + if (fstatat (fd, file, &statbuf, 0) < 0) + return -1; + if (!S_ISDIR (statbuf.st_mode)) + { + errno = ENOTDIR; + return -1; + } + } +# endif result = utimensat (fd, file, times, flag); /* Linux kernel 2.6.25 has a bug where it returns EINVAL for UTIME_NOW or UTIME_OMIT with non-zero tv_sec, which