linkat: Work around AIX 7.1 bug.
authorBruno Haible <bruno@clisp.org>
Sat, 31 Jul 2010 13:20:35 +0000 (15:20 +0200)
committerBruno Haible <bruno@clisp.org>
Sat, 31 Jul 2010 13:20:35 +0000 (15:20 +0200)
ChangeLog
doc/posix-functions/linkat.texi
lib/linkat.c
m4/linkat.m4

index e90bfe8..032df8e 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,16 @@
 2010-07-31  Bruno Haible  <bruno@clisp.org>
 
+       linkat: Work around AIX 7.1 bug.
+       * m4/linkat.m4 (gl_FUNC_LINKAT): Require AC_CANONICAL_HOST. Test
+       whether linkat handles trailing slash correctly. If not, replace linkat
+       and define LINKAT_TRAILING_SLASH_BUG.
+       * lib/linkat.c (rpl_linkat): If LINKAT_TRAILING_SLASH_BUG is defined,
+       check whether (fd1,file1) points to a directory if file1 or file2 ends
+       in a slash. Code taken from lib/link.c.
+       * doc/posix-functions/linkat.texi: Mention trailing slash bug.
+
+2010-07-31  Bruno Haible  <bruno@clisp.org>
+
        Correctly determine whether pow is available in libc on AIX 7 with xlc.
        * m4/mathfunc.m4 (gl_MATHFUNC): Actually use the 'funcptr' variable.
        This disables an xlc optimization that was causing wrong test results.
index 1fe5434..1d9cc89 100644 (file)
@@ -13,6 +13,10 @@ This function is missing on some platforms:
 glibc 2.3.6, MacOS X 10.3, FreeBSD 6.0, NetBSD 3.0, OpenBSD 3.8, AIX
 5.1, HP-UX 11, IRIX 6.5, OSF/1 5.1, Solaris 10, Cygwin 1.5.x, mingw, 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 reject trailing slashes on non-directories on
+some platforms:
+AIX 7.1.
 @end itemize
 
 Portability problems not fixed by Gnulib:
index 30a61e9..73b1e3e 100644 (file)
@@ -262,14 +262,38 @@ linkat_follow (int fd1, char const *file1, int fd2, char const *file2)
 int
 rpl_linkat (int fd1, char const *file1, int fd2, char const *file2, int flag)
 {
-  if (!flag)
-    return linkat (fd1, file1, fd2, file2, flag);
   if (flag & ~AT_SYMLINK_FOLLOW)
     {
       errno = EINVAL;
       return -1;
     }
 
+#if LINKAT_TRAILING_SLASH_BUG
+  /* Reject trailing slashes on non-directories.  */
+  {
+    size_t len1 = strlen (file1);
+    size_t len2 = strlen (file2);
+    if ((len1 && file1[len1 - 1] == '/')
+        || (len2 && file2[len2 - 1] == '/'))
+      {
+        /* Let linkat() decide whether hard-linking directories is legal.
+           If fstatat() fails, then linkat() should fail for the same reason;
+           if fstatat() succeeds, require a directory.  */
+        struct stat st;
+        if (fstatat (fd1, file1, &st, flag ? 0 : AT_SYMLINK_NOFOLLOW))
+          return -1;
+        if (!S_ISDIR (st.st_mode))
+          {
+            errno = ENOTDIR;
+            return -1;
+          }
+      }
+  }
+#endif
+
+  if (!flag)
+    return linkat (fd1, file1, fd2, file2, flag);
+
   /* Cache the information on whether the system call really works.  */
   {
     static int have_follow_really; /* 0 = unknown, 1 = yes, -1 = no */
index 630ce89..c0b2729 100644 (file)
@@ -1,4 +1,4 @@
-# serial 2
+# serial 3
 # See if we need to provide linkat replacement.
 
 dnl Copyright (C) 2009-2010 Free Software Foundation, Inc.
@@ -15,6 +15,7 @@ AC_DEFUN([gl_FUNC_LINKAT],
   AC_REQUIRE([gl_FUNC_LINK_FOLLOWS_SYMLINK])
   AC_REQUIRE([gl_UNISTD_H_DEFAULTS])
   AC_REQUIRE([gl_USE_SYSTEM_EXTENSIONS])
+  AC_REQUIRE([AC_CANONICAL_HOST]) dnl for cross-compiles
   AC_CHECK_FUNCS_ONCE([linkat symlink])
   AC_CHECK_HEADERS_ONCE([sys/param.h])
   if test $ac_cv_func_linkat = no; then
@@ -39,9 +40,60 @@ choke me
          [gl_cv_func_linkat_follow=yes],
          [gl_cv_func_linkat_follow="need runtime check"])
        rm -rf conftest.f1 conftest.f2])
-    if test "$gl_cv_func_linkat_follow" != yes; then
+    AC_CACHE_CHECK([whether linkat handles trailing slash correctly],
+      [gl_cv_func_linkat_slash],
+      [rm -rf conftest.a conftest.b conftest.c conftest.d
+       AC_RUN_IFELSE(
+         [AC_LANG_PROGRAM(
+            [[#include <unistd.h>
+              #include <fcntl.h>
+              #include <errno.h>
+              #include <stdio.h>
+            ]],
+            [[int fd;
+              int err;
+              int ret;
+              /* Create a regular file.  */
+              fd = open ("conftest.a", O_CREAT | O_EXCL | O_WRONLY, 0600);
+              if (fd < 0)
+                return 1;
+              if (write (fd, "hello", 5) < 5)
+                return 2;
+              if (close (fd) < 0)
+                return 3;
+              /* Test whether hard links are supported on the current
+                 device.  */
+              if (linkat (AT_FDCWD, "conftest.a", AT_FDCWD, "conftest.b",
+                          AT_SYMLINK_FOLLOW) < 0)
+                return 0;
+              /* Test whether a trailing "/" is treated like "/.".  */
+              if (linkat (AT_FDCWD, "conftest.a/", AT_FDCWD, "conftest.c",
+                          AT_SYMLINK_FOLLOW) == 0)
+                return 4;
+              if (linkat (AT_FDCWD, "conftest.a", AT_FDCWD, "conftest.d/",
+                          AT_SYMLINK_FOLLOW) == 0)
+                return 5;
+              return 0;
+            ]])],
+         [gl_cv_func_linkat_slash=yes],
+         [gl_cv_func_linkat_slash=no],
+         [# Guess yes on glibc systems, no otherwise.
+          case "$host_os" in
+            *-gnu*) gl_cv_func_linkat_slash="guessing yes";;
+            *)      gl_cv_func_linkat_slash="guessing no";;
+          esac
+         ])
+       rm -rf conftest.a conftest.b conftest.c conftest.d])
+    case "$gl_cv_func_linkat_slash" in
+      *yes) gl_linkat_slash_bug=0 ;;
+      *)    gl_linkat_slash_bug=1 ;;
+    esac
+    if test "$gl_cv_func_linkat_follow" != yes \
+       || test $gl_linkat_slash_bug = 1; then
       REPLACE_LINKAT=1
       AC_LIBOBJ([linkat])
+      AC_DEFINE_UNQUOTED([LINKAT_TRAILING_SLASH_BUG], [$gl_linkat_slash_bug],
+        [Define to 1 if linkat fails to recognize a trailing slash.])
     fi
   fi
 ])