In the symlink() replacement, the trailing slash handling uses lstat() to test whether the designated file exists:
struct stat st; if (lstat (name, &st) == 0 || errno == EOVERFLOW) errno = EEXIST; return -1; By symmetry, in the symlinkat() handling, it should use lstatat() (as defined in openat.h), i.e. fstatat() with AT_SYMLINK_NOFOLLOW flag. (The POSIX spec <https://pubs.opengroup.org/onlinepubs/9699919799/functions/symlinkat.html> is not very clear to me on this point.) Thus this patch. Tested on AIX (which is the platform for which this workaround was implemented in 2014). 2021-01-04 Bruno Haible <br...@clisp.org> symlinkat: Fix trailing slash handling. * lib/symlinkat.c (rpl_symlinkat): Don't follow symlinks during the trailing slash handling. * modules/symlinkat (Depends-on): Update conditions. diff --git a/lib/symlinkat.c b/lib/symlinkat.c index 404fa65..58ddb89 100644 --- a/lib/symlinkat.c +++ b/lib/symlinkat.c @@ -27,6 +27,7 @@ #if HAVE_SYMLINKAT # undef symlinkat +#include <fcntl.h> #include <sys/stat.h> #include <string.h> @@ -38,7 +39,8 @@ rpl_symlinkat (char const *contents, int fd, char const *name) if (len && name[len - 1] == '/') { struct stat st; - if (fstatat (fd, name, &st, 0) == 0 || errno == EOVERFLOW) + if (fstatat (fd, name, &st, AT_SYMLINK_NOFOLLOW) == 0 + || errno == EOVERFLOW) errno = EEXIST; return -1; } diff --git a/modules/symlinkat b/modules/symlinkat index 16d0fcc..03b6a40 100644 --- a/modules/symlinkat +++ b/modules/symlinkat @@ -9,10 +9,10 @@ m4/symlinkat.m4 Depends-on: unistd extensions +fcntl-h [test $HAVE_SYMLINKAT = 0 || test $REPLACE_SYMLINKAT = 1] at-internal [test $HAVE_SYMLINKAT = 0] errno [test $HAVE_SYMLINKAT = 0] fchdir [test $HAVE_SYMLINKAT = 0] -fcntl-h [test $HAVE_SYMLINKAT = 0] filename [test $HAVE_SYMLINKAT = 0] openat-die [test $HAVE_SYMLINKAT = 0] openat-h [test $HAVE_SYMLINKAT = 0]