]> Savannah Git Hosting - gnulib.git/commitdiff
linkat: wrap to handle symlinks on OS X 10.10
authorPádraig Brady <P@draigBrady.com>
Fri, 31 Oct 2014 18:12:15 +0000 (18:12 +0000)
committerPádraig Brady <P@draigBrady.com>
Mon, 3 Nov 2014 14:10:33 +0000 (15:10 +0100)
* 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
doc/posix-functions/linkat.texi
lib/linkat.c
m4/linkat.m4
modules/linkat

index 12dc39229c27994c3dd8c6019917f5bbd002385b..e9d848a611c863d479cb54d7b601a62da9eff2ff 100644 (file)
--- 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
index 232a50b65506eed99a5289916d371a73985f27e7..fadb350ab51819f3e6a052116920a06339d270d4 100644 (file)
@@ -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.
index 6ee30fb4c3d48bb0ab789b5ab010eead00de90de..c01e3e30f2a65fde50caa545180af0b761e9c0da 100644 (file)
@@ -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.  */
   {
index 2da0e30cb1d4154244a3dd748a7023ee862c23fb..a2b5213c16f411569c43119d5701b938e824f16a 100644 (file)
@@ -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
 ])
index f31dcf70c524901e2226cc1ddfc09636287a9ab7..e0c4165759685eaec97a584c5b170a4231835219 100644 (file)
@@ -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])