]> Savannah Git Hosting - gnulib.git/commitdiff
faccessat: port to macOS (Bug#29231)
authorPaul Eggert <eggert@cs.ucla.edu>
Sun, 12 Nov 2017 06:33:38 +0000 (22:33 -0800)
committerPaul Eggert <eggert@cs.ucla.edu>
Sun, 12 Nov 2017 06:34:01 +0000 (22:34 -0800)
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.

ChangeLog
doc/posix-functions/faccessat.texi
lib/faccessat.c
lib/unistd.in.h
m4/faccessat.m4
m4/unistd_h.m4
modules/faccessat
modules/faccessat-tests
modules/unistd
tests/test-faccessat.c

index d61de30698e12e0dfb865b4297219dfe7dda346f..651a9eb0079417c33a9afa51459189b5c2905127 100644 (file)
--- 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.
index 48ba17225fb83104c98a8da63fb9b444bc5efeb8..19c2b1d6531cff4a60c13b18c954b3b8d39287f1 100644 (file)
@@ -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:
index 6cf9c99df204b9977ccabeb8062600281efcb7c4..fa9235820dc310adcff25674a5043e557be983b3 100644 (file)
 
 /* 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
 # 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
    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
index b5b6e0e9c6656acc1a3d1ddae92155cfd9d791c2..47a26795679f29385eb3540a68cbe7e6ab4c1550 100644 (file)
@@ -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
index 837ae5407c925164722f51abbc19299e25ce39d7..f4cb49d166cde90953afb8ca9384142a9d7952bf 100644 (file)
@@ -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])
index cc44677d9eb20825cc7506ff9494f7c819b2e139..60e7ea4d04975eac0d56b0e4cd31305475553ff2 100644 (file)
@@ -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])
index ee4f2a5a9adfe84bee02a4c4ed1082f75184971e..d13d167e1f80a1055d89acd0fbf723f6be05cef4 100644 (file)
@@ -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
index 144e0968c66aa888f52ef87307c7f5b059cab171..3beffcd23b802b16a80437b5f20628c8e1e00f5e 100644 (file)
@@ -5,6 +5,7 @@ tests/macros.h
 
 Depends-on:
 fcntl-h
+symlink
 
 configure.ac:
 
index c258110785b6c19ed178d9ddcd8a5bdddf6a4527..98b6f9ef611e1fd44507ae0cc986d9b1a640a609 100644 (file)
@@ -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' \
index bea10cbc5a8f163f397ee08cd2aa0bcfd1bddfcd..53855ba91ad06e96f3feb5f5add6f0e81eb70006 100644 (file)
@@ -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;
 }