[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
access: Work around trailing slash bug on Mac OS X 10.5
From: |
Bruno Haible |
Subject: |
access: Work around trailing slash bug on Mac OS X 10.5 |
Date: |
Tue, 03 Oct 2023 16:38:42 +0200 |
On Mac OS X 10.5, I see this test failure:
../../gltests/test-faccessat.c:63: assertion 'faccessat (AT_FDCWD, BASE
"link2/", F_OK, 0) != 0' failed
FAIL test-faccessat (exit status: 134)
And indeed, POSIX
<https://pubs.opengroup.org/onlinepubs/9699919799/functions/access.html>
requires that faccessat() and access() fail with errno = ENOTDIR when
the file name argument has a trailing slash and points to "an existing file
that is neither a directory nor a symbolic link to a directory". Thus,
we have an faccessat() bug here.
On this platform, faccessat is emulated through access() and euidaccess():
$ nm gllib/faccessat.o
U ___error
U _access
U _euidaccess
00000000 T _faccessat
U _fchdir
U _free_cwd
U _openat_proc_name
U _openat_restore_fail
U _openat_save_fail
U _restore_cwd
U _rpl_free
U _save_cwd
So, the suspicion is that the bug comes from a bug in access() or euidaccess().
A 'dtruss' run ('dtruss' is the Mac OS X equivalent of 'strace') confirms it:
$ dtruss ./test-faccessat
...
symlink("test-faccessat.tfile\0", "test-faccessat.tlink2\0") = 0 0
access("test-faccessat.tlink2\0", 0x0, 0x0) = 0 0
access("test-faccessat.tlink2/\0", 0x0, 0x0) = 0 0
write_nocancel(0x2, "../../gltests/test-faccessat.c:63: assertion 'faccessat
(AT_FDCWD, BASE \"link2/\", F_OK, 0) != 0' failed\n\0", 0x68) =
104 0
...
So, the first thing we have to do here is to get access() work right.
2023-10-03 Bruno Haible <bruno@clisp.org>
access: Work around trailing slash bug on Mac OS X 10.5.
* m4/access.m4 (gl_FUNC_ACCESS): Test whether access honors a trailing
slash. Set REPLACE_ACCESS to 1 and define ACCESS_TRAILING_SLASH_BUG if
not.
* lib/access.c (access): Add an implementation for Unix-like platforms.
* tests/test-access.c (main): Test for result if the argument has a
trailing slash.
* modules/access-tests (Depends-on): Add 'symlink'.
* doc/posix-functions/access.texi: Mention the Mac OS X bug.
diff --git a/doc/posix-functions/access.texi b/doc/posix-functions/access.texi
index 8bfa2c1a67..fd114d300f 100644
--- a/doc/posix-functions/access.texi
+++ b/doc/posix-functions/access.texi
@@ -11,6 +11,10 @@
@item
This function does not support the @code{X_OK} mode on some platforms:
MSVC 14.
+@item
+This function does not reject trailing slashes on symlinks to non-directories
+on some platforms:
+Mac OS X 10.5.
@end itemize
Portability problems not fixed by Gnulib:
diff --git a/lib/access.c b/lib/access.c
index e2c12b1f19..75d2148453 100644
--- a/lib/access.c
+++ b/lib/access.c
@@ -20,7 +20,10 @@
#include <unistd.h>
#include <fcntl.h>
-#include <io.h>
+
+#if defined _WIN32 && ! defined __CYGWIN__
+
+# include <io.h>
int
access (const char *file, int mode)
@@ -29,3 +32,35 @@ access (const char *file, int mode)
mode = (mode & ~X_OK) | R_OK;
return _access (file, mode);
}
+
+#else
+
+# include <errno.h>
+# include <string.h>
+# include <sys/types.h>
+# include <sys/stat.h>
+
+int
+access (const char *file, int mode)
+# undef access
+{
+ int ret = access (file, mode);
+# if ACCESS_TRAILING_SLASH_BUG
+ if (ret == 0)
+ {
+ size_t file_len = strlen (file);
+ if (file_len > 0 && file[file_len - 1] == '/')
+ {
+ struct stat st;
+ if (stat (file, &st) == 0 && ! S_ISDIR (st.st_mode))
+ {
+ errno = ENOTDIR;
+ return -1;
+ }
+ }
+ }
+# endif
+ return ret;
+}
+
+#endif
diff --git a/m4/access.m4 b/m4/access.m4
index 259e2221fa..9fb9ee145e 100644
--- a/m4/access.m4
+++ b/m4/access.m4
@@ -1,4 +1,4 @@
-# access.m4 serial 2
+# access.m4 serial 3
dnl Copyright (C) 2019-2023 Free Software Foundation, Inc.
dnl This file is free software; the Free Software Foundation
dnl gives unlimited permission to copy and/or distribute it,
@@ -11,6 +11,55 @@ AC_DEFUN([gl_FUNC_ACCESS]
dnl On native Windows, access (= _access) does not support the X_OK mode.
dnl It works by chance on some versions of mingw.
case "$host_os" in
- mingw* | windows*) REPLACE_ACCESS=1 ;;
+ mingw* | windows*)
+ REPLACE_ACCESS=1
+ ;;
+ *)
+ dnl Mac OS X 10.5 mistakenly allows access("link-to-file/",amode).
+ AC_CHECK_FUNCS_ONCE([lstat])
+ AC_CACHE_CHECK([whether access honors trailing slash],
+ [gl_cv_func_access_slash_works],
+ [# Assume that if we have lstat, we can also check symlinks.
+ if test $ac_cv_func_lstat = yes; then
+ rm -rf conftest.f conftest.lnk
+ touch conftest.f || AC_MSG_ERROR([cannot create temporary files])
+ ln -s conftest.f conftest.lnk
+ AC_RUN_IFELSE(
+ [AC_LANG_PROGRAM([[
+ #include <unistd.h>
+ ]],
+ [[int result = 0;
+ if (access ("conftest.lnk/", R_OK) == 0)
+ result |= 1;
+ return result;
+ ]])],
+ [gl_cv_func_access_slash_works=yes],
+ [gl_cv_func_access_slash_works=no],
+ dnl When crosscompiling, assume access is broken.
+ [case "$host_os" in
+ # Guess yes on Linux systems.
+ linux-* | linux) gl_cv_func_access_slash_works="guessing
yes" ;;
+ # Guess yes on systems that emulate the
Linux system calls.
+ midipix*) gl_cv_func_access_slash_works="guessing
yes" ;;
+ # Guess yes on glibc systems.
+ *-gnu*) gl_cv_func_access_slash_works="guessing
yes" ;;
+ # If we don't know, obey
--enable-cross-guesses.
+ *)
gl_cv_func_access_slash_works="$gl_cross_guess_normal" ;;
+ esac
+ ])
+ rm -rf conftest.f conftest.lnk
+ else
+ gl_cv_func_access_slash_works="guessing yes"
+ fi
+ ])
+ case "$gl_cv_func_access_slash_works" in
+ *yes) ;;
+ *)
+ REPLACE_ACCESS=1
+ AC_DEFINE([ACCESS_TRAILING_SLASH_BUG], [1],
+ [Define if access does not correctly handle trailing slashes.])
+ ;;
+ esac
+ ;;
esac
])
diff --git a/modules/access-tests b/modules/access-tests
index 4b35c21150..78344887cd 100644
--- a/modules/access-tests
+++ b/modules/access-tests
@@ -6,6 +6,7 @@ tests/macros.h
Depends-on:
creat
root-uid
+symlink
configure.ac:
AC_CHECK_FUNCS_ONCE([geteuid])
diff --git a/tests/test-access.c b/tests/test-access.c
index 5f9a4ad216..771678f0f7 100644
--- a/tests/test-access.c
+++ b/tests/test-access.c
@@ -42,6 +42,7 @@ main ()
unlink (BASE "f1");
chmod (BASE "f2", 0600);
unlink (BASE "f2");
+ unlink (BASE "sl");
{
errno = 0;
@@ -59,9 +60,31 @@ main ()
{
ASSERT (close (creat (BASE "f1", 0700)) == 0);
+ ASSERT (access (BASE "f1", F_OK) == 0);
ASSERT (access (BASE "f1", R_OK) == 0);
ASSERT (access (BASE "f1", W_OK) == 0);
ASSERT (access (BASE "f1", X_OK) == 0);
+
+ ASSERT (access (BASE "f1/", F_OK) == -1);
+ ASSERT (errno == ENOTDIR);
+ ASSERT (access (BASE "f1/", R_OK) == -1);
+ ASSERT (errno == ENOTDIR);
+ ASSERT (access (BASE "f1/", W_OK) == -1);
+ ASSERT (errno == ENOTDIR);
+ ASSERT (access (BASE "f1/", X_OK) == -1);
+ ASSERT (errno == ENOTDIR);
+
+ if (symlink (BASE "f1", BASE "sl") == 0)
+ {
+ ASSERT (access (BASE "sl/", F_OK) == -1);
+ ASSERT (errno == ENOTDIR);
+ ASSERT (access (BASE "sl/", R_OK) == -1);
+ ASSERT (errno == ENOTDIR);
+ ASSERT (access (BASE "sl/", W_OK) == -1);
+ ASSERT (errno == ENOTDIR);
+ ASSERT (access (BASE "sl/", X_OK) == -1);
+ ASSERT (errno == ENOTDIR);
+ }
}
{
ASSERT (close (creat (BASE "f2", 0600)) == 0);
@@ -90,6 +113,7 @@ main ()
ASSERT (unlink (BASE "f1") == 0);
ASSERT (chmod (BASE "f2", 0600) == 0);
ASSERT (unlink (BASE "f2") == 0);
+ unlink (BASE "sl");
return 0;
}
- access: Work around trailing slash bug on Mac OS X 10.5,
Bruno Haible <=