summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog25
-rw-r--r--doc/posix-functions/faccessat.texi4
-rw-r--r--lib/faccessat.c60
-rw-r--r--lib/unistd.in.h16
-rw-r--r--m4/faccessat.m46
-rw-r--r--m4/unistd_h.m43
-rw-r--r--modules/faccessat21
-rw-r--r--modules/faccessat-tests1
-rw-r--r--modules/unistd1
-rw-r--r--tests/test-faccessat.c28
10 files changed, 143 insertions, 22 deletions
diff --git a/ChangeLog b/ChangeLog
index d61de30698..651a9eb007 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,28 @@
+2017-11-11 Paul Eggert <eggert@cs.ucla.edu>
+
+ faccessat: port to macOS (Bug#29231)
+ macOS faccessat has the same bug that lstat does: if the file
+ name ends in '/' it ignores the trailing slash.
+ Problem reported for Emacs by Vincent Zhang.
+ * doc/posix-functions/faccessat.texi (faccessat): Document this.
+ * lib/faccessat.c (_GL_INCLUDING_UNISTD_H): Define and undef
+ around the initial includes. Include errno.h, string.h, sys/stat.h.
+ (orig_faccessat) [HAVE_FACCESSAT]: New function.
+ Include "unistd.h" after defining it.
+ (rpl_faccessat) [HAVE_FACCESSAT]: New implementation.
+ * lib/unistd.in.h (faccessat) [REPLACE_FACCESSAT]:
+ Handle in the usual way.
+ * m4/faccessat.m4 (gl_FUNC_FACCESSAT): Replace faccessat if
+ lstat dereferences symlinks, since faccessat is likely to
+ have the same problem.
+ * m4/unistd_h.m4 (gl_UNISTD_H_DEFAULTS): Default REPLACE_ACCESSAT.
+ * modules/faccessat (Depends-on): Add fstatat.
+ Depend if REPLACE_FACCESSAT is 1, too.
+ (configure.ac): Link if REPLACE_FACCESSAT is 1.
+ * modules/faccessat-tests (Depends-on): Add symlink.
+ * modules/unistd (unistd.h): Substitute REPLACE_FACCESSAT.
+ * tests/test-faccessat.c (main): Test for the bug.
+
2017-11-11 Bruno Haible <bruno@clisp.org>
getprogname: Fix compilation error on IRIX.
diff --git a/doc/posix-functions/faccessat.texi b/doc/posix-functions/faccessat.texi
index 48ba17225f..19c2b1d653 100644
--- a/doc/posix-functions/faccessat.texi
+++ b/doc/posix-functions/faccessat.texi
@@ -13,6 +13,10 @@ This function is missing on some platforms:
glibc 2.3.6, macOS 10.12, FreeBSD 7.4, NetBSD 6.1.5, OpenBSD 4.9, Minix 3.1.8,
AIX 5.1, HP-UX 11, IRIX 6.5, OSF/1 5.1, Solaris 10, Cygwin 1.5.x, mingw, MSVC 14,
Interix 3.5, BeOS.
+@item
+On some platforms, @code{faccessat (dfd, "file/", amode, flag)}
+succeeds instead of failing when @file{file} is not a directory.
+macOS 10.13.
@end itemize
Portability problems not fixed by Gnulib:
diff --git a/lib/faccessat.c b/lib/faccessat.c
index 6cf9c99df2..fa9235820d 100644
--- a/lib/faccessat.c
+++ b/lib/faccessat.c
@@ -16,10 +16,31 @@
/* written by Eric Blake */
+/* If the user's config.h happens to include <unistd.h>, let it include only
+ the system's <unistd.h> here, so that orig_faccessat doesn't recurse to
+ rpl_faccessat. */
+#define _GL_INCLUDING_UNISTD_H
#include <config.h>
#include <unistd.h>
+#include <errno.h>
#include <fcntl.h>
+#include <string.h>
+#include <sys/stat.h>
+#undef _GL_INCLUDING_UNISTD_H
+
+#if HAVE_FACCESSAT
+static int
+orig_faccessat (int fd, char const *name, int mode, int flag)
+{
+ return faccessat (fd, name, mode, flag);
+}
+#endif
+
+/* Write "unistd.h" here, not <unistd.h>, otherwise OSF/1 5.1 DTK cc
+ eliminates this include because of the preliminary #include <unistd.h>
+ above. */
+#include "unistd.h"
#ifndef HAVE_ACCESS
/* Mingw lacks access, but it also lacks real vs. effective ids, so
@@ -28,6 +49,29 @@
# define access euidaccess
#endif
+#if HAVE_FACCESSAT
+
+int
+rpl_faccessat (int fd, char const *file, int mode, int flag)
+{
+ int result = orig_faccessat (fd, file, mode, flag);
+
+ if (result == 0 && file[strlen (file) - 1] == '/')
+ {
+ struct stat st;
+ result = fstatat (fd, file, &st, 0);
+ if (result == 0 && !S_ISDIR (st.st_mode))
+ {
+ errno = ENOTDIR;
+ return -1;
+ }
+ }
+
+ return result;
+}
+
+#else /* !HAVE_FACCESSAT */
+
/* Invoke access or euidaccess on file, FILE, using mode MODE, in the directory
open on descriptor FD. If possible, do it without changing the
working directory. Otherwise, resort to using save_cwd/fchdir, then
@@ -36,10 +80,12 @@
Note that this implementation only supports AT_EACCESS, although some
native versions also support AT_SYMLINK_NOFOLLOW. */
-#define AT_FUNC_NAME faccessat
-#define AT_FUNC_F1 euidaccess
-#define AT_FUNC_F2 access
-#define AT_FUNC_USE_F1_COND AT_EACCESS
-#define AT_FUNC_POST_FILE_PARAM_DECLS , int mode, int flag
-#define AT_FUNC_POST_FILE_ARGS , mode
-#include "at-func.c"
+# define AT_FUNC_NAME faccessat
+# define AT_FUNC_F1 euidaccess
+# define AT_FUNC_F2 access
+# define AT_FUNC_USE_F1_COND AT_EACCESS
+# define AT_FUNC_POST_FILE_PARAM_DECLS , int mode, int flag
+# define AT_FUNC_POST_FILE_ARGS , mode
+# include "at-func.c"
+
+#endif
diff --git a/lib/unistd.in.h b/lib/unistd.in.h
index b5b6e0e9c6..47a2679567 100644
--- a/lib/unistd.in.h
+++ b/lib/unistd.in.h
@@ -461,13 +461,25 @@ _GL_WARN_ON_USE (euidaccess, "euidaccess is unportable - "
#if @GNULIB_FACCESSAT@
-# if !@HAVE_FACCESSAT@
+# if @REPLACE_FACCESSAT@
+# if !(defined __cplusplus && defined GNULIB_NAMESPACE)
+# undef faccessat
+# define faccessat rpl_faccessat
+# endif
+_GL_FUNCDECL_RPL (faccessat, int,
+ (int fd, char const *name, int mode, int flag)
+ _GL_ARG_NONNULL ((2)));
+_GL_CXXALIAS_RPL (faccessat, int,
+ (int fd, char const *name, int mode, int flag));
+# else
+# if !@HAVE_FACCESSAT@
_GL_FUNCDECL_SYS (faccessat, int,
(int fd, char const *file, int mode, int flag)
_GL_ARG_NONNULL ((2)));
-# endif
+# endif
_GL_CXXALIAS_SYS (faccessat, int,
(int fd, char const *file, int mode, int flag));
+# endif
_GL_CXXALIASWARN (faccessat);
#elif defined GNULIB_POSIXCHECK
# undef faccessat
diff --git a/m4/faccessat.m4 b/m4/faccessat.m4
index 837ae5407c..f4cb49d166 100644
--- a/m4/faccessat.m4
+++ b/m4/faccessat.m4
@@ -1,4 +1,4 @@
-# serial 6
+# serial 7
# See if we need to provide faccessat replacement.
dnl Copyright (C) 2009-2017 Free Software Foundation, Inc.
@@ -18,10 +18,12 @@ AC_DEFUN([gl_FUNC_FACCESSAT],
AC_CHECK_FUNCS_ONCE([faccessat])
if test $ac_cv_func_faccessat = no; then
HAVE_FACCESSAT=0
+ elif test "$gl_cv_func_lstat_dereferences_slashed_symlink" != yes; then
+ REPLACE_FACCESSAT=1
fi
])
-# Prerequisites of lib/faccessat.m4.
+# Prerequisites of lib/faccessat.c.
AC_DEFUN([gl_PREREQ_FACCESSAT],
[
AC_CHECK_FUNCS([access])
diff --git a/m4/unistd_h.m4 b/m4/unistd_h.m4
index cc44677d9e..60e7ea4d04 100644
--- a/m4/unistd_h.m4
+++ b/m4/unistd_h.m4
@@ -1,4 +1,4 @@
-# unistd_h.m4 serial 70
+# unistd_h.m4 serial 71
dnl Copyright (C) 2006-2017 Free Software Foundation, Inc.
dnl This file is free software; the Free Software Foundation
dnl gives unlimited permission to copy and/or distribute it,
@@ -159,6 +159,7 @@ AC_DEFUN([gl_UNISTD_H_DEFAULTS],
REPLACE_CLOSE=0; AC_SUBST([REPLACE_CLOSE])
REPLACE_DUP=0; AC_SUBST([REPLACE_DUP])
REPLACE_DUP2=0; AC_SUBST([REPLACE_DUP2])
+ REPLACE_FACCESSAT=0; AC_SUBST([REPLACE_FACCESSAT])
REPLACE_FCHOWNAT=0; AC_SUBST([REPLACE_FCHOWNAT])
REPLACE_FTRUNCATE=0; AC_SUBST([REPLACE_FTRUNCATE])
REPLACE_GETCWD=0; AC_SUBST([REPLACE_GETCWD])
diff --git a/modules/faccessat b/modules/faccessat
index ee4f2a5a9a..d13d167e1f 100644
--- a/modules/faccessat
+++ b/modules/faccessat
@@ -9,19 +9,20 @@ m4/faccessat.m4
Depends-on:
unistd
extensions
-at-internal [test $HAVE_FACCESSAT = 0]
-dosname [test $HAVE_FACCESSAT = 0]
-errno [test $HAVE_FACCESSAT = 0]
-fchdir [test $HAVE_FACCESSAT = 0]
-fcntl-h [test $HAVE_FACCESSAT = 0]
-openat-die [test $HAVE_FACCESSAT = 0]
-openat-h [test $HAVE_FACCESSAT = 0]
-save-cwd [test $HAVE_FACCESSAT = 0]
-euidaccess [test $HAVE_FACCESSAT = 0]
+at-internal [test $HAVE_FACCESSAT = 0 || test $REPLACE_FACCESSAT = 1]
+dosname [test $HAVE_FACCESSAT = 0 || test $REPLACE_FACCESSAT = 1]
+errno [test $HAVE_FACCESSAT = 0 || test $REPLACE_FACCESSAT = 1]
+fchdir [test $HAVE_FACCESSAT = 0 || test $REPLACE_FACCESSAT = 1]
+fcntl-h [test $HAVE_FACCESSAT = 0 || test $REPLACE_FACCESSAT = 1]
+fstatat [test $HAVE_FACCESSAT = 0 || test $REPLACE_FACCESSAT = 1]
+openat-die [test $HAVE_FACCESSAT = 0 || test $REPLACE_FACCESSAT = 1]
+openat-h [test $HAVE_FACCESSAT = 0 || test $REPLACE_FACCESSAT = 1]
+save-cwd [test $HAVE_FACCESSAT = 0 || test $REPLACE_FACCESSAT = 1]
+euidaccess [test $HAVE_FACCESSAT = 0 || test $REPLACE_FACCESSAT = 1]
configure.ac:
gl_FUNC_FACCESSAT
-if test $HAVE_FACCESSAT = 0; then
+if test $HAVE_FACCESSAT = 0 || test $REPLACE_FACCESSAT = 1; then
AC_LIBOBJ([faccessat])
gl_PREREQ_FACCESSAT
fi
diff --git a/modules/faccessat-tests b/modules/faccessat-tests
index 144e0968c6..3beffcd23b 100644
--- a/modules/faccessat-tests
+++ b/modules/faccessat-tests
@@ -5,6 +5,7 @@ tests/macros.h
Depends-on:
fcntl-h
+symlink
configure.ac:
diff --git a/modules/unistd b/modules/unistd
index c258110785..98b6f9ef61 100644
--- a/modules/unistd
+++ b/modules/unistd
@@ -135,6 +135,7 @@ unistd.h: unistd.in.h $(top_builddir)/config.status $(CXXDEFS_H) $(ARG_NONNULL_H
-e 's|@''REPLACE_CLOSE''@|$(REPLACE_CLOSE)|g' \
-e 's|@''REPLACE_DUP''@|$(REPLACE_DUP)|g' \
-e 's|@''REPLACE_DUP2''@|$(REPLACE_DUP2)|g' \
+ -e 's|@''REPLACE_FACCESSAT''@|$(REPLACE_FACCESSAT)|g' \
-e 's|@''REPLACE_FCHOWNAT''@|$(REPLACE_FCHOWNAT)|g' \
-e 's|@''REPLACE_FTRUNCATE''@|$(REPLACE_FTRUNCATE)|g' \
-e 's|@''REPLACE_GETCWD''@|$(REPLACE_GETCWD)|g' \
diff --git a/tests/test-faccessat.c b/tests/test-faccessat.c
index bea10cbc5a..53855ba91a 100644
--- a/tests/test-faccessat.c
+++ b/tests/test-faccessat.c
@@ -26,6 +26,8 @@ SIGNATURE_CHECK (faccessat, int, (int, const char *, int, int));
#include "macros.h"
+#define BASE "test-lstat.t"
+
int
main (void)
{
@@ -42,5 +44,31 @@ main (void)
ASSERT (errno == EBADF);
}
+ /* Test behavior with trailing slash. */
+ ASSERT (faccessat (AT_FDCWD, ".", X_OK, 0) == 0);
+ ASSERT (faccessat (AT_FDCWD, "./", X_OK, 0) == 0);
+ ASSERT (close (open (BASE "file", O_CREAT | O_WRONLY, 0)) == 0);
+ ASSERT (faccessat (AT_FDCWD, BASE "file", F_OK, 0) == 0);
+ ASSERT (faccessat (AT_FDCWD, BASE "file/", F_OK, 0) != 0);
+ unlink (BASE "link1");
+ if (symlink (".", BASE "link1") == 0)
+ {
+ ASSERT (faccessat (AT_FDCWD, BASE "link1", X_OK, 0) == 0);
+ ASSERT (faccessat (AT_FDCWD, BASE "link1/", X_OK, 0) == 0);
+
+ unlink (BASE "link2");
+ ASSERT (symlink (BASE "file", BASE "link2") == 0);
+ ASSERT (faccessat (AT_FDCWD, BASE "link2", F_OK, 0) == 0);
+ ASSERT (faccessat (AT_FDCWD, BASE "link2/", F_OK, 0) != 0);
+ unlink (BASE "link2");
+
+ unlink (BASE "link3");
+ ASSERT (symlink (BASE "no-such-file", BASE "link3") == 0);
+ ASSERT (faccessat (AT_FDCWD, BASE "link3", F_OK, 0) != 0);
+ ASSERT (faccessat (AT_FDCWD, BASE "link3/", F_OK, 0) != 0);
+ unlink (BASE "link3");
+ }
+ unlink (BASE "link1");
+
return 0;
}