+2014-11-03 Pádraig Brady <P@draigBrady.com>
+
+ linkat: wrap to handle symlinks on OS X 10.10
+ * m4/linkat.m4 (gl_FUNC_LINKAT): linkat() is available on Yosemite
+ but not usable because it doesn't support creating hardlinks
+ to symlinks. Therefore add a generic test for this capability
+ and fallback to our emulation if linkat() fails with ENOTSUP.
+
2014-11-02 Paul Eggert <eggert@cs.ucla.edu>
open, openat: document nonstandard FreeBSD, NetBSD O_NOFOLLOW errno
@itemize
@item
This function is missing on some platforms:
-glibc 2.3.6, Mac OS X 10.5, FreeBSD 6.0, NetBSD 5.0, OpenBSD 3.8, Minix 3.1.8,
+glibc 2.3.6, Mac OS X < 10.10, FreeBSD 6.0, NetBSD 5.0, OpenBSD 3.8, 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 9, Interix 3.5, BeOS.
But the replacement function is not safe to be used in libraries and is not multithread-safe.
@item
+This function fails to directly hardlink symlinks on some platforms:
+Mac OS X 10.10.
+@item
This function fails to reject trailing slashes on non-directories on
some platforms:
AIX 7.1, Solaris 11 2011-11.
# endif
#endif
-#if !HAVE_LINKAT
+#if !HAVE_LINKAT || LINKAT_SYMLINK_NOTSUP
/* Create a link. If FILE1 is a symlink, either create a hardlink to
that symlink, or fake it by creating an identical symlink. */
# endif
+#endif /* !HAVE_LINKAT || LINKAT_SYMLINK_NOTSUP */
+
+#if !HAVE_LINKAT
+
/* Create a link to FILE1, in the directory open on descriptor FD1, to FILE2,
in the directory open on descriptor FD2. If FILE1 is a symlink, FLAG
controls whether to dereference FILE1 first. If possible, do it without
# endif
if (!flag)
- return linkat (fd1, file1, fd2, file2, flag);
+ {
+ int result = linkat (fd1, file1, fd2, file2, flag);
+# if LINKAT_SYMLINK_NOTSUP
+ /* OS X 10.10 has linkat() but it doesn't support
+ hardlinks to symlinks. Fallback to our emulation
+ in that case. */
+ if (result == -1 && (errno == ENOTSUP || errno == EOPNOTSUPP))
+ return at_func2 (fd1, file1, fd2, file2, link_immediate);
+# endif
+ return result;
+ }
/* Cache the information on whether the system call really works. */
{
if test $ac_cv_func_linkat = no; then
HAVE_LINKAT=0
else
+ dnl OS X Yosemite has linkat() but it's not sufficient
+ dnl to our needs since it doesn't support creating
+ dnl hardlinks to symlinks. Therefore check for that
+ dnl capability before considering using the system version.
+ AC_CACHE_CHECK([whether linkat() can link symlinks],
+ [gl_cv_func_linkat_nofollow],
+ [rm -rf conftest.l1 conftest.l2
+ ln -s target conftest.l1
+ AC_RUN_IFELSE([AC_LANG_PROGRAM(
+ [[#include <fcntl.h>
+ #include <unistd.h>
+ ]],
+ [return linkat (AT_FDCWD, "conftest.l1", AT_FDCWD,
+ "conftest.l2", 0);
+ ])],
+ [gl_cv_func_linkat_nofollow=yes
+ LINKAT_SYMLINK_NOTSUP=0],
+ [gl_cv_func_linkat_nofollow=no
+ LINKAT_SYMLINK_NOTSUP=1])
+ rm -rf conftest.l1 conftest.l2])
+
AC_CACHE_CHECK([whether linkat(,AT_SYMLINK_FOLLOW) works],
[gl_cv_func_linkat_follow],
[rm -rf conftest.f1 conftest.f2
[gl_cv_func_linkat_follow=yes],
[gl_cv_func_linkat_follow="need runtime check"])
rm -rf conftest.f1 conftest.f2])
+
AC_CACHE_CHECK([whether linkat handles trailing slash correctly],
[gl_cv_func_linkat_slash],
[rm -rf conftest.a conftest.b conftest.c conftest.d
*yes) gl_linkat_slash_bug=0 ;;
*) gl_linkat_slash_bug=1 ;;
esac
+
if test "$gl_cv_func_linkat_follow" != yes \
+ || test "$gl_cv_func_linkat_nofollow" != yes \
|| test $gl_linkat_slash_bug = 1; then
REPLACE_LINKAT=1
AC_DEFINE_UNQUOTED([LINKAT_TRAILING_SLASH_BUG], [$gl_linkat_slash_bug],
[Define to 1 if linkat fails to recognize a trailing slash.])
+ AC_DEFINE_UNQUOTED([LINKAT_SYMLINK_NOTSUP], [$LINKAT_SYMLINK_NOTSUP],
+ [Define to 1 if linkat can create hardlinks to symlinks])
fi
fi
])
fcntl-h [test $HAVE_LINKAT = 0 || test $REPLACE_LINKAT = 1]
filenamecat-lgpl [test $HAVE_LINKAT = 0 || test $REPLACE_LINKAT = 1]
link-follow [test $HAVE_LINKAT = 0 || test $REPLACE_LINKAT = 1]
-areadlink [test $HAVE_LINKAT = 0]
-at-internal [test $HAVE_LINKAT = 0]
-dosname [test $HAVE_LINKAT = 0]
-fstat [test $HAVE_LINKAT = 0]
-getcwd-lgpl [test $HAVE_LINKAT = 0]
-openat-h [test $HAVE_LINKAT = 0]
-openat-die [test $HAVE_LINKAT = 0]
-link [test $HAVE_LINKAT = 0]
-lstat [test $HAVE_LINKAT = 0]
-same-inode [test $HAVE_LINKAT = 0]
-save-cwd [test $HAVE_LINKAT = 0]
-symlink [test $HAVE_LINKAT = 0]
+areadlink [test $HAVE_LINKAT = 0 || test $REPLACE_LINKAT = 1]
+at-internal [test $HAVE_LINKAT = 0 || test $REPLACE_LINKAT = 1]
+dosname [test $HAVE_LINKAT = 0 || test $REPLACE_LINKAT = 1]
+fstat [test $HAVE_LINKAT = 0 || test $REPLACE_LINKAT = 1]
+getcwd-lgpl [test $HAVE_LINKAT = 0 || test $REPLACE_LINKAT = 1]
+openat-h [test $HAVE_LINKAT = 0 || test $REPLACE_LINKAT = 1]
+openat-die [test $HAVE_LINKAT = 0 || test $REPLACE_LINKAT = 1]
+link [test $HAVE_LINKAT = 0 || test $REPLACE_LINKAT = 1]
+lstat [test $HAVE_LINKAT = 0 || test $REPLACE_LINKAT = 1]
+same-inode [test $HAVE_LINKAT = 0 || test $REPLACE_LINKAT = 1]
+save-cwd [test $HAVE_LINKAT = 0 || test $REPLACE_LINKAT = 1]
+symlink [test $HAVE_LINKAT = 0 || test $REPLACE_LINKAT = 1]
areadlinkat [test $REPLACE_LINKAT = 1]
fstatat [test $REPLACE_LINKAT = 1]
gl_FUNC_LINKAT
if test $HAVE_LINKAT = 0 || test $REPLACE_LINKAT = 1; then
AC_LIBOBJ([linkat])
-fi
-if test $HAVE_LINKAT = 0; then
AC_LIBOBJ([at-func2])
fi
gl_UNISTD_MODULE_INDICATOR([linkat])