summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog8
-rw-r--r--doc/posix-functions/linkat.texi5
-rw-r--r--lib/linkat.c18
-rw-r--r--m4/linkat.m426
-rw-r--r--modules/linkat26
5 files changed, 66 insertions, 17 deletions
diff --git a/ChangeLog b/ChangeLog
index 12dc39229c..e9d848a611 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,11 @@
+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
diff --git a/doc/posix-functions/linkat.texi b/doc/posix-functions/linkat.texi
index 232a50b655..fadb350ab5 100644
--- a/doc/posix-functions/linkat.texi
+++ b/doc/posix-functions/linkat.texi
@@ -10,10 +10,13 @@ Portability problems fixed by Gnulib:
@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.
diff --git a/lib/linkat.c b/lib/linkat.c
index 6ee30fb4c3..c01e3e30f2 100644
--- a/lib/linkat.c
+++ b/lib/linkat.c
@@ -43,7 +43,7 @@
# 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. */
@@ -195,6 +195,10 @@ solaris_optimized_link_follow (char const *file1, char const *file2)
# 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
@@ -321,7 +325,17 @@ rpl_linkat (int fd1, char const *file1, int fd2, char const *file2, int flag)
# 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. */
{
diff --git a/m4/linkat.m4 b/m4/linkat.m4
index 2da0e30cb1..a2b5213c16 100644
--- a/m4/linkat.m4
+++ b/m4/linkat.m4
@@ -20,6 +20,27 @@ AC_DEFUN([gl_FUNC_LINKAT],
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
@@ -37,6 +58,7 @@ choke me
[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
@@ -85,11 +107,15 @@ choke me
*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
])
diff --git a/modules/linkat b/modules/linkat
index f31dcf70c5..e0c4165759 100644
--- a/modules/linkat
+++ b/modules/linkat
@@ -14,18 +14,18 @@ errno [test $HAVE_LINKAT = 0 || test $REPLACE_LINKAT = 1]
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]
@@ -33,8 +33,6 @@ configure.ac:
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])