These two patches remove the linkat wrapping on GNU/Linux,
and add the wrapping for OSX 10.10 to fall back to
emulation mode when trying to hardlink a symlink.

thanks,
Padraig.
>From 6de3a63272b5c4904cf9c85fcf3bc5488d36db19 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?P=C3=A1draig=20Brady?= <p...@draigbrady.com>
Date: Fri, 31 Oct 2014 18:12:15 +0000
Subject: [PATCH 1/2] 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.
---
 ChangeLog                       |    8 ++++++++
 doc/posix-functions/linkat.texi |    5 ++++-
 lib/linkat.c                    |   18 ++++++++++++++++--
 m4/linkat.m4                    |   26 ++++++++++++++++++++++++++
 modules/linkat                  |   26 ++++++++++++--------------
 5 files changed, 66 insertions(+), 17 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index 1f80f39..334a3c5 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,11 @@
+2014-10-31  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-10-31  Paul Eggert  <egg...@cs.ucla.edu>
 
 	obstack: add NEWS entry for recent incompatible changes
diff --git a/doc/posix-functions/linkat.texi b/doc/posix-functions/linkat.texi
index 232a50b..fadb350 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 6ee30fb..c01e3e3 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 2da0e30..a2b5213 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 f31dcf7..e0c4165 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])
-- 
1.7.7.6


>From c4ac895ee049c3860d6edfa6ce6556e80bb89734 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?P=C3=A1draig=20Brady?= <p...@draigbrady.com>
Date: Fri, 31 Oct 2014 18:18:36 +0000
Subject: [PATCH 2/2] linkat: don't unconditionally replace on GNU/Linux

* m4/linkat.m4 (gl_FUNC_LINKAT): The compile check for AT_SYMLINK_FOLLOW
was redundant for a few reasons.  It was present to support compiling
on new systems but running on the old narrow window of Linux 2.6.1[67].
It setup and cleaned up test files which weren't actually used.
On non __linux__ it compile tested AT_SYMLINK_FOLLOW, but that is
implicit in the following check.
---
 ChangeLog    |   10 ++++++++++
 m4/linkat.m4 |   21 +--------------------
 2 files changed, 11 insertions(+), 20 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index 334a3c5..b5de20a 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,15 @@
 2014-10-31  Pádraig Brady  <p...@draigbrady.com>
 
+	linkat: don't unconditionally replace on GNU/Linux
+	* m4/linkat.m4 (gl_FUNC_LINKAT): The compile check for AT_SYMLINK_FOLLOW
+	was redundant for a few reasons.  It was present to support compiling
+	on new systems but running on the old narrow window of Linux 2.6.1[67].
+	It setup and cleaned up test files which weren't actually used.
+	On non __linux__ it compile tested AT_SYMLINK_FOLLOW, but that is
+	implicit in the following check.
+
+2014-10-31  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
diff --git a/m4/linkat.m4 b/m4/linkat.m4
index a2b5213..fb0277d 100644
--- a/m4/linkat.m4
+++ b/m4/linkat.m4
@@ -41,24 +41,6 @@ AC_DEFUN([gl_FUNC_LINKAT],
           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
-       touch conftest.f1
-       AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
-#include <fcntl.h>
-#include <unistd.h>
-#ifdef __linux__
-/* Linux added linkat in 2.6.16, but did not add AT_SYMLINK_FOLLOW
-   until 2.6.18.  Always replace linkat to support older kernels.  */
-choke me
-#endif
-]], [return linkat (AT_FDCWD, "conftest.f1", AT_FDCWD, "conftest.f2",
-                    AT_SYMLINK_FOLLOW);])],
-         [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
@@ -108,8 +90,7 @@ choke me
       *)    gl_linkat_slash_bug=1 ;;
     esac
 
-    if test "$gl_cv_func_linkat_follow" != yes \
-       || test "$gl_cv_func_linkat_nofollow" != yes \
+    if 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],
-- 
1.7.7.6

Reply via email to