On AIX 7.2, I see these test failures: FAIL: test-utimens ==================
../../gltests/test-utimens.h:94: assertion 'func (BASE "file/", ts) == -1' failed FAIL test-utimens (exit status: 134) FAIL: test-utimensat ==================== ../../gltests/test-utimens.h:94: assertion 'func (BASE "file/", ts) == -1' failed FAIL test-utimensat (exit status: 134) FAIL: test-fdutimensat ====================== ../../gltests/test-utimens.h:94: assertion 'func (BASE "file/", ts) == -1' failed FAIL test-fdutimensat (exit status: 134) This patch makes them go away, more precisely, it makes these tests fail later. 2021-01-05 Bruno Haible <br...@clisp.org> utimensat: Work around trailing slash bug in utimensat() on AIX 7.2. * m4/utimensat.m4 (gl_FUNC_UTIMENSAT): Require AC_CANONICAL_HOST. Add a test for trailing slash handling. Improve cross-compilation guesses. Conditionally define HAVE_NEARLY_WORKING_UTIMENSAT. * lib/utimensat.c (rpl_utimensat): Add alternative implementation when HAVE_NEARLY_WORKING_UTIMENSAT is defined. * lib/utimens.c: Use the overridden utimensat when HAVE_NEARLY_WORKING_UTIMENSAT is defined. * doc/posix-functions/utimensat.texi: Mention the AIX bug. diff --git a/doc/glibc-functions/futimesat.texi b/doc/glibc-functions/futimesat.texi index 537bf42..9c11368 100644 --- a/doc/glibc-functions/futimesat.texi +++ b/doc/glibc-functions/futimesat.texi @@ -16,7 +16,7 @@ Portability problems not fixed by Gnulib: This function is missing on some platforms: glibc 2.3.6, Mac OS X 10.13, FreeBSD 6.0, NetBSD 9.0, OpenBSD 6.7, Minix 3.1.8, AIX 5.1, HP-UX 11, IRIX 6.5, Cygwin 1.5.x, mingw, MSVC 14, Android 7.1. @item -On some platforms, this function mis-handles trailing slash: +On some platforms, this function mis-handles a trailing slash: Solaris 9. @item This function cannot set full timestamp resolution. Use diff --git a/doc/posix-functions/utime.texi b/doc/posix-functions/utime.texi index 4e41e5d..79e97f5 100644 --- a/doc/posix-functions/utime.texi +++ b/doc/posix-functions/utime.texi @@ -25,7 +25,7 @@ Mac OS X 10.13. Portability problems not fixed by Gnulib: @itemize @item -On some platforms, this function mis-handles trailing slash: +On some platforms, this function mis-handles a trailing slash: Solaris 9. @item This function cannot set full timestamp resolution. Use diff --git a/doc/posix-functions/utimensat.texi b/doc/posix-functions/utimensat.texi index 92c2b8b..03a2912 100644 --- a/doc/posix-functions/utimensat.texi +++ b/doc/posix-functions/utimensat.texi @@ -36,6 +36,9 @@ Linux kernel 2.6.32, Mac OS X 10.13, NetBSD 9.0, Solaris 11.1. Out-of-range values of @code{tv_nsec} do not lead to a failure on some platforms: Linux kernel 2.6.22.19 on hppa. +@item +On some platforms, this function mis-handles a trailing slash: +AIX 7.2. @end itemize Portability problems not fixed by Gnulib: diff --git a/doc/posix-functions/utimes.texi b/doc/posix-functions/utimes.texi index 5505906..13ceb99 100644 --- a/doc/posix-functions/utimes.texi +++ b/doc/posix-functions/utimes.texi @@ -16,7 +16,7 @@ Portability problems not fixed by Gnulib: This function is missing on some platforms: Minix 3.1.8, mingw, MSVC 14. @item -On some platforms, this function mis-handles trailing slash: +On some platforms, this function mis-handles a trailing slash: FreeBSD 7.2, Solaris 9. @item This function cannot set full timestamp resolution. In particular, diff --git a/lib/utimens.c b/lib/utimens.c index 677e833..44d1ea0 100644 --- a/lib/utimens.c +++ b/lib/utimens.c @@ -53,7 +53,9 @@ /* Avoid recursion with rpl_futimens or rpl_utimensat. */ #undef futimens -#undef utimensat +#if !HAVE_NEARLY_WORKING_UTIMENSAT +# undef utimensat +#endif /* Solaris 9 mistakenly succeeds when given a non-directory with a trailing slash. Force the use of rpl_stat for a fix. */ diff --git a/lib/utimensat.c b/lib/utimensat.c index 1daff88..9fdecd6 100644 --- a/lib/utimensat.c +++ b/lib/utimensat.c @@ -31,9 +31,33 @@ #include "timespec.h" #include "utimens.h" -#if HAVE_UTIMENSAT +#if HAVE_NEARLY_WORKING_UTIMENSAT +/* Use the original utimensat(), but correct the trailing slash handling. */ +int +rpl_utimensat (int fd, char const *file, struct timespec const times[2], + int flag) # undef utimensat +{ + size_t len = strlen (file); + if (len && file[len - 1] == '/') + { + struct stat st; + if (fstatat (fd, file, &st, flag & AT_SYMLINK_NOFOLLOW) < 0) + return -1; + if (!S_ISDIR (st.st_mode)) + { + errno = ENOTDIR; + return -1; + } + } + + return utimensat (fd, file, times, flag); +} + +#else + +# if HAVE_UTIMENSAT /* If we have a native utimensat, but are compiling this file, then utimensat was defined to rpl_utimensat by our replacement @@ -44,24 +68,25 @@ local_utimensat provides the fallback manipulation. */ static int local_utimensat (int, char const *, struct timespec const[2], int); -# define AT_FUNC_NAME local_utimensat +# define AT_FUNC_NAME local_utimensat /* Like utimensat, but work around native bugs. */ int rpl_utimensat (int fd, char const *file, struct timespec const times[2], int flag) +# undef utimensat { -# if defined __linux__ || defined __sun +# if defined __linux__ || defined __sun struct timespec ts[2]; -# endif +# endif /* See comments in utimens.c for details. */ static int utimensat_works_really; /* 0 = unknown, 1 = yes, -1 = no. */ if (0 <= utimensat_works_really) { int result; -# if defined __linux__ || defined __sun +# if defined __linux__ || defined __sun struct stat st; /* As recently as Linux kernel 2.6.32 (Dec 2009), several file systems (xfs, ntfs-3g) have bugs with a single UTIME_OMIT, @@ -92,7 +117,7 @@ rpl_utimensat (int fd, char const *file, struct timespec const times[2], ts[1] = times[1]; times = ts; } -# ifdef __hppa__ +# ifdef __hppa__ /* Linux kernel 2.6.22.19 on hppa does not reject invalid tv_nsec values. */ else if (times @@ -106,9 +131,9 @@ rpl_utimensat (int fd, char const *file, struct timespec const times[2], errno = EINVAL; return -1; } +# endif # endif -# endif -# if defined __APPLE__ && defined __MACH__ +# if defined __APPLE__ && defined __MACH__ /* macOS 10.13 does not reject invalid tv_nsec values either. */ if (times && ((times[0].tv_nsec != UTIME_OMIT @@ -135,7 +160,7 @@ rpl_utimensat (int fd, char const *file, struct timespec const times[2], return -1; } } -# endif +# 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 @@ -159,11 +184,11 @@ rpl_utimensat (int fd, char const *file, struct timespec const times[2], return local_utimensat (fd, file, times, flag); } -#else /* !HAVE_UTIMENSAT */ +# else /* !HAVE_UTIMENSAT */ -# define AT_FUNC_NAME utimensat +# define AT_FUNC_NAME utimensat -#endif /* !HAVE_UTIMENSAT */ +# endif /* !HAVE_UTIMENSAT */ /* Set the access and modification timestamps of FILE to be TIMESPEC[0] and TIMESPEC[1], respectively; relative to directory @@ -176,15 +201,17 @@ rpl_utimensat (int fd, char const *file, struct timespec const times[2], Return 0 on success, -1 (setting errno) on failure. */ /* AT_FUNC_NAME is now utimensat or local_utimensat. */ -#define AT_FUNC_F1 lutimens -#define AT_FUNC_F2 utimens -#define AT_FUNC_USE_F1_COND AT_SYMLINK_NOFOLLOW -#define AT_FUNC_POST_FILE_PARAM_DECLS , struct timespec const ts[2], int flag -#define AT_FUNC_POST_FILE_ARGS , ts -#include "at-func.c" -#undef AT_FUNC_NAME -#undef AT_FUNC_F1 -#undef AT_FUNC_F2 -#undef AT_FUNC_USE_F1_COND -#undef AT_FUNC_POST_FILE_PARAM_DECLS -#undef AT_FUNC_POST_FILE_ARGS +# define AT_FUNC_F1 lutimens +# define AT_FUNC_F2 utimens +# define AT_FUNC_USE_F1_COND AT_SYMLINK_NOFOLLOW +# define AT_FUNC_POST_FILE_PARAM_DECLS , struct timespec const ts[2], int flag +# define AT_FUNC_POST_FILE_ARGS , ts +# include "at-func.c" +# undef AT_FUNC_NAME +# undef AT_FUNC_F1 +# undef AT_FUNC_F2 +# undef AT_FUNC_USE_F1_COND +# undef AT_FUNC_POST_FILE_PARAM_DECLS +# undef AT_FUNC_POST_FILE_ARGS + +#endif /* !HAVE_NEARLY_WORKING_UTIMENSAT */ diff --git a/m4/utimensat.m4 b/m4/utimensat.m4 index bdabe24..cd0128a 100644 --- a/m4/utimensat.m4 +++ b/m4/utimensat.m4 @@ -1,4 +1,4 @@ -# serial 7 +# serial 8 # See if we need to provide utimensat replacement. dnl Copyright (C) 2009-2021 Free Software Foundation, Inc. @@ -12,6 +12,7 @@ AC_DEFUN([gl_FUNC_UTIMENSAT], [ AC_REQUIRE([gl_SYS_STAT_H_DEFAULTS]) AC_REQUIRE([gl_USE_SYSTEM_EXTENSIONS]) + AC_REQUIRE([AC_CANONICAL_HOST]) dnl for cross-compiles AC_CHECK_FUNCS_ONCE([utimensat]) if test $ac_cv_func_utimensat = no; then HAVE_UTIMENSAT=0 @@ -28,10 +29,19 @@ AC_DEFUN([gl_FUNC_UTIMENSAT], const char *f = "conftest.file"; if (close (creat (f, 0600))) return 1; + /* Test whether a trailing slash is handled correctly. + This fails on AIX 7.2. */ + { + struct timespec ts[2]; + ts[0].tv_sec = 345183300; ts[0].tv_nsec = 0; + ts[1] = ts[0]; + if (utimensat (AT_FDCWD, "conftest.file/", ts, 0) == 0) + result |= 2; + } /* Test whether the AT_SYMLINK_NOFOLLOW flag is supported. */ { if (utimensat (AT_FDCWD, f, NULL, AT_SYMLINK_NOFOLLOW)) - result |= 2; + result |= 4; } /* Test whether UTIME_NOW and UTIME_OMIT work. */ { @@ -41,7 +51,7 @@ AC_DEFUN([gl_FUNC_UTIMENSAT], ts[1].tv_sec = 1; ts[1].tv_nsec = UTIME_NOW; if (utimensat (AT_FDCWD, f, ts, 0)) - result |= 4; + result |= 8; } sleep (1); { @@ -52,19 +62,44 @@ AC_DEFUN([gl_FUNC_UTIMENSAT], ts[1].tv_sec = 1; ts[1].tv_nsec = UTIME_OMIT; if (utimensat (AT_FDCWD, f, ts, 0)) - result |= 8; - if (stat (f, &st)) result |= 16; - else if (st.st_ctime < st.st_atime) + if (stat (f, &st)) result |= 32; + else if (st.st_ctime < st.st_atime) + result |= 64; } return result; ]])], [gl_cv_func_utimensat_works=yes], - [gl_cv_func_utimensat_works=no], - [gl_cv_func_utimensat_works="guessing yes"])]) - if test "$gl_cv_func_utimensat_works" = no; then - REPLACE_UTIMENSAT=1 - fi + [case $? in + 2) gl_cv_func_utimensat_works='nearly' ;; + *) gl_cv_func_utimensat_works=no ;; + esac + ], + [case "$host_os" in + # Guess yes on Linux or glibc systems. + linux-* | linux | *-gnu* | gnu*) + gl_cv_func_utimensat_works="guessing yes" ;; + # Guess 'nearly' on AIX. + aix*) + gl_cv_func_utimensat_works="guessing nearly" ;; + # If we don't know, obey --enable-cross-guesses. + *) + gl_cv_func_utimensat_works="$gl_cross_guess_normal" ;; + esac + ]) + ]) + case "$gl_cv_func_utimensat_works" in + *yes) + ;; + *nearly) + AC_DEFINE([HAVE_NEARLY_WORKING_UTIMENSAT], [1], + [Define to 1 of utimensat works, except for the trailing slash handling.]) + REPLACE_UTIMENSAT=1 + ;; + *) + REPLACE_UTIMENSAT=1 + ;; + esac fi ])