>From 6de3a63272b5c4904cf9c85fcf3bc5488d36db19 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C3=A1draig=20Brady?= 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 + + 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 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. address@hidden 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 + #include + ]], + [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?= 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 + 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 + 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 -#include -#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